Merge branch 'i2c-embedded/for-3.4' of git://git.pengutronix.de/git/wsa/linux

Pull i2c embedded updates from Wolfram Sang:
 "Nothing special from i2c-embedded for this merge window.  Two new
  drivers, minor feature additions, bugfixes, cleanups.

  All patches have been in linux-next for some time, too."

* 'i2c-embedded/for-3.4' of git://git.pengutronix.de/git/wsa/linux:
  i2c-eg20t: Remove write-only variables
  i2c-eg20t: Rework pch_i2c_wait_for_bus_idle to reduce wait time
  i2c-s3c2410: Add stub runtime power management
  i2c-s3c2410: Convert to devm_kzalloc()
  i2c: add CSR SiRFprimaII on-chip I2C controllers driver
  i2c: tegra: Remove unnecessary write to INT_STATUS
  i2c: imx: fix imx driver to work though signal is pending
  i2c: designware: dw_i2c_init_driver as subsys initcall
  misc: at24: describe platform_data with kernel_doc
  i2c: Move I2C_EG20T option to the right place.
  i2c: Support for Netlogic XLR/XLS I2C controller.
  i2c: mpc: Add support for SMBUS_READ_BLOCK_DATA
  i2c: versatile: Add Device Tree support
This commit is contained in:
Linus Torvalds 2012-03-23 14:12:04 -07:00
commit fc2bb8d1cd
13 changed files with 912 additions and 66 deletions

View File

@ -0,0 +1,19 @@
I2C for SiRFprimaII platforms
Required properties :
- compatible : Must be "sirf,prima2-i2c"
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: interrupt number to the cpu.
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 :
i2c0: i2c@b00e0000 {
compatible = "sirf,prima2-i2c";
reg = <0xb00e0000 0x10000>;
interrupts = <24>;
};

View File

@ -369,6 +369,21 @@ config I2C_DESIGNWARE_PCI
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called i2c-designware-pci. will be called i2c-designware-pci.
config I2C_EG20T
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
depends on PCI
help
This driver is for PCH(Platform controller Hub) I2C of EG20T which
is an IOH(Input/Output Hub) for x86 embedded processor.
This driver can access PCH I2C bus device.
This driver also can be used for LAPIS Semiconductor IOH(Input/
Output Hub), ML7213, ML7223 and ML7831.
ML7213 IOH is for IVI(In-Vehicle Infotainment) use, ML7223 IOH is
for MP(Media Phone) use and ML7831 IOH is for general purpose use.
ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
config I2C_GPIO config I2C_GPIO
tristate "GPIO-based bitbanging I2C" tristate "GPIO-based bitbanging I2C"
depends on GENERIC_GPIO depends on GENERIC_GPIO
@ -630,6 +645,16 @@ config I2C_SIMTEC
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called i2c-simtec. will be called i2c-simtec.
config I2C_SIRF
tristate "CSR SiRFprimaII I2C interface"
depends on ARCH_PRIMA2
help
If you say yes to this option, support will be included for the
CSR SiRFprimaII I2C interface.
This driver can also be built as a module. If so, the module
will be called i2c-sirf.
config I2C_STU300 config I2C_STU300
tristate "ST Microelectronics DDC I2C interface" tristate "ST Microelectronics DDC I2C interface"
depends on MACH_U300 depends on MACH_U300
@ -681,20 +706,15 @@ config I2C_XILINX
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called xilinx_i2c. will be called xilinx_i2c.
config I2C_EG20T config I2C_XLR
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C" tristate "XLR I2C support"
depends on PCI depends on CPU_XLR
help help
This driver is for PCH(Platform controller Hub) I2C of EG20T which This driver enables support for the on-chip I2C interface of
is an IOH(Input/Output Hub) for x86 embedded processor. the Netlogic XLR/XLS MIPS processors.
This driver can access PCH I2C bus device.
This driver also can be used for LAPIS Semiconductor IOH(Input/ This driver can also be built as a module. If so, the module
Output Hub), ML7213, ML7223 and ML7831. will be called i2c-xlr.
ML7213 IOH is for IVI(In-Vehicle Infotainment) use, ML7223 IOH is
for MP(Media Phone) use and ML7831 IOH is for general purpose use.
ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
comment "External I2C/SMBus adapter drivers" comment "External I2C/SMBus adapter drivers"

View File

@ -37,6 +37,7 @@ obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
i2c-designware-platform-objs := i2c-designware-platdrv.o i2c-designware-core.o i2c-designware-platform-objs := i2c-designware-platdrv.o i2c-designware-core.o
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
i2c-designware-pci-objs := i2c-designware-pcidrv.o i2c-designware-core.o i2c-designware-pci-objs := i2c-designware-pcidrv.o i2c-designware-core.o
obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
@ -63,12 +64,13 @@ obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
# External I2C/SMBus adapter drivers # External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o

View File

@ -214,7 +214,7 @@ static int __init dw_i2c_init_driver(void)
{ {
return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe); return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe);
} }
module_init(dw_i2c_init_driver); subsys_initcall(dw_i2c_init_driver);
static void __exit dw_i2c_exit_driver(void) static void __exit dw_i2c_exit_driver(void)
{ {

View File

@ -271,30 +271,36 @@ static inline bool ktime_lt(const ktime_t cmp1, const ktime_t cmp2)
/** /**
* pch_i2c_wait_for_bus_idle() - check the status of bus. * pch_i2c_wait_for_bus_idle() - check the status of bus.
* @adap: Pointer to struct i2c_algo_pch_data. * @adap: Pointer to struct i2c_algo_pch_data.
* @timeout: waiting time counter (us). * @timeout: waiting time counter (ms).
*/ */
static s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap, static s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
s32 timeout) s32 timeout)
{ {
void __iomem *p = adap->pch_base_address; void __iomem *p = adap->pch_base_address;
ktime_t ns_val; int schedule = 0;
unsigned long end = jiffies + msecs_to_jiffies(timeout);
if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0) while (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) {
return 0; if (time_after(jiffies, end)) {
pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR));
pch_err(adap, "%s: Timeout Error.return%d\n",
__func__, -ETIME);
pch_i2c_init(adap);
/* MAX timeout value is timeout*1000*1000nsec */ return -ETIME;
ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000); }
do {
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 (!schedule)
pch_err(adap, "%s: Timeout Error.return%d\n", __func__, -ETIME); /* Retry after some usecs */
pch_i2c_init(adap); udelay(5);
else
/* Wait a bit more without consuming CPU */
usleep_range(20, 1000);
return -ETIME; schedule = 1;
}
return 0;
} }
/** /**
@ -778,8 +784,6 @@ static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *pmsg; struct i2c_msg *pmsg;
u32 i = 0; u32 i = 0;
u32 status; u32 status;
u32 msglen;
u32 subaddrlen;
s32 ret; s32 ret;
struct i2c_algo_pch_data *adap = i2c_adap->algo_data; struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
@ -804,12 +808,6 @@ static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap,
status = pmsg->flags; status = pmsg->flags;
pch_dbg(adap, pch_dbg(adap,
"After invoking I2C_MODE_SEL :flag= 0x%x\n", status); "After invoking I2C_MODE_SEL :flag= 0x%x\n", status);
/* calculate sub address length and message length */
/* these are applicable only for buffer mode */
subaddrlen = pmsg->buf[0];
/* calculate actual message length excluding
* the sub address fields */
msglen = (pmsg->len) - (subaddrlen + 1);
if ((status & (I2C_M_RD)) != false) { if ((status & (I2C_M_RD)) != false) {
ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num), ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num),

View File

@ -149,11 +149,6 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy)
break; break;
if (!for_busy && !(temp & I2SR_IBB)) if (!for_busy && !(temp & I2SR_IBB))
break; break;
if (signal_pending(current)) {
dev_dbg(&i2c_imx->adapter.dev,
"<%s> I2C Interrupted\n", __func__);
return -EINTR;
}
if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
dev_dbg(&i2c_imx->adapter.dev, dev_dbg(&i2c_imx->adapter.dev,
"<%s> I2C bus is busy\n", __func__); "<%s> I2C bus is busy\n", __func__);

View File

@ -454,7 +454,7 @@ static int mpc_write(struct mpc_i2c *i2c, int target,
} }
static int mpc_read(struct mpc_i2c *i2c, int target, static int mpc_read(struct mpc_i2c *i2c, int target,
u8 *data, int length, int restart) u8 *data, int length, int restart, bool recv_len)
{ {
unsigned timeout = i2c->adap.timeout; unsigned timeout = i2c->adap.timeout;
int i, result; int i, result;
@ -470,7 +470,7 @@ static int mpc_read(struct mpc_i2c *i2c, int target,
return result; return result;
if (length) { if (length) {
if (length == 1) if (length == 1 && !recv_len)
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
else else
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA); writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA);
@ -479,17 +479,46 @@ static int mpc_read(struct mpc_i2c *i2c, int target,
} }
for (i = 0; i < length; i++) { for (i = 0; i < length; i++) {
u8 byte;
result = i2c_wait(i2c, timeout, 0); result = i2c_wait(i2c, timeout, 0);
if (result < 0) if (result < 0)
return result; return result;
/* Generate txack on next to last byte */ /*
if (i == length - 2) * For block reads, we have to know the total length (1st byte)
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); * before we can determine if we are done.
/* Do not generate stop on last byte */ */
if (i == length - 1) if (i || !recv_len) {
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX); /* Generate txack on next to last byte */
data[i] = readb(i2c->base + MPC_I2C_DR); if (i == length - 2)
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
| CCR_TXAK);
/* Do not generate stop on last byte */
if (i == length - 1)
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
| CCR_MTX);
}
byte = readb(i2c->base + MPC_I2C_DR);
/*
* Adjust length if first received byte is length.
* The length is 1 length byte plus actually data length
*/
if (i == 0 && recv_len) {
if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX)
return -EPROTO;
length += byte;
/*
* For block reads, generate txack here if data length
* is 1 byte (total length is 2 bytes).
*/
if (length == 2)
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
| CCR_TXAK);
}
data[i] = byte;
} }
return length; return length;
@ -532,12 +561,17 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
"Doing %s %d bytes to 0x%02x - %d of %d messages\n", "Doing %s %d bytes to 0x%02x - %d of %d messages\n",
pmsg->flags & I2C_M_RD ? "read" : "write", pmsg->flags & I2C_M_RD ? "read" : "write",
pmsg->len, pmsg->addr, i + 1, num); pmsg->len, pmsg->addr, i + 1, num);
if (pmsg->flags & I2C_M_RD) if (pmsg->flags & I2C_M_RD) {
ret = bool recv_len = pmsg->flags & I2C_M_RECV_LEN;
mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
else ret = mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i,
recv_len);
if (recv_len && ret > 0)
pmsg->len = ret;
} else {
ret = ret =
mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i); mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
}
} }
mpc_i2c_stop(i2c); mpc_i2c_stop(i2c);
return (ret < 0) ? ret : num; return (ret < 0) ? ret : num;
@ -545,7 +579,8 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
static u32 mpc_functionality(struct i2c_adapter *adap) static u32 mpc_functionality(struct i2c_adapter *adap)
{ {
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
| I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
} }
static const struct i2c_algorithm mpc_algo = { static const struct i2c_algorithm mpc_algo = {

View File

@ -31,6 +31,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -564,6 +565,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
int retry; int retry;
int ret; int ret;
pm_runtime_get_sync(&adap->dev);
clk_enable(i2c->clk); clk_enable(i2c->clk);
for (retry = 0; retry < adap->retries; retry++) { for (retry = 0; retry < adap->retries; retry++) {
@ -572,6 +574,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
if (ret != -EAGAIN) { if (ret != -EAGAIN) {
clk_disable(i2c->clk); clk_disable(i2c->clk);
pm_runtime_put_sync(&adap->dev);
return ret; return ret;
} }
@ -581,6 +584,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
} }
clk_disable(i2c->clk); clk_disable(i2c->clk);
pm_runtime_put_sync(&adap->dev);
return -EREMOTEIO; return -EREMOTEIO;
} }
@ -890,7 +894,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
} }
} }
i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL); i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);
if (!i2c) { if (!i2c) {
dev_err(&pdev->dev, "no memory for state\n"); dev_err(&pdev->dev, "no memory for state\n");
return -ENOMEM; return -ENOMEM;
@ -1013,6 +1017,9 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
of_i2c_register_devices(&i2c->adap); of_i2c_register_devices(&i2c->adap);
platform_set_drvdata(pdev, i2c); platform_set_drvdata(pdev, i2c);
pm_runtime_enable(&pdev->dev);
pm_runtime_enable(&i2c->adap.dev);
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev)); dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
clk_disable(i2c->clk); clk_disable(i2c->clk);
return 0; return 0;
@ -1035,7 +1042,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
clk_put(i2c->clk); clk_put(i2c->clk);
err_noclk: err_noclk:
kfree(i2c);
return ret; return ret;
} }
@ -1048,6 +1054,9 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
{ {
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
pm_runtime_disable(&i2c->adap.dev);
pm_runtime_disable(&pdev->dev);
s3c24xx_i2c_deregister_cpufreq(i2c); s3c24xx_i2c_deregister_cpufreq(i2c);
i2c_del_adapter(&i2c->adap); i2c_del_adapter(&i2c->adap);
@ -1061,7 +1070,6 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
release_resource(i2c->ioarea); release_resource(i2c->ioarea);
s3c24xx_i2c_dt_gpio_free(i2c); s3c24xx_i2c_dt_gpio_free(i2c);
kfree(i2c->ioarea); kfree(i2c->ioarea);
kfree(i2c);
return 0; return 0;
} }

View File

@ -0,0 +1,459 @@
/*
* I2C bus driver for CSR SiRFprimaII
*
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
*
* Licensed under GPLv2 or later.
*/
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#define SIRFSOC_I2C_CLK_CTRL 0x00
#define SIRFSOC_I2C_STATUS 0x0C
#define SIRFSOC_I2C_CTRL 0x10
#define SIRFSOC_I2C_IO_CTRL 0x14
#define SIRFSOC_I2C_SDA_DELAY 0x18
#define SIRFSOC_I2C_CMD_START 0x1C
#define SIRFSOC_I2C_CMD_BUF 0x30
#define SIRFSOC_I2C_DATA_BUF 0x80
#define SIRFSOC_I2C_CMD_BUF_MAX 16
#define SIRFSOC_I2C_DATA_BUF_MAX 16
#define SIRFSOC_I2C_CMD(x) (SIRFSOC_I2C_CMD_BUF + (x)*0x04)
#define SIRFSOC_I2C_DATA_MASK(x) (0xFF<<(((x)&3)*8))
#define SIRFSOC_I2C_DATA_SHIFT(x) (((x)&3)*8)
#define SIRFSOC_I2C_DIV_MASK (0xFFFF)
/* I2C status flags */
#define SIRFSOC_I2C_STAT_BUSY BIT(0)
#define SIRFSOC_I2C_STAT_TIP BIT(1)
#define SIRFSOC_I2C_STAT_NACK BIT(2)
#define SIRFSOC_I2C_STAT_TR_INT BIT(4)
#define SIRFSOC_I2C_STAT_STOP BIT(6)
#define SIRFSOC_I2C_STAT_CMD_DONE BIT(8)
#define SIRFSOC_I2C_STAT_ERR BIT(9)
#define SIRFSOC_I2C_CMD_INDEX (0x1F<<16)
/* I2C control flags */
#define SIRFSOC_I2C_RESET BIT(0)
#define SIRFSOC_I2C_CORE_EN BIT(1)
#define SIRFSOC_I2C_MASTER_MODE BIT(2)
#define SIRFSOC_I2C_CMD_DONE_EN BIT(11)
#define SIRFSOC_I2C_ERR_INT_EN BIT(12)
#define SIRFSOC_I2C_SDA_DELAY_MASK (0xFF)
#define SIRFSOC_I2C_SCLF_FILTER (3<<8)
#define SIRFSOC_I2C_START_CMD BIT(0)
#define SIRFSOC_I2C_CMD_RP(x) ((x)&0x7)
#define SIRFSOC_I2C_NACK BIT(3)
#define SIRFSOC_I2C_WRITE BIT(4)
#define SIRFSOC_I2C_READ BIT(5)
#define SIRFSOC_I2C_STOP BIT(6)
#define SIRFSOC_I2C_START BIT(7)
#define SIRFSOC_I2C_DEFAULT_SPEED 100000
struct sirfsoc_i2c {
void __iomem *base;
struct clk *clk;
u32 cmd_ptr; /* Current position in CMD buffer */
u8 *buf; /* Buffer passed by user */
u32 msg_len; /* Message length */
u32 finished_len; /* number of bytes read/written */
u32 read_cmd_len; /* number of read cmd sent */
int msg_read; /* 1 indicates a read message */
int err_status; /* 1 indicates an error on bus */
u32 sda_delay; /* For suspend/resume */
u32 clk_div;
int last; /* Last message in transfer, STOP cmd can be sent */
struct completion done; /* indicates completion of message transfer */
struct i2c_adapter adapter;
};
static void i2c_sirfsoc_read_data(struct sirfsoc_i2c *siic)
{
u32 data = 0;
int i;
for (i = 0; i < siic->read_cmd_len; i++) {
if (!(i & 0x3))
data = readl(siic->base + SIRFSOC_I2C_DATA_BUF + i);
siic->buf[siic->finished_len++] =
(u8)((data & SIRFSOC_I2C_DATA_MASK(i)) >>
SIRFSOC_I2C_DATA_SHIFT(i));
}
}
static void i2c_sirfsoc_queue_cmd(struct sirfsoc_i2c *siic)
{
u32 regval;
int i = 0;
if (siic->msg_read) {
while (((siic->finished_len + i) < siic->msg_len)
&& (siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX)) {
regval = SIRFSOC_I2C_READ | SIRFSOC_I2C_CMD_RP(0);
if (((siic->finished_len + i) ==
(siic->msg_len - 1)) && siic->last)
regval |= SIRFSOC_I2C_STOP | SIRFSOC_I2C_NACK;
writel(regval,
siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
i++;
}
siic->read_cmd_len = i;
} else {
while ((siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX - 1)
&& (siic->finished_len < siic->msg_len)) {
regval = SIRFSOC_I2C_WRITE | SIRFSOC_I2C_CMD_RP(0);
if ((siic->finished_len == (siic->msg_len - 1))
&& siic->last)
regval |= SIRFSOC_I2C_STOP;
writel(regval,
siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
writel(siic->buf[siic->finished_len++],
siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
}
}
siic->cmd_ptr = 0;
/* Trigger the transfer */
writel(SIRFSOC_I2C_START_CMD, siic->base + SIRFSOC_I2C_CMD_START);
}
static irqreturn_t i2c_sirfsoc_irq(int irq, void *dev_id)
{
struct sirfsoc_i2c *siic = (struct sirfsoc_i2c *)dev_id;
u32 i2c_stat = readl(siic->base + SIRFSOC_I2C_STATUS);
if (i2c_stat & SIRFSOC_I2C_STAT_ERR) {
/* Error conditions */
siic->err_status = 1;
writel(SIRFSOC_I2C_STAT_ERR, siic->base + SIRFSOC_I2C_STATUS);
if (i2c_stat & SIRFSOC_I2C_STAT_NACK)
dev_err(&siic->adapter.dev, "ACK not received\n");
else
dev_err(&siic->adapter.dev, "I2C error\n");
complete(&siic->done);
} else if (i2c_stat & SIRFSOC_I2C_STAT_CMD_DONE) {
/* CMD buffer execution complete */
if (siic->msg_read)
i2c_sirfsoc_read_data(siic);
if (siic->finished_len == siic->msg_len)
complete(&siic->done);
else /* Fill a new CMD buffer for left data */
i2c_sirfsoc_queue_cmd(siic);
writel(SIRFSOC_I2C_STAT_CMD_DONE, siic->base + SIRFSOC_I2C_STATUS);
}
return IRQ_HANDLED;
}
static void i2c_sirfsoc_set_address(struct sirfsoc_i2c *siic,
struct i2c_msg *msg)
{
unsigned char addr;
u32 regval = SIRFSOC_I2C_START | SIRFSOC_I2C_CMD_RP(0) | SIRFSOC_I2C_WRITE;
/* no data and last message -> add STOP */
if (siic->last && (msg->len == 0))
regval |= SIRFSOC_I2C_STOP;
writel(regval, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
addr = msg->addr << 1; /* Generate address */
if (msg->flags & I2C_M_RD)
addr |= 1;
writel(addr, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
}
static int i2c_sirfsoc_xfer_msg(struct sirfsoc_i2c *siic, struct i2c_msg *msg)
{
u32 regval = readl(siic->base + SIRFSOC_I2C_CTRL);
/* timeout waiting for the xfer to finish or fail */
int timeout = msecs_to_jiffies((msg->len + 1) * 50);
int ret = 0;
i2c_sirfsoc_set_address(siic, msg);
writel(regval | SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN,
siic->base + SIRFSOC_I2C_CTRL);
i2c_sirfsoc_queue_cmd(siic);
if (wait_for_completion_timeout(&siic->done, timeout) == 0) {
siic->err_status = 1;
dev_err(&siic->adapter.dev, "Transfer timeout\n");
}
writel(regval & ~(SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN),
siic->base + SIRFSOC_I2C_CTRL);
writel(0, siic->base + SIRFSOC_I2C_CMD_START);
if (siic->err_status) {
writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET,
siic->base + SIRFSOC_I2C_CTRL);
while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
cpu_relax();
ret = -EIO;
}
return ret;
}
static u32 i2c_sirfsoc_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static int i2c_sirfsoc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)
{
struct sirfsoc_i2c *siic = adap->algo_data;
int i, ret;
clk_enable(siic->clk);
for (i = 0; i < num; i++) {
siic->buf = msgs[i].buf;
siic->msg_len = msgs[i].len;
siic->msg_read = !!(msgs[i].flags & I2C_M_RD);
siic->err_status = 0;
siic->cmd_ptr = 0;
siic->finished_len = 0;
siic->last = (i == (num - 1));
ret = i2c_sirfsoc_xfer_msg(siic, &msgs[i]);
if (ret) {
clk_disable(siic->clk);
return ret;
}
}
clk_disable(siic->clk);
return num;
}
/* I2C algorithms associated with this master controller driver */
static const struct i2c_algorithm i2c_sirfsoc_algo = {
.master_xfer = i2c_sirfsoc_xfer,
.functionality = i2c_sirfsoc_func,
};
static int __devinit i2c_sirfsoc_probe(struct platform_device *pdev)
{
struct sirfsoc_i2c *siic;
struct i2c_adapter *adap;
struct resource *mem_res;
struct clk *clk;
int bitrate;
int ctrl_speed;
int irq;
int err;
u32 regval;
clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
err = PTR_ERR(clk);
dev_err(&pdev->dev, "Clock get failed\n");
goto err_get_clk;
}
err = clk_prepare(clk);
if (err) {
dev_err(&pdev->dev, "Clock prepare failed\n");
goto err_clk_prep;
}
err = clk_enable(clk);
if (err) {
dev_err(&pdev->dev, "Clock enable failed\n");
goto err_clk_en;
}
ctrl_speed = clk_get_rate(clk);
siic = devm_kzalloc(&pdev->dev, sizeof(*siic), GFP_KERNEL);
if (!siic) {
dev_err(&pdev->dev, "Can't allocate driver data\n");
err = -ENOMEM;
goto out;
}
adap = &siic->adapter;
adap->class = I2C_CLASS_HWMON;
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem_res == NULL) {
dev_err(&pdev->dev, "Unable to get MEM resource\n");
err = -EINVAL;
goto out;
}
siic->base = devm_request_and_ioremap(&pdev->dev, mem_res);
if (siic->base == NULL) {
dev_err(&pdev->dev, "IO remap failed!\n");
err = -ENOMEM;
goto out;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
err = irq;
goto out;
}
err = devm_request_irq(&pdev->dev, irq, i2c_sirfsoc_irq, 0,
dev_name(&pdev->dev), siic);
if (err)
goto out;
adap->algo = &i2c_sirfsoc_algo;
adap->algo_data = siic;
adap->dev.parent = &pdev->dev;
adap->nr = pdev->id;
strlcpy(adap->name, "sirfsoc-i2c", sizeof(adap->name));
platform_set_drvdata(pdev, adap);
init_completion(&siic->done);
/* Controller Initalisation */
writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
cpu_relax();
writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE,
siic->base + SIRFSOC_I2C_CTRL);
siic->clk = clk;
err = of_property_read_u32(pdev->dev.of_node,
"clock-frequency", &bitrate);
if (err < 0)
bitrate = SIRFSOC_I2C_DEFAULT_SPEED;
if (bitrate < 100000)
regval =
(2 * ctrl_speed) / (2 * bitrate * 11);
else
regval = ctrl_speed / (bitrate * 5);
writel(regval, siic->base + SIRFSOC_I2C_CLK_CTRL);
if (regval > 0xFF)
writel(0xFF, siic->base + SIRFSOC_I2C_SDA_DELAY);
else
writel(regval, siic->base + SIRFSOC_I2C_SDA_DELAY);
err = i2c_add_numbered_adapter(adap);
if (err < 0) {
dev_err(&pdev->dev, "Can't add new i2c adapter\n");
goto out;
}
clk_disable(clk);
dev_info(&pdev->dev, " I2C adapter ready to operate\n");
return 0;
out:
clk_disable(clk);
err_clk_en:
clk_unprepare(clk);
err_clk_prep:
clk_put(clk);
err_get_clk:
return err;
}
static int __devexit i2c_sirfsoc_remove(struct platform_device *pdev)
{
struct i2c_adapter *adapter = platform_get_drvdata(pdev);
struct sirfsoc_i2c *siic = adapter->algo_data;
writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
i2c_del_adapter(adapter);
clk_unprepare(siic->clk);
clk_put(siic->clk);
return 0;
}
#ifdef CONFIG_PM
static int i2c_sirfsoc_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct i2c_adapter *adapter = platform_get_drvdata(pdev);
struct sirfsoc_i2c *siic = adapter->algo_data;
clk_enable(siic->clk);
siic->sda_delay = readl(siic->base + SIRFSOC_I2C_SDA_DELAY);
siic->clk_div = readl(siic->base + SIRFSOC_I2C_CLK_CTRL);
clk_disable(siic->clk);
return 0;
}
static int i2c_sirfsoc_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct i2c_adapter *adapter = platform_get_drvdata(pdev);
struct sirfsoc_i2c *siic = adapter->algo_data;
clk_enable(siic->clk);
writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE,
siic->base + SIRFSOC_I2C_CTRL);
writel(siic->clk_div, siic->base + SIRFSOC_I2C_CLK_CTRL);
writel(siic->sda_delay, siic->base + SIRFSOC_I2C_SDA_DELAY);
clk_disable(siic->clk);
return 0;
}
static const struct dev_pm_ops i2c_sirfsoc_pm_ops = {
.suspend = i2c_sirfsoc_suspend,
.resume = i2c_sirfsoc_resume,
};
#endif
static const struct of_device_id sirfsoc_i2c_of_match[] __devinitconst = {
{ .compatible = "sirf,prima2-i2c", },
{},
};
MODULE_DEVICE_TABLE(of, sirfsoc_i2c_of_match);
static struct platform_driver i2c_sirfsoc_driver = {
.driver = {
.name = "sirfsoc_i2c",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &i2c_sirfsoc_pm_ops,
#endif
.of_match_table = sirfsoc_i2c_of_match,
},
.probe = i2c_sirfsoc_probe,
.remove = __devexit_p(i2c_sirfsoc_remove),
};
module_platform_driver(i2c_sirfsoc_driver);
MODULE_DESCRIPTION("SiRF SoC I2C master controller driver");
MODULE_AUTHOR("Zhiwu Song <Zhiwu.Song@csr.com>, "
"Xiangzhen Ye <Xiangzhen.Ye@csr.com>");
MODULE_LICENSE("GPL v2");

View File

@ -457,7 +457,6 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
int ret; int ret;
tegra_i2c_flush_fifos(i2c_dev); tegra_i2c_flush_fifos(i2c_dev);
i2c_writel(i2c_dev, 0xFF, I2C_INT_STATUS);
if (msg->len == 0) if (msg->len == 0)
return -EINVAL; return -EINVAL;

View File

@ -16,6 +16,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of_i2c.h>
#define I2C_CONTROL 0x00 #define I2C_CONTROL 0x00
#define I2C_CONTROLS 0x00 #define I2C_CONTROLS 0x00
@ -99,6 +100,7 @@ static int i2c_versatile_probe(struct platform_device *dev)
strlcpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name)); strlcpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name));
i2c->adap.algo_data = &i2c->algo; i2c->adap.algo_data = &i2c->algo;
i2c->adap.dev.parent = &dev->dev; i2c->adap.dev.parent = &dev->dev;
i2c->adap.dev.of_node = dev->dev.of_node;
i2c->algo = i2c_versatile_algo; i2c->algo = i2c_versatile_algo;
i2c->algo.data = i2c; i2c->algo.data = i2c;
@ -111,6 +113,7 @@ static int i2c_versatile_probe(struct platform_device *dev)
ret = i2c_bit_add_bus(&i2c->adap); ret = i2c_bit_add_bus(&i2c->adap);
if (ret >= 0) { if (ret >= 0) {
platform_set_drvdata(dev, i2c); platform_set_drvdata(dev, i2c);
of_i2c_register_devices(&i2c->adap);
return 0; return 0;
} }
@ -133,12 +136,19 @@ static int i2c_versatile_remove(struct platform_device *dev)
return 0; return 0;
} }
static const struct of_device_id i2c_versatile_match[] = {
{ .compatible = "arm,versatile-i2c", },
{},
};
MODULE_DEVICE_TABLE(of, i2c_versatile_match);
static struct platform_driver i2c_versatile_driver = { static struct platform_driver i2c_versatile_driver = {
.probe = i2c_versatile_probe, .probe = i2c_versatile_probe,
.remove = i2c_versatile_remove, .remove = i2c_versatile_remove,
.driver = { .driver = {
.name = "versatile-i2c", .name = "versatile-i2c",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = i2c_versatile_match,
}, },
}; };

View File

@ -0,0 +1,278 @@
/*
* Copyright 2011, Netlogic Microsystems Inc.
* Copyright 2004, Matt Porter <mporter@kernel.crashing.org>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/platform_device.h>
/* XLR I2C REGISTERS */
#define XLR_I2C_CFG 0x00
#define XLR_I2C_CLKDIV 0x01
#define XLR_I2C_DEVADDR 0x02
#define XLR_I2C_ADDR 0x03
#define XLR_I2C_DATAOUT 0x04
#define XLR_I2C_DATAIN 0x05
#define XLR_I2C_STATUS 0x06
#define XLR_I2C_STARTXFR 0x07
#define XLR_I2C_BYTECNT 0x08
#define XLR_I2C_HDSTATIM 0x09
/* XLR I2C REGISTERS FLAGS */
#define XLR_I2C_BUS_BUSY 0x01
#define XLR_I2C_SDOEMPTY 0x02
#define XLR_I2C_RXRDY 0x04
#define XLR_I2C_ACK_ERR 0x08
#define XLR_I2C_ARB_STARTERR 0x30
/* Register Values */
#define XLR_I2C_CFG_ADDR 0xF8
#define XLR_I2C_CFG_NOADDR 0xFA
#define XLR_I2C_STARTXFR_ND 0x02 /* No Data */
#define XLR_I2C_STARTXFR_RD 0x01 /* Read */
#define XLR_I2C_STARTXFR_WR 0x00 /* Write */
#define XLR_I2C_TIMEOUT 10 /* timeout per byte in msec */
/*
* On XLR/XLS, we need to use __raw_ IO to read the I2C registers
* because they are in the big-endian MMIO area on the SoC.
*
* The readl/writel implementation on XLR/XLS byteswaps, because
* those are for its little-endian PCI space (see arch/mips/Kconfig).
*/
static inline void xlr_i2c_wreg(u32 __iomem *base, unsigned int reg, u32 val)
{
__raw_writel(val, base + reg);
}
static inline u32 xlr_i2c_rdreg(u32 __iomem *base, unsigned int reg)
{
return __raw_readl(base + reg);
}
struct xlr_i2c_private {
struct i2c_adapter adap;
u32 __iomem *iobase;
};
static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
u8 *buf, u16 addr)
{
struct i2c_adapter *adap = &priv->adap;
unsigned long timeout, stoptime, checktime;
u32 i2c_status;
int pos, timedout;
u8 offset, byte;
offset = buf[0];
xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_ADDR);
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
stoptime = jiffies + timeout;
timedout = 0;
pos = 1;
retry:
if (len == 1) {
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
XLR_I2C_STARTXFR_ND);
} else {
xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos]);
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
XLR_I2C_STARTXFR_WR);
}
while (!timedout) {
checktime = jiffies;
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
if (i2c_status & XLR_I2C_SDOEMPTY) {
pos++;
/* need to do a empty dataout after the last byte */
byte = (pos < len) ? buf[pos] : 0;
xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, byte);
/* reset timeout on successful xmit */
stoptime = jiffies + timeout;
}
timedout = time_after(checktime, stoptime);
if (i2c_status & XLR_I2C_ARB_STARTERR) {
if (timedout)
break;
goto retry;
}
if (i2c_status & XLR_I2C_ACK_ERR)
return -EIO;
if ((i2c_status & XLR_I2C_BUS_BUSY) == 0 && pos >= len)
return 0;
}
dev_err(&adap->dev, "I2C transmit timeout\n");
return -ETIMEDOUT;
}
static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
{
struct i2c_adapter *adap = &priv->adap;
u32 i2c_status;
unsigned long timeout, stoptime, checktime;
int nbytes, timedout;
u8 byte;
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_NOADDR);
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
stoptime = jiffies + timeout;
timedout = 0;
nbytes = 0;
retry:
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, XLR_I2C_STARTXFR_RD);
while (!timedout) {
checktime = jiffies;
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
if (i2c_status & XLR_I2C_RXRDY) {
if (nbytes > len)
return -EIO; /* should not happen */
/* we need to do a dummy datain when nbytes == len */
byte = xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
if (nbytes < len)
buf[nbytes] = byte;
nbytes++;
/* reset timeout on successful read */
stoptime = jiffies + timeout;
}
timedout = time_after(checktime, stoptime);
if (i2c_status & XLR_I2C_ARB_STARTERR) {
if (timedout)
break;
goto retry;
}
if (i2c_status & XLR_I2C_ACK_ERR)
return -EIO;
if ((i2c_status & XLR_I2C_BUS_BUSY) == 0)
return 0;
}
dev_err(&adap->dev, "I2C receive timeout\n");
return -ETIMEDOUT;
}
static int xlr_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct i2c_msg *msg;
int i;
int ret = 0;
struct xlr_i2c_private *priv = i2c_get_adapdata(adap);
for (i = 0; ret == 0 && i < num; i++) {
msg = &msgs[i];
if (msg->flags & I2C_M_RD)
ret = xlr_i2c_rx(priv, msg->len, &msg->buf[0],
msg->addr);
else
ret = xlr_i2c_tx(priv, msg->len, &msg->buf[0],
msg->addr);
}
return (ret != 0) ? ret : num;
}
static u32 xlr_func(struct i2c_adapter *adap)
{
/* Emulate SMBUS over I2C */
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
}
static struct i2c_algorithm xlr_i2c_algo = {
.master_xfer = xlr_i2c_xfer,
.functionality = xlr_func,
};
static int __devinit xlr_i2c_probe(struct platform_device *pdev)
{
struct xlr_i2c_private *priv;
struct resource *res;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->iobase = devm_request_and_ioremap(&pdev->dev, res);
if (!priv->iobase) {
dev_err(&pdev->dev, "devm_request_and_ioremap failed\n");
return -EBUSY;
}
priv->adap.dev.parent = &pdev->dev;
priv->adap.owner = THIS_MODULE;
priv->adap.algo_data = priv;
priv->adap.algo = &xlr_i2c_algo;
priv->adap.nr = pdev->id;
priv->adap.class = I2C_CLASS_HWMON;
snprintf(priv->adap.name, sizeof(priv->adap.name), "xlr-i2c");
i2c_set_adapdata(&priv->adap, priv);
ret = i2c_add_numbered_adapter(&priv->adap);
if (ret < 0) {
dev_err(&priv->adap.dev, "Failed to add i2c bus.\n");
return ret;
}
platform_set_drvdata(pdev, priv);
dev_info(&priv->adap.dev, "Added I2C Bus.\n");
return 0;
}
static int __devexit xlr_i2c_remove(struct platform_device *pdev)
{
struct xlr_i2c_private *priv;
priv = platform_get_drvdata(pdev);
i2c_del_adapter(&priv->adap);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver xlr_i2c_driver = {
.probe = xlr_i2c_probe,
.remove = __devexit_p(xlr_i2c_remove),
.driver = {
.name = "xlr-i2cbus",
.owner = THIS_MODULE,
},
};
module_platform_driver(xlr_i2c_driver);
MODULE_AUTHOR("Ganesan Ramalingam <ganesanr@netlogicmicro.com>");
MODULE_DESCRIPTION("XLR/XLS SoC I2C Controller driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:xlr-i2cbus");

View File

@ -1,19 +1,42 @@
/*
* at24.h - platform_data for the at24 (generic eeprom) driver
* (C) Copyright 2008 by Pengutronix
* (C) Copyright 2012 by Wolfram Sang
* same license as the driver
*/
#ifndef _LINUX_AT24_H #ifndef _LINUX_AT24_H
#define _LINUX_AT24_H #define _LINUX_AT24_H
#include <linux/types.h> #include <linux/types.h>
#include <linux/memory.h> #include <linux/memory.h>
/* /**
* As seen through Linux I2C, differences between the most common types of I2C * struct at24_platform_data - data to set up at24 (generic eeprom) driver
* memory include: * @byte_len: size of eeprom in byte
* - How much memory is available (usually specified in bit)? * @page_size: number of byte which can be written in one go
* - What write page size does it support? * @flags: tunable options, check AT24_FLAG_* defines
* - Special flags (16 bit addresses, read_only, world readable...)? * @setup: an optional callback invoked after eeprom is probed; enables kernel
code to access eeprom via memory_accessor, see example
* @context: optional parameter passed to setup()
* *
* If you set up a custom eeprom type, please double-check the parameters. * If you set up a custom eeprom type, please double-check the parameters.
* Especially page_size needs extra care, as you risk data loss if your value * Especially page_size needs extra care, as you risk data loss if your value
* is bigger than what the chip actually supports! * is bigger than what the chip actually supports!
*
* An example in pseudo code for a setup() callback:
*
* void get_mac_addr(struct memory_accessor *mem_acc, void *context)
* {
* u8 *mac_addr = ethernet_pdata->mac_addr;
* off_t offset = context;
*
* // Read MAC addr from EEPROM
* if (mem_acc->read(mem_acc, mac_addr, offset, ETH_ALEN) == ETH_ALEN)
* pr_info("Read MAC addr from EEPROM: %pM\n", mac_addr);
* }
*
* This function pointer and context can now be set up in at24_platform_data.
*/ */
struct at24_platform_data { struct at24_platform_data {