2010-06-25 13:43:07 +08:00
/*
* linux / drivers / i2c / busses / i2c - nuc900 . c
*
* Copyright ( c ) 2010 Nuvoton technology corporation .
*
* This driver based on S3C2410 I2C driver of Ben Dooks < ben - Y5A6D6n0 / KfQXOPxS62xeg @ public . gmane . org > .
* Written by Wan ZongShun < mcuos . com - Re5JQEeQqe8AvxtiuMwx3w @ public . gmane . 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 ; version 2 of the License .
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/init.h>
# include <linux/time.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/err.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <linux/cpufreq.h>
# include <linux/slab.h>
# include <linux/io.h>
# include <mach/mfp.h>
# include <mach/i2c.h>
/* nuc900 i2c registers offset */
# define CSR 0x00
# define DIVIDER 0x04
# define CMDR 0x08
# define SWR 0x0C
# define RXR 0x10
# define TXR 0x14
/* nuc900 i2c CSR register bits */
# define IRQEN 0x003
# define I2CBUSY 0x400
# define I2CSTART 0x018
# define IRQFLAG 0x004
# define ARBIT_LOST 0x200
# define SLAVE_ACK 0x800
/* nuc900 i2c CMDR register bits */
# define I2C_CMD_START 0x10
# define I2C_CMD_STOP 0x08
# define I2C_CMD_READ 0x04
# define I2C_CMD_WRITE 0x02
# define I2C_CMD_NACK 0x01
/* i2c controller state */
enum nuc900_i2c_state {
STATE_IDLE ,
STATE_START ,
STATE_READ ,
STATE_WRITE ,
STATE_STOP
} ;
/* i2c controller private data */
struct nuc900_i2c {
spinlock_t lock ;
wait_queue_head_t wait ;
struct i2c_msg * msg ;
unsigned int msg_num ;
unsigned int msg_idx ;
unsigned int msg_ptr ;
unsigned int irq ;
enum nuc900_i2c_state state ;
void __iomem * regs ;
struct clk * clk ;
struct device * dev ;
struct resource * ioarea ;
struct i2c_adapter adap ;
} ;
/* nuc900_i2c_master_complete
*
* complete the message and wake up the caller , using the given return code ,
* or zero to mean ok .
*/
static inline void nuc900_i2c_master_complete ( struct nuc900_i2c * i2c , int ret )
{
dev_dbg ( i2c - > dev , " master_complete %d \n " , ret ) ;
i2c - > msg_ptr = 0 ;
i2c - > msg = NULL ;
i2c - > msg_idx + + ;
i2c - > msg_num = 0 ;
if ( ret )
i2c - > msg_idx = ret ;
wake_up ( & i2c - > wait ) ;
}
/* irq enable/disable functions */
static inline void nuc900_i2c_disable_irq ( struct nuc900_i2c * i2c )
{
unsigned long tmp ;
tmp = readl ( i2c - > regs + CSR ) ;
writel ( tmp & ~ IRQEN , i2c - > regs + CSR ) ;
}
static inline void nuc900_i2c_enable_irq ( struct nuc900_i2c * i2c )
{
unsigned long tmp ;
tmp = readl ( i2c - > regs + CSR ) ;
writel ( tmp | IRQEN , i2c - > regs + CSR ) ;
}
/* nuc900_i2c_message_start
*
* put the start of a message onto the bus
*/
static void nuc900_i2c_message_start ( struct nuc900_i2c * i2c ,
struct i2c_msg * msg )
{
unsigned int addr = ( msg - > addr & 0x7f ) < < 1 ;
if ( msg - > flags & I2C_M_RD )
addr | = 0x1 ;
writel ( addr & 0xff , i2c - > regs + TXR ) ;
writel ( I2C_CMD_START | I2C_CMD_WRITE , i2c - > regs + CMDR ) ;
}
static inline void nuc900_i2c_stop ( struct nuc900_i2c * i2c , int ret )
{
dev_dbg ( i2c - > dev , " STOP \n " ) ;
/* stop the transfer */
i2c - > state = STATE_STOP ;
writel ( I2C_CMD_STOP , i2c - > regs + CMDR ) ;
nuc900_i2c_master_complete ( i2c , ret ) ;
nuc900_i2c_disable_irq ( i2c ) ;
}
/* helper functions to determine the current state in the set of
* messages we are sending
*/
/* is_lastmsg()
*
* returns TRUE if the current message is the last in the set
*/
static inline int is_lastmsg ( struct nuc900_i2c * i2c )
{
return i2c - > msg_idx > = ( i2c - > msg_num - 1 ) ;
}
/* is_msglast
*
* returns TRUE if we this is the last byte in the current message
*/
static inline int is_msglast ( struct nuc900_i2c * i2c )
{
return i2c - > msg_ptr = = i2c - > msg - > len - 1 ;
}
/* is_msgend
*
* returns TRUE if we reached the end of the current message
*/
static inline int is_msgend ( struct nuc900_i2c * i2c )
{
return i2c - > msg_ptr > = i2c - > msg - > len ;
}
/* i2c_nuc900_irq_nextbyte
*
* process an interrupt and work out what to do
*/
static void i2c_nuc900_irq_nextbyte ( struct nuc900_i2c * i2c ,
unsigned long iicstat )
{
unsigned char byte ;
switch ( i2c - > state ) {
case STATE_IDLE :
dev_err ( i2c - > dev , " %s: called in STATE_IDLE \n " , __func__ ) ;
break ;
case STATE_STOP :
dev_err ( i2c - > dev , " %s: called in STATE_STOP \n " , __func__ ) ;
nuc900_i2c_disable_irq ( i2c ) ;
break ;
case STATE_START :
/* last thing we did was send a start condition on the
* bus , or started a new i2c message
*/
if ( iicstat & SLAVE_ACK & &
! ( i2c - > msg - > flags & I2C_M_IGNORE_NAK ) ) {
/* ack was not received... */
dev_dbg ( i2c - > dev , " ack was not received \n " ) ;
nuc900_i2c_stop ( i2c , - ENXIO ) ;
break ;
}
if ( i2c - > msg - > flags & I2C_M_RD )
i2c - > state = STATE_READ ;
else
i2c - > state = STATE_WRITE ;
/* terminate the transfer if there is nothing to do
* as this is used by the i2c probe to find devices .
*/
if ( is_lastmsg ( i2c ) & & i2c - > msg - > len = = 0 ) {
nuc900_i2c_stop ( i2c , 0 ) ;
break ;
}
if ( i2c - > state = = STATE_READ )
goto prepare_read ;
/* fall through to the write state, as we will need to
* send a byte as well
*/
case STATE_WRITE :
/* we are writing data to the device... check for the
* end of the message , and if so , work out what to do
*/
if ( ! ( i2c - > msg - > flags & I2C_M_IGNORE_NAK ) ) {
if ( iicstat & SLAVE_ACK ) {
dev_dbg ( i2c - > dev , " WRITE: No Ack \n " ) ;
nuc900_i2c_stop ( i2c , - ECONNREFUSED ) ;
break ;
}
}
retry_write :
if ( ! is_msgend ( i2c ) ) {
byte = i2c - > msg - > buf [ i2c - > msg_ptr + + ] ;
writeb ( byte , i2c - > regs + TXR ) ;
writel ( I2C_CMD_WRITE , i2c - > regs + CMDR ) ;
} else if ( ! is_lastmsg ( i2c ) ) {
/* we need to go to the next i2c message */
dev_dbg ( i2c - > dev , " WRITE: Next Message \n " ) ;
i2c - > msg_ptr = 0 ;
i2c - > msg_idx + + ;
i2c - > msg + + ;
/* check to see if we need to do another message */
if ( i2c - > msg - > flags & I2C_M_NOSTART ) {
if ( i2c - > msg - > flags & I2C_M_RD ) {
/* cannot do this, the controller
* forces us to send a new START
* when we change direction
*/
nuc900_i2c_stop ( i2c , - EINVAL ) ;
}
goto retry_write ;
} else {
/* send the new start */
nuc900_i2c_message_start ( i2c , i2c - > msg ) ;
i2c - > state = STATE_START ;
}
} else {
/* send stop */
nuc900_i2c_stop ( i2c , 0 ) ;
}
break ;
case STATE_READ :
/* we have a byte of data in the data register, do
* something with it , and then work out wether we are
* going to do any more read / write
*/
byte = readb ( i2c - > regs + RXR ) ;
i2c - > msg - > buf [ i2c - > msg_ptr + + ] = byte ;
prepare_read :
if ( is_msglast ( i2c ) ) {
/* last byte of buffer */
if ( is_lastmsg ( i2c ) )
writel ( I2C_CMD_READ | I2C_CMD_NACK ,
i2c - > regs + CMDR ) ;
} else if ( is_msgend ( i2c ) ) {
/* ok, we've read the entire buffer, see if there
* is anything else we need to do
*/
if ( is_lastmsg ( i2c ) ) {
/* last message, send stop and complete */
dev_dbg ( i2c - > dev , " READ: Send Stop \n " ) ;
nuc900_i2c_stop ( i2c , 0 ) ;
} else {
/* go to the next transfer */
dev_dbg ( i2c - > dev , " READ: Next Transfer \n " ) ;
i2c - > msg_ptr = 0 ;
i2c - > msg_idx + + ;
i2c - > msg + + ;
writel ( I2C_CMD_READ , i2c - > regs + CMDR ) ;
}
} else {
writel ( I2C_CMD_READ , i2c - > regs + CMDR ) ;
}
break ;
}
}
/* nuc900_i2c_irq
*
* top level IRQ servicing routine
*/
static irqreturn_t nuc900_i2c_irq ( int irqno , void * dev_id )
{
struct nuc900_i2c * i2c = dev_id ;
unsigned long status ;
status = readl ( i2c - > regs + CSR ) ;
writel ( status | IRQFLAG , i2c - > regs + CSR ) ;
if ( status & ARBIT_LOST ) {
/* deal with arbitration loss */
dev_err ( i2c - > dev , " deal with arbitration loss \n " ) ;
goto out ;
}
if ( i2c - > state = = STATE_IDLE ) {
dev_dbg ( i2c - > dev , " IRQ: error i2c->state == IDLE \n " ) ;
goto out ;
}
/* pretty much this leaves us with the fact that we've
* transmitted or received whatever byte we last sent
*/
i2c_nuc900_irq_nextbyte ( i2c , status ) ;
out :
return IRQ_HANDLED ;
}
/* nuc900_i2c_set_master
*
* get the i2c bus for a master transaction
*/
static int nuc900_i2c_set_master ( struct nuc900_i2c * i2c )
{
int timeout = 400 ;
while ( timeout - - > 0 ) {
if ( ( ( readl ( i2c - > regs + SWR ) & I2CSTART ) = = I2CSTART ) & &
( ( readl ( i2c - > regs + CSR ) & I2CBUSY ) = = 0 ) ) {
return 0 ;
}
msleep ( 1 ) ;
}
return - ETIMEDOUT ;
}
/* nuc900_i2c_doxfer
*
* this starts an i2c transfer
*/
static int nuc900_i2c_doxfer ( struct nuc900_i2c * i2c ,
struct i2c_msg * msgs , int num )
{
unsigned long iicstat , timeout ;
int spins = 20 ;
int ret ;
ret = nuc900_i2c_set_master ( i2c ) ;
if ( ret ! = 0 ) {
dev_err ( i2c - > dev , " cannot get bus (error %d) \n " , ret ) ;
ret = - EAGAIN ;
goto out ;
}
spin_lock_irq ( & i2c - > lock ) ;
i2c - > msg = msgs ;
i2c - > msg_num = num ;
i2c - > msg_ptr = 0 ;
i2c - > msg_idx = 0 ;
i2c - > state = STATE_START ;
nuc900_i2c_message_start ( i2c , msgs ) ;
spin_unlock_irq ( & i2c - > lock ) ;
timeout = wait_event_timeout ( i2c - > wait , i2c - > msg_num = = 0 , HZ * 5 ) ;
ret = i2c - > msg_idx ;
/* having these next two as dev_err() makes life very
* noisy when doing an i2cdetect
*/
if ( timeout = = 0 )
dev_dbg ( i2c - > dev , " timeout \n " ) ;
else if ( ret ! = num )
dev_dbg ( i2c - > dev , " incomplete xfer (%d) \n " , ret ) ;
/* ensure the stop has been through the bus */
dev_dbg ( i2c - > dev , " waiting for bus idle \n " ) ;
/* first, try busy waiting briefly */
do {
iicstat = readl ( i2c - > regs + CSR ) ;
} while ( ( iicstat & I2CBUSY ) & & - - spins ) ;
/* if that timed out sleep */
if ( ! spins ) {
msleep ( 1 ) ;
iicstat = readl ( i2c - > regs + CSR ) ;
}
if ( iicstat & I2CBUSY )
dev_warn ( i2c - > dev , " timeout waiting for bus idle \n " ) ;
out :
return ret ;
}
/* nuc900_i2c_xfer
*
* first port of call from the i2c bus code when an message needs
* transferring across the i2c bus .
*/
static int nuc900_i2c_xfer ( struct i2c_adapter * adap ,
struct i2c_msg * msgs , int num )
{
struct nuc900_i2c * i2c = ( struct nuc900_i2c * ) adap - > algo_data ;
int retry ;
int ret ;
nuc900_i2c_enable_irq ( i2c ) ;
for ( retry = 0 ; retry < adap - > retries ; retry + + ) {
ret = nuc900_i2c_doxfer ( i2c , msgs , num ) ;
if ( ret ! = - EAGAIN )
return ret ;
dev_dbg ( i2c - > dev , " Retrying transmission (%d) \n " , retry ) ;
udelay ( 100 ) ;
}
return - EREMOTEIO ;
}
/* declare our i2c functionality */
static u32 nuc900_i2c_func ( struct i2c_adapter * adap )
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING ;
}
/* i2c bus registration info */
static const struct i2c_algorithm nuc900_i2c_algorithm = {
. master_xfer = nuc900_i2c_xfer ,
. functionality = nuc900_i2c_func ,
} ;
/* nuc900_i2c_probe
*
* called by the bus driver when a suitable device is found
*/
static int __devinit nuc900_i2c_probe ( struct platform_device * pdev )
{
struct nuc900_i2c * i2c ;
struct nuc900_platform_i2c * pdata ;
struct resource * res ;
int ret ;
pdata = pdev - > dev . platform_data ;
if ( ! pdata ) {
dev_err ( & pdev - > dev , " no platform data \n " ) ;
return - EINVAL ;
}
i2c = kzalloc ( sizeof ( struct nuc900_i2c ) , GFP_KERNEL ) ;
if ( ! i2c ) {
dev_err ( & pdev - > dev , " no memory for state \n " ) ;
return - ENOMEM ;
}
strlcpy ( i2c - > adap . name , " nuc900-i2c0 " , sizeof ( i2c - > adap . name ) ) ;
i2c - > adap . owner = THIS_MODULE ;
i2c - > adap . algo = & nuc900_i2c_algorithm ;
i2c - > adap . retries = 2 ;
i2c - > adap . class = I2C_CLASS_HWMON | I2C_CLASS_SPD ;
spin_lock_init ( & i2c - > lock ) ;
init_waitqueue_head ( & i2c - > wait ) ;
/* find the clock and enable it */
i2c - > dev = & pdev - > dev ;
i2c - > clk = clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( i2c - > clk ) ) {
dev_err ( & pdev - > dev , " cannot get clock \n " ) ;
ret = - ENOENT ;
goto err_noclk ;
}
dev_dbg ( & pdev - > dev , " clock source %p \n " , i2c - > clk ) ;
clk_enable ( i2c - > clk ) ;
/* map the registers */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( res = = NULL ) {
dev_err ( & pdev - > dev , " cannot find IO resource \n " ) ;
ret = - ENOENT ;
goto err_clk ;
}
i2c - > ioarea = request_mem_region ( res - > start , resource_size ( res ) ,
pdev - > name ) ;
if ( i2c - > ioarea = = NULL ) {
dev_err ( & pdev - > dev , " cannot request IO \n " ) ;
ret = - ENXIO ;
goto err_clk ;
}
i2c - > regs = ioremap ( res - > start , resource_size ( res ) ) ;
if ( i2c - > regs = = NULL ) {
dev_err ( & pdev - > dev , " cannot map IO \n " ) ;
ret = - ENXIO ;
goto err_ioarea ;
}
dev_dbg ( & pdev - > dev , " registers %p (%p, %p) \n " ,
i2c - > regs , i2c - > ioarea , res ) ;
/* setup info block for the i2c core */
i2c - > adap . algo_data = i2c ;
i2c - > adap . dev . parent = & pdev - > dev ;
2011-11-25 00:23:28 +01:00
mfp_set_groupg ( & pdev - > dev , NULL ) ;
2010-06-25 13:43:07 +08:00
clk_get_rate ( i2c - > clk ) ;
ret = ( i2c - > clk . apbfreq ) / ( pdata - > bus_freq * 5 ) - 1 ;
writel ( ret & 0xffff , i2c - > regs + DIVIDER ) ;
/* find the IRQ for this unit (note, this relies on the init call to
* ensure no current IRQs pending
*/
i2c - > irq = ret = platform_get_irq ( pdev , 0 ) ;
if ( ret < = 0 ) {
dev_err ( & pdev - > dev , " cannot find IRQ \n " ) ;
goto err_iomap ;
}
2011-09-21 17:28:33 +08:00
ret = request_irq ( i2c - > irq , nuc900_i2c_irq , IRQF_SHARED ,
2010-06-25 13:43:07 +08:00
dev_name ( & pdev - > dev ) , i2c ) ;
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " cannot claim IRQ %d \n " , i2c - > irq ) ;
goto err_iomap ;
}
/* Note, previous versions of the driver used i2c_add_adapter()
* to add the bus at any number . We now pass the bus number via
* the platform data , so if unset it will now default to always
* being bus 0.
*/
i2c - > adap . nr = pdata - > bus_num ;
ret = i2c_add_numbered_adapter ( & i2c - > adap ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to add bus to i2c core \n " ) ;
goto err_irq ;
}
platform_set_drvdata ( pdev , i2c ) ;
dev_info ( & pdev - > dev , " %s: NUC900 I2C adapter \n " ,
dev_name ( & i2c - > adap . dev ) ) ;
return 0 ;
err_irq :
free_irq ( i2c - > irq , i2c ) ;
err_iomap :
iounmap ( i2c - > regs ) ;
err_ioarea :
release_resource ( i2c - > ioarea ) ;
kfree ( i2c - > ioarea ) ;
err_clk :
clk_disable ( i2c - > clk ) ;
clk_put ( i2c - > clk ) ;
err_noclk :
kfree ( i2c ) ;
return ret ;
}
/* nuc900_i2c_remove
*
* called when device is removed from the bus
*/
static int __devexit nuc900_i2c_remove ( struct platform_device * pdev )
{
struct nuc900_i2c * i2c = platform_get_drvdata ( pdev ) ;
i2c_del_adapter ( & i2c - > adap ) ;
free_irq ( i2c - > irq , i2c ) ;
clk_disable ( i2c - > clk ) ;
clk_put ( i2c - > clk ) ;
iounmap ( i2c - > regs ) ;
release_resource ( i2c - > ioarea ) ;
kfree ( i2c - > ioarea ) ;
kfree ( i2c ) ;
return 0 ;
}
static struct platform_driver nuc900_i2c_driver = {
. probe = nuc900_i2c_probe ,
. remove = __devexit_p ( nuc900_i2c_remove ) ,
. driver = {
. owner = THIS_MODULE ,
. name = " nuc900-i2c0 " ,
} ,
} ;
static int __init i2c_adap_nuc900_init ( void )
{
return platform_driver_register ( & nuc900_i2c_driver ) ;
}
static void __exit i2c_adap_nuc900_exit ( void )
{
platform_driver_unregister ( & nuc900_i2c_driver ) ;
}
subsys_initcall ( i2c_adap_nuc900_init ) ;
module_exit ( i2c_adap_nuc900_exit ) ;
MODULE_DESCRIPTION ( " NUC900 I2C Bus driver " ) ;
MODULE_AUTHOR ( " Wan ZongShun, <mcuos.com-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:nuc900-i2c0 " ) ;