2008-10-16 09:03:29 +04:00
/*
2009-05-22 20:35:39 +04:00
* Copyright 1998 - 2009 VIA Technologies , Inc . All Rights Reserved .
2008-10-16 09:03:29 +04:00
* Copyright 2001 - 2008 S3 Graphics , Inc . All Rights Reserved .
* 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 , or ( at your option ) any later version .
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTIES OR REPRESENTATIONS ; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . ,
* 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
2010-04-23 03:39:34 +04:00
# include <linux/platform_device.h>
2010-05-06 00:24:18 +04:00
# include <linux/delay.h>
# include <linux/spinlock.h>
# include <linux/module.h>
2010-05-06 00:44:55 +04:00
# include <linux/via-core.h>
# include <linux/via_i2c.h>
2008-10-16 09:03:29 +04:00
2009-12-02 06:29:39 +03:00
/*
* There can only be one set of these , so there ' s no point in having
* them be dynamically allocated . . .
*/
# define VIAFB_NUM_I2C 5
static struct via_i2c_stuff via_i2c_par [ VIAFB_NUM_I2C ] ;
2010-04-23 00:36:04 +04:00
struct viafb_dev * i2c_vdev ; /* Passed in from core */
2009-12-02 06:29:39 +03:00
2008-10-16 09:03:29 +04:00
static void via_i2c_setscl ( void * data , int state )
{
u8 val ;
2009-12-02 06:29:39 +03:00
struct via_port_cfg * adap_data = data ;
2010-04-23 00:36:04 +04:00
unsigned long flags ;
2008-10-16 09:03:29 +04:00
2010-04-23 00:36:04 +04:00
spin_lock_irqsave ( & i2c_vdev - > reg_lock , flags ) ;
val = via_read_reg ( adap_data - > io_port , adap_data - > ioport_index ) & 0xF0 ;
2008-10-16 09:03:29 +04:00
if ( state )
val | = 0x20 ;
else
val & = ~ 0x20 ;
2009-05-22 20:35:39 +04:00
switch ( adap_data - > type ) {
2009-12-02 06:29:39 +03:00
case VIA_PORT_I2C :
2008-10-16 09:03:29 +04:00
val | = 0x01 ;
break ;
2009-12-02 06:29:39 +03:00
case VIA_PORT_GPIO :
2008-10-16 09:03:29 +04:00
val | = 0x80 ;
break ;
default :
2010-05-06 00:28:53 +04:00
printk ( KERN_ERR " viafb_i2c: specify wrong i2c type. \n " ) ;
2008-10-16 09:03:29 +04:00
}
2010-04-23 00:36:04 +04:00
via_write_reg ( adap_data - > io_port , adap_data - > ioport_index , val ) ;
spin_unlock_irqrestore ( & i2c_vdev - > reg_lock , flags ) ;
2008-10-16 09:03:29 +04:00
}
static int via_i2c_getscl ( void * data )
{
2009-12-02 06:29:39 +03:00
struct via_port_cfg * adap_data = data ;
2010-04-23 00:36:04 +04:00
unsigned long flags ;
int ret = 0 ;
spin_lock_irqsave ( & i2c_vdev - > reg_lock , flags ) ;
if ( via_read_reg ( adap_data - > io_port , adap_data - > ioport_index ) & 0x08 )
ret = 1 ;
spin_unlock_irqrestore ( & i2c_vdev - > reg_lock , flags ) ;
return ret ;
2008-10-16 09:03:29 +04:00
}
static int via_i2c_getsda ( void * data )
{
2009-12-02 06:29:39 +03:00
struct via_port_cfg * adap_data = data ;
2010-04-23 00:36:04 +04:00
unsigned long flags ;
int ret = 0 ;
spin_lock_irqsave ( & i2c_vdev - > reg_lock , flags ) ;
if ( via_read_reg ( adap_data - > io_port , adap_data - > ioport_index ) & 0x04 )
ret = 1 ;
spin_unlock_irqrestore ( & i2c_vdev - > reg_lock , flags ) ;
return ret ;
2008-10-16 09:03:29 +04:00
}
static void via_i2c_setsda ( void * data , int state )
{
u8 val ;
2009-12-02 06:29:39 +03:00
struct via_port_cfg * adap_data = data ;
2010-04-23 00:36:04 +04:00
unsigned long flags ;
2008-10-16 09:03:29 +04:00
2010-04-23 00:36:04 +04:00
spin_lock_irqsave ( & i2c_vdev - > reg_lock , flags ) ;
val = via_read_reg ( adap_data - > io_port , adap_data - > ioport_index ) & 0xF0 ;
2008-10-16 09:03:29 +04:00
if ( state )
val | = 0x10 ;
else
val & = ~ 0x10 ;
2009-05-22 20:35:39 +04:00
switch ( adap_data - > type ) {
2009-12-02 06:29:39 +03:00
case VIA_PORT_I2C :
2008-10-16 09:03:29 +04:00
val | = 0x01 ;
break ;
2009-12-02 06:29:39 +03:00
case VIA_PORT_GPIO :
2008-10-16 09:03:29 +04:00
val | = 0x40 ;
break ;
default :
2010-05-06 00:28:53 +04:00
printk ( KERN_ERR " viafb_i2c: specify wrong i2c type. \n " ) ;
2008-10-16 09:03:29 +04:00
}
2010-04-23 00:36:04 +04:00
via_write_reg ( adap_data - > io_port , adap_data - > ioport_index , val ) ;
spin_unlock_irqrestore ( & i2c_vdev - > reg_lock , flags ) ;
2008-10-16 09:03:29 +04:00
}
2009-05-22 20:35:39 +04:00
int viafb_i2c_readbyte ( u8 adap , u8 slave_addr , u8 index , u8 * pdata )
2008-10-16 09:03:29 +04:00
{
u8 mm1 [ ] = { 0x00 } ;
struct i2c_msg msgs [ 2 ] ;
2009-12-28 20:04:02 +03:00
if ( ! via_i2c_par [ adap ] . is_active )
return - ENODEV ;
2008-10-16 09:03:29 +04:00
* pdata = 0 ;
msgs [ 0 ] . flags = 0 ;
msgs [ 1 ] . flags = I2C_M_RD ;
msgs [ 0 ] . addr = msgs [ 1 ] . addr = slave_addr / 2 ;
mm1 [ 0 ] = index ;
msgs [ 0 ] . len = 1 ; msgs [ 1 ] . len = 1 ;
msgs [ 0 ] . buf = mm1 ; msgs [ 1 ] . buf = pdata ;
2009-12-02 06:29:39 +03:00
return i2c_transfer ( & via_i2c_par [ adap ] . adapter , msgs , 2 ) ;
2008-10-16 09:03:29 +04:00
}
2009-05-22 20:35:39 +04:00
int viafb_i2c_writebyte ( u8 adap , u8 slave_addr , u8 index , u8 data )
2008-10-16 09:03:29 +04:00
{
u8 msg [ 2 ] = { index , data } ;
struct i2c_msg msgs ;
2009-12-28 20:04:02 +03:00
if ( ! via_i2c_par [ adap ] . is_active )
return - ENODEV ;
2008-10-16 09:03:29 +04:00
msgs . flags = 0 ;
msgs . addr = slave_addr / 2 ;
msgs . len = 2 ;
msgs . buf = msg ;
2009-12-02 06:29:39 +03:00
return i2c_transfer ( & via_i2c_par [ adap ] . adapter , & msgs , 1 ) ;
2008-10-16 09:03:29 +04:00
}
2009-05-22 20:35:39 +04:00
int viafb_i2c_readbytes ( u8 adap , u8 slave_addr , u8 index , u8 * buff , int buff_len )
2008-10-16 09:03:29 +04:00
{
u8 mm1 [ ] = { 0x00 } ;
struct i2c_msg msgs [ 2 ] ;
2009-12-28 20:04:02 +03:00
if ( ! via_i2c_par [ adap ] . is_active )
return - ENODEV ;
2008-10-16 09:03:29 +04:00
msgs [ 0 ] . flags = 0 ;
msgs [ 1 ] . flags = I2C_M_RD ;
msgs [ 0 ] . addr = msgs [ 1 ] . addr = slave_addr / 2 ;
mm1 [ 0 ] = index ;
msgs [ 0 ] . len = 1 ; msgs [ 1 ] . len = buff_len ;
msgs [ 0 ] . buf = mm1 ; msgs [ 1 ] . buf = buff ;
2009-12-02 06:29:39 +03:00
return i2c_transfer ( & via_i2c_par [ adap ] . adapter , msgs , 2 ) ;
2008-10-16 09:03:29 +04:00
}
2010-04-23 18:56:55 +04:00
/*
* Allow other viafb subdevices to look up a specific adapter
* by port name .
*/
struct i2c_adapter * viafb_find_i2c_adapter ( enum viafb_i2c_adap which )
{
struct via_i2c_stuff * stuff = & via_i2c_par [ which ] ;
return & stuff - > adapter ;
}
EXPORT_SYMBOL_GPL ( viafb_find_i2c_adapter ) ;
2009-05-22 20:35:39 +04:00
static int create_i2c_bus ( struct i2c_adapter * adapter ,
struct i2c_algo_bit_data * algo ,
2009-12-02 06:29:39 +03:00
struct via_port_cfg * adap_cfg ,
2009-05-22 20:35:39 +04:00
struct pci_dev * pdev )
2008-10-16 09:03:29 +04:00
{
2009-05-22 20:35:39 +04:00
algo - > setsda = via_i2c_setsda ;
algo - > setscl = via_i2c_setscl ;
algo - > getsda = via_i2c_getsda ;
algo - > getscl = via_i2c_getscl ;
algo - > udelay = 40 ;
algo - > timeout = 20 ;
algo - > data = adap_cfg ;
sprintf ( adapter - > name , " viafb i2c io_port idx 0x%02x " ,
adap_cfg - > ioport_index ) ;
adapter - > owner = THIS_MODULE ;
adapter - > id = 0x01FFFF ;
adapter - > class = I2C_CLASS_DDC ;
adapter - > algo_data = algo ;
if ( pdev )
adapter - > dev . parent = & pdev - > dev ;
else
adapter - > dev . parent = NULL ;
/* i2c_set_adapdata(adapter, adap_cfg); */
2008-10-16 09:03:29 +04:00
/* Raise SCL and SDA */
2009-05-22 20:35:39 +04:00
via_i2c_setsda ( adap_cfg , 1 ) ;
via_i2c_setscl ( adap_cfg , 1 ) ;
2008-10-16 09:03:29 +04:00
udelay ( 20 ) ;
2009-05-22 20:35:39 +04:00
return i2c_bit_add_bus ( adapter ) ;
}
2010-04-23 03:39:34 +04:00
static int viafb_i2c_probe ( struct platform_device * platdev )
2009-05-22 20:35:39 +04:00
{
int i , ret ;
2010-04-23 03:39:34 +04:00
struct via_port_cfg * configs ;
i2c_vdev = platdev - > dev . platform_data ;
configs = i2c_vdev - > port_cfg ;
2009-05-22 20:35:39 +04:00
2009-12-02 06:29:39 +03:00
for ( i = 0 ; i < VIAFB_NUM_PORTS ; i + + ) {
struct via_port_cfg * adap_cfg = configs + + ;
struct via_i2c_stuff * i2c_stuff = & via_i2c_par [ i ] ;
2009-05-22 20:35:39 +04:00
2009-12-28 20:04:02 +03:00
i2c_stuff - > is_active = 0 ;
2009-12-02 06:29:39 +03:00
if ( adap_cfg - > type = = 0 | | adap_cfg - > mode ! = VIA_MODE_I2C )
2010-04-25 18:30:41 +04:00
continue ;
2009-05-22 20:35:39 +04:00
ret = create_i2c_bus ( & i2c_stuff - > adapter ,
& i2c_stuff - > algo , adap_cfg ,
NULL ) ; /* FIXME: PCIDEV */
if ( ret < 0 ) {
printk ( KERN_ERR " viafb: cannot create i2c bus %u:%d \n " ,
i , ret ) ;
2009-12-28 20:04:02 +03:00
continue ; /* Still try to make the rest */
2009-05-22 20:35:39 +04:00
}
2009-12-28 20:04:02 +03:00
i2c_stuff - > is_active = 1 ;
2009-05-22 20:35:39 +04:00
}
return 0 ;
2008-10-16 09:03:29 +04:00
}
2010-04-23 03:39:34 +04:00
static int viafb_i2c_remove ( struct platform_device * platdev )
2008-10-16 09:03:29 +04:00
{
2009-05-22 20:35:39 +04:00
int i ;
2009-12-02 06:29:39 +03:00
for ( i = 0 ; i < VIAFB_NUM_PORTS ; i + + ) {
struct via_i2c_stuff * i2c_stuff = & via_i2c_par [ i ] ;
/*
* Only remove those entries in the array that we ' ve
* actually used ( and thus initialized algo_data )
*/
2009-12-28 20:04:02 +03:00
if ( i2c_stuff - > is_active )
2009-05-22 20:35:39 +04:00
i2c_del_adapter ( & i2c_stuff - > adapter ) ;
}
2010-04-23 03:39:34 +04:00
return 0 ;
}
static struct platform_driver via_i2c_driver = {
. driver = {
. name = " viafb-i2c " ,
} ,
. probe = viafb_i2c_probe ,
. remove = viafb_i2c_remove ,
} ;
int viafb_i2c_init ( void )
{
return platform_driver_register ( & via_i2c_driver ) ;
}
void viafb_i2c_exit ( void )
{
platform_driver_unregister ( & via_i2c_driver ) ;
2008-10-16 09:03:29 +04:00
}