2005-09-26 16:04:21 +10:00
/*
2006-01-07 11:30:44 +11:00
* arch / powerpc / platforms / powermac / low_i2c . c
2005-09-26 16:04:21 +10:00
*
2006-01-07 11:30:44 +11:00
* Copyright ( C ) 2003 - 2005 Ben . Herrenschmidt ( benh @ kernel . crashing . org )
2005-09-26 16:04:21 +10: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 .
*
2006-01-07 11:30:44 +11:00
* The linux i2c layer isn ' t completely suitable for our needs for various
* reasons ranging from too late initialisation to semantics not perfectly
* matching some requirements of the apple platform functions etc . . .
*
* This file thus provides a simple low level unified i2c interface for
* powermac that covers the various types of i2c busses used in Apple machines .
* For now , keywest , PMU and SMU , though we could add Cuda , or other bit
* banging busses found on older chipstes in earlier machines if we ever need
* one of them .
*
* The drivers in this file are synchronous / blocking . In addition , the
* keywest one is fairly slow due to the use of msleep instead of interrupts
* as the interrupt is currently used by i2c - keywest . In the long run , we
* might want to get rid of those high - level interfaces to linux i2c layer
* either completely ( converting all drivers ) or replacing them all with a
* single stub driver on top of this one . Once done , the interrupt will be
* available for our use .
2005-09-26 16:04:21 +10:00
*/
# undef DEBUG
2006-01-07 11:30:44 +11:00
# undef DEBUG_LOW
2005-09-26 16:04:21 +10:00
# include <linux/types.h>
# include <linux/sched.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/adb.h>
# include <linux/pmu.h>
2006-01-07 11:30:44 +11:00
# include <linux/delay.h>
# include <linux/completion.h>
2006-01-07 11:35:26 +11:00
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/completion.h>
# include <linux/timer.h>
2005-09-26 16:04:21 +10:00
# include <asm/keylargo.h>
# include <asm/uninorth.h>
# include <asm/io.h>
# include <asm/prom.h>
# include <asm/machdep.h>
2006-01-07 11:30:44 +11:00
# include <asm/smu.h>
2006-01-07 11:41:02 +11:00
# include <asm/pmac_pfunc.h>
2005-09-26 16:04:21 +10:00
# include <asm/pmac_low_i2c.h>
# ifdef DEBUG
# define DBG(x...) do {\
2005-11-23 17:57:25 +11:00
printk ( KERN_DEBUG " low_i2c: " x ) ; \
2005-09-26 16:04:21 +10:00
} while ( 0 )
# else
# define DBG(x...)
# endif
2006-01-07 11:30:44 +11:00
# ifdef DEBUG_LOW
# define DBG_LOW(x...) do {\
printk ( KERN_DEBUG " low_i2c: " x ) ; \
} while ( 0 )
# else
# define DBG_LOW(x...)
# endif
2005-09-26 16:04:21 +10:00
2006-01-07 11:35:26 +11:00
static int pmac_i2c_force_poll = 1 ;
2006-01-07 11:30:44 +11:00
/*
* A bus structure . Each bus in the system has such a structure associated .
2005-09-26 16:04:21 +10:00
*/
2006-01-07 11:30:44 +11:00
struct pmac_i2c_bus
2005-09-26 16:04:21 +10:00
{
2006-01-07 11:30:44 +11:00
struct list_head link ;
struct device_node * controller ;
struct device_node * busnode ;
int type ;
int flags ;
struct i2c_adapter * adapter ;
void * hostdata ;
int channel ; /* some hosts have multiple */
int mode ; /* current mode */
struct semaphore sem ;
int opened ;
int polled ; /* open mode */
2006-01-07 11:35:26 +11:00
struct platform_device * platform_dev ;
2006-01-07 11:30:44 +11:00
/* ops */
int ( * open ) ( struct pmac_i2c_bus * bus ) ;
void ( * close ) ( struct pmac_i2c_bus * bus ) ;
int ( * xfer ) ( struct pmac_i2c_bus * bus , u8 addrdir , int subsize ,
u32 subaddr , u8 * data , int len ) ;
} ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
static LIST_HEAD ( pmac_i2c_busses ) ;
2005-09-26 16:04:21 +10:00
/*
2006-01-07 11:30:44 +11:00
* Keywest implementation
2005-09-26 16:04:21 +10:00
*/
2006-01-07 11:30:44 +11:00
struct pmac_i2c_host_kw
{
struct semaphore mutex ; /* Access mutex for use by
* i2c - keywest */
void __iomem * base ; /* register base address */
int bsteps ; /* register stepping */
int speed ; /* speed */
2006-01-07 11:35:26 +11:00
int irq ;
u8 * data ;
unsigned len ;
int state ;
int rw ;
int polled ;
int result ;
struct completion complete ;
spinlock_t lock ;
struct timer_list timeout_timer ;
2006-01-07 11:30:44 +11:00
} ;
2005-09-26 16:04:21 +10:00
/* Register indices */
typedef enum {
reg_mode = 0 ,
reg_control ,
reg_status ,
reg_isr ,
reg_ier ,
reg_addr ,
reg_subaddr ,
reg_data
} reg_t ;
2006-01-07 11:35:26 +11:00
/* The Tumbler audio equalizer can be really slow sometimes */
# define KW_POLL_TIMEOUT (2*HZ)
2005-09-26 16:04:21 +10:00
/* Mode register */
# define KW_I2C_MODE_100KHZ 0x00
# define KW_I2C_MODE_50KHZ 0x01
# define KW_I2C_MODE_25KHZ 0x02
# define KW_I2C_MODE_DUMB 0x00
# define KW_I2C_MODE_STANDARD 0x04
# define KW_I2C_MODE_STANDARDSUB 0x08
# define KW_I2C_MODE_COMBINED 0x0C
# define KW_I2C_MODE_MODE_MASK 0x0C
# define KW_I2C_MODE_CHAN_MASK 0xF0
/* Control register */
# define KW_I2C_CTL_AAK 0x01
# define KW_I2C_CTL_XADDR 0x02
# define KW_I2C_CTL_STOP 0x04
# define KW_I2C_CTL_START 0x08
/* Status register */
# define KW_I2C_STAT_BUSY 0x01
# define KW_I2C_STAT_LAST_AAK 0x02
# define KW_I2C_STAT_LAST_RW 0x04
# define KW_I2C_STAT_SDA 0x08
# define KW_I2C_STAT_SCL 0x10
/* IER & ISR registers */
# define KW_I2C_IRQ_DATA 0x01
# define KW_I2C_IRQ_ADDR 0x02
# define KW_I2C_IRQ_STOP 0x04
# define KW_I2C_IRQ_START 0x08
# define KW_I2C_IRQ_MASK 0x0F
/* State machine states */
enum {
state_idle ,
state_addr ,
state_read ,
state_write ,
state_stop ,
state_dead
} ;
# define WRONG_STATE(name) do {\
2006-01-07 11:35:26 +11:00
printk ( KERN_DEBUG " KW: wrong state. Got %s, state: %s " \
" (isr: %02x) \n " , \
name , __kw_state_names [ host - > state ] , isr ) ; \
2005-09-26 16:04:21 +10:00
} while ( 0 )
static const char * __kw_state_names [ ] = {
" state_idle " ,
" state_addr " ,
" state_read " ,
" state_write " ,
" state_stop " ,
" state_dead "
} ;
2006-01-07 11:35:26 +11:00
static inline u8 __kw_read_reg ( struct pmac_i2c_host_kw * host , reg_t reg )
2005-09-26 16:04:21 +10:00
{
return readb ( host - > base + ( ( ( unsigned int ) reg ) < < host - > bsteps ) ) ;
}
2006-01-07 11:35:26 +11:00
static inline void __kw_write_reg ( struct pmac_i2c_host_kw * host ,
reg_t reg , u8 val )
2005-09-26 16:04:21 +10:00
{
writeb ( val , host - > base + ( ( ( unsigned ) reg ) < < host - > bsteps ) ) ;
2006-01-07 11:35:26 +11:00
( void ) __kw_read_reg ( host , reg_subaddr ) ;
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:35:26 +11:00
# define kw_write_reg(reg, val) __kw_write_reg(host, reg, val)
# define kw_read_reg(reg) __kw_read_reg(host, reg)
2005-09-26 16:04:21 +10:00
2006-01-07 11:35:26 +11:00
static u8 kw_i2c_wait_interrupt ( struct pmac_i2c_host_kw * host )
2005-09-26 16:04:21 +10:00
{
int i , j ;
u8 isr ;
2006-01-07 11:30:44 +11:00
for ( i = 0 ; i < 1000 ; i + + ) {
2005-09-26 16:04:21 +10:00
isr = kw_read_reg ( reg_isr ) & KW_I2C_IRQ_MASK ;
if ( isr ! = 0 )
return isr ;
/* This code is used with the timebase frozen, we cannot rely
2006-01-07 11:30:44 +11:00
* on udelay nor schedule when in polled mode !
* For now , just use a bogus loop . . . .
2005-09-26 16:04:21 +10:00
*/
2006-01-07 11:35:26 +11:00
if ( host - > polled ) {
for ( j = 1 ; j < 100000 ; j + + )
2006-01-07 11:30:44 +11:00
mb ( ) ;
} else
msleep ( 1 ) ;
2005-09-26 16:04:21 +10:00
}
return isr ;
}
2006-04-18 14:11:53 +10:00
static void kw_i2c_do_stop ( struct pmac_i2c_host_kw * host , int result )
{
kw_write_reg ( reg_control , KW_I2C_CTL_STOP ) ;
host - > state = state_stop ;
host - > result = result ;
}
2006-01-07 11:35:26 +11:00
static void kw_i2c_handle_interrupt ( struct pmac_i2c_host_kw * host , u8 isr )
2005-09-26 16:04:21 +10:00
{
u8 ack ;
2006-01-07 11:30:44 +11:00
DBG_LOW ( " kw_handle_interrupt(%s, isr: %x) \n " ,
2006-01-07 11:35:26 +11:00
__kw_state_names [ host - > state ] , isr ) ;
if ( host - > state = = state_idle ) {
printk ( KERN_WARNING " low_i2c: Keywest got an out of state "
" interrupt, ignoring \n " ) ;
kw_write_reg ( reg_isr , isr ) ;
return ;
}
2005-09-26 16:04:21 +10:00
if ( isr = = 0 ) {
2006-04-18 14:11:53 +10:00
printk ( KERN_WARNING " low_i2c: Timeout in i2c transfer "
" on keywest ! \n " ) ;
2006-01-07 11:35:26 +11:00
if ( host - > state ! = state_stop ) {
2006-04-18 14:11:53 +10:00
kw_i2c_do_stop ( host , - EIO ) ;
return ;
2005-09-26 16:04:21 +10:00
}
2006-04-18 14:11:53 +10:00
ack = kw_read_reg ( reg_status ) ;
if ( ack & KW_I2C_STAT_BUSY )
kw_write_reg ( reg_status , 0 ) ;
host - > state = state_idle ;
kw_write_reg ( reg_ier , 0x00 ) ;
if ( ! host - > polled )
complete ( & host - > complete ) ;
2006-01-07 11:35:26 +11:00
return ;
2005-09-26 16:04:21 +10:00
}
if ( isr & KW_I2C_IRQ_ADDR ) {
ack = kw_read_reg ( reg_status ) ;
2006-01-07 11:35:26 +11:00
if ( host - > state ! = state_addr ) {
2005-09-26 16:04:21 +10:00
WRONG_STATE ( " KW_I2C_IRQ_ADDR " ) ;
2006-04-18 14:11:53 +10:00
kw_i2c_do_stop ( host , - EIO ) ;
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:30:44 +11:00
if ( ( ack & KW_I2C_STAT_LAST_AAK ) = = 0 ) {
2006-04-18 14:11:53 +10:00
host - > result = - ENXIO ;
2006-01-07 11:35:26 +11:00
host - > state = state_stop ;
2006-04-18 14:11:53 +10:00
DBG_LOW ( " KW: NAK on address \n " ) ;
2005-09-26 16:04:21 +10:00
} else {
2006-04-18 14:11:53 +10:00
if ( host - > len = = 0 )
kw_i2c_do_stop ( host , 0 ) ;
else if ( host - > rw ) {
2006-01-07 11:35:26 +11:00
host - > state = state_read ;
if ( host - > len > 1 )
2006-01-07 11:30:44 +11:00
kw_write_reg ( reg_control ,
KW_I2C_CTL_AAK ) ;
2005-09-26 16:04:21 +10:00
} else {
2006-01-07 11:35:26 +11:00
host - > state = state_write ;
kw_write_reg ( reg_data , * ( host - > data + + ) ) ;
host - > len - - ;
2005-09-26 16:04:21 +10:00
}
}
kw_write_reg ( reg_isr , KW_I2C_IRQ_ADDR ) ;
}
if ( isr & KW_I2C_IRQ_DATA ) {
2006-01-07 11:35:26 +11:00
if ( host - > state = = state_read ) {
* ( host - > data + + ) = kw_read_reg ( reg_data ) ;
host - > len - - ;
2005-09-26 16:04:21 +10:00
kw_write_reg ( reg_isr , KW_I2C_IRQ_DATA ) ;
2006-01-07 11:35:26 +11:00
if ( host - > len = = 0 )
host - > state = state_stop ;
else if ( host - > len = = 1 )
2005-09-26 16:04:21 +10:00
kw_write_reg ( reg_control , 0 ) ;
2006-01-07 11:35:26 +11:00
} else if ( host - > state = = state_write ) {
2005-09-26 16:04:21 +10:00
ack = kw_read_reg ( reg_status ) ;
if ( ( ack & KW_I2C_STAT_LAST_AAK ) = = 0 ) {
2006-01-07 11:30:44 +11:00
DBG_LOW ( " KW: nack on data write \n " ) ;
2006-04-18 14:11:53 +10:00
host - > result = - EFBIG ;
host - > state = state_stop ;
2006-01-07 11:35:26 +11:00
} else if ( host - > len ) {
kw_write_reg ( reg_data , * ( host - > data + + ) ) ;
host - > len - - ;
2006-04-18 14:11:53 +10:00
} else
kw_i2c_do_stop ( host , 0 ) ;
2005-09-26 16:04:21 +10:00
} else {
WRONG_STATE ( " KW_I2C_IRQ_DATA " ) ;
2006-04-18 14:11:53 +10:00
if ( host - > state ! = state_stop )
kw_i2c_do_stop ( host , - EIO ) ;
2005-09-26 16:04:21 +10:00
}
2006-04-18 14:11:53 +10:00
kw_write_reg ( reg_isr , KW_I2C_IRQ_DATA ) ;
2005-09-26 16:04:21 +10:00
}
if ( isr & KW_I2C_IRQ_STOP ) {
kw_write_reg ( reg_isr , KW_I2C_IRQ_STOP ) ;
2006-01-07 11:35:26 +11:00
if ( host - > state ! = state_stop ) {
2005-09-26 16:04:21 +10:00
WRONG_STATE ( " KW_I2C_IRQ_STOP " ) ;
2006-01-07 11:35:26 +11:00
host - > result = - EIO ;
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:35:26 +11:00
host - > state = state_idle ;
if ( ! host - > polled )
complete ( & host - > complete ) ;
2005-09-26 16:04:21 +10:00
}
2006-04-18 14:11:53 +10:00
/* Below should only happen in manual mode which we don't use ... */
2005-09-26 16:04:21 +10:00
if ( isr & KW_I2C_IRQ_START )
kw_write_reg ( reg_isr , KW_I2C_IRQ_START ) ;
2006-01-07 11:35:26 +11:00
}
/* Interrupt handler */
static irqreturn_t kw_i2c_irq ( int irq , void * dev_id , struct pt_regs * regs )
{
struct pmac_i2c_host_kw * host = dev_id ;
unsigned long flags ;
spin_lock_irqsave ( & host - > lock , flags ) ;
del_timer ( & host - > timeout_timer ) ;
kw_i2c_handle_interrupt ( host , kw_read_reg ( reg_isr ) ) ;
if ( host - > state ! = state_idle ) {
host - > timeout_timer . expires = jiffies + KW_POLL_TIMEOUT ;
add_timer ( & host - > timeout_timer ) ;
}
spin_unlock_irqrestore ( & host - > lock , flags ) ;
return IRQ_HANDLED ;
}
static void kw_i2c_timeout ( unsigned long data )
{
struct pmac_i2c_host_kw * host = ( struct pmac_i2c_host_kw * ) data ;
unsigned long flags ;
spin_lock_irqsave ( & host - > lock , flags ) ;
kw_i2c_handle_interrupt ( host , kw_read_reg ( reg_isr ) ) ;
if ( host - > state ! = state_idle ) {
host - > timeout_timer . expires = jiffies + KW_POLL_TIMEOUT ;
add_timer ( & host - > timeout_timer ) ;
}
spin_unlock_irqrestore ( & host - > lock , flags ) ;
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:30:44 +11:00
static int kw_i2c_open ( struct pmac_i2c_bus * bus )
2005-09-26 16:04:21 +10:00
{
2006-01-07 11:30:44 +11:00
struct pmac_i2c_host_kw * host = bus - > hostdata ;
down ( & host - > mutex ) ;
return 0 ;
}
static void kw_i2c_close ( struct pmac_i2c_bus * bus )
{
struct pmac_i2c_host_kw * host = bus - > hostdata ;
up ( & host - > mutex ) ;
}
static int kw_i2c_xfer ( struct pmac_i2c_bus * bus , u8 addrdir , int subsize ,
u32 subaddr , u8 * data , int len )
{
struct pmac_i2c_host_kw * host = bus - > hostdata ;
2005-09-26 16:04:21 +10:00
u8 mode_reg = host - > speed ;
2006-01-07 11:35:26 +11:00
int use_irq = host - > irq ! = NO_IRQ & & ! bus - > polled ;
2005-09-26 16:04:21 +10:00
/* Setup mode & subaddress if any */
2006-01-07 11:30:44 +11:00
switch ( bus - > mode ) {
case pmac_i2c_mode_dumb :
2005-09-26 16:04:21 +10:00
return - EINVAL ;
2006-01-07 11:30:44 +11:00
case pmac_i2c_mode_std :
2005-09-26 16:04:21 +10:00
mode_reg | = KW_I2C_MODE_STANDARD ;
2006-01-07 11:30:44 +11:00
if ( subsize ! = 0 )
return - EINVAL ;
2005-09-26 16:04:21 +10:00
break ;
2006-01-07 11:30:44 +11:00
case pmac_i2c_mode_stdsub :
2005-09-26 16:04:21 +10:00
mode_reg | = KW_I2C_MODE_STANDARDSUB ;
2006-01-07 11:30:44 +11:00
if ( subsize ! = 1 )
return - EINVAL ;
2005-09-26 16:04:21 +10:00
break ;
2006-01-07 11:30:44 +11:00
case pmac_i2c_mode_combined :
2005-09-26 16:04:21 +10:00
mode_reg | = KW_I2C_MODE_COMBINED ;
2006-01-07 11:30:44 +11:00
if ( subsize ! = 1 )
return - EINVAL ;
2005-09-26 16:04:21 +10:00
break ;
}
/* Setup channel & clear pending irqs */
kw_write_reg ( reg_isr , kw_read_reg ( reg_isr ) ) ;
2006-01-07 11:30:44 +11:00
kw_write_reg ( reg_mode , mode_reg | ( bus - > channel < < 4 ) ) ;
2005-09-26 16:04:21 +10:00
kw_write_reg ( reg_status , 0 ) ;
2006-01-07 11:30:44 +11:00
/* Set up address and r/w bit, strip possible stale bus number from
* address top bits
*/
kw_write_reg ( reg_addr , addrdir & 0xff ) ;
2005-09-26 16:04:21 +10:00
/* Set up the sub address */
if ( ( mode_reg & KW_I2C_MODE_MODE_MASK ) = = KW_I2C_MODE_STANDARDSUB
| | ( mode_reg & KW_I2C_MODE_MODE_MASK ) = = KW_I2C_MODE_COMBINED )
kw_write_reg ( reg_subaddr , subaddr ) ;
2006-01-07 11:35:26 +11:00
/* Prepare for async operations */
host - > data = data ;
host - > len = len ;
host - > state = state_addr ;
host - > result = 0 ;
host - > rw = ( addrdir & 1 ) ;
host - > polled = bus - > polled ;
/* Enable interrupt if not using polled mode and interrupt is
* available
*/
if ( use_irq ) {
/* Clear completion */
INIT_COMPLETION ( host - > complete ) ;
/* Ack stale interrupts */
kw_write_reg ( reg_isr , kw_read_reg ( reg_isr ) ) ;
/* Arm timeout */
host - > timeout_timer . expires = jiffies + KW_POLL_TIMEOUT ;
add_timer ( & host - > timeout_timer ) ;
/* Enable emission */
kw_write_reg ( reg_ier , KW_I2C_IRQ_MASK ) ;
}
/* Start sending address */
2005-09-26 16:04:21 +10:00
kw_write_reg ( reg_control , KW_I2C_CTL_XADDR ) ;
2006-01-07 11:35:26 +11:00
/* Wait for completion */
if ( use_irq )
wait_for_completion ( & host - > complete ) ;
else {
while ( host - > state ! = state_idle ) {
unsigned long flags ;
u8 isr = kw_i2c_wait_interrupt ( host ) ;
spin_lock_irqsave ( & host - > lock , flags ) ;
kw_i2c_handle_interrupt ( host , isr ) ;
spin_unlock_irqrestore ( & host - > lock , flags ) ;
}
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:35:26 +11:00
/* Disable emission */
kw_write_reg ( reg_ier , 0 ) ;
return host - > result ;
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:30:44 +11:00
static struct pmac_i2c_host_kw * __init kw_i2c_host_init ( struct device_node * np )
2005-09-26 16:04:21 +10:00
{
2006-01-07 11:30:44 +11:00
struct pmac_i2c_host_kw * host ;
2005-11-23 17:57:25 +11:00
u32 * psteps , * prate , * addrp , steps ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
host = kzalloc ( sizeof ( struct pmac_i2c_host_kw ) , GFP_KERNEL ) ;
2005-09-26 16:04:21 +10:00
if ( host = = NULL ) {
printk ( KERN_ERR " low_i2c: Can't allocate host for %s \n " ,
np - > full_name ) ;
2006-01-07 11:30:44 +11:00
return NULL ;
2005-09-26 16:04:21 +10:00
}
2005-11-23 17:57:25 +11:00
/* Apple is kind enough to provide a valid AAPL,address property
* on all i2c keywest nodes so far . . . we would have to fallback
* to macio parsing if that wasn ' t the case
*/
addrp = ( u32 * ) get_property ( np , " AAPL,address " , NULL ) ;
if ( addrp = = NULL ) {
printk ( KERN_ERR " low_i2c: Can't find address for %s \n " ,
np - > full_name ) ;
2006-01-07 11:30:44 +11:00
kfree ( host ) ;
return NULL ;
2005-11-23 17:57:25 +11:00
}
2005-09-26 16:04:21 +10:00
init_MUTEX ( & host - > mutex ) ;
2006-01-07 11:35:26 +11:00
init_completion ( & host - > complete ) ;
spin_lock_init ( & host - > lock ) ;
init_timer ( & host - > timeout_timer ) ;
host - > timeout_timer . function = kw_i2c_timeout ;
host - > timeout_timer . data = ( unsigned long ) host ;
2005-09-26 16:04:21 +10:00
psteps = ( u32 * ) get_property ( np , " AAPL,address-step " , NULL ) ;
steps = psteps ? ( * psteps ) : 0x10 ;
for ( host - > bsteps = 0 ; ( steps & 0x01 ) = = 0 ; host - > bsteps + + )
steps > > = 1 ;
/* Select interface rate */
2005-11-23 17:57:25 +11:00
host - > speed = KW_I2C_MODE_25KHZ ;
2005-09-26 16:04:21 +10:00
prate = ( u32 * ) get_property ( np , " AAPL,i2c-rate " , NULL ) ;
if ( prate ) switch ( * prate ) {
case 100 :
host - > speed = KW_I2C_MODE_100KHZ ;
break ;
case 50 :
host - > speed = KW_I2C_MODE_50KHZ ;
break ;
case 25 :
host - > speed = KW_I2C_MODE_25KHZ ;
break ;
}
2006-07-03 21:36:01 +10:00
host - > irq = irq_of_parse_and_map ( np , 0 ) ;
if ( host - > irq = = NO_IRQ )
printk ( KERN_WARNING
" low_i2c: Failed to map interrupt for %s \n " ,
np - > full_name ) ;
2005-09-26 16:04:21 +10:00
2005-11-23 17:57:25 +11:00
host - > base = ioremap ( ( * addrp ) , 0x1000 ) ;
2006-01-07 11:35:26 +11:00
if ( host - > base = = NULL ) {
printk ( KERN_ERR " low_i2c: Can't map registers for %s \n " ,
np - > full_name ) ;
kfree ( host ) ;
return NULL ;
}
2006-04-18 14:11:53 +10:00
/* Make sure IRQ is disabled */
2006-01-07 11:35:26 +11:00
kw_write_reg ( reg_ier , 0 ) ;
/* Request chip interrupt */
2006-04-18 14:11:53 +10:00
if ( request_irq ( host - > irq , kw_i2c_irq , 0 , " keywest i2c " , host ) )
2006-01-07 11:35:26 +11:00
host - > irq = NO_IRQ ;
printk ( KERN_INFO " KeyWest i2c @0x%08x irq %d %s \n " ,
* addrp , host - > irq , np - > full_name ) ;
2006-01-07 11:30:44 +11:00
return host ;
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:30:44 +11:00
static void __init kw_i2c_add ( struct pmac_i2c_host_kw * host ,
struct device_node * controller ,
struct device_node * busnode ,
int channel )
{
struct pmac_i2c_bus * bus ;
bus = kzalloc ( sizeof ( struct pmac_i2c_bus ) , GFP_KERNEL ) ;
if ( bus = = NULL )
return ;
bus - > controller = of_node_get ( controller ) ;
bus - > busnode = of_node_get ( busnode ) ;
bus - > type = pmac_i2c_bus_keywest ;
bus - > hostdata = host ;
bus - > channel = channel ;
bus - > mode = pmac_i2c_mode_std ;
bus - > open = kw_i2c_open ;
bus - > close = kw_i2c_close ;
bus - > xfer = kw_i2c_xfer ;
init_MUTEX ( & bus - > sem ) ;
if ( controller = = busnode )
bus - > flags = pmac_i2c_multibus ;
list_add ( & bus - > link , & pmac_i2c_busses ) ;
printk ( KERN_INFO " channel %d bus %s \n " , channel ,
( controller = = busnode ) ? " <multibus> " : busnode - > full_name ) ;
}
static void __init kw_i2c_probe ( void )
{
struct device_node * np , * child , * parent ;
/* Probe keywest-i2c busses */
for ( np = NULL ;
( np = of_find_compatible_node ( np , " i2c " , " keywest-i2c " ) ) ! = NULL ; ) {
struct pmac_i2c_host_kw * host ;
int multibus , chans , i ;
/* Found one, init a host structure */
host = kw_i2c_host_init ( np ) ;
if ( host = = NULL )
continue ;
/* Now check if we have a multibus setup (old style) or if we
* have proper bus nodes . Note that the " new " way ( proper bus
* nodes ) might cause us to not create some busses that are
* kept hidden in the device - tree . In the future , we might
* want to work around that by creating busses without a node
* but not for now
*/
child = of_get_next_child ( np , NULL ) ;
multibus = ! child | | strcmp ( child - > name , " i2c-bus " ) ;
of_node_put ( child ) ;
/* For a multibus setup, we get the bus count based on the
* parent type
*/
if ( multibus ) {
parent = of_get_parent ( np ) ;
if ( parent = = NULL )
continue ;
chans = parent - > name [ 0 ] = = ' u ' ? 2 : 1 ;
for ( i = 0 ; i < chans ; i + + )
kw_i2c_add ( host , np , np , i ) ;
} else {
for ( child = NULL ;
( child = of_get_next_child ( np , child ) ) ! = NULL ; ) {
u32 * reg =
( u32 * ) get_property ( child , " reg " , NULL ) ;
if ( reg = = NULL )
continue ;
kw_i2c_add ( host , np , child , * reg ) ;
}
}
}
}
2005-09-26 16:04:21 +10:00
/*
*
* PMU implementation
*
*/
# ifdef CONFIG_ADB_PMU
2006-01-07 11:30:44 +11:00
/*
* i2c command block to the PMU
*/
struct pmu_i2c_hdr {
u8 bus ;
u8 mode ;
u8 bus2 ;
u8 address ;
u8 sub_addr ;
u8 comb_addr ;
u8 count ;
u8 data [ ] ;
} ;
static void pmu_i2c_complete ( struct adb_request * req )
2005-09-26 16:04:21 +10:00
{
2006-01-07 11:30:44 +11:00
complete ( req - > arg ) ;
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:30:44 +11:00
static int pmu_i2c_xfer ( struct pmac_i2c_bus * bus , u8 addrdir , int subsize ,
u32 subaddr , u8 * data , int len )
2005-09-26 16:04:21 +10:00
{
2006-01-07 11:30:44 +11:00
struct adb_request * req = bus - > hostdata ;
struct pmu_i2c_hdr * hdr = ( struct pmu_i2c_hdr * ) & req - > data [ 1 ] ;
struct completion comp ;
int read = addrdir & 1 ;
int retry ;
int rc = 0 ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
/* For now, limit ourselves to 16 bytes transfers */
if ( len > 16 )
return - EINVAL ;
init_completion ( & comp ) ;
for ( retry = 0 ; retry < 16 ; retry + + ) {
memset ( req , 0 , sizeof ( struct adb_request ) ) ;
hdr - > bus = bus - > channel ;
hdr - > count = len ;
switch ( bus - > mode ) {
case pmac_i2c_mode_std :
if ( subsize ! = 0 )
return - EINVAL ;
hdr - > address = addrdir ;
hdr - > mode = PMU_I2C_MODE_SIMPLE ;
break ;
case pmac_i2c_mode_stdsub :
case pmac_i2c_mode_combined :
if ( subsize ! = 1 )
return - EINVAL ;
hdr - > address = addrdir & 0xfe ;
hdr - > comb_addr = addrdir ;
hdr - > sub_addr = subaddr ;
if ( bus - > mode = = pmac_i2c_mode_stdsub )
hdr - > mode = PMU_I2C_MODE_STDSUB ;
else
hdr - > mode = PMU_I2C_MODE_COMBINED ;
break ;
default :
return - EINVAL ;
}
INIT_COMPLETION ( comp ) ;
req - > data [ 0 ] = PMU_I2C_CMD ;
req - > reply [ 0 ] = 0xff ;
req - > nbytes = sizeof ( struct pmu_i2c_hdr ) + 1 ;
req - > done = pmu_i2c_complete ;
req - > arg = & comp ;
2006-01-07 11:35:26 +11:00
if ( ! read & & len ) {
2006-01-07 11:30:44 +11:00
memcpy ( hdr - > data , data , len ) ;
req - > nbytes + = len ;
}
rc = pmu_queue_request ( req ) ;
if ( rc )
return rc ;
wait_for_completion ( & comp ) ;
if ( req - > reply [ 0 ] = = PMU_I2C_STATUS_OK )
break ;
msleep ( 15 ) ;
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:30:44 +11:00
if ( req - > reply [ 0 ] ! = PMU_I2C_STATUS_OK )
return - EIO ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
for ( retry = 0 ; retry < 16 ; retry + + ) {
memset ( req , 0 , sizeof ( struct adb_request ) ) ;
/* I know that looks like a lot, slow as hell, but darwin
* does it so let ' s be on the safe side for now
*/
msleep ( 15 ) ;
hdr - > bus = PMU_I2C_BUS_STATUS ;
INIT_COMPLETION ( comp ) ;
req - > data [ 0 ] = PMU_I2C_CMD ;
req - > reply [ 0 ] = 0xff ;
req - > nbytes = 2 ;
req - > done = pmu_i2c_complete ;
req - > arg = & comp ;
rc = pmu_queue_request ( req ) ;
if ( rc )
return rc ;
wait_for_completion ( & comp ) ;
if ( req - > reply [ 0 ] = = PMU_I2C_STATUS_OK & & ! read )
return 0 ;
if ( req - > reply [ 0 ] = = PMU_I2C_STATUS_DATAREAD & & read ) {
int rlen = req - > reply_len - 1 ;
if ( rlen ! = len ) {
printk ( KERN_WARNING " low_i2c: PMU returned %d "
" bytes, expected %d ! \n " , rlen , len ) ;
return - EIO ;
}
2006-01-07 11:35:26 +11:00
if ( len )
memcpy ( data , & req - > reply [ 1 ] , len ) ;
2006-01-07 11:30:44 +11:00
return 0 ;
}
}
return - EIO ;
}
static void __init pmu_i2c_probe ( void )
{
struct pmac_i2c_bus * bus ;
struct device_node * busnode ;
int channel , sz ;
if ( ! pmu_present ( ) )
return ;
/* There might or might not be a "pmu-i2c" node, we use that
* or via - pmu itself , whatever we find . I haven ' t seen a machine
* with separate bus nodes , so we assume a multibus setup
*/
busnode = of_find_node_by_name ( NULL , " pmu-i2c " ) ;
if ( busnode = = NULL )
busnode = of_find_node_by_name ( NULL , " via-pmu " ) ;
if ( busnode = = NULL )
return ;
printk ( KERN_INFO " PMU i2c %s \n " , busnode - > full_name ) ;
/*
* We add bus 1 and 2 only for now , bus 0 is " special "
*/
for ( channel = 1 ; channel < = 2 ; channel + + ) {
sz = sizeof ( struct pmac_i2c_bus ) + sizeof ( struct adb_request ) ;
bus = kzalloc ( sz , GFP_KERNEL ) ;
if ( bus = = NULL )
return ;
bus - > controller = busnode ;
bus - > busnode = busnode ;
bus - > type = pmac_i2c_bus_pmu ;
bus - > channel = channel ;
bus - > mode = pmac_i2c_mode_std ;
bus - > hostdata = bus + 1 ;
bus - > xfer = pmu_i2c_xfer ;
init_MUTEX ( & bus - > sem ) ;
bus - > flags = pmac_i2c_multibus ;
list_add ( & bus - > link , & pmac_i2c_busses ) ;
printk ( KERN_INFO " channel %d bus <multibus> \n " , channel ) ;
}
2005-09-26 16:04:21 +10:00
}
# endif /* CONFIG_ADB_PMU */
2006-01-07 11:30:44 +11:00
/*
*
* SMU implementation
*
*/
# ifdef CONFIG_PMAC_SMU
static void smu_i2c_complete ( struct smu_i2c_cmd * cmd , void * misc )
2005-09-26 16:04:21 +10:00
{
2006-01-07 11:30:44 +11:00
complete ( misc ) ;
}
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
static int smu_i2c_xfer ( struct pmac_i2c_bus * bus , u8 addrdir , int subsize ,
u32 subaddr , u8 * data , int len )
{
struct smu_i2c_cmd * cmd = bus - > hostdata ;
struct completion comp ;
int read = addrdir & 1 ;
int rc = 0 ;
2006-01-07 11:35:26 +11:00
if ( ( read & & len > SMU_I2C_READ_MAX ) | |
( ( ! read ) & & len > SMU_I2C_WRITE_MAX ) )
return - EINVAL ;
2006-01-07 11:30:44 +11:00
memset ( cmd , 0 , sizeof ( struct smu_i2c_cmd ) ) ;
cmd - > info . bus = bus - > channel ;
cmd - > info . devaddr = addrdir ;
cmd - > info . datalen = len ;
switch ( bus - > mode ) {
case pmac_i2c_mode_std :
if ( subsize ! = 0 )
return - EINVAL ;
cmd - > info . type = SMU_I2C_TRANSFER_SIMPLE ;
break ;
case pmac_i2c_mode_stdsub :
case pmac_i2c_mode_combined :
if ( subsize > 3 | | subsize < 1 )
return - EINVAL ;
cmd - > info . sublen = subsize ;
/* that's big-endian only but heh ! */
memcpy ( & cmd - > info . subaddr , ( ( char * ) & subaddr ) + ( 4 - subsize ) ,
subsize ) ;
if ( bus - > mode = = pmac_i2c_mode_stdsub )
cmd - > info . type = SMU_I2C_TRANSFER_STDSUB ;
else
cmd - > info . type = SMU_I2C_TRANSFER_COMBINED ;
break ;
default :
return - EINVAL ;
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:35:26 +11:00
if ( ! read & & len )
2006-01-07 11:30:44 +11:00
memcpy ( cmd - > info . data , data , len ) ;
init_completion ( & comp ) ;
cmd - > done = smu_i2c_complete ;
cmd - > misc = & comp ;
rc = smu_queue_i2c ( cmd ) ;
if ( rc < 0 )
return rc ;
wait_for_completion ( & comp ) ;
rc = cmd - > status ;
2006-01-07 11:35:26 +11:00
if ( read & & len )
2006-01-07 11:30:44 +11:00
memcpy ( data , cmd - > info . data , len ) ;
return rc < 0 ? rc : 0 ;
}
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
static void __init smu_i2c_probe ( void )
{
struct device_node * controller , * busnode ;
struct pmac_i2c_bus * bus ;
u32 * reg ;
int sz ;
if ( ! smu_present ( ) )
return ;
2006-01-07 11:35:26 +11:00
controller = of_find_node_by_name ( NULL , " smu-i2c-control " ) ;
2006-01-07 11:30:44 +11:00
if ( controller = = NULL )
controller = of_find_node_by_name ( NULL , " smu " ) ;
if ( controller = = NULL )
return ;
printk ( KERN_INFO " SMU i2c %s \n " , controller - > full_name ) ;
/* Look for childs, note that they might not be of the right
* type as older device trees mix i2c busses and other thigns
* at the same level
*/
for ( busnode = NULL ;
( busnode = of_get_next_child ( controller , busnode ) ) ! = NULL ; ) {
if ( strcmp ( busnode - > type , " i2c " ) & &
strcmp ( busnode - > type , " i2c-bus " ) )
continue ;
reg = ( u32 * ) get_property ( busnode , " reg " , NULL ) ;
if ( reg = = NULL )
continue ;
sz = sizeof ( struct pmac_i2c_bus ) + sizeof ( struct smu_i2c_cmd ) ;
bus = kzalloc ( sz , GFP_KERNEL ) ;
if ( bus = = NULL )
return ;
bus - > controller = controller ;
bus - > busnode = of_node_get ( busnode ) ;
bus - > type = pmac_i2c_bus_smu ;
bus - > channel = * reg ;
bus - > mode = pmac_i2c_mode_std ;
bus - > hostdata = bus + 1 ;
bus - > xfer = smu_i2c_xfer ;
init_MUTEX ( & bus - > sem ) ;
bus - > flags = 0 ;
list_add ( & bus - > link , & pmac_i2c_busses ) ;
printk ( KERN_INFO " channel %x bus %s \n " ,
bus - > channel , busnode - > full_name ) ;
}
}
# endif /* CONFIG_PMAC_SMU */
/*
*
* Core code
*
*/
struct pmac_i2c_bus * pmac_i2c_find_bus ( struct device_node * node )
{
struct device_node * p = of_node_get ( node ) ;
struct device_node * prev = NULL ;
struct pmac_i2c_bus * bus ;
while ( p ) {
list_for_each_entry ( bus , & pmac_i2c_busses , link ) {
if ( p = = bus - > busnode ) {
if ( prev & & bus - > flags & pmac_i2c_multibus ) {
u32 * reg ;
reg = ( u32 * ) get_property ( prev , " reg " ,
NULL ) ;
if ( ! reg )
continue ;
if ( ( ( * reg ) > > 8 ) ! = bus - > channel )
continue ;
}
of_node_put ( p ) ;
of_node_put ( prev ) ;
return bus ;
}
}
of_node_put ( prev ) ;
prev = p ;
p = of_get_parent ( p ) ;
}
return NULL ;
}
EXPORT_SYMBOL_GPL ( pmac_i2c_find_bus ) ;
u8 pmac_i2c_get_dev_addr ( struct device_node * device )
{
u32 * reg = ( u32 * ) get_property ( device , " reg " , NULL ) ;
if ( reg = = NULL )
return 0 ;
return ( * reg ) & 0xff ;
}
EXPORT_SYMBOL_GPL ( pmac_i2c_get_dev_addr ) ;
struct device_node * pmac_i2c_get_controller ( struct pmac_i2c_bus * bus )
{
return bus - > controller ;
}
EXPORT_SYMBOL_GPL ( pmac_i2c_get_controller ) ;
struct device_node * pmac_i2c_get_bus_node ( struct pmac_i2c_bus * bus )
{
return bus - > busnode ;
}
EXPORT_SYMBOL_GPL ( pmac_i2c_get_bus_node ) ;
int pmac_i2c_get_type ( struct pmac_i2c_bus * bus )
{
return bus - > type ;
}
EXPORT_SYMBOL_GPL ( pmac_i2c_get_type ) ;
int pmac_i2c_get_flags ( struct pmac_i2c_bus * bus )
{
return bus - > flags ;
}
EXPORT_SYMBOL_GPL ( pmac_i2c_get_flags ) ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:35:26 +11:00
int pmac_i2c_get_channel ( struct pmac_i2c_bus * bus )
{
return bus - > channel ;
}
EXPORT_SYMBOL_GPL ( pmac_i2c_get_channel ) ;
2006-01-07 11:30:44 +11:00
void pmac_i2c_attach_adapter ( struct pmac_i2c_bus * bus ,
struct i2c_adapter * adapter )
{
WARN_ON ( bus - > adapter ! = NULL ) ;
bus - > adapter = adapter ;
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:30:44 +11:00
EXPORT_SYMBOL_GPL ( pmac_i2c_attach_adapter ) ;
void pmac_i2c_detach_adapter ( struct pmac_i2c_bus * bus ,
struct i2c_adapter * adapter )
{
WARN_ON ( bus - > adapter ! = adapter ) ;
bus - > adapter = NULL ;
}
EXPORT_SYMBOL_GPL ( pmac_i2c_detach_adapter ) ;
struct i2c_adapter * pmac_i2c_get_adapter ( struct pmac_i2c_bus * bus )
{
return bus - > adapter ;
}
EXPORT_SYMBOL_GPL ( pmac_i2c_get_adapter ) ;
2006-01-07 11:35:26 +11:00
struct pmac_i2c_bus * pmac_i2c_adapter_to_bus ( struct i2c_adapter * adapter )
{
struct pmac_i2c_bus * bus ;
list_for_each_entry ( bus , & pmac_i2c_busses , link )
if ( bus - > adapter = = adapter )
return bus ;
return NULL ;
}
EXPORT_SYMBOL_GPL ( pmac_i2c_adapter_to_bus ) ;
2006-02-01 07:25:14 -05:00
int pmac_i2c_match_adapter ( struct device_node * dev , struct i2c_adapter * adapter )
2006-01-07 11:30:44 +11:00
{
struct pmac_i2c_bus * bus = pmac_i2c_find_bus ( dev ) ;
if ( bus = = NULL )
return 0 ;
return ( bus - > adapter = = adapter ) ;
}
EXPORT_SYMBOL_GPL ( pmac_i2c_match_adapter ) ;
2005-09-26 16:04:21 +10:00
int pmac_low_i2c_lock ( struct device_node * np )
{
2006-01-07 11:30:44 +11:00
struct pmac_i2c_bus * bus , * found = NULL ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
list_for_each_entry ( bus , & pmac_i2c_busses , link ) {
if ( np = = bus - > controller ) {
found = bus ;
break ;
}
}
if ( ! found )
2005-09-26 16:04:21 +10:00
return - ENODEV ;
2006-01-07 11:30:44 +11:00
return pmac_i2c_open ( bus , 0 ) ;
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:30:44 +11:00
EXPORT_SYMBOL_GPL ( pmac_low_i2c_lock ) ;
2005-09-26 16:04:21 +10:00
int pmac_low_i2c_unlock ( struct device_node * np )
{
2006-01-07 11:30:44 +11:00
struct pmac_i2c_bus * bus , * found = NULL ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
list_for_each_entry ( bus , & pmac_i2c_busses , link ) {
if ( np = = bus - > controller ) {
found = bus ;
break ;
}
}
if ( ! found )
2005-09-26 16:04:21 +10:00
return - ENODEV ;
2006-01-07 11:30:44 +11:00
pmac_i2c_close ( bus ) ;
2005-09-26 16:04:21 +10:00
return 0 ;
}
2006-01-07 11:30:44 +11:00
EXPORT_SYMBOL_GPL ( pmac_low_i2c_unlock ) ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
int pmac_i2c_open ( struct pmac_i2c_bus * bus , int polled )
2005-09-26 16:04:21 +10:00
{
2006-01-07 11:30:44 +11:00
int rc ;
down ( & bus - > sem ) ;
2006-01-07 11:35:26 +11:00
bus - > polled = polled | | pmac_i2c_force_poll ;
2006-01-07 11:30:44 +11:00
bus - > opened = 1 ;
bus - > mode = pmac_i2c_mode_std ;
if ( bus - > open & & ( rc = bus - > open ( bus ) ) ! = 0 ) {
bus - > opened = 0 ;
up ( & bus - > sem ) ;
return rc ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( pmac_i2c_open ) ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
void pmac_i2c_close ( struct pmac_i2c_bus * bus )
{
WARN_ON ( ! bus - > opened ) ;
if ( bus - > close )
bus - > close ( bus ) ;
bus - > opened = 0 ;
up ( & bus - > sem ) ;
}
EXPORT_SYMBOL_GPL ( pmac_i2c_close ) ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
int pmac_i2c_setmode ( struct pmac_i2c_bus * bus , int mode )
{
WARN_ON ( ! bus - > opened ) ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
/* Report me if you see the error below as there might be a new
* " combined4 " mode that I need to implement for the SMU bus
*/
if ( mode < pmac_i2c_mode_dumb | | mode > pmac_i2c_mode_combined ) {
printk ( KERN_ERR " low_i2c: Invalid mode %d requested on "
" bus %s ! \n " , mode , bus - > busnode - > full_name ) ;
return - EINVAL ;
}
bus - > mode = mode ;
2005-09-26 16:04:21 +10:00
return 0 ;
}
2006-01-07 11:30:44 +11:00
EXPORT_SYMBOL_GPL ( pmac_i2c_setmode ) ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
int pmac_i2c_xfer ( struct pmac_i2c_bus * bus , u8 addrdir , int subsize ,
u32 subaddr , u8 * data , int len )
2005-09-26 16:04:21 +10:00
{
2006-01-07 11:30:44 +11:00
int rc ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
WARN_ON ( ! bus - > opened ) ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
DBG ( " xfer() chan=%d, addrdir=0x%x, mode=%d, subsize=%d, subaddr=0x%x, "
" %d bytes, bus %s \n " , bus - > channel , addrdir , bus - > mode , subsize ,
subaddr , len , bus - > busnode - > full_name ) ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
rc = bus - > xfer ( bus , addrdir , subsize , subaddr , data , len ) ;
# ifdef DEBUG
if ( rc )
DBG ( " xfer error %d \n " , rc ) ;
# endif
return rc ;
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:30:44 +11:00
EXPORT_SYMBOL_GPL ( pmac_i2c_xfer ) ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:41:02 +11:00
/* some quirks for platform function decoding */
enum {
pmac_i2c_quirk_invmask = 0x00000001u ,
2006-05-30 21:26:51 -07:00
pmac_i2c_quirk_skip = 0x00000002u ,
2006-01-07 11:41:02 +11:00
} ;
static void pmac_i2c_devscan ( void ( * callback ) ( struct device_node * dev ,
int quirks ) )
{
struct pmac_i2c_bus * bus ;
struct device_node * np ;
static struct whitelist_ent {
char * name ;
char * compatible ;
int quirks ;
} whitelist [ ] = {
/* XXX Study device-tree's & apple drivers are get the quirks
* right !
*/
2006-05-30 21:26:51 -07:00
/* Workaround: It seems that running the clockspreading
* properties on the eMac will cause lockups during boot .
* The machine seems to work fine without that . So for now ,
* let ' s make sure i2c - hwclock doesn ' t match about " imic "
* clocks and we ' ll figure out if we really need to do
* something special about those later .
*/
{ " i2c-hwclock " , " imic5002 " , pmac_i2c_quirk_skip } ,
{ " i2c-hwclock " , " imic5003 " , pmac_i2c_quirk_skip } ,
2006-01-07 11:41:02 +11:00
{ " i2c-hwclock " , NULL , pmac_i2c_quirk_invmask } ,
{ " i2c-cpu-voltage " , NULL , 0 } ,
{ " temp-monitor " , NULL , 0 } ,
{ " supply-monitor " , NULL , 0 } ,
{ NULL , NULL , 0 } ,
} ;
/* Only some devices need to have platform functions instanciated
* here . For now , we have a table . Others , like 9554 i2c GPIOs used
* on Xserve , if we ever do a driver for them , will use their own
* platform function instance
*/
list_for_each_entry ( bus , & pmac_i2c_busses , link ) {
for ( np = NULL ;
( np = of_get_next_child ( bus - > busnode , np ) ) ! = NULL ; ) {
struct whitelist_ent * p ;
/* If multibus, check if device is on that bus */
if ( bus - > flags & pmac_i2c_multibus )
if ( bus ! = pmac_i2c_find_bus ( np ) )
continue ;
for ( p = whitelist ; p - > name ! = NULL ; p + + ) {
if ( strcmp ( np - > name , p - > name ) )
continue ;
if ( p - > compatible & &
! device_is_compatible ( np , p - > compatible ) )
continue ;
2006-05-30 21:26:51 -07:00
if ( p - > quirks & pmac_i2c_quirk_skip )
break ;
2006-01-07 11:41:02 +11:00
callback ( np , p - > quirks ) ;
break ;
}
}
}
}
# define MAX_I2C_DATA 64
struct pmac_i2c_pf_inst
{
struct pmac_i2c_bus * bus ;
u8 addr ;
u8 buffer [ MAX_I2C_DATA ] ;
u8 scratch [ MAX_I2C_DATA ] ;
int bytes ;
int quirks ;
} ;
static void * pmac_i2c_do_begin ( struct pmf_function * func , struct pmf_args * args )
{
struct pmac_i2c_pf_inst * inst ;
struct pmac_i2c_bus * bus ;
bus = pmac_i2c_find_bus ( func - > node ) ;
if ( bus = = NULL ) {
printk ( KERN_ERR " low_i2c: Can't find bus for %s (pfunc) \n " ,
func - > node - > full_name ) ;
return NULL ;
}
if ( pmac_i2c_open ( bus , 0 ) ) {
printk ( KERN_ERR " low_i2c: Can't open i2c bus for %s (pfunc) \n " ,
func - > node - > full_name ) ;
return NULL ;
}
/* XXX might need GFP_ATOMIC when called during the suspend process,
* but then , there are already lots of issues with suspending when
* near OOM that need to be resolved , the allocator itself should
* probably make GFP_NOIO implicit during suspend
*/
inst = kzalloc ( sizeof ( struct pmac_i2c_pf_inst ) , GFP_KERNEL ) ;
if ( inst = = NULL ) {
pmac_i2c_close ( bus ) ;
return NULL ;
}
inst - > bus = bus ;
inst - > addr = pmac_i2c_get_dev_addr ( func - > node ) ;
inst - > quirks = ( int ) ( long ) func - > driver_data ;
return inst ;
}
static void pmac_i2c_do_end ( struct pmf_function * func , void * instdata )
{
struct pmac_i2c_pf_inst * inst = instdata ;
if ( inst = = NULL )
return ;
pmac_i2c_close ( inst - > bus ) ;
if ( inst )
kfree ( inst ) ;
}
static int pmac_i2c_do_read ( PMF_STD_ARGS , u32 len )
{
struct pmac_i2c_pf_inst * inst = instdata ;
inst - > bytes = len ;
return pmac_i2c_xfer ( inst - > bus , inst - > addr | pmac_i2c_read , 0 , 0 ,
inst - > buffer , len ) ;
}
static int pmac_i2c_do_write ( PMF_STD_ARGS , u32 len , const u8 * data )
{
struct pmac_i2c_pf_inst * inst = instdata ;
return pmac_i2c_xfer ( inst - > bus , inst - > addr | pmac_i2c_write , 0 , 0 ,
( u8 * ) data , len ) ;
}
/* This function is used to do the masking & OR'ing for the "rmw" type
* callbacks . Ze should apply the mask and OR in the values in the
* buffer before writing back . The problem is that it seems that
* various darwin drivers implement the mask / or differently , thus
* we need to check the quirks first
*/
static void pmac_i2c_do_apply_rmw ( struct pmac_i2c_pf_inst * inst ,
u32 len , const u8 * mask , const u8 * val )
{
int i ;
if ( inst - > quirks & pmac_i2c_quirk_invmask ) {
for ( i = 0 ; i < len ; i + + )
inst - > scratch [ i ] = ( inst - > buffer [ i ] & mask [ i ] ) | val [ i ] ;
} else {
for ( i = 0 ; i < len ; i + + )
inst - > scratch [ i ] = ( inst - > buffer [ i ] & ~ mask [ i ] )
| ( val [ i ] & mask [ i ] ) ;
}
}
static int pmac_i2c_do_rmw ( PMF_STD_ARGS , u32 masklen , u32 valuelen ,
u32 totallen , const u8 * maskdata ,
const u8 * valuedata )
{
struct pmac_i2c_pf_inst * inst = instdata ;
if ( masklen > inst - > bytes | | valuelen > inst - > bytes | |
totallen > inst - > bytes | | valuelen > masklen )
return - EINVAL ;
pmac_i2c_do_apply_rmw ( inst , masklen , maskdata , valuedata ) ;
return pmac_i2c_xfer ( inst - > bus , inst - > addr | pmac_i2c_write , 0 , 0 ,
inst - > scratch , totallen ) ;
}
static int pmac_i2c_do_read_sub ( PMF_STD_ARGS , u8 subaddr , u32 len )
{
struct pmac_i2c_pf_inst * inst = instdata ;
inst - > bytes = len ;
return pmac_i2c_xfer ( inst - > bus , inst - > addr | pmac_i2c_read , 1 , subaddr ,
inst - > buffer , len ) ;
}
static int pmac_i2c_do_write_sub ( PMF_STD_ARGS , u8 subaddr , u32 len ,
const u8 * data )
{
struct pmac_i2c_pf_inst * inst = instdata ;
return pmac_i2c_xfer ( inst - > bus , inst - > addr | pmac_i2c_write , 1 ,
subaddr , ( u8 * ) data , len ) ;
}
static int pmac_i2c_do_set_mode ( PMF_STD_ARGS , int mode )
{
struct pmac_i2c_pf_inst * inst = instdata ;
return pmac_i2c_setmode ( inst - > bus , mode ) ;
}
static int pmac_i2c_do_rmw_sub ( PMF_STD_ARGS , u8 subaddr , u32 masklen ,
u32 valuelen , u32 totallen , const u8 * maskdata ,
const u8 * valuedata )
{
struct pmac_i2c_pf_inst * inst = instdata ;
if ( masklen > inst - > bytes | | valuelen > inst - > bytes | |
totallen > inst - > bytes | | valuelen > masklen )
return - EINVAL ;
pmac_i2c_do_apply_rmw ( inst , masklen , maskdata , valuedata ) ;
return pmac_i2c_xfer ( inst - > bus , inst - > addr | pmac_i2c_write , 1 ,
subaddr , inst - > scratch , totallen ) ;
}
static int pmac_i2c_do_mask_and_comp ( PMF_STD_ARGS , u32 len ,
const u8 * maskdata ,
const u8 * valuedata )
{
struct pmac_i2c_pf_inst * inst = instdata ;
int i , match ;
/* Get return value pointer, it's assumed to be a u32 */
if ( ! args | | ! args - > count | | ! args - > u [ 0 ] . p )
return - EINVAL ;
/* Check buffer */
if ( len > inst - > bytes )
return - EINVAL ;
for ( i = 0 , match = 1 ; match & & i < len ; i + + )
if ( ( inst - > buffer [ i ] & maskdata [ i ] ) ! = valuedata [ i ] )
match = 0 ;
* args - > u [ 0 ] . p = match ;
return 0 ;
}
static int pmac_i2c_do_delay ( PMF_STD_ARGS , u32 duration )
{
msleep ( ( duration + 999 ) / 1000 ) ;
return 0 ;
}
static struct pmf_handlers pmac_i2c_pfunc_handlers = {
. begin = pmac_i2c_do_begin ,
. end = pmac_i2c_do_end ,
. read_i2c = pmac_i2c_do_read ,
. write_i2c = pmac_i2c_do_write ,
. rmw_i2c = pmac_i2c_do_rmw ,
. read_i2c_sub = pmac_i2c_do_read_sub ,
. write_i2c_sub = pmac_i2c_do_write_sub ,
. rmw_i2c_sub = pmac_i2c_do_rmw_sub ,
. set_i2c_mode = pmac_i2c_do_set_mode ,
. mask_and_compare = pmac_i2c_do_mask_and_comp ,
. delay = pmac_i2c_do_delay ,
} ;
static void __init pmac_i2c_dev_create ( struct device_node * np , int quirks )
{
DBG ( " dev_create(%s) \n " , np - > full_name ) ;
pmf_register_driver ( np , & pmac_i2c_pfunc_handlers ,
( void * ) ( long ) quirks ) ;
}
static void __init pmac_i2c_dev_init ( struct device_node * np , int quirks )
{
DBG ( " dev_create(%s) \n " , np - > full_name ) ;
pmf_do_functions ( np , NULL , 0 , PMF_FLAGS_ON_INIT , NULL ) ;
}
static void pmac_i2c_dev_suspend ( struct device_node * np , int quirks )
{
DBG ( " dev_suspend(%s) \n " , np - > full_name ) ;
pmf_do_functions ( np , NULL , 0 , PMF_FLAGS_ON_SLEEP , NULL ) ;
}
static void pmac_i2c_dev_resume ( struct device_node * np , int quirks )
{
DBG ( " dev_resume(%s) \n " , np - > full_name ) ;
pmf_do_functions ( np , NULL , 0 , PMF_FLAGS_ON_WAKE , NULL ) ;
}
void pmac_pfunc_i2c_suspend ( void )
{
pmac_i2c_devscan ( pmac_i2c_dev_suspend ) ;
}
void pmac_pfunc_i2c_resume ( void )
{
pmac_i2c_devscan ( pmac_i2c_dev_resume ) ;
}
2006-01-07 11:30:44 +11:00
/*
2006-01-07 11:41:02 +11:00
* Initialize us : probe all i2c busses on the machine , instantiate
* busses and platform functions as needed .
2006-01-07 11:30:44 +11:00
*/
/* This is non-static as it might be called early by smp code */
int __init pmac_i2c_init ( void )
2005-09-26 16:04:21 +10:00
{
2006-01-07 11:30:44 +11:00
static int i2c_inited ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
if ( i2c_inited )
return 0 ;
i2c_inited = 1 ;
2005-09-26 16:04:21 +10:00
2006-03-28 23:15:54 +11:00
if ( ! machine_is ( powermac ) )
return 0 ;
2006-01-07 11:30:44 +11:00
/* Probe keywest-i2c busses */
kw_i2c_probe ( ) ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
# ifdef CONFIG_ADB_PMU
2006-01-07 11:35:26 +11:00
/* Probe PMU i2c busses */
2006-01-07 11:30:44 +11:00
pmu_i2c_probe ( ) ;
# endif
2005-09-26 16:04:21 +10:00
2006-01-07 11:30:44 +11:00
# ifdef CONFIG_PMAC_SMU
2006-01-07 11:35:26 +11:00
/* Probe SMU i2c busses */
2006-01-07 11:30:44 +11:00
smu_i2c_probe ( ) ;
# endif
2006-01-07 11:41:02 +11:00
/* Now add plaform functions for some known devices */
pmac_i2c_devscan ( pmac_i2c_dev_create ) ;
2006-01-07 11:30:44 +11:00
return 0 ;
2005-09-26 16:04:21 +10:00
}
2006-01-07 11:30:44 +11:00
arch_initcall ( pmac_i2c_init ) ;
2005-09-26 16:04:21 +10:00
2006-01-07 11:35:26 +11:00
/* Since pmac_i2c_init can be called too early for the platform device
* registration , we need to do it at a later time . In our case , subsys
* happens to fit well , though I agree it ' s a bit of a hack . . .
*/
static int __init pmac_i2c_create_platform_devices ( void )
{
struct pmac_i2c_bus * bus ;
int i = 0 ;
/* In the case where we are initialized from smp_init(), we must
* not use the timer ( and thus the irq ) . It ' s safe from now on
* though
*/
pmac_i2c_force_poll = 0 ;
/* Create platform devices */
list_for_each_entry ( bus , & pmac_i2c_busses , link ) {
bus - > platform_dev =
platform_device_alloc ( " i2c-powermac " , i + + ) ;
if ( bus - > platform_dev = = NULL )
return - ENOMEM ;
bus - > platform_dev - > dev . platform_data = bus ;
platform_device_add ( bus - > platform_dev ) ;
}
2006-01-07 11:41:02 +11:00
/* Now call platform "init" functions */
pmac_i2c_devscan ( pmac_i2c_dev_init ) ;
2006-01-07 11:35:26 +11:00
return 0 ;
}
subsys_initcall ( pmac_i2c_create_platform_devices ) ;