Root/drivers/ieee802154/spi_atben.c

1/*
2 * spi_atben.c - SPI host look-alike for ATBEN
3 *
4 * Written 2011 by Werner Almesberger
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
9 */
10
11#include <linux/kernel.h>
12#include <linux/module.h>
13#include <linux/platform_device.h>
14#include <linux/gpio.h>
15#include <linux/delay.h>
16#include <linux/spi/spi.h>
17#include <linux/spi/at86rf230.h>
18#include <asm/mach-jz4740/base.h>
19
20#include "at86rf230.h"
21
22
23enum {
24    VDD_OFF = 1 << 2, /* VDD disable, PD02 */
25    MOSI = 1 << 8, /* CMD, PD08 */
26    SLP_TR = 1 << 9, /* CLK, PD09 */
27    MISO = 1 << 10, /* DAT0, PD10 */
28    SCLK = 1 << 11, /* DAT1, PD11 */
29    IRQ = 1 << 12, /* DAT2, PD12 */
30    nSEL = 1 << 13, /* DAT3/CD, PD13 */
31};
32
33#define PDPIN (prv->regs)
34#define PDDATS (prv->regs+0x14)
35#define PDDATC (prv->regs+0x18)
36
37
38struct atben_prv {
39    struct device *dev;
40    void __iomem *regs;
41    struct resource *ioarea;
42    struct at86rf230_platform_data
43                platform_data;
44    /* copy platform_data so that we can adapt .reset_data */
45};
46
47
48/* ----- ATBEN reset ------------------------------------------------------- */
49
50
51static void atben_reset(void *reset_data)
52{
53    struct atben_prv *prv = reset_data;
54    const int charge = nSEL | MOSI | SLP_TR | SCLK;
55    const int discharge = charge | IRQ | MISO;
56
57    dev_info(prv->dev, "atben_reset\n");
58    jz_gpio_port_set_value(JZ_GPIO_PORTD(0), 1 << 2, 1 << 2);
59    jz_gpio_port_direction_output(JZ_GPIO_PORTD(0), discharge);
60    jz_gpio_port_set_value(JZ_GPIO_PORTD(0), 0, discharge);
61    msleep(100); /* let power drop */
62
63    /*
64     * Hack: PD12/DAT2/IRQ is an active-high interrupt input, which is
65     * indicated by setting its direction bit to 1. We thus must not
66     * configure it as an "input".
67     */
68    jz_gpio_port_direction_input(JZ_GPIO_PORTD(0), MISO);
69    jz_gpio_port_set_value(JZ_GPIO_PORTD(0), charge, charge);
70    msleep(10); /* precharge caps */
71
72    jz_gpio_port_set_value(JZ_GPIO_PORTD(0), 0, VDD_OFF | SLP_TR | SCLK);
73    msleep(10);
74}
75
76
77/* ----- SPI transfers ----------------------------------------------------- */
78
79
80static void rx_only(const struct atben_prv *prv, uint8_t *buf, int len)
81{
82    uint8_t v;
83
84    while (len--) {
85        writel(SCLK, PDDATS);
86        v = readl(PDPIN) & MISO ? 0x80 : 0;
87        writel(SCLK, PDDATC);
88
89        #define DO_BIT(m) \
90            writel(SCLK, PDDATS); \
91            if (readl(PDPIN) & MISO) \
92                v |= (m); \
93            writel(SCLK, PDDATC)
94
95        DO_BIT(0x40);
96        DO_BIT(0x20);
97        DO_BIT(0x10);
98        DO_BIT(0x08);
99        DO_BIT(0x04);
100        DO_BIT(0x02);
101        DO_BIT(0x01);
102
103        #undef DO_BIT
104
105        *buf++ = v;
106    }
107}
108
109
110static void tx_only(const struct atben_prv *prv, const uint8_t *buf, int len)
111{
112    uint8_t tv;
113
114    while (len--) {
115        tv = *buf++;
116
117        if (tv & 0x80) {
118            writel(MOSI, PDDATS);
119            goto b6_1;
120        } else {
121            writel(MOSI, PDDATC);
122            goto b6_0;
123        }
124
125        #define DO_BIT(m, this, next) \
126            this##_1: \
127                writel(SCLK, PDDATS); \
128                if (tv & (m)) { \
129                    writel(SCLK, PDDATC); \
130                    goto next##_1; \
131                } else { \
132                    writel(MOSI | SCLK, PDDATC); \
133                    goto next##_0; \
134                } \
135            this##_0: \
136                writel(SCLK, PDDATS); \
137                writel(SCLK, PDDATC); \
138                if (tv & (m)) { \
139                    writel(MOSI, PDDATS); \
140                    goto next##_1; \
141                } else { \
142                    goto next##_0; \
143                }
144
145        DO_BIT(0x40, b6, b5);
146        DO_BIT(0x20, b5, b4);
147        DO_BIT(0x10, b4, b3);
148        DO_BIT(0x08, b3, b2);
149        DO_BIT(0x04, b2, b1);
150        DO_BIT(0x02, b1, b0);
151        DO_BIT(0x01, b0, done);
152
153        #undef DO_BIT
154
155done_1:
156done_0:
157        writel(SCLK, PDDATS);
158        writel(SCLK, PDDATC);
159        writel(SCLK, PDDATC); /* delay to meet t5 timing */
160    }
161}
162
163
164static void bidir(const struct atben_prv *prv, const uint8_t *tx, uint8_t *rx,
165    int len)
166{
167    uint8_t mask, tv, rv;
168
169    while (len--) {
170        tv = *tx++;
171        for (mask = 0x80; mask; mask >>= 1) {
172            if (tv & mask)
173                writel(MOSI, PDDATS);
174            else
175                writel(MOSI, PDDATC);
176            writel(SCLK, PDDATS);
177            if (readl(PDPIN) & MISO)
178                rv |= mask;
179            writel(SCLK, PDDATC);
180        }
181        *rx++ = rv;
182    }
183}
184
185
186static int atben_transfer(struct spi_device *spi, struct spi_message *msg)
187{
188    struct atben_prv *prv = spi_master_get_devdata(spi->master);
189    struct spi_transfer *xfer;
190    const uint8_t *tx;
191    uint8_t *rx;
192
193    if (unlikely(list_empty(&msg->transfers))) {
194        dev_err(prv->dev, "transfer is empty\n");
195        return -EINVAL;
196    }
197
198    msg->actual_length = 0;
199
200    writel(nSEL, PDDATC);
201    list_for_each_entry(xfer, &msg->transfers, transfer_list) {
202        tx = xfer->tx_buf;
203        rx = xfer->rx_buf;
204        msg->actual_length += xfer->len;
205
206        if (!tx)
207            rx_only(prv, rx, xfer->len);
208        else if (!rx)
209            tx_only(prv, tx, xfer->len);
210        else
211            bidir(prv, tx, rx, xfer->len);
212    }
213    writel(nSEL, PDDATS);
214
215    msg->status = 0;
216    msg->complete(msg->context);
217
218    return 0;
219}
220
221static int atben_setup(struct spi_device *spi)
222{
223    return 0;
224}
225
226
227/* ----- SPI master creation/removal --------------------------------------- */
228
229
230const static struct at86rf230_platform_data at86rf230_platform_data = {
231    .rstn = -1,
232    .slp_tr = JZ_GPIO_PORTD(9),
233    .dig2 = -1,
234    .reset = atben_reset,
235    /* set .reset_data later */
236};
237
238static int __devinit atben_probe(struct platform_device *pdev)
239{
240    struct spi_board_info board_info = {
241        .modalias = "at86rf230",
242        /* set .irq later */
243        .chip_select = 0,
244        .bus_num = -1,
245        .max_speed_hz = 8 * 1000 * 1000,
246    };
247
248    struct spi_master *master;
249    struct atben_prv *prv;
250    struct resource *regs;
251    struct spi_device *spi;
252    int err = -ENXIO;
253
254    master = spi_alloc_master(&pdev->dev, sizeof(*prv));
255    if (!master)
256        return -ENOMEM;
257
258    prv = spi_master_get_devdata(master);
259    prv->dev = &pdev->dev;
260    platform_set_drvdata(pdev, spi_master_get(master));
261
262    master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
263    master->bus_num = pdev->id;
264    master->num_chipselect = 1;
265    master->setup = atben_setup;
266    master->transfer = atben_transfer;
267
268    dev_dbg(prv->dev, "Setting up ATBEN SPI\n");
269
270    regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
271    if (!regs) {
272        dev_err(prv->dev, "no IORESOURCE_MEM\n");
273        err = -ENOENT;
274        goto out_master;
275    }
276    prv->ioarea = request_mem_region(regs->start, resource_size(regs),
277                                        pdev->name);
278    if (!prv->ioarea) {
279        dev_err(prv->dev, "can't request ioarea\n");
280        goto out_master;
281    }
282
283    prv->regs = ioremap(regs->start, resource_size(regs));
284    if (!prv->regs) {
285        dev_err(prv->dev, "can't ioremap\n");
286        goto out_ioarea;
287    }
288
289    board_info.irq = platform_get_irq(pdev, 0);
290    if (board_info.irq < 0) {
291        dev_err(prv->dev, "can't get GPIO irq\n");
292        err = -ENOENT;
293        goto out_regs;
294    }
295
296    err = spi_register_master(master);
297    if (err) {
298        dev_err(prv->dev, "can't register master\n");
299        goto out_regs;
300    }
301
302    prv->platform_data = at86rf230_platform_data;
303    prv->platform_data.reset_data = prv;
304    board_info.platform_data = &prv->platform_data;
305
306    spi = spi_new_device(master, &board_info);
307    if (!spi) {
308        dev_err(&pdev->dev, "can't create new device for %s\n",
309            board_info.modalias);
310        err = -ENXIO;
311        goto out_registered;
312    }
313
314    dev_info(&spi->dev, "ATBEN ready for mischief (IRQ %d)\n",
315        board_info.irq);
316
317    return 0;
318
319out_registered:
320    spi_unregister_master(master);
321
322out_regs:
323    iounmap(prv->regs);
324
325out_ioarea:
326    release_resource(prv->ioarea);
327    kfree(prv->ioarea);
328
329out_master:
330    platform_set_drvdata(pdev, NULL);
331    spi_master_put(master);
332
333    return err;
334}
335
336static int __devexit atben_remove(struct platform_device *pdev)
337{
338    struct spi_master *master = platform_get_drvdata(pdev);
339    struct atben_prv *prv = spi_master_get_devdata(master);
340
341// restore GPIOs
342
343    spi_unregister_master(master);
344
345    iounmap(prv->regs);
346
347    release_resource(prv->ioarea);
348    kfree(prv->ioarea);
349
350    platform_set_drvdata(pdev, NULL);
351    spi_master_put(master);
352
353    return 0;
354}
355
356static struct platform_driver atben_driver = {
357    .driver = {
358        .name = "spi_atben",
359        .owner = THIS_MODULE,
360    },
361    .remove = __devexit_p(atben_remove),
362};
363
364static struct resource atben_resources[] = {
365    {
366        .start = JZ4740_GPIO_BASE_ADDR+0x300,
367        .end = JZ4740_GPIO_BASE_ADDR+0x3ff,
368        .flags = IORESOURCE_MEM,
369    },
370    {
371        /* set start and end later */
372        .flags = IORESOURCE_IRQ,
373    },
374};
375
376static struct platform_device atben_device = {
377    .name = "spi_atben",
378    .id = -1,
379    .num_resources = ARRAY_SIZE(atben_resources),
380    .resource = atben_resources,
381};
382
383/*
384 * Registering the platform device just to probe it immediately afterwards
385 * seems a little circuitous. Need to see if there's a better way.
386 *
387 * What we actually should do is this:
388 * - in module init, register the device
389 * - maybe probe as well, but keep the device also if the probe fails
390 * (due to a conflicting driver already occupying the 8:10 slot)
391 * - have a means for user space to kick off driver probing, e.g., when
392 * anything about the 8:10 slot changes
393 */
394
395static int __init atben_init(void)
396{
397    int err;
398
399    err = platform_device_register(&atben_device);
400    if (err)
401        return err;
402
403    atben_resources[1].start = atben_resources[1].end =
404        gpio_to_irq(JZ_GPIO_PORTD(12));
405
406    return platform_driver_probe(&atben_driver, atben_probe);
407}
408
409static void __exit atben_exit(void)
410{
411    platform_driver_unregister(&atben_driver);
412    platform_device_unregister(&atben_device);
413}
414
415module_init(atben_init);
416module_exit(atben_exit);
417
418
419MODULE_DESCRIPTION("ATBEN SPI Controller Driver");
420MODULE_AUTHOR("Werner Almesberger <werner@almesberger.net>");
421MODULE_LICENSE("GPL");
422

Archive Download this file



interactive