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:
commit
fc2bb8d1cd
|
@ -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>;
|
||||||
|
};
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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__);
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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");
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue