Blame view

drivers/spi/spi-butterfly.c 7.91 KB
c942fddf8   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
2e10c84b9   David Brownell   [PATCH] SPI: add ...
2
  /*
ca632f556   Grant Likely   spi: reorganize d...
3
   * parport-to-butterfly adapter
2e10c84b9   David Brownell   [PATCH] SPI: add ...
4
5
   *
   * Copyright (C) 2005 David Brownell
2e10c84b9   David Brownell   [PATCH] SPI: add ...
6
   */
2e10c84b9   David Brownell   [PATCH] SPI: add ...
7
8
9
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/delay.h>
d7614de42   Paul Gortmaker   spi: Add module.h...
10
  #include <linux/module.h>
da6752964   David Brownell   layered parport c...
11
  #include <linux/device.h>
2e10c84b9   David Brownell   [PATCH] SPI: add ...
12
  #include <linux/parport.h>
914e26379   Al Viro   [PATCH] severing ...
13
  #include <linux/sched.h>
2e10c84b9   David Brownell   [PATCH] SPI: add ...
14
15
16
17
18
  #include <linux/spi/spi.h>
  #include <linux/spi/spi_bitbang.h>
  #include <linux/spi/flash.h>
  
  #include <linux/mtd/partitions.h>
2e10c84b9   David Brownell   [PATCH] SPI: add ...
19
20
21
22
23
  /*
   * This uses SPI to talk with an "AVR Butterfly", which is a $US20 card
   * with a battery powered AVR microcontroller and lots of goodies.  You
   * can use GCC to develop firmware for this.
   *
9cdd273e2   Mauro Carvalho Chehab   spi: docs: conver...
24
   * See Documentation/spi/butterfly.rst for information about how to build
2e10c84b9   David Brownell   [PATCH] SPI: add ...
25
26
   * and use this custom parallel port cable.
   */
2e10c84b9   David Brownell   [PATCH] SPI: add ...
27
28
29
30
31
  /* DATA output bits (pins 2..9 == D0..D7) */
  #define	butterfly_nreset (1 << 1)		/* pin 3 */
  
  #define	spi_sck_bit	(1 << 0)		/* pin 2 */
  #define	spi_mosi_bit	(1 << 7)		/* pin 9 */
2e10c84b9   David Brownell   [PATCH] SPI: add ...
32
33
34
35
  #define	vcc_bits	((1 << 6) | (1 << 5))	/* pins 7, 8 */
  
  /* STATUS input bits */
  #define	spi_miso_bit	PARPORT_STATUS_BUSY	/* pin 11 */
2e10c84b9   David Brownell   [PATCH] SPI: add ...
36
37
  /* CONTROL output bits */
  #define	spi_cs_bit	PARPORT_CONTROL_SELECT	/* pin 17 */
2e10c84b9   David Brownell   [PATCH] SPI: add ...
38

2e10c84b9   David Brownell   [PATCH] SPI: add ...
39
40
41
42
  static inline struct butterfly *spidev_to_pp(struct spi_device *spi)
  {
  	return spi->controller_data;
  }
2e10c84b9   David Brownell   [PATCH] SPI: add ...
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
  struct butterfly {
  	/* REVISIT ... for now, this must be first */
  	struct spi_bitbang	bitbang;
  
  	struct parport		*port;
  	struct pardevice	*pd;
  
  	u8			lastbyte;
  
  	struct spi_device	*dataflash;
  	struct spi_device	*butterfly;
  	struct spi_board_info	info[2];
  
  };
  
  /*----------------------------------------------------------------------*/
2e10c84b9   David Brownell   [PATCH] SPI: add ...
59
60
61
62
63
  static inline void
  setsck(struct spi_device *spi, int is_on)
  {
  	struct butterfly	*pp = spidev_to_pp(spi);
  	u8			bit, byte = pp->lastbyte;
735ce95e6   David Brownell   minor spi_butterf...
64
  	bit = spi_sck_bit;
2e10c84b9   David Brownell   [PATCH] SPI: add ...
65
66
67
68
69
70
71
72
73
74
75
76
77
78
  
  	if (is_on)
  		byte |= bit;
  	else
  		byte &= ~bit;
  	parport_write_data(pp->port, byte);
  	pp->lastbyte = byte;
  }
  
  static inline void
  setmosi(struct spi_device *spi, int is_on)
  {
  	struct butterfly	*pp = spidev_to_pp(spi);
  	u8			bit, byte = pp->lastbyte;
735ce95e6   David Brownell   minor spi_butterf...
79
  	bit = spi_mosi_bit;
2e10c84b9   David Brownell   [PATCH] SPI: add ...
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  
  	if (is_on)
  		byte |= bit;
  	else
  		byte &= ~bit;
  	parport_write_data(pp->port, byte);
  	pp->lastbyte = byte;
  }
  
  static inline int getmiso(struct spi_device *spi)
  {
  	struct butterfly	*pp = spidev_to_pp(spi);
  	int			value;
  	u8			bit;
735ce95e6   David Brownell   minor spi_butterf...
94
  	bit = spi_miso_bit;
2e10c84b9   David Brownell   [PATCH] SPI: add ...
95
96
97
98
99
100
101
102
103
104
105
  
  	/* only STATUS_BUSY is NOT negated */
  	value = !(parport_read_status(pp->port) & bit);
  	return (bit == PARPORT_STATUS_BUSY) ? value : !value;
  }
  
  static void butterfly_chipselect(struct spi_device *spi, int value)
  {
  	struct butterfly	*pp = spidev_to_pp(spi);
  
  	/* set default clock polarity */
9c1da3cb4   David Brownell   [PATCH] SPI: spi_...
106
  	if (value != BITBANG_CS_INACTIVE)
2e10c84b9   David Brownell   [PATCH] SPI: add ...
107
  		setsck(spi, spi->mode & SPI_CPOL);
9c1da3cb4   David Brownell   [PATCH] SPI: spi_...
108
109
110
111
  	/* here, value == "activate or not";
  	 * most PARPORT_CONTROL_* bits are negated, so we must
  	 * morph it to value == "bit value to write in control register"
  	 */
2e10c84b9   David Brownell   [PATCH] SPI: add ...
112
113
  	if (spi_cs_bit == PARPORT_CONTROL_INIT)
  		value = !value;
2e10c84b9   David Brownell   [PATCH] SPI: add ...
114
115
  	parport_frob_control(pp->port, spi_cs_bit, value ? spi_cs_bit : 0);
  }
2e10c84b9   David Brownell   [PATCH] SPI: add ...
116
  /* we only needed to implement one mode here, and choose SPI_MODE_0 */
ac8ed236a   Jingoo Han   spi: butterfly: F...
117
118
  #define spidelay(X)	do { } while (0)
  /* #define spidelay	ndelay */
2e10c84b9   David Brownell   [PATCH] SPI: add ...
119

ca632f556   Grant Likely   spi: reorganize d...
120
  #include "spi-bitbang-txrx.h"
2e10c84b9   David Brownell   [PATCH] SPI: add ...
121
122
  
  static u32
e71fec735   Sudip Mukherjee   spi: butterfly: c...
123
  butterfly_txrx_word_mode0(struct spi_device *spi, unsigned nsecs, u32 word,
304d34360   Lorenzo Bianconi   spi: add flags pa...
124
  			  u8 bits, unsigned flags)
2e10c84b9   David Brownell   [PATCH] SPI: add ...
125
  {
304d34360   Lorenzo Bianconi   spi: add flags pa...
126
  	return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits);
2e10c84b9   David Brownell   [PATCH] SPI: add ...
127
128
129
130
131
132
  }
  
  /*----------------------------------------------------------------------*/
  
  /* override default partitioning with cmdlinepart */
  static struct mtd_partition partitions[] = { {
9c1da3cb4   David Brownell   [PATCH] SPI: spi_...
133
134
135
  	/* JFFS2 wants partitions of 4*N blocks for this device,
  	 * so sectors 0 and 1 can't be partitions by themselves.
  	 */
2e10c84b9   David Brownell   [PATCH] SPI: add ...
136
137
138
139
  
  	/* sector 0 = 8 pages * 264 bytes/page (1 block)
  	 * sector 1 = 248 pages * 264 bytes/page
  	 */
ac8ed236a   Jingoo Han   spi: butterfly: F...
140
  	.name		= "bookkeeping",	/* 66 KB */
2e10c84b9   David Brownell   [PATCH] SPI: add ...
141
142
  	.offset		= 0,
  	.size		= (8 + 248) * 264,
ac8ed236a   Jingoo Han   spi: butterfly: F...
143
  	/* .mask_flags	= MTD_WRITEABLE, */
2e10c84b9   David Brownell   [PATCH] SPI: add ...
144
145
146
147
  }, {
  	/* sector 2 = 256 pages * 264 bytes/page
  	 * sectors 3-5 = 512 pages * 264 bytes/page
  	 */
ac8ed236a   Jingoo Han   spi: butterfly: F...
148
  	.name		= "filesystem",		/* 462 KB */
2e10c84b9   David Brownell   [PATCH] SPI: add ...
149
150
151
152
153
154
155
156
157
  	.offset		= MTDPART_OFS_APPEND,
  	.size		= MTDPART_SIZ_FULL,
  } };
  
  static struct flash_platform_data flash = {
  	.name		= "butterflash",
  	.parts		= partitions,
  	.nr_parts	= ARRAY_SIZE(partitions),
  };
2e10c84b9   David Brownell   [PATCH] SPI: add ...
158
159
160
161
162
163
164
165
166
  /* REVISIT remove this ugly global and its "only one" limitation */
  static struct butterfly *butterfly;
  
  static void butterfly_attach(struct parport *p)
  {
  	struct pardevice	*pd;
  	int			status;
  	struct butterfly	*pp;
  	struct spi_master	*master;
da6752964   David Brownell   layered parport c...
167
  	struct device		*dev = p->physport->dev;
1d3029cc5   Sudip Mukherjee   spi: butterfly: u...
168
  	struct pardev_cb	butterfly_cb;
2e10c84b9   David Brownell   [PATCH] SPI: add ...
169

da6752964   David Brownell   layered parport c...
170
  	if (butterfly || !dev)
2e10c84b9   David Brownell   [PATCH] SPI: add ...
171
172
173
174
175
  		return;
  
  	/* REVISIT:  this just _assumes_ a butterfly is there ... no probe,
  	 * and no way to be selective about what it binds to.
  	 */
ac8ed236a   Jingoo Han   spi: butterfly: F...
176
  	master = spi_alloc_master(dev, sizeof(*pp));
2e10c84b9   David Brownell   [PATCH] SPI: add ...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
  	if (!master) {
  		status = -ENOMEM;
  		goto done;
  	}
  	pp = spi_master_get_devdata(master);
  
  	/*
  	 * SPI and bitbang hookup
  	 *
  	 * use default setup(), cleanup(), and transfer() methods; and
  	 * only bother implementing mode 0.  Start it later.
  	 */
  	master->bus_num = 42;
  	master->num_chipselect = 2;
94c69f765   Axel Lin   spi: bitbang: Let...
191
  	pp->bitbang.master = master;
2e10c84b9   David Brownell   [PATCH] SPI: add ...
192
193
194
195
196
197
198
  	pp->bitbang.chipselect = butterfly_chipselect;
  	pp->bitbang.txrx_word[SPI_MODE_0] = butterfly_txrx_word_mode0;
  
  	/*
  	 * parport hookup
  	 */
  	pp->port = p;
1d3029cc5   Sudip Mukherjee   spi: butterfly: u...
199
200
201
  	memset(&butterfly_cb, 0, sizeof(butterfly_cb));
  	butterfly_cb.private = pp;
  	pd = parport_register_dev_model(p, "spi_butterfly", &butterfly_cb, 0);
2e10c84b9   David Brownell   [PATCH] SPI: add ...
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
  	if (!pd) {
  		status = -ENOMEM;
  		goto clean0;
  	}
  	pp->pd = pd;
  
  	status = parport_claim(pd);
  	if (status < 0)
  		goto clean1;
  
  	/*
  	 * Butterfly reset, powerup, run firmware
  	 */
  	pr_debug("%s: powerup/reset Butterfly
  ", p->name);
  
  	/* nCS for dataflash (this bit is inverted on output) */
  	parport_frob_control(pp->port, spi_cs_bit, 0);
  
  	/* stabilize power with chip in reset (nRESET), and
735ce95e6   David Brownell   minor spi_butterf...
222
  	 * spi_sck_bit clear (CPOL=0)
2e10c84b9   David Brownell   [PATCH] SPI: add ...
223
224
225
226
227
228
229
230
231
  	 */
  	pp->lastbyte |= vcc_bits;
  	parport_write_data(pp->port, pp->lastbyte);
  	msleep(5);
  
  	/* take it out of reset; assume long reset delay */
  	pp->lastbyte |= butterfly_nreset;
  	parport_write_data(pp->port, pp->lastbyte);
  	msleep(100);
2e10c84b9   David Brownell   [PATCH] SPI: add ...
232
233
234
235
236
237
  	/*
  	 * Start SPI ... for now, hide that we're two physical busses.
  	 */
  	status = spi_bitbang_start(&pp->bitbang);
  	if (status < 0)
  		goto clean2;
9c1da3cb4   David Brownell   [PATCH] SPI: spi_...
238
239
240
  	/* Bus 1 lets us talk to at45db041b (firmware disables AVR SPI), AVR
  	 * (firmware resets at45, acts as spi slave) or neither (we ignore
  	 * both, AVR uses AT45).  Here we expect firmware for the first option.
2e10c84b9   David Brownell   [PATCH] SPI: add ...
241
  	 */
1fc7547d4   Ben Dooks   [PATCH] S3C24XX: ...
242

2e10c84b9   David Brownell   [PATCH] SPI: add ...
243
244
245
246
247
248
249
250
251
  	pp->info[0].max_speed_hz = 15 * 1000 * 1000;
  	strcpy(pp->info[0].modalias, "mtd_dataflash");
  	pp->info[0].platform_data = &flash;
  	pp->info[0].chip_select = 1;
  	pp->info[0].controller_data = pp;
  	pp->dataflash = spi_new_device(pp->bitbang.master, &pp->info[0]);
  	if (pp->dataflash)
  		pr_debug("%s: dataflash at %s
  ", p->name,
e71fec735   Sudip Mukherjee   spi: butterfly: c...
252
  			 dev_name(&pp->dataflash->dev));
2e10c84b9   David Brownell   [PATCH] SPI: add ...
253

2e10c84b9   David Brownell   [PATCH] SPI: add ...
254
255
256
257
258
259
260
261
262
263
264
265
266
  	pr_info("%s: AVR Butterfly
  ", p->name);
  	butterfly = pp;
  	return;
  
  clean2:
  	/* turn off VCC */
  	parport_write_data(pp->port, 0);
  
  	parport_release(pp->pd);
  clean1:
  	parport_unregister_device(pd);
  clean0:
b12fe7250   Sudip Mukherjee   spi: butterfly: r...
267
  	spi_master_put(pp->bitbang.master);
2e10c84b9   David Brownell   [PATCH] SPI: add ...
268
  done:
2e10c84b9   David Brownell   [PATCH] SPI: add ...
269
270
271
272
273
274
275
  	pr_debug("%s: butterfly probe, fail %d
  ", p->name, status);
  }
  
  static void butterfly_detach(struct parport *p)
  {
  	struct butterfly	*pp;
2e10c84b9   David Brownell   [PATCH] SPI: add ...
276
277
278
279
280
281
282
283
284
  
  	/* FIXME this global is ugly ... but, how to quickly get from
  	 * the parport to the "struct butterfly" associated with it?
  	 * "old school" driver-internal device lists?
  	 */
  	if (!butterfly || butterfly->port != p)
  		return;
  	pp = butterfly;
  	butterfly = NULL;
9c1da3cb4   David Brownell   [PATCH] SPI: spi_...
285
  	/* stop() unregisters child devices too */
d9721ae14   Axel Lin   spi: bitbang: Mak...
286
  	spi_bitbang_stop(&pp->bitbang);
2e10c84b9   David Brownell   [PATCH] SPI: add ...
287
288
289
290
291
292
293
  
  	/* turn off VCC */
  	parport_write_data(pp->port, 0);
  	msleep(10);
  
  	parport_release(pp->pd);
  	parport_unregister_device(pp->pd);
b12fe7250   Sudip Mukherjee   spi: butterfly: r...
294
  	spi_master_put(pp->bitbang.master);
2e10c84b9   David Brownell   [PATCH] SPI: add ...
295
296
297
298
  }
  
  static struct parport_driver butterfly_driver = {
  	.name =		"spi_butterfly",
1d3029cc5   Sudip Mukherjee   spi: butterfly: u...
299
  	.match_port =	butterfly_attach,
2e10c84b9   David Brownell   [PATCH] SPI: add ...
300
  	.detach =	butterfly_detach,
1d3029cc5   Sudip Mukherjee   spi: butterfly: u...
301
  	.devmodel = true,
2e10c84b9   David Brownell   [PATCH] SPI: add ...
302
  };
2e10c84b9   David Brownell   [PATCH] SPI: add ...
303
304
305
306
307
308
309
310
311
312
313
  static int __init butterfly_init(void)
  {
  	return parport_register_driver(&butterfly_driver);
  }
  device_initcall(butterfly_init);
  
  static void __exit butterfly_exit(void)
  {
  	parport_unregister_driver(&butterfly_driver);
  }
  module_exit(butterfly_exit);
9c1da3cb4   David Brownell   [PATCH] SPI: spi_...
314
  MODULE_DESCRIPTION("Parport Adapter driver for AVR Butterfly");
2e10c84b9   David Brownell   [PATCH] SPI: add ...
315
  MODULE_LICENSE("GPL");