/* Copyright (c) 2018 Dell Inc. * dell_ich.c - ICH driver for Dell Avoton/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 "dell_ich" #define AVOTON_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 avoton_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) // 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 *dell_kobj; static unsigned short force_id; module_param(force_id, ushort, 0); #define SMF_REG_ADDR 0x200 #define SIO_REG_DEVID 0x1 #define SIO_Z9100_ID 0x1 #define SIO_S6100_ID 0x2 #define SIO_S4200_ID 0x3 #define SIO_S5148_ID 0x4 enum kinds { z9100smf, s6100smf }; struct dell_ich_data { enum kinds kind; 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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_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 dell_ich_sci_handler(void *context) { unsigned int data; struct device *dev = (struct device *)context; struct dell_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 dell_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); // Trigger on 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); // 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(dell_ich_sci_handler,(void*)dev); if(ret) { pr_info("dell_ich acpi_install_sci_handler failed %d\n",ret); return ret; } return ret; } static int dell_ich_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct dell_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,AVOTON_PCU_DEVICE_ID,NULL); priv=(struct lpc_ich_priv*) pci_get_drvdata(lpc_ich_dev); if(!priv) { pr_info("dell_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("dell_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("dell_ich gpio resource conflict ret %d\n",ret); } ich_data->gpio_base=res; // Request regions for GPIO registers for(i=0; istart+avoton_gpio_regs[i],GPIO_REG_LEN, "dell_ich_gpio")) { pr_info("dell_ich: request_region failed for GPIO : %x\n",(unsigned int) res->start+avoton_gpio_regs[i]); ret = -EBUSY; goto probe_err; } ich_data->gpio_alloc |= (1<kobj, &gpio_attrs_group); if (ret) { pr_info("dell_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("dell_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("dell_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("dell_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("dell_ich base_addr_cfg %x base_addr %x\n",(int)base_addr_cfg,(int)base_addr); if (!base_addr) { pr_info("dell_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("dell_ich pmc res_start:end %x:%x\n",(int)res->start,(int)res->end); ret = acpi_check_resource_conflict(res); if (ret) { pr_info("dell_ich acpi resource conflict ret %d\n",ret); } if (!request_mem_region(res->start,resource_size(res),"dell_ich_pmc")) { pr_info("dell_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("dell_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("dell_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("dell_ich cannot create sysfs for SCI %d\n",ret); ret = -ENOMEM; goto probe_err; } if((ich_data->kind == z9100smf) || (ich_data->kind == s6100smf)) { ret = setup_gpio_sus6_sci_interrupt(dev); if (ret) { pr_info("dell_ich unable to setup SCI interrupt %d\n",ret); goto probe_err; } } return 0; probe_err: pr_info("dell_ich dell_ich_probe failed with : %d\n",ret); return ret; } static int dell_ich_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct dell_ich_data *ich_data = dev_get_platdata(dev); int i,ret; // Release GPIO regions for(i=0; igpio_alloc & (1<gpio_base->start+avoton_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_mem_region(pmc_res.start, PMC_REG_LEN); ret = acpi_remove_sci_handler(dell_ich_sci_handler); if(ret) { pr_info("dell_ich acpi_remove_sci_handler failed %d\n",ret); return ret; } pr_info("dell_ich : dell_ich_remove done.\n"); return 0; } static struct platform_driver dell_ich_driver= { .driver = { .name = DRV_NAME, }, .probe = dell_ich_probe, .remove = dell_ich_remove, }; int __init init_ich_data(int smf_address, struct dell_ich_data *ich_data) { int val; memset(ich_data, 0, sizeof(struct dell_ich_data)); if (force_id) val = force_id; else val = inb(smf_address + SIO_REG_DEVID); switch (val) { case SIO_Z9100_ID: ich_data->kind = z9100smf; break; case SIO_S6100_ID: ich_data->kind = s6100smf; break; default: if (val != 0xffff) pr_debug("unsupported chip ID: 0x%04x\n", val); return -ENODEV; } pr_info("dell_ich: found SMF for ID %#x\n", ich_data->kind); return (0); } static struct platform_device *pdev; static int __init dell_ich_init(void) { int err; struct dell_ich_data ich_data; if (init_ich_data(SMF_REG_ADDR, &ich_data)) return -ENODEV; err = platform_driver_register(&dell_ich_driver); if (err) goto exit; pdev = platform_device_alloc(DRV_NAME, 0); if (!pdev) { err = -ENOMEM; pr_err("dell_ich: Device allocation failed\n"); goto exit_unregister; } err = platform_device_add_data(pdev, &ich_data, sizeof(struct dell_ich_data)); if (err) { pr_err("dell_ich: Platform data allocation failed\n"); goto exit_device_put; } /* platform_device_add calls probe() */ err = platform_device_add(pdev); if (err) { pr_err("dell_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(&dell_ich_driver); exit: pr_err("dell_ich: dell_ich_init failed (%d)\n", err); return err; } static void __exit dell_ich_exit(void) { platform_device_unregister(pdev); platform_driver_unregister(&dell_ich_driver); /*Remove sysfs dell_kobj*/ kobject_put(dell_kobj); } MODULE_AUTHOR("Padmanabhan Narayanan"); MODULE_DESCRIPTION("ICH driver for Dell Avoton/Rangeley switches"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:"DRV_NAME); MODULE_PARM_DESC(force_id, "Override the detected device ID"); MODULE_SOFTDEP("pre: lpc_ich"); module_init(dell_ich_init); module_exit(dell_ich_exit);