root/drivers/i2c/busses/i2c-parport-light.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. port_write
  2. port_read
  3. line_set
  4. line_get
  5. parport_setscl
  6. parport_setsda
  7. parport_getscl
  8. parport_getsda
  9. i2c_parport_probe
  10. i2c_parport_remove
  11. i2c_parport_device_add
  12. i2c_parport_init
  13. i2c_parport_exit

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

/* [<][>][^][v][top][bottom][index][help] */