1/* ------------------------------------------------------------------------ * 2 * i2c-parport-light.c I2C bus over parallel port * 3 * ------------------------------------------------------------------------ * 4 Copyright (C) 2003-2010 Jean Delvare <khali@linux-fr.org> 5 6 Based on older i2c-velleman.c driver 7 Copyright (C) 1995-2000 Simon G. Vogl 8 With some changes from: 9 Frodo Looijaard <frodol@dds.nl> 10 Kyösti Mälkki <kmalkki@cc.hut.fi> 11 12 This program is free software; you can redistribute it and/or modify 13 it under the terms of the GNU General Public License as published by 14 the Free Software Foundation; either version 2 of the License, or 15 (at your option) any later version. 16 17 This program is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License for more details. 21 22 You should have received a copy of the GNU General Public License 23 along with this program; if not, write to the Free Software 24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 25 * ------------------------------------------------------------------------ */ 26 27#include <linux/kernel.h> 28#include <linux/module.h> 29#include <linux/init.h> 30#include <linux/delay.h> 31#include <linux/platform_device.h> 32#include <linux/ioport.h> 33#include <linux/i2c.h> 34#include <linux/i2c-algo-bit.h> 35#include <linux/i2c-smbus.h> 36#include <linux/io.h> 37#include "i2c-parport.h" 38 39#define DEFAULT_BASE 0x378 40#define DRVNAME "i2c-parport-light" 41 42static struct platform_device *pdev; 43 44static u16 base; 45module_param(base, ushort, 0); 46MODULE_PARM_DESC(base, "Base I/O address"); 47 48static int irq; 49module_param(irq, int, 0); 50MODULE_PARM_DESC(irq, "IRQ (optional)"); 51 52/* ----- Low-level parallel port access ----------------------------------- */ 53 54static inline void port_write(unsigned char p, unsigned char d) 55{ 56 outb(d, base+p); 57} 58 59static inline unsigned char port_read(unsigned char p) 60{ 61 return inb(base+p); 62} 63 64/* ----- Unified line operation functions --------------------------------- */ 65 66static inline void line_set(int state, const struct lineop *op) 67{ 68 u8 oldval = port_read(op->port); 69 70 /* Touch only the bit(s) needed */ 71 if ((op->inverted && !state) || (!op->inverted && state)) 72 port_write(op->port, oldval | op->val); 73 else 74 port_write(op->port, oldval & ~op->val); 75} 76 77static inline int line_get(const struct lineop *op) 78{ 79 u8 oldval = port_read(op->port); 80 81 return ((op->inverted && (oldval & op->val) != op->val) 82 || (!op->inverted && (oldval & op->val) == op->val)); 83} 84 85/* ----- I2C algorithm call-back functions and structures ----------------- */ 86 87static void parport_setscl(void *data, int state) 88{ 89 line_set(state, &adapter_parm[type].setscl); 90} 91 92static void parport_setsda(void *data, int state) 93{ 94 line_set(state, &adapter_parm[type].setsda); 95} 96 97static int parport_getscl(void *data) 98{ 99 return line_get(&adapter_parm[type].getscl); 100} 101 102static int parport_getsda(void *data) 103{ 104 return line_get(&adapter_parm[type].getsda); 105} 106 107/* Encapsulate the functions above in the correct structure 108 Note that getscl will be set to NULL by the attaching code for adapters 109 that cannot read SCL back */ 110static struct i2c_algo_bit_data parport_algo_data = { 111 .setsda = parport_setsda, 112 .setscl = parport_setscl, 113 .getsda = parport_getsda, 114 .getscl = parport_getscl, 115 .udelay = 50, 116 .timeout = HZ, 117}; 118 119/* ----- Driver registration ---------------------------------------------- */ 120 121static struct i2c_adapter parport_adapter = { 122 .owner = THIS_MODULE, 123 .class = I2C_CLASS_HWMON, 124 .algo_data = &parport_algo_data, 125 .name = "Parallel port adapter (light)", 126}; 127 128/* SMBus alert support */ 129static struct i2c_smbus_alert_setup alert_data = { 130 .alert_edge_triggered = 1, 131}; 132static struct i2c_client *ara; 133static struct lineop parport_ctrl_irq = { 134 .val = (1 << 4), 135 .port = PORT_CTRL, 136}; 137 138static int __devinit i2c_parport_probe(struct platform_device *pdev) 139{ 140 int err; 141 142 /* Reset hardware to a sane state (SCL and SDA high) */ 143 parport_setsda(NULL, 1); 144 parport_setscl(NULL, 1); 145 /* Other init if needed (power on...) */ 146 if (adapter_parm[type].init.val) { 147 line_set(1, &adapter_parm[type].init); 148 /* Give powered devices some time to settle */ 149 msleep(100); 150 } 151 152 parport_adapter.dev.parent = &pdev->dev; 153 err = i2c_bit_add_bus(&parport_adapter); 154 if (err) { 155 dev_err(&pdev->dev, "Unable to register with I2C\n"); 156 return err; 157 } 158 159 /* Setup SMBus alert if supported */ 160 if (adapter_parm[type].smbus_alert && irq) { 161 alert_data.irq = irq; 162 ara = i2c_setup_smbus_alert(&parport_adapter, &alert_data); 163 if (ara) 164 line_set(1, &parport_ctrl_irq); 165 else 166 dev_warn(&pdev->dev, "Failed to register ARA client\n"); 167 } 168 169 return 0; 170} 171 172static int __devexit i2c_parport_remove(struct platform_device *pdev) 173{ 174 if (ara) { 175 line_set(0, &parport_ctrl_irq); 176 i2c_unregister_device(ara); 177 ara = NULL; 178 } 179 i2c_del_adapter(&parport_adapter); 180 181 /* Un-init if needed (power off...) */ 182 if (adapter_parm[type].init.val) 183 line_set(0, &adapter_parm[type].init); 184 185 return 0; 186} 187 188static struct platform_driver i2c_parport_driver = { 189 .driver = { 190 .owner = THIS_MODULE, 191 .name = DRVNAME, 192 }, 193 .probe = i2c_parport_probe, 194 .remove = __devexit_p(i2c_parport_remove), 195}; 196 197static int __init i2c_parport_device_add(u16 address) 198{ 199 int err; 200 201 pdev = platform_device_alloc(DRVNAME, -1); 202 if (!pdev) { 203 err = -ENOMEM; 204 printk(KERN_ERR DRVNAME ": Device allocation failed\n"); 205 goto exit; 206 } 207 208 err = platform_device_add(pdev); 209 if (err) { 210 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", 211 err); 212 goto exit_device_put; 213 } 214 215 return 0; 216 217exit_device_put: 218 platform_device_put(pdev); 219exit: 220 return err; 221} 222 223static int __init i2c_parport_init(void) 224{ 225 int err; 226 227 if (type < 0) { 228 printk(KERN_ERR DRVNAME ": adapter type unspecified\n"); 229 return -ENODEV; 230 } 231 232 if (type >= ARRAY_SIZE(adapter_parm)) { 233 printk(KERN_ERR DRVNAME ": invalid type (%d)\n", type); 234 return -ENODEV; 235 } 236 237 if (base == 0) { 238 pr_info(DRVNAME ": using default base 0x%x\n", DEFAULT_BASE); 239 base = DEFAULT_BASE; 240 } 241 242 if (!request_region(base, 3, DRVNAME)) 243 return -EBUSY; 244 245 if (irq != 0) 246 pr_info(DRVNAME ": using irq %d\n", irq); 247 248 if (!adapter_parm[type].getscl.val) 249 parport_algo_data.getscl = NULL; 250 251 /* Sets global pdev as a side effect */ 252 err = i2c_parport_device_add(base); 253 if (err) 254 goto exit_release; 255 256 err = platform_driver_register(&i2c_parport_driver); 257 if (err) 258 goto exit_device; 259 260 return 0; 261 262exit_device: 263 platform_device_unregister(pdev); 264exit_release: 265 release_region(base, 3); 266 return err; 267} 268 269static void __exit i2c_parport_exit(void) 270{ 271 platform_driver_unregister(&i2c_parport_driver); 272 platform_device_unregister(pdev); 273 release_region(base, 3); 274} 275 276MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); 277MODULE_DESCRIPTION("I2C bus over parallel port (light)"); 278MODULE_LICENSE("GPL"); 279 280module_init(i2c_parport_init); 281module_exit(i2c_parport_exit); 282