diff options
Diffstat (limited to 'drivers/gpio/gpio-ich.c')
-rw-r--r-- | drivers/gpio/gpio-ich.c | 79 |
1 files changed, 69 insertions, 10 deletions
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index b7c06517403d..d4d617966696 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c | |||
@@ -49,6 +49,10 @@ static const u8 ichx_regs[3][3] = { | |||
49 | {0x0c, 0x38, 0x48}, /* LVL[1-3] offsets */ | 49 | {0x0c, 0x38, 0x48}, /* LVL[1-3] offsets */ |
50 | }; | 50 | }; |
51 | 51 | ||
52 | static const u8 ichx_reglen[3] = { | ||
53 | 0x30, 0x10, 0x10, | ||
54 | }; | ||
55 | |||
52 | #define ICHX_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start) | 56 | #define ICHX_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start) |
53 | #define ICHX_READ(reg, base_res) inl((reg) + (base_res)->start) | 57 | #define ICHX_READ(reg, base_res) inl((reg) + (base_res)->start) |
54 | 58 | ||
@@ -75,6 +79,7 @@ static struct { | |||
75 | struct resource *pm_base; /* Power Mangagment IO base */ | 79 | struct resource *pm_base; /* Power Mangagment IO base */ |
76 | struct ichx_desc *desc; /* Pointer to chipset-specific description */ | 80 | struct ichx_desc *desc; /* Pointer to chipset-specific description */ |
77 | u32 orig_gpio_ctrl; /* Orig CTRL value, used to restore on exit */ | 81 | u32 orig_gpio_ctrl; /* Orig CTRL value, used to restore on exit */ |
82 | u8 use_gpio; /* Which GPIO groups are usable */ | ||
78 | } ichx_priv; | 83 | } ichx_priv; |
79 | 84 | ||
80 | static int modparam_gpiobase = -1; /* dynamic */ | 85 | static int modparam_gpiobase = -1; /* dynamic */ |
@@ -123,8 +128,16 @@ static int ichx_read_bit(int reg, unsigned nr) | |||
123 | return data & (1 << bit) ? 1 : 0; | 128 | return data & (1 << bit) ? 1 : 0; |
124 | } | 129 | } |
125 | 130 | ||
131 | static int ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr) | ||
132 | { | ||
133 | return (ichx_priv.use_gpio & (1 << (nr / 32))) ? 0 : -ENXIO; | ||
134 | } | ||
135 | |||
126 | static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) | 136 | static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) |
127 | { | 137 | { |
138 | if (!ichx_gpio_check_available(gpio, nr)) | ||
139 | return -ENXIO; | ||
140 | |||
128 | /* | 141 | /* |
129 | * Try setting pin as an input and verify it worked since many pins | 142 | * Try setting pin as an input and verify it worked since many pins |
130 | * are output-only. | 143 | * are output-only. |
@@ -138,6 +151,9 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) | |||
138 | static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, | 151 | static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, |
139 | int val) | 152 | int val) |
140 | { | 153 | { |
154 | if (!ichx_gpio_check_available(gpio, nr)) | ||
155 | return -ENXIO; | ||
156 | |||
141 | /* Set GPIO output value. */ | 157 | /* Set GPIO output value. */ |
142 | ichx_write_bit(GPIO_LVL, nr, val, 0); | 158 | ichx_write_bit(GPIO_LVL, nr, val, 0); |
143 | 159 | ||
@@ -153,6 +169,9 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, | |||
153 | 169 | ||
154 | static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr) | 170 | static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr) |
155 | { | 171 | { |
172 | if (!ichx_gpio_check_available(chip, nr)) | ||
173 | return -ENXIO; | ||
174 | |||
156 | return ichx_read_bit(GPIO_LVL, nr); | 175 | return ichx_read_bit(GPIO_LVL, nr); |
157 | } | 176 | } |
158 | 177 | ||
@@ -161,6 +180,9 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr) | |||
161 | unsigned long flags; | 180 | unsigned long flags; |
162 | u32 data; | 181 | u32 data; |
163 | 182 | ||
183 | if (!ichx_gpio_check_available(chip, nr)) | ||
184 | return -ENXIO; | ||
185 | |||
164 | /* | 186 | /* |
165 | * GPI 0 - 15 need to be read from the power management registers on | 187 | * GPI 0 - 15 need to be read from the power management registers on |
166 | * a ICH6/3100 bridge. | 188 | * a ICH6/3100 bridge. |
@@ -291,6 +313,46 @@ static struct ichx_desc intel5_desc = { | |||
291 | .ngpio = 76, | 313 | .ngpio = 76, |
292 | }; | 314 | }; |
293 | 315 | ||
316 | static int __devinit ichx_gpio_request_regions(struct resource *res_base, | ||
317 | const char *name, u8 use_gpio) | ||
318 | { | ||
319 | int i; | ||
320 | |||
321 | if (!res_base || !res_base->start || !res_base->end) | ||
322 | return -ENODEV; | ||
323 | |||
324 | for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) { | ||
325 | if (!(use_gpio & (1 << i))) | ||
326 | continue; | ||
327 | if (!request_region(res_base->start + ichx_regs[0][i], | ||
328 | ichx_reglen[i], name)) | ||
329 | goto request_err; | ||
330 | } | ||
331 | return 0; | ||
332 | |||
333 | request_err: | ||
334 | /* Clean up: release already requested regions, if any */ | ||
335 | for (i--; i >= 0; i--) { | ||
336 | if (!(use_gpio & (1 << i))) | ||
337 | continue; | ||
338 | release_region(res_base->start + ichx_regs[0][i], | ||
339 | ichx_reglen[i]); | ||
340 | } | ||
341 | return -EBUSY; | ||
342 | } | ||
343 | |||
344 | static void ichx_gpio_release_regions(struct resource *res_base, u8 use_gpio) | ||
345 | { | ||
346 | int i; | ||
347 | |||
348 | for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) { | ||
349 | if (!(use_gpio & (1 << i))) | ||
350 | continue; | ||
351 | release_region(res_base->start + ichx_regs[0][i], | ||
352 | ichx_reglen[i]); | ||
353 | } | ||
354 | } | ||
355 | |||
294 | static int __devinit ichx_gpio_probe(struct platform_device *pdev) | 356 | static int __devinit ichx_gpio_probe(struct platform_device *pdev) |
295 | { | 357 | { |
296 | struct resource *res_base, *res_pm; | 358 | struct resource *res_base, *res_pm; |
@@ -329,12 +391,11 @@ static int __devinit ichx_gpio_probe(struct platform_device *pdev) | |||
329 | } | 391 | } |
330 | 392 | ||
331 | res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO); | 393 | res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO); |
332 | if (!res_base || !res_base->start || !res_base->end) | 394 | ichx_priv.use_gpio = ich_info->use_gpio; |
333 | return -ENODEV; | 395 | err = ichx_gpio_request_regions(res_base, pdev->name, |
334 | 396 | ichx_priv.use_gpio); | |
335 | if (!request_region(res_base->start, resource_size(res_base), | 397 | if (err) |
336 | pdev->name)) | 398 | return err; |
337 | return -EBUSY; | ||
338 | 399 | ||
339 | ichx_priv.gpio_base = res_base; | 400 | ichx_priv.gpio_base = res_base; |
340 | 401 | ||
@@ -374,8 +435,7 @@ init: | |||
374 | return 0; | 435 | return 0; |
375 | 436 | ||
376 | add_err: | 437 | add_err: |
377 | release_region(ichx_priv.gpio_base->start, | 438 | ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio); |
378 | resource_size(ichx_priv.gpio_base)); | ||
379 | if (ichx_priv.pm_base) | 439 | if (ichx_priv.pm_base) |
380 | release_region(ichx_priv.pm_base->start, | 440 | release_region(ichx_priv.pm_base->start, |
381 | resource_size(ichx_priv.pm_base)); | 441 | resource_size(ichx_priv.pm_base)); |
@@ -393,8 +453,7 @@ static int __devexit ichx_gpio_remove(struct platform_device *pdev) | |||
393 | return err; | 453 | return err; |
394 | } | 454 | } |
395 | 455 | ||
396 | release_region(ichx_priv.gpio_base->start, | 456 | ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio); |
397 | resource_size(ichx_priv.gpio_base)); | ||
398 | if (ichx_priv.pm_base) | 457 | if (ichx_priv.pm_base) |
399 | release_region(ichx_priv.pm_base->start, | 458 | release_region(ichx_priv.pm_base->start, |
400 | resource_size(ichx_priv.pm_base)); | 459 | resource_size(ichx_priv.pm_base)); |