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

Archive Download this file



interactive