Merge branches 'for-32/i2c/omap-v4', 'for-32/i2c/imx-dt', 'for-32/i2c/eg20t-v4', 'for-32/i2c/designware-v5' and 'for-32/i2c/au1550' into for-linus/i2c-3.2
This commit is contained in:
commit
ce0fda1608
|
@ -0,0 +1,25 @@
|
|||
* Freescale Inter IC (I2C) and High Speed Inter IC (HS-I2C) for i.MX
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "fsl,<chip>-i2c"
|
||||
- reg : Should contain I2C/HS-I2C registers location and length
|
||||
- interrupts : Should contain I2C/HS-I2C interrupt
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz.
|
||||
The absence of the propoerty indicates the default frequency 100 kHz.
|
||||
|
||||
Examples:
|
||||
|
||||
i2c@83fc4000 { /* I2C2 on i.MX51 */
|
||||
compatible = "fsl,imx51-i2c", "fsl,imx1-i2c";
|
||||
reg = <0x83fc4000 0x4000>;
|
||||
interrupts = <63>;
|
||||
};
|
||||
|
||||
i2c@70038000 { /* HS-I2C on i.MX51 */
|
||||
compatible = "fsl,imx51-i2c", "fsl,imx1-i2c";
|
||||
reg = <0x70038000 0x4000>;
|
||||
interrupts = <64>;
|
||||
clock-frequency = <400000>;
|
||||
};
|
|
@ -11,14 +11,10 @@
|
|||
|
||||
/**
|
||||
* struct imxi2c_platform_data - structure of platform data for MXC I2C driver
|
||||
* @init: Initialise gpio's and other board specific things
|
||||
* @exit: Free everything initialised by @init
|
||||
* @bitrate: Bus speed measured in Hz
|
||||
*
|
||||
**/
|
||||
struct imxi2c_platform_data {
|
||||
int (*init)(struct device *dev);
|
||||
void (*exit)(struct device *dev);
|
||||
int bitrate;
|
||||
};
|
||||
|
||||
|
|
|
@ -394,19 +394,6 @@ typedef struct psc_spi {
|
|||
#define PSC_SPITXRX_LC (1 << 29)
|
||||
#define PSC_SPITXRX_SR (1 << 28)
|
||||
|
||||
/* PSC in SMBus (I2C) Mode. */
|
||||
typedef struct psc_smb {
|
||||
u32 psc_sel;
|
||||
u32 psc_ctrl;
|
||||
u32 psc_smbcfg;
|
||||
u32 psc_smbmsk;
|
||||
u32 psc_smbpcr;
|
||||
u32 psc_smbstat;
|
||||
u32 psc_smbevnt;
|
||||
u32 psc_smbtxrx;
|
||||
u32 psc_smbtmr;
|
||||
} psc_smb_t;
|
||||
|
||||
/* SMBus Config Register. */
|
||||
#define PSC_SMBCFG_RT_MASK (3 << 30)
|
||||
#define PSC_SMBCFG_RT_FIFO1 (0 << 30)
|
||||
|
|
|
@ -350,15 +350,25 @@ config I2C_DAVINCI
|
|||
devices such as DaVinci NIC.
|
||||
For details please see http://www.ti.com/davinci
|
||||
|
||||
config I2C_DESIGNWARE
|
||||
tristate "Synopsys DesignWare"
|
||||
config I2C_DESIGNWARE_PLATFORM
|
||||
tristate "Synopsys DesignWare Platfrom"
|
||||
depends on HAVE_CLK
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
Synopsys DesignWare I2C adapter. Only master mode is supported.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-designware.
|
||||
will be called i2c-designware-platform.
|
||||
|
||||
config I2C_DESIGNWARE_PCI
|
||||
tristate "Synopsys DesignWare PCI"
|
||||
depends on PCI
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
Synopsys DesignWare I2C adapter. Only master mode is supported.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-designware-pci.
|
||||
|
||||
config I2C_GPIO
|
||||
tristate "GPIO-based bitbanging I2C"
|
||||
|
|
|
@ -33,7 +33,10 @@ obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
|
|||
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
|
||||
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
|
||||
obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
|
||||
obj-$(CONFIG_I2C_DESIGNWARE) += i2c-designware.o
|
||||
obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
|
||||
i2c-designware-platform-objs := i2c-designware-platdrv.o i2c-designware-core.o
|
||||
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
|
||||
i2c-designware-pci-objs := i2c-designware-pcidrv.o i2c-designware-core.o
|
||||
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
|
||||
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
|
||||
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
|
||||
|
|
|
@ -39,29 +39,41 @@
|
|||
#include <asm/mach-au1x00/au1xxx.h>
|
||||
#include <asm/mach-au1x00/au1xxx_psc.h>
|
||||
|
||||
#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
|
||||
|
||||
struct i2c_au1550_data {
|
||||
u32 psc_base;
|
||||
void __iomem *psc_base;
|
||||
int xfer_timeout;
|
||||
int ack_timeout;
|
||||
struct i2c_adapter adap;
|
||||
struct resource *ioarea;
|
||||
};
|
||||
|
||||
static int
|
||||
wait_xfer_done(struct i2c_au1550_data *adap)
|
||||
static inline void WR(struct i2c_au1550_data *a, int r, unsigned long v)
|
||||
{
|
||||
__raw_writel(v, a->psc_base + r);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static inline unsigned long RD(struct i2c_au1550_data *a, int r)
|
||||
{
|
||||
return __raw_readl(a->psc_base + r);
|
||||
}
|
||||
|
||||
static int wait_xfer_done(struct i2c_au1550_data *adap)
|
||||
{
|
||||
u32 stat;
|
||||
int i;
|
||||
volatile psc_smb_t *sp;
|
||||
|
||||
sp = (volatile psc_smb_t *)(adap->psc_base);
|
||||
|
||||
/* Wait for Tx Buffer Empty
|
||||
*/
|
||||
/* Wait for Tx Buffer Empty */
|
||||
for (i = 0; i < adap->xfer_timeout; i++) {
|
||||
stat = sp->psc_smbstat;
|
||||
au_sync();
|
||||
if ((stat & PSC_SMBSTAT_TE) != 0)
|
||||
if (RD(adap, PSC_SMBSTAT) & PSC_SMBSTAT_TE)
|
||||
return 0;
|
||||
|
||||
udelay(1);
|
||||
|
@ -70,41 +82,27 @@ wait_xfer_done(struct i2c_au1550_data *adap)
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int
|
||||
wait_ack(struct i2c_au1550_data *adap)
|
||||
static int wait_ack(struct i2c_au1550_data *adap)
|
||||
{
|
||||
u32 stat;
|
||||
volatile psc_smb_t *sp;
|
||||
unsigned long stat;
|
||||
|
||||
if (wait_xfer_done(adap))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
sp = (volatile psc_smb_t *)(adap->psc_base);
|
||||
|
||||
stat = sp->psc_smbevnt;
|
||||
au_sync();
|
||||
|
||||
stat = RD(adap, PSC_SMBEVNT);
|
||||
if ((stat & (PSC_SMBEVNT_DN | PSC_SMBEVNT_AN | PSC_SMBEVNT_AL)) != 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
wait_master_done(struct i2c_au1550_data *adap)
|
||||
static int wait_master_done(struct i2c_au1550_data *adap)
|
||||
{
|
||||
u32 stat;
|
||||
int i;
|
||||
volatile psc_smb_t *sp;
|
||||
|
||||
sp = (volatile psc_smb_t *)(adap->psc_base);
|
||||
|
||||
/* Wait for Master Done.
|
||||
*/
|
||||
for (i = 0; i < adap->xfer_timeout; i++) {
|
||||
stat = sp->psc_smbevnt;
|
||||
au_sync();
|
||||
if ((stat & PSC_SMBEVNT_MD) != 0)
|
||||
/* Wait for Master Done. */
|
||||
for (i = 0; i < 2 * adap->xfer_timeout; i++) {
|
||||
if ((RD(adap, PSC_SMBEVNT) & PSC_SMBEVNT_MD) != 0)
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
|
@ -115,29 +113,20 @@ wait_master_done(struct i2c_au1550_data *adap)
|
|||
static int
|
||||
do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd, int q)
|
||||
{
|
||||
volatile psc_smb_t *sp;
|
||||
u32 stat;
|
||||
unsigned long stat;
|
||||
|
||||
sp = (volatile psc_smb_t *)(adap->psc_base);
|
||||
|
||||
/* Reset the FIFOs, clear events.
|
||||
*/
|
||||
stat = sp->psc_smbstat;
|
||||
sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR;
|
||||
au_sync();
|
||||
/* Reset the FIFOs, clear events. */
|
||||
stat = RD(adap, PSC_SMBSTAT);
|
||||
WR(adap, PSC_SMBEVNT, PSC_SMBEVNT_ALLCLR);
|
||||
|
||||
if (!(stat & PSC_SMBSTAT_TE) || !(stat & PSC_SMBSTAT_RE)) {
|
||||
sp->psc_smbpcr = PSC_SMBPCR_DC;
|
||||
au_sync();
|
||||
do {
|
||||
stat = sp->psc_smbpcr;
|
||||
au_sync();
|
||||
} while ((stat & PSC_SMBPCR_DC) != 0);
|
||||
WR(adap, PSC_SMBPCR, PSC_SMBPCR_DC);
|
||||
while ((RD(adap, PSC_SMBPCR) & PSC_SMBPCR_DC) != 0)
|
||||
cpu_relax();
|
||||
udelay(50);
|
||||
}
|
||||
|
||||
/* Write out the i2c chip address and specify operation
|
||||
*/
|
||||
/* Write out the i2c chip address and specify operation */
|
||||
addr <<= 1;
|
||||
if (rd)
|
||||
addr |= 1;
|
||||
|
@ -146,56 +135,42 @@ do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd, int q)
|
|||
if (q)
|
||||
addr |= PSC_SMBTXRX_STP;
|
||||
|
||||
/* Put byte into fifo, start up master.
|
||||
*/
|
||||
sp->psc_smbtxrx = addr;
|
||||
au_sync();
|
||||
sp->psc_smbpcr = PSC_SMBPCR_MS;
|
||||
au_sync();
|
||||
/* Put byte into fifo, start up master. */
|
||||
WR(adap, PSC_SMBTXRX, addr);
|
||||
WR(adap, PSC_SMBPCR, PSC_SMBPCR_MS);
|
||||
if (wait_ack(adap))
|
||||
return -EIO;
|
||||
return (q) ? wait_master_done(adap) : 0;
|
||||
}
|
||||
|
||||
static u32
|
||||
wait_for_rx_byte(struct i2c_au1550_data *adap, u32 *ret_data)
|
||||
static int wait_for_rx_byte(struct i2c_au1550_data *adap, unsigned char *out)
|
||||
{
|
||||
int j;
|
||||
u32 data, stat;
|
||||
volatile psc_smb_t *sp;
|
||||
|
||||
if (wait_xfer_done(adap))
|
||||
return -EIO;
|
||||
|
||||
sp = (volatile psc_smb_t *)(adap->psc_base);
|
||||
|
||||
j = adap->xfer_timeout * 100;
|
||||
do {
|
||||
j--;
|
||||
if (j <= 0)
|
||||
return -EIO;
|
||||
|
||||
stat = sp->psc_smbstat;
|
||||
au_sync();
|
||||
if ((stat & PSC_SMBSTAT_RE) == 0)
|
||||
if ((RD(adap, PSC_SMBSTAT) & PSC_SMBSTAT_RE) == 0)
|
||||
j = 0;
|
||||
else
|
||||
udelay(1);
|
||||
} while (j > 0);
|
||||
data = sp->psc_smbtxrx;
|
||||
au_sync();
|
||||
*ret_data = data;
|
||||
|
||||
*out = RD(adap, PSC_SMBTXRX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
|
||||
static int i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
|
||||
unsigned int len)
|
||||
{
|
||||
int i;
|
||||
u32 data;
|
||||
volatile psc_smb_t *sp;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
@ -204,62 +179,46 @@ i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
|
|||
* zero bytes for timing, waiting for bytes to appear in the
|
||||
* receive fifo, then reading the bytes.
|
||||
*/
|
||||
|
||||
sp = (volatile psc_smb_t *)(adap->psc_base);
|
||||
|
||||
i = 0;
|
||||
while (i < (len-1)) {
|
||||
sp->psc_smbtxrx = 0;
|
||||
au_sync();
|
||||
if (wait_for_rx_byte(adap, &data))
|
||||
while (i < (len - 1)) {
|
||||
WR(adap, PSC_SMBTXRX, 0);
|
||||
if (wait_for_rx_byte(adap, &buf[i]))
|
||||
return -EIO;
|
||||
|
||||
buf[i] = data;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* The last byte has to indicate transfer done.
|
||||
*/
|
||||
sp->psc_smbtxrx = PSC_SMBTXRX_STP;
|
||||
au_sync();
|
||||
/* The last byte has to indicate transfer done. */
|
||||
WR(adap, PSC_SMBTXRX, PSC_SMBTXRX_STP);
|
||||
if (wait_master_done(adap))
|
||||
return -EIO;
|
||||
|
||||
data = sp->psc_smbtxrx;
|
||||
au_sync();
|
||||
buf[i] = data;
|
||||
buf[i] = (unsigned char)(RD(adap, PSC_SMBTXRX) & 0xff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
|
||||
static int i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
|
||||
unsigned int len)
|
||||
{
|
||||
int i;
|
||||
u32 data;
|
||||
volatile psc_smb_t *sp;
|
||||
unsigned long data;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
sp = (volatile psc_smb_t *)(adap->psc_base);
|
||||
|
||||
i = 0;
|
||||
while (i < (len-1)) {
|
||||
data = buf[i];
|
||||
sp->psc_smbtxrx = data;
|
||||
au_sync();
|
||||
WR(adap, PSC_SMBTXRX, data);
|
||||
if (wait_ack(adap))
|
||||
return -EIO;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* The last byte has to indicate transfer done.
|
||||
*/
|
||||
/* The last byte has to indicate transfer done. */
|
||||
data = buf[i];
|
||||
data |= PSC_SMBTXRX_STP;
|
||||
sp->psc_smbtxrx = data;
|
||||
au_sync();
|
||||
WR(adap, PSC_SMBTXRX, data);
|
||||
if (wait_master_done(adap))
|
||||
return -EIO;
|
||||
return 0;
|
||||
|
@ -269,12 +228,10 @@ static int
|
|||
au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct i2c_au1550_data *adap = i2c_adap->algo_data;
|
||||
volatile psc_smb_t *sp = (volatile psc_smb_t *)adap->psc_base;
|
||||
struct i2c_msg *p;
|
||||
int i, err = 0;
|
||||
|
||||
sp->psc_ctrl = PSC_CTRL_ENABLE;
|
||||
au_sync();
|
||||
WR(adap, PSC_CTRL, PSC_CTRL_ENABLE);
|
||||
|
||||
for (i = 0; !err && i < num; i++) {
|
||||
p = &msgs[i];
|
||||
|
@ -293,14 +250,12 @@ au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
|
|||
if (err == 0)
|
||||
err = num;
|
||||
|
||||
sp->psc_ctrl = PSC_CTRL_SUSPEND;
|
||||
au_sync();
|
||||
WR(adap, PSC_CTRL, PSC_CTRL_SUSPEND);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static u32
|
||||
au1550_func(struct i2c_adapter *adap)
|
||||
static u32 au1550_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
@ -312,57 +267,45 @@ static const struct i2c_algorithm au1550_algo = {
|
|||
|
||||
static void i2c_au1550_setup(struct i2c_au1550_data *priv)
|
||||
{
|
||||
volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base;
|
||||
u32 stat;
|
||||
unsigned long cfg;
|
||||
|
||||
sp->psc_ctrl = PSC_CTRL_DISABLE;
|
||||
au_sync();
|
||||
sp->psc_sel = PSC_SEL_PS_SMBUSMODE;
|
||||
sp->psc_smbcfg = 0;
|
||||
au_sync();
|
||||
sp->psc_ctrl = PSC_CTRL_ENABLE;
|
||||
au_sync();
|
||||
do {
|
||||
stat = sp->psc_smbstat;
|
||||
au_sync();
|
||||
} while ((stat & PSC_SMBSTAT_SR) == 0);
|
||||
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();
|
||||
|
||||
sp->psc_smbcfg = (PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 |
|
||||
PSC_SMBCFG_DD_DISABLE);
|
||||
cfg = PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 | PSC_SMBCFG_DD_DISABLE;
|
||||
WR(priv, PSC_SMBCFG, cfg);
|
||||
|
||||
/* Divide by 8 to get a 6.25 MHz clock. The later protocol
|
||||
* timings are based on this clock.
|
||||
*/
|
||||
sp->psc_smbcfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8);
|
||||
sp->psc_smbmsk = PSC_SMBMSK_ALLMASK;
|
||||
au_sync();
|
||||
cfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8);
|
||||
WR(priv, PSC_SMBCFG, cfg);
|
||||
WR(priv, PSC_SMBMSK, PSC_SMBMSK_ALLMASK);
|
||||
|
||||
/* Set the protocol timer values. See Table 71 in the
|
||||
* Au1550 Data Book for standard timing values.
|
||||
*/
|
||||
sp->psc_smbtmr = PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \
|
||||
WR(priv, PSC_SMBTMR, PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \
|
||||
PSC_SMBTMR_SET_PU(15) | PSC_SMBTMR_SET_SH(15) | \
|
||||
PSC_SMBTMR_SET_SU(15) | PSC_SMBTMR_SET_CL(15) | \
|
||||
PSC_SMBTMR_SET_CH(15);
|
||||
au_sync();
|
||||
PSC_SMBTMR_SET_CH(15));
|
||||
|
||||
sp->psc_smbcfg |= PSC_SMBCFG_DE_ENABLE;
|
||||
do {
|
||||
stat = sp->psc_smbstat;
|
||||
au_sync();
|
||||
} while ((stat & PSC_SMBSTAT_SR) == 0);
|
||||
cfg |= PSC_SMBCFG_DE_ENABLE;
|
||||
WR(priv, PSC_SMBCFG, cfg);
|
||||
while ((RD(priv, PSC_SMBSTAT) & PSC_SMBSTAT_SR) == 0)
|
||||
cpu_relax();
|
||||
|
||||
sp->psc_ctrl = PSC_CTRL_SUSPEND;
|
||||
au_sync();
|
||||
WR(priv, PSC_CTRL, PSC_CTRL_SUSPEND);
|
||||
}
|
||||
|
||||
static void i2c_au1550_disable(struct i2c_au1550_data *priv)
|
||||
{
|
||||
volatile psc_smb_t *sp = (volatile psc_smb_t *)priv->psc_base;
|
||||
|
||||
sp->psc_smbcfg = 0;
|
||||
sp->psc_ctrl = PSC_CTRL_DISABLE;
|
||||
au_sync();
|
||||
WR(priv, PSC_SMBCFG, 0);
|
||||
WR(priv, PSC_CTRL, PSC_CTRL_DISABLE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -396,9 +339,12 @@ i2c_au1550_probe(struct platform_device *pdev)
|
|||
goto out_mem;
|
||||
}
|
||||
|
||||
priv->psc_base = CKSEG1ADDR(r->start);
|
||||
priv->psc_base = ioremap(r->start, resource_size(r));
|
||||
if (!priv->psc_base) {
|
||||
ret = -EIO;
|
||||
goto out_map;
|
||||
}
|
||||
priv->xfer_timeout = 200;
|
||||
priv->ack_timeout = 200;
|
||||
|
||||
priv->adap.nr = pdev->id;
|
||||
priv->adap.algo = &au1550_algo;
|
||||
|
@ -406,8 +352,7 @@ i2c_au1550_probe(struct platform_device *pdev)
|
|||
priv->adap.dev.parent = &pdev->dev;
|
||||
strlcpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name));
|
||||
|
||||
/* Now, set up the PSC for SMBus PIO mode.
|
||||
*/
|
||||
/* Now, set up the PSC for SMBus PIO mode. */
|
||||
i2c_au1550_setup(priv);
|
||||
|
||||
ret = i2c_add_numbered_adapter(&priv->adap);
|
||||
|
@ -417,7 +362,8 @@ i2c_au1550_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
i2c_au1550_disable(priv);
|
||||
|
||||
iounmap(priv->psc_base);
|
||||
out_map:
|
||||
release_resource(priv->ioarea);
|
||||
kfree(priv->ioarea);
|
||||
out_mem:
|
||||
|
@ -426,14 +372,14 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit
|
||||
i2c_au1550_remove(struct platform_device *pdev)
|
||||
static int __devexit i2c_au1550_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_au1550_data *priv = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
i2c_del_adapter(&priv->adap);
|
||||
i2c_au1550_disable(priv);
|
||||
iounmap(priv->psc_base);
|
||||
release_resource(priv->ioarea);
|
||||
kfree(priv->ioarea);
|
||||
kfree(priv);
|
||||
|
@ -441,49 +387,51 @@ i2c_au1550_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int
|
||||
i2c_au1550_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
static int i2c_au1550_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_au1550_data *priv = platform_get_drvdata(pdev);
|
||||
struct i2c_au1550_data *priv = dev_get_drvdata(dev);
|
||||
|
||||
i2c_au1550_disable(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_au1550_resume(struct platform_device *pdev)
|
||||
static int i2c_au1550_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_au1550_data *priv = platform_get_drvdata(pdev);
|
||||
struct i2c_au1550_data *priv = dev_get_drvdata(dev);
|
||||
|
||||
i2c_au1550_setup(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops i2c_au1550_pmops = {
|
||||
.suspend = i2c_au1550_suspend,
|
||||
.resume = i2c_au1550_resume,
|
||||
};
|
||||
|
||||
#define AU1XPSC_SMBUS_PMOPS (&i2c_au1550_pmops)
|
||||
|
||||
#else
|
||||
#define i2c_au1550_suspend NULL
|
||||
#define i2c_au1550_resume NULL
|
||||
#define AU1XPSC_SMBUS_PMOPS NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver au1xpsc_smbus_driver = {
|
||||
.driver = {
|
||||
.name = "au1xpsc_smbus",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = AU1XPSC_SMBUS_PMOPS,
|
||||
},
|
||||
.probe = i2c_au1550_probe,
|
||||
.remove = __devexit_p(i2c_au1550_remove),
|
||||
.suspend = i2c_au1550_suspend,
|
||||
.resume = i2c_au1550_resume,
|
||||
};
|
||||
|
||||
static int __init
|
||||
i2c_au1550_init(void)
|
||||
static int __init i2c_au1550_init(void)
|
||||
{
|
||||
return platform_driver_register(&au1xpsc_smbus_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
i2c_au1550_exit(void)
|
||||
static void __exit i2c_au1550_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&au1xpsc_smbus_driver);
|
||||
}
|
||||
|
|
|
@ -25,18 +25,15 @@
|
|||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/delay.h>
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
/*
|
||||
* Registers offset
|
||||
|
@ -68,15 +65,10 @@
|
|||
#define DW_IC_STATUS 0x70
|
||||
#define DW_IC_TXFLR 0x74
|
||||
#define DW_IC_RXFLR 0x78
|
||||
#define DW_IC_COMP_PARAM_1 0xf4
|
||||
#define DW_IC_TX_ABRT_SOURCE 0x80
|
||||
|
||||
#define DW_IC_CON_MASTER 0x1
|
||||
#define DW_IC_CON_SPEED_STD 0x2
|
||||
#define DW_IC_CON_SPEED_FAST 0x4
|
||||
#define DW_IC_CON_10BITADDR_MASTER 0x10
|
||||
#define DW_IC_CON_RESTART_EN 0x20
|
||||
#define DW_IC_CON_SLAVE_DISABLE 0x40
|
||||
#define DW_IC_COMP_PARAM_1 0xf4
|
||||
#define DW_IC_COMP_TYPE 0xfc
|
||||
#define DW_IC_COMP_TYPE_VALUE 0x44570140
|
||||
|
||||
#define DW_IC_INTR_RX_UNDER 0x001
|
||||
#define DW_IC_INTR_RX_OVER 0x002
|
||||
|
@ -170,55 +162,23 @@ static char *abort_sources[] = {
|
|||
"lost arbitration",
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dw_i2c_dev - private i2c-designware data
|
||||
* @dev: driver model device node
|
||||
* @base: IO registers pointer
|
||||
* @cmd_complete: tx completion indicator
|
||||
* @lock: protect this struct and IO registers
|
||||
* @clk: input reference clock
|
||||
* @cmd_err: run time hadware error code
|
||||
* @msgs: points to an array of messages currently being transferred
|
||||
* @msgs_num: the number of elements in msgs
|
||||
* @msg_write_idx: the element index of the current tx message in the msgs
|
||||
* array
|
||||
* @tx_buf_len: the length of the current tx buffer
|
||||
* @tx_buf: the current tx buffer
|
||||
* @msg_read_idx: the element index of the current rx message in the msgs
|
||||
* array
|
||||
* @rx_buf_len: the length of the current rx buffer
|
||||
* @rx_buf: the current rx buffer
|
||||
* @msg_err: error status of the current transfer
|
||||
* @status: i2c master status, one of STATUS_*
|
||||
* @abort_source: copy of the TX_ABRT_SOURCE register
|
||||
* @irq: interrupt number for the i2c master
|
||||
* @adapter: i2c subsystem adapter node
|
||||
* @tx_fifo_depth: depth of the hardware tx fifo
|
||||
* @rx_fifo_depth: depth of the hardware rx fifo
|
||||
*/
|
||||
struct dw_i2c_dev {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct completion cmd_complete;
|
||||
struct mutex lock;
|
||||
struct clk *clk;
|
||||
int cmd_err;
|
||||
struct i2c_msg *msgs;
|
||||
int msgs_num;
|
||||
int msg_write_idx;
|
||||
u32 tx_buf_len;
|
||||
u8 *tx_buf;
|
||||
int msg_read_idx;
|
||||
u32 rx_buf_len;
|
||||
u8 *rx_buf;
|
||||
int msg_err;
|
||||
unsigned int status;
|
||||
u32 abort_source;
|
||||
int irq;
|
||||
struct i2c_adapter adapter;
|
||||
unsigned int tx_fifo_depth;
|
||||
unsigned int rx_fifo_depth;
|
||||
};
|
||||
u32 dw_readl(struct dw_i2c_dev *dev, int offset)
|
||||
{
|
||||
u32 value = readl(dev->base + offset);
|
||||
|
||||
if (dev->swab)
|
||||
return swab32(value);
|
||||
else
|
||||
return value;
|
||||
}
|
||||
|
||||
void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset)
|
||||
{
|
||||
if (dev->swab)
|
||||
b = swab32(b);
|
||||
|
||||
writel(b, dev->base + offset);
|
||||
}
|
||||
|
||||
static u32
|
||||
i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
|
||||
|
@ -283,13 +243,29 @@ static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
|
|||
* This function is called during I2C init function, and in case of timeout at
|
||||
* run time.
|
||||
*/
|
||||
static void i2c_dw_init(struct dw_i2c_dev *dev)
|
||||
int i2c_dw_init(struct dw_i2c_dev *dev)
|
||||
{
|
||||
u32 input_clock_khz = clk_get_rate(dev->clk) / 1000;
|
||||
u32 ic_con, hcnt, lcnt;
|
||||
u32 input_clock_khz;
|
||||
u32 hcnt, lcnt;
|
||||
u32 reg;
|
||||
|
||||
input_clock_khz = dev->get_clk_rate_khz(dev);
|
||||
|
||||
/* Configure register endianess access */
|
||||
reg = dw_readl(dev, DW_IC_COMP_TYPE);
|
||||
if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) {
|
||||
dev->swab = 1;
|
||||
reg = DW_IC_COMP_TYPE_VALUE;
|
||||
}
|
||||
|
||||
if (reg != DW_IC_COMP_TYPE_VALUE) {
|
||||
dev_err(dev->dev, "Unknown Synopsys component type: "
|
||||
"0x%08x\n", reg);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Disable the adapter */
|
||||
writel(0, dev->base + DW_IC_ENABLE);
|
||||
dw_writel(dev, 0, DW_IC_ENABLE);
|
||||
|
||||
/* set standard and fast speed deviders for high/low periods */
|
||||
|
||||
|
@ -303,8 +279,8 @@ static void i2c_dw_init(struct dw_i2c_dev *dev)
|
|||
47, /* tLOW = 4.7 us */
|
||||
3, /* tf = 0.3 us */
|
||||
0); /* No offset */
|
||||
writel(hcnt, dev->base + DW_IC_SS_SCL_HCNT);
|
||||
writel(lcnt, dev->base + DW_IC_SS_SCL_LCNT);
|
||||
dw_writel(dev, hcnt, DW_IC_SS_SCL_HCNT);
|
||||
dw_writel(dev, lcnt, DW_IC_SS_SCL_LCNT);
|
||||
dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
|
||||
|
||||
/* Fast-mode */
|
||||
|
@ -317,18 +293,17 @@ static void i2c_dw_init(struct dw_i2c_dev *dev)
|
|||
13, /* tLOW = 1.3 us */
|
||||
3, /* tf = 0.3 us */
|
||||
0); /* No offset */
|
||||
writel(hcnt, dev->base + DW_IC_FS_SCL_HCNT);
|
||||
writel(lcnt, dev->base + DW_IC_FS_SCL_LCNT);
|
||||
dw_writel(dev, hcnt, DW_IC_FS_SCL_HCNT);
|
||||
dw_writel(dev, lcnt, DW_IC_FS_SCL_LCNT);
|
||||
dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
|
||||
|
||||
/* Configure Tx/Rx FIFO threshold levels */
|
||||
writel(dev->tx_fifo_depth - 1, dev->base + DW_IC_TX_TL);
|
||||
writel(0, dev->base + DW_IC_RX_TL);
|
||||
dw_writel(dev, dev->tx_fifo_depth - 1, DW_IC_TX_TL);
|
||||
dw_writel(dev, 0, DW_IC_RX_TL);
|
||||
|
||||
/* configure the i2c master */
|
||||
ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
|
||||
DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
|
||||
writel(ic_con, dev->base + DW_IC_CON);
|
||||
dw_writel(dev, dev->master_cfg , DW_IC_CON);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -338,7 +313,7 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
|
|||
{
|
||||
int timeout = TIMEOUT;
|
||||
|
||||
while (readl(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
|
||||
while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
|
||||
if (timeout <= 0) {
|
||||
dev_warn(dev->dev, "timeout waiting for bus ready\n");
|
||||
return -ETIMEDOUT;
|
||||
|
@ -356,24 +331,24 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
|
|||
u32 ic_con;
|
||||
|
||||
/* Disable the adapter */
|
||||
writel(0, dev->base + DW_IC_ENABLE);
|
||||
dw_writel(dev, 0, DW_IC_ENABLE);
|
||||
|
||||
/* set the slave (target) address */
|
||||
writel(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR);
|
||||
dw_writel(dev, msgs[dev->msg_write_idx].addr, DW_IC_TAR);
|
||||
|
||||
/* if the slave address is ten bit address, enable 10BITADDR */
|
||||
ic_con = readl(dev->base + DW_IC_CON);
|
||||
ic_con = dw_readl(dev, DW_IC_CON);
|
||||
if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
|
||||
ic_con |= DW_IC_CON_10BITADDR_MASTER;
|
||||
else
|
||||
ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
|
||||
writel(ic_con, dev->base + DW_IC_CON);
|
||||
dw_writel(dev, ic_con, DW_IC_CON);
|
||||
|
||||
/* Enable the adapter */
|
||||
writel(1, dev->base + DW_IC_ENABLE);
|
||||
dw_writel(dev, 1, DW_IC_ENABLE);
|
||||
|
||||
/* Enable interrupts */
|
||||
writel(DW_IC_INTR_DEFAULT_MASK, dev->base + DW_IC_INTR_MASK);
|
||||
dw_writel(dev, DW_IC_INTR_DEFAULT_MASK, DW_IC_INTR_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -382,7 +357,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
|
|||
* messages into the tx buffer. Even if the size of i2c_msg data is
|
||||
* longer than the size of the tx buffer, it handles everything.
|
||||
*/
|
||||
static void
|
||||
void
|
||||
i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
||||
{
|
||||
struct i2c_msg *msgs = dev->msgs;
|
||||
|
@ -420,15 +395,15 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
|||
buf_len = msgs[dev->msg_write_idx].len;
|
||||
}
|
||||
|
||||
tx_limit = dev->tx_fifo_depth - readl(dev->base + DW_IC_TXFLR);
|
||||
rx_limit = dev->rx_fifo_depth - readl(dev->base + DW_IC_RXFLR);
|
||||
tx_limit = dev->tx_fifo_depth - dw_readl(dev, DW_IC_TXFLR);
|
||||
rx_limit = dev->rx_fifo_depth - dw_readl(dev, DW_IC_RXFLR);
|
||||
|
||||
while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
|
||||
if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
|
||||
writel(0x100, dev->base + DW_IC_DATA_CMD);
|
||||
dw_writel(dev, 0x100, DW_IC_DATA_CMD);
|
||||
rx_limit--;
|
||||
} else
|
||||
writel(*buf++, dev->base + DW_IC_DATA_CMD);
|
||||
dw_writel(dev, *buf++, DW_IC_DATA_CMD);
|
||||
tx_limit--; buf_len--;
|
||||
}
|
||||
|
||||
|
@ -453,7 +428,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
|||
if (dev->msg_err)
|
||||
intr_mask = 0;
|
||||
|
||||
writel(intr_mask, dev->base + DW_IC_INTR_MASK);
|
||||
dw_writel(dev, intr_mask, DW_IC_INTR_MASK);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -477,10 +452,10 @@ i2c_dw_read(struct dw_i2c_dev *dev)
|
|||
buf = dev->rx_buf;
|
||||
}
|
||||
|
||||
rx_valid = readl(dev->base + DW_IC_RXFLR);
|
||||
rx_valid = dw_readl(dev, DW_IC_RXFLR);
|
||||
|
||||
for (; len > 0 && rx_valid > 0; len--, rx_valid--)
|
||||
*buf++ = readl(dev->base + DW_IC_DATA_CMD);
|
||||
*buf++ = dw_readl(dev, DW_IC_DATA_CMD);
|
||||
|
||||
if (len > 0) {
|
||||
dev->status |= STATUS_READ_IN_PROGRESS;
|
||||
|
@ -518,7 +493,7 @@ static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
|
|||
/*
|
||||
* Prepare controller for a transaction and call i2c_dw_xfer_msg
|
||||
*/
|
||||
static int
|
||||
int
|
||||
i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||
{
|
||||
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
|
||||
|
@ -527,6 +502,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
|
||||
INIT_COMPLETION(dev->cmd_complete);
|
||||
dev->msgs = msgs;
|
||||
|
@ -563,7 +539,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
/* no error */
|
||||
if (likely(!dev->cmd_err)) {
|
||||
/* Disable the adapter */
|
||||
writel(0, dev->base + DW_IC_ENABLE);
|
||||
dw_writel(dev, 0, DW_IC_ENABLE);
|
||||
ret = num;
|
||||
goto done;
|
||||
}
|
||||
|
@ -576,19 +552,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
ret = -EIO;
|
||||
|
||||
done:
|
||||
pm_runtime_put(dev->dev);
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 i2c_dw_func(struct i2c_adapter *adap)
|
||||
u32 i2c_dw_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C |
|
||||
I2C_FUNC_10BIT_ADDR |
|
||||
I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK;
|
||||
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
|
||||
return dev->functionality;
|
||||
}
|
||||
|
||||
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
||||
|
@ -601,47 +574,47 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
|||
* in the IC_RAW_INTR_STAT register.
|
||||
*
|
||||
* That is,
|
||||
* stat = readl(IC_INTR_STAT);
|
||||
* stat = dw_readl(IC_INTR_STAT);
|
||||
* equals to,
|
||||
* stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK);
|
||||
* stat = dw_readl(IC_RAW_INTR_STAT) & dw_readl(IC_INTR_MASK);
|
||||
*
|
||||
* The raw version might be useful for debugging purposes.
|
||||
*/
|
||||
stat = readl(dev->base + DW_IC_INTR_STAT);
|
||||
stat = dw_readl(dev, DW_IC_INTR_STAT);
|
||||
|
||||
/*
|
||||
* Do not use the IC_CLR_INTR register to clear interrupts, or
|
||||
* you'll miss some interrupts, triggered during the period from
|
||||
* readl(IC_INTR_STAT) to readl(IC_CLR_INTR).
|
||||
* dw_readl(IC_INTR_STAT) to dw_readl(IC_CLR_INTR).
|
||||
*
|
||||
* Instead, use the separately-prepared IC_CLR_* registers.
|
||||
*/
|
||||
if (stat & DW_IC_INTR_RX_UNDER)
|
||||
readl(dev->base + DW_IC_CLR_RX_UNDER);
|
||||
dw_readl(dev, DW_IC_CLR_RX_UNDER);
|
||||
if (stat & DW_IC_INTR_RX_OVER)
|
||||
readl(dev->base + DW_IC_CLR_RX_OVER);
|
||||
dw_readl(dev, DW_IC_CLR_RX_OVER);
|
||||
if (stat & DW_IC_INTR_TX_OVER)
|
||||
readl(dev->base + DW_IC_CLR_TX_OVER);
|
||||
dw_readl(dev, DW_IC_CLR_TX_OVER);
|
||||
if (stat & DW_IC_INTR_RD_REQ)
|
||||
readl(dev->base + DW_IC_CLR_RD_REQ);
|
||||
dw_readl(dev, DW_IC_CLR_RD_REQ);
|
||||
if (stat & DW_IC_INTR_TX_ABRT) {
|
||||
/*
|
||||
* The IC_TX_ABRT_SOURCE register is cleared whenever
|
||||
* the IC_CLR_TX_ABRT is read. Preserve it beforehand.
|
||||
*/
|
||||
dev->abort_source = readl(dev->base + DW_IC_TX_ABRT_SOURCE);
|
||||
readl(dev->base + DW_IC_CLR_TX_ABRT);
|
||||
dev->abort_source = dw_readl(dev, DW_IC_TX_ABRT_SOURCE);
|
||||
dw_readl(dev, DW_IC_CLR_TX_ABRT);
|
||||
}
|
||||
if (stat & DW_IC_INTR_RX_DONE)
|
||||
readl(dev->base + DW_IC_CLR_RX_DONE);
|
||||
dw_readl(dev, DW_IC_CLR_RX_DONE);
|
||||
if (stat & DW_IC_INTR_ACTIVITY)
|
||||
readl(dev->base + DW_IC_CLR_ACTIVITY);
|
||||
dw_readl(dev, DW_IC_CLR_ACTIVITY);
|
||||
if (stat & DW_IC_INTR_STOP_DET)
|
||||
readl(dev->base + DW_IC_CLR_STOP_DET);
|
||||
dw_readl(dev, DW_IC_CLR_STOP_DET);
|
||||
if (stat & DW_IC_INTR_START_DET)
|
||||
readl(dev->base + DW_IC_CLR_START_DET);
|
||||
dw_readl(dev, DW_IC_CLR_START_DET);
|
||||
if (stat & DW_IC_INTR_GEN_CALL)
|
||||
readl(dev->base + DW_IC_CLR_GEN_CALL);
|
||||
dw_readl(dev, DW_IC_CLR_GEN_CALL);
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
@ -650,13 +623,19 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
|||
* Interrupt service routine. This gets called whenever an I2C interrupt
|
||||
* occurs.
|
||||
*/
|
||||
static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
|
||||
irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_id;
|
||||
u32 stat;
|
||||
u32 stat, enabled;
|
||||
|
||||
enabled = dw_readl(dev, DW_IC_ENABLE);
|
||||
stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
|
||||
dev_dbg(dev->dev, "%s: %s enabled= 0x%x stat=0x%x\n", __func__,
|
||||
dev->adapter.name, enabled, stat);
|
||||
if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))
|
||||
return IRQ_NONE;
|
||||
|
||||
stat = i2c_dw_read_clear_intrbits(dev);
|
||||
dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);
|
||||
|
||||
if (stat & DW_IC_INTR_TX_ABRT) {
|
||||
dev->cmd_err |= DW_IC_ERR_TX_ABRT;
|
||||
|
@ -666,7 +645,7 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
|
|||
* Anytime TX_ABRT is set, the contents of the tx/rx
|
||||
* buffers are flushed. Make sure to skip them.
|
||||
*/
|
||||
writel(0, dev->base + DW_IC_INTR_MASK);
|
||||
dw_writel(dev, 0, DW_IC_INTR_MASK);
|
||||
goto tx_aborted;
|
||||
}
|
||||
|
||||
|
@ -689,159 +668,38 @@ tx_aborted:
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct i2c_algorithm i2c_dw_algo = {
|
||||
.master_xfer = i2c_dw_xfer,
|
||||
.functionality = i2c_dw_func,
|
||||
};
|
||||
|
||||
static int __devinit dw_i2c_probe(struct platform_device *pdev)
|
||||
void i2c_dw_enable(struct dw_i2c_dev *dev)
|
||||
{
|
||||
struct dw_i2c_dev *dev;
|
||||
struct i2c_adapter *adap;
|
||||
struct resource *mem, *ioarea;
|
||||
int irq, r;
|
||||
|
||||
/* NOTE: driver uses the static register mapping */
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem) {
|
||||
dev_err(&pdev->dev, "no mem resource?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return irq; /* -ENXIO */
|
||||
}
|
||||
|
||||
ioarea = request_mem_region(mem->start, resource_size(mem),
|
||||
pdev->name);
|
||||
if (!ioarea) {
|
||||
dev_err(&pdev->dev, "I2C region already claimed\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
r = -ENOMEM;
|
||||
goto err_release_region;
|
||||
}
|
||||
|
||||
init_completion(&dev->cmd_complete);
|
||||
mutex_init(&dev->lock);
|
||||
dev->dev = get_device(&pdev->dev);
|
||||
dev->irq = irq;
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
dev->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(dev->clk)) {
|
||||
r = -ENODEV;
|
||||
goto err_free_mem;
|
||||
}
|
||||
clk_enable(dev->clk);
|
||||
|
||||
dev->base = ioremap(mem->start, resource_size(mem));
|
||||
if (dev->base == NULL) {
|
||||
dev_err(&pdev->dev, "failure mapping io resources\n");
|
||||
r = -EBUSY;
|
||||
goto err_unuse_clocks;
|
||||
}
|
||||
{
|
||||
u32 param1 = readl(dev->base + DW_IC_COMP_PARAM_1);
|
||||
|
||||
dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1;
|
||||
dev->rx_fifo_depth = ((param1 >> 8) & 0xff) + 1;
|
||||
}
|
||||
i2c_dw_init(dev);
|
||||
|
||||
writel(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */
|
||||
r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
|
||||
goto err_iounmap;
|
||||
}
|
||||
|
||||
adap = &dev->adapter;
|
||||
i2c_set_adapdata(adap, dev);
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = I2C_CLASS_HWMON;
|
||||
strlcpy(adap->name, "Synopsys DesignWare I2C adapter",
|
||||
sizeof(adap->name));
|
||||
adap->algo = &i2c_dw_algo;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
|
||||
adap->nr = pdev->id;
|
||||
r = i2c_add_numbered_adapter(adap);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failure adding adapter\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(dev->irq, dev);
|
||||
err_iounmap:
|
||||
iounmap(dev->base);
|
||||
err_unuse_clocks:
|
||||
clk_disable(dev->clk);
|
||||
clk_put(dev->clk);
|
||||
dev->clk = NULL;
|
||||
err_free_mem:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
put_device(&pdev->dev);
|
||||
kfree(dev);
|
||||
err_release_region:
|
||||
release_mem_region(mem->start, resource_size(mem));
|
||||
|
||||
return r;
|
||||
/* Enable the adapter */
|
||||
dw_writel(dev, 1, DW_IC_ENABLE);
|
||||
}
|
||||
|
||||
static int __devexit dw_i2c_remove(struct platform_device *pdev)
|
||||
u32 i2c_dw_is_enabled(struct dw_i2c_dev *dev)
|
||||
{
|
||||
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
||||
struct resource *mem;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
i2c_del_adapter(&dev->adapter);
|
||||
put_device(&pdev->dev);
|
||||
|
||||
clk_disable(dev->clk);
|
||||
clk_put(dev->clk);
|
||||
dev->clk = NULL;
|
||||
|
||||
writel(0, dev->base + DW_IC_ENABLE);
|
||||
free_irq(dev->irq, dev);
|
||||
kfree(dev);
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(mem->start, resource_size(mem));
|
||||
return 0;
|
||||
return dw_readl(dev, DW_IC_ENABLE);
|
||||
}
|
||||
|
||||
/* work with hotplug and coldplug */
|
||||
MODULE_ALIAS("platform:i2c_designware");
|
||||
|
||||
static struct platform_driver dw_i2c_driver = {
|
||||
.remove = __devexit_p(dw_i2c_remove),
|
||||
.driver = {
|
||||
.name = "i2c_designware",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init dw_i2c_init_driver(void)
|
||||
void i2c_dw_disable(struct dw_i2c_dev *dev)
|
||||
{
|
||||
return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe);
|
||||
}
|
||||
module_init(dw_i2c_init_driver);
|
||||
/* Disable controller */
|
||||
dw_writel(dev, 0, DW_IC_ENABLE);
|
||||
|
||||
static void __exit dw_i2c_exit_driver(void)
|
||||
/* Disable all interupts */
|
||||
dw_writel(dev, 0, DW_IC_INTR_MASK);
|
||||
dw_readl(dev, DW_IC_CLR_INTR);
|
||||
}
|
||||
|
||||
void i2c_dw_clear_int(struct dw_i2c_dev *dev)
|
||||
{
|
||||
platform_driver_unregister(&dw_i2c_driver);
|
||||
dw_readl(dev, DW_IC_CLR_INTR);
|
||||
}
|
||||
module_exit(dw_i2c_exit_driver);
|
||||
|
||||
MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
|
||||
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter");
|
||||
MODULE_LICENSE("GPL");
|
||||
void i2c_dw_disable_int(struct dw_i2c_dev *dev)
|
||||
{
|
||||
dw_writel(dev, 0, DW_IC_INTR_MASK);
|
||||
}
|
||||
|
||||
u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev)
|
||||
{
|
||||
return dw_readl(dev, DW_IC_COMP_PARAM_1);
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Synopsys DesignWare I2C adapter driver (master only).
|
||||
*
|
||||
* Based on the TI DAVINCI I2C adapter driver.
|
||||
*
|
||||
* Copyright (C) 2006 Texas Instruments.
|
||||
* Copyright (C) 2007 MontaVista Software Inc.
|
||||
* Copyright (C) 2009 Provigent Ltd.
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 02139, USA.
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#define DW_IC_CON_MASTER 0x1
|
||||
#define DW_IC_CON_SPEED_STD 0x2
|
||||
#define DW_IC_CON_SPEED_FAST 0x4
|
||||
#define DW_IC_CON_10BITADDR_MASTER 0x10
|
||||
#define DW_IC_CON_RESTART_EN 0x20
|
||||
#define DW_IC_CON_SLAVE_DISABLE 0x40
|
||||
|
||||
|
||||
/**
|
||||
* struct dw_i2c_dev - private i2c-designware data
|
||||
* @dev: driver model device node
|
||||
* @base: IO registers pointer
|
||||
* @cmd_complete: tx completion indicator
|
||||
* @lock: protect this struct and IO registers
|
||||
* @clk: input reference clock
|
||||
* @cmd_err: run time hadware error code
|
||||
* @msgs: points to an array of messages currently being transfered
|
||||
* @msgs_num: the number of elements in msgs
|
||||
* @msg_write_idx: the element index of the current tx message in the msgs
|
||||
* array
|
||||
* @tx_buf_len: the length of the current tx buffer
|
||||
* @tx_buf: the current tx buffer
|
||||
* @msg_read_idx: the element index of the current rx message in the msgs
|
||||
* array
|
||||
* @rx_buf_len: the length of the current rx buffer
|
||||
* @rx_buf: the current rx buffer
|
||||
* @msg_err: error status of the current transfer
|
||||
* @status: i2c master status, one of STATUS_*
|
||||
* @abort_source: copy of the TX_ABRT_SOURCE register
|
||||
* @irq: interrupt number for the i2c master
|
||||
* @adapter: i2c subsystem adapter node
|
||||
* @tx_fifo_depth: depth of the hardware tx fifo
|
||||
* @rx_fifo_depth: depth of the hardware rx fifo
|
||||
*/
|
||||
struct dw_i2c_dev {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct completion cmd_complete;
|
||||
struct mutex lock;
|
||||
struct clk *clk;
|
||||
u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev);
|
||||
struct dw_pci_controller *controller;
|
||||
int cmd_err;
|
||||
struct i2c_msg *msgs;
|
||||
int msgs_num;
|
||||
int msg_write_idx;
|
||||
u32 tx_buf_len;
|
||||
u8 *tx_buf;
|
||||
int msg_read_idx;
|
||||
u32 rx_buf_len;
|
||||
u8 *rx_buf;
|
||||
int msg_err;
|
||||
unsigned int status;
|
||||
u32 abort_source;
|
||||
int irq;
|
||||
int swab;
|
||||
struct i2c_adapter adapter;
|
||||
u32 functionality;
|
||||
u32 master_cfg;
|
||||
unsigned int tx_fifo_depth;
|
||||
unsigned int rx_fifo_depth;
|
||||
};
|
||||
|
||||
extern u32 dw_readl(struct dw_i2c_dev *dev, int offset);
|
||||
extern void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset);
|
||||
extern int i2c_dw_init(struct dw_i2c_dev *dev);
|
||||
extern int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
||||
int num);
|
||||
extern u32 i2c_dw_func(struct i2c_adapter *adap);
|
||||
extern irqreturn_t i2c_dw_isr(int this_irq, void *dev_id);
|
||||
extern void i2c_dw_enable(struct dw_i2c_dev *dev);
|
||||
extern u32 i2c_dw_is_enabled(struct dw_i2c_dev *dev);
|
||||
extern void i2c_dw_disable(struct dw_i2c_dev *dev);
|
||||
extern void i2c_dw_clear_int(struct dw_i2c_dev *dev);
|
||||
extern void i2c_dw_disable_int(struct dw_i2c_dev *dev);
|
||||
extern u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev);
|
|
@ -0,0 +1,392 @@
|
|||
/*
|
||||
* Synopsys DesignWare I2C adapter driver (master only).
|
||||
*
|
||||
* Based on the TI DAVINCI I2C adapter driver.
|
||||
*
|
||||
* Copyright (C) 2006 Texas Instruments.
|
||||
* Copyright (C) 2007 MontaVista Software Inc.
|
||||
* Copyright (C) 2009 Provigent Ltd.
|
||||
* Copyright (C) 2011 Intel corporation.
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 02139, USA.
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
#define DRIVER_NAME "i2c-designware-pci"
|
||||
|
||||
enum dw_pci_ctl_id_t {
|
||||
moorestown_0,
|
||||
moorestown_1,
|
||||
moorestown_2,
|
||||
|
||||
medfield_0,
|
||||
medfield_1,
|
||||
medfield_2,
|
||||
medfield_3,
|
||||
medfield_4,
|
||||
medfield_5,
|
||||
};
|
||||
|
||||
struct dw_pci_controller {
|
||||
u32 bus_num;
|
||||
u32 bus_cfg;
|
||||
u32 tx_fifo_depth;
|
||||
u32 rx_fifo_depth;
|
||||
u32 clk_khz;
|
||||
};
|
||||
|
||||
#define INTEL_MID_STD_CFG (DW_IC_CON_MASTER | \
|
||||
DW_IC_CON_SLAVE_DISABLE | \
|
||||
DW_IC_CON_RESTART_EN)
|
||||
|
||||
static struct dw_pci_controller dw_pci_controllers[] = {
|
||||
[moorestown_0] = {
|
||||
.bus_num = 0,
|
||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||
.tx_fifo_depth = 32,
|
||||
.rx_fifo_depth = 32,
|
||||
.clk_khz = 25000,
|
||||
},
|
||||
[moorestown_1] = {
|
||||
.bus_num = 1,
|
||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||
.tx_fifo_depth = 32,
|
||||
.rx_fifo_depth = 32,
|
||||
.clk_khz = 25000,
|
||||
},
|
||||
[moorestown_2] = {
|
||||
.bus_num = 2,
|
||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||
.tx_fifo_depth = 32,
|
||||
.rx_fifo_depth = 32,
|
||||
.clk_khz = 25000,
|
||||
},
|
||||
[medfield_0] = {
|
||||
.bus_num = 0,
|
||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||
.tx_fifo_depth = 32,
|
||||
.rx_fifo_depth = 32,
|
||||
.clk_khz = 25000,
|
||||
},
|
||||
[medfield_1] = {
|
||||
.bus_num = 1,
|
||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||
.tx_fifo_depth = 32,
|
||||
.rx_fifo_depth = 32,
|
||||
.clk_khz = 25000,
|
||||
},
|
||||
[medfield_2] = {
|
||||
.bus_num = 2,
|
||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||
.tx_fifo_depth = 32,
|
||||
.rx_fifo_depth = 32,
|
||||
.clk_khz = 25000,
|
||||
},
|
||||
[medfield_3] = {
|
||||
.bus_num = 3,
|
||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_STD,
|
||||
.tx_fifo_depth = 32,
|
||||
.rx_fifo_depth = 32,
|
||||
.clk_khz = 25000,
|
||||
},
|
||||
[medfield_4] = {
|
||||
.bus_num = 4,
|
||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||
.tx_fifo_depth = 32,
|
||||
.rx_fifo_depth = 32,
|
||||
.clk_khz = 25000,
|
||||
},
|
||||
[medfield_5] = {
|
||||
.bus_num = 5,
|
||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||
.tx_fifo_depth = 32,
|
||||
.rx_fifo_depth = 32,
|
||||
.clk_khz = 25000,
|
||||
},
|
||||
};
|
||||
static struct i2c_algorithm i2c_dw_algo = {
|
||||
.master_xfer = i2c_dw_xfer,
|
||||
.functionality = i2c_dw_func,
|
||||
};
|
||||
|
||||
static int i2c_dw_pci_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
|
||||
struct dw_i2c_dev *i2c = pci_get_drvdata(pdev);
|
||||
int err;
|
||||
|
||||
|
||||
i2c_dw_disable(i2c);
|
||||
|
||||
err = pci_save_state(pdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "pci_save_state failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pci_set_power_state(pdev, PCI_D3hot);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "pci_set_power_state failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_dw_pci_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
|
||||
struct dw_i2c_dev *i2c = pci_get_drvdata(pdev);
|
||||
int err;
|
||||
u32 enabled;
|
||||
|
||||
enabled = i2c_dw_is_enabled(i2c);
|
||||
if (enabled)
|
||||
return 0;
|
||||
|
||||
err = pci_set_power_state(pdev, PCI_D0);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "pci_set_power_state() failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
pci_restore_state(pdev);
|
||||
|
||||
i2c_dw_init(i2c);
|
||||
i2c_dw_enable(i2c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_dw_pci_runtime_idle(struct device *dev)
|
||||
{
|
||||
int err = pm_schedule_suspend(dev, 500);
|
||||
dev_dbg(dev, "runtime_idle called\n");
|
||||
|
||||
if (err != 0)
|
||||
return 0;
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops i2c_dw_pm_ops = {
|
||||
.resume = i2c_dw_pci_resume,
|
||||
.suspend = i2c_dw_pci_suspend,
|
||||
SET_RUNTIME_PM_OPS(i2c_dw_pci_suspend, i2c_dw_pci_resume,
|
||||
i2c_dw_pci_runtime_idle)
|
||||
};
|
||||
|
||||
static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
||||
{
|
||||
return dev->controller->clk_khz;
|
||||
}
|
||||
|
||||
static int __devinit i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct dw_i2c_dev *dev;
|
||||
struct i2c_adapter *adap;
|
||||
unsigned long start, len;
|
||||
void __iomem *base;
|
||||
int r;
|
||||
struct dw_pci_controller *controller;
|
||||
|
||||
if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) {
|
||||
printk(KERN_ERR "dw_i2c_pci_probe: invalid driver data %ld\n",
|
||||
id->driver_data);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
controller = &dw_pci_controllers[id->driver_data];
|
||||
|
||||
r = pci_enable_device(pdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to enable I2C PCI device (%d)\n",
|
||||
r);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Determine the address of the I2C area */
|
||||
start = pci_resource_start(pdev, 0);
|
||||
len = pci_resource_len(pdev, 0);
|
||||
if (!start || len == 0) {
|
||||
dev_err(&pdev->dev, "base address not set\n");
|
||||
r = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
r = pci_request_region(pdev, 0, DRIVER_NAME);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failed to request I2C region "
|
||||
"0x%lx-0x%lx\n", start,
|
||||
(unsigned long)pci_resource_end(pdev, 0));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
base = ioremap_nocache(start, len);
|
||||
if (!base) {
|
||||
dev_err(&pdev->dev, "I/O memory remapping failed\n");
|
||||
r = -ENOMEM;
|
||||
goto err_release_region;
|
||||
}
|
||||
|
||||
|
||||
dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
r = -ENOMEM;
|
||||
goto err_release_region;
|
||||
}
|
||||
|
||||
init_completion(&dev->cmd_complete);
|
||||
mutex_init(&dev->lock);
|
||||
dev->clk = NULL;
|
||||
dev->controller = controller;
|
||||
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
|
||||
dev->base = base;
|
||||
dev->dev = get_device(&pdev->dev);
|
||||
dev->functionality =
|
||||
I2C_FUNC_I2C |
|
||||
I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK;
|
||||
dev->master_cfg = controller->bus_cfg;
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
dev->tx_fifo_depth = controller->tx_fifo_depth;
|
||||
dev->rx_fifo_depth = controller->rx_fifo_depth;
|
||||
r = i2c_dw_init(dev);
|
||||
if (r)
|
||||
goto err_iounmap;
|
||||
|
||||
adap = &dev->adapter;
|
||||
i2c_set_adapdata(adap, dev);
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = 0;
|
||||
adap->algo = &i2c_dw_algo;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
adap->nr = controller->bus_num;
|
||||
snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci-%d",
|
||||
adap->nr);
|
||||
|
||||
r = request_irq(pdev->irq, i2c_dw_isr, IRQF_SHARED, adap->name, dev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
|
||||
goto err_iounmap;
|
||||
}
|
||||
|
||||
i2c_dw_disable_int(dev);
|
||||
i2c_dw_clear_int(dev);
|
||||
r = i2c_add_numbered_adapter(adap);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failure adding adapter\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(pdev->irq, dev);
|
||||
err_iounmap:
|
||||
iounmap(dev->base);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
put_device(&pdev->dev);
|
||||
kfree(dev);
|
||||
err_release_region:
|
||||
pci_release_region(pdev, 0);
|
||||
exit:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void __devexit i2c_dw_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct dw_i2c_dev *dev = pci_get_drvdata(pdev);
|
||||
|
||||
i2c_dw_disable(dev);
|
||||
pm_runtime_forbid(&pdev->dev);
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
i2c_del_adapter(&dev->adapter);
|
||||
put_device(&pdev->dev);
|
||||
|
||||
free_irq(dev->irq, dev);
|
||||
kfree(dev);
|
||||
pci_release_region(pdev, 0);
|
||||
}
|
||||
|
||||
/* work with hotplug and coldplug */
|
||||
MODULE_ALIAS("i2c_designware-pci");
|
||||
|
||||
DEFINE_PCI_DEVICE_TABLE(i2_designware_pci_ids) = {
|
||||
/* Moorestown */
|
||||
{ PCI_VDEVICE(INTEL, 0x0802), moorestown_0 },
|
||||
{ PCI_VDEVICE(INTEL, 0x0803), moorestown_1 },
|
||||
{ PCI_VDEVICE(INTEL, 0x0804), moorestown_2 },
|
||||
/* Medfield */
|
||||
{ PCI_VDEVICE(INTEL, 0x0817), medfield_3,},
|
||||
{ PCI_VDEVICE(INTEL, 0x0818), medfield_4 },
|
||||
{ PCI_VDEVICE(INTEL, 0x0819), medfield_5 },
|
||||
{ PCI_VDEVICE(INTEL, 0x082C), medfield_0 },
|
||||
{ PCI_VDEVICE(INTEL, 0x082D), medfield_1 },
|
||||
{ PCI_VDEVICE(INTEL, 0x082E), medfield_2 },
|
||||
{ 0,}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids);
|
||||
|
||||
static struct pci_driver dw_i2c_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.id_table = i2_designware_pci_ids,
|
||||
.probe = i2c_dw_pci_probe,
|
||||
.remove = __devexit_p(i2c_dw_pci_remove),
|
||||
.driver = {
|
||||
.pm = &i2c_dw_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init dw_i2c_init_driver(void)
|
||||
{
|
||||
return pci_register_driver(&dw_i2c_driver);
|
||||
}
|
||||
module_init(dw_i2c_init_driver);
|
||||
|
||||
static void __exit dw_i2c_exit_driver(void)
|
||||
{
|
||||
pci_unregister_driver(&dw_i2c_driver);
|
||||
}
|
||||
module_exit(dw_i2c_exit_driver);
|
||||
|
||||
MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
|
||||
MODULE_DESCRIPTION("Synopsys DesignWare PCI I2C bus adapter");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Synopsys DesignWare I2C adapter driver (master only).
|
||||
*
|
||||
* Based on the TI DAVINCI I2C adapter driver.
|
||||
*
|
||||
* Copyright (C) 2006 Texas Instruments.
|
||||
* Copyright (C) 2007 MontaVista Software Inc.
|
||||
* Copyright (C) 2009 Provigent Ltd.
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* 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 02139, USA.
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
static struct i2c_algorithm i2c_dw_algo = {
|
||||
.master_xfer = i2c_dw_xfer,
|
||||
.functionality = i2c_dw_func,
|
||||
};
|
||||
static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
||||
{
|
||||
return clk_get_rate(dev->clk)/1000;
|
||||
}
|
||||
|
||||
static int __devinit dw_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_i2c_dev *dev;
|
||||
struct i2c_adapter *adap;
|
||||
struct resource *mem, *ioarea;
|
||||
int irq, r;
|
||||
|
||||
/* NOTE: driver uses the static register mapping */
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem) {
|
||||
dev_err(&pdev->dev, "no mem resource?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return irq; /* -ENXIO */
|
||||
}
|
||||
|
||||
ioarea = request_mem_region(mem->start, resource_size(mem),
|
||||
pdev->name);
|
||||
if (!ioarea) {
|
||||
dev_err(&pdev->dev, "I2C region already claimed\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
r = -ENOMEM;
|
||||
goto err_release_region;
|
||||
}
|
||||
|
||||
init_completion(&dev->cmd_complete);
|
||||
mutex_init(&dev->lock);
|
||||
dev->dev = get_device(&pdev->dev);
|
||||
dev->irq = irq;
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
dev->clk = clk_get(&pdev->dev, NULL);
|
||||
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
|
||||
|
||||
if (IS_ERR(dev->clk)) {
|
||||
r = -ENODEV;
|
||||
goto err_free_mem;
|
||||
}
|
||||
clk_enable(dev->clk);
|
||||
|
||||
dev->functionality =
|
||||
I2C_FUNC_I2C |
|
||||
I2C_FUNC_10BIT_ADDR |
|
||||
I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK;
|
||||
dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
|
||||
DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
|
||||
|
||||
dev->base = ioremap(mem->start, resource_size(mem));
|
||||
if (dev->base == NULL) {
|
||||
dev_err(&pdev->dev, "failure mapping io resources\n");
|
||||
r = -EBUSY;
|
||||
goto err_unuse_clocks;
|
||||
}
|
||||
{
|
||||
u32 param1 = i2c_dw_read_comp_param(dev);
|
||||
|
||||
dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1;
|
||||
dev->rx_fifo_depth = ((param1 >> 8) & 0xff) + 1;
|
||||
}
|
||||
r = i2c_dw_init(dev);
|
||||
if (r)
|
||||
goto err_iounmap;
|
||||
|
||||
i2c_dw_disable_int(dev);
|
||||
r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
|
||||
goto err_iounmap;
|
||||
}
|
||||
|
||||
adap = &dev->adapter;
|
||||
i2c_set_adapdata(adap, dev);
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = I2C_CLASS_HWMON;
|
||||
strlcpy(adap->name, "Synopsys DesignWare I2C adapter",
|
||||
sizeof(adap->name));
|
||||
adap->algo = &i2c_dw_algo;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
|
||||
adap->nr = pdev->id;
|
||||
r = i2c_add_numbered_adapter(adap);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failure adding adapter\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(dev->irq, dev);
|
||||
err_iounmap:
|
||||
iounmap(dev->base);
|
||||
err_unuse_clocks:
|
||||
clk_disable(dev->clk);
|
||||
clk_put(dev->clk);
|
||||
dev->clk = NULL;
|
||||
err_free_mem:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
put_device(&pdev->dev);
|
||||
kfree(dev);
|
||||
err_release_region:
|
||||
release_mem_region(mem->start, resource_size(mem));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __devexit dw_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
||||
struct resource *mem;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
i2c_del_adapter(&dev->adapter);
|
||||
put_device(&pdev->dev);
|
||||
|
||||
clk_disable(dev->clk);
|
||||
clk_put(dev->clk);
|
||||
dev->clk = NULL;
|
||||
|
||||
i2c_dw_disable(dev);
|
||||
free_irq(dev->irq, dev);
|
||||
kfree(dev);
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(mem->start, resource_size(mem));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* work with hotplug and coldplug */
|
||||
MODULE_ALIAS("platform:i2c_designware");
|
||||
|
||||
static struct platform_driver dw_i2c_driver = {
|
||||
.remove = __devexit_p(dw_i2c_remove),
|
||||
.driver = {
|
||||
.name = "i2c_designware",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init dw_i2c_init_driver(void)
|
||||
{
|
||||
return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe);
|
||||
}
|
||||
module_init(dw_i2c_init_driver);
|
||||
|
||||
static void __exit dw_i2c_exit_driver(void)
|
||||
{
|
||||
platform_driver_unregister(&dw_i2c_driver);
|
||||
}
|
||||
module_exit(dw_i2c_exit_driver);
|
||||
|
||||
MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
|
||||
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -64,6 +64,7 @@
|
|||
#define TEN_BIT_ADDR_DEFAULT 0xF000
|
||||
#define TEN_BIT_ADDR_MASK 0xF0
|
||||
#define PCH_START 0x0020
|
||||
#define PCH_RESTART 0x0004
|
||||
#define PCH_ESR_START 0x0001
|
||||
#define PCH_BUFF_START 0x1
|
||||
#define PCH_REPSTART 0x0004
|
||||
|
@ -273,23 +274,24 @@ static s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
|
|||
s32 timeout)
|
||||
{
|
||||
void __iomem *p = adap->pch_base_address;
|
||||
ktime_t ns_val;
|
||||
|
||||
if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
|
||||
return 0;
|
||||
|
||||
/* MAX timeout value is timeout*1000*1000nsec */
|
||||
ktime_t ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000);
|
||||
ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000);
|
||||
do {
|
||||
if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
|
||||
break;
|
||||
msleep(20);
|
||||
if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
|
||||
return 0;
|
||||
} while (ktime_lt(ktime_get(), ns_val));
|
||||
|
||||
pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR));
|
||||
|
||||
if (timeout == 0) {
|
||||
pch_err(adap, "%s: Timeout Error.return%d\n", __func__, -ETIME);
|
||||
return -ETIME;
|
||||
}
|
||||
pch_i2c_init(adap);
|
||||
|
||||
return 0;
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -311,21 +313,19 @@ static void pch_i2c_start(struct i2c_algo_pch_data *adap)
|
|||
*/
|
||||
static s32 pch_i2c_wait_for_xfer_complete(struct i2c_algo_pch_data *adap)
|
||||
{
|
||||
s32 ret;
|
||||
long ret;
|
||||
ret = wait_event_timeout(pch_event,
|
||||
(adap->pch_event_flag != 0), msecs_to_jiffies(50));
|
||||
if (ret < 0) {
|
||||
pch_err(adap, "timeout: %x\n", adap->pch_event_flag);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
pch_err(adap, "timeout: %x\n", adap->pch_event_flag);
|
||||
adap->pch_event_flag = 0;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (adap->pch_event_flag & I2C_ERROR_MASK) {
|
||||
pch_err(adap, "error bits set: %x\n", adap->pch_event_flag);
|
||||
adap->pch_event_flag = 0;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -394,6 +394,7 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap,
|
|||
u32 addr_2_msb;
|
||||
u32 addr_8_lsb;
|
||||
s32 wrcount;
|
||||
s32 rtn;
|
||||
void __iomem *p = adap->pch_base_address;
|
||||
|
||||
length = msgs->len;
|
||||
|
@ -412,15 +413,29 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap,
|
|||
}
|
||||
|
||||
if (msgs->flags & I2C_M_TEN) {
|
||||
addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7);
|
||||
addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7) & 0x06;
|
||||
iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
|
||||
if (first)
|
||||
pch_i2c_start(adap);
|
||||
if (pch_i2c_wait_for_xfer_complete(adap) == 0 &&
|
||||
pch_i2c_getack(adap) == 0) {
|
||||
|
||||
rtn = pch_i2c_wait_for_xfer_complete(adap);
|
||||
if (rtn == 0) {
|
||||
if (pch_i2c_getack(adap)) {
|
||||
pch_dbg(adap, "Receive NACK for slave address"
|
||||
"setting\n");
|
||||
return -EIO;
|
||||
}
|
||||
addr_8_lsb = (addr & I2C_ADDR_MSK);
|
||||
iowrite32(addr_8_lsb, p + PCH_I2CDR);
|
||||
} else {
|
||||
} else if (rtn == -EIO) { /* Arbitration Lost */
|
||||
pch_err(adap, "Lost Arbitration\n");
|
||||
pch_clrbit(adap->pch_base_address, PCH_I2CSR,
|
||||
I2CMAL_BIT);
|
||||
pch_clrbit(adap->pch_base_address, PCH_I2CSR,
|
||||
I2CMIF_BIT);
|
||||
pch_i2c_init(adap);
|
||||
return -EAGAIN;
|
||||
} else { /* wait-event timeout */
|
||||
pch_i2c_stop(adap);
|
||||
return -ETIME;
|
||||
}
|
||||
|
@ -431,30 +446,51 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap,
|
|||
pch_i2c_start(adap);
|
||||
}
|
||||
|
||||
if ((pch_i2c_wait_for_xfer_complete(adap) == 0) &&
|
||||
(pch_i2c_getack(adap) == 0)) {
|
||||
rtn = pch_i2c_wait_for_xfer_complete(adap);
|
||||
if (rtn == 0) {
|
||||
if (pch_i2c_getack(adap)) {
|
||||
pch_dbg(adap, "Receive NACK for slave address"
|
||||
"setting\n");
|
||||
return -EIO;
|
||||
}
|
||||
} else if (rtn == -EIO) { /* Arbitration Lost */
|
||||
pch_err(adap, "Lost Arbitration\n");
|
||||
pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMAL_BIT);
|
||||
pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT);
|
||||
pch_i2c_init(adap);
|
||||
return -EAGAIN;
|
||||
} else { /* wait-event timeout */
|
||||
pch_i2c_stop(adap);
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
for (wrcount = 0; wrcount < length; ++wrcount) {
|
||||
/* write buffer value to I2C data register */
|
||||
iowrite32(buf[wrcount], p + PCH_I2CDR);
|
||||
pch_dbg(adap, "writing %x to Data register\n",
|
||||
buf[wrcount]);
|
||||
pch_dbg(adap, "writing %x to Data register\n", buf[wrcount]);
|
||||
|
||||
if (pch_i2c_wait_for_xfer_complete(adap) != 0)
|
||||
return -ETIME;
|
||||
|
||||
if (pch_i2c_getack(adap))
|
||||
rtn = pch_i2c_wait_for_xfer_complete(adap);
|
||||
if (rtn == 0) {
|
||||
if (pch_i2c_getack(adap)) {
|
||||
pch_dbg(adap, "Receive NACK for slave address"
|
||||
"setting\n");
|
||||
return -EIO;
|
||||
}
|
||||
pch_clrbit(adap->pch_base_address, PCH_I2CSR,
|
||||
I2CMCF_BIT);
|
||||
pch_clrbit(adap->pch_base_address, PCH_I2CSR,
|
||||
I2CMIF_BIT);
|
||||
} else { /* wait-event timeout */
|
||||
pch_i2c_stop(adap);
|
||||
return -ETIME;
|
||||
}
|
||||
}
|
||||
|
||||
/* check if this is the last message */
|
||||
if (last)
|
||||
pch_i2c_stop(adap);
|
||||
else
|
||||
pch_i2c_repstart(adap);
|
||||
} else {
|
||||
pch_i2c_stop(adap);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
pch_dbg(adap, "return=%d\n", wrcount);
|
||||
|
||||
|
@ -483,6 +519,19 @@ static void pch_i2c_sendnack(struct i2c_algo_pch_data *adap)
|
|||
pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_ACK);
|
||||
}
|
||||
|
||||
/**
|
||||
* pch_i2c_restart() - Generate I2C restart condition in normal mode.
|
||||
* @adap: Pointer to struct i2c_algo_pch_data.
|
||||
*
|
||||
* Generate I2C restart condition in normal mode by setting I2CCTL.I2CRSTA.
|
||||
*/
|
||||
static void pch_i2c_restart(struct i2c_algo_pch_data *adap)
|
||||
{
|
||||
void __iomem *p = adap->pch_base_address;
|
||||
pch_dbg(adap, "I2CCTL = %x\n", ioread32(p + PCH_I2CCTL));
|
||||
pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_RESTART);
|
||||
}
|
||||
|
||||
/**
|
||||
* pch_i2c_readbytes() - read data from I2C bus in normal mode.
|
||||
* @i2c_adap: Pointer to the struct i2c_adapter.
|
||||
|
@ -500,7 +549,9 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
|
|||
u32 length;
|
||||
u32 addr;
|
||||
u32 addr_2_msb;
|
||||
u32 addr_8_lsb;
|
||||
void __iomem *p = adap->pch_base_address;
|
||||
s32 rtn;
|
||||
|
||||
length = msgs->len;
|
||||
buf = msgs->buf;
|
||||
|
@ -515,9 +566,55 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
|
|||
}
|
||||
|
||||
if (msgs->flags & I2C_M_TEN) {
|
||||
addr_2_msb = (((addr & I2C_MSB_2B_MSK) >> 7) | (I2C_RD));
|
||||
addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7);
|
||||
iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR);
|
||||
if (first)
|
||||
pch_i2c_start(adap);
|
||||
|
||||
rtn = pch_i2c_wait_for_xfer_complete(adap);
|
||||
if (rtn == 0) {
|
||||
if (pch_i2c_getack(adap)) {
|
||||
pch_dbg(adap, "Receive NACK for slave address"
|
||||
"setting\n");
|
||||
return -EIO;
|
||||
}
|
||||
addr_8_lsb = (addr & I2C_ADDR_MSK);
|
||||
iowrite32(addr_8_lsb, p + PCH_I2CDR);
|
||||
} else if (rtn == -EIO) { /* Arbitration Lost */
|
||||
pch_err(adap, "Lost Arbitration\n");
|
||||
pch_clrbit(adap->pch_base_address, PCH_I2CSR,
|
||||
I2CMAL_BIT);
|
||||
pch_clrbit(adap->pch_base_address, PCH_I2CSR,
|
||||
I2CMIF_BIT);
|
||||
pch_i2c_init(adap);
|
||||
return -EAGAIN;
|
||||
} else { /* wait-event timeout */
|
||||
pch_i2c_stop(adap);
|
||||
return -ETIME;
|
||||
}
|
||||
pch_i2c_restart(adap);
|
||||
rtn = pch_i2c_wait_for_xfer_complete(adap);
|
||||
if (rtn == 0) {
|
||||
if (pch_i2c_getack(adap)) {
|
||||
pch_dbg(adap, "Receive NACK for slave address"
|
||||
"setting\n");
|
||||
return -EIO;
|
||||
}
|
||||
addr_2_msb |= I2C_RD;
|
||||
iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK,
|
||||
p + PCH_I2CDR);
|
||||
} else if (rtn == -EIO) { /* Arbitration Lost */
|
||||
pch_err(adap, "Lost Arbitration\n");
|
||||
pch_clrbit(adap->pch_base_address, PCH_I2CSR,
|
||||
I2CMAL_BIT);
|
||||
pch_clrbit(adap->pch_base_address, PCH_I2CSR,
|
||||
I2CMIF_BIT);
|
||||
pch_i2c_init(adap);
|
||||
return -EAGAIN;
|
||||
} else { /* wait-event timeout */
|
||||
pch_i2c_stop(adap);
|
||||
return -ETIME;
|
||||
}
|
||||
} else {
|
||||
/* 7 address bits + R/W bit */
|
||||
addr = (((addr) << 1) | (I2C_RD));
|
||||
|
@ -528,9 +625,23 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
|
|||
if (first)
|
||||
pch_i2c_start(adap);
|
||||
|
||||
if ((pch_i2c_wait_for_xfer_complete(adap) == 0) &&
|
||||
(pch_i2c_getack(adap) == 0)) {
|
||||
pch_dbg(adap, "return %d\n", 0);
|
||||
rtn = pch_i2c_wait_for_xfer_complete(adap);
|
||||
if (rtn == 0) {
|
||||
if (pch_i2c_getack(adap)) {
|
||||
pch_dbg(adap, "Receive NACK for slave address"
|
||||
"setting\n");
|
||||
return -EIO;
|
||||
}
|
||||
} else if (rtn == -EIO) { /* Arbitration Lost */
|
||||
pch_err(adap, "Lost Arbitration\n");
|
||||
pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMAL_BIT);
|
||||
pch_clrbit(adap->pch_base_address, PCH_I2CSR, I2CMIF_BIT);
|
||||
pch_i2c_init(adap);
|
||||
return -EAGAIN;
|
||||
} else { /* wait-event timeout */
|
||||
pch_i2c_stop(adap);
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
pch_i2c_stop(adap);
|
||||
|
@ -549,35 +660,46 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
|
|||
if (loop != 1)
|
||||
read_index++;
|
||||
|
||||
if (pch_i2c_wait_for_xfer_complete(adap) != 0) {
|
||||
rtn = pch_i2c_wait_for_xfer_complete(adap);
|
||||
if (rtn == 0) {
|
||||
if (pch_i2c_getack(adap)) {
|
||||
pch_dbg(adap, "Receive NACK for slave"
|
||||
"address setting\n");
|
||||
return -EIO;
|
||||
}
|
||||
} else { /* wait-event timeout */
|
||||
pch_i2c_stop(adap);
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
} /* end for */
|
||||
|
||||
pch_i2c_sendnack(adap);
|
||||
|
||||
buf[read_index] = ioread32(p + PCH_I2CDR);
|
||||
buf[read_index] = ioread32(p + PCH_I2CDR); /* Read final - 1 */
|
||||
|
||||
if (length != 1)
|
||||
read_index++;
|
||||
|
||||
if (pch_i2c_wait_for_xfer_complete(adap) == 0) {
|
||||
rtn = pch_i2c_wait_for_xfer_complete(adap);
|
||||
if (rtn == 0) {
|
||||
if (pch_i2c_getack(adap)) {
|
||||
pch_dbg(adap, "Receive NACK for slave"
|
||||
"address setting\n");
|
||||
return -EIO;
|
||||
}
|
||||
} else { /* wait-event timeout */
|
||||
pch_i2c_stop(adap);
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
if (last)
|
||||
pch_i2c_stop(adap);
|
||||
else
|
||||
pch_i2c_repstart(adap);
|
||||
|
||||
buf[read_index++] = ioread32(p + PCH_I2CDR);
|
||||
buf[read_index++] = ioread32(p + PCH_I2CDR); /* Read Final */
|
||||
count = read_index;
|
||||
} else {
|
||||
count = -ETIME;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
count = -ETIME;
|
||||
pch_i2c_stop(adap);
|
||||
}
|
||||
|
||||
return count;
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_i2c.h>
|
||||
|
||||
#include <mach/irqs.h>
|
||||
#include <mach/hardware.h>
|
||||
|
@ -125,6 +128,11 @@ struct imx_i2c_struct {
|
|||
unsigned int ifdr; /* IMX_I2C_IFDR */
|
||||
};
|
||||
|
||||
static const struct of_device_id i2c_imx_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx1-i2c", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
/** Functions for IMX I2C adapter driver ***************************************
|
||||
*******************************************************************************/
|
||||
|
||||
|
@ -466,10 +474,10 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct imx_i2c_struct *i2c_imx;
|
||||
struct resource *res;
|
||||
struct imxi2c_platform_data *pdata;
|
||||
struct imxi2c_platform_data *pdata = pdev->dev.platform_data;
|
||||
void __iomem *base;
|
||||
resource_size_t res_size;
|
||||
int irq;
|
||||
int irq, bitrate;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&pdev->dev, "<%s>\n", __func__);
|
||||
|
@ -485,19 +493,11 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
|
||||
if (pdata && pdata->init) {
|
||||
ret = pdata->init(&pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
res_size = resource_size(res);
|
||||
|
||||
if (!request_mem_region(res->start, res_size, DRIVER_NAME)) {
|
||||
ret = -EBUSY;
|
||||
goto fail0;
|
||||
dev_err(&pdev->dev, "request_mem_region failed\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
base = ioremap(res->start, res_size);
|
||||
|
@ -520,6 +520,7 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
|
|||
i2c_imx->adapter.algo = &i2c_imx_algo;
|
||||
i2c_imx->adapter.dev.parent = &pdev->dev;
|
||||
i2c_imx->adapter.nr = pdev->id;
|
||||
i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
|
||||
i2c_imx->irq = irq;
|
||||
i2c_imx->base = base;
|
||||
i2c_imx->res = res;
|
||||
|
@ -546,10 +547,12 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
|
|||
i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
|
||||
|
||||
/* Set up clock divider */
|
||||
if (pdata && pdata->bitrate)
|
||||
i2c_imx_set_clk(i2c_imx, pdata->bitrate);
|
||||
else
|
||||
i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE);
|
||||
bitrate = IMX_I2C_BIT_RATE;
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"clock-frequency", &bitrate);
|
||||
if (ret < 0 && pdata && pdata->bitrate)
|
||||
bitrate = pdata->bitrate;
|
||||
i2c_imx_set_clk(i2c_imx, bitrate);
|
||||
|
||||
/* Set up chip registers to defaults */
|
||||
writeb(0, i2c_imx->base + IMX_I2C_I2CR);
|
||||
|
@ -562,6 +565,8 @@ static int __init i2c_imx_probe(struct platform_device *pdev)
|
|||
goto fail5;
|
||||
}
|
||||
|
||||
of_i2c_register_devices(&i2c_imx->adapter);
|
||||
|
||||
/* Set up platform driver data */
|
||||
platform_set_drvdata(pdev, i2c_imx);
|
||||
|
||||
|
@ -586,16 +591,12 @@ fail2:
|
|||
iounmap(base);
|
||||
fail1:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
fail0:
|
||||
if (pdata && pdata->exit)
|
||||
pdata->exit(&pdev->dev);
|
||||
return ret; /* Return error number */
|
||||
}
|
||||
|
||||
static int __exit i2c_imx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
|
||||
struct imxi2c_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
/* remove adapter */
|
||||
dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
|
||||
|
@ -611,10 +612,6 @@ static int __exit i2c_imx_remove(struct platform_device *pdev)
|
|||
writeb(0, i2c_imx->base + IMX_I2C_I2CR);
|
||||
writeb(0, i2c_imx->base + IMX_I2C_I2SR);
|
||||
|
||||
/* Shut down hardware */
|
||||
if (pdata && pdata->exit)
|
||||
pdata->exit(&pdev->dev);
|
||||
|
||||
clk_put(i2c_imx->clk);
|
||||
|
||||
iounmap(i2c_imx->base);
|
||||
|
@ -628,6 +625,7 @@ static struct platform_driver i2c_imx_driver = {
|
|||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = i2c_imx_dt_ids,
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue