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

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [spi/] [spi_butterfly.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * spi_butterfly.c - parport-to-butterfly adapter
3
 *
4
 * Copyright (C) 2005 David Brownell
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 as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
 */
20
#include <linux/kernel.h>
21
#include <linux/init.h>
22
#include <linux/delay.h>
23
#include <linux/device.h>
24
#include <linux/parport.h>
25
 
26
#include <linux/sched.h>
27
#include <linux/spi/spi.h>
28
#include <linux/spi/spi_bitbang.h>
29
#include <linux/spi/flash.h>
30
 
31
#include <linux/mtd/partitions.h>
32
 
33
 
34
/*
35
 * This uses SPI to talk with an "AVR Butterfly", which is a $US20 card
36
 * with a battery powered AVR microcontroller and lots of goodies.  You
37
 * can use GCC to develop firmware for this.
38
 *
39
 * See Documentation/spi/butterfly for information about how to build
40
 * and use this custom parallel port cable.
41
 */
42
 
43
 
44
/* DATA output bits (pins 2..9 == D0..D7) */
45
#define butterfly_nreset (1 << 1)               /* pin 3 */
46
 
47
#define spi_sck_bit     (1 << 0)                /* pin 2 */
48
#define spi_mosi_bit    (1 << 7)                /* pin 9 */
49
 
50
#define vcc_bits        ((1 << 6) | (1 << 5))   /* pins 7, 8 */
51
 
52
/* STATUS input bits */
53
#define spi_miso_bit    PARPORT_STATUS_BUSY     /* pin 11 */
54
 
55
/* CONTROL output bits */
56
#define spi_cs_bit      PARPORT_CONTROL_SELECT  /* pin 17 */
57
 
58
 
59
 
60
static inline struct butterfly *spidev_to_pp(struct spi_device *spi)
61
{
62
        return spi->controller_data;
63
}
64
 
65
 
66
struct butterfly {
67
        /* REVISIT ... for now, this must be first */
68
        struct spi_bitbang      bitbang;
69
 
70
        struct parport          *port;
71
        struct pardevice        *pd;
72
 
73
        u8                      lastbyte;
74
 
75
        struct spi_device       *dataflash;
76
        struct spi_device       *butterfly;
77
        struct spi_board_info   info[2];
78
 
79
};
80
 
81
/*----------------------------------------------------------------------*/
82
 
83
static inline void
84
setsck(struct spi_device *spi, int is_on)
85
{
86
        struct butterfly        *pp = spidev_to_pp(spi);
87
        u8                      bit, byte = pp->lastbyte;
88
 
89
        bit = spi_sck_bit;
90
 
91
        if (is_on)
92
                byte |= bit;
93
        else
94
                byte &= ~bit;
95
        parport_write_data(pp->port, byte);
96
        pp->lastbyte = byte;
97
}
98
 
99
static inline void
100
setmosi(struct spi_device *spi, int is_on)
101
{
102
        struct butterfly        *pp = spidev_to_pp(spi);
103
        u8                      bit, byte = pp->lastbyte;
104
 
105
        bit = spi_mosi_bit;
106
 
107
        if (is_on)
108
                byte |= bit;
109
        else
110
                byte &= ~bit;
111
        parport_write_data(pp->port, byte);
112
        pp->lastbyte = byte;
113
}
114
 
115
static inline int getmiso(struct spi_device *spi)
116
{
117
        struct butterfly        *pp = spidev_to_pp(spi);
118
        int                     value;
119
        u8                      bit;
120
 
121
        bit = spi_miso_bit;
122
 
123
        /* only STATUS_BUSY is NOT negated */
124
        value = !(parport_read_status(pp->port) & bit);
125
        return (bit == PARPORT_STATUS_BUSY) ? value : !value;
126
}
127
 
128
static void butterfly_chipselect(struct spi_device *spi, int value)
129
{
130
        struct butterfly        *pp = spidev_to_pp(spi);
131
 
132
        /* set default clock polarity */
133
        if (value != BITBANG_CS_INACTIVE)
134
                setsck(spi, spi->mode & SPI_CPOL);
135
 
136
        /* here, value == "activate or not";
137
         * most PARPORT_CONTROL_* bits are negated, so we must
138
         * morph it to value == "bit value to write in control register"
139
         */
140
        if (spi_cs_bit == PARPORT_CONTROL_INIT)
141
                value = !value;
142
 
143
        parport_frob_control(pp->port, spi_cs_bit, value ? spi_cs_bit : 0);
144
}
145
 
146
 
147
/* we only needed to implement one mode here, and choose SPI_MODE_0 */
148
 
149
#define spidelay(X)     do{}while(0)
150
//#define       spidelay        ndelay
151
 
152
#define EXPAND_BITBANG_TXRX
153
#include <linux/spi/spi_bitbang.h>
154
 
155
static u32
156
butterfly_txrx_word_mode0(struct spi_device *spi,
157
                unsigned nsecs,
158
                u32 word, u8 bits)
159
{
160
        return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
161
}
162
 
163
/*----------------------------------------------------------------------*/
164
 
165
/* override default partitioning with cmdlinepart */
166
static struct mtd_partition partitions[] = { {
167
        /* JFFS2 wants partitions of 4*N blocks for this device,
168
         * so sectors 0 and 1 can't be partitions by themselves.
169
         */
170
 
171
        /* sector 0 = 8 pages * 264 bytes/page (1 block)
172
         * sector 1 = 248 pages * 264 bytes/page
173
         */
174
        .name           = "bookkeeping",        // 66 KB
175
        .offset         = 0,
176
        .size           = (8 + 248) * 264,
177
//      .mask_flags     = MTD_WRITEABLE,
178
}, {
179
        /* sector 2 = 256 pages * 264 bytes/page
180
         * sectors 3-5 = 512 pages * 264 bytes/page
181
         */
182
        .name           = "filesystem",         // 462 KB
183
        .offset         = MTDPART_OFS_APPEND,
184
        .size           = MTDPART_SIZ_FULL,
185
} };
186
 
187
static struct flash_platform_data flash = {
188
        .name           = "butterflash",
189
        .parts          = partitions,
190
        .nr_parts       = ARRAY_SIZE(partitions),
191
};
192
 
193
 
194
/* REVISIT remove this ugly global and its "only one" limitation */
195
static struct butterfly *butterfly;
196
 
197
static void butterfly_attach(struct parport *p)
198
{
199
        struct pardevice        *pd;
200
        int                     status;
201
        struct butterfly        *pp;
202
        struct spi_master       *master;
203
        struct device           *dev = p->physport->dev;
204
 
205
        if (butterfly || !dev)
206
                return;
207
 
208
        /* REVISIT:  this just _assumes_ a butterfly is there ... no probe,
209
         * and no way to be selective about what it binds to.
210
         */
211
 
212
        master = spi_alloc_master(dev, sizeof *pp);
213
        if (!master) {
214
                status = -ENOMEM;
215
                goto done;
216
        }
217
        pp = spi_master_get_devdata(master);
218
 
219
        /*
220
         * SPI and bitbang hookup
221
         *
222
         * use default setup(), cleanup(), and transfer() methods; and
223
         * only bother implementing mode 0.  Start it later.
224
         */
225
        master->bus_num = 42;
226
        master->num_chipselect = 2;
227
 
228
        pp->bitbang.master = spi_master_get(master);
229
        pp->bitbang.chipselect = butterfly_chipselect;
230
        pp->bitbang.txrx_word[SPI_MODE_0] = butterfly_txrx_word_mode0;
231
 
232
        /*
233
         * parport hookup
234
         */
235
        pp->port = p;
236
        pd = parport_register_device(p, "spi_butterfly",
237
                        NULL, NULL, NULL,
238
 
239
        if (!pd) {
240
                status = -ENOMEM;
241
                goto clean0;
242
        }
243
        pp->pd = pd;
244
 
245
        status = parport_claim(pd);
246
        if (status < 0)
247
                goto clean1;
248
 
249
        /*
250
         * Butterfly reset, powerup, run firmware
251
         */
252
        pr_debug("%s: powerup/reset Butterfly\n", p->name);
253
 
254
        /* nCS for dataflash (this bit is inverted on output) */
255
        parport_frob_control(pp->port, spi_cs_bit, 0);
256
 
257
        /* stabilize power with chip in reset (nRESET), and
258
         * spi_sck_bit clear (CPOL=0)
259
         */
260
        pp->lastbyte |= vcc_bits;
261
        parport_write_data(pp->port, pp->lastbyte);
262
        msleep(5);
263
 
264
        /* take it out of reset; assume long reset delay */
265
        pp->lastbyte |= butterfly_nreset;
266
        parport_write_data(pp->port, pp->lastbyte);
267
        msleep(100);
268
 
269
 
270
        /*
271
         * Start SPI ... for now, hide that we're two physical busses.
272
         */
273
        status = spi_bitbang_start(&pp->bitbang);
274
        if (status < 0)
275
                goto clean2;
276
 
277
        /* Bus 1 lets us talk to at45db041b (firmware disables AVR SPI), AVR
278
         * (firmware resets at45, acts as spi slave) or neither (we ignore
279
         * both, AVR uses AT45).  Here we expect firmware for the first option.
280
         */
281
 
282
        pp->info[0].max_speed_hz = 15 * 1000 * 1000;
283
        strcpy(pp->info[0].modalias, "mtd_dataflash");
284
        pp->info[0].platform_data = &flash;
285
        pp->info[0].chip_select = 1;
286
        pp->info[0].controller_data = pp;
287
        pp->dataflash = spi_new_device(pp->bitbang.master, &pp->info[0]);
288
        if (pp->dataflash)
289
                pr_debug("%s: dataflash at %s\n", p->name,
290
                                pp->dataflash->dev.bus_id);
291
 
292
        // dev_info(_what?_, ...)
293
        pr_info("%s: AVR Butterfly\n", p->name);
294
        butterfly = pp;
295
        return;
296
 
297
clean2:
298
        /* turn off VCC */
299
        parport_write_data(pp->port, 0);
300
 
301
        parport_release(pp->pd);
302
clean1:
303
        parport_unregister_device(pd);
304
clean0:
305
        (void) spi_master_put(pp->bitbang.master);
306
done:
307
        pr_debug("%s: butterfly probe, fail %d\n", p->name, status);
308
}
309
 
310
static void butterfly_detach(struct parport *p)
311
{
312
        struct butterfly        *pp;
313
        int                     status;
314
 
315
        /* FIXME this global is ugly ... but, how to quickly get from
316
         * the parport to the "struct butterfly" associated with it?
317
         * "old school" driver-internal device lists?
318
         */
319
        if (!butterfly || butterfly->port != p)
320
                return;
321
        pp = butterfly;
322
        butterfly = NULL;
323
 
324
        /* stop() unregisters child devices too */
325
        status = spi_bitbang_stop(&pp->bitbang);
326
 
327
        /* turn off VCC */
328
        parport_write_data(pp->port, 0);
329
        msleep(10);
330
 
331
        parport_release(pp->pd);
332
        parport_unregister_device(pp->pd);
333
 
334
        (void) spi_master_put(pp->bitbang.master);
335
}
336
 
337
static struct parport_driver butterfly_driver = {
338
        .name =         "spi_butterfly",
339
        .attach =       butterfly_attach,
340
        .detach =       butterfly_detach,
341
};
342
 
343
 
344
static int __init butterfly_init(void)
345
{
346
        return parport_register_driver(&butterfly_driver);
347
}
348
device_initcall(butterfly_init);
349
 
350
static void __exit butterfly_exit(void)
351
{
352
        parport_unregister_driver(&butterfly_driver);
353
}
354
module_exit(butterfly_exit);
355
 
356
MODULE_DESCRIPTION("Parport Adapter driver for AVR Butterfly");
357
MODULE_LICENSE("GPL");

powered by: WebSVN 2.1.0

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