1/* ------------------------------------------------------------------------ * 2 * i2c-parport-light.c I2C bus over parallel port * 3 * ------------------------------------------------------------------------ * 4 Copyright (C) 2003-2007 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/platform_device.h> 31#include <linux/ioport.h> 32#include <linux/i2c.h> 33#include <linux/i2c-algo-bit.h> 34#include <asm/io.h> 35#include "i2c-parport.h" 36 37#define DEFAULT_BASE 0x378 38#define DRVNAME "i2c-parport-light" 39 40static struct platform_device *pdev; 41 42static u16 base; 43module_param(base, ushort, 0); 44MODULE_PARM_DESC(base, "Base I/O address"); 45 46/* ----- Low-level parallel port access ----------------------------------- */ 47 48static inline void port_write(unsigned char p, unsigned char d) 49{ 50 outb(d, base+p); 51} 52 53static inline unsigned char port_read(unsigned char p) 54{ 55 return inb(base+p); 56} 57 58/* ----- Unified line operation functions --------------------------------- */ 59 60static inline void line_set(int state, const struct lineop *op) 61{ 62 u8 oldval = port_read(op->port); 63 64 /* Touch only the bit(s) needed */ 65 if ((op->inverted && !state) || (!op->inverted && state)) 66 port_write(op->port, oldval | op->val); 67 else 68 port_write(op->port, oldval & ~op->val); 69} 70 71static inline int line_get(const struct lineop *op) 72{ 73 u8 oldval = port_read(op->port); 74 75 return ((op->inverted && (oldval & op->val) != op->val) 76 || (!op->inverted && (oldval & op->val) == op->val)); 77} 78 79/* ----- I2C algorithm call-back functions and structures ----------------- */ 80 81static void parport_setscl(void *data, int state) 82{ 83 line_set(state, &adapter_parm[type].setscl); 84} 85 86static void parport_setsda(void *data, int state) 87{ 88 line_set(state, &adapter_parm[type].setsda); 89} 90 91static int parport_getscl(void *data) 92{ 93 return line_get(&adapter_parm[type].getscl); 94} 95 96static int parport_getsda(void *data) 97{ 98 return line_get(&adapter_parm[type].getsda); 99} 100 101/* Encapsulate the functions above in the correct structure 102 Note that getscl will be set to NULL by the attaching code for adapters 103 that cannot read SCL back */ 104static struct i2c_algo_bit_data parport_algo_data = { 105 .setsda = parport_setsda, 106 .setscl = parport_setscl, 107 .getsda = parport_getsda, 108 .getscl = parport_getscl, 109 .udelay = 50, 110 .timeout = HZ, 111}; 112 113/* ----- Driver registration ---------------------------------------------- */ 114 115static struct i2c_adapter parport_adapter = { 116 .owner = THIS_MODULE, 117 .class = I2C_CLASS_HWMON, 118 .id = I2C_HW_B_LP, 119 .algo_data = &parport_algo_data, 120 .name = "Parallel port adapter (light)", 121}; 122 123static int __devinit i2c_parport_probe(struct platform_device *pdev) 124{ 125 int err; 126 struct resource *res; 127 128 res = platform_get_resource(pdev, IORESOURCE_IO, 0); 129 if (!request_region(res->start, res->end - res->start + 1, DRVNAME)) 130 return -EBUSY; 131 132 /* Reset hardware to a sane state (SCL and SDA high) */ 133 parport_setsda(NULL, 1); 134 parport_setscl(NULL, 1); 135 /* Other init if needed (power on...) */ 136 if (adapter_parm[type].init.val) 137 line_set(1, &adapter_parm[type].init); 138 139 parport_adapter.dev.parent = &pdev->dev; 140 err = i2c_bit_add_bus(&parport_adapter); 141 if (err) { 142 dev_err(&pdev->dev, "Unable to register with I2C\n"); 143 goto exit_region; 144 } 145 return 0; 146 147exit_region: 148 release_region(res->start, res->end - res->start + 1); 149 return err; 150} 151 152static int __devexit i2c_parport_remove(struct platform_device *pdev) 153{ 154 struct resource *res; 155 156 i2c_del_adapter(&parport_adapter); 157 158 /* Un-init if needed (power off...) */ 159 if (adapter_parm[type].init.val) 160 line_set(0, &adapter_parm[type].init); 161 162 res = platform_get_resource(pdev, IORESOURCE_IO, 0); 163 release_region(res->start, res->end - res->start + 1); 164 return 0; 165} 166 167static struct platform_driver i2c_parport_driver = { 168 .driver = { 169 .owner = THIS_MODULE, 170 .name = DRVNAME, 171 }, 172 .probe = i2c_parport_probe, 173 .remove = __devexit_p(i2c_parport_remove), 174}; 175 176static int __init i2c_parport_device_add(u16 address) 177{ 178 struct resource res = { 179 .start = address, 180 .end = address + 2, 181 .name = DRVNAME, 182 .flags = IORESOURCE_IO, 183 }; 184 int err; 185 186 pdev = platform_device_alloc(DRVNAME, -1); 187 if (!pdev) { 188 err = -ENOMEM; 189 printk(KERN_ERR DRVNAME ": Device allocation failed\n"); 190 goto exit; 191 } 192 193 err = platform_device_add_resources(pdev, &res, 1); 194 if (err) { 195 printk(KERN_ERR DRVNAME ": Device resource addition failed " 196 "(%d)\n", err); 197 goto exit_device_put; 198 } 199 200 err = platform_device_add(pdev); 201 if (err) { 202 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", 203 err); 204 goto exit_device_put; 205 } 206 207 return 0; 208 209exit_device_put: 210 platform_device_put(pdev); 211exit: 212 return err; 213} 214 215static int __init i2c_parport_init(void) 216{ 217 int err; 218 219 if (type < 0) { 220 printk(KERN_ERR DRVNAME ": adapter type unspecified\n"); 221 return -ENODEV; 222 } 223 224 if (type >= ARRAY_SIZE(adapter_parm)) { 225 printk(KERN_ERR DRVNAME ": invalid type (%d)\n", type); 226 return -ENODEV; 227 } 228 229 if (base == 0) { 230 pr_info(DRVNAME ": using default base 0x%x\n", DEFAULT_BASE); 231 base = DEFAULT_BASE; 232 } 233 234 if (!adapter_parm[type].getscl.val) 235 parport_algo_data.getscl = NULL; 236 237 /* Sets global pdev as a side effect */ 238 err = i2c_parport_device_add(base); 239 if (err) 240 goto exit; 241 242 err = platform_driver_register(&i2c_parport_driver); 243 if (err) 244 goto exit_device; 245 246 return 0; 247 248exit_device: 249 platform_device_unregister(pdev); 250exit: 251 return err; 252} 253 254static void __exit i2c_parport_exit(void) 255{ 256 platform_driver_unregister(&i2c_parport_driver); 257 platform_device_unregister(pdev); 258} 259 260MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); 261MODULE_DESCRIPTION("I2C bus over parallel port (light)"); 262MODULE_LICENSE("GPL"); 263 264module_init(i2c_parport_init); 265module_exit(i2c_parport_exit); 266