aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCory Maccarrone <darkstar6262@gmail.com>2010-01-19 05:22:45 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2010-03-07 16:17:09 -0500
commit6048a3dd2371c58611ea0ab8b306f8f1469399ae (patch)
tree961cff65dda1ea4ab5049d01d858c95ee2ac5044
parent08ff6f2a9908c87b0adffe6453291be22db3a62f (diff)
mfd: Add HTCPLD driver
This change introduces a driver for the HTC PLD chip found on some smartphones, such as the HTC Wizard and HTC Herald. It works through the I2C bus and acts as a GPIO extender. Specifically: * it can have several sub-devices, each with its own I2C address * Each sub-device provides 8 output and 8 input pins * The chip attaches to one GPIO to signal when any of the input GPIOs change -- at which point all chips must be scanned for changes This driver implements the GPIOs throught the kernel's GPIO and IRQ framework. This allows any GPIO-servicing drivers to operate on htcpld pins, such as the gpio-keys and gpio-leds drivers. Signed-off-by: Cory Maccarrone <darkstar6262@gmail.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r--drivers/mfd/Kconfig9
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/htc-i2cpld.c710
-rw-r--r--include/linux/htcpld.h24
4 files changed, 744 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index d63ab2eec661..9bcd447e71e2 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -78,6 +78,15 @@ config HTC_PASIC3
78 HTC Magician devices, respectively. Actual functionality is 78 HTC Magician devices, respectively. Actual functionality is
79 handled by the leds-pasic3 and ds1wm drivers. 79 handled by the leds-pasic3 and ds1wm drivers.
80 80
81config HTC_I2CPLD
82 bool "HTC I2C PLD chip support"
83 depends on I2C=y
84 help
85 If you say yes here you get support for the supposed CPLD
86 found on omap850 HTC devices like the HTC Wizard and HTC Herald.
87 This device provides input and output GPIOs through an I2C
88 interface to one or more sub-chips.
89
81config UCB1400_CORE 90config UCB1400_CORE
82 tristate "Philips UCB1400 Core driver" 91 tristate "Philips UCB1400 Core driver"
83 depends on AC97_BUS 92 depends on AC97_BUS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 261635700243..142d31202b14 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_MFD_SH_MOBILE_SDHI) += sh_mobile_sdhi.o
10 10
11obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o 11obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
12obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o 12obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
13obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
13 14
14obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o 15obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
15 16
diff --git a/drivers/mfd/htc-i2cpld.c b/drivers/mfd/htc-i2cpld.c
new file mode 100644
index 000000000000..37b9fdab4f36
--- /dev/null
+++ b/drivers/mfd/htc-i2cpld.c
@@ -0,0 +1,710 @@
1/*
2 * htc-i2cpld.c
3 * Chip driver for an unknown CPLD chip found on omap850 HTC devices like
4 * the HTC Wizard and HTC Herald.
5 * The cpld is located on the i2c bus and acts as an input/output GPIO
6 * extender.
7 *
8 * Copyright (C) 2009 Cory Maccarrone <darkstar6262@gmail.com>
9 *
10 * Based on work done in the linwizard project
11 * Copyright (C) 2008-2009 Angelo Arrifano <miknix@gmail.com>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 */
27
28#include <linux/kernel.h>
29#include <linux/init.h>
30#include <linux/module.h>
31#include <linux/interrupt.h>
32#include <linux/platform_device.h>
33#include <linux/i2c.h>
34#include <linux/irq.h>
35#include <linux/spinlock.h>
36#include <linux/htcpld.h>
37#include <linux/gpio.h>
38
39struct htcpld_chip {
40 spinlock_t lock;
41
42 /* chip info */
43 u8 reset;
44 u8 addr;
45 struct device *dev;
46 struct i2c_client *client;
47
48 /* Output details */
49 u8 cache_out;
50 struct gpio_chip chip_out;
51
52 /* Input details */
53 u8 cache_in;
54 struct gpio_chip chip_in;
55
56 u16 irqs_enabled;
57 uint irq_start;
58 int nirqs;
59
60 /*
61 * Work structure to allow for setting values outside of any
62 * possible interrupt context
63 */
64 struct work_struct set_val_work;
65};
66
67struct htcpld_data {
68 /* irq info */
69 u16 irqs_enabled;
70 uint irq_start;
71 int nirqs;
72 uint chained_irq;
73 unsigned int int_reset_gpio_hi;
74 unsigned int int_reset_gpio_lo;
75
76 /* htcpld info */
77 struct htcpld_chip *chip;
78 unsigned int nchips;
79};
80
81/* There does not appear to be a way to proactively mask interrupts
82 * on the htcpld chip itself. So, we simply ignore interrupts that
83 * aren't desired. */
84static void htcpld_mask(unsigned int irq)
85{
86 struct htcpld_chip *chip = get_irq_chip_data(irq);
87 chip->irqs_enabled &= ~(1 << (irq - chip->irq_start));
88 pr_debug("HTCPLD mask %d %04x\n", irq, chip->irqs_enabled);
89}
90static void htcpld_unmask(unsigned int irq)
91{
92 struct htcpld_chip *chip = get_irq_chip_data(irq);
93 chip->irqs_enabled |= 1 << (irq - chip->irq_start);
94 pr_debug("HTCPLD unmask %d %04x\n", irq, chip->irqs_enabled);
95}
96
97static int htcpld_set_type(unsigned int irq, unsigned int flags)
98{
99 struct irq_desc *d = irq_to_desc(irq);
100
101 if (!d) {
102 pr_err("HTCPLD invalid IRQ: %d\n", irq);
103 return -EINVAL;
104 }
105
106 if (flags & ~IRQ_TYPE_SENSE_MASK)
107 return -EINVAL;
108
109 /* We only allow edge triggering */
110 if (flags & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH))
111 return -EINVAL;
112
113 d->status &= ~IRQ_TYPE_SENSE_MASK;
114 d->status |= flags;
115
116 return 0;
117}
118
119static struct irq_chip htcpld_muxed_chip = {
120 .name = "htcpld",
121 .mask = htcpld_mask,
122 .unmask = htcpld_unmask,
123 .set_type = htcpld_set_type,
124};
125
126/* To properly dispatch IRQ events, we need to read from the
127 * chip. This is an I2C action that could possibly sleep
128 * (which is bad in interrupt context) -- so we use a threaded
129 * interrupt handler to get around that.
130 */
131static irqreturn_t htcpld_handler(int irq, void *dev)
132{
133 struct htcpld_data *htcpld = dev;
134 unsigned int i;
135 unsigned long flags;
136 int irqpin;
137 struct irq_desc *desc;
138
139 if (!htcpld) {
140 pr_debug("htcpld is null in ISR\n");
141 return IRQ_HANDLED;
142 }
143
144 /*
145 * For each chip, do a read of the chip and trigger any interrupts
146 * desired. The interrupts will be triggered from LSB to MSB (i.e.
147 * bit 0 first, then bit 1, etc.)
148 *
149 * For chips that have no interrupt range specified, just skip 'em.
150 */
151 for (i = 0; i < htcpld->nchips; i++) {
152 struct htcpld_chip *chip = &htcpld->chip[i];
153 struct i2c_client *client;
154 int val;
155 unsigned long uval, old_val;
156
157 if (!chip) {
158 pr_debug("chip %d is null in ISR\n", i);
159 continue;
160 }
161
162 if (chip->nirqs == 0)
163 continue;
164
165 client = chip->client;
166 if (!client) {
167 pr_debug("client %d is null in ISR\n", i);
168 continue;
169 }
170
171 /* Scan the chip */
172 val = i2c_smbus_read_byte_data(client, chip->cache_out);
173 if (val < 0) {
174 /* Throw a warning and skip this chip */
175 dev_warn(chip->dev, "Unable to read from chip: %d\n",
176 val);
177 continue;
178 }
179
180 uval = (unsigned long)val;
181
182 spin_lock_irqsave(&chip->lock, flags);
183
184 /* Save away the old value so we can compare it */
185 old_val = chip->cache_in;
186
187 /* Write the new value */
188 chip->cache_in = uval;
189
190 spin_unlock_irqrestore(&chip->lock, flags);
191
192 /*
193 * For each bit in the data (starting at bit 0), trigger
194 * associated interrupts.
195 */
196 for (irqpin = 0; irqpin < chip->nirqs; irqpin++) {
197 unsigned oldb, newb;
198 int flags;
199
200 irq = chip->irq_start + irqpin;
201 desc = irq_to_desc(irq);
202 flags = desc->status;
203
204 /* Run the IRQ handler, but only if the bit value
205 * changed, and the proper flags are set */
206 oldb = (old_val >> irqpin) & 1;
207 newb = (uval >> irqpin) & 1;
208
209 if ((!oldb && newb && (flags & IRQ_TYPE_EDGE_RISING)) ||
210 (oldb && !newb &&
211 (flags & IRQ_TYPE_EDGE_FALLING))) {
212 pr_debug("fire IRQ %d\n", irqpin);
213 desc->handle_irq(irq, desc);
214 }
215 }
216 }
217
218 /*
219 * In order to continue receiving interrupts, the int_reset_gpio must
220 * be asserted.
221 */
222 if (htcpld->int_reset_gpio_hi)
223 gpio_set_value(htcpld->int_reset_gpio_hi, 1);
224 if (htcpld->int_reset_gpio_lo)
225 gpio_set_value(htcpld->int_reset_gpio_lo, 0);
226
227 return IRQ_HANDLED;
228}
229
230/*
231 * The GPIO set routines can be called from interrupt context, especially if,
232 * for example they're attached to the led-gpio framework and a trigger is
233 * enabled. As such, we declared work above in the htcpld_chip structure,
234 * and that work is scheduled in the set routine. The kernel can then run
235 * the I2C functions, which will sleep, in process context.
236 */
237void htcpld_chip_set(struct gpio_chip *chip, unsigned offset, int val)
238{
239 struct i2c_client *client;
240 struct htcpld_chip *chip_data;
241 unsigned long flags;
242
243 chip_data = container_of(chip, struct htcpld_chip, chip_out);
244 if (!chip_data)
245 return;
246
247 client = chip_data->client;
248 if (client == NULL)
249 return;
250
251 spin_lock_irqsave(&chip_data->lock, flags);
252 if (val)
253 chip_data->cache_out |= (1 << offset);
254 else
255 chip_data->cache_out &= ~(1 << offset);
256 spin_unlock_irqrestore(&chip_data->lock, flags);
257
258 schedule_work(&(chip_data->set_val_work));
259}
260
261void htcpld_chip_set_ni(struct work_struct *work)
262{
263 struct htcpld_chip *chip_data;
264 struct i2c_client *client;
265
266 chip_data = container_of(work, struct htcpld_chip, set_val_work);
267 client = chip_data->client;
268 i2c_smbus_read_byte_data(client, chip_data->cache_out);
269}
270
271int htcpld_chip_get(struct gpio_chip *chip, unsigned offset)
272{
273 struct htcpld_chip *chip_data;
274 int val = 0;
275 int is_input = 0;
276
277 /* Try out first */
278 chip_data = container_of(chip, struct htcpld_chip, chip_out);
279 if (!chip_data) {
280 /* Try in */
281 is_input = 1;
282 chip_data = container_of(chip, struct htcpld_chip, chip_in);
283 if (!chip_data)
284 return -EINVAL;
285 }
286
287 /* Determine if this is an input or output GPIO */
288 if (!is_input)
289 /* Use the output cache */
290 val = (chip_data->cache_out >> offset) & 1;
291 else
292 /* Use the input cache */
293 val = (chip_data->cache_in >> offset) & 1;
294
295 if (val)
296 return 1;
297 else
298 return 0;
299}
300
301static int htcpld_direction_output(struct gpio_chip *chip,
302 unsigned offset, int value)
303{
304 htcpld_chip_set(chip, offset, value);
305 return 0;
306}
307
308static int htcpld_direction_input(struct gpio_chip *chip,
309 unsigned offset)
310{
311 /*
312 * No-op: this function can only be called on the input chip.
313 * We do however make sure the offset is within range.
314 */
315 return (offset < chip->ngpio) ? 0 : -EINVAL;
316}
317
318int htcpld_chip_to_irq(struct gpio_chip *chip, unsigned offset)
319{
320 struct htcpld_chip *chip_data;
321
322 chip_data = container_of(chip, struct htcpld_chip, chip_in);
323
324 if (offset < chip_data->nirqs)
325 return chip_data->irq_start + offset;
326 else
327 return -EINVAL;
328}
329
330void htcpld_chip_reset(struct i2c_client *client)
331{
332 struct htcpld_chip *chip_data = i2c_get_clientdata(client);
333 if (!chip_data)
334 return;
335
336 i2c_smbus_read_byte_data(
337 client, (chip_data->cache_out = chip_data->reset));
338}
339
340static int __devinit htcpld_setup_chip_irq(
341 struct platform_device *pdev,
342 int chip_index)
343{
344 struct htcpld_data *htcpld;
345 struct device *dev = &pdev->dev;
346 struct htcpld_core_platform_data *pdata;
347 struct htcpld_chip *chip;
348 struct htcpld_chip_platform_data *plat_chip_data;
349 unsigned int irq, irq_end;
350 int ret = 0;
351
352 /* Get the platform and driver data */
353 pdata = dev->platform_data;
354 htcpld = platform_get_drvdata(pdev);
355 chip = &htcpld->chip[chip_index];
356 plat_chip_data = &pdata->chip[chip_index];
357
358 /* Setup irq handlers */
359 irq_end = chip->irq_start + chip->nirqs;
360 for (irq = chip->irq_start; irq < irq_end; irq++) {
361 set_irq_chip(irq, &htcpld_muxed_chip);
362 set_irq_chip_data(irq, chip);
363 set_irq_handler(irq, handle_simple_irq);
364#ifdef CONFIG_ARM
365 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
366#else
367 set_irq_probe(irq);
368#endif
369 }
370
371 return ret;
372}
373
374static int __devinit htcpld_register_chip_i2c(
375 struct platform_device *pdev,
376 int chip_index)
377{
378 struct htcpld_data *htcpld;
379 struct device *dev = &pdev->dev;
380 struct htcpld_core_platform_data *pdata;
381 struct htcpld_chip *chip;
382 struct htcpld_chip_platform_data *plat_chip_data;
383 struct i2c_adapter *adapter;
384 struct i2c_client *client;
385 struct i2c_board_info info;
386
387 /* Get the platform and driver data */
388 pdata = dev->platform_data;
389 htcpld = platform_get_drvdata(pdev);
390 chip = &htcpld->chip[chip_index];
391 plat_chip_data = &pdata->chip[chip_index];
392
393 adapter = i2c_get_adapter(pdata->i2c_adapter_id);
394 if (adapter == NULL) {
395 /* Eek, no such I2C adapter! Bail out. */
396 dev_warn(dev, "Chip at i2c address 0x%x: Invalid i2c adapter %d\n",
397 plat_chip_data->addr, pdata->i2c_adapter_id);
398 return -ENODEV;
399 }
400
401 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
402 dev_warn(dev, "i2c adapter %d non-functional\n",
403 pdata->i2c_adapter_id);
404 return -EINVAL;
405 }
406
407 memset(&info, 0, sizeof(struct i2c_board_info));
408 info.addr = plat_chip_data->addr;
409 strlcpy(info.type, "htcpld-chip", I2C_NAME_SIZE);
410 info.platform_data = chip;
411
412 /* Add the I2C device. This calls the probe() function. */
413 client = i2c_new_device(adapter, &info);
414 if (!client) {
415 /* I2C device registration failed, contineu with the next */
416 dev_warn(dev, "Unable to add I2C device for 0x%x\n",
417 plat_chip_data->addr);
418 return -ENODEV;
419 }
420
421 i2c_set_clientdata(client, chip);
422 snprintf(client->name, I2C_NAME_SIZE, "Chip_0x%d", client->addr);
423 chip->client = client;
424
425 /* Reset the chip */
426 htcpld_chip_reset(client);
427 chip->cache_in = i2c_smbus_read_byte_data(client, chip->cache_out);
428
429 return 0;
430}
431
432static void __devinit htcpld_unregister_chip_i2c(
433 struct platform_device *pdev,
434 int chip_index)
435{
436 struct htcpld_data *htcpld;
437 struct htcpld_chip *chip;
438
439 /* Get the platform and driver data */
440 htcpld = platform_get_drvdata(pdev);
441 chip = &htcpld->chip[chip_index];
442
443 if (chip->client)
444 i2c_unregister_device(chip->client);
445}
446
447static int __devinit htcpld_register_chip_gpio(
448 struct platform_device *pdev,
449 int chip_index)
450{
451 struct htcpld_data *htcpld;
452 struct device *dev = &pdev->dev;
453 struct htcpld_core_platform_data *pdata;
454 struct htcpld_chip *chip;
455 struct htcpld_chip_platform_data *plat_chip_data;
456 struct gpio_chip *gpio_chip;
457 int ret = 0;
458
459 /* Get the platform and driver data */
460 pdata = dev->platform_data;
461 htcpld = platform_get_drvdata(pdev);
462 chip = &htcpld->chip[chip_index];
463 plat_chip_data = &pdata->chip[chip_index];
464
465 /* Setup the GPIO chips */
466 gpio_chip = &(chip->chip_out);
467 gpio_chip->label = "htcpld-out";
468 gpio_chip->dev = dev;
469 gpio_chip->owner = THIS_MODULE;
470 gpio_chip->get = htcpld_chip_get;
471 gpio_chip->set = htcpld_chip_set;
472 gpio_chip->direction_input = NULL;
473 gpio_chip->direction_output = htcpld_direction_output;
474 gpio_chip->base = plat_chip_data->gpio_out_base;
475 gpio_chip->ngpio = plat_chip_data->num_gpios;
476
477 gpio_chip = &(chip->chip_in);
478 gpio_chip->label = "htcpld-in";
479 gpio_chip->dev = dev;
480 gpio_chip->owner = THIS_MODULE;
481 gpio_chip->get = htcpld_chip_get;
482 gpio_chip->set = NULL;
483 gpio_chip->direction_input = htcpld_direction_input;
484 gpio_chip->direction_output = NULL;
485 gpio_chip->to_irq = htcpld_chip_to_irq;
486 gpio_chip->base = plat_chip_data->gpio_in_base;
487 gpio_chip->ngpio = plat_chip_data->num_gpios;
488
489 /* Add the GPIO chips */
490 ret = gpiochip_add(&(chip->chip_out));
491 if (ret) {
492 dev_warn(dev, "Unable to register output GPIOs for 0x%x: %d\n",
493 plat_chip_data->addr, ret);
494 return ret;
495 }
496
497 ret = gpiochip_add(&(chip->chip_in));
498 if (ret) {
499 int error;
500
501 dev_warn(dev, "Unable to register input GPIOs for 0x%x: %d\n",
502 plat_chip_data->addr, ret);
503
504 error = gpiochip_remove(&(chip->chip_out));
505 if (error)
506 dev_warn(dev, "Error while trying to unregister gpio chip: %d\n", error);
507
508 return ret;
509 }
510
511 return 0;
512}
513
514static int __devinit htcpld_setup_chips(struct platform_device *pdev)
515{
516 struct htcpld_data *htcpld;
517 struct device *dev = &pdev->dev;
518 struct htcpld_core_platform_data *pdata;
519 int i;
520
521 /* Get the platform and driver data */
522 pdata = dev->platform_data;
523 htcpld = platform_get_drvdata(pdev);
524
525 /* Setup each chip's output GPIOs */
526 htcpld->nchips = pdata->num_chip;
527 htcpld->chip = kzalloc(sizeof(struct htcpld_chip) * htcpld->nchips,
528 GFP_KERNEL);
529 if (!htcpld->chip) {
530 dev_warn(dev, "Unable to allocate memory for chips\n");
531 return -ENOMEM;
532 }
533
534 /* Add the chips as best we can */
535 for (i = 0; i < htcpld->nchips; i++) {
536 int ret;
537
538 /* Setup the HTCPLD chips */
539 htcpld->chip[i].reset = pdata->chip[i].reset;
540 htcpld->chip[i].cache_out = pdata->chip[i].reset;
541 htcpld->chip[i].cache_in = 0;
542 htcpld->chip[i].dev = dev;
543 htcpld->chip[i].irq_start = pdata->chip[i].irq_base;
544 htcpld->chip[i].nirqs = pdata->chip[i].num_irqs;
545
546 INIT_WORK(&(htcpld->chip[i].set_val_work), &htcpld_chip_set_ni);
547 spin_lock_init(&(htcpld->chip[i].lock));
548
549 /* Setup the interrupts for the chip */
550 if (htcpld->chained_irq) {
551 ret = htcpld_setup_chip_irq(pdev, i);
552 if (ret)
553 continue;
554 }
555
556 /* Register the chip with I2C */
557 ret = htcpld_register_chip_i2c(pdev, i);
558 if (ret)
559 continue;
560
561
562 /* Register the chips with the GPIO subsystem */
563 ret = htcpld_register_chip_gpio(pdev, i);
564 if (ret) {
565 /* Unregister the chip from i2c and continue */
566 htcpld_unregister_chip_i2c(pdev, i);
567 continue;
568 }
569
570 dev_info(dev, "Registered chip at 0x%x\n", pdata->chip[i].addr);
571 }
572
573 return 0;
574}
575
576static int __devinit htcpld_core_probe(struct platform_device *pdev)
577{
578 struct htcpld_data *htcpld;
579 struct device *dev = &pdev->dev;
580 struct htcpld_core_platform_data *pdata;
581 struct resource *res;
582 int ret = 0;
583
584 if (!dev)
585 return -ENODEV;
586
587 pdata = dev->platform_data;
588 if (!pdata) {
589 dev_warn(dev, "Platform data not found for htcpld core!\n");
590 return -ENXIO;
591 }
592
593 htcpld = kzalloc(sizeof(struct htcpld_data), GFP_KERNEL);
594 if (!htcpld)
595 return -ENOMEM;
596
597 /* Find chained irq */
598 ret = -EINVAL;
599 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
600 if (res) {
601 int flags;
602 htcpld->chained_irq = res->start;
603
604 /* Setup the chained interrupt handler */
605 flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
606 ret = request_threaded_irq(htcpld->chained_irq,
607 NULL, htcpld_handler,
608 flags, pdev->name, htcpld);
609 if (ret) {
610 dev_warn(dev, "Unable to setup chained irq handler: %d\n", ret);
611 goto fail;
612 } else
613 device_init_wakeup(dev, 0);
614 }
615
616 /* Set the driver data */
617 platform_set_drvdata(pdev, htcpld);
618
619 /* Setup the htcpld chips */
620 ret = htcpld_setup_chips(pdev);
621 if (ret)
622 goto fail;
623
624 /* Request the GPIO(s) for the int reset and set them up */
625 if (pdata->int_reset_gpio_hi) {
626 ret = gpio_request(pdata->int_reset_gpio_hi, "htcpld-core");
627 if (ret) {
628 /*
629 * If it failed, that sucks, but we can probably
630 * continue on without it.
631 */
632 dev_warn(dev, "Unable to request int_reset_gpio_hi -- interrupts may not work\n");
633 htcpld->int_reset_gpio_hi = 0;
634 } else {
635 htcpld->int_reset_gpio_hi = pdata->int_reset_gpio_hi;
636 gpio_set_value(htcpld->int_reset_gpio_hi, 1);
637 }
638 }
639
640 if (pdata->int_reset_gpio_lo) {
641 ret = gpio_request(pdata->int_reset_gpio_lo, "htcpld-core");
642 if (ret) {
643 /*
644 * If it failed, that sucks, but we can probably
645 * continue on without it.
646 */
647 dev_warn(dev, "Unable to request int_reset_gpio_lo -- interrupts may not work\n");
648 htcpld->int_reset_gpio_lo = 0;
649 } else {
650 htcpld->int_reset_gpio_lo = pdata->int_reset_gpio_lo;
651 gpio_set_value(htcpld->int_reset_gpio_lo, 0);
652 }
653 }
654
655 dev_info(dev, "Initialized successfully\n");
656 return 0;
657
658fail:
659 kfree(htcpld);
660 return ret;
661}
662
663/* The I2C Driver -- used internally */
664static const struct i2c_device_id htcpld_chip_id[] = {
665 { "htcpld-chip", 0 },
666 { }
667};
668MODULE_DEVICE_TABLE(i2c, htcpld_chip_id);
669
670
671static struct i2c_driver htcpld_chip_driver = {
672 .driver = {
673 .name = "htcpld-chip",
674 },
675 .id_table = htcpld_chip_id,
676};
677
678/* The Core Driver */
679static struct platform_driver htcpld_core_driver = {
680 .driver = {
681 .name = "i2c-htcpld",
682 },
683};
684
685static int __init htcpld_core_init(void)
686{
687 int ret;
688
689 /* Register the I2C Chip driver */
690 ret = i2c_add_driver(&htcpld_chip_driver);
691 if (ret)
692 return ret;
693
694 /* Probe for our chips */
695 return platform_driver_probe(&htcpld_core_driver, htcpld_core_probe);
696}
697
698static void __exit htcpld_core_exit(void)
699{
700 i2c_del_driver(&htcpld_chip_driver);
701 platform_driver_unregister(&htcpld_core_driver);
702}
703
704module_init(htcpld_core_init);
705module_exit(htcpld_core_exit);
706
707MODULE_AUTHOR("Cory Maccarrone <darkstar6262@gmail.com>");
708MODULE_DESCRIPTION("I2C HTC PLD Driver");
709MODULE_LICENSE("GPL");
710
diff --git a/include/linux/htcpld.h b/include/linux/htcpld.h
new file mode 100644
index 000000000000..ab3f6cb4dddc
--- /dev/null
+++ b/include/linux/htcpld.h
@@ -0,0 +1,24 @@
1#ifndef __LINUX_HTCPLD_H
2#define __LINUX_HTCPLD_H
3
4struct htcpld_chip_platform_data {
5 unsigned int addr;
6 unsigned int reset;
7 unsigned int num_gpios;
8 unsigned int gpio_out_base;
9 unsigned int gpio_in_base;
10 unsigned int irq_base;
11 unsigned int num_irqs;
12};
13
14struct htcpld_core_platform_data {
15 unsigned int int_reset_gpio_hi;
16 unsigned int int_reset_gpio_lo;
17 unsigned int i2c_adapter_id;
18
19 struct htcpld_chip_platform_data *chip;
20 unsigned int num_chip;
21};
22
23#endif /* __LINUX_HTCPLD_H */
24