162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* ------------------------------------------------------------------------ * 362306a36Sopenharmony_ci * i2c-parport.c I2C bus over parallel port * 462306a36Sopenharmony_ci * ------------------------------------------------------------------------ * 562306a36Sopenharmony_ci Copyright (C) 2003-2011 Jean Delvare <jdelvare@suse.de> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Based on older i2c-philips-par.c driver 862306a36Sopenharmony_ci Copyright (C) 1995-2000 Simon G. Vogl 962306a36Sopenharmony_ci With some changes from: 1062306a36Sopenharmony_ci Frodo Looijaard <frodol@dds.nl> 1162306a36Sopenharmony_ci Kyösti Mälkki <kmalkki@cc.hut.fi> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci * ------------------------------------------------------------------------ */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define pr_fmt(fmt) "i2c-parport: " fmt 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/init.h> 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci#include <linux/parport.h> 2262306a36Sopenharmony_ci#include <linux/i2c.h> 2362306a36Sopenharmony_ci#include <linux/i2c-algo-bit.h> 2462306a36Sopenharmony_ci#include <linux/i2c-smbus.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci#include <linux/list.h> 2762306a36Sopenharmony_ci#include <linux/mutex.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define PORT_DATA 0 3062306a36Sopenharmony_ci#define PORT_STAT 1 3162306a36Sopenharmony_ci#define PORT_CTRL 2 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct lineop { 3462306a36Sopenharmony_ci u8 val; 3562306a36Sopenharmony_ci u8 port; 3662306a36Sopenharmony_ci u8 inverted; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct adapter_parm { 4062306a36Sopenharmony_ci struct lineop setsda; 4162306a36Sopenharmony_ci struct lineop setscl; 4262306a36Sopenharmony_ci struct lineop getsda; 4362306a36Sopenharmony_ci struct lineop getscl; 4462306a36Sopenharmony_ci struct lineop init; 4562306a36Sopenharmony_ci unsigned int smbus_alert:1; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic const struct adapter_parm adapter_parm[] = { 4962306a36Sopenharmony_ci /* type 0: Philips adapter */ 5062306a36Sopenharmony_ci { 5162306a36Sopenharmony_ci .setsda = { 0x80, PORT_DATA, 1 }, 5262306a36Sopenharmony_ci .setscl = { 0x08, PORT_CTRL, 0 }, 5362306a36Sopenharmony_ci .getsda = { 0x80, PORT_STAT, 0 }, 5462306a36Sopenharmony_ci .getscl = { 0x08, PORT_STAT, 0 }, 5562306a36Sopenharmony_ci }, 5662306a36Sopenharmony_ci /* type 1: home brew teletext adapter */ 5762306a36Sopenharmony_ci { 5862306a36Sopenharmony_ci .setsda = { 0x02, PORT_DATA, 0 }, 5962306a36Sopenharmony_ci .setscl = { 0x01, PORT_DATA, 0 }, 6062306a36Sopenharmony_ci .getsda = { 0x80, PORT_STAT, 1 }, 6162306a36Sopenharmony_ci }, 6262306a36Sopenharmony_ci /* type 2: Velleman K8000 adapter */ 6362306a36Sopenharmony_ci { 6462306a36Sopenharmony_ci .setsda = { 0x02, PORT_CTRL, 1 }, 6562306a36Sopenharmony_ci .setscl = { 0x08, PORT_CTRL, 1 }, 6662306a36Sopenharmony_ci .getsda = { 0x10, PORT_STAT, 0 }, 6762306a36Sopenharmony_ci }, 6862306a36Sopenharmony_ci /* type 3: ELV adapter */ 6962306a36Sopenharmony_ci { 7062306a36Sopenharmony_ci .setsda = { 0x02, PORT_DATA, 1 }, 7162306a36Sopenharmony_ci .setscl = { 0x01, PORT_DATA, 1 }, 7262306a36Sopenharmony_ci .getsda = { 0x40, PORT_STAT, 1 }, 7362306a36Sopenharmony_ci .getscl = { 0x08, PORT_STAT, 1 }, 7462306a36Sopenharmony_ci }, 7562306a36Sopenharmony_ci /* type 4: ADM1032 evaluation board */ 7662306a36Sopenharmony_ci { 7762306a36Sopenharmony_ci .setsda = { 0x02, PORT_DATA, 1 }, 7862306a36Sopenharmony_ci .setscl = { 0x01, PORT_DATA, 1 }, 7962306a36Sopenharmony_ci .getsda = { 0x10, PORT_STAT, 1 }, 8062306a36Sopenharmony_ci .init = { 0xf0, PORT_DATA, 0 }, 8162306a36Sopenharmony_ci .smbus_alert = 1, 8262306a36Sopenharmony_ci }, 8362306a36Sopenharmony_ci /* type 5: ADM1025, ADM1030 and ADM1031 evaluation boards */ 8462306a36Sopenharmony_ci { 8562306a36Sopenharmony_ci .setsda = { 0x02, PORT_DATA, 1 }, 8662306a36Sopenharmony_ci .setscl = { 0x01, PORT_DATA, 1 }, 8762306a36Sopenharmony_ci .getsda = { 0x10, PORT_STAT, 1 }, 8862306a36Sopenharmony_ci }, 8962306a36Sopenharmony_ci /* type 6: Barco LPT->DVI (K5800236) adapter */ 9062306a36Sopenharmony_ci { 9162306a36Sopenharmony_ci .setsda = { 0x02, PORT_DATA, 1 }, 9262306a36Sopenharmony_ci .setscl = { 0x01, PORT_DATA, 1 }, 9362306a36Sopenharmony_ci .getsda = { 0x20, PORT_STAT, 0 }, 9462306a36Sopenharmony_ci .getscl = { 0x40, PORT_STAT, 0 }, 9562306a36Sopenharmony_ci .init = { 0xfc, PORT_DATA, 0 }, 9662306a36Sopenharmony_ci }, 9762306a36Sopenharmony_ci /* type 7: One For All JP1 parallel port adapter */ 9862306a36Sopenharmony_ci { 9962306a36Sopenharmony_ci .setsda = { 0x01, PORT_DATA, 0 }, 10062306a36Sopenharmony_ci .setscl = { 0x02, PORT_DATA, 0 }, 10162306a36Sopenharmony_ci .getsda = { 0x80, PORT_STAT, 1 }, 10262306a36Sopenharmony_ci .init = { 0x04, PORT_DATA, 1 }, 10362306a36Sopenharmony_ci }, 10462306a36Sopenharmony_ci /* type 8: VCT-jig */ 10562306a36Sopenharmony_ci { 10662306a36Sopenharmony_ci .setsda = { 0x04, PORT_DATA, 1 }, 10762306a36Sopenharmony_ci .setscl = { 0x01, PORT_DATA, 1 }, 10862306a36Sopenharmony_ci .getsda = { 0x40, PORT_STAT, 0 }, 10962306a36Sopenharmony_ci .getscl = { 0x80, PORT_STAT, 1 }, 11062306a36Sopenharmony_ci }, 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* ----- Device list ------------------------------------------------------ */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistruct i2c_par { 11662306a36Sopenharmony_ci struct pardevice *pdev; 11762306a36Sopenharmony_ci struct i2c_adapter adapter; 11862306a36Sopenharmony_ci struct i2c_algo_bit_data algo_data; 11962306a36Sopenharmony_ci struct i2c_smbus_alert_setup alert_data; 12062306a36Sopenharmony_ci struct i2c_client *ara; 12162306a36Sopenharmony_ci struct list_head node; 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic LIST_HEAD(adapter_list); 12562306a36Sopenharmony_cistatic DEFINE_MUTEX(adapter_list_lock); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define MAX_DEVICE 4 12862306a36Sopenharmony_cistatic int parport[MAX_DEVICE] = {0, -1, -1, -1}; 12962306a36Sopenharmony_cimodule_param_array(parport, int, NULL, 0); 13062306a36Sopenharmony_ciMODULE_PARM_DESC(parport, 13162306a36Sopenharmony_ci "List of parallel ports to bind to, by index.\n" 13262306a36Sopenharmony_ci " At most " __stringify(MAX_DEVICE) " devices are supported.\n" 13362306a36Sopenharmony_ci " Default is one device connected to parport0.\n" 13462306a36Sopenharmony_ci); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int type = -1; 13762306a36Sopenharmony_cimodule_param(type, int, 0); 13862306a36Sopenharmony_ciMODULE_PARM_DESC(type, 13962306a36Sopenharmony_ci "Type of adapter:\n" 14062306a36Sopenharmony_ci " 0 = Philips adapter\n" 14162306a36Sopenharmony_ci " 1 = home brew teletext adapter\n" 14262306a36Sopenharmony_ci " 2 = Velleman K8000 adapter\n" 14362306a36Sopenharmony_ci " 3 = ELV adapter\n" 14462306a36Sopenharmony_ci " 4 = ADM1032 evaluation board\n" 14562306a36Sopenharmony_ci " 5 = ADM1025, ADM1030 and ADM1031 evaluation boards\n" 14662306a36Sopenharmony_ci " 6 = Barco LPT->DVI (K5800236) adapter\n" 14762306a36Sopenharmony_ci " 7 = One For All JP1 parallel port adapter\n" 14862306a36Sopenharmony_ci " 8 = VCT-jig\n" 14962306a36Sopenharmony_ci); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* ----- Low-level parallel port access ----------------------------------- */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void port_write_data(struct parport *p, unsigned char d) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci parport_write_data(p, d); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void port_write_control(struct parport *p, unsigned char d) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci parport_write_control(p, d); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic unsigned char port_read_data(struct parport *p) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci return parport_read_data(p); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic unsigned char port_read_status(struct parport *p) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci return parport_read_status(p); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic unsigned char port_read_control(struct parport *p) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci return parport_read_control(p); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void (* const port_write[])(struct parport *, unsigned char) = { 17962306a36Sopenharmony_ci port_write_data, 18062306a36Sopenharmony_ci NULL, 18162306a36Sopenharmony_ci port_write_control, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic unsigned char (* const port_read[])(struct parport *) = { 18562306a36Sopenharmony_ci port_read_data, 18662306a36Sopenharmony_ci port_read_status, 18762306a36Sopenharmony_ci port_read_control, 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* ----- Unified line operation functions --------------------------------- */ 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic inline void line_set(struct parport *data, int state, 19362306a36Sopenharmony_ci const struct lineop *op) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci u8 oldval = port_read[op->port](data); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Touch only the bit(s) needed */ 19862306a36Sopenharmony_ci if ((op->inverted && !state) || (!op->inverted && state)) 19962306a36Sopenharmony_ci port_write[op->port](data, oldval | op->val); 20062306a36Sopenharmony_ci else 20162306a36Sopenharmony_ci port_write[op->port](data, oldval & ~op->val); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic inline int line_get(struct parport *data, 20562306a36Sopenharmony_ci const struct lineop *op) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci u8 oldval = port_read[op->port](data); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return ((op->inverted && (oldval & op->val) != op->val) 21062306a36Sopenharmony_ci || (!op->inverted && (oldval & op->val) == op->val)); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* ----- I2C algorithm call-back functions and structures ----------------- */ 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic void parport_setscl(void *data, int state) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci line_set((struct parport *) data, state, &adapter_parm[type].setscl); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void parport_setsda(void *data, int state) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci line_set((struct parport *) data, state, &adapter_parm[type].setsda); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int parport_getscl(void *data) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci return line_get((struct parport *) data, &adapter_parm[type].getscl); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int parport_getsda(void *data) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci return line_get((struct parport *) data, &adapter_parm[type].getsda); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* Encapsulate the functions above in the correct structure. 23662306a36Sopenharmony_ci Note that this is only a template, from which the real structures are 23762306a36Sopenharmony_ci copied. The attaching code will set getscl to NULL for adapters that 23862306a36Sopenharmony_ci cannot read SCL back, and will also make the data field point to 23962306a36Sopenharmony_ci the parallel port structure. */ 24062306a36Sopenharmony_cistatic const struct i2c_algo_bit_data parport_algo_data = { 24162306a36Sopenharmony_ci .setsda = parport_setsda, 24262306a36Sopenharmony_ci .setscl = parport_setscl, 24362306a36Sopenharmony_ci .getsda = parport_getsda, 24462306a36Sopenharmony_ci .getscl = parport_getscl, 24562306a36Sopenharmony_ci .udelay = 10, /* ~50 kbps */ 24662306a36Sopenharmony_ci .timeout = HZ, 24762306a36Sopenharmony_ci}; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/* ----- I2c and parallel port call-back functions and structures --------- */ 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void i2c_parport_irq(void *data) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct i2c_par *adapter = data; 25462306a36Sopenharmony_ci struct i2c_client *ara = adapter->ara; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (ara) { 25762306a36Sopenharmony_ci dev_dbg(&ara->dev, "SMBus alert received\n"); 25862306a36Sopenharmony_ci i2c_handle_smbus_alert(ara); 25962306a36Sopenharmony_ci } else 26062306a36Sopenharmony_ci dev_dbg(&adapter->adapter.dev, 26162306a36Sopenharmony_ci "SMBus alert received but no ARA client!\n"); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void i2c_parport_attach(struct parport *port) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct i2c_par *adapter; 26762306a36Sopenharmony_ci int i; 26862306a36Sopenharmony_ci struct pardev_cb i2c_parport_cb; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (type < 0) { 27162306a36Sopenharmony_ci pr_warn("adapter type unspecified\n"); 27262306a36Sopenharmony_ci return; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (type >= ARRAY_SIZE(adapter_parm)) { 27662306a36Sopenharmony_ci pr_warn("invalid type (%d)\n", type); 27762306a36Sopenharmony_ci return; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci for (i = 0; i < MAX_DEVICE; i++) { 28162306a36Sopenharmony_ci if (parport[i] == -1) 28262306a36Sopenharmony_ci continue; 28362306a36Sopenharmony_ci if (port->number == parport[i]) 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci if (i == MAX_DEVICE) { 28762306a36Sopenharmony_ci pr_debug("Not using parport%d.\n", port->number); 28862306a36Sopenharmony_ci return; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci adapter = kzalloc(sizeof(struct i2c_par), GFP_KERNEL); 29262306a36Sopenharmony_ci if (!adapter) 29362306a36Sopenharmony_ci return; 29462306a36Sopenharmony_ci memset(&i2c_parport_cb, 0, sizeof(i2c_parport_cb)); 29562306a36Sopenharmony_ci i2c_parport_cb.flags = PARPORT_FLAG_EXCL; 29662306a36Sopenharmony_ci i2c_parport_cb.irq_func = i2c_parport_irq; 29762306a36Sopenharmony_ci i2c_parport_cb.private = adapter; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci pr_debug("attaching to %s\n", port->name); 30062306a36Sopenharmony_ci parport_disable_irq(port); 30162306a36Sopenharmony_ci adapter->pdev = parport_register_dev_model(port, "i2c-parport", 30262306a36Sopenharmony_ci &i2c_parport_cb, i); 30362306a36Sopenharmony_ci if (!adapter->pdev) { 30462306a36Sopenharmony_ci pr_err("Unable to register with parport\n"); 30562306a36Sopenharmony_ci goto err_free; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Fill the rest of the structure */ 30962306a36Sopenharmony_ci adapter->adapter.owner = THIS_MODULE; 31062306a36Sopenharmony_ci adapter->adapter.class = I2C_CLASS_HWMON; 31162306a36Sopenharmony_ci strscpy(adapter->adapter.name, "Parallel port adapter", 31262306a36Sopenharmony_ci sizeof(adapter->adapter.name)); 31362306a36Sopenharmony_ci adapter->algo_data = parport_algo_data; 31462306a36Sopenharmony_ci /* Slow down if we can't sense SCL */ 31562306a36Sopenharmony_ci if (!adapter_parm[type].getscl.val) { 31662306a36Sopenharmony_ci adapter->algo_data.getscl = NULL; 31762306a36Sopenharmony_ci adapter->algo_data.udelay = 50; /* ~10 kbps */ 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci adapter->algo_data.data = port; 32062306a36Sopenharmony_ci adapter->adapter.algo_data = &adapter->algo_data; 32162306a36Sopenharmony_ci adapter->adapter.dev.parent = port->physport->dev; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (parport_claim_or_block(adapter->pdev) < 0) { 32462306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 32562306a36Sopenharmony_ci "Could not claim parallel port\n"); 32662306a36Sopenharmony_ci goto err_unregister; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* Reset hardware to a sane state (SCL and SDA high) */ 33062306a36Sopenharmony_ci parport_setsda(port, 1); 33162306a36Sopenharmony_ci parport_setscl(port, 1); 33262306a36Sopenharmony_ci /* Other init if needed (power on...) */ 33362306a36Sopenharmony_ci if (adapter_parm[type].init.val) { 33462306a36Sopenharmony_ci line_set(port, 1, &adapter_parm[type].init); 33562306a36Sopenharmony_ci /* Give powered devices some time to settle */ 33662306a36Sopenharmony_ci msleep(100); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (i2c_bit_add_bus(&adapter->adapter) < 0) { 34062306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, "Unable to register with I2C\n"); 34162306a36Sopenharmony_ci goto err_unregister; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Setup SMBus alert if supported */ 34562306a36Sopenharmony_ci if (adapter_parm[type].smbus_alert) { 34662306a36Sopenharmony_ci struct i2c_client *ara; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci ara = i2c_new_smbus_alert_device(&adapter->adapter, 34962306a36Sopenharmony_ci &adapter->alert_data); 35062306a36Sopenharmony_ci if (!IS_ERR(ara)) { 35162306a36Sopenharmony_ci adapter->ara = ara; 35262306a36Sopenharmony_ci parport_enable_irq(port); 35362306a36Sopenharmony_ci } else { 35462306a36Sopenharmony_ci dev_warn(&adapter->pdev->dev, 35562306a36Sopenharmony_ci "Failed to register ARA client\n"); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* Add the new adapter to the list */ 36062306a36Sopenharmony_ci mutex_lock(&adapter_list_lock); 36162306a36Sopenharmony_ci list_add_tail(&adapter->node, &adapter_list); 36262306a36Sopenharmony_ci mutex_unlock(&adapter_list_lock); 36362306a36Sopenharmony_ci return; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci err_unregister: 36662306a36Sopenharmony_ci parport_release(adapter->pdev); 36762306a36Sopenharmony_ci parport_unregister_device(adapter->pdev); 36862306a36Sopenharmony_ci err_free: 36962306a36Sopenharmony_ci kfree(adapter); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void i2c_parport_detach(struct parport *port) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct i2c_par *adapter, *_n; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* Walk the list */ 37762306a36Sopenharmony_ci mutex_lock(&adapter_list_lock); 37862306a36Sopenharmony_ci list_for_each_entry_safe(adapter, _n, &adapter_list, node) { 37962306a36Sopenharmony_ci if (adapter->pdev->port == port) { 38062306a36Sopenharmony_ci if (adapter->ara) { 38162306a36Sopenharmony_ci parport_disable_irq(port); 38262306a36Sopenharmony_ci i2c_unregister_device(adapter->ara); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci i2c_del_adapter(&adapter->adapter); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Un-init if needed (power off...) */ 38762306a36Sopenharmony_ci if (adapter_parm[type].init.val) 38862306a36Sopenharmony_ci line_set(port, 0, &adapter_parm[type].init); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci parport_release(adapter->pdev); 39162306a36Sopenharmony_ci parport_unregister_device(adapter->pdev); 39262306a36Sopenharmony_ci list_del(&adapter->node); 39362306a36Sopenharmony_ci kfree(adapter); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci mutex_unlock(&adapter_list_lock); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic struct parport_driver i2c_parport_driver = { 40062306a36Sopenharmony_ci .name = "i2c-parport", 40162306a36Sopenharmony_ci .match_port = i2c_parport_attach, 40262306a36Sopenharmony_ci .detach = i2c_parport_detach, 40362306a36Sopenharmony_ci .devmodel = true, 40462306a36Sopenharmony_ci}; 40562306a36Sopenharmony_cimodule_parport_driver(i2c_parport_driver); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ciMODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); 40862306a36Sopenharmony_ciMODULE_DESCRIPTION("I2C bus over parallel port"); 40962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 410