diff --git a/device/celestica/x86_64-cel_seastone-r0/plugins/sfputil.py b/device/celestica/x86_64-cel_seastone-r0/plugins/sfputil.py index c361659b0f17..453475aad7f8 100644 --- a/device/celestica/x86_64-cel_seastone-r0/plugins/sfputil.py +++ b/device/celestica/x86_64-cel_seastone-r0/plugins/sfputil.py @@ -5,6 +5,8 @@ try: import time + import os + import select from sonic_sfp.sfputilbase import SfpUtilBase except ImportError as e: raise ImportError("%s - required module not found" % str(e)) @@ -201,4 +203,40 @@ def reset(self, port_num): return True def get_transceiver_change_event(self, timeout=0): - raise NotImplementedError + epoll = select.epoll() + port_dict = {} + timeout_sec = timeout/1000 + modabs_interrupt_path = '/sys/devices/platform/dx010_cpld/qsfp_modprs_irq' + + with open(modabs_interrupt_path, 'r') as port_changes: + port_changes.read() + try: + # We get notified when there is an SCI interrupt from GPIO SUS6 + fd = open("/sys/devices/platform/slx-ich.0/sci_int_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: + found_flag = 0 + # Read the QSFP ABS interrupt & status registers + with open(modabs_interrupt_path, 'r') as port_changes: + changes = int(port_changes.read(), 16) + for port_num in self.qsfp_ports: + change = (changes >> port_num-1) & 1 + if change == 1: + port_dict[str(port_num)] = str( + int(self.get_presence(port_num))) + found_flag = 1 + + if not found_flag: + return False, {} + + return True, port_dict + + finally: + fd.close() + epoll.close() + + return False, {} + 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 99d0aab3fd61..2aac7b2c75d8 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 7893220ff6d5..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 @@ -322,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, @@ -336,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, }; @@ -568,7 +712,7 @@ static struct i2c_adapter * cel_dx010_i2c_init(struct platform_device *pdev, int static int cel_dx010_lpc_drv_probe(struct platform_device *pdev) { struct resource *res; - int ret =0; + int ret = 0; int portid_count; cpld_data = devm_kzalloc(&pdev->dev, sizeof(struct dx010_cpld_data), @@ -594,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; } @@ -634,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);