Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 329950
b: refs/heads/master
c: 4f600ad
h: refs/heads/master
v: v3
  • Loading branch information
Jean Delvare authored and Samuel Ortiz committed Sep 14, 2012
1 parent ed5737a commit 02bac96
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 13 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: cdabc1c88a12e9fc2a49f2a54ce9be470398d8a9
refs/heads/master: 4f600ada70beeb1dfe08e11e871bf31015aa0a3d
79 changes: 69 additions & 10 deletions trunk/drivers/gpio/gpio-ich.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ static const u8 ichx_regs[3][3] = {
{0x0c, 0x38, 0x48}, /* LVL[1-3] offsets */
};

static const u8 ichx_reglen[3] = {
0x30, 0x10, 0x10,
};

#define ICHX_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start)
#define ICHX_READ(reg, base_res) inl((reg) + (base_res)->start)

Expand All @@ -75,6 +79,7 @@ static struct {
struct resource *pm_base; /* Power Mangagment IO base */
struct ichx_desc *desc; /* Pointer to chipset-specific description */
u32 orig_gpio_ctrl; /* Orig CTRL value, used to restore on exit */
u8 use_gpio; /* Which GPIO groups are usable */
} ichx_priv;

static int modparam_gpiobase = -1; /* dynamic */
Expand Down Expand Up @@ -123,8 +128,16 @@ static int ichx_read_bit(int reg, unsigned nr)
return data & (1 << bit) ? 1 : 0;
}

static int ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr)
{
return (ichx_priv.use_gpio & (1 << (nr / 32))) ? 0 : -ENXIO;
}

static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
{
if (!ichx_gpio_check_available(gpio, nr))
return -ENXIO;

/*
* Try setting pin as an input and verify it worked since many pins
* are output-only.
Expand All @@ -138,6 +151,9 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
int val)
{
if (!ichx_gpio_check_available(gpio, nr))
return -ENXIO;

/* Set GPIO output value. */
ichx_write_bit(GPIO_LVL, nr, val, 0);

Expand All @@ -153,6 +169,9 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,

static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr)
{
if (!ichx_gpio_check_available(chip, nr))
return -ENXIO;

return ichx_read_bit(GPIO_LVL, nr);
}

Expand All @@ -161,6 +180,9 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr)
unsigned long flags;
u32 data;

if (!ichx_gpio_check_available(chip, nr))
return -ENXIO;

/*
* GPI 0 - 15 need to be read from the power management registers on
* a ICH6/3100 bridge.
Expand Down Expand Up @@ -291,6 +313,46 @@ static struct ichx_desc intel5_desc = {
.ngpio = 76,
};

static int __devinit ichx_gpio_request_regions(struct resource *res_base,
const char *name, u8 use_gpio)
{
int i;

if (!res_base || !res_base->start || !res_base->end)
return -ENODEV;

for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) {
if (!(use_gpio & (1 << i)))
continue;
if (!request_region(res_base->start + ichx_regs[0][i],
ichx_reglen[i], name))
goto request_err;
}
return 0;

request_err:
/* Clean up: release already requested regions, if any */
for (i--; i >= 0; i--) {
if (!(use_gpio & (1 << i)))
continue;
release_region(res_base->start + ichx_regs[0][i],
ichx_reglen[i]);
}
return -EBUSY;
}

static void ichx_gpio_release_regions(struct resource *res_base, u8 use_gpio)
{
int i;

for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) {
if (!(use_gpio & (1 << i)))
continue;
release_region(res_base->start + ichx_regs[0][i],
ichx_reglen[i]);
}
}

static int __devinit ichx_gpio_probe(struct platform_device *pdev)
{
struct resource *res_base, *res_pm;
Expand Down Expand Up @@ -329,12 +391,11 @@ static int __devinit ichx_gpio_probe(struct platform_device *pdev)
}

res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO);
if (!res_base || !res_base->start || !res_base->end)
return -ENODEV;

if (!request_region(res_base->start, resource_size(res_base),
pdev->name))
return -EBUSY;
ichx_priv.use_gpio = ich_info->use_gpio;
err = ichx_gpio_request_regions(res_base, pdev->name,
ichx_priv.use_gpio);
if (err)
return err;

ichx_priv.gpio_base = res_base;

Expand Down Expand Up @@ -374,8 +435,7 @@ static int __devinit ichx_gpio_probe(struct platform_device *pdev)
return 0;

add_err:
release_region(ichx_priv.gpio_base->start,
resource_size(ichx_priv.gpio_base));
ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio);
if (ichx_priv.pm_base)
release_region(ichx_priv.pm_base->start,
resource_size(ichx_priv.pm_base));
Expand All @@ -393,8 +453,7 @@ static int __devexit ichx_gpio_remove(struct platform_device *pdev)
return err;
}

release_region(ichx_priv.gpio_base->start,
resource_size(ichx_priv.gpio_base));
ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio);
if (ichx_priv.pm_base)
release_region(ichx_priv.pm_base->start,
resource_size(ichx_priv.pm_base));
Expand Down
29 changes: 27 additions & 2 deletions trunk/drivers/mfd/lpc_ich.c
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,30 @@ static void __devinit lpc_ich_finalize_cell(struct mfd_cell *cell,
cell->pdata_size = sizeof(struct lpc_ich_info);
}

/*
* We don't check for resource conflict globally. There are 2 or 3 independent
* GPIO groups and it's enough to have access to one of these to instantiate
* the device.
*/
static int __devinit lpc_ich_check_conflict_gpio(struct resource *res)
{
int ret;
u8 use_gpio = 0;

if (resource_size(res) >= 0x50 &&
!acpi_check_region(res->start + 0x40, 0x10, "LPC ICH GPIO3"))
use_gpio |= 1 << 2;

if (!acpi_check_region(res->start + 0x30, 0x10, "LPC ICH GPIO2"))
use_gpio |= 1 << 1;

ret = acpi_check_region(res->start + 0x00, 0x30, "LPC ICH GPIO1");
if (!ret)
use_gpio |= 1 << 0;

return use_gpio ? use_gpio : ret;
}

static int __devinit lpc_ich_init_gpio(struct pci_dev *dev,
const struct pci_device_id *id)
{
Expand Down Expand Up @@ -740,12 +764,13 @@ static int __devinit lpc_ich_init_gpio(struct pci_dev *dev,
break;
}

ret = acpi_check_resource_conflict(res);
if (ret) {
ret = lpc_ich_check_conflict_gpio(res);
if (ret < 0) {
/* this isn't necessarily fatal for the GPIO */
acpi_conflict = true;
goto gpio_done;
}
lpc_chipset_info[id->driver_data].use_gpio = ret;
lpc_ich_enable_gpio_space(dev);

lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id);
Expand Down
1 change: 1 addition & 0 deletions trunk/include/linux/mfd/lpc_ich.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ struct lpc_ich_info {
char name[32];
unsigned int iTCO_version;
unsigned int gpio_version;
u8 use_gpio;
};

#endif

0 comments on commit 02bac96

Please sign in to comment.