From 4257c792a26c016a51f173469f4cb5e5a781cc35 Mon Sep 17 00:00:00 2001 From: Wirut Getbamrung Date: Tue, 15 Dec 2020 01:22:56 +0700 Subject: [PATCH] [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. --- .../sonic_platform/chassis.py | 42 +- .../sonic_platform/event.py | 52 + .../sonic_platform/sfp.py | 20 +- .../debian/platform-modules-dx010.init | 1 + .../dx010/modules/Makefile | 2 +- .../dx010/modules/dx010_cpld.c | 530 +++++---- .../dx010/modules/slx_gpio_ich.c | 1003 +++++++++++++++++ 7 files changed, 1419 insertions(+), 231 deletions(-) create mode 100644 device/celestica/x86_64-cel_seastone-r0/sonic_platform/event.py create mode 100644 platform/broadcom/sonic-platform-modules-cel/dx010/modules/slx_gpio_ich.c diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py index 5466d629fb2f..49c79263815d 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py @@ -9,6 +9,9 @@ try: import sys 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 except ImportError as e: raise ImportError(str(e) + "- required module not found") @@ -45,9 +48,14 @@ def __init__(self): self.__initialize_components() 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 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_module_initialized = True @@ -141,6 +149,38 @@ def get_reboot_cause(self): 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 ######################### ############################################################## diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/event.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/event.py new file mode 100644 index 000000000000..351b2a5ac087 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/event.py @@ -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 diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/sfp.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/sfp.py index 4c0a645714af..9daa005cd2bf 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/sfp.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/sfp.py @@ -17,7 +17,6 @@ from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId from sonic_platform_base.sonic_sfp.sff8436 import sff8436Dom from sonic_platform_base.sonic_sfp.inf8628 import inf8628InterfaceId - from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper from .helper import APIHelper except ImportError as e: raise ImportError(str(e) + "- required module not found") @@ -166,10 +165,8 @@ class Sfp(SfpBase): RESET_PATH = "/sys/devices/platform/dx010_cpld/qsfp_reset" LP_PATH = "/sys/devices/platform/dx010_cpld/qsfp_lpmode" 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) # Init index self.index = sfp_index @@ -177,8 +174,7 @@ def __init__(self, sfp_index): self.dom_supported = False self.sfp_type, self.port_name = self.__get_sfp_info() self._api_helper = APIHelper() - self.platform = self._api_helper.platform - self.hwsku = self._api_helper.hwsku + self.name = sfp_name # Init eeprom path eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom' @@ -233,12 +229,6 @@ def __convert_string_to_num(self, value_str): else: 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): sysfsfile_eeprom = None eeprom_raw = [] @@ -1317,11 +1307,7 @@ def get_name(self): Returns: string: The name of the device """ - sfputil_helper = SfpUtilHelper() - sfputil_helper.read_porttab_mappings( - self.__get_path_to_port_config_file()) - name = sfputil_helper.logical[self.index] or "Unknown" - return name + return self.name def get_presence(self): """ diff --git a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.init b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.init index 23d268f9d14a..b77df92a1133 100644 --- a/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.init +++ b/platform/broadcom/sonic-platform-modules-cel/debian/platform-modules-dx010.init @@ -46,6 +46,7 @@ start) modprobe dx010_wdt modprobe leds-dx010 modprobe lm75 + modprobe slx_gpio_ich found=0 for devnum in 0 1; do diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/Makefile b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/Makefile index 9b0f10604811..29cf0f71d62b 100644 --- a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/Makefile +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/Makefile @@ -1 +1 @@ -obj-m := dx010_cpld.o mc24lc64t.o emc2305.o dx010_wdt.o leds-dx010.o \ No newline at end of file +obj-m := dx010_cpld.o mc24lc64t.o emc2305.o dx010_wdt.o leds-dx010.o slx_gpio_ich.o \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_cpld.c b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_cpld.c index ead28807832d..d8142777c6e1 100644 --- a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_cpld.c +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/dx010_cpld.c @@ -65,6 +65,22 @@ #define INT2229 0x3d6 #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 PORT_BANK1_START 1 @@ -76,52 +92,42 @@ #define PORT_SFPP1 33 #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 CPLD_I2C_DATA_SZ_MASK GENMASK(7,4) -#define CPLD_I2C_CMD_SZ_MASK GENMASK(1,0) -#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 OPCODE_ID_BANK1 0x211 +#define OPCODE_ID_BANK2 0x291 +#define OPCODE_ID_BANK3 0x391 +#define DEVADDR_ID_BANK1 0x212 +#define DEVADDR_ID_BANK2 0x292 +#define DEVADDR_ID_BANK3 0x392 -#define CPLD_I2C_BANK1_BASE 0x210 -#define CPLD_I2C_BANK2_BASE 0x290 -#define CPLD_I2C_BANK3_BASE 0x390 +#define CMDBYT_ID_BANK1 0x213 +#define CMDBYT_ID_BANK2 0x293 +#define CMDBYT_ID_BANK3 0x393 -#define I2C_PORT_ID 0x0 -#define I2C_OPCODE 0x1 -#define I2C_DEV_ADDR 0x2 -#define I2C_CMD_BYT0 0x3 -#define I2C_SSR 0x6 -#define I2C_WRITE_DATA 0x10 -#define I2C_READ_DATA 0x20 +#define WRITE_ID_BANK1 0x220 +#define WRITE_ID_BANK2 0x2A0 +#define WRITE_ID_BANK3 0x3A0 -/* - * private data to send to I2C core - */ -struct current_xfer { - u8 addr; - u8 cmd[CPLD_I2C_CMD_SZ_MAX]; - u8 cmd_len; - u8 data_len; - union i2c_smbus_data *data; -}; +#define READ_ID_BANK1 0x230 +#define READ_ID_BANK2 0x2B0 +#define READ_ID_BANK3 0x3B0 + +#define SSRR_ID_BANK1 0x216 +#define SSRR_ID_BANK2 0x296 +#define SSRR_ID_BANK3 0x396 + +#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 { - int base_addr; int portid; - struct current_xfer curr_xfer; }; 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); } +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_WO(setreg); 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_modprs, S_IRUGO, get_modprs, 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[] = { &dev_attr_getreg.attr, @@ -346,6 +476,10 @@ static struct attribute *dx010_lpc_attrs[] = { &dev_attr_qsfp_lpmode.attr, &dev_attr_qsfp_modprs.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, }; @@ -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; - unsigned long ioBase; - short portid, opcode, devaddr, cmdbyte0, ssr, writedata, readdata; - union i2c_smbus_data *data; +/** + * Read eeprom of QSFP device. + * @param a i2c adapter. + * @param addr address to read. + * @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); - ioBase = priv->base_addr; - data = priv->curr_xfer.data; - - portid = ioBase + I2C_PORT_ID; - opcode = ioBase + I2C_OPCODE; - devaddr = ioBase + I2C_DEV_ADDR; - cmdbyte0 = ioBase + I2C_CMD_BYT0; - ssr = ioBase + I2C_SSR; - 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); - outb(CPLD_I2C_UNRESET, ssr); - - 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; - } + if (((new_data->portid >= PORT_BANK1_START) + && (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; } - /* Configure PortID */ - val = priv->portid | CPLD_I2C_CLK_100Khz_BIT; - outb(val, portid); - pr_debug("CPLD_I2C Write PortID 0x%x\n", val); - - /* Configure OP_Code */ - val = (priv->curr_xfer.data_len << 4) & CPLD_I2C_DATA_SZ_MASK; - val |= (priv->curr_xfer.cmd_len & CPLD_I2C_CMD_SZ_MASK); - outb(val, opcode); - pr_debug("CPLD_I2C Write OP_Code 0x%x\n", val); - - /* 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; - } + while ((inb(ioBase + ssrr) & 0x40)); + if ((inb(ioBase + ssrr) & 0x80) == 0x80) { + error = -EIO; + /* Read error reset the port */ + outb(0x00, ioBase + ssrr); + udelay(3000); + outb(0x01, ioBase + ssrr); + goto exit; } - /* 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; + byte = 0x40 +new_data->portid; + reg = cmd; + outb(byte, ioBase + portid); + outb(reg,ioBase + cmdbyte0); + byte = 33; + outb(byte, ioBase + opcode); + addr = addr << 1; + addr |= 0x01; + outb(addr, ioBase + devaddr); + while ((inb(ioBase + ssrr) & 0x40)) + { udelay(100); - } while (true); // Risky - add timeout + } - pr_debug("CPLD_I2C Check error bit(7)\n"); - if (val & CPLD_I2C_ERR) { + if ((inb(ioBase + ssrr) & 0x80) == 0x80) { + /* Read error reset the port */ error = -EIO; - goto exit_unlock; + outb(0x00, ioBase + ssrr); + udelay(3000); + outb(0x01, ioBase + ssrr); + goto exit; } - /* Get the data from buffer */ - if ((priv->curr_xfer.addr & BIT(0)) == I2C_SMBUS_READ){ - pr_debug("CPLD_I2C Read RD_DATA buffer\n"); - 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; - } - } + temp = ioBase + readdata; + word_data = inb(temp); + word_data |= (inb(++temp) << 8); - error = 0; + mutex_unlock(&cpld_data->cpld_lock); + data->word = word_data; + return 0; -exit_unlock: - pr_debug("CPLD_I2C Exit with %d\n", error); +exit: mutex_unlock(&cpld_data->cpld_lock); return error; - } -/* - * dx010_smbus_xfer - execute LPC-SMBus transfer - * Returns a negative errno code else zero on success. - */ -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) { +static int dx010_i2c_access(struct i2c_adapter *a, u16 addr, + unsigned short flags, char rw, u8 cmd, + int size, union i2c_smbus_data *data) +{ int error = 0; - struct dx010_i2c_data *priv; - - priv = i2c_get_adapdata(adap); - pr_debug("smbus_xfer called RW:%x CMD:%x SIZE:0x%x", - read_write, command, size); + struct dx010_i2c_data *new_data; - priv->curr_xfer.addr = (addr << 1) | read_write; - priv->curr_xfer.data = data; + /* Write the command register */ + new_data = i2c_get_adapdata(a); /* Map the size to what the chip understands */ switch (size) { + case I2C_SMBUS_QUICK: + size = HST_CNTL2_QUICK; + break; case I2C_SMBUS_BYTE: - priv->curr_xfer.cmd_len = 0; - priv->curr_xfer.data_len = 1; - break; + size = HST_CNTL2_BYTE; + break; case I2C_SMBUS_BYTE_DATA: - priv->curr_xfer.cmd_len = 1; - priv->curr_xfer.data_len = 1; - priv->curr_xfer.cmd[0] = command; - break; + size = HST_CNTL2_BYTE_DATA; + break; case I2C_SMBUS_WORD_DATA: - priv->curr_xfer.cmd_len = 1; - priv->curr_xfer.data_len = 2; - priv->curr_xfer.cmd[0] = command; - break; + size = HST_CNTL2_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + size = HST_CNTL2_BLOCK; + break; default: - dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + dev_warn(&a->dev, "Unsupported transaction %d\n", size); error = -EOPNOTSUPP; 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: 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) { - return I2C_FUNC_SMBUS_READ_BYTE | - I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WORD_DATA; +static u32 dx010_i2c_func(struct i2c_adapter *a) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; } static const struct i2c_algorithm dx010_i2c_algorithm = { - .smbus_xfer = dx010_smbus_xfer, + .smbus_xfer = dx010_i2c_access, .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 base_addr; + struct i2c_adapter *new_adapter; - struct dx010_i2c_data *priv; - - switch (portid) { - case PORT_SFPP1 ... PORT_SFPP2: - 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; - } + struct dx010_i2c_data *new_data; new_adapter = kzalloc(sizeof(*new_adapter), GFP_KERNEL); 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->owner = THIS_MODULE; - new_adapter->class = I2C_CLASS_DEPRECATED; + new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; new_adapter->algo = &dx010_i2c_algorithm; 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); - if (!priv) { - goto free_adap; - } + new_data = kzalloc(sizeof(*new_data), GFP_KERNEL); + if (!new_data) + return NULL; + + new_data->portid = portid; - priv->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); if(error) - goto free_data; + return NULL; 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) @@ -643,6 +738,17 @@ static int cel_dx010_lpc_drv_probe(struct platform_device *pdev) cpld_data->i2c_adapter[portid_count-1] = 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; } @@ -683,7 +789,7 @@ void cel_dx010_lpc_exit(void) module_init(cel_dx010_lpc_init); module_exit(cel_dx010_lpc_exit); -MODULE_AUTHOR("Abhisit Sangjan "); -MODULE_AUTHOR("Pariwat Leamsumran "); +MODULE_AUTHOR("Pradchaya P "); +MODULE_VERSION("1.0.1"); MODULE_DESCRIPTION("Celestica SeaStone DX010 LPC Driver"); MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-cel/dx010/modules/slx_gpio_ich.c b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/slx_gpio_ich.c new file mode 100644 index 000000000000..0d6ca743b1fc --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-cel/dx010/modules/slx_gpio_ich.c @@ -0,0 +1,1003 @@ +/* Copyright (c) 2018 Dell Inc. + * slx_gpio_ich.c - ICH driver for Rangeley switches + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "slx-ich" + +#define C2000_PCU_DEVICE_ID 0x1f38 + +// GPIO registers +// GPIO Core Control/Access Registers in I/O Space +#define GPIO_SC_USE_SEL 0x0 +#define GPIO_SC_IO_SEL 0x4 +#define GPIO_SC_GP_LVL 0x8 +#define GPIO_SC_TPE 0xC +#define GPIO_SC_TNE 0x10 +#define GPIO_SC_TS 0x14 + +// GPIO SUS Control/Access Registers in I/O Space +#define GPIO_SUS_USE_SEL 0x80 +#define GPIO_SUS_IO_SEL 0x84 +#define GPIO_SUS_GP_LVL 0x88 +#define GPIO_SUS_TPE 0x8C +#define GPIO_SUS_TNE 0x90 +#define GPIO_SUS_TS 0x94 +#define GPIO_SUS_WAKE_EN 0x98 + +static const unsigned char c2000_gpio_regs[] = { + GPIO_SC_USE_SEL, + GPIO_SC_IO_SEL, + GPIO_SC_GP_LVL, + GPIO_SC_TPE, + GPIO_SC_TNE, + GPIO_SC_TS, + GPIO_SUS_USE_SEL, + GPIO_SUS_IO_SEL, + GPIO_SUS_GP_LVL, + GPIO_SUS_TPE, + GPIO_SUS_TNE, + GPIO_SUS_TS, + GPIO_SUS_WAKE_EN +}; + +#define GPIO_REG_LEN 0x4 + +#define GPIOS0_EN (1 << 0) +#define GPIOS1_EN (1 << 1) +#define GPIOS2_EN (1 << 2) +#define GPIOS3_EN (1 << 3) +#define GPIOS4_EN (1 << 4) +#define GPIOS5_EN (1 << 5) +#define GPIOS6_EN (1 << 6) +#define GPIOS7_EN (1 << 7) + +#define GPIOSUS0_EN (1 << 0) +#define GPIOSUS1_EN (1 << 1) +#define GPIOSUS2_EN (1 << 2) +#define GPIOSUS3_EN (1 << 3) +// GPIOSUS4_EN : unused +// GPIOSUS5_EN : unused +#define GPIOSUS6_EN (1 << 6) +#define GPIOSUS7_EN (1 << 7) + + +// GPE0a_EN - General Purpose Event 0 Enables +#define GPIO_GPE0a_EN_SUS0 (1 << 16) +#define GPIO_GPE0a_EN_SUS1 (1 << 17) +#define GPIO_GPE0a_EN_SUS2 (1 << 18) +#define GPIO_GPE0a_EN_SUS3 (1 << 19) +// GPIO_GPE0a_EN_SUS4 : unused +// GPIO_GPE0a_EN_SUS5 : unused +#define GPIO_GPE0a_EN_SUS6 (1 << 22) +#define GPIO_GPE0a_EN_SUS7 (1 << 23) + +#define GPIO_GPE0a_EN_CORE0 (1 << 24) +#define GPIO_GPE0a_EN_CORE1 (1 << 25) +#define GPIO_GPE0a_EN_CORE2 (1 << 26) +#define GPIO_GPE0a_EN_CORE3 (1 << 27) +#define GPIO_GPE0a_EN_CORE4 (1 << 28) +#define GPIO_GPE0a_EN_CORE5 (1 << 29) +#define GPIO_GPE0a_EN_CORE6 (1 << 30) +#define GPIO_GPE0a_EN_CORE7 (1 << 31) + +// GPE0a_STS - General Purpose Event 0 Status +// We're interested in only SUS6 for now +#define GPIO_GPE0a_STS_SUS6 (1 << 22) +#define GPIO_GPE0a_STS_SUS7 (1 << 23) + +// GPIO_ROUT - GPIO_ROUT Register +#define GPIO_ROUT_OFFSET_SUS0 0 +#define GPIO_ROUT_OFFSET_SUS1 2 +#define GPIO_ROUT_OFFSET_SUS2 4 +#define GPIO_ROUT_OFFSET_SUS3 6 +// GPIO_ROUT_OFFSET_SUS4 : unused +// GPIO_ROUT_OFFSET_SUS5 : unused +#define GPIO_ROUT_OFFSET_SUS6 12 +#define GPIO_ROUT_OFFSET_SUS7 14 + +#define GPIO_ROUT_OFFSET_CORE0 16 +#define GPIO_ROUT_OFFSET_CORE1 18 +#define GPIO_ROUT_OFFSET_CORE2 20 +#define GPIO_ROUT_OFFSET_CORE3 22 +#define GPIO_ROUT_OFFSET_CORE4 24 +#define GPIO_ROUT_OFFSET_CORE5 26 +#define GPIO_ROUT_OFFSET_CORE6 28 +#define GPIO_ROUT_OFFSET_CORE7 30 + +enum GPIO_ROUT { + GPIO_NO_EFFECT = 0, + GPIO_SMI, + GPIO_SCI, + GPIO_RESERVED +}; + +/* + * GPIO resources + * defined as in drivers/gpio/gpio-ich.c + */ +#define ICH_RES_GPIO 0 +#define ICH_RES_GPE0 1 +static struct resource gpio_ich_res[] = { + /* GPIO */ + { + .flags = IORESOURCE_IO, + }, + /* ACPI - GPE0 */ + { + .flags = IORESOURCE_IO, + }, +}; + +// ACPI registers +#define ACPI_GPE0a_STS 0x20 +#define ACPI_GPE0a_EN 0x28 + +// PMC registers +#define PMC_GPIO_ROUT 0x58 +#define PMC_REG_LEN 0x4 + +// lpc_ich_priv is derived from drivers/mfd/lpc_ich.c +struct lpc_ich_priv { + int chipset; + + int abase; /* ACPI base */ + int actrl_pbase; /* ACPI control or PMC base */ + int gbase; /* GPIO base */ + int gctrl; /* GPIO control */ + + int abase_save; /* Cached ACPI base value */ + int actrl_pbase_save; /* Cached ACPI control or PMC base value */ + int gctrl_save; /* Cached GPIO control value */ +}; + +#define ICH_RES_MEM_GCS_PMC 0 + +#define IO_REG_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start) +#define IO_REG_READ(reg, base_res) inl((reg) + (base_res)->start) + +struct resource pmc_res = {.flags = IORESOURCE_MEM,}; + +static struct kobject *slx_kobj; +static unsigned short force_id; +module_param(force_id, ushort, 0); + +struct slx_ich_data { + struct resource *gpio_base, *acpi_base, *pmc_base; + int gpio_alloc,pmc_alloc; + unsigned int int_gpio_sus6_count; +}; + +// GPIO sysfs attributes + +static ssize_t get_sc_use_sel(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(GPIO_SC_USE_SEL,ich_data->gpio_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_sc_use_sel(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,GPIO_SUS_USE_SEL,ich_data->gpio_base); + + return count; +} + +static ssize_t get_sc_io_sel(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(GPIO_SC_IO_SEL,ich_data->gpio_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_sc_io_sel(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,GPIO_SC_IO_SEL,ich_data->gpio_base); + + return count; +} + +static ssize_t get_sc_gp_lvl(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(GPIO_SC_GP_LVL,ich_data->gpio_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_sc_gp_lvl(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,GPIO_SC_GP_LVL,ich_data->gpio_base); + + return count; +} + +static ssize_t get_sc_gp_tpe(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(GPIO_SC_TPE,ich_data->gpio_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_sc_gp_tpe(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,GPIO_SC_TPE,ich_data->gpio_base); + + return count; +} + +static ssize_t get_sc_gp_tne(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(GPIO_SC_TNE,ich_data->gpio_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_sc_gp_tne(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,GPIO_SC_TNE,ich_data->gpio_base); + + return count; +} + +static ssize_t get_sc_gp_ts(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(GPIO_SC_TS,ich_data->gpio_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_sc_gp_ts(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,GPIO_SC_TS,ich_data->gpio_base); + + return count; +} + +static ssize_t get_sus_use_sel(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(GPIO_SUS_USE_SEL,ich_data->gpio_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_sus_use_sel(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,GPIO_SUS_USE_SEL,ich_data->gpio_base); + + return count; +} + +static ssize_t get_sus_io_sel(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(GPIO_SUS_IO_SEL,ich_data->gpio_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_sus_io_sel(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,GPIO_SUS_IO_SEL,ich_data->gpio_base); + + return count; +} + +static ssize_t get_sus_gp_lvl(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(GPIO_SUS_GP_LVL,ich_data->gpio_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_sus_gp_lvl(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,GPIO_SUS_GP_LVL,ich_data->gpio_base); + + return count; +} + +static ssize_t get_sus_gp_tpe(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(GPIO_SUS_TPE,ich_data->gpio_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_sus_gp_tpe(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,GPIO_SUS_TPE,ich_data->gpio_base); + + return count; +} + +static ssize_t get_sus_gp_tne(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(GPIO_SUS_TNE,ich_data->gpio_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_sus_gp_tne(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,GPIO_SUS_TNE,ich_data->gpio_base); + + return count; +} + +static ssize_t get_sus_gp_ts(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(GPIO_SUS_TS,ich_data->gpio_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_sus_gp_ts(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,GPIO_SUS_TS,ich_data->gpio_base); + + return count; +} + +static DEVICE_ATTR(sc_use_sel, S_IRUGO | S_IWUSR, get_sc_use_sel, set_sc_use_sel); +static DEVICE_ATTR(sc_io_sel, S_IRUGO | S_IWUSR, get_sc_io_sel, set_sc_io_sel); +static DEVICE_ATTR(sc_gp_lvl, S_IRUGO | S_IWUSR, get_sc_gp_lvl, set_sc_gp_lvl); +static DEVICE_ATTR(sc_gp_tpe, S_IRUGO | S_IWUSR, get_sc_gp_tpe, set_sc_gp_tpe); +static DEVICE_ATTR(sc_gp_tne, S_IRUGO | S_IWUSR, get_sc_gp_tne, set_sc_gp_tne); +static DEVICE_ATTR(sc_gp_ts, S_IRUGO | S_IWUSR, get_sc_gp_ts, set_sc_gp_ts); +static DEVICE_ATTR(sus_use_sel, S_IRUGO | S_IWUSR, get_sus_use_sel,set_sus_use_sel); +static DEVICE_ATTR(sus_io_sel, S_IRUGO | S_IWUSR, get_sus_io_sel, set_sus_io_sel); +static DEVICE_ATTR(sus_gp_lvl, S_IRUGO | S_IWUSR, get_sus_gp_lvl, set_sus_gp_lvl); +static DEVICE_ATTR(sus_gp_tpe, S_IRUGO | S_IWUSR, get_sus_gp_tpe, set_sus_gp_tpe); +static DEVICE_ATTR(sus_gp_tne, S_IRUGO | S_IWUSR, get_sus_gp_tne, set_sus_gp_tne); +static DEVICE_ATTR(sus_gp_ts, S_IRUGO | S_IWUSR, get_sus_gp_ts, set_sus_gp_ts); + +static struct attribute *gpio_attrs[] = { + &dev_attr_sc_use_sel.attr, + &dev_attr_sc_io_sel.attr, + &dev_attr_sc_gp_lvl.attr, + &dev_attr_sc_gp_tpe.attr, + &dev_attr_sc_gp_tne.attr, + &dev_attr_sc_gp_ts.attr, + &dev_attr_sus_use_sel.attr, + &dev_attr_sus_io_sel.attr, + &dev_attr_sus_gp_lvl.attr, + &dev_attr_sus_gp_tpe.attr, + &dev_attr_sus_gp_tne.attr, + &dev_attr_sus_gp_ts.attr, + NULL, +}; + +static struct attribute_group gpio_attrs_group= { + .attrs = gpio_attrs, +}; + +// ACPI sysfs attributes + +static ssize_t get_gpe0a_sts(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(ACPI_GPE0a_STS,ich_data->acpi_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_gpe0a_sts(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,ACPI_GPE0a_STS,ich_data->acpi_base); + + return count; +} + +static ssize_t get_gpe0a_en(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = IO_REG_READ(ACPI_GPE0a_EN,ich_data->acpi_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_gpe0a_en(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + IO_REG_WRITE(devdata,ACPI_GPE0a_EN,ich_data->acpi_base); + + return count; +} + +static DEVICE_ATTR(gpe0a_sts, S_IRUGO | S_IWUSR, get_gpe0a_sts, set_gpe0a_sts); +static DEVICE_ATTR(gpe0a_en, S_IRUGO | S_IWUSR, get_gpe0a_en, set_gpe0a_en); + +static struct attribute *acpi_attrs[] = { + &dev_attr_gpe0a_sts.attr, + &dev_attr_gpe0a_en.attr, + NULL, +}; + +static struct attribute_group acpi_attrs_group= { + .attrs = acpi_attrs, +}; + +static ssize_t get_gpio_rout(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = readl(ich_data->pmc_base); + return sprintf(buf,"0x%08x\n",devdata); +} + +// PMC sysfs attributes + +static ssize_t set_gpio_rout(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + writel(devdata,ich_data->pmc_base); + + return count; +} + +static DEVICE_ATTR(gpio_rout, S_IRUGO | S_IWUSR, get_gpio_rout, set_gpio_rout); + +static struct attribute *pmc_attrs[] = { + &dev_attr_gpio_rout.attr, + NULL, +}; + +static struct attribute_group pmc_attrs_group= { + .attrs = pmc_attrs, +}; + +// SCI interrupt sysfs attributes + +static ssize_t get_sci_int_gpio_sus6(struct device *dev, struct device_attribute *devattr, char *buf) +{ + u32 devdata=0; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!ich_data) return sprintf(buf, "read error"); + + devdata = ich_data->int_gpio_sus6_count; + return sprintf(buf,"0x%08x\n",devdata); +} + +static ssize_t set_sci_int_gpio_sus6(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + unsigned long devdata; + int err; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + err = kstrtoul(buf, 16, &devdata); + if (err) + return err; + + ich_data->int_gpio_sus6_count = devdata; + + return count; +} + +static DEVICE_ATTR(sci_int_gpio_sus6, S_IRUGO | S_IWUSR, get_sci_int_gpio_sus6, set_sci_int_gpio_sus6); + +static struct attribute *sci_attrs[] = { + &dev_attr_sci_int_gpio_sus6.attr, + NULL, +}; + +static struct attribute_group sci_attrs_group= { + .attrs = sci_attrs, +}; + +static u32 slx_ich_sci_handler(void *context) +{ + unsigned int data; + struct device *dev = (struct device *)context; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + if(!dev) return ACPI_INTERRUPT_NOT_HANDLED; + + ich_data = dev_get_platdata(dev); + if(!ich_data) return ACPI_INTERRUPT_NOT_HANDLED; + + data=IO_REG_READ(ACPI_GPE0a_STS,ich_data->acpi_base); + if(data & GPIO_GPE0a_STS_SUS6) { + // Clear the SUS6 status + IO_REG_WRITE(data,ACPI_GPE0a_STS,ich_data->acpi_base); + ich_data->int_gpio_sus6_count++; + // and notify the user space clients + sysfs_notify(&dev->kobj, NULL, "sci_int_gpio_sus6"); + return ACPI_INTERRUPT_HANDLED; + } + + return ACPI_INTERRUPT_NOT_HANDLED; +} + +/* + * Setup GPIO SUS6 to generate an SCI interrupt for optics detection + * This can be alternatively be setup using sysfs + */ +int setup_gpio_sus6_sci_interrupt(struct device *dev) +{ + int ret=0; + unsigned int data; + struct resource *acpi_base, *pmc_base, *gpio_base; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + + gpio_base = ich_data->gpio_base; + acpi_base = ich_data->acpi_base; + pmc_base = ich_data->pmc_base; + + // Enable GPIOSUS6_EN + data = IO_REG_READ(GPIO_SUS_USE_SEL,gpio_base); + data |= GPIOSUS6_EN; + IO_REG_WRITE(data,GPIO_SUS_USE_SEL,gpio_base); + + // GPIOSUS6_EN is input + data = IO_REG_READ(GPIO_SUS_IO_SEL,gpio_base); + data |= GPIOSUS6_EN; + IO_REG_WRITE(data,GPIO_SUS_IO_SEL,gpio_base); + + // Clear the positive edge for GPIOSUS6_EN + data = IO_REG_READ(GPIO_SUS_TPE,gpio_base); + data &= ~(GPIOSUS6_EN); + IO_REG_WRITE(data,GPIO_SUS_TPE,gpio_base); + + // Trigger on negative edge for GPIOSUS6_EN + data = IO_REG_READ(GPIO_SUS_TNE,gpio_base); + data |= GPIOSUS6_EN; + IO_REG_WRITE(data,GPIO_SUS_TNE,gpio_base); + + // Enable GPE for SUS6 to generate an SCI + data=IO_REG_READ(ACPI_GPE0a_EN,acpi_base); + data|=GPIO_GPE0a_EN_SUS6; + IO_REG_WRITE(data,ACPI_GPE0a_EN,acpi_base); + + data=readl(pmc_base); + data=(data & ~(0x3 << GPIO_ROUT_OFFSET_SUS6)) | (GPIO_SCI << GPIO_ROUT_OFFSET_SUS6); + writel(data,pmc_base); + + ret = acpi_install_sci_handler(slx_ich_sci_handler,(void*)dev); + if(ret) { + pr_info("slx_ich acpi_install_sci_handler failed %d\n",ret); + return ret; + } + + return ret; +} + +static int slx_ich_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + struct pci_dev *lpc_ich_dev; + struct lpc_ich_priv *priv; + struct resource *res; + unsigned int base_addr_cfg, base_addr; + int ret,i; + + // Get the PCU device + lpc_ich_dev=pci_get_device(PCI_VENDOR_ID_INTEL,C2000_PCU_DEVICE_ID,NULL); + priv=(struct lpc_ich_priv*) pci_get_drvdata(lpc_ich_dev); + if(!priv) { + pr_info("slx_ich: Unable to retrieve private data\n"); + return -ENODEV; + } + + // Retrieve the GPIO Base (that was initialized by lpc-ich) + pci_read_config_dword(lpc_ich_dev, priv->gbase, &base_addr_cfg); + base_addr = base_addr_cfg & 0x0000ff80; + if (!base_addr) { + pr_info("slx_ich I/O space for GPIO uninitialized\n"); + ret = -ENODEV; + goto probe_err; + } + + res = &gpio_ich_res[ICH_RES_GPIO]; + res->start = base_addr; + res->end = res->start + 0x9c - 1; + ret = acpi_check_resource_conflict(res); + if (ret) { + pr_info("slx_ich gpio resource conflict ret %d\n",ret); + } + + ich_data->gpio_base=res; + // Request regions for GPIO registers + for(i=0; istart+c2000_gpio_regs[i],GPIO_REG_LEN, "slx_ich_gpio")) { + pr_info("slx_ich: request_region failed for GPIO : %x\n",(unsigned int) res->start+c2000_gpio_regs[i]); + ret = -EBUSY; + goto probe_err; + } + ich_data->gpio_alloc |= (1<kobj, &gpio_attrs_group); + if (ret) { + pr_info("slx_ich cannot create sysfs for GPIO %d\n",ret); + ret = -ENOMEM; + goto probe_err; + } + + // Retrieve the ACPI Base (that was initialized by lpc-ich) + pci_read_config_dword(lpc_ich_dev, priv->abase, &base_addr_cfg); + base_addr = base_addr_cfg & 0x0000ff80; + if (!base_addr) { + pr_info("slx_ich I/O space for ACPI uninitialized\n"); + ret = -ENODEV; + goto probe_err; + } + + res = &gpio_ich_res[ICH_RES_GPE0]; + res->start = base_addr; + res->end = base_addr + 0x40; + ret = acpi_check_resource_conflict(res); + if (ret) { + pr_info("slx_ich acpi resource conflict ret %d\n",ret); + } + + // ACPI region is requested by pnp 00:01/ACPI GPE0_BLK + ich_data->acpi_base=res; + + /* Register sysfs hooks for ACPI */ + ret = sysfs_create_group(&dev->kobj, &acpi_attrs_group); + if (ret) { + pr_info("slx_ich cannot create sysfs for ACPI %d\n",ret); + ret = -ENOMEM; + goto probe_err; + } + + // Retrieve the PMC Base (that was initialized by lpc-ich) + pci_read_config_dword(lpc_ich_dev, priv->actrl_pbase, &base_addr_cfg); + base_addr = base_addr_cfg & 0xfffffe00; + pr_info("slx_ich base_addr_cfg %x base_addr %x\n",(int)base_addr_cfg,(int)base_addr); + if (!base_addr) { + pr_info("slx_ich PMC space for GPIO uninitialized\n"); + ret = -ENODEV; + goto probe_err; + } + + res = &pmc_res; + res->start = base_addr + PMC_GPIO_ROUT; + res->end = base_addr + PMC_GPIO_ROUT + PMC_REG_LEN - 1; + pr_info("slx_ich pmc res_start:end %x:%x\n",(int)res->start,(int)res->end); + + ret = acpi_check_resource_conflict(res); + if (ret) { + pr_info("slx_ich acpi resource conflict ret %d\n",ret); + } + + if (!request_mem_region(res->start,resource_size(res),"slx_ich_pmc")) { + pr_info("slx_ich pmc request_region failed\n"); + ret = -EBUSY; + goto probe_err; + } else { + ich_data->pmc_alloc=1; + } + + ich_data->pmc_base = ioremap(res->start, resource_size(res)); + if(!ich_data->pmc_base) { + pr_info("slx_ich pmc ioremap failed\n"); + ret = -ENOMEM; + goto probe_err; + } + + /* Register sysfs hooks for pmc */ + ret = sysfs_create_group(&dev->kobj, &pmc_attrs_group); + if (ret) { + pr_info("slx_ich cannot create sysfs for PMC %d\n",ret); + ret = -ENOMEM; + goto probe_err; + } + + /* Register sysfs hooks for SCI interrupts*/ + ret = sysfs_create_group(&dev->kobj, &sci_attrs_group); + if (ret) { + pr_info("slx_ich cannot create sysfs for SCI %d\n",ret); + ret = -ENOMEM; + goto probe_err; + } + + ret = setup_gpio_sus6_sci_interrupt(dev); + if (ret) { + pr_info("slx_ich unable to setup SCI interrupt %d\n",ret); + goto probe_err; + } + + return 0; + +probe_err: + pr_info("slx_ich slx_ich_probe failed with : %d\n",ret); + return ret; +} + +static int slx_ich_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct slx_ich_data *ich_data = dev_get_platdata(dev); + int i,ret; + + // Release GPIO regions + for(i=0; igpio_alloc & (1<gpio_base->start+c2000_gpio_regs[i], GPIO_REG_LEN); + } + } + + // Unmap and release PMC regions + if(ich_data->pmc_base) iounmap(ich_data->pmc_base); + if(ich_data->pmc_alloc) release_region(pmc_res.start, PMC_REG_LEN); + + ret = acpi_remove_sci_handler(slx_ich_sci_handler); + if(ret) { + pr_info("slx_ich acpi_remove_sci_handler failed %d\n",ret); + return ret; + } + + pr_info("slx_ich : slx_ich_remove done.\n"); + + return 0; +} + +static struct platform_driver slx_ich_driver= { + .driver = { + .name = DRV_NAME, + }, + .probe = slx_ich_probe, + .remove = slx_ich_remove, +}; + +static struct platform_device *pdev; + +static int __init slx_ich_init(void) +{ + int err; + struct slx_ich_data ich_data; + + memset(&ich_data, 0, sizeof(struct slx_ich_data)); + + err = platform_driver_register(&slx_ich_driver); + if (err) + goto exit; + + pdev = platform_device_alloc(DRV_NAME, 0); + if (!pdev) { + err = -ENOMEM; + pr_err("slx_ich: Device allocation failed\n"); + goto exit_unregister; + } + + err = platform_device_add_data(pdev, &ich_data, + sizeof(struct slx_ich_data)); + if (err) { + pr_err("slx_ich: Platform data allocation failed\n"); + goto exit_device_put; + } + + /* platform_device_add calls probe() */ + err = platform_device_add(pdev); + if (err) { + pr_err("slx_ich: Device addition failed (%d)\n", err); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(pdev); +exit_unregister: + platform_driver_unregister(&slx_ich_driver); +exit: + pr_err("slx_ich: slx_ich_init failed (%d)\n", err); + return err; +} + +static void __exit slx_ich_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&slx_ich_driver); + + /*Remove sysfs slx_kobj*/ + kobject_put(slx_kobj); +} + +MODULE_AUTHOR("Padmanabhan Narayanan"); +MODULE_DESCRIPTION("ICH driver for Rangeley switches"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:"DRV_NAME); +MODULE_PARM_DESC(force_id, "Override the detected device ID"); + +module_init(slx_ich_init); +module_exit(slx_ich_exit);