[device/celestica]: Add xcvrd event support for Seastone-DX010 (#5896)
- Add sysfs interrupt to notify userspace app of external interrupt - Implement get_change_event() in chassis api.
This commit is contained in:
parent
9580b0407f
commit
4257c792a2
@ -9,6 +9,9 @@
|
|||||||
try:
|
try:
|
||||||
import sys
|
import sys
|
||||||
from sonic_platform_base.chassis_base import ChassisBase
|
from sonic_platform_base.chassis_base import ChassisBase
|
||||||
|
from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper
|
||||||
|
from sonic_py_common import device_info
|
||||||
|
from .event import SfpEvent
|
||||||
from .helper import APIHelper
|
from .helper import APIHelper
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
raise ImportError(str(e) + "- required module not found")
|
raise ImportError(str(e) + "- required module not found")
|
||||||
@ -45,9 +48,14 @@ class Chassis(ChassisBase):
|
|||||||
self.__initialize_components()
|
self.__initialize_components()
|
||||||
|
|
||||||
def __initialize_sfp(self):
|
def __initialize_sfp(self):
|
||||||
|
sfputil_helper = SfpUtilHelper()
|
||||||
|
port_config_file_path = device_info.get_path_to_port_config_file()
|
||||||
|
sfputil_helper.read_porttab_mappings(port_config_file_path, 0)
|
||||||
|
|
||||||
from sonic_platform.sfp import Sfp
|
from sonic_platform.sfp import Sfp
|
||||||
for index in range(0, NUM_SFP):
|
for index in range(0, NUM_SFP):
|
||||||
sfp = Sfp(index)
|
name_idx = 0 if index+1 == NUM_SFP else index+1
|
||||||
|
sfp = Sfp(index, sfputil_helper.logical[name_idx])
|
||||||
self._sfp_list.append(sfp)
|
self._sfp_list.append(sfp)
|
||||||
self.sfp_module_initialized = True
|
self.sfp_module_initialized = True
|
||||||
|
|
||||||
@ -141,6 +149,38 @@ class Chassis(ChassisBase):
|
|||||||
|
|
||||||
return prev_reboot_cause
|
return prev_reboot_cause
|
||||||
|
|
||||||
|
|
||||||
|
def get_change_event(self, timeout=0):
|
||||||
|
"""
|
||||||
|
Returns a nested dictionary containing all devices which have
|
||||||
|
experienced a change at chassis level
|
||||||
|
Args:
|
||||||
|
timeout: Timeout in milliseconds (optional). If timeout == 0,
|
||||||
|
this method will block until a change is detected.
|
||||||
|
Returns:
|
||||||
|
(bool, dict):
|
||||||
|
- True if call successful, False if not;
|
||||||
|
- A nested dictionary where key is a device type,
|
||||||
|
value is a dictionary with key:value pairs in the format of
|
||||||
|
{'device_id':'device_event'},
|
||||||
|
where device_id is the device ID for this device and
|
||||||
|
device_event,
|
||||||
|
status='1' represents device inserted,
|
||||||
|
status='0' represents device removed.
|
||||||
|
Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0'}}
|
||||||
|
indicates that fan 0 has been removed, fan 2
|
||||||
|
has been inserted and sfp 11 has been removed.
|
||||||
|
"""
|
||||||
|
# SFP event
|
||||||
|
if not self.sfp_module_initialized:
|
||||||
|
self.__initialize_sfp()
|
||||||
|
|
||||||
|
sfp_event = SfpEvent(self._sfp_list).get_sfp_event(timeout)
|
||||||
|
if sfp_event:
|
||||||
|
return True, {'sfp': sfp_event}
|
||||||
|
|
||||||
|
return False, {'sfp': {}}
|
||||||
|
|
||||||
##############################################################
|
##############################################################
|
||||||
######################## SFP methods #########################
|
######################## SFP methods #########################
|
||||||
##############################################################
|
##############################################################
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
try:
|
||||||
|
import select
|
||||||
|
from .helper import APIHelper
|
||||||
|
from sonic_py_common.logger import Logger
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(repr(e) + " - required module not found")
|
||||||
|
|
||||||
|
|
||||||
|
class SfpEvent:
|
||||||
|
''' Listen to insert/remove sfp events '''
|
||||||
|
|
||||||
|
QSFP_MODPRS_IRQ = '/sys/devices/platform/dx010_cpld/qsfp_modprs_irq'
|
||||||
|
GPIO_SUS6 = "/sys/devices/platform/slx-ich.0/sci_int_gpio_sus6"
|
||||||
|
|
||||||
|
def __init__(self, sfp_list):
|
||||||
|
self._api_helper = APIHelper()
|
||||||
|
self._sfp_list = sfp_list
|
||||||
|
self._logger = Logger()
|
||||||
|
|
||||||
|
def get_sfp_event(self, timeout):
|
||||||
|
epoll = select.epoll()
|
||||||
|
port_dict = {}
|
||||||
|
timeout_sec = timeout/1000
|
||||||
|
|
||||||
|
try:
|
||||||
|
# We get notified when there is an SCI interrupt from GPIO SUS6
|
||||||
|
fd = open(self.GPIO_SUS6, "r")
|
||||||
|
fd.read()
|
||||||
|
|
||||||
|
epoll.register(fd.fileno(), select.EPOLLIN & select.EPOLLET)
|
||||||
|
events = epoll.poll(timeout=timeout_sec if timeout != 0 else -1)
|
||||||
|
if events:
|
||||||
|
# Read the QSFP ABS interrupt & status registers
|
||||||
|
port_changes = self._api_helper.read_one_line_file(
|
||||||
|
self.QSFP_MODPRS_IRQ)
|
||||||
|
changes = int(port_changes, 16)
|
||||||
|
for sfp in self._sfp_list:
|
||||||
|
change = (changes >> sfp.port_num-1) & 1
|
||||||
|
if change == 1:
|
||||||
|
port_dict[str(sfp.port_num)] = str(
|
||||||
|
int(sfp.get_presence()))
|
||||||
|
|
||||||
|
return port_dict
|
||||||
|
except Exception as e:
|
||||||
|
self._logger.log_error("Failed to detect SfpEvent - " + repr(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
finally:
|
||||||
|
fd.close()
|
||||||
|
epoll.close()
|
||||||
|
|
||||||
|
return False
|
@ -17,7 +17,6 @@ try:
|
|||||||
from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId
|
from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId
|
||||||
from sonic_platform_base.sonic_sfp.sff8436 import sff8436Dom
|
from sonic_platform_base.sonic_sfp.sff8436 import sff8436Dom
|
||||||
from sonic_platform_base.sonic_sfp.inf8628 import inf8628InterfaceId
|
from sonic_platform_base.sonic_sfp.inf8628 import inf8628InterfaceId
|
||||||
from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper
|
|
||||||
from .helper import APIHelper
|
from .helper import APIHelper
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
raise ImportError(str(e) + "- required module not found")
|
raise ImportError(str(e) + "- required module not found")
|
||||||
@ -166,10 +165,8 @@ class Sfp(SfpBase):
|
|||||||
RESET_PATH = "/sys/devices/platform/dx010_cpld/qsfp_reset"
|
RESET_PATH = "/sys/devices/platform/dx010_cpld/qsfp_reset"
|
||||||
LP_PATH = "/sys/devices/platform/dx010_cpld/qsfp_lpmode"
|
LP_PATH = "/sys/devices/platform/dx010_cpld/qsfp_lpmode"
|
||||||
PRS_PATH = "/sys/devices/platform/dx010_cpld/qsfp_modprs"
|
PRS_PATH = "/sys/devices/platform/dx010_cpld/qsfp_modprs"
|
||||||
PLATFORM_ROOT_PATH = "/usr/share/sonic/device"
|
|
||||||
PMON_HWSKU_PATH = "/usr/share/sonic/hwsku"
|
|
||||||
|
|
||||||
def __init__(self, sfp_index):
|
def __init__(self, sfp_index, sfp_name):
|
||||||
SfpBase.__init__(self)
|
SfpBase.__init__(self)
|
||||||
# Init index
|
# Init index
|
||||||
self.index = sfp_index
|
self.index = sfp_index
|
||||||
@ -177,8 +174,7 @@ class Sfp(SfpBase):
|
|||||||
self.dom_supported = False
|
self.dom_supported = False
|
||||||
self.sfp_type, self.port_name = self.__get_sfp_info()
|
self.sfp_type, self.port_name = self.__get_sfp_info()
|
||||||
self._api_helper = APIHelper()
|
self._api_helper = APIHelper()
|
||||||
self.platform = self._api_helper.platform
|
self.name = sfp_name
|
||||||
self.hwsku = self._api_helper.hwsku
|
|
||||||
|
|
||||||
# Init eeprom path
|
# Init eeprom path
|
||||||
eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom'
|
eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom'
|
||||||
@ -233,12 +229,6 @@ class Sfp(SfpBase):
|
|||||||
else:
|
else:
|
||||||
return 'N/A'
|
return 'N/A'
|
||||||
|
|
||||||
def __get_path_to_port_config_file(self):
|
|
||||||
platform_path = "/".join([self.PLATFORM_ROOT_PATH, self.platform])
|
|
||||||
hwsku_path = "/".join([platform_path, self.hwsku]
|
|
||||||
) if self._api_helper.is_host() else self.PMON_HWSKU_PATH
|
|
||||||
return "/".join([hwsku_path, "port_config.ini"])
|
|
||||||
|
|
||||||
def __read_eeprom_specific_bytes(self, offset, num_bytes):
|
def __read_eeprom_specific_bytes(self, offset, num_bytes):
|
||||||
sysfsfile_eeprom = None
|
sysfsfile_eeprom = None
|
||||||
eeprom_raw = []
|
eeprom_raw = []
|
||||||
@ -1317,11 +1307,7 @@ class Sfp(SfpBase):
|
|||||||
Returns:
|
Returns:
|
||||||
string: The name of the device
|
string: The name of the device
|
||||||
"""
|
"""
|
||||||
sfputil_helper = SfpUtilHelper()
|
return self.name
|
||||||
sfputil_helper.read_porttab_mappings(
|
|
||||||
self.__get_path_to_port_config_file())
|
|
||||||
name = sfputil_helper.logical[self.index] or "Unknown"
|
|
||||||
return name
|
|
||||||
|
|
||||||
def get_presence(self):
|
def get_presence(self):
|
||||||
"""
|
"""
|
||||||
|
@ -46,6 +46,7 @@ start)
|
|||||||
modprobe dx010_wdt
|
modprobe dx010_wdt
|
||||||
modprobe leds-dx010
|
modprobe leds-dx010
|
||||||
modprobe lm75
|
modprobe lm75
|
||||||
|
modprobe slx_gpio_ich
|
||||||
|
|
||||||
found=0
|
found=0
|
||||||
for devnum in 0 1; do
|
for devnum in 0 1; do
|
||||||
|
@ -1 +1 @@
|
|||||||
obj-m := dx010_cpld.o mc24lc64t.o emc2305.o dx010_wdt.o leds-dx010.o
|
obj-m := dx010_cpld.o mc24lc64t.o emc2305.o dx010_wdt.o leds-dx010.o slx_gpio_ich.o
|
@ -65,6 +65,22 @@
|
|||||||
#define INT2229 0x3d6
|
#define INT2229 0x3d6
|
||||||
#define INT3032 0x3d7
|
#define INT3032 0x3d7
|
||||||
|
|
||||||
|
#define ABS_INT0108 0x260
|
||||||
|
#define ABS_INT0910 0x261
|
||||||
|
#define ABS_INT1118 0x2E0
|
||||||
|
#define ABS_INT1921 0x2E1
|
||||||
|
#define ABS_INT2229 0x3E0
|
||||||
|
#define ABS_INT3032 0x3E1
|
||||||
|
|
||||||
|
#define ABS_INT_MSK0108 0x262
|
||||||
|
#define ABS_INT_MSK0910 0x263
|
||||||
|
#define ABS_INT_MSK1118 0x2E2
|
||||||
|
#define ABS_INT_MSK1921 0x2E3
|
||||||
|
#define ABS_INT_MSK2229 0x3E2
|
||||||
|
#define ABS_INT_MSK3032 0x3E3
|
||||||
|
|
||||||
|
#define CPLD4_INT0 0x313
|
||||||
|
#define CPLD4_INT0_MSK 0x315
|
||||||
|
|
||||||
#define LENGTH_PORT_CPLD 34
|
#define LENGTH_PORT_CPLD 34
|
||||||
#define PORT_BANK1_START 1
|
#define PORT_BANK1_START 1
|
||||||
@ -76,52 +92,42 @@
|
|||||||
#define PORT_SFPP1 33
|
#define PORT_SFPP1 33
|
||||||
#define PORT_SFPP2 34
|
#define PORT_SFPP2 34
|
||||||
|
|
||||||
|
#define PORT_ID_BANK1 0x210
|
||||||
|
#define PORT_ID_BANK2 0x290
|
||||||
|
#define PORT_ID_BANK3 0x390
|
||||||
|
|
||||||
#define CPLD_I2C_CLK_100Khz_BIT BIT(6)
|
#define OPCODE_ID_BANK1 0x211
|
||||||
#define CPLD_I2C_DATA_SZ_MASK GENMASK(7,4)
|
#define OPCODE_ID_BANK2 0x291
|
||||||
#define CPLD_I2C_CMD_SZ_MASK GENMASK(1,0)
|
#define OPCODE_ID_BANK3 0x391
|
||||||
#define CPLD_I2C_ERR BIT(7)
|
|
||||||
#define CPLD_I2C_BUSY BIT(6)
|
|
||||||
#define CPLD_I2C_RST_BIT BIT(0)
|
|
||||||
#define CPLD_I2C_RESET 0
|
|
||||||
#define CPLD_I2C_UNRESET 1
|
|
||||||
#define CPLD_I2C_DATA_SZ_MAX 8
|
|
||||||
#define CPLD_I2C_CMD_SZ_MAX 3
|
|
||||||
|
|
||||||
|
#define DEVADDR_ID_BANK1 0x212
|
||||||
|
#define DEVADDR_ID_BANK2 0x292
|
||||||
|
#define DEVADDR_ID_BANK3 0x392
|
||||||
|
|
||||||
#define CPLD_I2C_BANK1_BASE 0x210
|
#define CMDBYT_ID_BANK1 0x213
|
||||||
#define CPLD_I2C_BANK2_BASE 0x290
|
#define CMDBYT_ID_BANK2 0x293
|
||||||
#define CPLD_I2C_BANK3_BASE 0x390
|
#define CMDBYT_ID_BANK3 0x393
|
||||||
|
|
||||||
#define I2C_PORT_ID 0x0
|
#define WRITE_ID_BANK1 0x220
|
||||||
#define I2C_OPCODE 0x1
|
#define WRITE_ID_BANK2 0x2A0
|
||||||
#define I2C_DEV_ADDR 0x2
|
#define WRITE_ID_BANK3 0x3A0
|
||||||
#define I2C_CMD_BYT0 0x3
|
|
||||||
#define I2C_SSR 0x6
|
|
||||||
#define I2C_WRITE_DATA 0x10
|
|
||||||
#define I2C_READ_DATA 0x20
|
|
||||||
|
|
||||||
/*
|
#define READ_ID_BANK1 0x230
|
||||||
* private data to send to I2C core
|
#define READ_ID_BANK2 0x2B0
|
||||||
*/
|
#define READ_ID_BANK3 0x3B0
|
||||||
struct current_xfer {
|
|
||||||
u8 addr;
|
#define SSRR_ID_BANK1 0x216
|
||||||
u8 cmd[CPLD_I2C_CMD_SZ_MAX];
|
#define SSRR_ID_BANK2 0x296
|
||||||
u8 cmd_len;
|
#define SSRR_ID_BANK3 0x396
|
||||||
u8 data_len;
|
|
||||||
union i2c_smbus_data *data;
|
#define HST_CNTL2_QUICK 0x00
|
||||||
};
|
#define HST_CNTL2_BYTE 0x01
|
||||||
|
#define HST_CNTL2_BYTE_DATA 0x02
|
||||||
|
#define HST_CNTL2_WORD_DATA 0x03
|
||||||
|
#define HST_CNTL2_BLOCK 0x05
|
||||||
|
|
||||||
/*
|
|
||||||
* private data of I2C adapter
|
|
||||||
* base_addr: Base address of this I2C adapter core.
|
|
||||||
* port_id: The port ID, use to mux an i2c core to a font panel port.
|
|
||||||
* current_xfer: The struct carry current data setup of current smbus transfer.
|
|
||||||
*/
|
|
||||||
struct dx010_i2c_data {
|
struct dx010_i2c_data {
|
||||||
int base_addr;
|
|
||||||
int portid;
|
int portid;
|
||||||
struct current_xfer curr_xfer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dx010_cpld_data {
|
struct dx010_cpld_data {
|
||||||
@ -332,12 +338,136 @@ static ssize_t get_modirq(struct device *dev, struct device_attribute *devattr,
|
|||||||
return sprintf(buf,"0x%8.8lx\n", irq & 0xffffffff);
|
return sprintf(buf,"0x%8.8lx\n", irq & 0xffffffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t get_modprs_irq(struct device *dev, struct device_attribute *devattr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
unsigned long prs_int = 0;
|
||||||
|
|
||||||
|
mutex_lock(&cpld_data->cpld_lock);
|
||||||
|
|
||||||
|
/* Clear interrupt source */
|
||||||
|
inb(CPLD4_INT0);
|
||||||
|
|
||||||
|
prs_int =
|
||||||
|
(inb(ABS_INT3032) & 0x07) << (24+5) |
|
||||||
|
inb(ABS_INT2229) << (24-3) |
|
||||||
|
(inb(ABS_INT1921) & 0x07) << (16 + 2) |
|
||||||
|
inb(ABS_INT1118) << (16-6) |
|
||||||
|
(inb(ABS_INT0910) & 0x03 ) << 8 |
|
||||||
|
inb(ABS_INT0108);
|
||||||
|
|
||||||
|
mutex_unlock(&cpld_data->cpld_lock);
|
||||||
|
|
||||||
|
return sprintf(buf,"0x%8.8lx\n", prs_int & 0xffffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t get_modprs_msk(struct device *dev, struct device_attribute *devattr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
unsigned long prs_int_msk = 0;
|
||||||
|
|
||||||
|
mutex_lock(&cpld_data->cpld_lock);
|
||||||
|
|
||||||
|
prs_int_msk =
|
||||||
|
(inb(ABS_INT_MSK3032) & 0x07) << (24+5) |
|
||||||
|
inb(ABS_INT_MSK2229) << (24-3) |
|
||||||
|
(inb(ABS_INT_MSK1921) & 0x07) << (16 + 2) |
|
||||||
|
inb(ABS_INT_MSK1118) << (16-6) |
|
||||||
|
(inb(ABS_INT_MSK0910) & 0x03 ) << 8 |
|
||||||
|
inb(ABS_INT_MSK0108);
|
||||||
|
|
||||||
|
mutex_unlock(&cpld_data->cpld_lock);
|
||||||
|
|
||||||
|
return sprintf(buf,"0x%8.8lx\n", prs_int_msk & 0xffffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t set_modprs_msk(struct device *dev, struct device_attribute *devattr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
unsigned long prs_int_msk;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&cpld_data->cpld_lock);
|
||||||
|
|
||||||
|
err = kstrtoul(buf, 16, &prs_int_msk);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
mutex_unlock(&cpld_data->cpld_lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
outb( (prs_int_msk >> 0) & 0xFF, ABS_INT_MSK0108);
|
||||||
|
outb( (prs_int_msk >> 8) & 0x03, ABS_INT_MSK0910);
|
||||||
|
outb( (prs_int_msk >> 10) & 0xFF, ABS_INT_MSK1118);
|
||||||
|
outb( (prs_int_msk >> 18) & 0x07, ABS_INT_MSK1921);
|
||||||
|
outb( (prs_int_msk >> 21) & 0xFF, ABS_INT_MSK2229);
|
||||||
|
outb( (prs_int_msk >> 29) & 0x07, ABS_INT_MSK3032);
|
||||||
|
|
||||||
|
mutex_unlock(&cpld_data->cpld_lock);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t get_cpld4_int0(struct device *dev, struct device_attribute *devattr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
unsigned char int0 = 0;
|
||||||
|
|
||||||
|
mutex_lock(&cpld_data->cpld_lock);
|
||||||
|
|
||||||
|
int0 = inb(CPLD4_INT0);
|
||||||
|
|
||||||
|
mutex_unlock(&cpld_data->cpld_lock);
|
||||||
|
|
||||||
|
return sprintf(buf,"0x%2.2x\n", int0 & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t get_cpld4_int0_msk(struct device *dev, struct device_attribute *devattr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
unsigned char int0_msk = 0;
|
||||||
|
|
||||||
|
mutex_lock(&cpld_data->cpld_lock);
|
||||||
|
|
||||||
|
int0_msk = inb(CPLD4_INT0_MSK);
|
||||||
|
|
||||||
|
mutex_unlock(&cpld_data->cpld_lock);
|
||||||
|
|
||||||
|
return sprintf(buf,"0x%2.2x\n", int0_msk & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t set_cpld4_int0_msk(struct device *dev, struct device_attribute *devattr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
unsigned long int0_msk;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&cpld_data->cpld_lock);
|
||||||
|
|
||||||
|
err = kstrtoul(buf, 16, &int0_msk);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
mutex_unlock(&cpld_data->cpld_lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(int0_msk & 0x3f, CPLD4_INT0_MSK);
|
||||||
|
|
||||||
|
mutex_unlock(&cpld_data->cpld_lock);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR_RW(getreg);
|
static DEVICE_ATTR_RW(getreg);
|
||||||
static DEVICE_ATTR_WO(setreg);
|
static DEVICE_ATTR_WO(setreg);
|
||||||
static DEVICE_ATTR(qsfp_reset, S_IRUGO | S_IWUSR, get_reset, set_reset);
|
static DEVICE_ATTR(qsfp_reset, S_IRUGO | S_IWUSR, get_reset, set_reset);
|
||||||
static DEVICE_ATTR(qsfp_lpmode, S_IRUGO | S_IWUSR, get_lpmode, set_lpmode);
|
static DEVICE_ATTR(qsfp_lpmode, S_IRUGO | S_IWUSR, get_lpmode, set_lpmode);
|
||||||
static DEVICE_ATTR(qsfp_modprs, S_IRUGO, get_modprs, NULL);
|
static DEVICE_ATTR(qsfp_modprs, S_IRUGO, get_modprs, NULL);
|
||||||
static DEVICE_ATTR(qsfp_modirq, S_IRUGO, get_modirq, NULL);
|
static DEVICE_ATTR(qsfp_modirq, S_IRUGO, get_modirq, NULL);
|
||||||
|
static DEVICE_ATTR(qsfp_modprs_irq, S_IRUGO, get_modprs_irq, NULL);
|
||||||
|
static DEVICE_ATTR(qsfp_modprs_msk, S_IRUGO | S_IWUSR, get_modprs_msk, set_modprs_msk);
|
||||||
|
static DEVICE_ATTR(cpld4_int0, S_IRUGO, get_cpld4_int0, NULL);
|
||||||
|
static DEVICE_ATTR(cpld4_int0_msk, S_IRUGO | S_IWUSR, get_cpld4_int0_msk, set_cpld4_int0_msk);
|
||||||
|
|
||||||
static struct attribute *dx010_lpc_attrs[] = {
|
static struct attribute *dx010_lpc_attrs[] = {
|
||||||
&dev_attr_getreg.attr,
|
&dev_attr_getreg.attr,
|
||||||
@ -346,6 +476,10 @@ static struct attribute *dx010_lpc_attrs[] = {
|
|||||||
&dev_attr_qsfp_lpmode.attr,
|
&dev_attr_qsfp_lpmode.attr,
|
||||||
&dev_attr_qsfp_modprs.attr,
|
&dev_attr_qsfp_modprs.attr,
|
||||||
&dev_attr_qsfp_modirq.attr,
|
&dev_attr_qsfp_modirq.attr,
|
||||||
|
&dev_attr_qsfp_modprs_irq.attr,
|
||||||
|
&dev_attr_qsfp_modprs_msk.attr,
|
||||||
|
&dev_attr_cpld4_int0.attr,
|
||||||
|
&dev_attr_cpld4_int0_msk.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -374,210 +508,179 @@ static struct platform_device cel_dx010_lpc_dev = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Refactoring this function with helper functions.
|
|
||||||
static s32 cpld_smbus_transfer(struct dx010_i2c_data *priv) {
|
|
||||||
|
|
||||||
u8 val;
|
/**
|
||||||
s32 error;
|
* Read eeprom of QSFP device.
|
||||||
unsigned long ioBase;
|
* @param a i2c adapter.
|
||||||
short portid, opcode, devaddr, cmdbyte0, ssr, writedata, readdata;
|
* @param addr address to read.
|
||||||
union i2c_smbus_data *data;
|
* @param new_data QSFP port number struct.
|
||||||
|
* @param cmd i2c command.
|
||||||
|
* @return 0 if not error, else the error code.
|
||||||
|
*/
|
||||||
|
static int i2c_read_eeprom(struct i2c_adapter *a, u16 addr,
|
||||||
|
struct dx010_i2c_data *new_data, u8 cmd, union i2c_smbus_data *data){
|
||||||
|
|
||||||
error = -EIO;
|
u32 reg;
|
||||||
|
int ioBase=0;
|
||||||
|
char byte;
|
||||||
|
short temp;
|
||||||
|
short portid, opcode, devaddr, cmdbyte0, ssrr, writedata, readdata;
|
||||||
|
__u16 word_data;
|
||||||
|
int error = -EIO;
|
||||||
|
|
||||||
mutex_lock(&cpld_data->cpld_lock);
|
mutex_lock(&cpld_data->cpld_lock);
|
||||||
|
|
||||||
ioBase = priv->base_addr;
|
if (((new_data->portid >= PORT_BANK1_START)
|
||||||
data = priv->curr_xfer.data;
|
&& (new_data->portid <= PORT_BANK1_END))
|
||||||
|
|| (new_data->portid == PORT_SFPP1)
|
||||||
|
|| (new_data->portid == PORT_SFPP2))
|
||||||
|
{
|
||||||
|
portid = PORT_ID_BANK1;
|
||||||
|
opcode = OPCODE_ID_BANK1;
|
||||||
|
devaddr = DEVADDR_ID_BANK1;
|
||||||
|
cmdbyte0 = CMDBYT_ID_BANK1;
|
||||||
|
ssrr = SSRR_ID_BANK1;
|
||||||
|
writedata = WRITE_ID_BANK1;
|
||||||
|
readdata = READ_ID_BANK1;
|
||||||
|
}else if ((new_data->portid >= PORT_BANK2_START) && (new_data->portid <= PORT_BANK2_END)){
|
||||||
|
portid = PORT_ID_BANK2;
|
||||||
|
opcode = OPCODE_ID_BANK2;
|
||||||
|
devaddr = DEVADDR_ID_BANK2;
|
||||||
|
cmdbyte0 = CMDBYT_ID_BANK2;
|
||||||
|
ssrr = SSRR_ID_BANK2;
|
||||||
|
writedata = WRITE_ID_BANK2;
|
||||||
|
readdata = READ_ID_BANK2;
|
||||||
|
}else if ((new_data->portid >= PORT_BANK3_START) && (new_data->portid <= PORT_BANK3_END)){
|
||||||
|
portid = PORT_ID_BANK3;
|
||||||
|
opcode = OPCODE_ID_BANK3;
|
||||||
|
devaddr = DEVADDR_ID_BANK3;
|
||||||
|
cmdbyte0 = CMDBYT_ID_BANK3;
|
||||||
|
ssrr = SSRR_ID_BANK3;
|
||||||
|
writedata = WRITE_ID_BANK3;
|
||||||
|
readdata = READ_ID_BANK3;
|
||||||
|
}else{
|
||||||
|
/* Invalid parameter! */
|
||||||
|
error = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
portid = ioBase + I2C_PORT_ID;
|
while ((inb(ioBase + ssrr) & 0x40));
|
||||||
opcode = ioBase + I2C_OPCODE;
|
if ((inb(ioBase + ssrr) & 0x80) == 0x80) {
|
||||||
devaddr = ioBase + I2C_DEV_ADDR;
|
error = -EIO;
|
||||||
cmdbyte0 = ioBase + I2C_CMD_BYT0;
|
/* Read error reset the port */
|
||||||
ssr = ioBase + I2C_SSR;
|
outb(0x00, ioBase + ssrr);
|
||||||
writedata = ioBase + I2C_WRITE_DATA;
|
|
||||||
readdata = ioBase + I2C_READ_DATA;
|
|
||||||
|
|
||||||
/* Wait for the core to be free */
|
|
||||||
pr_debug("CPLD_I2C Wait busy bit(6) to be cleared\n");
|
|
||||||
do {
|
|
||||||
val = inb(ssr);
|
|
||||||
if ((val & CPLD_I2C_BUSY) == 0)
|
|
||||||
break;
|
|
||||||
udelay(100);
|
|
||||||
} while (true); // Risky - add timeout
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If any error happen here, we do soft-reset
|
|
||||||
* and check the BUSY/ERROR again.
|
|
||||||
*/
|
|
||||||
pr_debug("CPLD_I2C Check error bit(7)\n");
|
|
||||||
if (val & CPLD_I2C_ERR) {
|
|
||||||
pr_debug("CPLD_I2C Error, try soft-reset\n");
|
|
||||||
outb(CPLD_I2C_RESET, ssr);
|
|
||||||
udelay(3000);
|
udelay(3000);
|
||||||
outb(CPLD_I2C_UNRESET, ssr);
|
outb(0x01, ioBase + ssrr);
|
||||||
|
goto exit;
|
||||||
val = inb(ssr);
|
|
||||||
if (val & (CPLD_I2C_BUSY | CPLD_I2C_ERR)) {
|
|
||||||
pr_debug("CPLD_I2C Error, core busy after reset\n");
|
|
||||||
error = -EIO;
|
|
||||||
goto exit_unlock;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Configure PortID */
|
byte = 0x40 +new_data->portid;
|
||||||
val = priv->portid | CPLD_I2C_CLK_100Khz_BIT;
|
reg = cmd;
|
||||||
outb(val, portid);
|
outb(byte, ioBase + portid);
|
||||||
pr_debug("CPLD_I2C Write PortID 0x%x\n", val);
|
outb(reg,ioBase + cmdbyte0);
|
||||||
|
byte = 33;
|
||||||
/* Configure OP_Code */
|
outb(byte, ioBase + opcode);
|
||||||
val = (priv->curr_xfer.data_len << 4) & CPLD_I2C_DATA_SZ_MASK;
|
addr = addr << 1;
|
||||||
val |= (priv->curr_xfer.cmd_len & CPLD_I2C_CMD_SZ_MASK);
|
addr |= 0x01;
|
||||||
outb(val, opcode);
|
outb(addr, ioBase + devaddr);
|
||||||
pr_debug("CPLD_I2C Write OP_Code 0x%x\n", val);
|
while ((inb(ioBase + ssrr) & 0x40))
|
||||||
|
{
|
||||||
/* Configure CMD_Byte */
|
|
||||||
outb(priv->curr_xfer.cmd[0], cmdbyte0);
|
|
||||||
pr_debug("CPLD_I2C Write CMD_Byte 0x%x\n", priv->curr_xfer.cmd[0]);
|
|
||||||
|
|
||||||
/* Configure write data buffer */
|
|
||||||
if ((priv->curr_xfer.addr & BIT(0)) == I2C_SMBUS_WRITE){
|
|
||||||
pr_debug("CPLD_I2C Write WR_DATA buffer\n");
|
|
||||||
switch(priv->curr_xfer.data_len){
|
|
||||||
case 1:
|
|
||||||
outb(data->byte, writedata);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
outb(data->block[0], writedata);
|
|
||||||
outb(data->block[1], ++writedata);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start transfer, write the device address register */
|
|
||||||
pr_debug("CPLD_I2C Write DEV_ADDR 0x%x\n", priv->curr_xfer.addr);
|
|
||||||
outb(priv->curr_xfer.addr, devaddr);
|
|
||||||
|
|
||||||
/* Wait for transfer finish */
|
|
||||||
pr_debug("CPLD_I2C Wait busy bit(6) to be cleared\n");
|
|
||||||
do {
|
|
||||||
val = inb(ssr);
|
|
||||||
if ((val & CPLD_I2C_BUSY) == 0)
|
|
||||||
break;
|
|
||||||
udelay(100);
|
udelay(100);
|
||||||
} while (true); // Risky - add timeout
|
}
|
||||||
|
|
||||||
pr_debug("CPLD_I2C Check error bit(7)\n");
|
if ((inb(ioBase + ssrr) & 0x80) == 0x80) {
|
||||||
if (val & CPLD_I2C_ERR) {
|
/* Read error reset the port */
|
||||||
error = -EIO;
|
error = -EIO;
|
||||||
goto exit_unlock;
|
outb(0x00, ioBase + ssrr);
|
||||||
|
udelay(3000);
|
||||||
|
outb(0x01, ioBase + ssrr);
|
||||||
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the data from buffer */
|
temp = ioBase + readdata;
|
||||||
if ((priv->curr_xfer.addr & BIT(0)) == I2C_SMBUS_READ){
|
word_data = inb(temp);
|
||||||
pr_debug("CPLD_I2C Read RD_DATA buffer\n");
|
word_data |= (inb(++temp) << 8);
|
||||||
switch (priv->curr_xfer.data_len) {
|
|
||||||
case 1:
|
|
||||||
data->byte = inb(readdata);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
data->block[0] = inb(readdata);
|
|
||||||
data->block[1] = inb(++readdata);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
error = 0;
|
mutex_unlock(&cpld_data->cpld_lock);
|
||||||
|
data->word = word_data;
|
||||||
|
return 0;
|
||||||
|
|
||||||
exit_unlock:
|
exit:
|
||||||
pr_debug("CPLD_I2C Exit with %d\n", error);
|
|
||||||
mutex_unlock(&cpld_data->cpld_lock);
|
mutex_unlock(&cpld_data->cpld_lock);
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static int dx010_i2c_access(struct i2c_adapter *a, u16 addr,
|
||||||
* dx010_smbus_xfer - execute LPC-SMBus transfer
|
unsigned short flags, char rw, u8 cmd,
|
||||||
* Returns a negative errno code else zero on success.
|
int size, union i2c_smbus_data *data)
|
||||||
*/
|
{
|
||||||
static s32 dx010_smbus_xfer(struct i2c_adapter *adap, u16 addr,
|
|
||||||
unsigned short flags, char read_write,
|
|
||||||
u8 command, int size, union i2c_smbus_data *data) {
|
|
||||||
|
|
||||||
int error = 0;
|
int error = 0;
|
||||||
struct dx010_i2c_data *priv;
|
|
||||||
|
|
||||||
priv = i2c_get_adapdata(adap);
|
struct dx010_i2c_data *new_data;
|
||||||
|
|
||||||
pr_debug("smbus_xfer called RW:%x CMD:%x SIZE:0x%x",
|
/* Write the command register */
|
||||||
read_write, command, size);
|
new_data = i2c_get_adapdata(a);
|
||||||
|
|
||||||
priv->curr_xfer.addr = (addr << 1) | read_write;
|
|
||||||
priv->curr_xfer.data = data;
|
|
||||||
|
|
||||||
/* Map the size to what the chip understands */
|
/* Map the size to what the chip understands */
|
||||||
switch (size) {
|
switch (size) {
|
||||||
|
case I2C_SMBUS_QUICK:
|
||||||
|
size = HST_CNTL2_QUICK;
|
||||||
|
break;
|
||||||
case I2C_SMBUS_BYTE:
|
case I2C_SMBUS_BYTE:
|
||||||
priv->curr_xfer.cmd_len = 0;
|
size = HST_CNTL2_BYTE;
|
||||||
priv->curr_xfer.data_len = 1;
|
|
||||||
break;
|
break;
|
||||||
case I2C_SMBUS_BYTE_DATA:
|
case I2C_SMBUS_BYTE_DATA:
|
||||||
priv->curr_xfer.cmd_len = 1;
|
size = HST_CNTL2_BYTE_DATA;
|
||||||
priv->curr_xfer.data_len = 1;
|
|
||||||
priv->curr_xfer.cmd[0] = command;
|
|
||||||
break;
|
break;
|
||||||
case I2C_SMBUS_WORD_DATA:
|
case I2C_SMBUS_WORD_DATA:
|
||||||
priv->curr_xfer.cmd_len = 1;
|
size = HST_CNTL2_WORD_DATA;
|
||||||
priv->curr_xfer.data_len = 2;
|
break;
|
||||||
priv->curr_xfer.cmd[0] = command;
|
case I2C_SMBUS_BLOCK_DATA:
|
||||||
|
size = HST_CNTL2_BLOCK;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
|
dev_warn(&a->dev, "Unsupported transaction %d\n", size);
|
||||||
error = -EOPNOTSUPP;
|
error = -EOPNOTSUPP;
|
||||||
goto Done;
|
goto Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = cpld_smbus_transfer(priv);
|
switch (size) {
|
||||||
|
case HST_CNTL2_BYTE: /* Result put in SMBHSTDAT0 */
|
||||||
|
break;
|
||||||
|
case HST_CNTL2_BYTE_DATA:
|
||||||
|
break;
|
||||||
|
case HST_CNTL2_WORD_DATA:
|
||||||
|
if( 0 == i2c_read_eeprom(a,addr,new_data,cmd,data)){
|
||||||
|
error = 0;
|
||||||
|
}else{
|
||||||
|
error = -EIO;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
Done:
|
Done:
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add support for I2C_FUNC_SMBUS_PROC_CALL and I2C_FUNC_SMBUS_I2C_BLOCK
|
static u32 dx010_i2c_func(struct i2c_adapter *a)
|
||||||
static u32 dx010_i2c_func(struct i2c_adapter *a) {
|
{
|
||||||
return I2C_FUNC_SMBUS_READ_BYTE |
|
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
||||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
|
||||||
I2C_FUNC_SMBUS_WORD_DATA;
|
I2C_FUNC_SMBUS_BLOCK_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_algorithm dx010_i2c_algorithm = {
|
static const struct i2c_algorithm dx010_i2c_algorithm = {
|
||||||
.smbus_xfer = dx010_smbus_xfer,
|
.smbus_xfer = dx010_i2c_access,
|
||||||
.functionality = dx010_i2c_func,
|
.functionality = dx010_i2c_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct i2c_adapter * cel_dx010_i2c_init(struct platform_device *pdev, int portid)
|
static struct i2c_adapter * cel_dx010_i2c_init(struct platform_device *pdev, int portid)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
int base_addr;
|
|
||||||
struct i2c_adapter *new_adapter;
|
|
||||||
struct dx010_i2c_data *priv;
|
|
||||||
|
|
||||||
switch (portid) {
|
struct i2c_adapter *new_adapter;
|
||||||
case PORT_SFPP1 ... PORT_SFPP2:
|
struct dx010_i2c_data *new_data;
|
||||||
case PORT_BANK1_START ... PORT_BANK1_END:
|
|
||||||
base_addr = CPLD_I2C_BANK1_BASE;
|
|
||||||
break;
|
|
||||||
case PORT_BANK2_START ... PORT_BANK2_END:
|
|
||||||
base_addr = CPLD_I2C_BANK2_BASE;
|
|
||||||
break;
|
|
||||||
case PORT_BANK3_START ... PORT_BANK3_END:
|
|
||||||
base_addr = CPLD_I2C_BANK3_BASE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dev_err(&pdev->dev, "Invalid port adapter ID: %d\n", portid);
|
|
||||||
goto error_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_adapter = kzalloc(sizeof(*new_adapter), GFP_KERNEL);
|
new_adapter = kzalloc(sizeof(*new_adapter), GFP_KERNEL);
|
||||||
if (!new_adapter)
|
if (!new_adapter)
|
||||||
@ -585,33 +688,25 @@ static struct i2c_adapter *cel_dx010_i2c_init(struct platform_device *pdev, int
|
|||||||
|
|
||||||
new_adapter->dev.parent = &pdev->dev;
|
new_adapter->dev.parent = &pdev->dev;
|
||||||
new_adapter->owner = THIS_MODULE;
|
new_adapter->owner = THIS_MODULE;
|
||||||
new_adapter->class = I2C_CLASS_DEPRECATED;
|
new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||||
new_adapter->algo = &dx010_i2c_algorithm;
|
new_adapter->algo = &dx010_i2c_algorithm;
|
||||||
|
|
||||||
snprintf(new_adapter->name, sizeof(new_adapter->name),
|
snprintf(new_adapter->name, sizeof(new_adapter->name),
|
||||||
"SMBus dx010 i2c Adapter port %d", portid);
|
"SMBus dx010 i2c Adapter portid@%04x", portid);
|
||||||
|
|
||||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
new_data = kzalloc(sizeof(*new_data), GFP_KERNEL);
|
||||||
if (!priv) {
|
if (!new_data)
|
||||||
goto free_adap;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
priv->portid = portid;
|
new_data->portid = portid;
|
||||||
priv->base_addr = base_addr;
|
|
||||||
|
i2c_set_adapdata(new_adapter,new_data);
|
||||||
|
|
||||||
i2c_set_adapdata(new_adapter, priv);
|
|
||||||
error = i2c_add_adapter(new_adapter);
|
error = i2c_add_adapter(new_adapter);
|
||||||
if(error)
|
if(error)
|
||||||
goto free_data;
|
return NULL;
|
||||||
|
|
||||||
return new_adapter;
|
return new_adapter;
|
||||||
|
|
||||||
free_adap:
|
|
||||||
kzfree(new_adapter);
|
|
||||||
free_data:
|
|
||||||
kzfree(priv);
|
|
||||||
error_exit:
|
|
||||||
return NULL;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int cel_dx010_lpc_drv_probe(struct platform_device *pdev)
|
static int cel_dx010_lpc_drv_probe(struct platform_device *pdev)
|
||||||
@ -643,6 +738,17 @@ static int cel_dx010_lpc_drv_probe(struct platform_device *pdev)
|
|||||||
cpld_data->i2c_adapter[portid_count-1] =
|
cpld_data->i2c_adapter[portid_count-1] =
|
||||||
cel_dx010_i2c_init(pdev, portid_count);
|
cel_dx010_i2c_init(pdev, portid_count);
|
||||||
|
|
||||||
|
/* Enable INT0 interrupt register */
|
||||||
|
outb(inb(CPLD4_INT0_MSK) & 0xf8, CPLD4_INT0_MSK);
|
||||||
|
|
||||||
|
/* Enable modprs interrupt register */
|
||||||
|
outb(0, ABS_INT_MSK0108);
|
||||||
|
outb(0, ABS_INT_MSK0910);
|
||||||
|
outb(0, ABS_INT_MSK1118);
|
||||||
|
outb(0, ABS_INT_MSK1921);
|
||||||
|
outb(0, ABS_INT_MSK2229);
|
||||||
|
outb(0, ABS_INT_MSK3032);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -683,7 +789,7 @@ void cel_dx010_lpc_exit(void)
|
|||||||
module_init(cel_dx010_lpc_init);
|
module_init(cel_dx010_lpc_init);
|
||||||
module_exit(cel_dx010_lpc_exit);
|
module_exit(cel_dx010_lpc_exit);
|
||||||
|
|
||||||
MODULE_AUTHOR("Abhisit Sangjan <asang@celestica.com>");
|
MODULE_AUTHOR("Pradchaya P <pphuchar@celestica.com>");
|
||||||
MODULE_AUTHOR("Pariwat Leamsumran <pleamsum@celestica.com>");
|
MODULE_VERSION("1.0.1");
|
||||||
MODULE_DESCRIPTION("Celestica SeaStone DX010 LPC Driver");
|
MODULE_DESCRIPTION("Celestica SeaStone DX010 LPC Driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user