| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) ST-Ericsson SA 2010 | 
|  | 3 | * | 
|  | 4 | * License Terms: GNU General Public License, version 2 | 
|  | 5 | * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson | 
|  | 6 | * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson | 
|  | 7 | */ | 
|  | 8 |  | 
|  | 9 | #include <linux/module.h> | 
|  | 10 | #include <linux/interrupt.h> | 
|  | 11 | #include <linux/irq.h> | 
|  | 12 | #include <linux/slab.h> | 
|  | 13 | #include <linux/i2c.h> | 
|  | 14 | #include <linux/mfd/core.h> | 
| Sundar Iyer | c6eda6c | 2010-12-13 09:33:12 +0530 | [diff] [blame] | 15 | #include <linux/mfd/tc3589x.h> | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 16 |  | 
| Sundar Iyer | 593e9d7 | 2010-12-13 09:33:18 +0530 | [diff] [blame] | 17 | #define TC3589x_CLKMODE_MODCTL_SLEEP		0x0 | 
|  | 18 | #define TC3589x_CLKMODE_MODCTL_OPERATION	(1 << 0) | 
|  | 19 |  | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 20 | /** | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 21 | * tc3589x_reg_read() - read a single TC3589x register | 
|  | 22 | * @tc3589x:	Device to read from | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 23 | * @reg:	Register to read | 
|  | 24 | */ | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 25 | int tc3589x_reg_read(struct tc3589x *tc3589x, u8 reg) | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 26 | { | 
|  | 27 | int ret; | 
|  | 28 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 29 | ret = i2c_smbus_read_byte_data(tc3589x->i2c, reg); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 30 | if (ret < 0) | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 31 | dev_err(tc3589x->dev, "failed to read reg %#x: %d\n", | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 32 | reg, ret); | 
|  | 33 |  | 
|  | 34 | return ret; | 
|  | 35 | } | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 36 | EXPORT_SYMBOL_GPL(tc3589x_reg_read); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 37 |  | 
|  | 38 | /** | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 39 | * tc3589x_reg_read() - write a single TC3589x register | 
|  | 40 | * @tc3589x:	Device to write to | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 41 | * @reg:	Register to read | 
|  | 42 | * @data:	Value to write | 
|  | 43 | */ | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 44 | int tc3589x_reg_write(struct tc3589x *tc3589x, u8 reg, u8 data) | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 45 | { | 
|  | 46 | int ret; | 
|  | 47 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 48 | ret = i2c_smbus_write_byte_data(tc3589x->i2c, reg, data); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 49 | if (ret < 0) | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 50 | dev_err(tc3589x->dev, "failed to write reg %#x: %d\n", | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 51 | reg, ret); | 
|  | 52 |  | 
|  | 53 | return ret; | 
|  | 54 | } | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 55 | EXPORT_SYMBOL_GPL(tc3589x_reg_write); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 56 |  | 
|  | 57 | /** | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 58 | * tc3589x_block_read() - read multiple TC3589x registers | 
|  | 59 | * @tc3589x:	Device to read from | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 60 | * @reg:	First register | 
|  | 61 | * @length:	Number of registers | 
|  | 62 | * @values:	Buffer to write to | 
|  | 63 | */ | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 64 | int tc3589x_block_read(struct tc3589x *tc3589x, u8 reg, u8 length, u8 *values) | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 65 | { | 
|  | 66 | int ret; | 
|  | 67 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 68 | ret = i2c_smbus_read_i2c_block_data(tc3589x->i2c, reg, length, values); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 69 | if (ret < 0) | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 70 | dev_err(tc3589x->dev, "failed to read regs %#x: %d\n", | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 71 | reg, ret); | 
|  | 72 |  | 
|  | 73 | return ret; | 
|  | 74 | } | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 75 | EXPORT_SYMBOL_GPL(tc3589x_block_read); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 76 |  | 
|  | 77 | /** | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 78 | * tc3589x_block_write() - write multiple TC3589x registers | 
|  | 79 | * @tc3589x:	Device to write to | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 80 | * @reg:	First register | 
|  | 81 | * @length:	Number of registers | 
|  | 82 | * @values:	Values to write | 
|  | 83 | */ | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 84 | int tc3589x_block_write(struct tc3589x *tc3589x, u8 reg, u8 length, | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 85 | const u8 *values) | 
|  | 86 | { | 
|  | 87 | int ret; | 
|  | 88 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 89 | ret = i2c_smbus_write_i2c_block_data(tc3589x->i2c, reg, length, | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 90 | values); | 
|  | 91 | if (ret < 0) | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 92 | dev_err(tc3589x->dev, "failed to write regs %#x: %d\n", | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 93 | reg, ret); | 
|  | 94 |  | 
|  | 95 | return ret; | 
|  | 96 | } | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 97 | EXPORT_SYMBOL_GPL(tc3589x_block_write); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 98 |  | 
|  | 99 | /** | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 100 | * tc3589x_set_bits() - set the value of a bitfield in a TC3589x register | 
|  | 101 | * @tc3589x:	Device to write to | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 102 | * @reg:	Register to write | 
|  | 103 | * @mask:	Mask of bits to set | 
|  | 104 | * @values:	Value to set | 
|  | 105 | */ | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 106 | int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val) | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 107 | { | 
|  | 108 | int ret; | 
|  | 109 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 110 | mutex_lock(&tc3589x->lock); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 111 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 112 | ret = tc3589x_reg_read(tc3589x, reg); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 113 | if (ret < 0) | 
|  | 114 | goto out; | 
|  | 115 |  | 
|  | 116 | ret &= ~mask; | 
|  | 117 | ret |= val; | 
|  | 118 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 119 | ret = tc3589x_reg_write(tc3589x, reg, ret); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 120 |  | 
|  | 121 | out: | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 122 | mutex_unlock(&tc3589x->lock); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 123 | return ret; | 
|  | 124 | } | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 125 | EXPORT_SYMBOL_GPL(tc3589x_set_bits); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 126 |  | 
|  | 127 | static struct resource gpio_resources[] = { | 
|  | 128 | { | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 129 | .start	= TC3589x_INT_GPIIRQ, | 
|  | 130 | .end	= TC3589x_INT_GPIIRQ, | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 131 | .flags	= IORESOURCE_IRQ, | 
|  | 132 | }, | 
|  | 133 | }; | 
|  | 134 |  | 
| Sundar Iyer | 09c730a | 2010-12-21 15:53:31 +0530 | [diff] [blame] | 135 | static struct resource keypad_resources[] = { | 
|  | 136 | { | 
|  | 137 | .start  = TC3589x_INT_KBDIRQ, | 
|  | 138 | .end    = TC3589x_INT_KBDIRQ, | 
|  | 139 | .flags  = IORESOURCE_IRQ, | 
|  | 140 | }, | 
|  | 141 | }; | 
|  | 142 |  | 
| Sundar Iyer | 611b759 | 2010-12-13 09:33:15 +0530 | [diff] [blame] | 143 | static struct mfd_cell tc3589x_dev_gpio[] = { | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 144 | { | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 145 | .name		= "tc3589x-gpio", | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 146 | .num_resources	= ARRAY_SIZE(gpio_resources), | 
|  | 147 | .resources	= &gpio_resources[0], | 
|  | 148 | }, | 
|  | 149 | }; | 
|  | 150 |  | 
| Sundar Iyer | 09c730a | 2010-12-21 15:53:31 +0530 | [diff] [blame] | 151 | static struct mfd_cell tc3589x_dev_keypad[] = { | 
|  | 152 | { | 
|  | 153 | .name           = "tc3589x-keypad", | 
|  | 154 | .num_resources  = ARRAY_SIZE(keypad_resources), | 
|  | 155 | .resources      = &keypad_resources[0], | 
|  | 156 | }, | 
|  | 157 | }; | 
|  | 158 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 159 | static irqreturn_t tc3589x_irq(int irq, void *data) | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 160 | { | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 161 | struct tc3589x *tc3589x = data; | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 162 | int status; | 
|  | 163 |  | 
| Sundar Iyer | bd77efd | 2010-12-13 09:33:16 +0530 | [diff] [blame] | 164 | again: | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 165 | status = tc3589x_reg_read(tc3589x, TC3589x_IRQST); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 166 | if (status < 0) | 
|  | 167 | return IRQ_NONE; | 
|  | 168 |  | 
|  | 169 | while (status) { | 
|  | 170 | int bit = __ffs(status); | 
|  | 171 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 172 | handle_nested_irq(tc3589x->irq_base + bit); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 173 | status &= ~(1 << bit); | 
|  | 174 | } | 
|  | 175 |  | 
|  | 176 | /* | 
|  | 177 | * A dummy read or write (to any register) appears to be necessary to | 
|  | 178 | * have the last interrupt clear (for example, GPIO IC write) take | 
| Sundar Iyer | bd77efd | 2010-12-13 09:33:16 +0530 | [diff] [blame] | 179 | * effect. In such a case, recheck for any interrupt which is still | 
|  | 180 | * pending. | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 181 | */ | 
| Sundar Iyer | bd77efd | 2010-12-13 09:33:16 +0530 | [diff] [blame] | 182 | status = tc3589x_reg_read(tc3589x, TC3589x_IRQST); | 
|  | 183 | if (status) | 
|  | 184 | goto again; | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 185 |  | 
|  | 186 | return IRQ_HANDLED; | 
|  | 187 | } | 
|  | 188 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 189 | static int tc3589x_irq_init(struct tc3589x *tc3589x) | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 190 | { | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 191 | int base = tc3589x->irq_base; | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 192 | int irq; | 
|  | 193 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 194 | for (irq = base; irq < base + TC3589x_NR_INTERNAL_IRQS; irq++) { | 
| Thomas Gleixner | d5bb122 | 2011-03-25 11:12:32 +0000 | [diff] [blame] | 195 | irq_set_chip_data(irq, tc3589x); | 
|  | 196 | irq_set_chip_and_handler(irq, &dummy_irq_chip, | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 197 | handle_edge_irq); | 
| Thomas Gleixner | d5bb122 | 2011-03-25 11:12:32 +0000 | [diff] [blame] | 198 | irq_set_nested_thread(irq, 1); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 199 | #ifdef CONFIG_ARM | 
|  | 200 | set_irq_flags(irq, IRQF_VALID); | 
|  | 201 | #else | 
| Thomas Gleixner | d5bb122 | 2011-03-25 11:12:32 +0000 | [diff] [blame] | 202 | irq_set_noprobe(irq); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 203 | #endif | 
|  | 204 | } | 
|  | 205 |  | 
|  | 206 | return 0; | 
|  | 207 | } | 
|  | 208 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 209 | static void tc3589x_irq_remove(struct tc3589x *tc3589x) | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 210 | { | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 211 | int base = tc3589x->irq_base; | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 212 | int irq; | 
|  | 213 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 214 | for (irq = base; irq < base + TC3589x_NR_INTERNAL_IRQS; irq++) { | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 215 | #ifdef CONFIG_ARM | 
|  | 216 | set_irq_flags(irq, 0); | 
|  | 217 | #endif | 
| Thomas Gleixner | d5bb122 | 2011-03-25 11:12:32 +0000 | [diff] [blame] | 218 | irq_set_chip_and_handler(irq, NULL, NULL); | 
|  | 219 | irq_set_chip_data(irq, NULL); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 220 | } | 
|  | 221 | } | 
|  | 222 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 223 | static int tc3589x_chip_init(struct tc3589x *tc3589x) | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 224 | { | 
|  | 225 | int manf, ver, ret; | 
|  | 226 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 227 | manf = tc3589x_reg_read(tc3589x, TC3589x_MANFCODE); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 228 | if (manf < 0) | 
|  | 229 | return manf; | 
|  | 230 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 231 | ver = tc3589x_reg_read(tc3589x, TC3589x_VERSION); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 232 | if (ver < 0) | 
|  | 233 | return ver; | 
|  | 234 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 235 | if (manf != TC3589x_MANFCODE_MAGIC) { | 
|  | 236 | dev_err(tc3589x->dev, "unknown manufacturer: %#x\n", manf); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 237 | return -EINVAL; | 
|  | 238 | } | 
|  | 239 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 240 | dev_info(tc3589x->dev, "manufacturer: %#x, version: %#x\n", manf, ver); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 241 |  | 
| Sundar Iyer | 523bc38 | 2010-12-13 09:33:17 +0530 | [diff] [blame] | 242 | /* | 
|  | 243 | * Put everything except the IRQ module into reset; | 
|  | 244 | * also spare the GPIO module for any pin initialization | 
|  | 245 | * done during pre-kernel boot | 
|  | 246 | */ | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 247 | ret = tc3589x_reg_write(tc3589x, TC3589x_RSTCTRL, | 
|  | 248 | TC3589x_RSTCTRL_TIMRST | 
|  | 249 | | TC3589x_RSTCTRL_ROTRST | 
| Sundar Iyer | 523bc38 | 2010-12-13 09:33:17 +0530 | [diff] [blame] | 250 | | TC3589x_RSTCTRL_KBDRST); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 251 | if (ret < 0) | 
|  | 252 | return ret; | 
|  | 253 |  | 
|  | 254 | /* Clear the reset interrupt. */ | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 255 | return tc3589x_reg_write(tc3589x, TC3589x_RSTINTCLR, 0x1); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 256 | } | 
|  | 257 |  | 
| Sundar Iyer | 611b759 | 2010-12-13 09:33:15 +0530 | [diff] [blame] | 258 | static int __devinit tc3589x_device_init(struct tc3589x *tc3589x) | 
|  | 259 | { | 
|  | 260 | int ret = 0; | 
|  | 261 | unsigned int blocks = tc3589x->pdata->block; | 
|  | 262 |  | 
|  | 263 | if (blocks & TC3589x_BLOCK_GPIO) { | 
|  | 264 | ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio, | 
|  | 265 | ARRAY_SIZE(tc3589x_dev_gpio), NULL, | 
|  | 266 | tc3589x->irq_base); | 
|  | 267 | if (ret) { | 
|  | 268 | dev_err(tc3589x->dev, "failed to add gpio child\n"); | 
|  | 269 | return ret; | 
|  | 270 | } | 
|  | 271 | dev_info(tc3589x->dev, "added gpio block\n"); | 
|  | 272 | } | 
|  | 273 |  | 
| Sundar Iyer | 09c730a | 2010-12-21 15:53:31 +0530 | [diff] [blame] | 274 | if (blocks & TC3589x_BLOCK_KEYPAD) { | 
|  | 275 | ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad, | 
|  | 276 | ARRAY_SIZE(tc3589x_dev_keypad), NULL, | 
|  | 277 | tc3589x->irq_base); | 
|  | 278 | if (ret) { | 
|  | 279 | dev_err(tc3589x->dev, "failed to keypad child\n"); | 
|  | 280 | return ret; | 
|  | 281 | } | 
|  | 282 | dev_info(tc3589x->dev, "added keypad block\n"); | 
|  | 283 | } | 
| Sundar Iyer | 611b759 | 2010-12-13 09:33:15 +0530 | [diff] [blame] | 284 |  | 
| Sundar Iyer | 09c730a | 2010-12-21 15:53:31 +0530 | [diff] [blame] | 285 | return ret; | 
| Sundar Iyer | 611b759 | 2010-12-13 09:33:15 +0530 | [diff] [blame] | 286 | } | 
|  | 287 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 288 | static int __devinit tc3589x_probe(struct i2c_client *i2c, | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 289 | const struct i2c_device_id *id) | 
|  | 290 | { | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 291 | struct tc3589x_platform_data *pdata = i2c->dev.platform_data; | 
|  | 292 | struct tc3589x *tc3589x; | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 293 | int ret; | 
|  | 294 |  | 
|  | 295 | if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA | 
|  | 296 | | I2C_FUNC_SMBUS_I2C_BLOCK)) | 
|  | 297 | return -EIO; | 
|  | 298 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 299 | tc3589x = kzalloc(sizeof(struct tc3589x), GFP_KERNEL); | 
|  | 300 | if (!tc3589x) | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 301 | return -ENOMEM; | 
|  | 302 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 303 | mutex_init(&tc3589x->lock); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 304 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 305 | tc3589x->dev = &i2c->dev; | 
|  | 306 | tc3589x->i2c = i2c; | 
|  | 307 | tc3589x->pdata = pdata; | 
|  | 308 | tc3589x->irq_base = pdata->irq_base; | 
|  | 309 | tc3589x->num_gpio = id->driver_data; | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 310 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 311 | i2c_set_clientdata(i2c, tc3589x); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 312 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 313 | ret = tc3589x_chip_init(tc3589x); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 314 | if (ret) | 
|  | 315 | goto out_free; | 
|  | 316 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 317 | ret = tc3589x_irq_init(tc3589x); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 318 | if (ret) | 
|  | 319 | goto out_free; | 
|  | 320 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 321 | ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq, | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 322 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 323 | "tc3589x", tc3589x); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 324 | if (ret) { | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 325 | dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 326 | goto out_removeirq; | 
|  | 327 | } | 
|  | 328 |  | 
| Sundar Iyer | 611b759 | 2010-12-13 09:33:15 +0530 | [diff] [blame] | 329 | ret = tc3589x_device_init(tc3589x); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 330 | if (ret) { | 
| Sundar Iyer | 611b759 | 2010-12-13 09:33:15 +0530 | [diff] [blame] | 331 | dev_err(tc3589x->dev, "failed to add child devices\n"); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 332 | goto out_freeirq; | 
|  | 333 | } | 
|  | 334 |  | 
|  | 335 | return 0; | 
|  | 336 |  | 
|  | 337 | out_freeirq: | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 338 | free_irq(tc3589x->i2c->irq, tc3589x); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 339 | out_removeirq: | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 340 | tc3589x_irq_remove(tc3589x); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 341 | out_free: | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 342 | kfree(tc3589x); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 343 | return ret; | 
|  | 344 | } | 
|  | 345 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 346 | static int __devexit tc3589x_remove(struct i2c_client *client) | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 347 | { | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 348 | struct tc3589x *tc3589x = i2c_get_clientdata(client); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 349 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 350 | mfd_remove_devices(tc3589x->dev); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 351 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 352 | free_irq(tc3589x->i2c->irq, tc3589x); | 
|  | 353 | tc3589x_irq_remove(tc3589x); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 354 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 355 | kfree(tc3589x); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 356 |  | 
|  | 357 | return 0; | 
|  | 358 | } | 
|  | 359 |  | 
| Linus Walleij | 54d8e2c | 2011-08-09 20:37:17 +0200 | [diff] [blame] | 360 | #ifdef CONFIG_PM | 
| Sundar Iyer | 593e9d7 | 2010-12-13 09:33:18 +0530 | [diff] [blame] | 361 | static int tc3589x_suspend(struct device *dev) | 
|  | 362 | { | 
|  | 363 | struct tc3589x *tc3589x = dev_get_drvdata(dev); | 
|  | 364 | struct i2c_client *client = tc3589x->i2c; | 
|  | 365 | int ret = 0; | 
|  | 366 |  | 
|  | 367 | /* put the system to sleep mode */ | 
|  | 368 | if (!device_may_wakeup(&client->dev)) | 
|  | 369 | ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE, | 
|  | 370 | TC3589x_CLKMODE_MODCTL_SLEEP); | 
|  | 371 |  | 
|  | 372 | return ret; | 
|  | 373 | } | 
|  | 374 |  | 
|  | 375 | static int tc3589x_resume(struct device *dev) | 
|  | 376 | { | 
|  | 377 | struct tc3589x *tc3589x = dev_get_drvdata(dev); | 
|  | 378 | struct i2c_client *client = tc3589x->i2c; | 
|  | 379 | int ret = 0; | 
|  | 380 |  | 
|  | 381 | /* enable the system into operation */ | 
|  | 382 | if (!device_may_wakeup(&client->dev)) | 
|  | 383 | ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE, | 
|  | 384 | TC3589x_CLKMODE_MODCTL_OPERATION); | 
|  | 385 |  | 
|  | 386 | return ret; | 
|  | 387 | } | 
|  | 388 |  | 
|  | 389 | static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, | 
|  | 390 | tc3589x_resume); | 
| Linus Walleij | 54d8e2c | 2011-08-09 20:37:17 +0200 | [diff] [blame] | 391 | #endif | 
| Sundar Iyer | 593e9d7 | 2010-12-13 09:33:18 +0530 | [diff] [blame] | 392 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 393 | static const struct i2c_device_id tc3589x_id[] = { | 
|  | 394 | { "tc3589x", 24 }, | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 395 | { } | 
|  | 396 | }; | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 397 | MODULE_DEVICE_TABLE(i2c, tc3589x_id); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 398 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 399 | static struct i2c_driver tc3589x_driver = { | 
|  | 400 | .driver.name	= "tc3589x", | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 401 | .driver.owner	= THIS_MODULE, | 
| Sundar Iyer | 593e9d7 | 2010-12-13 09:33:18 +0530 | [diff] [blame] | 402 | #ifdef CONFIG_PM | 
|  | 403 | .driver.pm	= &tc3589x_dev_pm_ops, | 
|  | 404 | #endif | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 405 | .probe		= tc3589x_probe, | 
|  | 406 | .remove		= __devexit_p(tc3589x_remove), | 
|  | 407 | .id_table	= tc3589x_id, | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 408 | }; | 
|  | 409 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 410 | static int __init tc3589x_init(void) | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 411 | { | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 412 | return i2c_add_driver(&tc3589x_driver); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 413 | } | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 414 | subsys_initcall(tc3589x_init); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 415 |  | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 416 | static void __exit tc3589x_exit(void) | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 417 | { | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 418 | i2c_del_driver(&tc3589x_driver); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 419 | } | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 420 | module_exit(tc3589x_exit); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 421 |  | 
|  | 422 | MODULE_LICENSE("GPL v2"); | 
| Sundar Iyer | 20406eb | 2010-12-13 09:33:14 +0530 | [diff] [blame] | 423 | MODULE_DESCRIPTION("TC3589x MFD core driver"); | 
| Rabin Vincent | b4ecd32 | 2010-05-10 23:39:47 +0200 | [diff] [blame] | 424 | MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); |