diff options
Diffstat (limited to 'drivers/gpio/mcp23s08.c')
-rw-r--r-- | drivers/gpio/mcp23s08.c | 191 |
1 files changed, 143 insertions, 48 deletions
diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/mcp23s08.c index 69f6f1955a3..40e076083ec 100644 --- a/drivers/gpio/mcp23s08.c +++ b/drivers/gpio/mcp23s08.c | |||
@@ -10,7 +10,13 @@ | |||
10 | #include <linux/spi/spi.h> | 10 | #include <linux/spi/spi.h> |
11 | #include <linux/spi/mcp23s08.h> | 11 | #include <linux/spi/mcp23s08.h> |
12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
13 | #include <asm/byteorder.h> | ||
13 | 14 | ||
15 | /** | ||
16 | * MCP types supported by driver | ||
17 | */ | ||
18 | #define MCP_TYPE_S08 0 | ||
19 | #define MCP_TYPE_S17 1 | ||
14 | 20 | ||
15 | /* Registers are all 8 bits wide. | 21 | /* Registers are all 8 bits wide. |
16 | * | 22 | * |
@@ -35,27 +41,38 @@ | |||
35 | #define MCP_GPIO 0x09 | 41 | #define MCP_GPIO 0x09 |
36 | #define MCP_OLAT 0x0a | 42 | #define MCP_OLAT 0x0a |
37 | 43 | ||
44 | struct mcp23s08; | ||
45 | |||
46 | struct mcp23s08_ops { | ||
47 | int (*read)(struct mcp23s08 *mcp, unsigned reg); | ||
48 | int (*write)(struct mcp23s08 *mcp, unsigned reg, unsigned val); | ||
49 | int (*read_regs)(struct mcp23s08 *mcp, unsigned reg, | ||
50 | u16 *vals, unsigned n); | ||
51 | }; | ||
52 | |||
38 | struct mcp23s08 { | 53 | struct mcp23s08 { |
39 | struct spi_device *spi; | 54 | struct spi_device *spi; |
40 | u8 addr; | 55 | u8 addr; |
41 | 56 | ||
42 | u8 cache[11]; | 57 | u16 cache[11]; |
43 | /* lock protects the cached values */ | 58 | /* lock protects the cached values */ |
44 | struct mutex lock; | 59 | struct mutex lock; |
45 | 60 | ||
46 | struct gpio_chip chip; | 61 | struct gpio_chip chip; |
47 | 62 | ||
48 | struct work_struct work; | 63 | struct work_struct work; |
64 | |||
65 | const struct mcp23s08_ops *ops; | ||
49 | }; | 66 | }; |
50 | 67 | ||
51 | /* A given spi_device can represent up to four mcp23s08 chips | 68 | /* A given spi_device can represent up to eight mcp23sxx chips |
52 | * sharing the same chipselect but using different addresses | 69 | * sharing the same chipselect but using different addresses |
53 | * (e.g. chips #0 and #3 might be populated, but not #1 or $2). | 70 | * (e.g. chips #0 and #3 might be populated, but not #1 or $2). |
54 | * Driver data holds all the per-chip data. | 71 | * Driver data holds all the per-chip data. |
55 | */ | 72 | */ |
56 | struct mcp23s08_driver_data { | 73 | struct mcp23s08_driver_data { |
57 | unsigned ngpio; | 74 | unsigned ngpio; |
58 | struct mcp23s08 *mcp[4]; | 75 | struct mcp23s08 *mcp[8]; |
59 | struct mcp23s08 chip[]; | 76 | struct mcp23s08 chip[]; |
60 | }; | 77 | }; |
61 | 78 | ||
@@ -70,7 +87,7 @@ static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) | |||
70 | return (status < 0) ? status : rx[0]; | 87 | return (status < 0) ? status : rx[0]; |
71 | } | 88 | } |
72 | 89 | ||
73 | static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val) | 90 | static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) |
74 | { | 91 | { |
75 | u8 tx[3]; | 92 | u8 tx[3]; |
76 | 93 | ||
@@ -81,17 +98,81 @@ static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val) | |||
81 | } | 98 | } |
82 | 99 | ||
83 | static int | 100 | static int |
84 | mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u8 *vals, unsigned n) | 101 | mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) |
85 | { | 102 | { |
86 | u8 tx[2]; | 103 | u8 tx[2], *tmp; |
104 | int status; | ||
87 | 105 | ||
88 | if ((n + reg) > sizeof mcp->cache) | 106 | if ((n + reg) > sizeof mcp->cache) |
89 | return -EINVAL; | 107 | return -EINVAL; |
90 | tx[0] = mcp->addr | 0x01; | 108 | tx[0] = mcp->addr | 0x01; |
91 | tx[1] = reg; | 109 | tx[1] = reg; |
92 | return spi_write_then_read(mcp->spi, tx, sizeof tx, vals, n); | 110 | |
111 | tmp = (u8 *)vals; | ||
112 | status = spi_write_then_read(mcp->spi, tx, sizeof tx, tmp, n); | ||
113 | if (status >= 0) { | ||
114 | while (n--) | ||
115 | vals[n] = tmp[n]; /* expand to 16bit */ | ||
116 | } | ||
117 | return status; | ||
118 | } | ||
119 | |||
120 | static int mcp23s17_read(struct mcp23s08 *mcp, unsigned reg) | ||
121 | { | ||
122 | u8 tx[2], rx[2]; | ||
123 | int status; | ||
124 | |||
125 | tx[0] = mcp->addr | 0x01; | ||
126 | tx[1] = reg << 1; | ||
127 | status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx); | ||
128 | return (status < 0) ? status : (rx[0] | (rx[1] << 8)); | ||
129 | } | ||
130 | |||
131 | static int mcp23s17_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) | ||
132 | { | ||
133 | u8 tx[4]; | ||
134 | |||
135 | tx[0] = mcp->addr; | ||
136 | tx[1] = reg << 1; | ||
137 | tx[2] = val; | ||
138 | tx[3] = val >> 8; | ||
139 | return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); | ||
140 | } | ||
141 | |||
142 | static int | ||
143 | mcp23s17_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) | ||
144 | { | ||
145 | u8 tx[2]; | ||
146 | int status; | ||
147 | |||
148 | if ((n + reg) > sizeof mcp->cache) | ||
149 | return -EINVAL; | ||
150 | tx[0] = mcp->addr | 0x01; | ||
151 | tx[1] = reg << 1; | ||
152 | |||
153 | status = spi_write_then_read(mcp->spi, tx, sizeof tx, | ||
154 | (u8 *)vals, n * 2); | ||
155 | if (status >= 0) { | ||
156 | while (n--) | ||
157 | vals[n] = __le16_to_cpu((__le16)vals[n]); | ||
158 | } | ||
159 | |||
160 | return status; | ||
93 | } | 161 | } |
94 | 162 | ||
163 | static const struct mcp23s08_ops mcp23s08_ops = { | ||
164 | .read = mcp23s08_read, | ||
165 | .write = mcp23s08_write, | ||
166 | .read_regs = mcp23s08_read_regs, | ||
167 | }; | ||
168 | |||
169 | static const struct mcp23s08_ops mcp23s17_ops = { | ||
170 | .read = mcp23s17_read, | ||
171 | .write = mcp23s17_write, | ||
172 | .read_regs = mcp23s17_read_regs, | ||
173 | }; | ||
174 | |||
175 | |||
95 | /*----------------------------------------------------------------------*/ | 176 | /*----------------------------------------------------------------------*/ |
96 | 177 | ||
97 | static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) | 178 | static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) |
@@ -101,7 +182,7 @@ static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) | |||
101 | 182 | ||
102 | mutex_lock(&mcp->lock); | 183 | mutex_lock(&mcp->lock); |
103 | mcp->cache[MCP_IODIR] |= (1 << offset); | 184 | mcp->cache[MCP_IODIR] |= (1 << offset); |
104 | status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); | 185 | status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); |
105 | mutex_unlock(&mcp->lock); | 186 | mutex_unlock(&mcp->lock); |
106 | return status; | 187 | return status; |
107 | } | 188 | } |
@@ -114,7 +195,7 @@ static int mcp23s08_get(struct gpio_chip *chip, unsigned offset) | |||
114 | mutex_lock(&mcp->lock); | 195 | mutex_lock(&mcp->lock); |
115 | 196 | ||
116 | /* REVISIT reading this clears any IRQ ... */ | 197 | /* REVISIT reading this clears any IRQ ... */ |
117 | status = mcp23s08_read(mcp, MCP_GPIO); | 198 | status = mcp->ops->read(mcp, MCP_GPIO); |
118 | if (status < 0) | 199 | if (status < 0) |
119 | status = 0; | 200 | status = 0; |
120 | else { | 201 | else { |
@@ -127,20 +208,20 @@ static int mcp23s08_get(struct gpio_chip *chip, unsigned offset) | |||
127 | 208 | ||
128 | static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value) | 209 | static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value) |
129 | { | 210 | { |
130 | u8 olat = mcp->cache[MCP_OLAT]; | 211 | unsigned olat = mcp->cache[MCP_OLAT]; |
131 | 212 | ||
132 | if (value) | 213 | if (value) |
133 | olat |= mask; | 214 | olat |= mask; |
134 | else | 215 | else |
135 | olat &= ~mask; | 216 | olat &= ~mask; |
136 | mcp->cache[MCP_OLAT] = olat; | 217 | mcp->cache[MCP_OLAT] = olat; |
137 | return mcp23s08_write(mcp, MCP_OLAT, olat); | 218 | return mcp->ops->write(mcp, MCP_OLAT, olat); |
138 | } | 219 | } |
139 | 220 | ||
140 | static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) | 221 | static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) |
141 | { | 222 | { |
142 | struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); | 223 | struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); |
143 | u8 mask = 1 << offset; | 224 | unsigned mask = 1 << offset; |
144 | 225 | ||
145 | mutex_lock(&mcp->lock); | 226 | mutex_lock(&mcp->lock); |
146 | __mcp23s08_set(mcp, mask, value); | 227 | __mcp23s08_set(mcp, mask, value); |
@@ -151,14 +232,14 @@ static int | |||
151 | mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) | 232 | mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) |
152 | { | 233 | { |
153 | struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); | 234 | struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); |
154 | u8 mask = 1 << offset; | 235 | unsigned mask = 1 << offset; |
155 | int status; | 236 | int status; |
156 | 237 | ||
157 | mutex_lock(&mcp->lock); | 238 | mutex_lock(&mcp->lock); |
158 | status = __mcp23s08_set(mcp, mask, value); | 239 | status = __mcp23s08_set(mcp, mask, value); |
159 | if (status == 0) { | 240 | if (status == 0) { |
160 | mcp->cache[MCP_IODIR] &= ~mask; | 241 | mcp->cache[MCP_IODIR] &= ~mask; |
161 | status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); | 242 | status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); |
162 | } | 243 | } |
163 | mutex_unlock(&mcp->lock); | 244 | mutex_unlock(&mcp->lock); |
164 | return status; | 245 | return status; |
@@ -184,16 +265,16 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip) | |||
184 | mcp = container_of(chip, struct mcp23s08, chip); | 265 | mcp = container_of(chip, struct mcp23s08, chip); |
185 | 266 | ||
186 | /* NOTE: we only handle one bank for now ... */ | 267 | /* NOTE: we only handle one bank for now ... */ |
187 | bank = '0' + ((mcp->addr >> 1) & 0x3); | 268 | bank = '0' + ((mcp->addr >> 1) & 0x7); |
188 | 269 | ||
189 | mutex_lock(&mcp->lock); | 270 | mutex_lock(&mcp->lock); |
190 | t = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache); | 271 | t = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache)); |
191 | if (t < 0) { | 272 | if (t < 0) { |
192 | seq_printf(s, " I/O ERROR %d\n", t); | 273 | seq_printf(s, " I/O ERROR %d\n", t); |
193 | goto done; | 274 | goto done; |
194 | } | 275 | } |
195 | 276 | ||
196 | for (t = 0, mask = 1; t < 8; t++, mask <<= 1) { | 277 | for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) { |
197 | const char *label; | 278 | const char *label; |
198 | 279 | ||
199 | label = gpiochip_is_requested(chip, t); | 280 | label = gpiochip_is_requested(chip, t); |
@@ -219,28 +300,33 @@ done: | |||
219 | /*----------------------------------------------------------------------*/ | 300 | /*----------------------------------------------------------------------*/ |
220 | 301 | ||
221 | static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, | 302 | static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, |
222 | unsigned base, unsigned pullups) | 303 | unsigned type, unsigned base, unsigned pullups) |
223 | { | 304 | { |
224 | struct mcp23s08_driver_data *data = spi_get_drvdata(spi); | 305 | struct mcp23s08_driver_data *data = spi_get_drvdata(spi); |
225 | struct mcp23s08 *mcp = data->mcp[addr]; | 306 | struct mcp23s08 *mcp = data->mcp[addr]; |
226 | int status; | 307 | int status; |
227 | int do_update = 0; | ||
228 | 308 | ||
229 | mutex_init(&mcp->lock); | 309 | mutex_init(&mcp->lock); |
230 | 310 | ||
231 | mcp->spi = spi; | 311 | mcp->spi = spi; |
232 | mcp->addr = 0x40 | (addr << 1); | 312 | mcp->addr = 0x40 | (addr << 1); |
233 | 313 | ||
234 | mcp->chip.label = "mcp23s08", | ||
235 | |||
236 | mcp->chip.direction_input = mcp23s08_direction_input; | 314 | mcp->chip.direction_input = mcp23s08_direction_input; |
237 | mcp->chip.get = mcp23s08_get; | 315 | mcp->chip.get = mcp23s08_get; |
238 | mcp->chip.direction_output = mcp23s08_direction_output; | 316 | mcp->chip.direction_output = mcp23s08_direction_output; |
239 | mcp->chip.set = mcp23s08_set; | 317 | mcp->chip.set = mcp23s08_set; |
240 | mcp->chip.dbg_show = mcp23s08_dbg_show; | 318 | mcp->chip.dbg_show = mcp23s08_dbg_show; |
241 | 319 | ||
320 | if (type == MCP_TYPE_S17) { | ||
321 | mcp->ops = &mcp23s17_ops; | ||
322 | mcp->chip.ngpio = 16; | ||
323 | mcp->chip.label = "mcp23s17"; | ||
324 | } else { | ||
325 | mcp->ops = &mcp23s08_ops; | ||
326 | mcp->chip.ngpio = 8; | ||
327 | mcp->chip.label = "mcp23s08"; | ||
328 | } | ||
242 | mcp->chip.base = base; | 329 | mcp->chip.base = base; |
243 | mcp->chip.ngpio = 8; | ||
244 | mcp->chip.can_sleep = 1; | 330 | mcp->chip.can_sleep = 1; |
245 | mcp->chip.dev = &spi->dev; | 331 | mcp->chip.dev = &spi->dev; |
246 | mcp->chip.owner = THIS_MODULE; | 332 | mcp->chip.owner = THIS_MODULE; |
@@ -248,45 +334,39 @@ static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, | |||
248 | /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, | 334 | /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, |
249 | * and MCP_IOCON.HAEN = 1, so we work with all chips. | 335 | * and MCP_IOCON.HAEN = 1, so we work with all chips. |
250 | */ | 336 | */ |
251 | status = mcp23s08_read(mcp, MCP_IOCON); | 337 | status = mcp->ops->read(mcp, MCP_IOCON); |
252 | if (status < 0) | 338 | if (status < 0) |
253 | goto fail; | 339 | goto fail; |
254 | if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { | 340 | if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { |
255 | status &= ~IOCON_SEQOP; | 341 | /* mcp23s17 has IOCON twice, make sure they are in sync */ |
256 | status |= IOCON_HAEN; | 342 | status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8)); |
257 | status = mcp23s08_write(mcp, MCP_IOCON, (u8) status); | 343 | status |= IOCON_HAEN | (IOCON_HAEN << 8); |
344 | status = mcp->ops->write(mcp, MCP_IOCON, status); | ||
258 | if (status < 0) | 345 | if (status < 0) |
259 | goto fail; | 346 | goto fail; |
260 | } | 347 | } |
261 | 348 | ||
262 | /* configure ~100K pullups */ | 349 | /* configure ~100K pullups */ |
263 | status = mcp23s08_write(mcp, MCP_GPPU, pullups); | 350 | status = mcp->ops->write(mcp, MCP_GPPU, pullups); |
264 | if (status < 0) | 351 | if (status < 0) |
265 | goto fail; | 352 | goto fail; |
266 | 353 | ||
267 | status = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache); | 354 | status = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache)); |
268 | if (status < 0) | 355 | if (status < 0) |
269 | goto fail; | 356 | goto fail; |
270 | 357 | ||
271 | /* disable inverter on input */ | 358 | /* disable inverter on input */ |
272 | if (mcp->cache[MCP_IPOL] != 0) { | 359 | if (mcp->cache[MCP_IPOL] != 0) { |
273 | mcp->cache[MCP_IPOL] = 0; | 360 | mcp->cache[MCP_IPOL] = 0; |
274 | do_update = 1; | 361 | status = mcp->ops->write(mcp, MCP_IPOL, 0); |
362 | if (status < 0) | ||
363 | goto fail; | ||
275 | } | 364 | } |
276 | 365 | ||
277 | /* disable irqs */ | 366 | /* disable irqs */ |
278 | if (mcp->cache[MCP_GPINTEN] != 0) { | 367 | if (mcp->cache[MCP_GPINTEN] != 0) { |
279 | mcp->cache[MCP_GPINTEN] = 0; | 368 | mcp->cache[MCP_GPINTEN] = 0; |
280 | do_update = 1; | 369 | status = mcp->ops->write(mcp, MCP_GPINTEN, 0); |
281 | } | ||
282 | |||
283 | if (do_update) { | ||
284 | u8 tx[4]; | ||
285 | |||
286 | tx[0] = mcp->addr; | ||
287 | tx[1] = MCP_IPOL; | ||
288 | memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2); | ||
289 | status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); | ||
290 | if (status < 0) | 370 | if (status < 0) |
291 | goto fail; | 371 | goto fail; |
292 | } | 372 | } |
@@ -305,19 +385,26 @@ static int mcp23s08_probe(struct spi_device *spi) | |||
305 | unsigned addr; | 385 | unsigned addr; |
306 | unsigned chips = 0; | 386 | unsigned chips = 0; |
307 | struct mcp23s08_driver_data *data; | 387 | struct mcp23s08_driver_data *data; |
308 | int status; | 388 | int status, type; |
309 | unsigned base; | 389 | unsigned base; |
310 | 390 | ||
391 | type = spi_get_device_id(spi)->driver_data; | ||
392 | |||
311 | pdata = spi->dev.platform_data; | 393 | pdata = spi->dev.platform_data; |
312 | if (!pdata || !gpio_is_valid(pdata->base)) { | 394 | if (!pdata || !gpio_is_valid(pdata->base)) { |
313 | dev_dbg(&spi->dev, "invalid or missing platform data\n"); | 395 | dev_dbg(&spi->dev, "invalid or missing platform data\n"); |
314 | return -EINVAL; | 396 | return -EINVAL; |
315 | } | 397 | } |
316 | 398 | ||
317 | for (addr = 0; addr < 4; addr++) { | 399 | for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { |
318 | if (!pdata->chip[addr].is_present) | 400 | if (!pdata->chip[addr].is_present) |
319 | continue; | 401 | continue; |
320 | chips++; | 402 | chips++; |
403 | if ((type == MCP_TYPE_S08) && (addr > 3)) { | ||
404 | dev_err(&spi->dev, | ||
405 | "mcp23s08 only supports address 0..3\n"); | ||
406 | return -EINVAL; | ||
407 | } | ||
321 | } | 408 | } |
322 | if (!chips) | 409 | if (!chips) |
323 | return -ENODEV; | 410 | return -ENODEV; |
@@ -329,16 +416,17 @@ static int mcp23s08_probe(struct spi_device *spi) | |||
329 | spi_set_drvdata(spi, data); | 416 | spi_set_drvdata(spi, data); |
330 | 417 | ||
331 | base = pdata->base; | 418 | base = pdata->base; |
332 | for (addr = 0; addr < 4; addr++) { | 419 | for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { |
333 | if (!pdata->chip[addr].is_present) | 420 | if (!pdata->chip[addr].is_present) |
334 | continue; | 421 | continue; |
335 | chips--; | 422 | chips--; |
336 | data->mcp[addr] = &data->chip[chips]; | 423 | data->mcp[addr] = &data->chip[chips]; |
337 | status = mcp23s08_probe_one(spi, addr, base, | 424 | status = mcp23s08_probe_one(spi, addr, type, base, |
338 | pdata->chip[addr].pullups); | 425 | pdata->chip[addr].pullups); |
339 | if (status < 0) | 426 | if (status < 0) |
340 | goto fail; | 427 | goto fail; |
341 | base += 8; | 428 | |
429 | base += (type == MCP_TYPE_S17) ? 16 : 8; | ||
342 | } | 430 | } |
343 | data->ngpio = base - pdata->base; | 431 | data->ngpio = base - pdata->base; |
344 | 432 | ||
@@ -358,7 +446,7 @@ static int mcp23s08_probe(struct spi_device *spi) | |||
358 | return 0; | 446 | return 0; |
359 | 447 | ||
360 | fail: | 448 | fail: |
361 | for (addr = 0; addr < 4; addr++) { | 449 | for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { |
362 | int tmp; | 450 | int tmp; |
363 | 451 | ||
364 | if (!data->mcp[addr]) | 452 | if (!data->mcp[addr]) |
@@ -388,7 +476,7 @@ static int mcp23s08_remove(struct spi_device *spi) | |||
388 | } | 476 | } |
389 | } | 477 | } |
390 | 478 | ||
391 | for (addr = 0; addr < 4; addr++) { | 479 | for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) { |
392 | int tmp; | 480 | int tmp; |
393 | 481 | ||
394 | if (!data->mcp[addr]) | 482 | if (!data->mcp[addr]) |
@@ -405,9 +493,17 @@ static int mcp23s08_remove(struct spi_device *spi) | |||
405 | return status; | 493 | return status; |
406 | } | 494 | } |
407 | 495 | ||
496 | static const struct spi_device_id mcp23s08_ids[] = { | ||
497 | { "mcp23s08", MCP_TYPE_S08 }, | ||
498 | { "mcp23s17", MCP_TYPE_S17 }, | ||
499 | { }, | ||
500 | }; | ||
501 | MODULE_DEVICE_TABLE(spi, mcp23s08_ids); | ||
502 | |||
408 | static struct spi_driver mcp23s08_driver = { | 503 | static struct spi_driver mcp23s08_driver = { |
409 | .probe = mcp23s08_probe, | 504 | .probe = mcp23s08_probe, |
410 | .remove = mcp23s08_remove, | 505 | .remove = mcp23s08_remove, |
506 | .id_table = mcp23s08_ids, | ||
411 | .driver = { | 507 | .driver = { |
412 | .name = "mcp23s08", | 508 | .name = "mcp23s08", |
413 | .owner = THIS_MODULE, | 509 | .owner = THIS_MODULE, |
@@ -432,4 +528,3 @@ static void __exit mcp23s08_exit(void) | |||
432 | module_exit(mcp23s08_exit); | 528 | module_exit(mcp23s08_exit); |
433 | 529 | ||
434 | MODULE_LICENSE("GPL"); | 530 | MODULE_LICENSE("GPL"); |
435 | MODULE_ALIAS("spi:mcp23s08"); | ||