2006-01-07 11:35:26 +11:00
/*
i2c Support for Apple SMU Controller
Copyright ( c ) 2005 Benjamin Herrenschmidt , IBM Corp .
< benh @ kernel . crashing . org >
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 .
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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/i2c.h>
# include <linux/device.h>
# include <linux/platform_device.h>
2013-09-17 14:28:33 -05:00
# include <linux/of_irq.h>
2006-01-07 11:35:26 +11:00
# include <asm/prom.h>
# include <asm/pmac_low_i2c.h>
MODULE_AUTHOR ( " Benjamin Herrenschmidt <benh@kernel.crashing.org> " ) ;
MODULE_DESCRIPTION ( " I2C driver for Apple PowerMac " ) ;
MODULE_LICENSE ( " GPL " ) ;
/*
* SMBUS - type transfer entrypoint
*/
static s32 i2c_powermac_smbus_xfer ( struct i2c_adapter * adap ,
u16 addr ,
unsigned short flags ,
char read_write ,
u8 command ,
int size ,
union i2c_smbus_data * data )
{
struct pmac_i2c_bus * bus = i2c_get_adapdata ( adap ) ;
int rc = 0 ;
int read = ( read_write = = I2C_SMBUS_READ ) ;
int addrdir = ( addr < < 1 ) | read ;
2009-12-06 17:06:17 +01:00
int mode , subsize , len ;
u32 subaddr ;
u8 * buf ;
2006-01-07 11:35:26 +11:00
u8 local [ 2 ] ;
2009-12-06 17:06:17 +01:00
if ( size = = I2C_SMBUS_QUICK | | size = = I2C_SMBUS_BYTE ) {
mode = pmac_i2c_mode_std ;
subsize = 0 ;
subaddr = 0 ;
} else {
mode = read ? pmac_i2c_mode_combined : pmac_i2c_mode_stdsub ;
subsize = 1 ;
subaddr = command ;
}
2006-01-07 11:35:26 +11:00
switch ( size ) {
case I2C_SMBUS_QUICK :
2009-12-06 17:06:17 +01:00
buf = NULL ;
len = 0 ;
2006-01-07 11:35:26 +11:00
break ;
case I2C_SMBUS_BYTE :
case I2C_SMBUS_BYTE_DATA :
2009-12-06 17:06:17 +01:00
buf = & data - > byte ;
len = 1 ;
2006-01-07 11:35:26 +11:00
break ;
case I2C_SMBUS_WORD_DATA :
if ( ! read ) {
local [ 0 ] = data - > word & 0xff ;
local [ 1 ] = ( data - > word > > 8 ) & 0xff ;
}
2009-12-06 17:06:17 +01:00
buf = local ;
len = 2 ;
2006-01-07 11:35:26 +11:00
break ;
/* Note that these are broken vs. the expected smbus API where
2008-01-14 21:53:30 +01:00
* on reads , the length is actually returned from the function ,
2006-01-07 11:35:26 +11:00
* but I think the current API makes no sense and I don ' t want
* any driver that I haven ' t verified for correctness to go
* anywhere near a pmac i2c bus anyway . . .
*
* I ' m also not completely sure what kind of phases to do between
* the actual command and the data ( what I am _supposed_ to do that
* is ) . For now , I assume writes are a single stream and reads have
* a repeat start / addr phase ( but not stop in between )
*/
case I2C_SMBUS_BLOCK_DATA :
2009-12-06 17:06:17 +01:00
buf = data - > block ;
len = data - > block [ 0 ] + 1 ;
2006-01-07 11:35:26 +11:00
break ;
case I2C_SMBUS_I2C_BLOCK_DATA :
2009-12-06 17:06:17 +01:00
buf = & data - > block [ 1 ] ;
len = data - > block [ 0 ] ;
2006-01-07 11:35:26 +11:00
break ;
default :
2009-12-06 17:06:17 +01:00
return - EINVAL ;
2006-01-07 11:35:26 +11:00
}
2009-12-06 17:06:17 +01:00
rc = pmac_i2c_open ( bus , 0 ) ;
2009-12-06 17:06:18 +01:00
if ( rc ) {
dev_err ( & adap - > dev , " Failed to open I2C, err %d \n " , rc ) ;
2009-12-06 17:06:17 +01:00
return rc ;
2009-12-06 17:06:18 +01:00
}
2009-12-06 17:06:17 +01:00
rc = pmac_i2c_setmode ( bus , mode ) ;
2009-12-06 17:06:18 +01:00
if ( rc ) {
dev_err ( & adap - > dev , " Failed to set I2C mode %d, err %d \n " ,
mode , rc ) ;
2009-12-06 17:06:17 +01:00
goto bail ;
2009-12-06 17:06:18 +01:00
}
2009-12-06 17:06:17 +01:00
rc = pmac_i2c_xfer ( bus , addrdir , subsize , subaddr , buf , len ) ;
2009-12-06 17:06:18 +01:00
if ( rc ) {
2010-03-13 20:56:52 +01:00
if ( rc = = - ENXIO )
dev_dbg ( & adap - > dev ,
" I2C transfer at 0x%02x failed, size %d, "
" err %d \n " , addrdir > > 1 , size , rc ) ;
else
dev_err ( & adap - > dev ,
" I2C transfer at 0x%02x failed, size %d, "
" err %d \n " , addrdir > > 1 , size , rc ) ;
2009-12-06 17:06:17 +01:00
goto bail ;
2009-12-06 17:06:18 +01:00
}
2009-12-06 17:06:17 +01:00
if ( size = = I2C_SMBUS_WORD_DATA & & read ) {
data - > word = ( ( u16 ) local [ 1 ] ) < < 8 ;
data - > word | = local [ 0 ] ;
}
2006-01-07 11:35:26 +11:00
bail :
pmac_i2c_close ( bus ) ;
return rc ;
}
/*
* Generic i2c master transfer entrypoint . This driver only support single
* messages ( for " lame i2c " transfers ) . Anything else should use the smbus
* entry point
*/
static int i2c_powermac_master_xfer ( struct i2c_adapter * adap ,
struct i2c_msg * msgs ,
int num )
{
struct pmac_i2c_bus * bus = i2c_get_adapdata ( adap ) ;
int rc = 0 ;
int read ;
int addrdir ;
2009-12-06 17:06:17 +01:00
if ( num ! = 1 ) {
dev_err ( & adap - > dev ,
" Multi-message I2C transactions not supported \n " ) ;
return - EOPNOTSUPP ;
}
2006-01-07 11:35:26 +11:00
if ( msgs - > flags & I2C_M_TEN )
return - EINVAL ;
read = ( msgs - > flags & I2C_M_RD ) ! = 0 ;
addrdir = ( msgs - > addr < < 1 ) | read ;
rc = pmac_i2c_open ( bus , 0 ) ;
2009-12-06 17:06:18 +01:00
if ( rc ) {
dev_err ( & adap - > dev , " Failed to open I2C, err %d \n " , rc ) ;
2006-01-07 11:35:26 +11:00
return rc ;
2009-12-06 17:06:18 +01:00
}
2006-01-07 11:35:26 +11:00
rc = pmac_i2c_setmode ( bus , pmac_i2c_mode_std ) ;
2009-12-06 17:06:18 +01:00
if ( rc ) {
dev_err ( & adap - > dev , " Failed to set I2C mode %d, err %d \n " ,
pmac_i2c_mode_std , rc ) ;
2006-01-07 11:35:26 +11:00
goto bail ;
2009-12-06 17:06:18 +01:00
}
2006-01-07 11:35:26 +11:00
rc = pmac_i2c_xfer ( bus , addrdir , 0 , 0 , msgs - > buf , msgs - > len ) ;
2010-03-13 20:56:52 +01:00
if ( rc < 0 ) {
if ( rc = = - ENXIO )
dev_dbg ( & adap - > dev , " I2C %s 0x%02x failed, err %d \n " ,
addrdir & 1 ? " read from " : " write to " ,
addrdir > > 1 , rc ) ;
else
dev_err ( & adap - > dev , " I2C %s 0x%02x failed, err %d \n " ,
addrdir & 1 ? " read from " : " write to " ,
addrdir > > 1 , rc ) ;
}
2006-01-07 11:35:26 +11:00
bail :
pmac_i2c_close ( bus ) ;
2006-07-01 17:12:53 +02:00
return rc < 0 ? rc : 1 ;
2006-01-07 11:35:26 +11:00
}
static u32 i2c_powermac_func ( struct i2c_adapter * adapter )
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_I2C ;
}
/* For now, we only handle smbus */
2006-09-03 22:39:46 +02:00
static const struct i2c_algorithm i2c_powermac_algorithm = {
2006-01-07 11:35:26 +11:00
. smbus_xfer = i2c_powermac_smbus_xfer ,
. master_xfer = i2c_powermac_master_xfer ,
. functionality = i2c_powermac_func ,
} ;
2012-11-27 15:59:38 -05:00
static int i2c_powermac_remove ( struct platform_device * dev )
2006-01-07 11:35:26 +11:00
{
2006-10-10 11:45:45 +10:00
struct i2c_adapter * adapter = platform_get_drvdata ( dev ) ;
2013-03-09 08:16:46 +00:00
i2c_del_adapter ( adapter ) ;
2009-12-06 17:06:19 +01:00
memset ( adapter , 0 , sizeof ( * adapter ) ) ;
2006-01-07 11:35:26 +11:00
return 0 ;
}
2012-11-27 15:59:38 -05:00
static u32 i2c_powermac_get_addr ( struct i2c_adapter * adap ,
2012-06-18 12:00:50 +10:00
struct pmac_i2c_bus * bus ,
struct device_node * node )
{
const __be32 * prop ;
int len ;
/* First check for valid "reg" */
prop = of_get_property ( node , " reg " , & len ) ;
if ( prop & & ( len > = sizeof ( int ) ) )
return ( be32_to_cpup ( prop ) & 0xff ) > > 1 ;
/* Then check old-style "i2c-address" */
prop = of_get_property ( node , " i2c-address " , & len ) ;
if ( prop & & ( len > = sizeof ( int ) ) )
return ( be32_to_cpup ( prop ) & 0xff ) > > 1 ;
/* Now handle some devices with missing "reg" properties */
if ( ! strcmp ( node - > name , " cereal " ) )
return 0x60 ;
else if ( ! strcmp ( node - > name , " deq " ) )
return 0x34 ;
dev_warn ( & adap - > dev , " No i2c address for %s \n " , node - > full_name ) ;
return 0xffffffff ;
}
2012-11-27 15:59:38 -05:00
static void i2c_powermac_create_one ( struct i2c_adapter * adap ,
2012-06-18 12:00:50 +10:00
const char * type ,
u32 addr )
{
struct i2c_board_info info = { } ;
struct i2c_client * newdev ;
strncpy ( info . type , type , sizeof ( info . type ) ) ;
info . addr = addr ;
newdev = i2c_new_device ( adap , & info ) ;
if ( ! newdev )
dev_err ( & adap - > dev ,
" i2c-powermac: Failure to register missing %s \n " ,
type ) ;
}
2012-11-27 15:59:38 -05:00
static void i2c_powermac_add_missing ( struct i2c_adapter * adap ,
2012-06-18 12:00:50 +10:00
struct pmac_i2c_bus * bus ,
bool found_onyx )
{
struct device_node * busnode = pmac_i2c_get_bus_node ( bus ) ;
int rc ;
/* Check for the onyx audio codec */
# define ONYX_REG_CONTROL 67
if ( of_device_is_compatible ( busnode , " k2-i2c " ) & & ! found_onyx ) {
union i2c_smbus_data data ;
rc = i2c_smbus_xfer ( adap , 0x46 , 0 , I2C_SMBUS_READ ,
ONYX_REG_CONTROL , I2C_SMBUS_BYTE_DATA ,
& data ) ;
if ( rc > = 0 )
i2c_powermac_create_one ( adap , " MAC,pcm3052 " , 0x46 ) ;
rc = i2c_smbus_xfer ( adap , 0x47 , 0 , I2C_SMBUS_READ ,
ONYX_REG_CONTROL , I2C_SMBUS_BYTE_DATA ,
& data ) ;
if ( rc > = 0 )
i2c_powermac_create_one ( adap , " MAC,pcm3052 " , 0x47 ) ;
}
}
2012-11-27 15:59:38 -05:00
static bool i2c_powermac_get_type ( struct i2c_adapter * adap ,
2012-06-18 12:00:50 +10:00
struct device_node * node ,
u32 addr , char * type , int type_size )
{
char tmp [ 16 ] ;
/* Note: we to _NOT_ want the standard
* i2c drivers to match with any of our powermac stuff
* unless they have been specifically modified to handle
* it on a case by case basis . For example , for thermal
* control , things like lm75 etc . . . shall match with their
* corresponding windfarm drivers , _NOT_ the generic ones ,
* so we force a prefix of AAPL , onto the modalias to
* make that happen
*/
/* First try proper modalias */
if ( of_modalias_node ( node , tmp , sizeof ( tmp ) ) > = 0 ) {
snprintf ( type , type_size , " MAC,%s " , tmp ) ;
return true ;
}
/* Now look for known workarounds */
if ( ! strcmp ( node - > name , " deq " ) ) {
/* Apple uses address 0x34 for TAS3001 and 0x35 for TAS3004 */
if ( addr = = 0x34 ) {
snprintf ( type , type_size , " MAC,tas3001 " ) ;
return true ;
} else if ( addr = = 0x35 ) {
snprintf ( type , type_size , " MAC,tas3004 " ) ;
return true ;
}
}
dev_err ( & adap - > dev , " i2c-powermac: modalias failure "
" on %s \n " , node - > full_name ) ;
return false ;
}
2012-11-27 15:59:38 -05:00
static void i2c_powermac_register_devices ( struct i2c_adapter * adap ,
2012-04-18 22:16:42 +00:00
struct pmac_i2c_bus * bus )
{
struct i2c_client * newdev ;
struct device_node * node ;
2012-06-18 12:00:50 +10:00
bool found_onyx = 0 ;
/*
* In some cases we end up with the via - pmu node itself , in this
* case we skip this function completely as the device - tree will
* not contain anything useful .
*/
if ( ! strcmp ( adap - > dev . of_node - > name , " via-pmu " ) )
return ;
2012-04-18 22:16:42 +00:00
for_each_child_of_node ( adap - > dev . of_node , node ) {
struct i2c_board_info info = { } ;
u32 addr ;
/* Get address & channel */
2012-06-18 12:00:50 +10:00
addr = i2c_powermac_get_addr ( adap , bus , node ) ;
if ( addr = = 0xffffffff )
2012-04-18 22:16:42 +00:00
continue ;
/* Multibus setup, check channel */
if ( ! pmac_i2c_match_adapter ( node , adap ) )
continue ;
dev_dbg ( & adap - > dev , " i2c-powermac: register %s \n " ,
node - > full_name ) ;
2012-06-18 12:00:50 +10:00
/*
* Keep track of some device existence to handle
* workarounds later .
2012-04-18 22:16:42 +00:00
*/
2012-06-18 12:00:50 +10:00
if ( of_device_is_compatible ( node , " pcm3052 " ) )
found_onyx = true ;
/* Make up a modalias */
if ( ! i2c_powermac_get_type ( adap , node , addr ,
info . type , sizeof ( info . type ) ) ) {
2012-04-18 22:16:42 +00:00
continue ;
}
/* Fill out the rest of the info structure */
2012-06-18 12:00:50 +10:00
info . addr = addr ;
2012-04-18 22:16:42 +00:00
info . irq = irq_of_parse_and_map ( node , 0 ) ;
info . of_node = of_node_get ( node ) ;
newdev = i2c_new_device ( adap , & info ) ;
if ( ! newdev ) {
dev_err ( & adap - > dev , " i2c-powermac: Failure to register "
" %s \n " , node - > full_name ) ;
of_node_put ( node ) ;
/* We do not dispose of the interrupt mapping on
* purpose . It ' s not necessary ( interrupt cannot be
* re - used ) and somebody else might have grabbed it
* via direct DT lookup so let ' s not bother
*/
continue ;
}
}
2012-06-18 12:00:50 +10:00
/* Additional workarounds */
i2c_powermac_add_missing ( adap , bus , found_onyx ) ;
2012-04-18 22:16:42 +00:00
}
2006-01-07 11:35:26 +11:00
2012-11-27 15:59:38 -05:00
static int i2c_powermac_probe ( struct platform_device * dev )
2006-01-07 11:35:26 +11:00
{
2013-07-30 16:59:33 +09:00
struct pmac_i2c_bus * bus = dev_get_platdata ( & dev - > dev ) ;
2006-01-07 11:35:26 +11:00
struct device_node * parent = NULL ;
struct i2c_adapter * adapter ;
2006-07-12 15:40:29 +10:00
const char * basename ;
2006-01-07 11:35:26 +11:00
int rc ;
if ( bus = = NULL )
return - EINVAL ;
2009-12-06 17:06:20 +01:00
adapter = pmac_i2c_get_adapter ( bus ) ;
2006-01-07 11:35:26 +11:00
/* Ok, now we need to make up a name for the interface that will
* match what we used to do in the past , that is basically the
* controller ' s parent device node for keywest . PMU didn ' t have a
* naming convention and SMU has a different one
*/
switch ( pmac_i2c_get_type ( bus ) ) {
case pmac_i2c_bus_keywest :
parent = of_get_parent ( pmac_i2c_get_controller ( bus ) ) ;
if ( parent = = NULL )
return - EINVAL ;
basename = parent - > name ;
break ;
case pmac_i2c_bus_pmu :
basename = " pmu " ;
break ;
case pmac_i2c_bus_smu :
/* This is not what we used to do but I'm fixing drivers at
* the same time as this change
*/
basename = " smu " ;
break ;
default :
return - EINVAL ;
}
2009-12-06 17:06:20 +01:00
snprintf ( adapter - > name , sizeof ( adapter - > name ) , " %s %d " , basename ,
pmac_i2c_get_channel ( bus ) ) ;
2006-01-07 11:35:26 +11:00
of_node_put ( parent ) ;
2006-10-10 11:45:45 +10:00
platform_set_drvdata ( dev , adapter ) ;
2006-01-07 11:35:26 +11:00
adapter - > algo = & i2c_powermac_algorithm ;
i2c_set_adapdata ( adapter , bus ) ;
2006-10-10 11:45:45 +10:00
adapter - > dev . parent = & dev - > dev ;
2013-07-11 12:56:15 +01:00
/* Clear of_node to skip automatic registration of i2c child nodes */
adapter - > dev . of_node = NULL ;
2006-01-07 11:35:26 +11:00
rc = i2c_add_adapter ( adapter ) ;
if ( rc ) {
printk ( KERN_ERR " i2c-powermac: Adapter %s registration "
2009-12-06 17:06:20 +01:00
" failed \n " , adapter - > name ) ;
2009-12-06 17:06:19 +01:00
memset ( adapter , 0 , sizeof ( * adapter ) ) ;
2013-07-11 22:02:03 +01:00
return rc ;
2006-01-07 11:35:26 +11:00
}
2009-12-06 17:06:20 +01:00
printk ( KERN_INFO " PowerMac i2c bus %s registered \n " , adapter - > name ) ;
2008-10-17 17:51:12 +02:00
2013-07-11 12:56:15 +01:00
/* Use custom child registration due to Apple device-tree funkyness */
adapter - > dev . of_node = dev - > dev . of_node ;
2012-04-18 22:16:42 +00:00
i2c_powermac_register_devices ( adapter , bus ) ;
2008-10-17 17:51:12 +02:00
2013-07-11 22:02:03 +01:00
return 0 ;
2006-01-07 11:35:26 +11:00
}
2006-10-10 11:45:45 +10:00
static struct platform_driver i2c_powermac_driver = {
2006-01-07 11:35:26 +11:00
. probe = i2c_powermac_probe ,
2012-11-27 15:59:38 -05:00
. remove = i2c_powermac_remove ,
2006-10-10 11:45:45 +10:00
. driver = {
. name = " i2c-powermac " ,
. bus = & platform_bus_type ,
} ,
2006-01-07 11:35:26 +11:00
} ;
2012-01-12 20:32:04 +01:00
module_platform_driver ( i2c_powermac_driver ) ;
2006-01-07 11:35:26 +11:00
2012-01-12 20:32:04 +01:00
MODULE_ALIAS ( " platform:i2c-powermac " ) ;