aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi_lm70llp.c
diff options
context:
space:
mode:
authorKaiwan N Billimoria <kaiwan@designergraphix.com>2007-07-17 07:04:05 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-17 13:23:04 -0400
commit78961a5740374a8143f8fe120300f2ed160dd276 (patch)
treefccfe279a3c01f139bc845f6c1e9575e8e0efd94 /drivers/spi/spi_lm70llp.c
parent4917d927809918f0070bd1077b41e3daf78643b2 (diff)
spi_lm70llp parport adapter driver
This adds a driver for the LM70-LLP parport adapter, which is an eval board for the LM70 temperature sensor. For those without that board, it may be a simpler example of a parport-to-SPI adapter then spi_butterfly. Signed-off-by: Kaiwan N Billimoria <kaiwan@designergraphix.com> Doc, coding style, and interface updates; build fixes. Minor rename. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/spi/spi_lm70llp.c')
-rw-r--r--drivers/spi/spi_lm70llp.c361
1 files changed, 361 insertions, 0 deletions
diff --git a/drivers/spi/spi_lm70llp.c b/drivers/spi/spi_lm70llp.c
new file mode 100644
index 000000000000..4ea68ac16115
--- /dev/null
+++ b/drivers/spi/spi_lm70llp.c
@@ -0,0 +1,361 @@
1/*
2 * spi_lm70llp.c - driver for lm70llp eval board for the LM70 sensor
3 *
4 * Copyright (C) 2006 Kaiwan N Billimoria <kaiwan@designergraphix.com>
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
21#include <linux/init.h>
22#include <linux/module.h>
23#include <linux/kernel.h>
24#include <linux/delay.h>
25#include <linux/device.h>
26#include <linux/parport.h>
27#include <linux/sysfs.h>
28#include <linux/workqueue.h>
29
30
31#include <linux/spi/spi.h>
32#include <linux/spi/spi_bitbang.h>
33
34
35/*
36 * The LM70 communicates with a host processor using a 3-wire variant of
37 * the SPI/Microwire bus interface. This driver specifically supports an
38 * NS LM70 LLP Evaluation Board, interfacing to a PC using its parallel
39 * port to bitbang an SPI-parport bridge. Accordingly, this is an SPI
40 * master controller driver. The hwmon/lm70 driver is a "SPI protocol
41 * driver", layered on top of this one and usable without the lm70llp.
42 *
43 * The LM70 is a temperature sensor chip from National Semiconductor; its
44 * datasheet is available at http://www.national.com/pf/LM/LM70.html
45 *
46 * Also see Documentation/spi/spi-lm70llp. The SPI<->parport code here is
47 * (heavily) based on spi-butterfly by David Brownell.
48 *
49 * The LM70 LLP connects to the PC parallel port in the following manner:
50 *
51 * Parallel LM70 LLP
52 * Port Direction JP2 Header
53 * ----------- --------- ------------
54 * D0 2 - -
55 * D1 3 --> V+ 5
56 * D2 4 --> V+ 5
57 * D3 5 --> V+ 5
58 * D4 6 --> V+ 5
59 * D5 7 --> nCS 8
60 * D6 8 --> SCLK 3
61 * D7 9 --> SI/O 5
62 * GND 25 - GND 7
63 * Select 13 <-- SI/O 1
64 *
65 * Note that parport pin 13 actually gets inverted by the transistor
66 * arrangement which lets either the parport or the LM70 drive the
67 * SI/SO signal.
68 */
69
70#define DRVNAME "spi-lm70llp"
71
72#define lm70_INIT 0xBE
73#define SIO 0x10
74#define nCS 0x20
75#define SCLK 0x40
76
77/*-------------------------------------------------------------------------*/
78
79struct spi_lm70llp {
80 struct spi_bitbang bitbang;
81 struct parport *port;
82 struct pardevice *pd;
83 struct spi_device *spidev_lm70;
84 struct spi_board_info info;
85 struct class_device *cdev;
86};
87
88/* REVISIT : ugly global ; provides "exclusive open" facility */
89static struct spi_lm70llp *lm70llp;
90
91
92/*-------------------------------------------------------------------*/
93
94static inline struct spi_lm70llp *spidev_to_pp(struct spi_device *spi)
95{
96 return spi->controller_data;
97}
98
99/*---------------------- LM70 LLP eval board-specific inlines follow */
100
101/* NOTE: we don't actually need to reread the output values, since they'll
102 * still be what we wrote before. Plus, going through parport builds in
103 * a ~1ms/operation delay; these SPI transfers could easily be faster.
104 */
105
106static inline void deassertCS(struct spi_lm70llp *pp)
107{
108 u8 data = parport_read_data(pp->port);
109 parport_write_data(pp->port, data | nCS);
110}
111
112static inline void assertCS(struct spi_lm70llp *pp)
113{
114 u8 data = parport_read_data(pp->port);
115 parport_write_data(pp->port, data & ~nCS);
116}
117
118static inline void clkHigh(struct spi_lm70llp *pp)
119{
120 u8 data = parport_read_data(pp->port);
121 parport_write_data(pp->port, data | SCLK);
122}
123
124static inline void clkLow(struct spi_lm70llp *pp)
125{
126 u8 data = parport_read_data(pp->port);
127 parport_write_data(pp->port, data & ~SCLK);
128}
129
130/*------------------------- SPI-LM70-specific inlines ----------------------*/
131
132static inline void spidelay(unsigned d)
133{
134 udelay(d);
135}
136
137static inline void setsck(struct spi_device *s, int is_on)
138{
139 struct spi_lm70llp *pp = spidev_to_pp(s);
140
141 if (is_on)
142 clkHigh(pp);
143 else
144 clkLow(pp);
145}
146
147static inline void setmosi(struct spi_device *s, int is_on)
148{
149 /* FIXME update D7 ... this way we can put the chip
150 * into shutdown mode and read the manufacturer ID,
151 * but we can't put it back into operational mode.
152 */
153}
154
155/*
156 * getmiso:
157 * Why do we return 0 when the SIO line is high and vice-versa?
158 * The fact is, the lm70 eval board from NS (which this driver drives),
159 * is wired in just such a way : when the lm70's SIO goes high, a transistor
160 * switches it to low reflecting this on the parport (pin 13), and vice-versa.
161 */
162static inline int getmiso(struct spi_device *s)
163{
164 struct spi_lm70llp *pp = spidev_to_pp(s);
165 return ((SIO == (parport_read_status(pp->port) & SIO)) ? 0 : 1 );
166}
167/*--------------------------------------------------------------------*/
168
169#define EXPAND_BITBANG_TXRX 1
170#include <linux/spi/spi_bitbang.h>
171
172static void lm70_chipselect(struct spi_device *spi, int value)
173{
174 struct spi_lm70llp *pp = spidev_to_pp(spi);
175
176 if (value)
177 assertCS(pp);
178 else
179 deassertCS(pp);
180}
181
182/*
183 * Our actual bitbanger routine.
184 */
185static u32 lm70_txrx(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits)
186{
187 static u32 sio=0;
188 static int first_time=1;
189
190 /* First time: perform SPI bitbang and return the LSB of
191 * the result of the SPI call.
192 */
193 if (first_time) {
194 sio = bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
195 first_time=0;
196 return (sio & 0x00ff);
197 }
198 /* Return the MSB of the result of the SPI call */
199 else {
200 first_time=1;
201 return (sio >> 8);
202 }
203}
204
205static void spi_lm70llp_attach(struct parport *p)
206{
207 struct pardevice *pd;
208 struct spi_lm70llp *pp;
209 struct spi_master *master;
210 int status;
211
212 if (lm70llp) {
213 printk(KERN_WARNING
214 "%s: spi_lm70llp instance already loaded. Aborting.\n",
215 DRVNAME);
216 return;
217 }
218
219 /* TODO: this just _assumes_ a lm70 is there ... no probe;
220 * the lm70 driver could verify it, reading the manf ID.
221 */
222
223 master = spi_alloc_master(p->physport->dev, sizeof *pp);
224 if (!master) {
225 status = -ENOMEM;
226 goto out_fail;
227 }
228 pp = spi_master_get_devdata(master);
229
230 master->bus_num = -1; /* dynamic alloc of a bus number */
231 master->num_chipselect = 1;
232
233 /*
234 * SPI and bitbang hookup.
235 */
236 pp->bitbang.master = spi_master_get(master);
237 pp->bitbang.chipselect = lm70_chipselect;
238 pp->bitbang.txrx_word[SPI_MODE_0] = lm70_txrx;
239 pp->bitbang.flags = SPI_3WIRE;
240
241 /*
242 * Parport hookup
243 */
244 pp->port = p;
245 pd = parport_register_device(p, DRVNAME,
246 NULL, NULL, NULL,
247 PARPORT_FLAG_EXCL, pp);
248 if (!pd) {
249 status = -ENOMEM;
250 goto out_free_master;
251 }
252 pp->pd = pd;
253
254 status = parport_claim(pd);
255 if (status < 0)
256 goto out_parport_unreg;
257
258 /*
259 * Start SPI ...
260 */
261 status = spi_bitbang_start(&pp->bitbang);
262 if (status < 0) {
263 printk(KERN_WARNING
264 "%s: spi_bitbang_start failed with status %d\n",
265 DRVNAME, status);
266 goto out_off_and_release;
267 }
268
269 /*
270 * The modalias name MUST match the device_driver name
271 * for the bus glue code to match and subsequently bind them.
272 * We are binding to the generic drivers/hwmon/lm70.c device
273 * driver.
274 */
275 strcpy(pp->info.modalias, "lm70");
276 pp->info.max_speed_hz = 6 * 1000 * 1000;
277 pp->info.chip_select = 0;
278 pp->info.mode = SPI_3WIRE | SPI_MODE_0;
279
280 /* power up the chip, and let the LM70 control SI/SO */
281 parport_write_data(pp->port, lm70_INIT);
282
283 /* Enable access to our primary data structure via
284 * the board info's (void *)controller_data.
285 */
286 pp->info.controller_data = pp;
287 pp->spidev_lm70 = spi_new_device(pp->bitbang.master, &pp->info);
288 if (pp->spidev_lm70)
289 dev_dbg(&pp->spidev_lm70->dev, "spidev_lm70 at %s\n",
290 pp->spidev_lm70->dev.bus_id);
291 else {
292 printk(KERN_WARNING "%s: spi_new_device failed\n", DRVNAME);
293 status = -ENODEV;
294 goto out_bitbang_stop;
295 }
296 pp->spidev_lm70->bits_per_word = 16;
297
298 lm70llp = pp;
299
300 return;
301
302out_bitbang_stop:
303 spi_bitbang_stop(&pp->bitbang);
304out_off_and_release:
305 /* power down */
306 parport_write_data(pp->port, 0);
307 mdelay(10);
308 parport_release(pp->pd);
309out_parport_unreg:
310 parport_unregister_device(pd);
311out_free_master:
312 (void) spi_master_put(master);
313out_fail:
314 pr_info("%s: spi_lm70llp probe fail, status %d\n", DRVNAME, status);
315}
316
317static void spi_lm70llp_detach(struct parport *p)
318{
319 struct spi_lm70llp *pp;
320
321 if (!lm70llp || lm70llp->port != p)
322 return;
323
324 pp = lm70llp;
325 spi_bitbang_stop(&pp->bitbang);
326
327 /* power down */
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 lm70llp = NULL;
337}
338
339
340static struct parport_driver spi_lm70llp_drv = {
341 .name = DRVNAME,
342 .attach = spi_lm70llp_attach,
343 .detach = spi_lm70llp_detach,
344};
345
346static int __init init_spi_lm70llp(void)
347{
348 return parport_register_driver(&spi_lm70llp_drv);
349}
350module_init(init_spi_lm70llp);
351
352static void __exit cleanup_spi_lm70llp(void)
353{
354 parport_unregister_driver(&spi_lm70llp_drv);
355}
356module_exit(cleanup_spi_lm70llp);
357
358MODULE_AUTHOR("Kaiwan N Billimoria <kaiwan@designergraphix.com>");
359MODULE_DESCRIPTION(
360 "Parport adapter for the National Semiconductor LM70 LLP eval board");
361MODULE_LICENSE("GPL");