2005-04-17 02:20:36 +04:00
/*
* i2c - au1550 . c : SMBus ( i2c ) adapter for Alchemy PSC interface
* Copyright ( C ) 2004 Embedded Edge , LLC < dan @ embeddededge . com >
*
* 2.6 port by Matt Porter < mporter @ kernel . crashing . org >
*
* The documentation describes this as an SMBus controller , but it doesn ' t
* understand any of the SMBus protocol in hardware . It ' s really an I2C
* controller that could emulate most of the SMBus in software .
*
* This is just a skeleton adapter to use with the Au1550 PSC
* algorithm . It was developed for the Pb1550 , but will work with
* any Au1550 board that has a similar PSC configuration .
*
* 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/delay.h>
# include <linux/kernel.h>
# include <linux/module.h>
2008-01-27 20:14:52 +03:00
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <linux/i2c.h>
2008-01-27 20:14:52 +03:00
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
2011-08-12 13:39:43 +04:00
# include <asm/mach-au1x00/au1000.h>
2005-04-17 02:20:36 +04:00
# include <asm/mach-au1x00/au1xxx_psc.h>
2011-10-22 15:34:36 +04:00
# define PSC_SEL 0x00
# define PSC_CTRL 0x04
# define PSC_SMBCFG 0x08
# define PSC_SMBMSK 0x0C
# define PSC_SMBPCR 0x10
# define PSC_SMBSTAT 0x14
# define PSC_SMBEVNT 0x18
# define PSC_SMBTXRX 0x1C
# define PSC_SMBTMR 0x20
2008-01-27 20:14:52 +03:00
struct i2c_au1550_data {
2011-10-22 15:34:36 +04:00
void __iomem * psc_base ;
2008-01-27 20:14:52 +03:00
int xfer_timeout ;
struct i2c_adapter adap ;
} ;
2005-04-17 02:20:36 +04:00
2011-10-22 15:34:36 +04:00
static inline void WR ( struct i2c_au1550_data * a , int r , unsigned long v )
2005-04-17 02:20:36 +04:00
{
2011-10-22 15:34:36 +04:00
__raw_writel ( v , a - > psc_base + r ) ;
wmb ( ) ;
}
2005-04-17 02:20:36 +04:00
2011-10-22 15:34:36 +04:00
static inline unsigned long RD ( struct i2c_au1550_data * a , int r )
{
return __raw_readl ( a - > psc_base + r ) ;
}
2005-04-17 02:20:36 +04:00
2011-10-22 15:34:36 +04:00
static int wait_xfer_done ( struct i2c_au1550_data * adap )
{
int i ;
/* Wait for Tx Buffer Empty */
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < adap - > xfer_timeout ; i + + ) {
2011-10-22 15:34:36 +04:00
if ( RD ( adap , PSC_SMBSTAT ) & PSC_SMBSTAT_TE )
2005-04-17 02:20:36 +04:00
return 0 ;
2007-10-14 01:56:33 +04:00
2005-04-17 02:20:36 +04:00
udelay ( 1 ) ;
}
return - ETIMEDOUT ;
}
2011-10-22 15:34:36 +04:00
static int wait_ack ( struct i2c_au1550_data * adap )
2005-04-17 02:20:36 +04:00
{
2011-10-22 15:34:36 +04:00
unsigned long stat ;
2005-04-17 02:20:36 +04:00
if ( wait_xfer_done ( adap ) )
return - ETIMEDOUT ;
2011-10-22 15:34:36 +04:00
stat = RD ( adap , PSC_SMBEVNT ) ;
2005-04-17 02:20:36 +04:00
if ( ( stat & ( PSC_SMBEVNT_DN | PSC_SMBEVNT_AN | PSC_SMBEVNT_AL ) ) ! = 0 )
return - ETIMEDOUT ;
return 0 ;
}
2011-10-22 15:34:36 +04:00
static int wait_master_done ( struct i2c_au1550_data * adap )
2005-04-17 02:20:36 +04:00
{
2011-10-22 15:34:36 +04:00
int i ;
2005-04-17 02:20:36 +04:00
2011-10-22 15:34:36 +04:00
/* Wait for Master Done. */
2011-10-22 15:34:38 +04:00
for ( i = 0 ; i < 2 * adap - > xfer_timeout ; i + + ) {
2011-10-22 15:34:36 +04:00
if ( ( RD ( adap , PSC_SMBEVNT ) & PSC_SMBEVNT_MD ) ! = 0 )
2005-04-17 02:20:36 +04:00
return 0 ;
udelay ( 1 ) ;
}
return - ETIMEDOUT ;
}
static int
2008-01-27 20:14:52 +03:00
do_address ( struct i2c_au1550_data * adap , unsigned int addr , int rd , int q )
2005-04-17 02:20:36 +04:00
{
2011-10-22 15:34:36 +04:00
unsigned long stat ;
2005-04-17 02:20:36 +04:00
2011-10-22 15:34:36 +04:00
/* Reset the FIFOs, clear events. */
stat = RD ( adap , PSC_SMBSTAT ) ;
WR ( adap , PSC_SMBEVNT , PSC_SMBEVNT_ALLCLR ) ;
2006-08-14 01:35:40 +04:00
if ( ! ( stat & PSC_SMBSTAT_TE ) | | ! ( stat & PSC_SMBSTAT_RE ) ) {
2011-10-22 15:34:36 +04:00
WR ( adap , PSC_SMBPCR , PSC_SMBPCR_DC ) ;
while ( ( RD ( adap , PSC_SMBPCR ) & PSC_SMBPCR_DC ) ! = 0 )
cpu_relax ( ) ;
2006-08-14 01:35:40 +04:00
udelay ( 50 ) ;
}
2005-04-17 02:20:36 +04:00
2011-10-22 15:34:36 +04:00
/* Write out the i2c chip address and specify operation */
2005-04-17 02:20:36 +04:00
addr < < = 1 ;
if ( rd )
addr | = 1 ;
2008-01-27 20:14:52 +03:00
/* zero-byte xfers stop immediately */
if ( q )
addr | = PSC_SMBTXRX_STP ;
2011-10-22 15:34:36 +04:00
/* Put byte into fifo, start up master. */
WR ( adap , PSC_SMBTXRX , addr ) ;
WR ( adap , PSC_SMBPCR , PSC_SMBPCR_MS ) ;
2005-04-17 02:20:36 +04:00
if ( wait_ack ( adap ) )
return - EIO ;
2008-01-27 20:14:52 +03:00
return ( q ) ? wait_master_done ( adap ) : 0 ;
2005-04-17 02:20:36 +04:00
}
2011-10-22 15:34:36 +04:00
static int wait_for_rx_byte ( struct i2c_au1550_data * adap , unsigned char * out )
2005-04-17 02:20:36 +04:00
{
2011-10-22 15:34:36 +04:00
int j ;
2005-04-17 02:20:36 +04:00
if ( wait_xfer_done ( adap ) )
return - EIO ;
j = adap - > xfer_timeout * 100 ;
do {
j - - ;
if ( j < = 0 )
return - EIO ;
2011-10-22 15:34:36 +04:00
if ( ( RD ( adap , PSC_SMBSTAT ) & PSC_SMBSTAT_RE ) = = 0 )
2005-04-17 02:20:36 +04:00
j = 0 ;
else
udelay ( 1 ) ;
} while ( j > 0 ) ;
2011-10-22 15:34:36 +04:00
* out = RD ( adap , PSC_SMBTXRX ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2011-10-22 15:34:36 +04:00
static int i2c_read ( struct i2c_au1550_data * adap , unsigned char * buf ,
2005-04-17 02:20:36 +04:00
unsigned int len )
{
2011-10-22 15:34:36 +04:00
int i ;
2005-04-17 02:20:36 +04:00
if ( len = = 0 )
return 0 ;
/* A read is performed by stuffing the transmit fifo with
* zero bytes for timing , waiting for bytes to appear in the
* receive fifo , then reading the bytes .
*/
i = 0 ;
2011-10-22 15:34:36 +04:00
while ( i < ( len - 1 ) ) {
WR ( adap , PSC_SMBTXRX , 0 ) ;
if ( wait_for_rx_byte ( adap , & buf [ i ] ) )
2005-04-17 02:20:36 +04:00
return - EIO ;
i + + ;
}
2011-10-22 15:34:36 +04:00
/* The last byte has to indicate transfer done. */
WR ( adap , PSC_SMBTXRX , PSC_SMBTXRX_STP ) ;
2005-04-17 02:20:36 +04:00
if ( wait_master_done ( adap ) )
return - EIO ;
2011-10-22 15:34:36 +04:00
buf [ i ] = ( unsigned char ) ( RD ( adap , PSC_SMBTXRX ) & 0xff ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2011-10-22 15:34:36 +04:00
static int i2c_write ( struct i2c_au1550_data * adap , unsigned char * buf ,
2005-04-17 02:20:36 +04:00
unsigned int len )
{
2011-10-22 15:34:36 +04:00
int i ;
unsigned long data ;
2005-04-17 02:20:36 +04:00
if ( len = = 0 )
return 0 ;
i = 0 ;
while ( i < ( len - 1 ) ) {
data = buf [ i ] ;
2011-10-22 15:34:36 +04:00
WR ( adap , PSC_SMBTXRX , data ) ;
2005-04-17 02:20:36 +04:00
if ( wait_ack ( adap ) )
return - EIO ;
i + + ;
}
2011-10-22 15:34:36 +04:00
/* The last byte has to indicate transfer done. */
2005-04-17 02:20:36 +04:00
data = buf [ i ] ;
data | = PSC_SMBTXRX_STP ;
2011-10-22 15:34:36 +04:00
WR ( adap , PSC_SMBTXRX , data ) ;
2005-04-17 02:20:36 +04:00
if ( wait_master_done ( adap ) )
return - EIO ;
return 0 ;
}
static int
au1550_xfer ( struct i2c_adapter * i2c_adap , struct i2c_msg * msgs , int num )
{
struct i2c_au1550_data * adap = i2c_adap - > algo_data ;
struct i2c_msg * p ;
int i , err = 0 ;
2011-10-22 15:34:36 +04:00
WR ( adap , PSC_CTRL , PSC_CTRL_ENABLE ) ;
2008-07-15 00:38:34 +04:00
2005-04-17 02:20:36 +04:00
for ( i = 0 ; ! err & & i < num ; i + + ) {
p = & msgs [ i ] ;
2008-01-27 20:14:52 +03:00
err = do_address ( adap , p - > addr , p - > flags & I2C_M_RD ,
( p - > len = = 0 ) ) ;
2005-04-17 02:20:36 +04:00
if ( err | | ! p - > len )
continue ;
if ( p - > flags & I2C_M_RD )
err = i2c_read ( adap , p - > buf , p - > len ) ;
else
err = i2c_write ( adap , p - > buf , p - > len ) ;
}
/* Return the number of messages processed, or the error code.
*/
if ( err = = 0 )
err = num ;
2008-07-15 00:38:34 +04:00
2011-10-22 15:34:36 +04:00
WR ( adap , PSC_CTRL , PSC_CTRL_SUSPEND ) ;
2008-07-15 00:38:34 +04:00
2005-04-17 02:20:36 +04:00
return err ;
}
2011-10-22 15:34:36 +04:00
static u32 au1550_func ( struct i2c_adapter * adap )
2005-04-17 02:20:36 +04:00
{
2006-08-14 01:36:27 +04:00
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL ;
2005-04-17 02:20:36 +04:00
}
2006-09-04 00:39:46 +04:00
static const struct i2c_algorithm au1550_algo = {
2005-04-17 02:20:36 +04:00
. master_xfer = au1550_xfer ,
. functionality = au1550_func ,
} ;
2008-07-15 00:38:34 +04:00
static void i2c_au1550_setup ( struct i2c_au1550_data * priv )
{
2011-10-22 15:34:36 +04:00
unsigned long cfg ;
2008-07-15 00:38:34 +04:00
2011-10-22 15:34:36 +04:00
WR ( priv , PSC_CTRL , PSC_CTRL_DISABLE ) ;
WR ( priv , PSC_SEL , PSC_SEL_PS_SMBUSMODE ) ;
WR ( priv , PSC_SMBCFG , 0 ) ;
WR ( priv , PSC_CTRL , PSC_CTRL_ENABLE ) ;
while ( ( RD ( priv , PSC_SMBSTAT ) & PSC_SMBSTAT_SR ) = = 0 )
cpu_relax ( ) ;
cfg = PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 | PSC_SMBCFG_DD_DISABLE ;
WR ( priv , PSC_SMBCFG , cfg ) ;
2008-07-15 00:38:34 +04:00
/* Divide by 8 to get a 6.25 MHz clock. The later protocol
* timings are based on this clock .
*/
2011-10-22 15:34:36 +04:00
cfg | = PSC_SMBCFG_SET_DIV ( PSC_SMBCFG_DIV8 ) ;
WR ( priv , PSC_SMBCFG , cfg ) ;
WR ( priv , PSC_SMBMSK , PSC_SMBMSK_ALLMASK ) ;
2008-07-15 00:38:34 +04:00
/* Set the protocol timer values. See Table 71 in the
* Au1550 Data Book for standard timing values .
*/
2015-09-08 09:56:23 +03:00
WR ( priv , PSC_SMBTMR , PSC_SMBTMR_SET_TH ( 0 ) | PSC_SMBTMR_SET_PS ( 20 ) | \
PSC_SMBTMR_SET_PU ( 20 ) | PSC_SMBTMR_SET_SH ( 20 ) | \
PSC_SMBTMR_SET_SU ( 20 ) | PSC_SMBTMR_SET_CL ( 20 ) | \
PSC_SMBTMR_SET_CH ( 20 ) ) ;
2008-07-15 00:38:34 +04:00
2011-10-22 15:34:36 +04:00
cfg | = PSC_SMBCFG_DE_ENABLE ;
WR ( priv , PSC_SMBCFG , cfg ) ;
while ( ( RD ( priv , PSC_SMBSTAT ) & PSC_SMBSTAT_SR ) = = 0 )
cpu_relax ( ) ;
2008-07-15 00:38:34 +04:00
2011-10-22 15:34:36 +04:00
WR ( priv , PSC_CTRL , PSC_CTRL_SUSPEND ) ;
2008-07-15 00:38:34 +04:00
}
static void i2c_au1550_disable ( struct i2c_au1550_data * priv )
{
2011-10-22 15:34:36 +04:00
WR ( priv , PSC_SMBCFG , 0 ) ;
WR ( priv , PSC_CTRL , PSC_CTRL_DISABLE ) ;
2008-07-15 00:38:34 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* registering functions to load algorithms at runtime
* Prior to calling us , the 50 MHz clock frequency and routing
* must have been set up for the PSC indicated by the adapter .
*/
2012-11-28 00:59:38 +04:00
static int
2008-01-27 20:14:52 +03:00
i2c_au1550_probe ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2008-01-27 20:14:52 +03:00
struct i2c_au1550_data * priv ;
struct resource * r ;
int ret ;
2015-06-09 10:52:49 +03:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( struct i2c_au1550_data ) ,
GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2015-06-09 10:52:49 +03:00
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
priv - > psc_base = devm_ioremap_resource ( & pdev - > dev , r ) ;
if ( IS_ERR ( priv - > psc_base ) )
return PTR_ERR ( priv - > psc_base ) ;
2008-01-27 20:14:52 +03:00
priv - > xfer_timeout = 200 ;
priv - > adap . nr = pdev - > id ;
priv - > adap . algo = & au1550_algo ;
priv - > adap . algo_data = priv ;
priv - > adap . dev . parent = & pdev - > dev ;
strlcpy ( priv - > adap . name , " Au1xxx PSC I2C " , sizeof ( priv - > adap . name ) ) ;
2005-04-17 02:20:36 +04:00
2011-10-22 15:34:36 +04:00
/* Now, set up the PSC for SMBus PIO mode. */
2008-07-15 00:38:34 +04:00
i2c_au1550_setup ( priv ) ;
2005-04-17 02:20:36 +04:00
2008-01-27 20:14:52 +03:00
ret = i2c_add_numbered_adapter ( & priv - > adap ) ;
2015-06-09 10:52:49 +03:00
if ( ret ) {
i2c_au1550_disable ( priv ) ;
return ret ;
2008-01-27 20:14:52 +03:00
}
2015-06-09 10:52:49 +03:00
platform_set_drvdata ( pdev , priv ) ;
return 0 ;
2008-01-27 20:14:52 +03:00
}
2005-04-17 02:20:36 +04:00
2012-11-28 00:59:38 +04:00
static int i2c_au1550_remove ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2008-01-27 20:14:52 +03:00
struct i2c_au1550_data * priv = platform_get_drvdata ( pdev ) ;
i2c_del_adapter ( & priv - > adap ) ;
2008-07-15 00:38:34 +04:00
i2c_au1550_disable ( priv ) ;
2008-01-27 20:14:52 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-07-15 00:38:34 +04:00
# ifdef CONFIG_PM
2011-10-22 15:34:39 +04:00
static int i2c_au1550_suspend ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2011-10-22 15:34:39 +04:00
struct i2c_au1550_data * priv = dev_get_drvdata ( dev ) ;
2008-01-27 20:14:52 +03:00
2008-07-15 00:38:34 +04:00
i2c_au1550_disable ( priv ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2011-10-22 15:34:39 +04:00
static int i2c_au1550_resume ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2011-10-22 15:34:39 +04:00
struct i2c_au1550_data * priv = dev_get_drvdata ( dev ) ;
2008-01-27 20:14:52 +03:00
2008-07-15 00:38:34 +04:00
i2c_au1550_setup ( priv ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2011-10-22 15:34:39 +04:00
static const struct dev_pm_ops i2c_au1550_pmops = {
. suspend = i2c_au1550_suspend ,
. resume = i2c_au1550_resume ,
} ;
# define AU1XPSC_SMBUS_PMOPS (&i2c_au1550_pmops)
2008-07-15 00:38:34 +04:00
# else
2011-10-22 15:34:39 +04:00
# define AU1XPSC_SMBUS_PMOPS NULL
2008-07-15 00:38:34 +04:00
# endif
2005-04-17 02:20:36 +04:00
2008-01-27 20:14:52 +03:00
static struct platform_driver au1xpsc_smbus_driver = {
. driver = {
. name = " au1xpsc_smbus " ,
2011-10-22 15:34:39 +04:00
. pm = AU1XPSC_SMBUS_PMOPS ,
2008-01-27 20:14:52 +03:00
} ,
. probe = i2c_au1550_probe ,
2012-11-28 00:59:38 +04:00
. remove = i2c_au1550_remove ,
2005-04-17 02:20:36 +04:00
} ;
2012-01-12 23:32:04 +04:00
module_platform_driver ( au1xpsc_smbus_driver ) ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Dan Malek, Embedded Edge, LLC. " ) ;
MODULE_DESCRIPTION ( " SMBus adapter Alchemy pb1550 " ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-04-23 00:16:49 +04:00
MODULE_ALIAS ( " platform:au1xpsc_smbus " ) ;