Skip to content

Commit

Permalink
mfd: lpc_ich: Fix gpio base and control offsets
Browse files Browse the repository at this point in the history
In ICH5 and earlier the GPIOBASE and GPIOCTRL registers are found at
offsets 0x58 and 0x5C, respectively. This patch allows GPIO access to
properly be enabled (and disabled) for these chipsets.

Signed-off-by: Agócs Pál <agocs.pal.86@gmail.com>
Signed-off-by: Aaron Sierra <asierra@xes-inc.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
  • Loading branch information
Aaron Sierra authored and Samuel Ortiz committed Feb 13, 2013
1 parent fbc6ae3 commit 01560f6
Showing 1 changed file with 76 additions and 33 deletions.
109 changes: 76 additions & 33 deletions drivers/mfd/lpc_ich.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,28 @@
#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_io_res(i) wdt_res(0, i)
#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 */
Expand Down Expand Up @@ -661,39 +672,44 @@ 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);
}

Expand Down Expand Up @@ -721,17 +737,17 @@ 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;
bool acpi_conflict = false;
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");
Expand All @@ -757,7 +773,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");
Expand All @@ -768,7 +784,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
/* 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;
Expand All @@ -784,10 +800,10 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
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);

Expand All @@ -798,16 +814,16 @@ static int lpc_ich_init_gpio(struct pci_dev *dev,
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");
Expand All @@ -830,7 +846,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 {
Expand All @@ -847,7 +863,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);

Expand All @@ -858,14 +874,35 @@ static int lpc_ich_init_wdt(struct pci_dev *dev,
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 = kmalloc(GFP_KERNEL, sizeof(struct lpc_ich_priv));
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;

Expand All @@ -876,6 +913,8 @@ 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);
kfree(priv);
return -ENODEV;
}

Expand All @@ -884,8 +923,12 @@ static int lpc_ich_probe(struct pci_dev *dev,

static void lpc_ich_remove(struct pci_dev *dev)
{
void *priv = pci_get_drvdata(dev);

mfd_remove_devices(&dev->dev);
lpc_ich_restore_config_space(dev);
pci_set_drvdata(dev, NULL);
kfree(priv);
}

static struct pci_driver lpc_ich_driver = {
Expand Down

0 comments on commit 01560f6

Please sign in to comment.