Merge tag 'md-3.9' of git://neil.brown.name/md
[firefly-linux-kernel-4.4.55.git] / drivers / mfd / lpc_ich.c
index d9d930302e98123c756e47477ca95cac0acebc59..9f12f91d6296e6793dca0a5f29e07c8a6621b314 100644 (file)
@@ -50,6 +50,7 @@
  *     document number TBD : Panther Point
  *     document number TBD : Lynx Point
  *     document number TBD : Lynx Point-LP
+ *     document number TBD : Wellsburg
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #define ACPIBASE_GCS_OFF       0x3410
 #define ACPIBASE_GCS_END       0x3414
 
-#define GPIOBASE               0x48
-#define GPIOCTRL               0x4C
+#define GPIOBASE_ICH0          0x58
+#define GPIOCTRL_ICH0          0x5C
+#define GPIOBASE_ICH6          0x48
+#define GPIOCTRL_ICH6          0x4C
 
 #define RCBABASE               0xf0
 
 #define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i)
 #define wdt_res(b, i) (&wdt_ich_res[(b) + (i)])
 
-static int lpc_ich_acpi_save = -1;
-static int lpc_ich_gpio_save = -1;
+struct lpc_ich_cfg {
+       int base;
+       int ctrl;
+       int save;
+};
+
+struct lpc_ich_priv {
+       int chipset;
+       struct lpc_ich_cfg acpi;
+       struct lpc_ich_cfg gpio;
+};
 
 static struct resource wdt_ich_res[] = {
        /* ACPI - TCO */
@@ -194,6 +206,7 @@ enum lpc_chipsets {
        LPC_PPT,        /* Panther Point */
        LPC_LPT,        /* Lynx Point */
        LPC_LPT_LP,     /* Lynx Point-LP */
+       LPC_WBG,        /* Wellsburg */
 };
 
 struct lpc_ich_info lpc_chipset_info[] = {
@@ -474,6 +487,10 @@ struct lpc_ich_info lpc_chipset_info[] = {
                .name = "Lynx Point_LP",
                .iTCO_version = 2,
        },
+       [LPC_WBG] = {
+               .name = "Wellsburg",
+               .iTCO_version = 2,
+       },
 };
 
 /*
@@ -655,45 +672,82 @@ static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = {
        { PCI_VDEVICE(INTEL, 0x9c45), LPC_LPT_LP},
        { PCI_VDEVICE(INTEL, 0x9c46), LPC_LPT_LP},
        { PCI_VDEVICE(INTEL, 0x9c47), LPC_LPT_LP},
+       { PCI_VDEVICE(INTEL, 0x8d40), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d41), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d42), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d43), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d44), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d45), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d46), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d47), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d48), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d49), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d4a), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d4b), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d4c), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d4d), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d4e), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d4f), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d50), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d51), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d52), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d53), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d54), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d55), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d56), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d57), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d58), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d59), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d5a), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d5b), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d5c), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d5d), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d5e), LPC_WBG},
+       { PCI_VDEVICE(INTEL, 0x8d5f), LPC_WBG},
        { 0, },                 /* End of list */
 };
 MODULE_DEVICE_TABLE(pci, lpc_ich_ids);
 
 static void lpc_ich_restore_config_space(struct pci_dev *dev)
 {
-       if (lpc_ich_acpi_save >= 0) {
-               pci_write_config_byte(dev, ACPICTRL, lpc_ich_acpi_save);
-               lpc_ich_acpi_save = -1;
+       struct lpc_ich_priv *priv = pci_get_drvdata(dev);
+
+       if (priv->acpi.save >= 0) {
+               pci_write_config_byte(dev, priv->acpi.ctrl, priv->acpi.save);
+               priv->acpi.save = -1;
        }
 
-       if (lpc_ich_gpio_save >= 0) {
-               pci_write_config_byte(dev, GPIOCTRL, lpc_ich_gpio_save);
-               lpc_ich_gpio_save = -1;
+       if (priv->gpio.save >= 0) {
+               pci_write_config_byte(dev, priv->gpio.ctrl, priv->gpio.save);
+               priv->gpio.save = -1;
        }
 }
 
 static void lpc_ich_enable_acpi_space(struct pci_dev *dev)
 {
+       struct lpc_ich_priv *priv = pci_get_drvdata(dev);
        u8 reg_save;
 
-       pci_read_config_byte(dev, ACPICTRL, &reg_save);
-       pci_write_config_byte(dev, ACPICTRL, reg_save | 0x10);
-       lpc_ich_acpi_save = reg_save;
+       pci_read_config_byte(dev, priv->acpi.ctrl, &reg_save);
+       pci_write_config_byte(dev, priv->acpi.ctrl, reg_save | 0x10);
+       priv->acpi.save = reg_save;
 }
 
 static void lpc_ich_enable_gpio_space(struct pci_dev *dev)
 {
+       struct lpc_ich_priv *priv = pci_get_drvdata(dev);
        u8 reg_save;
 
-       pci_read_config_byte(dev, GPIOCTRL, &reg_save);
-       pci_write_config_byte(dev, GPIOCTRL, reg_save | 0x10);
-       lpc_ich_gpio_save = reg_save;
+       pci_read_config_byte(dev, priv->gpio.ctrl, &reg_save);
+       pci_write_config_byte(dev, priv->gpio.ctrl, reg_save | 0x10);
+       priv->gpio.save = reg_save;
 }
 
-static void lpc_ich_finalize_cell(struct mfd_cell *cell,
-                                       const struct pci_device_id *id)
+static void lpc_ich_finalize_cell(struct pci_dev *dev, struct mfd_cell *cell)
 {
-       cell->platform_data = &lpc_chipset_info[id->driver_data];
+       struct lpc_ich_priv *priv = pci_get_drvdata(dev);
+
+       cell->platform_data = &lpc_chipset_info[priv->chipset];
        cell->pdata_size = sizeof(struct lpc_ich_info);
 }
 
@@ -721,9 +775,9 @@ static int lpc_ich_check_conflict_gpio(struct resource *res)
        return use_gpio ? use_gpio : ret;
 }
 
-static int lpc_ich_init_gpio(struct pci_dev *dev,
-                               const struct pci_device_id *id)
+static int lpc_ich_init_gpio(struct pci_dev *dev)
 {
+       struct lpc_ich_priv *priv = pci_get_drvdata(dev);
        u32 base_addr_cfg;
        u32 base_addr;
        int ret;
@@ -731,7 +785,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
        struct resource *res;
 
        /* Setup power management base register */
-       pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
+       pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg);
        base_addr = base_addr_cfg & 0x0000ff80;
        if (!base_addr) {
                dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
@@ -757,7 +811,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
 
 gpe0_done:
        /* Setup GPIO base register */
-       pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
+       pci_read_config_dword(dev, priv->gpio.base, &base_addr_cfg);
        base_addr = base_addr_cfg & 0x0000ff80;
        if (!base_addr) {
                dev_notice(&dev->dev, "I/O space for GPIO uninitialized\n");
@@ -768,7 +822,7 @@ gpe0_done:
        /* Older devices provide fewer GPIO and have a smaller resource size. */
        res = &gpio_ich_res[ICH_RES_GPIO];
        res->start = base_addr;
-       switch (lpc_chipset_info[id->driver_data].gpio_version) {
+       switch (lpc_chipset_info[priv->chipset].gpio_version) {
        case ICH_V5_GPIO:
        case ICH_V10CORP_GPIO:
                res->end = res->start + 128 - 1;
@@ -784,10 +838,10 @@ gpe0_done:
                acpi_conflict = true;
                goto gpio_done;
        }
-       lpc_chipset_info[id->driver_data].use_gpio = ret;
+       lpc_chipset_info[priv->chipset].use_gpio = ret;
        lpc_ich_enable_gpio_space(dev);
 
-       lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id);
+       lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_GPIO]);
        ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO],
                              1, NULL, 0, NULL);
 
@@ -798,16 +852,16 @@ gpio_done:
        return ret;
 }
 
-static int lpc_ich_init_wdt(struct pci_dev *dev,
-                               const struct pci_device_id *id)
+static int lpc_ich_init_wdt(struct pci_dev *dev)
 {
+       struct lpc_ich_priv *priv = pci_get_drvdata(dev);
        u32 base_addr_cfg;
        u32 base_addr;
        int ret;
        struct resource *res;
 
        /* Setup power management base register */
-       pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
+       pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg);
        base_addr = base_addr_cfg & 0x0000ff80;
        if (!base_addr) {
                dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
@@ -830,7 +884,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev,
         * we have to read RCBA from PCI Config space 0xf0 and use
         * it as base. GCS = RCBA + ICH6_GCS(0x3410).
         */
-       if (lpc_chipset_info[id->driver_data].iTCO_version == 1) {
+       if (lpc_chipset_info[priv->chipset].iTCO_version == 1) {
                /* Don't register iomem for TCO ver 1 */
                lpc_ich_cells[LPC_WDT].num_resources--;
        } else {
@@ -847,7 +901,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev,
                res->end = base_addr + ACPIBASE_GCS_END;
        }
 
-       lpc_ich_finalize_cell(&lpc_ich_cells[LPC_WDT], id);
+       lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]);
        ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT],
                              1, NULL, 0, NULL);
 
@@ -858,14 +912,36 @@ wdt_done:
 static int lpc_ich_probe(struct pci_dev *dev,
                                const struct pci_device_id *id)
 {
+       struct lpc_ich_priv *priv;
        int ret;
        bool cell_added = false;
 
-       ret = lpc_ich_init_wdt(dev, id);
+       priv = devm_kzalloc(&dev->dev,
+                           sizeof(struct lpc_ich_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->chipset = id->driver_data;
+       priv->acpi.save = -1;
+       priv->acpi.base = ACPIBASE;
+       priv->acpi.ctrl = ACPICTRL;
+
+       priv->gpio.save = -1;
+       if (priv->chipset <= LPC_ICH5) {
+               priv->gpio.base = GPIOBASE_ICH0;
+               priv->gpio.ctrl = GPIOCTRL_ICH0;
+       } else {
+               priv->gpio.base = GPIOBASE_ICH6;
+               priv->gpio.ctrl = GPIOCTRL_ICH6;
+       }
+
+       pci_set_drvdata(dev, priv);
+
+       ret = lpc_ich_init_wdt(dev);
        if (!ret)
                cell_added = true;
 
-       ret = lpc_ich_init_gpio(dev, id);
+       ret = lpc_ich_init_gpio(dev);
        if (!ret)
                cell_added = true;
 
@@ -876,6 +952,7 @@ static int lpc_ich_probe(struct pci_dev *dev,
        if (!cell_added) {
                dev_warn(&dev->dev, "No MFD cells added\n");
                lpc_ich_restore_config_space(dev);
+               pci_set_drvdata(dev, NULL);
                return -ENODEV;
        }
 
@@ -886,6 +963,7 @@ static void lpc_ich_remove(struct pci_dev *dev)
 {
        mfd_remove_devices(&dev->dev);
        lpc_ich_restore_config_space(dev);
+       pci_set_drvdata(dev, NULL);
 }
 
 static struct pci_driver lpc_ich_driver = {