2005-04-16 15:20:36 -07:00
/* ------------------------------------------------------------------------ *
2007-05-01 23:26:30 +02:00
* i2c - parport - light . c I2C bus over parallel port *
2005-04-16 15:20:36 -07:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
2014-01-29 20:40:08 +01:00
Copyright ( C ) 2003 - 2010 Jean Delvare < jdelvare @ suse . de >
2011-05-24 20:58:49 +02:00
2005-04-16 15:20:36 -07:00
Based on older i2c - velleman . c driver
Copyright ( C ) 1995 - 2000 Simon G . Vogl
With some changes from :
Frodo Looijaard < frodol @ dds . nl >
2007-10-19 23:21:04 +02:00
Kyösti Mälkki < kmalkki @ cc . hut . fi >
2011-05-24 20:58:49 +02:00
2005-04-16 15:20:36 -07:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
2010-03-02 12:23:41 +01:00
# include <linux/delay.h>
2007-05-01 23:26:30 +02:00
# include <linux/platform_device.h>
2005-04-16 15:20:36 -07:00
# include <linux/ioport.h>
# include <linux/i2c.h>
# include <linux/i2c-algo-bit.h>
2010-03-02 12:23:45 +01:00
# include <linux/i2c-smbus.h>
2010-05-21 18:41:01 +02:00
# include <linux/io.h>
2005-04-16 15:20:36 -07:00
# include "i2c-parport.h"
# define DEFAULT_BASE 0x378
2007-05-01 23:26:30 +02:00
# define DRVNAME "i2c-parport-light"
static struct platform_device * pdev ;
2005-04-16 15:20:36 -07:00
static u16 base ;
module_param ( base , ushort , 0 ) ;
MODULE_PARM_DESC ( base , " Base I/O address " ) ;
2010-03-02 12:23:45 +01:00
static int irq ;
module_param ( irq , int , 0 ) ;
MODULE_PARM_DESC ( irq , " IRQ (optional) " ) ;
2005-04-16 15:20:36 -07:00
/* ----- Low-level parallel port access ----------------------------------- */
static inline void port_write ( unsigned char p , unsigned char d )
{
outb ( d , base + p ) ;
}
static inline unsigned char port_read ( unsigned char p )
{
return inb ( base + p ) ;
}
/* ----- Unified line operation functions --------------------------------- */
static inline void line_set ( int state , const struct lineop * op )
{
u8 oldval = port_read ( op - > port ) ;
/* Touch only the bit(s) needed */
if ( ( op - > inverted & & ! state ) | | ( ! op - > inverted & & state ) )
port_write ( op - > port , oldval | op - > val ) ;
else
port_write ( op - > port , oldval & ~ op - > val ) ;
}
static inline int line_get ( const struct lineop * op )
{
u8 oldval = port_read ( op - > port ) ;
return ( ( op - > inverted & & ( oldval & op - > val ) ! = op - > val )
| | ( ! op - > inverted & & ( oldval & op - > val ) = = op - > val ) ) ;
}
/* ----- I2C algorithm call-back functions and structures ----------------- */
static void parport_setscl ( void * data , int state )
{
line_set ( state , & adapter_parm [ type ] . setscl ) ;
}
static void parport_setsda ( void * data , int state )
{
line_set ( state , & adapter_parm [ type ] . setsda ) ;
}
static int parport_getscl ( void * data )
{
return line_get ( & adapter_parm [ type ] . getscl ) ;
}
static int parport_getsda ( void * data )
{
return line_get ( & adapter_parm [ type ] . getsda ) ;
}
/* Encapsulate the functions above in the correct structure
Note that getscl will be set to NULL by the attaching code for adapters
that cannot read SCL back */
static struct i2c_algo_bit_data parport_algo_data = {
. setsda = parport_setsda ,
. setscl = parport_setscl ,
. getsda = parport_getsda ,
. getscl = parport_getscl ,
. udelay = 50 ,
. timeout = HZ ,
2011-05-24 20:58:49 +02:00
} ;
2005-04-16 15:20:36 -07:00
2007-05-01 23:26:30 +02:00
/* ----- Driver registration ---------------------------------------------- */
2005-04-16 15:20:36 -07:00
static struct i2c_adapter parport_adapter = {
. owner = THIS_MODULE ,
. class = I2C_CLASS_HWMON ,
. algo_data = & parport_algo_data ,
. name = " Parallel port adapter (light) " ,
} ;
2010-03-02 12:23:45 +01:00
/* SMBus alert support */
static struct i2c_smbus_alert_setup alert_data = {
. alert_edge_triggered = 1 ,
} ;
static struct i2c_client * ara ;
static struct lineop parport_ctrl_irq = {
. val = ( 1 < < 4 ) ,
2011-05-24 20:58:49 +02:00
. port = PORT_CTRL ,
2010-03-02 12:23:45 +01:00
} ;
2012-11-27 15:59:38 -05:00
static int i2c_parport_probe ( struct platform_device * pdev )
2007-05-01 23:26:30 +02:00
{
int err ;
/* Reset hardware to a sane state (SCL and SDA high) */
parport_setsda ( NULL , 1 ) ;
parport_setscl ( NULL , 1 ) ;
/* Other init if needed (power on...) */
2010-03-02 12:23:41 +01:00
if ( adapter_parm [ type ] . init . val ) {
2007-05-01 23:26:30 +02:00
line_set ( 1 , & adapter_parm [ type ] . init ) ;
2010-03-02 12:23:41 +01:00
/* Give powered devices some time to settle */
msleep ( 100 ) ;
}
2007-05-01 23:26:30 +02:00
parport_adapter . dev . parent = & pdev - > dev ;
err = i2c_bit_add_bus ( & parport_adapter ) ;
2010-03-02 12:23:45 +01:00
if ( err ) {
2007-05-01 23:26:30 +02:00
dev_err ( & pdev - > dev , " Unable to register with I2C \n " ) ;
2010-03-02 12:23:45 +01:00
return err ;
}
/* Setup SMBus alert if supported */
if ( adapter_parm [ type ] . smbus_alert & & irq ) {
alert_data . irq = irq ;
ara = i2c_setup_smbus_alert ( & parport_adapter , & alert_data ) ;
if ( ara )
line_set ( 1 , & parport_ctrl_irq ) ;
else
dev_warn ( & pdev - > dev , " Failed to register ARA client \n " ) ;
}
return 0 ;
2007-05-01 23:26:30 +02:00
}
2012-11-27 15:59:38 -05:00
static int i2c_parport_remove ( struct platform_device * pdev )
2007-05-01 23:26:30 +02:00
{
2010-03-02 12:23:45 +01:00
if ( ara ) {
line_set ( 0 , & parport_ctrl_irq ) ;
i2c_unregister_device ( ara ) ;
ara = NULL ;
}
2007-05-01 23:26:30 +02:00
i2c_del_adapter ( & parport_adapter ) ;
/* Un-init if needed (power off...) */
if ( adapter_parm [ type ] . init . val )
line_set ( 0 , & adapter_parm [ type ] . init ) ;
return 0 ;
}
static struct platform_driver i2c_parport_driver = {
. driver = {
. name = DRVNAME ,
} ,
. probe = i2c_parport_probe ,
2012-11-27 15:59:38 -05:00
. remove = i2c_parport_remove ,
2007-05-01 23:26:30 +02:00
} ;
static int __init i2c_parport_device_add ( u16 address )
{
int err ;
pdev = platform_device_alloc ( DRVNAME , - 1 ) ;
if ( ! pdev ) {
err = - ENOMEM ;
printk ( KERN_ERR DRVNAME " : Device allocation failed \n " ) ;
goto exit ;
}
err = platform_device_add ( pdev ) ;
if ( err ) {
printk ( KERN_ERR DRVNAME " : Device addition failed (%d) \n " ,
err ) ;
goto exit_device_put ;
}
return 0 ;
exit_device_put :
platform_device_put ( pdev ) ;
exit :
return err ;
}
2005-04-16 15:20:36 -07:00
static int __init i2c_parport_init ( void )
{
2007-05-01 23:26:30 +02:00
int err ;
2006-03-23 16:50:25 +01:00
if ( type < 0 ) {
2007-05-01 23:26:30 +02:00
printk ( KERN_ERR DRVNAME " : adapter type unspecified \n " ) ;
2006-03-23 16:50:25 +01:00
return - ENODEV ;
}
if ( type > = ARRAY_SIZE ( adapter_parm ) ) {
2007-05-01 23:26:30 +02:00
printk ( KERN_ERR DRVNAME " : invalid type (%d) \n " , type ) ;
2006-03-23 16:50:25 +01:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
}
2006-01-09 23:19:51 +01:00
2005-04-16 15:20:36 -07:00
if ( base = = 0 ) {
2007-05-01 23:26:30 +02:00
pr_info ( DRVNAME " : using default base 0x%x \n " , DEFAULT_BASE ) ;
2005-04-16 15:20:36 -07:00
base = DEFAULT_BASE ;
}
2008-10-14 17:30:04 +02:00
if ( ! request_region ( base , 3 , DRVNAME ) )
return - EBUSY ;
2010-03-02 12:23:45 +01:00
if ( irq ! = 0 )
pr_info ( DRVNAME " : using irq %d \n " , irq ) ;
2011-05-24 20:58:49 +02:00
if ( ! adapter_parm [ type ] . getscl . val )
2005-04-16 15:20:36 -07:00
parport_algo_data . getscl = NULL ;
2007-05-01 23:26:30 +02:00
/* Sets global pdev as a side effect */
err = i2c_parport_device_add ( base ) ;
if ( err )
2008-10-14 17:30:04 +02:00
goto exit_release ;
2005-04-16 15:20:36 -07:00
2007-05-01 23:26:30 +02:00
err = platform_driver_register ( & i2c_parport_driver ) ;
if ( err )
goto exit_device ;
2006-01-09 23:19:51 +01:00
2005-04-16 15:20:36 -07:00
return 0 ;
2007-05-01 23:26:30 +02:00
exit_device :
platform_device_unregister ( pdev ) ;
2008-10-14 17:30:04 +02:00
exit_release :
release_region ( base , 3 ) ;
2007-05-01 23:26:30 +02:00
return err ;
2005-04-16 15:20:36 -07:00
}
static void __exit i2c_parport_exit ( void )
{
2007-05-01 23:26:30 +02:00
platform_driver_unregister ( & i2c_parport_driver ) ;
platform_device_unregister ( pdev ) ;
2008-10-14 17:30:04 +02:00
release_region ( base , 3 ) ;
2005-04-16 15:20:36 -07:00
}
2014-01-29 20:40:08 +01:00
MODULE_AUTHOR ( " Jean Delvare <jdelvare@suse.de> " ) ;
2005-04-16 15:20:36 -07:00
MODULE_DESCRIPTION ( " I2C bus over parallel port (light) " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( i2c_parport_init ) ;
module_exit ( i2c_parport_exit ) ;