OpenCores
URL https://opencores.org/ocsvn/tiny_spi/tiny_spi/trunk

Subversion Repositories tiny_spi

[/] [tiny_spi/] [trunk/] [driver/] [linux-2.6/] [oc_tiny_spi.c] - Blame information for rev 6

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 6 hippo5329
/*
2
 * Opencore tiny_spi driver
3
 *
4
 * Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw>
5
 *
6
 * Based on spi_s3c24xx.c, which is:
7
 * Copyright (c) 2006 Ben Dooks
8
 * Copyright (c) 2006 Simtec Electronics
9
 *      Ben Dooks <ben@simtec.co.uk>
10
 *
11
 * This program is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License version 2 as
13
 * published by the Free Software Foundation.
14
 */
15
#include <linux/init.h>
16
#include <linux/spinlock.h>
17
#include <linux/interrupt.h>
18
#include <linux/delay.h>
19
#include <linux/errno.h>
20
#include <linux/err.h>
21
#include <linux/clk.h>
22
#include <linux/platform_device.h>
23
#include <linux/spi/spi.h>
24
#include <linux/spi/spi_bitbang.h>
25
#include <linux/io.h>
26
#include <linux/gpio.h>
27
 
28
#define DRV_NAME "oc_tiny_spi"
29
 
30
#define TINY_SPI_RXDATA 0
31
#define TINY_SPI_TXDATA 4
32
#define TINY_SPI_STATUS 8
33
#define TINY_SPI_CONTROL        12
34
#define TINY_SPI_BAUD   16
35
 
36
#define TINY_SPI_STATUS_TXE 0x1
37
#define TINY_SPI_STATUS_TXR 0x2
38
 
39
struct tiny_spi {
40
        /* bitbang has to be first */
41
        struct spi_bitbang bitbang;
42
        struct completion done;
43
 
44
        void __iomem *base;
45
        int irq;
46
        uint freq;
47
        uint baudwidth;
48
        uint baud;
49
        uint speed_hz;
50
 
51
        struct spi_master *master;
52
        struct resource *ioarea;
53
        struct device *dev;
54
};
55
 
56
static inline struct tiny_spi *to_hw(struct spi_device *sdev)
57
{
58
        return spi_master_get_devdata(sdev->master);
59
}
60
 
61
static uint tiny_spi_baud(struct spi_device *spi, uint hz)
62
{
63
        struct tiny_spi *hw = to_hw(spi);
64
        uint baud;
65
        baud = DIV_ROUND_UP(hw->freq, hz * 2) - 1;
66
        if (baud > (1 << hw->baudwidth) - 1)
67
                baud = (1 << hw->baudwidth) - 1;
68
        return baud;
69
}
70
 
71
static void tiny_spi_chipselect(struct spi_device *spi, int is_active)
72
{
73
        gpio_set_value(spi->chip_select, (spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
74
}
75
 
76
static int tiny_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
77
{
78
        struct tiny_spi *hw = to_hw(spi);
79
        uint baud = hw->baud;
80
        if (t) {
81
                dev_dbg(&spi->dev, "%s: %u bpw, %d hz\n",
82
                        __func__, t->bits_per_word, t->speed_hz);
83
                if (t->speed_hz && t->speed_hz != hw->speed_hz)
84
                        baud = tiny_spi_baud(spi, t->speed_hz);
85
        }
86
        writel(baud, hw->base + TINY_SPI_BAUD);
87
        return 0;
88
}
89
 
90
static int tiny_spi_setup(struct spi_device *spi)
91
{
92
        struct tiny_spi *hw = to_hw(spi);
93
 
94
        dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n",
95
                __func__, spi->mode, spi->bits_per_word, spi->max_speed_hz);
96
        if (spi->max_speed_hz != hw->speed_hz) {
97
                hw->speed_hz = spi->max_speed_hz;
98
                hw->baud = tiny_spi_baud(spi, hw->speed_hz);
99
        }
100
        return 0;
101
}
102
 
103
#ifndef CONFIG_TINY_SPI_IDLE_VAL
104
# define CONFIG_TINY_SPI_IDLE_VAL 0xff
105
#endif
106
 
107
static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
108
{
109
        struct tiny_spi *hw = to_hw(spi);
110
        const u8 *txp = t->tx_buf;
111
        u8 *rxp = t->rx_buf;
112
        uint i;
113
 
114
        dev_dbg(&spi->dev, "%s: tx %p, rx %p, len %d\n",
115
                __func__, t->tx_buf, t->rx_buf, t->len);
116
 
117
        /* we need to tighten the transfer loop */
118
        if (txp && rxp) {
119
                writeb(*txp++, hw->base + TINY_SPI_TXDATA);
120
                if (t->len > 1) {
121
                        writeb(*txp++, hw->base + TINY_SPI_TXDATA);
122
                        for (i = 2; i < t->len; i++) {
123
                                u8 rx, tx = *txp++;
124
                                while (!(readb(hw->base + TINY_SPI_STATUS) &
125
                                         TINY_SPI_STATUS_TXR))
126
                                        cpu_relax();
127
                                rx = readb(hw->base + TINY_SPI_TXDATA);
128
                                writeb(tx, hw->base + TINY_SPI_TXDATA);
129
                                *rxp++ = rx;
130
                        }
131
                        while (!(readb(hw->base + TINY_SPI_STATUS) &
132
                                 TINY_SPI_STATUS_TXR))
133
                                cpu_relax();
134
                        *rxp++ = readb(hw->base + TINY_SPI_TXDATA);
135
                }
136
                while (!(readb(hw->base + TINY_SPI_STATUS) &
137
                         TINY_SPI_STATUS_TXE))
138
                        cpu_relax();
139
                *rxp++ = readb(hw->base + TINY_SPI_RXDATA);
140
        } else if (rxp) {
141
                writeb(CONFIG_TINY_SPI_IDLE_VAL, hw->base + TINY_SPI_TXDATA);
142
                if (t->len > 1) {
143
                        writeb(CONFIG_TINY_SPI_IDLE_VAL,
144
                               hw->base + TINY_SPI_TXDATA);
145
                        for (i = 2; i < t->len; i++) {
146
                                u8 rx;
147
                                while (!(readb(hw->base + TINY_SPI_STATUS) &
148
                                         TINY_SPI_STATUS_TXR))
149
                                        cpu_relax();
150
                                rx = readb(hw->base + TINY_SPI_TXDATA);
151
                                writeb(CONFIG_TINY_SPI_IDLE_VAL,
152
                                       hw->base + TINY_SPI_TXDATA);
153
                                *rxp++ = rx;
154
                        }
155
                        while (!(readb(hw->base + TINY_SPI_STATUS) &
156
                                 TINY_SPI_STATUS_TXR))
157
                                cpu_relax();
158
                        *rxp++ = readb(hw->base + TINY_SPI_TXDATA);
159
                }
160
                while (!(readb(hw->base + TINY_SPI_STATUS) &
161
                         TINY_SPI_STATUS_TXE))
162
                        cpu_relax();
163
                *rxp++ = readb(hw->base + TINY_SPI_RXDATA);
164
        } else if (txp) {
165
                writeb(*txp++, hw->base + TINY_SPI_TXDATA);
166
                if (t->len > 1) {
167
                        writeb(*txp++, hw->base + TINY_SPI_TXDATA);
168
                        for (i = 2; i < t->len; i++) {
169
                                u8 tx = *txp++;
170
                                while (!(readb(hw->base + TINY_SPI_STATUS) &
171
                                         TINY_SPI_STATUS_TXR))
172
                                        cpu_relax();
173
                                writeb(tx, hw->base + TINY_SPI_TXDATA);
174
                        }
175
                }
176
                while (!(readb(hw->base + TINY_SPI_STATUS) &
177
                         TINY_SPI_STATUS_TXE))
178
                        cpu_relax();
179
        } else {
180
                writeb(CONFIG_TINY_SPI_IDLE_VAL, hw->base + TINY_SPI_TXDATA);
181
                if (t->len > 1) {
182
                        writeb(CONFIG_TINY_SPI_IDLE_VAL,
183
                               hw->base + TINY_SPI_TXDATA);
184
                        for (i = 2; i < t->len; i++) {
185
                                while (!(readb(hw->base + TINY_SPI_STATUS) &
186
                                         TINY_SPI_STATUS_TXR))
187
                                        cpu_relax();
188
                                writeb(CONFIG_TINY_SPI_IDLE_VAL,
189
                                       hw->base + TINY_SPI_TXDATA);
190
                        }
191
                }
192
                while (!(readb(hw->base + TINY_SPI_STATUS) &
193
                         TINY_SPI_STATUS_TXE))
194
                        cpu_relax();
195
        }
196
        return t->len;
197
}
198
 
199
static irqreturn_t tiny_spi_irq(int irq, void *dev)
200
{
201
        struct tiny_spi *hw = dev;
202
        writeb(0, hw->base + TINY_SPI_STATUS);
203
        complete(&hw->done);
204
        return IRQ_HANDLED;
205
}
206
 
207
static int __init tiny_spi_probe(struct platform_device *pdev)
208
{
209
        struct tiny_spi *hw;
210
        struct spi_master *master;
211
        struct resource *res;
212
        int err = 0;
213
 
214
        master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi));
215
        if (master == NULL) {
216
                dev_err(&pdev->dev, "No memory for spi_master\n");
217
                err = -ENOMEM;
218
                goto err_nomem;
219
        }
220
 
221
        hw = spi_master_get_devdata(master);
222
        memset(hw, 0, sizeof(struct tiny_spi));
223
 
224
        hw->master = spi_master_get(master);
225
        hw->dev = &pdev->dev;
226
 
227
        platform_set_drvdata(pdev, hw);
228
        init_completion(&hw->done);
229
 
230
        /* setup the master state. */
231
        master->bus_num = pdev->id;
232
        master->num_chipselect = 256;
233
        master->mode_bits = SPI_CS_HIGH;
234
 
235
        /* setup the state for the bitbang driver */
236
 
237
        hw->bitbang.master = hw->master;
238
        hw->bitbang.setup_transfer = tiny_spi_setup_transfer;
239
        hw->bitbang.chipselect = tiny_spi_chipselect;
240
        hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs;
241
        hw->bitbang.master->setup = tiny_spi_setup;
242
 
243
        dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);
244
 
245
        /* find and map our resources */
246
 
247
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
248
        if (res == NULL) {
249
                dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
250
                err = -ENOENT;
251
                goto err_no_iores;
252
        }
253
 
254
        hw->ioarea = request_mem_region(res->start, (res->end - res->start) + 1,
255
                                        pdev->name);
256
 
257
        if (hw->ioarea == NULL) {
258
                dev_err(&pdev->dev, "Cannot reserve region\n");
259
                err = -ENXIO;
260
                goto err_no_iores;
261
        }
262
 
263
        hw->base =
264
            ioremap(res->start, (res->end - res->start) + 1);
265
        if (hw->base == 0) {
266
                dev_err(&pdev->dev, "Cannot map IO\n");
267
                err = -ENXIO;
268
                goto err_no_iomap;
269
        }
270
 
271
        hw->irq = platform_get_irq(pdev, 0);
272
        if (hw->irq < 0) {
273
                dev_err(&pdev->dev, "No IRQ specified\n");
274
                err = -ENOENT;
275
                goto err_no_irq;
276
        }
277
 
278
        err = request_irq(hw->irq, tiny_spi_irq, 0, pdev->name, hw);
279
        if (err) {
280
                dev_err(&pdev->dev, "Cannot claim IRQ\n");
281
                goto err_no_irq;
282
        }
283
 
284
        hw->freq = 100000000;
285
        hw->baudwidth = 7;
286
        hw->baud = 1;
287
 
288
        dev_info(hw->dev, "base %p, irq %d\n", hw->base, hw->irq);
289
 
290
        /* register our spi controller */
291
        err = spi_bitbang_start(&hw->bitbang);
292
        if (err) {
293
                dev_err(&pdev->dev, "Failed to register SPI master\n");
294
                goto err_register;
295
        }
296
 
297
        return 0;
298
 
299
err_register:
300
        free_irq(hw->irq, hw);
301
err_no_irq:
302
        iounmap((void *)hw->base);
303
err_no_iomap:
304
        release_resource(hw->ioarea);
305
        kfree(hw->ioarea);
306
err_no_iores:
307
        spi_master_put(hw->master);;
308
err_nomem:
309
        return err;
310
}
311
 
312
static int __exit tiny_spi_remove(struct platform_device *dev)
313
{
314
        struct tiny_spi *hw = platform_get_drvdata(dev);
315
 
316
        platform_set_drvdata(dev, NULL);
317
 
318
        spi_unregister_master(hw->master);
319
 
320
        free_irq(hw->irq, hw);
321
        iounmap((void *)hw->base);
322
 
323
        release_resource(hw->ioarea);
324
        kfree(hw->ioarea);
325
 
326
        spi_master_put(hw->master);
327
        return 0;
328
}
329
 
330
static struct platform_driver tiny_spidrv = {
331
        .remove = __exit_p(tiny_spi_remove),
332
        .driver = {
333
                .name = DRV_NAME,
334
                .owner = THIS_MODULE,
335
                .pm = NULL,
336
        },
337
};
338
 
339
static int __init tiny_spi_init(void)
340
{
341
        return platform_driver_probe(&tiny_spidrv, tiny_spi_probe);
342
}
343
 
344
static void __exit tiny_spi_exit(void)
345
{
346
        platform_driver_unregister(&tiny_spidrv);
347
}
348
 
349
module_init(tiny_spi_init);
350
module_exit(tiny_spi_exit);
351
 
352
MODULE_DESCRIPTION("Opencore tiny_spi driver");
353
MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
354
MODULE_LICENSE("GPL");
355
MODULE_ALIAS("platform:" DRV_NAME);

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.