1ca632f55SGrant Likely /* 2ca632f55SGrant Likely * Driver for LM70EVAL-LLP board for the LM70 sensor 3ca632f55SGrant Likely * 4ca632f55SGrant Likely * Copyright (C) 2006 Kaiwan N Billimoria <kaiwan@designergraphix.com> 5ca632f55SGrant Likely * 6ca632f55SGrant Likely * This program is free software; you can redistribute it and/or modify 7ca632f55SGrant Likely * it under the terms of the GNU General Public License as published by 8ca632f55SGrant Likely * the Free Software Foundation; either version 2 of the License, or 9ca632f55SGrant Likely * (at your option) any later version. 10ca632f55SGrant Likely * 11ca632f55SGrant Likely * This program is distributed in the hope that it will be useful, 12ca632f55SGrant Likely * but WITHOUT ANY WARRANTY; without even the implied warranty of 13ca632f55SGrant Likely * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14ca632f55SGrant Likely * GNU General Public License for more details. 15ca632f55SGrant Likely */ 16ca632f55SGrant Likely 17*22de54a9SSudip Mukherjee #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 18*22de54a9SSudip Mukherjee 19ca632f55SGrant Likely #include <linux/init.h> 20ca632f55SGrant Likely #include <linux/module.h> 21ca632f55SGrant Likely #include <linux/kernel.h> 22ca632f55SGrant Likely #include <linux/delay.h> 23ca632f55SGrant Likely #include <linux/device.h> 24ca632f55SGrant Likely #include <linux/parport.h> 25ca632f55SGrant Likely #include <linux/sysfs.h> 26ca632f55SGrant Likely #include <linux/workqueue.h> 27ca632f55SGrant Likely 28ca632f55SGrant Likely #include <linux/spi/spi.h> 29ca632f55SGrant Likely #include <linux/spi/spi_bitbang.h> 30ca632f55SGrant Likely 31ca632f55SGrant Likely /* 32ca632f55SGrant Likely * The LM70 communicates with a host processor using a 3-wire variant of 33ca632f55SGrant Likely * the SPI/Microwire bus interface. This driver specifically supports an 34ca632f55SGrant Likely * NS LM70 LLP Evaluation Board, interfacing to a PC using its parallel 35ca632f55SGrant Likely * port to bitbang an SPI-parport bridge. Accordingly, this is an SPI 36ca632f55SGrant Likely * master controller driver. The hwmon/lm70 driver is a "SPI protocol 37ca632f55SGrant Likely * driver", layered on top of this one and usable without the lm70llp. 38ca632f55SGrant Likely * 39ca632f55SGrant Likely * Datasheet and Schematic: 40ca632f55SGrant Likely * The LM70 is a temperature sensor chip from National Semiconductor; its 41ca632f55SGrant Likely * datasheet is available at http://www.national.com/pf/LM/LM70.html 42ca632f55SGrant Likely * The schematic for this particular board (the LM70EVAL-LLP) is 43ca632f55SGrant Likely * available (on page 4) here: 44ca632f55SGrant Likely * http://www.national.com/appinfo/tempsensors/files/LM70LLPEVALmanual.pdf 45ca632f55SGrant Likely * 46ca632f55SGrant Likely * Also see Documentation/spi/spi-lm70llp. The SPI<->parport code here is 47ca632f55SGrant Likely * (heavily) based on spi-butterfly by David Brownell. 48ca632f55SGrant Likely * 49ca632f55SGrant Likely * The LM70 LLP connects to the PC parallel port in the following manner: 50ca632f55SGrant Likely * 51ca632f55SGrant Likely * Parallel LM70 LLP 52ca632f55SGrant Likely * Port Direction JP2 Header 53ca632f55SGrant Likely * ----------- --------- ------------ 54ca632f55SGrant Likely * D0 2 - - 55ca632f55SGrant Likely * D1 3 --> V+ 5 56ca632f55SGrant Likely * D2 4 --> V+ 5 57ca632f55SGrant Likely * D3 5 --> V+ 5 58ca632f55SGrant Likely * D4 6 --> V+ 5 59ca632f55SGrant Likely * D5 7 --> nCS 8 60ca632f55SGrant Likely * D6 8 --> SCLK 3 61ca632f55SGrant Likely * D7 9 --> SI/O 5 62ca632f55SGrant Likely * GND 25 - GND 7 63ca632f55SGrant Likely * Select 13 <-- SI/O 1 64ca632f55SGrant Likely * 65ca632f55SGrant Likely * Note that parport pin 13 actually gets inverted by the transistor 66ca632f55SGrant Likely * arrangement which lets either the parport or the LM70 drive the 67ca632f55SGrant Likely * SI/SO signal (see the schematic for details). 68ca632f55SGrant Likely */ 69ca632f55SGrant Likely 70ca632f55SGrant Likely #define DRVNAME "spi-lm70llp" 71ca632f55SGrant Likely 72ca632f55SGrant Likely #define lm70_INIT 0xBE 73ca632f55SGrant Likely #define SIO 0x10 74ca632f55SGrant Likely #define nCS 0x20 75ca632f55SGrant Likely #define SCLK 0x40 76ca632f55SGrant Likely 77ca632f55SGrant Likely /*-------------------------------------------------------------------------*/ 78ca632f55SGrant Likely 79ca632f55SGrant Likely struct spi_lm70llp { 80ca632f55SGrant Likely struct spi_bitbang bitbang; 81ca632f55SGrant Likely struct parport *port; 82ca632f55SGrant Likely struct pardevice *pd; 83ca632f55SGrant Likely struct spi_device *spidev_lm70; 84ca632f55SGrant Likely struct spi_board_info info; 85ca632f55SGrant Likely //struct device *dev; 86ca632f55SGrant Likely }; 87ca632f55SGrant Likely 88ca632f55SGrant Likely /* REVISIT : ugly global ; provides "exclusive open" facility */ 89ca632f55SGrant Likely static struct spi_lm70llp *lm70llp; 90ca632f55SGrant Likely 91ca632f55SGrant Likely /*-------------------------------------------------------------------*/ 92ca632f55SGrant Likely 93ca632f55SGrant Likely static inline struct spi_lm70llp *spidev_to_pp(struct spi_device *spi) 94ca632f55SGrant Likely { 95ca632f55SGrant Likely return spi->controller_data; 96ca632f55SGrant Likely } 97ca632f55SGrant Likely 98ca632f55SGrant Likely /*---------------------- LM70 LLP eval board-specific inlines follow */ 99ca632f55SGrant Likely 100ca632f55SGrant Likely /* NOTE: we don't actually need to reread the output values, since they'll 101ca632f55SGrant Likely * still be what we wrote before. Plus, going through parport builds in 102ca632f55SGrant Likely * a ~1ms/operation delay; these SPI transfers could easily be faster. 103ca632f55SGrant Likely */ 104ca632f55SGrant Likely 105ca632f55SGrant Likely static inline void deassertCS(struct spi_lm70llp *pp) 106ca632f55SGrant Likely { 107ca632f55SGrant Likely u8 data = parport_read_data(pp->port); 108ca632f55SGrant Likely 109ca632f55SGrant Likely data &= ~0x80; /* pull D7/SI-out low while de-asserted */ 110ca632f55SGrant Likely parport_write_data(pp->port, data | nCS); 111ca632f55SGrant Likely } 112ca632f55SGrant Likely 113ca632f55SGrant Likely static inline void assertCS(struct spi_lm70llp *pp) 114ca632f55SGrant Likely { 115ca632f55SGrant Likely u8 data = parport_read_data(pp->port); 116ca632f55SGrant Likely 117ca632f55SGrant Likely data |= 0x80; /* pull D7/SI-out high so lm70 drives SO-in */ 118ca632f55SGrant Likely parport_write_data(pp->port, data & ~nCS); 119ca632f55SGrant Likely } 120ca632f55SGrant Likely 121ca632f55SGrant Likely static inline void clkHigh(struct spi_lm70llp *pp) 122ca632f55SGrant Likely { 123ca632f55SGrant Likely u8 data = parport_read_data(pp->port); 124832329d7SSudip Mukherjee 125ca632f55SGrant Likely parport_write_data(pp->port, data | SCLK); 126ca632f55SGrant Likely } 127ca632f55SGrant Likely 128ca632f55SGrant Likely static inline void clkLow(struct spi_lm70llp *pp) 129ca632f55SGrant Likely { 130ca632f55SGrant Likely u8 data = parport_read_data(pp->port); 131832329d7SSudip Mukherjee 132ca632f55SGrant Likely parport_write_data(pp->port, data & ~SCLK); 133ca632f55SGrant Likely } 134ca632f55SGrant Likely 135ca632f55SGrant Likely /*------------------------- SPI-LM70-specific inlines ----------------------*/ 136ca632f55SGrant Likely 137ca632f55SGrant Likely static inline void spidelay(unsigned d) 138ca632f55SGrant Likely { 139ca632f55SGrant Likely udelay(d); 140ca632f55SGrant Likely } 141ca632f55SGrant Likely 142ca632f55SGrant Likely static inline void setsck(struct spi_device *s, int is_on) 143ca632f55SGrant Likely { 144ca632f55SGrant Likely struct spi_lm70llp *pp = spidev_to_pp(s); 145ca632f55SGrant Likely 146ca632f55SGrant Likely if (is_on) 147ca632f55SGrant Likely clkHigh(pp); 148ca632f55SGrant Likely else 149ca632f55SGrant Likely clkLow(pp); 150ca632f55SGrant Likely } 151ca632f55SGrant Likely 152ca632f55SGrant Likely static inline void setmosi(struct spi_device *s, int is_on) 153ca632f55SGrant Likely { 154ca632f55SGrant Likely /* FIXME update D7 ... this way we can put the chip 155ca632f55SGrant Likely * into shutdown mode and read the manufacturer ID, 156ca632f55SGrant Likely * but we can't put it back into operational mode. 157ca632f55SGrant Likely */ 158ca632f55SGrant Likely } 159ca632f55SGrant Likely 160ca632f55SGrant Likely /* 161ca632f55SGrant Likely * getmiso: 162ca632f55SGrant Likely * Why do we return 0 when the SIO line is high and vice-versa? 163ca632f55SGrant Likely * The fact is, the lm70 eval board from NS (which this driver drives), 164ca632f55SGrant Likely * is wired in just such a way : when the lm70's SIO goes high, a transistor 165ca632f55SGrant Likely * switches it to low reflecting this on the parport (pin 13), and vice-versa. 166ca632f55SGrant Likely */ 167ca632f55SGrant Likely static inline int getmiso(struct spi_device *s) 168ca632f55SGrant Likely { 169ca632f55SGrant Likely struct spi_lm70llp *pp = spidev_to_pp(s); 170832329d7SSudip Mukherjee 171ca632f55SGrant Likely return ((SIO == (parport_read_status(pp->port) & SIO)) ? 0 : 1); 172ca632f55SGrant Likely } 173832329d7SSudip Mukherjee 174ca632f55SGrant Likely /*--------------------------------------------------------------------*/ 175ca632f55SGrant Likely 176ca632f55SGrant Likely #include "spi-bitbang-txrx.h" 177ca632f55SGrant Likely 178ca632f55SGrant Likely static void lm70_chipselect(struct spi_device *spi, int value) 179ca632f55SGrant Likely { 180ca632f55SGrant Likely struct spi_lm70llp *pp = spidev_to_pp(spi); 181ca632f55SGrant Likely 182ca632f55SGrant Likely if (value) 183ca632f55SGrant Likely assertCS(pp); 184ca632f55SGrant Likely else 185ca632f55SGrant Likely deassertCS(pp); 186ca632f55SGrant Likely } 187ca632f55SGrant Likely 188ca632f55SGrant Likely /* 189ca632f55SGrant Likely * Our actual bitbanger routine. 190ca632f55SGrant Likely */ 191ca632f55SGrant Likely static u32 lm70_txrx(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) 192ca632f55SGrant Likely { 193ca632f55SGrant Likely return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); 194ca632f55SGrant Likely } 195ca632f55SGrant Likely 196ca632f55SGrant Likely static void spi_lm70llp_attach(struct parport *p) 197ca632f55SGrant Likely { 198ca632f55SGrant Likely struct pardevice *pd; 199ca632f55SGrant Likely struct spi_lm70llp *pp; 200ca632f55SGrant Likely struct spi_master *master; 201ca632f55SGrant Likely int status; 2022baed30cSSudip Mukherjee struct pardev_cb lm70llp_cb; 203ca632f55SGrant Likely 204ca632f55SGrant Likely if (lm70llp) { 205*22de54a9SSudip Mukherjee pr_warn("spi_lm70llp instance already loaded. Aborting.\n"); 206ca632f55SGrant Likely return; 207ca632f55SGrant Likely } 208ca632f55SGrant Likely 209ca632f55SGrant Likely /* TODO: this just _assumes_ a lm70 is there ... no probe; 210ca632f55SGrant Likely * the lm70 driver could verify it, reading the manf ID. 211ca632f55SGrant Likely */ 212ca632f55SGrant Likely 213ca632f55SGrant Likely master = spi_alloc_master(p->physport->dev, sizeof *pp); 214ca632f55SGrant Likely if (!master) { 215ca632f55SGrant Likely status = -ENOMEM; 216ca632f55SGrant Likely goto out_fail; 217ca632f55SGrant Likely } 218ca632f55SGrant Likely pp = spi_master_get_devdata(master); 219ca632f55SGrant Likely 220ca632f55SGrant Likely /* 221ca632f55SGrant Likely * SPI and bitbang hookup. 222ca632f55SGrant Likely */ 22394c69f76SAxel Lin pp->bitbang.master = master; 224ca632f55SGrant Likely pp->bitbang.chipselect = lm70_chipselect; 225ca632f55SGrant Likely pp->bitbang.txrx_word[SPI_MODE_0] = lm70_txrx; 226ca632f55SGrant Likely pp->bitbang.flags = SPI_3WIRE; 227ca632f55SGrant Likely 228ca632f55SGrant Likely /* 229ca632f55SGrant Likely * Parport hookup 230ca632f55SGrant Likely */ 231ca632f55SGrant Likely pp->port = p; 2322baed30cSSudip Mukherjee memset(&lm70llp_cb, 0, sizeof(lm70llp_cb)); 2332baed30cSSudip Mukherjee lm70llp_cb.private = pp; 2342baed30cSSudip Mukherjee lm70llp_cb.flags = PARPORT_FLAG_EXCL; 2352baed30cSSudip Mukherjee pd = parport_register_dev_model(p, DRVNAME, &lm70llp_cb, 0); 2362baed30cSSudip Mukherjee 237ca632f55SGrant Likely if (!pd) { 238ca632f55SGrant Likely status = -ENOMEM; 239ca632f55SGrant Likely goto out_free_master; 240ca632f55SGrant Likely } 241ca632f55SGrant Likely pp->pd = pd; 242ca632f55SGrant Likely 243ca632f55SGrant Likely status = parport_claim(pd); 244ca632f55SGrant Likely if (status < 0) 245ca632f55SGrant Likely goto out_parport_unreg; 246ca632f55SGrant Likely 247ca632f55SGrant Likely /* 248ca632f55SGrant Likely * Start SPI ... 249ca632f55SGrant Likely */ 250ca632f55SGrant Likely status = spi_bitbang_start(&pp->bitbang); 251ca632f55SGrant Likely if (status < 0) { 25246c52c28SSudip Mukherjee dev_warn(&pd->dev, "spi_bitbang_start failed with status %d\n", 25346c52c28SSudip Mukherjee status); 254ca632f55SGrant Likely goto out_off_and_release; 255ca632f55SGrant Likely } 256ca632f55SGrant Likely 257ca632f55SGrant Likely /* 258ca632f55SGrant Likely * The modalias name MUST match the device_driver name 259ca632f55SGrant Likely * for the bus glue code to match and subsequently bind them. 260ca632f55SGrant Likely * We are binding to the generic drivers/hwmon/lm70.c device 261ca632f55SGrant Likely * driver. 262ca632f55SGrant Likely */ 263ca632f55SGrant Likely strcpy(pp->info.modalias, "lm70"); 264ca632f55SGrant Likely pp->info.max_speed_hz = 6 * 1000 * 1000; 265ca632f55SGrant Likely pp->info.chip_select = 0; 266ca632f55SGrant Likely pp->info.mode = SPI_3WIRE | SPI_MODE_0; 267ca632f55SGrant Likely 268ca632f55SGrant Likely /* power up the chip, and let the LM70 control SI/SO */ 269ca632f55SGrant Likely parport_write_data(pp->port, lm70_INIT); 270ca632f55SGrant Likely 271ca632f55SGrant Likely /* Enable access to our primary data structure via 272ca632f55SGrant Likely * the board info's (void *)controller_data. 273ca632f55SGrant Likely */ 274ca632f55SGrant Likely pp->info.controller_data = pp; 275ca632f55SGrant Likely pp->spidev_lm70 = spi_new_device(pp->bitbang.master, &pp->info); 276ca632f55SGrant Likely if (pp->spidev_lm70) 277ca632f55SGrant Likely dev_dbg(&pp->spidev_lm70->dev, "spidev_lm70 at %s\n", 278ca632f55SGrant Likely dev_name(&pp->spidev_lm70->dev)); 279ca632f55SGrant Likely else { 28046c52c28SSudip Mukherjee dev_warn(&pd->dev, "spi_new_device failed\n"); 281ca632f55SGrant Likely status = -ENODEV; 282ca632f55SGrant Likely goto out_bitbang_stop; 283ca632f55SGrant Likely } 284ca632f55SGrant Likely pp->spidev_lm70->bits_per_word = 8; 285ca632f55SGrant Likely 286ca632f55SGrant Likely lm70llp = pp; 287ca632f55SGrant Likely return; 288ca632f55SGrant Likely 289ca632f55SGrant Likely out_bitbang_stop: 290ca632f55SGrant Likely spi_bitbang_stop(&pp->bitbang); 291ca632f55SGrant Likely out_off_and_release: 292ca632f55SGrant Likely /* power down */ 293ca632f55SGrant Likely parport_write_data(pp->port, 0); 294ca632f55SGrant Likely mdelay(10); 295ca632f55SGrant Likely parport_release(pp->pd); 296ca632f55SGrant Likely out_parport_unreg: 297ca632f55SGrant Likely parport_unregister_device(pd); 298ca632f55SGrant Likely out_free_master: 29925fb1a46SSudip Mukherjee spi_master_put(master); 300ca632f55SGrant Likely out_fail: 301*22de54a9SSudip Mukherjee pr_info("spi_lm70llp probe fail, status %d\n", status); 302ca632f55SGrant Likely } 303ca632f55SGrant Likely 304ca632f55SGrant Likely static void spi_lm70llp_detach(struct parport *p) 305ca632f55SGrant Likely { 306ca632f55SGrant Likely struct spi_lm70llp *pp; 307ca632f55SGrant Likely 308ca632f55SGrant Likely if (!lm70llp || lm70llp->port != p) 309ca632f55SGrant Likely return; 310ca632f55SGrant Likely 311ca632f55SGrant Likely pp = lm70llp; 312ca632f55SGrant Likely spi_bitbang_stop(&pp->bitbang); 313ca632f55SGrant Likely 314ca632f55SGrant Likely /* power down */ 315ca632f55SGrant Likely parport_write_data(pp->port, 0); 316ca632f55SGrant Likely 317ca632f55SGrant Likely parport_release(pp->pd); 318ca632f55SGrant Likely parport_unregister_device(pp->pd); 319ca632f55SGrant Likely 32025fb1a46SSudip Mukherjee spi_master_put(pp->bitbang.master); 321ca632f55SGrant Likely 322ca632f55SGrant Likely lm70llp = NULL; 323ca632f55SGrant Likely } 324ca632f55SGrant Likely 325ca632f55SGrant Likely static struct parport_driver spi_lm70llp_drv = { 326ca632f55SGrant Likely .name = DRVNAME, 3272baed30cSSudip Mukherjee .match_port = spi_lm70llp_attach, 328ca632f55SGrant Likely .detach = spi_lm70llp_detach, 3292baed30cSSudip Mukherjee .devmodel = true, 330ca632f55SGrant Likely }; 331ca632f55SGrant Likely 332ca632f55SGrant Likely static int __init init_spi_lm70llp(void) 333ca632f55SGrant Likely { 334ca632f55SGrant Likely return parport_register_driver(&spi_lm70llp_drv); 335ca632f55SGrant Likely } 336ca632f55SGrant Likely module_init(init_spi_lm70llp); 337ca632f55SGrant Likely 338ca632f55SGrant Likely static void __exit cleanup_spi_lm70llp(void) 339ca632f55SGrant Likely { 340ca632f55SGrant Likely parport_unregister_driver(&spi_lm70llp_drv); 341ca632f55SGrant Likely } 342ca632f55SGrant Likely module_exit(cleanup_spi_lm70llp); 343ca632f55SGrant Likely 344ca632f55SGrant Likely MODULE_AUTHOR("Kaiwan N Billimoria <kaiwan@designergraphix.com>"); 345ca632f55SGrant Likely MODULE_DESCRIPTION( 346ca632f55SGrant Likely "Parport adapter for the National Semiconductor LM70 LLP eval board"); 347ca632f55SGrant Likely MODULE_LICENSE("GPL"); 348