ar71xx: add support for the jjPlus JA76PF2 board
[openwrt.git] / target / linux / ar71xx / files / drivers / spi / spi-rb4xx-cpld.c
1 /*
2 * SPI driver for the CPLD chip on the Mikrotik RB4xx boards
3 *
4 * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
5 *
6 * This file was based on the patches for Linux 2.6.27.39 published by
7 * MikroTik for their RouterBoard 4xx series devices.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License version 2 as published
11 * by the Free Software Foundation.
12 */
13
14 #include <linux/types.h>
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/init.h>
18 #include <linux/module.h>
19 #include <linux/device.h>
20 #include <linux/bitops.h>
21 #include <linux/spi/spi.h>
22 #include <linux/gpio.h>
23 #include <linux/slab.h>
24
25 #include <asm/mach-ath79/rb4xx_cpld.h>
26
27 #define DRV_NAME "spi-rb4xx-cpld"
28 #define DRV_DESC "RB4xx CPLD driver"
29 #define DRV_VERSION "0.1.0"
30
31 #define CPLD_CMD_WRITE_NAND 0x08 /* send cmd, n x send data, send indle */
32 #define CPLD_CMD_WRITE_CFG 0x09 /* send cmd, n x send cfg */
33 #define CPLD_CMD_READ_NAND 0x0a /* send cmd, send idle, n x read data */
34 #define CPLD_CMD_READ_FAST 0x0b /* send cmd, 4 x idle, n x read data */
35 #define CPLD_CMD_LED5_ON 0x0c /* send cmd */
36 #define CPLD_CMD_LED5_OFF 0x0d /* send cmd */
37
38 struct rb4xx_cpld {
39 struct spi_device *spi;
40 struct mutex lock;
41 struct gpio_chip chip;
42 unsigned int config;
43 };
44
45 static struct rb4xx_cpld *rb4xx_cpld;
46
47 static inline struct rb4xx_cpld *gpio_to_cpld(struct gpio_chip *chip)
48 {
49 return container_of(chip, struct rb4xx_cpld, chip);
50 }
51
52 static int rb4xx_cpld_write_cmd(struct rb4xx_cpld *cpld, unsigned char cmd)
53 {
54 struct spi_transfer t[1];
55 struct spi_message m;
56 unsigned char tx_buf[1];
57 int err;
58
59 spi_message_init(&m);
60 memset(&t, 0, sizeof(t));
61
62 t[0].tx_buf = tx_buf;
63 t[0].len = sizeof(tx_buf);
64 spi_message_add_tail(&t[0], &m);
65
66 tx_buf[0] = cmd;
67
68 err = spi_sync(cpld->spi, &m);
69 return err;
70 }
71
72 static int rb4xx_cpld_write_cfg(struct rb4xx_cpld *cpld, unsigned char config)
73 {
74 struct spi_transfer t[1];
75 struct spi_message m;
76 unsigned char cmd[2];
77 int err;
78
79 spi_message_init(&m);
80 memset(&t, 0, sizeof(t));
81
82 t[0].tx_buf = cmd;
83 t[0].len = sizeof(cmd);
84 spi_message_add_tail(&t[0], &m);
85
86 cmd[0] = CPLD_CMD_WRITE_CFG;
87 cmd[1] = config;
88
89 err = spi_sync(cpld->spi, &m);
90 return err;
91 }
92
93 static int __rb4xx_cpld_change_cfg(struct rb4xx_cpld *cpld, unsigned mask,
94 unsigned value)
95 {
96 unsigned int config;
97 int err;
98
99 config = cpld->config & ~mask;
100 config |= value;
101
102 if ((cpld->config ^ config) & 0xff) {
103 err = rb4xx_cpld_write_cfg(cpld, config);
104 if (err)
105 return err;
106 }
107
108 if ((cpld->config ^ config) & CPLD_CFG_nLED5) {
109 err = rb4xx_cpld_write_cmd(cpld, (value) ? CPLD_CMD_LED5_ON :
110 CPLD_CMD_LED5_OFF);
111 if (err)
112 return err;
113 }
114
115 cpld->config = config;
116 return 0;
117 }
118
119 int rb4xx_cpld_change_cfg(unsigned mask, unsigned value)
120 {
121 int ret;
122
123 if (rb4xx_cpld == NULL)
124 return -ENODEV;
125
126 mutex_lock(&rb4xx_cpld->lock);
127 ret = __rb4xx_cpld_change_cfg(rb4xx_cpld, mask, value);
128 mutex_unlock(&rb4xx_cpld->lock);
129
130 return ret;
131 }
132 EXPORT_SYMBOL_GPL(rb4xx_cpld_change_cfg);
133
134 int rb4xx_cpld_read_from(unsigned addr, unsigned char *rx_buf,
135 const unsigned char *verify_buf, unsigned count)
136 {
137 const unsigned char cmd[5] = {
138 CPLD_CMD_READ_FAST,
139 (addr >> 16) & 0xff,
140 (addr >> 8) & 0xff,
141 addr & 0xff,
142 0
143 };
144 struct spi_transfer t[2] = {
145 {
146 .tx_buf = &cmd,
147 .len = 5,
148 },
149 {
150 .tx_buf = verify_buf,
151 .rx_buf = rx_buf,
152 .len = count,
153 .verify = (verify_buf != NULL),
154 },
155 };
156 struct spi_message m;
157
158 if (rb4xx_cpld == NULL)
159 return -ENODEV;
160
161 spi_message_init(&m);
162 m.fast_read = 1;
163 spi_message_add_tail(&t[0], &m);
164 spi_message_add_tail(&t[1], &m);
165 return spi_sync(rb4xx_cpld->spi, &m);
166 }
167 EXPORT_SYMBOL_GPL(rb4xx_cpld_read_from);
168
169 #if 0
170 int rb4xx_cpld_read(unsigned char *buf, unsigned char *verify_buf,
171 unsigned count)
172 {
173 struct spi_transfer t[2];
174 struct spi_message m;
175 unsigned char cmd[2];
176
177 if (rb4xx_cpld == NULL)
178 return -ENODEV;
179
180 spi_message_init(&m);
181 memset(&t, 0, sizeof(t));
182
183 /* send command */
184 t[0].tx_buf = cmd;
185 t[0].len = sizeof(cmd);
186 spi_message_add_tail(&t[0], &m);
187
188 cmd[0] = CPLD_CMD_READ_NAND;
189 cmd[1] = 0;
190
191 /* read data */
192 t[1].rx_buf = buf;
193 t[1].len = count;
194 spi_message_add_tail(&t[1], &m);
195
196 return spi_sync(rb4xx_cpld->spi, &m);
197 }
198 #else
199 int rb4xx_cpld_read(unsigned char *rx_buf, const unsigned char *verify_buf,
200 unsigned count)
201 {
202 static const unsigned char cmd[2] = { CPLD_CMD_READ_NAND, 0 };
203 struct spi_transfer t[2] = {
204 {
205 .tx_buf = &cmd,
206 .len = 2,
207 }, {
208 .tx_buf = verify_buf,
209 .rx_buf = rx_buf,
210 .len = count,
211 .verify = (verify_buf != NULL),
212 },
213 };
214 struct spi_message m;
215
216 if (rb4xx_cpld == NULL)
217 return -ENODEV;
218
219 spi_message_init(&m);
220 spi_message_add_tail(&t[0], &m);
221 spi_message_add_tail(&t[1], &m);
222 return spi_sync(rb4xx_cpld->spi, &m);
223 }
224 #endif
225 EXPORT_SYMBOL_GPL(rb4xx_cpld_read);
226
227 int rb4xx_cpld_write(const unsigned char *buf, unsigned count)
228 {
229 #if 0
230 struct spi_transfer t[3];
231 struct spi_message m;
232 unsigned char cmd[1];
233
234 if (rb4xx_cpld == NULL)
235 return -ENODEV;
236
237 memset(&t, 0, sizeof(t));
238 spi_message_init(&m);
239
240 /* send command */
241 t[0].tx_buf = cmd;
242 t[0].len = sizeof(cmd);
243 spi_message_add_tail(&t[0], &m);
244
245 cmd[0] = CPLD_CMD_WRITE_NAND;
246
247 /* write data */
248 t[1].tx_buf = buf;
249 t[1].len = count;
250 spi_message_add_tail(&t[1], &m);
251
252 /* send idle */
253 t[2].len = 1;
254 spi_message_add_tail(&t[2], &m);
255
256 return spi_sync(rb4xx_cpld->spi, &m);
257 #else
258 static const unsigned char cmd = CPLD_CMD_WRITE_NAND;
259 struct spi_transfer t[3] = {
260 {
261 .tx_buf = &cmd,
262 .len = 1,
263 }, {
264 .tx_buf = buf,
265 .len = count,
266 .fast_write = 1,
267 }, {
268 .len = 1,
269 .fast_write = 1,
270 },
271 };
272 struct spi_message m;
273
274 if (rb4xx_cpld == NULL)
275 return -ENODEV;
276
277 spi_message_init(&m);
278 spi_message_add_tail(&t[0], &m);
279 spi_message_add_tail(&t[1], &m);
280 spi_message_add_tail(&t[2], &m);
281 return spi_sync(rb4xx_cpld->spi, &m);
282 #endif
283 }
284 EXPORT_SYMBOL_GPL(rb4xx_cpld_write);
285
286 static int rb4xx_cpld_gpio_get(struct gpio_chip *chip, unsigned offset)
287 {
288 struct rb4xx_cpld *cpld = gpio_to_cpld(chip);
289 int ret;
290
291 mutex_lock(&cpld->lock);
292 ret = (cpld->config >> offset) & 1;
293 mutex_unlock(&cpld->lock);
294
295 return ret;
296 }
297
298 static void rb4xx_cpld_gpio_set(struct gpio_chip *chip, unsigned offset,
299 int value)
300 {
301 struct rb4xx_cpld *cpld = gpio_to_cpld(chip);
302
303 mutex_lock(&cpld->lock);
304 __rb4xx_cpld_change_cfg(cpld, (1 << offset), !!value << offset);
305 mutex_unlock(&cpld->lock);
306 }
307
308 static int rb4xx_cpld_gpio_direction_input(struct gpio_chip *chip,
309 unsigned offset)
310 {
311 return -EOPNOTSUPP;
312 }
313
314 static int rb4xx_cpld_gpio_direction_output(struct gpio_chip *chip,
315 unsigned offset,
316 int value)
317 {
318 struct rb4xx_cpld *cpld = gpio_to_cpld(chip);
319 int ret;
320
321 mutex_lock(&cpld->lock);
322 ret = __rb4xx_cpld_change_cfg(cpld, (1 << offset), !!value << offset);
323 mutex_unlock(&cpld->lock);
324
325 return ret;
326 }
327
328 static int rb4xx_cpld_gpio_init(struct rb4xx_cpld *cpld, unsigned int base)
329 {
330 int err;
331
332 /* init config */
333 cpld->config = CPLD_CFG_nLED1 | CPLD_CFG_nLED2 | CPLD_CFG_nLED3 |
334 CPLD_CFG_nLED4 | CPLD_CFG_nCE;
335 rb4xx_cpld_write_cfg(cpld, cpld->config);
336
337 /* setup GPIO chip */
338 cpld->chip.label = DRV_NAME;
339
340 cpld->chip.get = rb4xx_cpld_gpio_get;
341 cpld->chip.set = rb4xx_cpld_gpio_set;
342 cpld->chip.direction_input = rb4xx_cpld_gpio_direction_input;
343 cpld->chip.direction_output = rb4xx_cpld_gpio_direction_output;
344
345 cpld->chip.base = base;
346 cpld->chip.ngpio = CPLD_NUM_GPIOS;
347 cpld->chip.can_sleep = 1;
348 cpld->chip.dev = &cpld->spi->dev;
349 cpld->chip.owner = THIS_MODULE;
350
351 err = gpiochip_add(&cpld->chip);
352 if (err)
353 dev_err(&cpld->spi->dev, "adding GPIO chip failed, err=%d\n",
354 err);
355
356 return err;
357 }
358
359 static int __devinit rb4xx_cpld_probe(struct spi_device *spi)
360 {
361 struct rb4xx_cpld *cpld;
362 struct rb4xx_cpld_platform_data *pdata;
363 int err;
364
365 pdata = spi->dev.platform_data;
366 if (!pdata) {
367 dev_dbg(&spi->dev, "no platform data\n");
368 return -EINVAL;
369 }
370
371 cpld = kzalloc(sizeof(*cpld), GFP_KERNEL);
372 if (!cpld) {
373 dev_err(&spi->dev, "no memory for private data\n");
374 return -ENOMEM;
375 }
376
377 mutex_init(&cpld->lock);
378 cpld->spi = spi_dev_get(spi);
379 dev_set_drvdata(&spi->dev, cpld);
380
381 spi->mode = SPI_MODE_0;
382 spi->bits_per_word = 8;
383 err = spi_setup(spi);
384 if (err) {
385 dev_err(&spi->dev, "spi_setup failed, err=%d\n", err);
386 goto err_drvdata;
387 }
388
389 err = rb4xx_cpld_gpio_init(cpld, pdata->gpio_base);
390 if (err)
391 goto err_drvdata;
392
393 rb4xx_cpld = cpld;
394
395 return 0;
396
397 err_drvdata:
398 dev_set_drvdata(&spi->dev, NULL);
399 kfree(cpld);
400
401 return err;
402 }
403
404 static int __devexit rb4xx_cpld_remove(struct spi_device *spi)
405 {
406 struct rb4xx_cpld *cpld;
407
408 rb4xx_cpld = NULL;
409 cpld = dev_get_drvdata(&spi->dev);
410 dev_set_drvdata(&spi->dev, NULL);
411 kfree(cpld);
412
413 return 0;
414 }
415
416 static struct spi_driver rb4xx_cpld_driver = {
417 .driver = {
418 .name = DRV_NAME,
419 .bus = &spi_bus_type,
420 .owner = THIS_MODULE,
421 },
422 .probe = rb4xx_cpld_probe,
423 .remove = __devexit_p(rb4xx_cpld_remove),
424 };
425
426 static int __init rb4xx_cpld_init(void)
427 {
428 return spi_register_driver(&rb4xx_cpld_driver);
429 }
430 module_init(rb4xx_cpld_init);
431
432 static void __exit rb4xx_cpld_exit(void)
433 {
434 spi_unregister_driver(&rb4xx_cpld_driver);
435 }
436 module_exit(rb4xx_cpld_exit);
437
438 MODULE_DESCRIPTION(DRV_DESC);
439 MODULE_VERSION(DRV_VERSION);
440 MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
441 MODULE_LICENSE("GPL v2");
This page took 0.077058 seconds and 5 git commands to generate.