Merge branch 'i2c/for-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: - mostly driver updates. Bigger ones for mlxcpld and iproc. But most of them are all over the place. - removal of the efm32, sirf, u300, and zte zx bus drivers because of platform removal. So, we have a pleasant diffstat this time. - first set of cleanups in the I2C core as preparation to increase maximum length of SMBus transfers to 255 (as specified in the new standard). Better documentation of struct i2c_msg and its flags stand out here. - the testunit can now respond to SMBus block process calls which is the testcase when implementing the above new maximum length. * 'i2c/for-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (62 commits) i2c: remove redundant error print in stm32f7_i2c_probe i2c: testunit: add support for block process calls i2c: busses: Replace spin_lock_irqsave with spin_lock in hard IRQ dt-bindings: eeprom: at24: Document ROHM BR24G01 i2c: i801: Add support for Intel Alder Lake PCH-P i2c: mv64xxx: Fix check for missing clock after adding RPM i2c: mux: mlxcpld: Add callback to notify mux creation completion i2c: mux: mlxcpld: Extend supported mux number i2c: mux: mlxcpld: Extend driver to support word address space devices i2c: mux: mlxcpld: Get rid of adapter numbers enforcement i2c: mux: mlxcpld: Prepare mux selection infrastructure for two-byte support i2c: mux: mlxcpld: Convert driver to platform driver i2c: imx: Synthesize end of transaction events without idle interrupts i2c: i2c-qcom-geni: Add shutdown callback for i2c i2c: mv64xxx: Add runtime PM support i2c: amd-mp2: Remove unused macro i2c: amd-mp2: convert to PCI logging functions i2c: mux: mlxcpld: Move header file out of x86 realm platform/x86: mlxcpld: Update module license i2c: mux: mlxcpld: Update module license ...
This commit is contained in:
Коммит
32c080c4b5
|
@ -95,9 +95,6 @@ properties:
|
|||
pattern: spd$
|
||||
# These are special cases that don't conform to the above pattern.
|
||||
# Each requires a standard at24 model as fallback.
|
||||
- items:
|
||||
- const: rohm,br24t01
|
||||
- const: atmel,24c01
|
||||
- items:
|
||||
- const: nxp,se97b
|
||||
- const: atmel,24c02
|
||||
|
@ -113,6 +110,12 @@ properties:
|
|||
- items:
|
||||
- const: renesas,r1ex24128
|
||||
- const: atmel,24c128
|
||||
- items:
|
||||
- const: rohm,br24g01
|
||||
- const: atmel,24c01
|
||||
- items:
|
||||
- const: rohm,br24t01
|
||||
- const: atmel,24c01
|
||||
|
||||
label:
|
||||
description: Descriptive name of the EEPROM.
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
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 property indicates the default frequency 100 kHz.
|
||||
|
||||
Examples :
|
||||
|
||||
i2c0: i2c@b00e0000 {
|
||||
compatible = "sirf,prima2-i2c";
|
||||
reg = <0xb00e0000 0x10000>;
|
||||
interrupts = <24>;
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
ST Microelectronics DDC I2C
|
||||
|
||||
Required properties :
|
||||
- compatible : Must be "st,ddci2c"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: interrupt number to the cpu.
|
||||
- #address-cells = <1>;
|
||||
- #size-cells = <0>;
|
||||
|
||||
Optional properties:
|
||||
- Child nodes conforming to i2c bus binding
|
||||
|
||||
Examples :
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
ZTE zx2967 I2C controller
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "zte,zx296718-i2c"
|
||||
- reg: physical address and length of the device registers
|
||||
- interrupts: a single interrupt specifier
|
||||
- clocks: clock for the device
|
||||
- #address-cells: should be <1>
|
||||
- #size-cells: should be <0>
|
||||
- clock-frequency: the desired I2C bus clock frequency.
|
||||
|
||||
Examples:
|
||||
|
||||
i2c@112000 {
|
||||
compatible = "zte,zx296718-i2c";
|
||||
reg = <0x00112000 0x1000>;
|
||||
interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&osc24m>;
|
||||
#address-cells = <1>
|
||||
#size-cells = <0>;
|
||||
clock-frequency = <1600000>;
|
||||
};
|
|
@ -18,21 +18,14 @@ properties:
|
|||
- const: allwinner,sun4i-a10-i2c
|
||||
- const: allwinner,sun6i-a31-i2c
|
||||
- items:
|
||||
- const: allwinner,sun8i-a23-i2c
|
||||
- enum:
|
||||
- allwinner,sun8i-a23-i2c
|
||||
- allwinner,sun8i-a83t-i2c
|
||||
- allwinner,sun50i-a64-i2c
|
||||
- allwinner,sun50i-a100-i2c
|
||||
- allwinner,sun50i-h6-i2c
|
||||
- allwinner,sun50i-h616-i2c
|
||||
- const: allwinner,sun6i-a31-i2c
|
||||
- items:
|
||||
- const: allwinner,sun8i-a83t-i2c
|
||||
- const: allwinner,sun6i-a31-i2c
|
||||
- items:
|
||||
- const: allwinner,sun50i-a64-i2c
|
||||
- const: allwinner,sun6i-a31-i2c
|
||||
- items:
|
||||
- const: allwinner,sun50i-a100-i2c
|
||||
- const: allwinner,sun6i-a31-i2c
|
||||
- items:
|
||||
- const: allwinner,sun50i-h6-i2c
|
||||
- const: allwinner,sun6i-a31-i2c
|
||||
|
||||
- const: marvell,mv64xxx-i2c
|
||||
- const: marvell,mv78230-i2c
|
||||
- const: marvell,mv78230-a0-i2c
|
||||
|
|
|
@ -26,6 +26,7 @@ Required properties:
|
|||
"renesas,i2c-r8a77980" if the device is a part of a R8A77980 SoC.
|
||||
"renesas,i2c-r8a77990" if the device is a part of a R8A77990 SoC.
|
||||
"renesas,i2c-r8a77995" if the device is a part of a R8A77995 SoC.
|
||||
"renesas,i2c-r8a779a0" if the device is a part of a R8A779A0 SoC.
|
||||
"renesas,rcar-gen1-i2c" for a generic R-Car Gen1 compatible device.
|
||||
"renesas,rcar-gen2-i2c" for a generic R-Car Gen2 or RZ/G1 compatible
|
||||
device.
|
||||
|
|
|
@ -22,8 +22,9 @@ Instantiating the device is regular. Example for bus 0, address 0x30:
|
|||
|
||||
After that, you will have a write-only device listening. Reads will just return
|
||||
an 8-bit version number of the testunit. When writing, the device consists of 4
|
||||
8-bit registers and all must be written to start a testcase, i.e. you must
|
||||
always write 4 bytes to the device. The registers are:
|
||||
8-bit registers and, except for some "partial" commands, all registers must be
|
||||
written to start a testcase, i.e. you usually write 4 bytes to the device. The
|
||||
registers are:
|
||||
|
||||
0x00 CMD - which test to trigger
|
||||
0x01 DATAL - configuration byte 1 for the test
|
||||
|
@ -67,3 +68,21 @@ status word is currently ignored in the Linux Kernel. Example to send a
|
|||
notification after 10ms:
|
||||
|
||||
# i2cset -y 0 0x30 0x02 0x42 0x64 0x01 i
|
||||
|
||||
0x03 SMBUS_BLOCK_PROC_CALL (partial command)
|
||||
DATAL - must be '1', i.e. one further byte will be written
|
||||
DATAH - number of bytes to be sent back
|
||||
DELAY - not applicable, partial command!
|
||||
|
||||
This test will respond to a block process call as defined by the SMBus
|
||||
specification. The one data byte written specifies how many bytes will be sent
|
||||
back in the following read transfer. Note that in this read transfer, the
|
||||
testunit will prefix the length of the bytes to follow. So, if your host bus
|
||||
driver emulates SMBus calls like the majority does, it needs to support the
|
||||
I2C_M_RECV_LEN flag of an i2c_msg. This is a good testcase for it. The returned
|
||||
data consists of the length first, and then of an array of bytes from length-1
|
||||
to 0. Here is an example which emulates i2c_smbus_block_process_call() using
|
||||
i2ctransfer (you need i2c-tools v4.2 or later):
|
||||
|
||||
# i2ctransfer -y 0 w3@0x30 0x03 0x01 0x10 r?
|
||||
0x10 0x0f 0x0e 0x0d 0x0c 0x0b 0x0a 0x09 0x08 0x07 0x06 0x05 0x04 0x03 0x02 0x01 0x00
|
||||
|
|
|
@ -3469,6 +3469,10 @@ EXPORT_SYMBOL_GPL(gpiod_add_lookup_table);
|
|||
*/
|
||||
void gpiod_remove_lookup_table(struct gpiod_lookup_table *table)
|
||||
{
|
||||
/* Nothing to remove */
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
mutex_lock(&gpio_lookup_lock);
|
||||
|
||||
list_del(&table->list);
|
||||
|
|
|
@ -622,9 +622,7 @@ static int bit_xfer_atomic(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[],
|
|||
|
||||
static u32 bit_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
|
||||
I2C_FUNC_SMBUS_READ_BLOCK_DATA |
|
||||
I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
|
||||
return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL_ALL |
|
||||
I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
|
||||
}
|
||||
|
||||
|
|
|
@ -586,13 +586,6 @@ config I2C_DIGICOLOR
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-digicolor.
|
||||
|
||||
config I2C_EFM32
|
||||
tristate "EFM32 I2C controller"
|
||||
depends on ARCH_EFM32 || COMPILE_TEST
|
||||
help
|
||||
This driver supports the i2c block found in Energy Micro's EFM32
|
||||
SoCs.
|
||||
|
||||
config I2C_EG20T
|
||||
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
|
||||
depends on PCI && (X86_32 || MIPS || COMPILE_TEST)
|
||||
|
@ -1000,16 +993,6 @@ config I2C_SIMTEC
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-simtec.
|
||||
|
||||
config I2C_SIRF
|
||||
tristate "CSR SiRFprimaII I2C interface"
|
||||
depends on ARCH_SIRF || COMPILE_TEST
|
||||
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_SPRD
|
||||
tristate "Spreadtrum I2C interface"
|
||||
depends on I2C=y && (ARCH_SPRD || COMPILE_TEST)
|
||||
|
@ -1050,19 +1033,6 @@ config I2C_STM32F7
|
|||
This driver can also be built as module. If so, the module
|
||||
will be called i2c-stm32f7.
|
||||
|
||||
config I2C_STU300
|
||||
tristate "ST Microelectronics DDC I2C interface"
|
||||
depends on MACH_U300 || COMPILE_TEST
|
||||
default y if MACH_U300
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
I2C interface from ST Microelectronics simply called "DDC I2C"
|
||||
supporting both I2C and DDC, used in e.g. the U300 series
|
||||
mobile platforms.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-stu300.
|
||||
|
||||
config I2C_SUN6I_P2WI
|
||||
tristate "Allwinner sun6i internal P2WI controller"
|
||||
depends on RESET_CONTROLLER
|
||||
|
@ -1401,15 +1371,6 @@ config I2C_OPAL
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called as i2c-opal.
|
||||
|
||||
config I2C_ZX2967
|
||||
tristate "ZTE ZX2967 I2C support"
|
||||
depends on ARCH_ZX
|
||||
default y
|
||||
help
|
||||
Selecting this option will add ZX2967 I2C driver.
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called i2c-zx2967.
|
||||
|
||||
config I2C_FSI
|
||||
tristate "FSI I2C driver"
|
||||
depends on FSI
|
||||
|
|
|
@ -58,7 +58,6 @@ i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-bayt
|
|||
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
|
||||
i2c-designware-pci-y := i2c-designware-pcidrv.o
|
||||
obj-$(CONFIG_I2C_DIGICOLOR) += i2c-digicolor.o
|
||||
obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o
|
||||
obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
|
||||
obj-$(CONFIG_I2C_EMEV2) += i2c-emev2.o
|
||||
obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o
|
||||
|
@ -99,13 +98,11 @@ obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
|
|||
obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
|
||||
obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
|
||||
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
|
||||
obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o
|
||||
obj-$(CONFIG_I2C_SPRD) += i2c-sprd.o
|
||||
obj-$(CONFIG_I2C_ST) += i2c-st.o
|
||||
obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o
|
||||
i2c-stm32f7-drv-objs := i2c-stm32f7.o i2c-stm32.o
|
||||
obj-$(CONFIG_I2C_STM32F7) += i2c-stm32f7-drv.o
|
||||
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
|
||||
obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o
|
||||
obj-$(CONFIG_I2C_SYNQUACER) += i2c-synquacer.o
|
||||
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
|
||||
|
@ -122,7 +119,6 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
|
|||
obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
|
||||
obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
|
||||
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
|
||||
obj-$(CONFIG_I2C_ZX2967) += i2c-zx2967.o
|
||||
|
||||
# External I2C/SMBus adapter drivers
|
||||
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
|
||||
|
|
|
@ -30,7 +30,7 @@ static void amd_mp2_c2p_mutex_unlock(struct amd_i2c_common *i2c_common)
|
|||
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||
|
||||
if (unlikely(privdata->c2p_lock_busid != i2c_common->bus_id)) {
|
||||
dev_warn(ndev_dev(privdata),
|
||||
pci_warn(privdata->pci_dev,
|
||||
"bus %d attempting to unlock C2P locked by bus %d\n",
|
||||
i2c_common->bus_id, privdata->c2p_lock_busid);
|
||||
return;
|
||||
|
@ -59,8 +59,7 @@ int amd_mp2_bus_enable_set(struct amd_i2c_common *i2c_common, bool enable)
|
|||
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||
union i2c_cmd_base i2c_cmd_base;
|
||||
|
||||
dev_dbg(ndev_dev(privdata), "%s id: %d\n", __func__,
|
||||
i2c_common->bus_id);
|
||||
pci_dbg(privdata->pci_dev, "id: %d\n", i2c_common->bus_id);
|
||||
|
||||
i2c_cmd_base.ul = 0;
|
||||
i2c_cmd_base.s.i2c_cmd = enable ? i2c_enable : i2c_disable;
|
||||
|
@ -111,20 +110,19 @@ EXPORT_SYMBOL_GPL(amd_mp2_rw);
|
|||
static void amd_mp2_pci_check_rw_event(struct amd_i2c_common *i2c_common)
|
||||
{
|
||||
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||
struct pci_dev *pdev = privdata->pci_dev;
|
||||
int len = i2c_common->eventval.r.length;
|
||||
u32 slave_addr = i2c_common->eventval.r.slave_addr;
|
||||
bool err = false;
|
||||
|
||||
if (unlikely(len != i2c_common->msg->len)) {
|
||||
dev_err(ndev_dev(privdata),
|
||||
"length %d in event doesn't match buffer length %d!\n",
|
||||
pci_err(pdev, "length %d in event doesn't match buffer length %d!\n",
|
||||
len, i2c_common->msg->len);
|
||||
err = true;
|
||||
}
|
||||
|
||||
if (unlikely(slave_addr != i2c_common->msg->addr)) {
|
||||
dev_err(ndev_dev(privdata),
|
||||
"unexpected slave address %x (expected: %x)!\n",
|
||||
pci_err(pdev, "unexpected slave address %x (expected: %x)!\n",
|
||||
slave_addr, i2c_common->msg->addr);
|
||||
err = true;
|
||||
}
|
||||
|
@ -136,13 +134,14 @@ static void amd_mp2_pci_check_rw_event(struct amd_i2c_common *i2c_common)
|
|||
static void __amd_mp2_process_event(struct amd_i2c_common *i2c_common)
|
||||
{
|
||||
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||
struct pci_dev *pdev = privdata->pci_dev;
|
||||
enum status_type sts = i2c_common->eventval.r.status;
|
||||
enum response_type res = i2c_common->eventval.r.response;
|
||||
int len = i2c_common->eventval.r.length;
|
||||
|
||||
if (res != command_success) {
|
||||
if (res != command_failed)
|
||||
dev_err(ndev_dev(privdata), "invalid response to i2c command!\n");
|
||||
pci_err(pdev, "invalid response to i2c command!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -155,32 +154,26 @@ static void __amd_mp2_process_event(struct amd_i2c_common *i2c_common)
|
|||
privdata->mmio + AMD_C2P_MSG2,
|
||||
len);
|
||||
} else if (sts != i2c_readfail_event) {
|
||||
dev_err(ndev_dev(privdata),
|
||||
"invalid i2c status after read (%d)!\n", sts);
|
||||
pci_err(pdev, "invalid i2c status after read (%d)!\n", sts);
|
||||
}
|
||||
break;
|
||||
case i2c_write:
|
||||
if (sts == i2c_writecomplete_event)
|
||||
amd_mp2_pci_check_rw_event(i2c_common);
|
||||
else if (sts != i2c_writefail_event)
|
||||
dev_err(ndev_dev(privdata),
|
||||
"invalid i2c status after write (%d)!\n", sts);
|
||||
pci_err(pdev, "invalid i2c status after write (%d)!\n", sts);
|
||||
break;
|
||||
case i2c_enable:
|
||||
if (sts == i2c_busenable_complete)
|
||||
i2c_common->cmd_success = true;
|
||||
else if (sts != i2c_busenable_failed)
|
||||
dev_err(ndev_dev(privdata),
|
||||
"invalid i2c status after bus enable (%d)!\n",
|
||||
sts);
|
||||
pci_err(pdev, "invalid i2c status after bus enable (%d)!\n", sts);
|
||||
break;
|
||||
case i2c_disable:
|
||||
if (sts == i2c_busdisable_complete)
|
||||
i2c_common->cmd_success = true;
|
||||
else if (sts != i2c_busdisable_failed)
|
||||
dev_err(ndev_dev(privdata),
|
||||
"invalid i2c status after bus disable (%d)!\n",
|
||||
sts);
|
||||
pci_err(pdev, "invalid i2c status after bus disable (%d)!\n", sts);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -190,10 +183,10 @@ static void __amd_mp2_process_event(struct amd_i2c_common *i2c_common)
|
|||
void amd_mp2_process_event(struct amd_i2c_common *i2c_common)
|
||||
{
|
||||
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||
struct pci_dev *pdev = privdata->pci_dev;
|
||||
|
||||
if (unlikely(i2c_common->reqcmd == i2c_none)) {
|
||||
dev_warn(ndev_dev(privdata),
|
||||
"received msg but no cmd was sent (bus = %d)!\n",
|
||||
pci_warn(pdev, "received msg but no cmd was sent (bus = %d)!\n",
|
||||
i2c_common->bus_id);
|
||||
return;
|
||||
}
|
||||
|
@ -208,6 +201,7 @@ EXPORT_SYMBOL_GPL(amd_mp2_process_event);
|
|||
static irqreturn_t amd_mp2_irq_isr(int irq, void *dev)
|
||||
{
|
||||
struct amd_mp2_dev *privdata = dev;
|
||||
struct pci_dev *pdev = privdata->pci_dev;
|
||||
struct amd_i2c_common *i2c_common;
|
||||
u32 val;
|
||||
unsigned int bus_id;
|
||||
|
@ -236,8 +230,7 @@ static irqreturn_t amd_mp2_irq_isr(int irq, void *dev)
|
|||
val = readl(privdata->mmio + AMD_P2C_MSG_INTEN);
|
||||
if (val != 0) {
|
||||
writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
|
||||
dev_warn(ndev_dev(privdata),
|
||||
"received irq without message\n");
|
||||
pci_warn(pdev, "received irq without message\n");
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
|
@ -255,13 +248,13 @@ EXPORT_SYMBOL_GPL(amd_mp2_rw_timeout);
|
|||
int amd_mp2_register_cb(struct amd_i2c_common *i2c_common)
|
||||
{
|
||||
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||
struct pci_dev *pdev = privdata->pci_dev;
|
||||
|
||||
if (i2c_common->bus_id > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (privdata->busses[i2c_common->bus_id]) {
|
||||
dev_err(ndev_dev(privdata),
|
||||
"Bus %d already taken!\n", i2c_common->bus_id);
|
||||
pci_err(pdev, "Bus %d already taken!\n", i2c_common->bus_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -301,13 +294,13 @@ static int amd_mp2_pci_init(struct amd_mp2_dev *privdata,
|
|||
|
||||
rc = pcim_enable_device(pci_dev);
|
||||
if (rc) {
|
||||
dev_err(ndev_dev(privdata), "Failed to enable MP2 PCI device\n");
|
||||
pci_err(pci_dev, "Failed to enable MP2 PCI device\n");
|
||||
goto err_pci_enable;
|
||||
}
|
||||
|
||||
rc = pcim_iomap_regions(pci_dev, 1 << 2, pci_name(pci_dev));
|
||||
if (rc) {
|
||||
dev_err(ndev_dev(privdata), "I/O memory remapping failed\n");
|
||||
pci_err(pci_dev, "I/O memory remapping failed\n");
|
||||
goto err_pci_enable;
|
||||
}
|
||||
privdata->mmio = pcim_iomap_table(pci_dev)[2];
|
||||
|
@ -327,7 +320,7 @@ static int amd_mp2_pci_init(struct amd_mp2_dev *privdata,
|
|||
rc = devm_request_irq(&pci_dev->dev, pci_dev->irq, amd_mp2_irq_isr,
|
||||
IRQF_SHARED, dev_name(&pci_dev->dev), privdata);
|
||||
if (rc)
|
||||
dev_err(&pci_dev->dev, "Failure requesting irq %i: %d\n",
|
||||
pci_err(pci_dev, "Failure requesting irq %i: %d\n",
|
||||
pci_dev->irq, rc);
|
||||
|
||||
return rc;
|
||||
|
@ -363,7 +356,7 @@ static int amd_mp2_pci_probe(struct pci_dev *pci_dev,
|
|||
|
||||
privdata->probed = true;
|
||||
|
||||
dev_info(&pci_dev->dev, "MP2 device registered.\n");
|
||||
pci_info(pci_dev, "MP2 device registered.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -397,8 +390,7 @@ static int amd_mp2_pci_suspend(struct device *dev)
|
|||
|
||||
ret = pci_save_state(pci_dev);
|
||||
if (ret) {
|
||||
dev_err(ndev_dev(privdata),
|
||||
"pci_save_state failed = %d\n", ret);
|
||||
pci_err(pci_dev, "pci_save_state failed = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -417,8 +409,7 @@ static int amd_mp2_pci_resume(struct device *dev)
|
|||
pci_restore_state(pci_dev);
|
||||
ret = pci_enable_device(pci_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(ndev_dev(privdata),
|
||||
"pci_enable_device failed = %d\n", ret);
|
||||
pci_err(pci_dev, "pci_enable_device failed = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,8 +88,7 @@ static void i2c_amd_cmd_completion(struct amd_i2c_common *i2c_common)
|
|||
union i2c_event *event = &i2c_common->eventval;
|
||||
|
||||
if (event->r.status == i2c_readcomplete_event)
|
||||
dev_dbg(&i2c_dev->pdev->dev, "%s readdata:%*ph\n",
|
||||
__func__, event->r.length,
|
||||
dev_dbg(&i2c_dev->pdev->dev, "readdata:%*ph\n", event->r.length,
|
||||
i2c_common->msg->buf);
|
||||
|
||||
complete(&i2c_dev->cmd_complete);
|
||||
|
|
|
@ -185,12 +185,6 @@ struct amd_mp2_dev {
|
|||
unsigned int probed;
|
||||
};
|
||||
|
||||
#define ndev_pdev(ndev) ((ndev)->pci_dev)
|
||||
#define ndev_name(ndev) pci_name(ndev_pdev(ndev))
|
||||
#define ndev_dev(ndev) (&ndev_pdev(ndev)->dev)
|
||||
#define work_amd_i2c_common(__work) \
|
||||
container_of(__work, struct amd_i2c_common, work.work)
|
||||
|
||||
/* PCIe communication driver */
|
||||
|
||||
int amd_mp2_rw(struct amd_i2c_common *i2c_common, enum i2c_cmd reqcmd);
|
||||
|
|
|
@ -93,6 +93,7 @@
|
|||
#define S_CMD_STATUS_MASK 0x07
|
||||
#define S_CMD_STATUS_SUCCESS 0x0
|
||||
#define S_CMD_STATUS_TIMEOUT 0x5
|
||||
#define S_CMD_STATUS_MASTER_ABORT 0x7
|
||||
|
||||
#define IE_OFFSET 0x38
|
||||
#define IE_M_RX_FIFO_FULL_SHIFT 31
|
||||
|
@ -159,6 +160,11 @@
|
|||
|
||||
#define IE_S_ALL_INTERRUPT_SHIFT 21
|
||||
#define IE_S_ALL_INTERRUPT_MASK 0x3f
|
||||
/*
|
||||
* It takes ~18us to reading 10bytes of data, hence to keep tasklet
|
||||
* running for less time, max slave read per tasklet is set to 10 bytes.
|
||||
*/
|
||||
#define MAX_SLAVE_RX_PER_INT 10
|
||||
|
||||
enum i2c_slave_read_status {
|
||||
I2C_SLAVE_RX_FIFO_EMPTY = 0,
|
||||
|
@ -205,8 +211,18 @@ struct bcm_iproc_i2c_dev {
|
|||
/* bytes that have been read */
|
||||
unsigned int rx_bytes;
|
||||
unsigned int thld_bytes;
|
||||
|
||||
bool slave_rx_only;
|
||||
bool rx_start_rcvd;
|
||||
bool slave_read_complete;
|
||||
u32 tx_underrun;
|
||||
u32 slave_int_mask;
|
||||
struct tasklet_struct slave_rx_tasklet;
|
||||
};
|
||||
|
||||
/* tasklet to process slave rx data */
|
||||
static void slave_rx_tasklet_fn(unsigned long);
|
||||
|
||||
/*
|
||||
* Can be expanded in the future if more interrupt status bits are utilized
|
||||
*/
|
||||
|
@ -215,7 +231,8 @@ struct bcm_iproc_i2c_dev {
|
|||
|
||||
#define ISR_MASK_SLAVE (BIT(IS_S_START_BUSY_SHIFT)\
|
||||
| BIT(IS_S_RX_EVENT_SHIFT) | BIT(IS_S_RD_EVENT_SHIFT)\
|
||||
| BIT(IS_S_TX_UNDERRUN_SHIFT))
|
||||
| BIT(IS_S_TX_UNDERRUN_SHIFT) | BIT(IS_S_RX_FIFO_FULL_SHIFT)\
|
||||
| BIT(IS_S_RX_THLD_SHIFT))
|
||||
|
||||
static int bcm_iproc_i2c_reg_slave(struct i2c_client *slave);
|
||||
static int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave);
|
||||
|
@ -259,6 +276,7 @@ static void bcm_iproc_i2c_slave_init(
|
|||
{
|
||||
u32 val;
|
||||
|
||||
iproc_i2c->tx_underrun = 0;
|
||||
if (need_reset) {
|
||||
/* put controller in reset */
|
||||
val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET);
|
||||
|
@ -295,8 +313,13 @@ static void bcm_iproc_i2c_slave_init(
|
|||
|
||||
/* Enable interrupt register to indicate a valid byte in receive fifo */
|
||||
val = BIT(IE_S_RX_EVENT_SHIFT);
|
||||
/* Enable interrupt register to indicate Slave Rx FIFO Full */
|
||||
val |= BIT(IE_S_RX_FIFO_FULL_SHIFT);
|
||||
/* Enable interrupt register to indicate a Master read transaction */
|
||||
val |= BIT(IE_S_RD_EVENT_SHIFT);
|
||||
/* Enable interrupt register for the Slave BUSY command */
|
||||
val |= BIT(IE_S_START_BUSY_SHIFT);
|
||||
iproc_i2c->slave_int_mask = val;
|
||||
iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
|
||||
}
|
||||
|
||||
|
@ -311,9 +334,10 @@ static void bcm_iproc_i2c_check_slave_status(
|
|||
return;
|
||||
|
||||
val = (val >> S_CMD_STATUS_SHIFT) & S_CMD_STATUS_MASK;
|
||||
if (val == S_CMD_STATUS_TIMEOUT) {
|
||||
dev_err(iproc_i2c->device, "slave random stretch time timeout\n");
|
||||
|
||||
if (val == S_CMD_STATUS_TIMEOUT || val == S_CMD_STATUS_MASTER_ABORT) {
|
||||
dev_err(iproc_i2c->device, (val == S_CMD_STATUS_TIMEOUT) ?
|
||||
"slave random stretch time timeout\n" :
|
||||
"Master aborted read transaction\n");
|
||||
/* re-initialize i2c for recovery */
|
||||
bcm_iproc_i2c_enable_disable(iproc_i2c, false);
|
||||
bcm_iproc_i2c_slave_init(iproc_i2c, true);
|
||||
|
@ -321,76 +345,187 @@ static void bcm_iproc_i2c_check_slave_status(
|
|||
}
|
||||
}
|
||||
|
||||
static void bcm_iproc_i2c_slave_read(struct bcm_iproc_i2c_dev *iproc_i2c)
|
||||
{
|
||||
u8 rx_data, rx_status;
|
||||
u32 rx_bytes = 0;
|
||||
u32 val;
|
||||
|
||||
while (rx_bytes < MAX_SLAVE_RX_PER_INT) {
|
||||
val = iproc_i2c_rd_reg(iproc_i2c, S_RX_OFFSET);
|
||||
rx_status = (val >> S_RX_STATUS_SHIFT) & S_RX_STATUS_MASK;
|
||||
rx_data = ((val >> S_RX_DATA_SHIFT) & S_RX_DATA_MASK);
|
||||
|
||||
if (rx_status == I2C_SLAVE_RX_START) {
|
||||
/* Start of SMBUS Master write */
|
||||
i2c_slave_event(iproc_i2c->slave,
|
||||
I2C_SLAVE_WRITE_REQUESTED, &rx_data);
|
||||
iproc_i2c->rx_start_rcvd = true;
|
||||
iproc_i2c->slave_read_complete = false;
|
||||
} else if (rx_status == I2C_SLAVE_RX_DATA &&
|
||||
iproc_i2c->rx_start_rcvd) {
|
||||
/* Middle of SMBUS Master write */
|
||||
i2c_slave_event(iproc_i2c->slave,
|
||||
I2C_SLAVE_WRITE_RECEIVED, &rx_data);
|
||||
} else if (rx_status == I2C_SLAVE_RX_END &&
|
||||
iproc_i2c->rx_start_rcvd) {
|
||||
/* End of SMBUS Master write */
|
||||
if (iproc_i2c->slave_rx_only)
|
||||
i2c_slave_event(iproc_i2c->slave,
|
||||
I2C_SLAVE_WRITE_RECEIVED,
|
||||
&rx_data);
|
||||
|
||||
i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP,
|
||||
&rx_data);
|
||||
} else if (rx_status == I2C_SLAVE_RX_FIFO_EMPTY) {
|
||||
iproc_i2c->rx_start_rcvd = false;
|
||||
iproc_i2c->slave_read_complete = true;
|
||||
break;
|
||||
}
|
||||
|
||||
rx_bytes++;
|
||||
}
|
||||
}
|
||||
|
||||
static void slave_rx_tasklet_fn(unsigned long data)
|
||||
{
|
||||
struct bcm_iproc_i2c_dev *iproc_i2c = (struct bcm_iproc_i2c_dev *)data;
|
||||
u32 int_clr;
|
||||
|
||||
bcm_iproc_i2c_slave_read(iproc_i2c);
|
||||
|
||||
/* clear pending IS_S_RX_EVENT_SHIFT interrupt */
|
||||
int_clr = BIT(IS_S_RX_EVENT_SHIFT);
|
||||
|
||||
if (!iproc_i2c->slave_rx_only && iproc_i2c->slave_read_complete) {
|
||||
/*
|
||||
* In case of single byte master-read request,
|
||||
* IS_S_TX_UNDERRUN_SHIFT event is generated before
|
||||
* IS_S_START_BUSY_SHIFT event. Hence start slave data send
|
||||
* from first IS_S_TX_UNDERRUN_SHIFT event.
|
||||
*
|
||||
* This means don't send any data from slave when
|
||||
* IS_S_RD_EVENT_SHIFT event is generated else it will increment
|
||||
* eeprom or other backend slave driver read pointer twice.
|
||||
*/
|
||||
iproc_i2c->tx_underrun = 0;
|
||||
iproc_i2c->slave_int_mask |= BIT(IE_S_TX_UNDERRUN_SHIFT);
|
||||
|
||||
/* clear IS_S_RD_EVENT_SHIFT interrupt */
|
||||
int_clr |= BIT(IS_S_RD_EVENT_SHIFT);
|
||||
}
|
||||
|
||||
/* clear slave interrupt */
|
||||
iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, int_clr);
|
||||
/* enable slave interrupts */
|
||||
iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, iproc_i2c->slave_int_mask);
|
||||
}
|
||||
|
||||
static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
u32 status)
|
||||
{
|
||||
u32 val;
|
||||
u8 value, rx_status;
|
||||
u8 value;
|
||||
|
||||
/* Slave RX byte receive */
|
||||
if (status & BIT(IS_S_RX_EVENT_SHIFT)) {
|
||||
val = iproc_i2c_rd_reg(iproc_i2c, S_RX_OFFSET);
|
||||
rx_status = (val >> S_RX_STATUS_SHIFT) & S_RX_STATUS_MASK;
|
||||
if (rx_status == I2C_SLAVE_RX_START) {
|
||||
/* Start of SMBUS for Master write */
|
||||
i2c_slave_event(iproc_i2c->slave,
|
||||
I2C_SLAVE_WRITE_REQUESTED, &value);
|
||||
/*
|
||||
* Slave events in case of master-write, master-write-read and,
|
||||
* master-read
|
||||
*
|
||||
* Master-write : only IS_S_RX_EVENT_SHIFT event
|
||||
* Master-write-read: both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
|
||||
* events
|
||||
* Master-read : both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
|
||||
* events or only IS_S_RD_EVENT_SHIFT
|
||||
*
|
||||
* iproc has a slave rx fifo size of 64 bytes. Rx fifo full interrupt
|
||||
* (IS_S_RX_FIFO_FULL_SHIFT) will be generated when RX fifo becomes
|
||||
* full. This can happen if Master issues write requests of more than
|
||||
* 64 bytes.
|
||||
*/
|
||||
if (status & BIT(IS_S_RX_EVENT_SHIFT) ||
|
||||
status & BIT(IS_S_RD_EVENT_SHIFT) ||
|
||||
status & BIT(IS_S_RX_FIFO_FULL_SHIFT)) {
|
||||
/* disable slave interrupts */
|
||||
val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
|
||||
val &= ~iproc_i2c->slave_int_mask;
|
||||
iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
|
||||
|
||||
val = iproc_i2c_rd_reg(iproc_i2c, S_RX_OFFSET);
|
||||
value = (u8)((val >> S_RX_DATA_SHIFT) & S_RX_DATA_MASK);
|
||||
i2c_slave_event(iproc_i2c->slave,
|
||||
I2C_SLAVE_WRITE_RECEIVED, &value);
|
||||
} else if (status & BIT(IS_S_RD_EVENT_SHIFT)) {
|
||||
if (status & BIT(IS_S_RD_EVENT_SHIFT))
|
||||
/* Master-write-read request */
|
||||
iproc_i2c->slave_rx_only = false;
|
||||
else
|
||||
/* Master-write request only */
|
||||
iproc_i2c->slave_rx_only = true;
|
||||
|
||||
/* schedule tasklet to read data later */
|
||||
tasklet_schedule(&iproc_i2c->slave_rx_tasklet);
|
||||
|
||||
/*
|
||||
* clear only IS_S_RX_EVENT_SHIFT and
|
||||
* IS_S_RX_FIFO_FULL_SHIFT interrupt.
|
||||
*/
|
||||
val = BIT(IS_S_RX_EVENT_SHIFT);
|
||||
if (status & BIT(IS_S_RX_FIFO_FULL_SHIFT))
|
||||
val |= BIT(IS_S_RX_FIFO_FULL_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, val);
|
||||
}
|
||||
|
||||
if (status & BIT(IS_S_TX_UNDERRUN_SHIFT)) {
|
||||
iproc_i2c->tx_underrun++;
|
||||
if (iproc_i2c->tx_underrun == 1)
|
||||
/* Start of SMBUS for Master Read */
|
||||
i2c_slave_event(iproc_i2c->slave,
|
||||
I2C_SLAVE_READ_REQUESTED, &value);
|
||||
iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, value);
|
||||
|
||||
val = BIT(S_CMD_START_BUSY_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val);
|
||||
|
||||
/*
|
||||
* Enable interrupt for TX FIFO becomes empty and
|
||||
* less than PKT_LENGTH bytes were output on the SMBUS
|
||||
*/
|
||||
val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
|
||||
val |= BIT(IE_S_TX_UNDERRUN_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
|
||||
} else {
|
||||
/* Master write other than start */
|
||||
value = (u8)((val >> S_RX_DATA_SHIFT) & S_RX_DATA_MASK);
|
||||
I2C_SLAVE_READ_REQUESTED,
|
||||
&value);
|
||||
else
|
||||
/* Master read other than start */
|
||||
i2c_slave_event(iproc_i2c->slave,
|
||||
I2C_SLAVE_WRITE_RECEIVED, &value);
|
||||
if (rx_status == I2C_SLAVE_RX_END)
|
||||
i2c_slave_event(iproc_i2c->slave,
|
||||
I2C_SLAVE_STOP, &value);
|
||||
}
|
||||
} else if (status & BIT(IS_S_TX_UNDERRUN_SHIFT)) {
|
||||
/* Master read other than start */
|
||||
i2c_slave_event(iproc_i2c->slave,
|
||||
I2C_SLAVE_READ_PROCESSED, &value);
|
||||
I2C_SLAVE_READ_PROCESSED,
|
||||
&value);
|
||||
|
||||
iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, value);
|
||||
/* start transfer */
|
||||
val = BIT(S_CMD_START_BUSY_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val);
|
||||
|
||||
/* clear interrupt */
|
||||
iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET,
|
||||
BIT(IS_S_TX_UNDERRUN_SHIFT));
|
||||
}
|
||||
|
||||
/* Stop */
|
||||
/* Stop received from master in case of master read transaction */
|
||||
if (status & BIT(IS_S_START_BUSY_SHIFT)) {
|
||||
i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP, &value);
|
||||
/*
|
||||
* Enable interrupt for TX FIFO becomes empty and
|
||||
* Disable interrupt for TX FIFO becomes empty and
|
||||
* less than PKT_LENGTH bytes were output on the SMBUS
|
||||
*/
|
||||
val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
|
||||
val &= ~BIT(IE_S_TX_UNDERRUN_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
|
||||
iproc_i2c->slave_int_mask &= ~BIT(IE_S_TX_UNDERRUN_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET,
|
||||
iproc_i2c->slave_int_mask);
|
||||
|
||||
/* End of SMBUS for Master Read */
|
||||
val = BIT(S_TX_WR_STATUS_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, val);
|
||||
|
||||
val = BIT(S_CMD_START_BUSY_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val);
|
||||
|
||||
/* flush TX FIFOs */
|
||||
val = iproc_i2c_rd_reg(iproc_i2c, S_FIFO_CTRL_OFFSET);
|
||||
val |= (BIT(S_FIFO_TX_FLUSH_SHIFT));
|
||||
iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, val);
|
||||
|
||||
i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP, &value);
|
||||
|
||||
/* clear interrupt */
|
||||
iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET,
|
||||
BIT(IS_S_START_BUSY_SHIFT));
|
||||
}
|
||||
|
||||
/* clear interrupt status */
|
||||
iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status);
|
||||
/* check slave transmit status only if slave is transmitting */
|
||||
if (!iproc_i2c->slave_rx_only)
|
||||
bcm_iproc_i2c_check_slave_status(iproc_i2c);
|
||||
|
||||
bcm_iproc_i2c_check_slave_status(iproc_i2c);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -505,12 +640,17 @@ static void bcm_iproc_i2c_process_m_event(struct bcm_iproc_i2c_dev *iproc_i2c,
|
|||
static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
|
||||
{
|
||||
struct bcm_iproc_i2c_dev *iproc_i2c = data;
|
||||
u32 status = iproc_i2c_rd_reg(iproc_i2c, IS_OFFSET);
|
||||
u32 slave_status;
|
||||
u32 status;
|
||||
bool ret;
|
||||
u32 sl_status = status & ISR_MASK_SLAVE;
|
||||
|
||||
if (sl_status) {
|
||||
ret = bcm_iproc_i2c_slave_isr(iproc_i2c, sl_status);
|
||||
status = iproc_i2c_rd_reg(iproc_i2c, IS_OFFSET);
|
||||
/* process only slave interrupt which are enabled */
|
||||
slave_status = status & iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET) &
|
||||
ISR_MASK_SLAVE;
|
||||
|
||||
if (slave_status) {
|
||||
ret = bcm_iproc_i2c_slave_isr(iproc_i2c, slave_status);
|
||||
if (ret)
|
||||
return IRQ_HANDLED;
|
||||
else
|
||||
|
@ -1066,6 +1206,10 @@ static int bcm_iproc_i2c_reg_slave(struct i2c_client *slave)
|
|||
return -EAFNOSUPPORT;
|
||||
|
||||
iproc_i2c->slave = slave;
|
||||
|
||||
tasklet_init(&iproc_i2c->slave_rx_tasklet, slave_rx_tasklet_fn,
|
||||
(unsigned long)iproc_i2c);
|
||||
|
||||
bcm_iproc_i2c_slave_init(iproc_i2c, false);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1086,6 +1230,8 @@ static int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave)
|
|||
IE_S_ALL_INTERRUPT_SHIFT);
|
||||
iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, tmp);
|
||||
|
||||
tasklet_kill(&iproc_i2c->slave_rx_tasklet);
|
||||
|
||||
/* Erase the slave address programmed */
|
||||
tmp = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET);
|
||||
tmp &= ~BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT);
|
||||
|
|
|
@ -160,12 +160,11 @@ static irqreturn_t dc_i2c_irq(int irq, void *dev_id)
|
|||
{
|
||||
struct dc_i2c *i2c = dev_id;
|
||||
int cmd_status = dc_i2c_cmd_status(i2c);
|
||||
unsigned long flags;
|
||||
u8 addr_cmd;
|
||||
|
||||
writeb_relaxed(1, i2c->regs + II_INTFLAG_CLEAR);
|
||||
|
||||
spin_lock_irqsave(&i2c->lock, flags);
|
||||
spin_lock(&i2c->lock);
|
||||
|
||||
if (cmd_status == II_CMD_STATUS_ACK_BAD
|
||||
|| cmd_status == II_CMD_STATUS_ABORT) {
|
||||
|
@ -207,7 +206,7 @@ static irqreturn_t dc_i2c_irq(int irq, void *dev_id)
|
|||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&i2c->lock, flags);
|
||||
spin_unlock(&i2c->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,469 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2014 Uwe Kleine-Koenig for Pengutronix
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#define DRIVER_NAME "efm32-i2c"
|
||||
|
||||
#define MASK_VAL(mask, val) ((val << __ffs(mask)) & mask)
|
||||
|
||||
#define REG_CTRL 0x00
|
||||
#define REG_CTRL_EN 0x00001
|
||||
#define REG_CTRL_SLAVE 0x00002
|
||||
#define REG_CTRL_AUTOACK 0x00004
|
||||
#define REG_CTRL_AUTOSE 0x00008
|
||||
#define REG_CTRL_AUTOSN 0x00010
|
||||
#define REG_CTRL_ARBDIS 0x00020
|
||||
#define REG_CTRL_GCAMEN 0x00040
|
||||
#define REG_CTRL_CLHR__MASK 0x00300
|
||||
#define REG_CTRL_BITO__MASK 0x03000
|
||||
#define REG_CTRL_BITO_OFF 0x00000
|
||||
#define REG_CTRL_BITO_40PCC 0x01000
|
||||
#define REG_CTRL_BITO_80PCC 0x02000
|
||||
#define REG_CTRL_BITO_160PCC 0x03000
|
||||
#define REG_CTRL_GIBITO 0x08000
|
||||
#define REG_CTRL_CLTO__MASK 0x70000
|
||||
#define REG_CTRL_CLTO_OFF 0x00000
|
||||
|
||||
#define REG_CMD 0x04
|
||||
#define REG_CMD_START 0x00001
|
||||
#define REG_CMD_STOP 0x00002
|
||||
#define REG_CMD_ACK 0x00004
|
||||
#define REG_CMD_NACK 0x00008
|
||||
#define REG_CMD_CONT 0x00010
|
||||
#define REG_CMD_ABORT 0x00020
|
||||
#define REG_CMD_CLEARTX 0x00040
|
||||
#define REG_CMD_CLEARPC 0x00080
|
||||
|
||||
#define REG_STATE 0x08
|
||||
#define REG_STATE_BUSY 0x00001
|
||||
#define REG_STATE_MASTER 0x00002
|
||||
#define REG_STATE_TRANSMITTER 0x00004
|
||||
#define REG_STATE_NACKED 0x00008
|
||||
#define REG_STATE_BUSHOLD 0x00010
|
||||
#define REG_STATE_STATE__MASK 0x000e0
|
||||
#define REG_STATE_STATE_IDLE 0x00000
|
||||
#define REG_STATE_STATE_WAIT 0x00020
|
||||
#define REG_STATE_STATE_START 0x00040
|
||||
#define REG_STATE_STATE_ADDR 0x00060
|
||||
#define REG_STATE_STATE_ADDRACK 0x00080
|
||||
#define REG_STATE_STATE_DATA 0x000a0
|
||||
#define REG_STATE_STATE_DATAACK 0x000c0
|
||||
|
||||
#define REG_STATUS 0x0c
|
||||
#define REG_STATUS_PSTART 0x00001
|
||||
#define REG_STATUS_PSTOP 0x00002
|
||||
#define REG_STATUS_PACK 0x00004
|
||||
#define REG_STATUS_PNACK 0x00008
|
||||
#define REG_STATUS_PCONT 0x00010
|
||||
#define REG_STATUS_PABORT 0x00020
|
||||
#define REG_STATUS_TXC 0x00040
|
||||
#define REG_STATUS_TXBL 0x00080
|
||||
#define REG_STATUS_RXDATAV 0x00100
|
||||
|
||||
#define REG_CLKDIV 0x10
|
||||
#define REG_CLKDIV_DIV__MASK 0x001ff
|
||||
#define REG_CLKDIV_DIV(div) MASK_VAL(REG_CLKDIV_DIV__MASK, (div))
|
||||
|
||||
#define REG_SADDR 0x14
|
||||
#define REG_SADDRMASK 0x18
|
||||
#define REG_RXDATA 0x1c
|
||||
#define REG_RXDATAP 0x20
|
||||
#define REG_TXDATA 0x24
|
||||
#define REG_IF 0x28
|
||||
#define REG_IF_START 0x00001
|
||||
#define REG_IF_RSTART 0x00002
|
||||
#define REG_IF_ADDR 0x00004
|
||||
#define REG_IF_TXC 0x00008
|
||||
#define REG_IF_TXBL 0x00010
|
||||
#define REG_IF_RXDATAV 0x00020
|
||||
#define REG_IF_ACK 0x00040
|
||||
#define REG_IF_NACK 0x00080
|
||||
#define REG_IF_MSTOP 0x00100
|
||||
#define REG_IF_ARBLOST 0x00200
|
||||
#define REG_IF_BUSERR 0x00400
|
||||
#define REG_IF_BUSHOLD 0x00800
|
||||
#define REG_IF_TXOF 0x01000
|
||||
#define REG_IF_RXUF 0x02000
|
||||
#define REG_IF_BITO 0x04000
|
||||
#define REG_IF_CLTO 0x08000
|
||||
#define REG_IF_SSTOP 0x10000
|
||||
|
||||
#define REG_IFS 0x2c
|
||||
#define REG_IFC 0x30
|
||||
#define REG_IFC__MASK 0x1ffcf
|
||||
|
||||
#define REG_IEN 0x34
|
||||
|
||||
#define REG_ROUTE 0x38
|
||||
#define REG_ROUTE_SDAPEN 0x00001
|
||||
#define REG_ROUTE_SCLPEN 0x00002
|
||||
#define REG_ROUTE_LOCATION__MASK 0x00700
|
||||
#define REG_ROUTE_LOCATION(n) MASK_VAL(REG_ROUTE_LOCATION__MASK, (n))
|
||||
|
||||
struct efm32_i2c_ddata {
|
||||
struct i2c_adapter adapter;
|
||||
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
unsigned int irq;
|
||||
u8 location;
|
||||
unsigned long frequency;
|
||||
|
||||
/* transfer data */
|
||||
struct completion done;
|
||||
struct i2c_msg *msgs;
|
||||
size_t num_msgs;
|
||||
size_t current_word, current_msg;
|
||||
int retval;
|
||||
};
|
||||
|
||||
static u32 efm32_i2c_read32(struct efm32_i2c_ddata *ddata, unsigned offset)
|
||||
{
|
||||
return readl(ddata->base + offset);
|
||||
}
|
||||
|
||||
static void efm32_i2c_write32(struct efm32_i2c_ddata *ddata,
|
||||
unsigned offset, u32 value)
|
||||
{
|
||||
writel(value, ddata->base + offset);
|
||||
}
|
||||
|
||||
static void efm32_i2c_send_next_msg(struct efm32_i2c_ddata *ddata)
|
||||
{
|
||||
struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
|
||||
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_START);
|
||||
efm32_i2c_write32(ddata, REG_TXDATA, i2c_8bit_addr_from_msg(cur_msg));
|
||||
}
|
||||
|
||||
static void efm32_i2c_send_next_byte(struct efm32_i2c_ddata *ddata)
|
||||
{
|
||||
struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
|
||||
|
||||
if (ddata->current_word >= cur_msg->len) {
|
||||
/* cur_msg completely transferred */
|
||||
ddata->current_word = 0;
|
||||
ddata->current_msg += 1;
|
||||
|
||||
if (ddata->current_msg >= ddata->num_msgs) {
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
|
||||
complete(&ddata->done);
|
||||
} else {
|
||||
efm32_i2c_send_next_msg(ddata);
|
||||
}
|
||||
} else {
|
||||
efm32_i2c_write32(ddata, REG_TXDATA,
|
||||
cur_msg->buf[ddata->current_word++]);
|
||||
}
|
||||
}
|
||||
|
||||
static void efm32_i2c_recv_next_byte(struct efm32_i2c_ddata *ddata)
|
||||
{
|
||||
struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
|
||||
|
||||
cur_msg->buf[ddata->current_word] = efm32_i2c_read32(ddata, REG_RXDATA);
|
||||
ddata->current_word += 1;
|
||||
if (ddata->current_word >= cur_msg->len) {
|
||||
/* cur_msg completely transferred */
|
||||
ddata->current_word = 0;
|
||||
ddata->current_msg += 1;
|
||||
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_NACK);
|
||||
|
||||
if (ddata->current_msg >= ddata->num_msgs) {
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
|
||||
complete(&ddata->done);
|
||||
} else {
|
||||
efm32_i2c_send_next_msg(ddata);
|
||||
}
|
||||
} else {
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_ACK);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t efm32_i2c_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct efm32_i2c_ddata *ddata = dev_id;
|
||||
struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
|
||||
u32 irqflag = efm32_i2c_read32(ddata, REG_IF);
|
||||
u32 state = efm32_i2c_read32(ddata, REG_STATE);
|
||||
|
||||
efm32_i2c_write32(ddata, REG_IFC, irqflag & REG_IFC__MASK);
|
||||
|
||||
switch (state & REG_STATE_STATE__MASK) {
|
||||
case REG_STATE_STATE_IDLE:
|
||||
/* arbitration lost? */
|
||||
ddata->retval = -EAGAIN;
|
||||
complete(&ddata->done);
|
||||
break;
|
||||
case REG_STATE_STATE_WAIT:
|
||||
/*
|
||||
* huh, this shouldn't happen.
|
||||
* Reset hardware state and get out
|
||||
*/
|
||||
ddata->retval = -EIO;
|
||||
efm32_i2c_write32(ddata, REG_CMD,
|
||||
REG_CMD_STOP | REG_CMD_ABORT |
|
||||
REG_CMD_CLEARTX | REG_CMD_CLEARPC);
|
||||
complete(&ddata->done);
|
||||
break;
|
||||
case REG_STATE_STATE_START:
|
||||
/* "caller" is expected to send an address */
|
||||
break;
|
||||
case REG_STATE_STATE_ADDR:
|
||||
/* wait for Ack or NAck of slave */
|
||||
break;
|
||||
case REG_STATE_STATE_ADDRACK:
|
||||
if (state & REG_STATE_NACKED) {
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
|
||||
ddata->retval = -ENXIO;
|
||||
complete(&ddata->done);
|
||||
} else if (cur_msg->flags & I2C_M_RD) {
|
||||
/* wait for slave to send first data byte */
|
||||
} else {
|
||||
efm32_i2c_send_next_byte(ddata);
|
||||
}
|
||||
break;
|
||||
case REG_STATE_STATE_DATA:
|
||||
if (cur_msg->flags & I2C_M_RD) {
|
||||
efm32_i2c_recv_next_byte(ddata);
|
||||
} else {
|
||||
/* wait for Ack or Nack of slave */
|
||||
}
|
||||
break;
|
||||
case REG_STATE_STATE_DATAACK:
|
||||
if (state & REG_STATE_NACKED) {
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
|
||||
complete(&ddata->done);
|
||||
} else {
|
||||
efm32_i2c_send_next_byte(ddata);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int efm32_i2c_master_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct efm32_i2c_ddata *ddata = i2c_get_adapdata(adap);
|
||||
int ret;
|
||||
|
||||
if (ddata->msgs)
|
||||
return -EBUSY;
|
||||
|
||||
ddata->msgs = msgs;
|
||||
ddata->num_msgs = num;
|
||||
ddata->current_word = 0;
|
||||
ddata->current_msg = 0;
|
||||
ddata->retval = -EIO;
|
||||
|
||||
reinit_completion(&ddata->done);
|
||||
|
||||
dev_dbg(&ddata->adapter.dev, "state: %08x, status: %08x\n",
|
||||
efm32_i2c_read32(ddata, REG_STATE),
|
||||
efm32_i2c_read32(ddata, REG_STATUS));
|
||||
|
||||
efm32_i2c_send_next_msg(ddata);
|
||||
|
||||
wait_for_completion(&ddata->done);
|
||||
|
||||
if (ddata->current_msg >= ddata->num_msgs)
|
||||
ret = ddata->num_msgs;
|
||||
else
|
||||
ret = ddata->retval;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 efm32_i2c_functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm efm32_i2c_algo = {
|
||||
.master_xfer = efm32_i2c_master_xfer,
|
||||
.functionality = efm32_i2c_functionality,
|
||||
};
|
||||
|
||||
static u32 efm32_i2c_get_configured_location(struct efm32_i2c_ddata *ddata)
|
||||
{
|
||||
u32 reg = efm32_i2c_read32(ddata, REG_ROUTE);
|
||||
|
||||
return (reg & REG_ROUTE_LOCATION__MASK) >>
|
||||
__ffs(REG_ROUTE_LOCATION__MASK);
|
||||
}
|
||||
|
||||
static int efm32_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct efm32_i2c_ddata *ddata;
|
||||
struct resource *res;
|
||||
unsigned long rate;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
u32 location, frequency;
|
||||
int ret;
|
||||
u32 clkdiv;
|
||||
|
||||
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
init_completion(&ddata->done);
|
||||
strlcpy(ddata->adapter.name, pdev->name, sizeof(ddata->adapter.name));
|
||||
ddata->adapter.owner = THIS_MODULE;
|
||||
ddata->adapter.algo = &efm32_i2c_algo;
|
||||
ddata->adapter.dev.parent = &pdev->dev;
|
||||
ddata->adapter.dev.of_node = pdev->dev.of_node;
|
||||
i2c_set_adapdata(&ddata->adapter, ddata);
|
||||
|
||||
ddata->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(ddata->clk)) {
|
||||
ret = PTR_ERR(ddata->clk);
|
||||
dev_err(&pdev->dev, "failed to get clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ddata->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(ddata->base))
|
||||
return PTR_ERR(ddata->base);
|
||||
|
||||
if (resource_size(res) < 0x42) {
|
||||
dev_err(&pdev->dev, "memory resource too small\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret <= 0) {
|
||||
if (!ret)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ddata->irq = ret;
|
||||
|
||||
ret = clk_prepare_enable(ddata->clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable clock (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
ret = of_property_read_u32(np, "energymicro,location", &location);
|
||||
|
||||
if (ret)
|
||||
/* fall back to wrongly namespaced property */
|
||||
ret = of_property_read_u32(np, "efm32,location", &location);
|
||||
|
||||
if (!ret) {
|
||||
dev_dbg(&pdev->dev, "using location %u\n", location);
|
||||
} else {
|
||||
/* default to location configured in hardware */
|
||||
location = efm32_i2c_get_configured_location(ddata);
|
||||
|
||||
dev_info(&pdev->dev, "fall back to location %u\n", location);
|
||||
}
|
||||
|
||||
ddata->location = location;
|
||||
|
||||
ret = of_property_read_u32(np, "clock-frequency", &frequency);
|
||||
if (!ret) {
|
||||
dev_dbg(&pdev->dev, "using frequency %u\n", frequency);
|
||||
} else {
|
||||
frequency = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
dev_info(&pdev->dev, "defaulting to 100 kHz\n");
|
||||
}
|
||||
ddata->frequency = frequency;
|
||||
|
||||
rate = clk_get_rate(ddata->clk);
|
||||
if (!rate) {
|
||||
dev_err(&pdev->dev, "there is no input clock available\n");
|
||||
ret = -EINVAL;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
clkdiv = DIV_ROUND_UP(rate, 8 * ddata->frequency) - 1;
|
||||
if (clkdiv >= 0x200) {
|
||||
dev_err(&pdev->dev,
|
||||
"input clock too fast (%lu) to divide down to bus freq (%lu)",
|
||||
rate, ddata->frequency);
|
||||
ret = -EINVAL;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev, "input clock = %lu, bus freq = %lu, clkdiv = %lu\n",
|
||||
rate, ddata->frequency, (unsigned long)clkdiv);
|
||||
efm32_i2c_write32(ddata, REG_CLKDIV, REG_CLKDIV_DIV(clkdiv));
|
||||
|
||||
efm32_i2c_write32(ddata, REG_ROUTE, REG_ROUTE_SDAPEN |
|
||||
REG_ROUTE_SCLPEN |
|
||||
REG_ROUTE_LOCATION(ddata->location));
|
||||
|
||||
efm32_i2c_write32(ddata, REG_CTRL, REG_CTRL_EN |
|
||||
REG_CTRL_BITO_160PCC | 0 * REG_CTRL_GIBITO);
|
||||
|
||||
efm32_i2c_write32(ddata, REG_IFC, REG_IFC__MASK);
|
||||
efm32_i2c_write32(ddata, REG_IEN, REG_IF_TXC | REG_IF_ACK | REG_IF_NACK
|
||||
| REG_IF_ARBLOST | REG_IF_BUSERR | REG_IF_RXDATAV);
|
||||
|
||||
/* to make bus idle */
|
||||
efm32_i2c_write32(ddata, REG_CMD, REG_CMD_ABORT);
|
||||
|
||||
ret = request_irq(ddata->irq, efm32_i2c_irq, 0, DRIVER_NAME, ddata);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to request irq (%d)\n", ret);
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
ret = i2c_add_adapter(&ddata->adapter);
|
||||
if (ret) {
|
||||
free_irq(ddata->irq, ddata);
|
||||
|
||||
err_disable_clk:
|
||||
clk_disable_unprepare(ddata->clk);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int efm32_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct efm32_i2c_ddata *ddata = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_del_adapter(&ddata->adapter);
|
||||
free_irq(ddata->irq, ddata);
|
||||
clk_disable_unprepare(ddata->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id efm32_i2c_dt_ids[] = {
|
||||
{
|
||||
.compatible = "energymicro,efm32-i2c",
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, efm32_i2c_dt_ids);
|
||||
|
||||
static struct platform_driver efm32_i2c_driver = {
|
||||
.probe = efm32_i2c_probe,
|
||||
.remove = efm32_i2c_remove,
|
||||
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = efm32_i2c_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(efm32_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
|
||||
MODULE_DESCRIPTION("EFM32 i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
|
@ -49,7 +49,7 @@ static int mmapped;
|
|||
|
||||
static wait_queue_head_t pcf_wait;
|
||||
static int pcf_pending;
|
||||
static spinlock_t lock;
|
||||
static DEFINE_SPINLOCK(lock);
|
||||
|
||||
static struct i2c_adapter pcf_isa_ops;
|
||||
|
||||
|
@ -132,7 +132,6 @@ static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id) {
|
|||
|
||||
static int pcf_isa_init(void)
|
||||
{
|
||||
spin_lock_init(&lock);
|
||||
if (!mmapped) {
|
||||
if (!request_region(base, 2, pcf_isa_ops.name)) {
|
||||
printk(KERN_ERR "%s: requested I/O region (%#x:2) is "
|
||||
|
|
|
@ -520,5 +520,5 @@ module_exit(i2c_gpio_exit);
|
|||
|
||||
MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
|
||||
MODULE_DESCRIPTION("Platform-independent bitbanging I2C driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:i2c-gpio");
|
||||
|
|
|
@ -413,10 +413,8 @@ static int hix5hd2_i2c_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(priv->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev, "cannot find HS-I2C IRQ\n");
|
||||
if (irq <= 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
* Jasper Lake (SOC) 0x4da3 32 hard yes yes yes
|
||||
* Comet Lake-V (PCH) 0xa3a3 32 hard yes yes yes
|
||||
* Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes
|
||||
* Alder Lake-P (PCH) 0x51a3 32 hard yes yes yes
|
||||
*
|
||||
* Features supported by this driver:
|
||||
* Software PEC no
|
||||
|
@ -228,6 +229,7 @@
|
|||
#define PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS 0x43a3
|
||||
#define PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS 0x4b23
|
||||
#define PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS 0x4da3
|
||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS 0x51a3
|
||||
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
|
||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS 0x7aa3
|
||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
|
||||
|
@ -1084,6 +1086,7 @@ static const struct pci_device_id i801_ids[] = {
|
|||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS) },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
|
@ -1433,7 +1436,7 @@ static int i801_add_mux(struct i801_priv *priv)
|
|||
const struct i801_mux_config *mux_config;
|
||||
struct i2c_mux_gpio_platform_data gpio_data;
|
||||
struct gpiod_lookup_table *lookup;
|
||||
int err, i;
|
||||
int i;
|
||||
|
||||
if (!priv->mux_drvdata)
|
||||
return 0;
|
||||
|
@ -1473,22 +1476,17 @@ static int i801_add_mux(struct i801_priv *priv)
|
|||
PLATFORM_DEVID_NONE, &gpio_data,
|
||||
sizeof(struct i2c_mux_gpio_platform_data));
|
||||
if (IS_ERR(priv->mux_pdev)) {
|
||||
err = PTR_ERR(priv->mux_pdev);
|
||||
gpiod_remove_lookup_table(lookup);
|
||||
priv->mux_pdev = NULL;
|
||||
dev_err(dev, "Failed to register i2c-mux-gpio device\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(priv->mux_pdev);
|
||||
}
|
||||
|
||||
static void i801_del_mux(struct i801_priv *priv)
|
||||
{
|
||||
if (priv->mux_pdev)
|
||||
platform_device_unregister(priv->mux_pdev);
|
||||
if (priv->lookup)
|
||||
gpiod_remove_lookup_table(priv->lookup);
|
||||
platform_device_unregister(priv->mux_pdev);
|
||||
gpiod_remove_lookup_table(priv->lookup);
|
||||
}
|
||||
|
||||
static unsigned int i801_get_adapter_class(struct i801_priv *priv)
|
||||
|
@ -1772,6 +1770,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
case PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_EBG_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS:
|
||||
priv->features |= FEATURE_BLOCK_PROC;
|
||||
priv->features |= FEATURE_I2C_BLOCK_READ;
|
||||
priv->features |= FEATURE_IRQ;
|
||||
|
|
|
@ -209,6 +209,7 @@ struct imx_i2c_struct {
|
|||
|
||||
struct imx_i2c_dma *dma;
|
||||
struct i2c_client *slave;
|
||||
enum i2c_slave_event last_slave_event;
|
||||
};
|
||||
|
||||
static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
|
||||
|
@ -550,7 +551,7 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
|
|||
|
||||
i2c_imx->cur_clk = i2c_clk_rate;
|
||||
|
||||
div = (i2c_clk_rate + i2c_imx->bitrate - 1) / i2c_imx->bitrate;
|
||||
div = DIV_ROUND_UP(i2c_clk_rate, i2c_imx->bitrate);
|
||||
if (div < i2c_clk_div[0].div)
|
||||
i = 0;
|
||||
else if (div > i2c_clk_div[i2c_imx->hwdata->ndivs - 1].div)
|
||||
|
@ -568,8 +569,8 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
|
|||
* This delay is used in I2C bus disable function
|
||||
* to fix chip hardware bug.
|
||||
*/
|
||||
i2c_imx->disable_delay = (500000U * i2c_clk_div[i].div
|
||||
+ (i2c_clk_rate / 2) - 1) / (i2c_clk_rate / 2);
|
||||
i2c_imx->disable_delay = DIV_ROUND_UP(500000U * i2c_clk_div[i].div,
|
||||
i2c_clk_rate / 2);
|
||||
|
||||
#ifdef CONFIG_I2C_DEBUG_BUS
|
||||
dev_dbg(&i2c_imx->adapter.dev, "I2C_CLK=%d, REQ DIV=%d\n",
|
||||
|
@ -675,6 +676,36 @@ static void i2c_imx_enable_bus_idle(struct imx_i2c_struct *i2c_imx)
|
|||
}
|
||||
}
|
||||
|
||||
static void i2c_imx_slave_event(struct imx_i2c_struct *i2c_imx,
|
||||
enum i2c_slave_event event, u8 *val)
|
||||
{
|
||||
i2c_slave_event(i2c_imx->slave, event, val);
|
||||
i2c_imx->last_slave_event = event;
|
||||
}
|
||||
|
||||
static void i2c_imx_slave_finish_op(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
while (i2c_imx->last_slave_event != I2C_SLAVE_STOP) {
|
||||
switch (i2c_imx->last_slave_event) {
|
||||
case I2C_SLAVE_READ_REQUESTED:
|
||||
i2c_imx_slave_event(i2c_imx, I2C_SLAVE_READ_PROCESSED,
|
||||
&val);
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_WRITE_REQUESTED:
|
||||
case I2C_SLAVE_READ_PROCESSED:
|
||||
case I2C_SLAVE_WRITE_RECEIVED:
|
||||
i2c_imx_slave_event(i2c_imx, I2C_SLAVE_STOP, &val);
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_STOP:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
|
||||
unsigned int status, unsigned int ctl)
|
||||
{
|
||||
|
@ -687,9 +718,11 @@ static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
|
|||
}
|
||||
|
||||
if (status & I2SR_IAAS) { /* Addressed as a slave */
|
||||
i2c_imx_slave_finish_op(i2c_imx);
|
||||
if (status & I2SR_SRW) { /* Master wants to read from us*/
|
||||
dev_dbg(&i2c_imx->adapter.dev, "read requested");
|
||||
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
|
||||
i2c_imx_slave_event(i2c_imx,
|
||||
I2C_SLAVE_READ_REQUESTED, &value);
|
||||
|
||||
/* Slave transmit */
|
||||
ctl |= I2CR_MTX;
|
||||
|
@ -699,7 +732,8 @@ static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
|
|||
imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
|
||||
} else { /* Master wants to write to us */
|
||||
dev_dbg(&i2c_imx->adapter.dev, "write requested");
|
||||
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
|
||||
i2c_imx_slave_event(i2c_imx,
|
||||
I2C_SLAVE_WRITE_REQUESTED, &value);
|
||||
|
||||
/* Slave receive */
|
||||
ctl &= ~I2CR_MTX;
|
||||
|
@ -710,17 +744,20 @@ static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
|
|||
} else if (!(ctl & I2CR_MTX)) { /* Receive mode */
|
||||
if (status & I2SR_IBB) { /* No STOP signal detected */
|
||||
value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
|
||||
i2c_imx_slave_event(i2c_imx,
|
||||
I2C_SLAVE_WRITE_RECEIVED, &value);
|
||||
} else { /* STOP signal is detected */
|
||||
dev_dbg(&i2c_imx->adapter.dev,
|
||||
"STOP signal detected");
|
||||
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &value);
|
||||
i2c_imx_slave_event(i2c_imx,
|
||||
I2C_SLAVE_STOP, &value);
|
||||
}
|
||||
} else if (!(status & I2SR_RXAK)) { /* Transmit mode received ACK */
|
||||
ctl |= I2CR_MTX;
|
||||
imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
|
||||
|
||||
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_PROCESSED, &value);
|
||||
i2c_imx_slave_event(i2c_imx,
|
||||
I2C_SLAVE_READ_PROCESSED, &value);
|
||||
|
||||
imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
|
||||
} else { /* Transmit mode received NAK */
|
||||
|
@ -761,6 +798,7 @@ static int i2c_imx_reg_slave(struct i2c_client *client)
|
|||
return -EBUSY;
|
||||
|
||||
i2c_imx->slave = client;
|
||||
i2c_imx->last_slave_event = I2C_SLAVE_STOP;
|
||||
|
||||
/* Resume */
|
||||
ret = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
|
||||
|
@ -813,10 +851,17 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
|
|||
|
||||
status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||
ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
|
||||
if (status & I2SR_IIF) {
|
||||
i2c_imx_clear_irq(i2c_imx, I2SR_IIF);
|
||||
if (i2c_imx->slave && !(ctl & I2CR_MSTA))
|
||||
return i2c_imx_slave_isr(i2c_imx, status, ctl);
|
||||
if (i2c_imx->slave) {
|
||||
if (!(ctl & I2CR_MSTA)) {
|
||||
return i2c_imx_slave_isr(i2c_imx, status, ctl);
|
||||
} else if (i2c_imx->last_slave_event !=
|
||||
I2C_SLAVE_STOP) {
|
||||
i2c_imx_slave_finish_op(i2c_imx);
|
||||
}
|
||||
}
|
||||
return i2c_imx_master_isr(i2c_imx, status);
|
||||
}
|
||||
|
||||
|
|
|
@ -437,9 +437,8 @@ static irqreturn_t jz4780_i2c_irq(int irqno, void *dev_id)
|
|||
unsigned short intst;
|
||||
unsigned short intmsk;
|
||||
struct jz4780_i2c *i2c = dev_id;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&i2c->lock, flags);
|
||||
spin_lock(&i2c->lock);
|
||||
intmsk = jz4780_i2c_readw(i2c, JZ4780_I2C_INTM);
|
||||
intst = jz4780_i2c_readw(i2c, JZ4780_I2C_INTST);
|
||||
|
||||
|
@ -551,7 +550,7 @@ static irqreturn_t jz4780_i2c_irq(int irqno, void *dev_id)
|
|||
}
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&i2c->lock, flags);
|
||||
spin_unlock(&i2c->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,34 +1,8 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2016 Michael Shych <michaels@mellanox.com>
|
||||
* Mellanox i2c driver
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the names of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* Copyright (C) 2016-2020 Mellanox Technologies
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
|
@ -37,7 +11,9 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/mlxreg.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* General defines */
|
||||
#define MLXPLAT_CPLD_LPC_I2C_BASE_ADDR 0x2000
|
||||
|
@ -51,7 +27,7 @@
|
|||
#define MLXCPLD_I2C_MAX_ADDR_LEN 4
|
||||
#define MLXCPLD_I2C_RETR_NUM 2
|
||||
#define MLXCPLD_I2C_XFER_TO 500000 /* usec */
|
||||
#define MLXCPLD_I2C_POLL_TIME 2000 /* usec */
|
||||
#define MLXCPLD_I2C_POLL_TIME 400 /* usec */
|
||||
|
||||
/* LPC I2C registers */
|
||||
#define MLXCPLD_LPCI2C_CPBLTY_REG 0x0
|
||||
|
@ -72,6 +48,16 @@
|
|||
#define MLXCPLD_LPCI2C_ACK_IND 1
|
||||
#define MLXCPLD_LPCI2C_NACK_IND 2
|
||||
|
||||
#define MLXCPLD_I2C_FREQ_1000KHZ_SET 0x04
|
||||
#define MLXCPLD_I2C_FREQ_400KHZ_SET 0x0f
|
||||
#define MLXCPLD_I2C_FREQ_100KHZ_SET 0x42
|
||||
|
||||
enum mlxcpld_i2c_frequency {
|
||||
MLXCPLD_I2C_FREQ_1000KHZ = 1,
|
||||
MLXCPLD_I2C_FREQ_400KHZ = 2,
|
||||
MLXCPLD_I2C_FREQ_100KHZ = 3,
|
||||
};
|
||||
|
||||
struct mlxcpld_i2c_curr_xfer {
|
||||
u8 cmd;
|
||||
u8 addr_width;
|
||||
|
@ -489,8 +475,45 @@ static struct i2c_adapter mlxcpld_i2c_adapter = {
|
|||
.nr = MLXCPLD_I2C_BUS_NUM,
|
||||
};
|
||||
|
||||
static int
|
||||
mlxcpld_i2c_set_frequency(struct mlxcpld_i2c_priv *priv,
|
||||
struct mlxreg_core_hotplug_platform_data *pdata)
|
||||
{
|
||||
struct mlxreg_core_item *item = pdata->items;
|
||||
struct mlxreg_core_data *data;
|
||||
u32 regval;
|
||||
u8 freq;
|
||||
int err;
|
||||
|
||||
if (!item)
|
||||
return 0;
|
||||
|
||||
/* Read frequency setting. */
|
||||
data = item->data;
|
||||
err = regmap_read(pdata->regmap, data->reg, ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Set frequency only if it is not 100KHz, which is default. */
|
||||
switch ((data->reg & data->mask) >> data->bit) {
|
||||
case MLXCPLD_I2C_FREQ_1000KHZ:
|
||||
freq = MLXCPLD_I2C_FREQ_1000KHZ_SET;
|
||||
break;
|
||||
case MLXCPLD_I2C_FREQ_400KHZ:
|
||||
freq = MLXCPLD_I2C_FREQ_400KHZ_SET;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_HALF_CYC_REG, &freq, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxcpld_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mlxreg_core_hotplug_platform_data *pdata;
|
||||
struct mlxcpld_i2c_priv *priv;
|
||||
int err;
|
||||
u8 val;
|
||||
|
@ -505,6 +528,14 @@ static int mlxcpld_i2c_probe(struct platform_device *pdev)
|
|||
priv->dev = &pdev->dev;
|
||||
priv->base_addr = MLXPLAT_CPLD_LPC_I2C_BASE_ADDR;
|
||||
|
||||
/* Set I2C bus frequency if platform data provides this info. */
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (pdata) {
|
||||
err = mlxcpld_i2c_set_frequency(priv, pdata);
|
||||
if (err)
|
||||
goto mlxcpld_i2_probe_failed;
|
||||
}
|
||||
|
||||
/* Register with i2c layer */
|
||||
mlxcpld_i2c_adapter.timeout = usecs_to_jiffies(MLXCPLD_I2C_XFER_TO);
|
||||
/* Read capability register */
|
||||
|
@ -523,8 +554,12 @@ static int mlxcpld_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
err = i2c_add_numbered_adapter(&priv->adap);
|
||||
if (err)
|
||||
mutex_destroy(&priv->lock);
|
||||
goto mlxcpld_i2_probe_failed;
|
||||
|
||||
return 0;
|
||||
|
||||
mlxcpld_i2_probe_failed:
|
||||
mutex_destroy(&priv->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/mv643xx_i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
@ -717,6 +718,10 @@ mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
|
||||
int rc, ret = num;
|
||||
|
||||
rc = pm_runtime_resume_and_get(&adap->dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
BUG_ON(drv_data->msgs != NULL);
|
||||
drv_data->msgs = msgs;
|
||||
drv_data->num_msgs = num;
|
||||
|
@ -732,6 +737,9 @@ mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
drv_data->num_msgs = 0;
|
||||
drv_data->msgs = NULL;
|
||||
|
||||
pm_runtime_mark_last_busy(&adap->dev);
|
||||
pm_runtime_put_autosuspend(&adap->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -805,7 +813,7 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
|
|||
* need to know tclk in order to calculate bus clock
|
||||
* factors.
|
||||
*/
|
||||
if (IS_ERR(drv_data->clk)) {
|
||||
if (!drv_data->clk) {
|
||||
rc = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
@ -828,7 +836,6 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
|
|||
rc = PTR_ERR(drv_data->rstc);
|
||||
goto out;
|
||||
}
|
||||
reset_control_deassert(drv_data->rstc);
|
||||
|
||||
/* Its not yet defined how timeouts will be specified in device tree.
|
||||
* So hard code the value to 1 second.
|
||||
|
@ -893,6 +900,32 @@ static int mv64xxx_i2c_init_recovery_info(struct mv64xxx_i2c_data *drv_data,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mv64xxx_i2c_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct mv64xxx_i2c_data *drv_data = dev_get_drvdata(dev);
|
||||
|
||||
reset_control_assert(drv_data->rstc);
|
||||
clk_disable_unprepare(drv_data->reg_clk);
|
||||
clk_disable_unprepare(drv_data->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mv64xxx_i2c_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mv64xxx_i2c_data *drv_data = dev_get_drvdata(dev);
|
||||
|
||||
clk_prepare_enable(drv_data->clk);
|
||||
clk_prepare_enable(drv_data->reg_clk);
|
||||
reset_control_reset(drv_data->rstc);
|
||||
|
||||
mv64xxx_i2c_hw_init(drv_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mv64xxx_i2c_probe(struct platform_device *pd)
|
||||
{
|
||||
|
@ -920,18 +953,22 @@ mv64xxx_i2c_probe(struct platform_device *pd)
|
|||
|
||||
/* Not all platforms have clocks */
|
||||
drv_data->clk = devm_clk_get(&pd->dev, NULL);
|
||||
if (PTR_ERR(drv_data->clk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (!IS_ERR(drv_data->clk))
|
||||
clk_prepare_enable(drv_data->clk);
|
||||
if (IS_ERR(drv_data->clk)) {
|
||||
if (PTR_ERR(drv_data->clk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
drv_data->clk = NULL;
|
||||
}
|
||||
|
||||
drv_data->reg_clk = devm_clk_get(&pd->dev, "reg");
|
||||
if (PTR_ERR(drv_data->reg_clk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (!IS_ERR(drv_data->reg_clk))
|
||||
clk_prepare_enable(drv_data->reg_clk);
|
||||
if (IS_ERR(drv_data->reg_clk)) {
|
||||
if (PTR_ERR(drv_data->reg_clk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
drv_data->reg_clk = NULL;
|
||||
}
|
||||
|
||||
drv_data->irq = platform_get_irq(pd, 0);
|
||||
if (drv_data->irq < 0)
|
||||
return drv_data->irq;
|
||||
|
||||
if (pdata) {
|
||||
drv_data->freq_m = pdata->freq_m;
|
||||
|
@ -942,16 +979,12 @@ mv64xxx_i2c_probe(struct platform_device *pd)
|
|||
} else if (pd->dev.of_node) {
|
||||
rc = mv64xxx_of_config(drv_data, &pd->dev);
|
||||
if (rc)
|
||||
goto exit_clk;
|
||||
}
|
||||
if (drv_data->irq < 0) {
|
||||
rc = drv_data->irq;
|
||||
goto exit_reset;
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = mv64xxx_i2c_init_recovery_info(drv_data, &pd->dev);
|
||||
if (rc == -EPROBE_DEFER)
|
||||
goto exit_reset;
|
||||
return rc;
|
||||
|
||||
drv_data->adapter.dev.parent = &pd->dev;
|
||||
drv_data->adapter.algo = &mv64xxx_i2c_algo;
|
||||
|
@ -962,7 +995,14 @@ mv64xxx_i2c_probe(struct platform_device *pd)
|
|||
platform_set_drvdata(pd, drv_data);
|
||||
i2c_set_adapdata(&drv_data->adapter, drv_data);
|
||||
|
||||
mv64xxx_i2c_hw_init(drv_data);
|
||||
pm_runtime_set_autosuspend_delay(&pd->dev, MSEC_PER_SEC);
|
||||
pm_runtime_use_autosuspend(&pd->dev);
|
||||
pm_runtime_enable(&pd->dev);
|
||||
if (!pm_runtime_enabled(&pd->dev)) {
|
||||
rc = mv64xxx_i2c_runtime_resume(&pd->dev);
|
||||
if (rc)
|
||||
goto exit_disable_pm;
|
||||
}
|
||||
|
||||
rc = request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
|
||||
MV64XXX_I2C_CTLR_NAME, drv_data);
|
||||
|
@ -970,7 +1010,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
|
|||
dev_err(&drv_data->adapter.dev,
|
||||
"mv64xxx: Can't register intr handler irq%d: %d\n",
|
||||
drv_data->irq, rc);
|
||||
goto exit_reset;
|
||||
goto exit_disable_pm;
|
||||
} else if ((rc = i2c_add_numbered_adapter(&drv_data->adapter)) != 0) {
|
||||
dev_err(&drv_data->adapter.dev,
|
||||
"mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
|
||||
|
@ -981,54 +1021,50 @@ mv64xxx_i2c_probe(struct platform_device *pd)
|
|||
|
||||
exit_free_irq:
|
||||
free_irq(drv_data->irq, drv_data);
|
||||
exit_reset:
|
||||
reset_control_assert(drv_data->rstc);
|
||||
exit_clk:
|
||||
clk_disable_unprepare(drv_data->reg_clk);
|
||||
clk_disable_unprepare(drv_data->clk);
|
||||
exit_disable_pm:
|
||||
pm_runtime_disable(&pd->dev);
|
||||
if (!pm_runtime_status_suspended(&pd->dev))
|
||||
mv64xxx_i2c_runtime_suspend(&pd->dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
mv64xxx_i2c_remove(struct platform_device *dev)
|
||||
mv64xxx_i2c_remove(struct platform_device *pd)
|
||||
{
|
||||
struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(dev);
|
||||
struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(pd);
|
||||
|
||||
i2c_del_adapter(&drv_data->adapter);
|
||||
free_irq(drv_data->irq, drv_data);
|
||||
reset_control_assert(drv_data->rstc);
|
||||
clk_disable_unprepare(drv_data->reg_clk);
|
||||
clk_disable_unprepare(drv_data->clk);
|
||||
pm_runtime_disable(&pd->dev);
|
||||
if (!pm_runtime_status_suspended(&pd->dev))
|
||||
mv64xxx_i2c_runtime_suspend(&pd->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mv64xxx_i2c_resume(struct device *dev)
|
||||
static void
|
||||
mv64xxx_i2c_shutdown(struct platform_device *pd)
|
||||
{
|
||||
struct mv64xxx_i2c_data *drv_data = dev_get_drvdata(dev);
|
||||
|
||||
mv64xxx_i2c_hw_init(drv_data);
|
||||
|
||||
return 0;
|
||||
pm_runtime_disable(&pd->dev);
|
||||
if (!pm_runtime_status_suspended(&pd->dev))
|
||||
mv64xxx_i2c_runtime_suspend(&pd->dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mv64xxx_i2c_pm = {
|
||||
.resume = mv64xxx_i2c_resume,
|
||||
static const struct dev_pm_ops mv64xxx_i2c_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(mv64xxx_i2c_runtime_suspend,
|
||||
mv64xxx_i2c_runtime_resume, NULL)
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
#define mv64xxx_i2c_pm_ops (&mv64xxx_i2c_pm)
|
||||
#else
|
||||
#define mv64xxx_i2c_pm_ops NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver mv64xxx_i2c_driver = {
|
||||
.probe = mv64xxx_i2c_probe,
|
||||
.remove = mv64xxx_i2c_remove,
|
||||
.shutdown = mv64xxx_i2c_shutdown,
|
||||
.driver = {
|
||||
.name = MV64XXX_I2C_CTLR_NAME,
|
||||
.pm = mv64xxx_i2c_pm_ops,
|
||||
.pm = &mv64xxx_i2c_pm_ops,
|
||||
.of_match_table = mv64xxx_i2c_of_match_table,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -86,6 +86,9 @@ struct geni_i2c_dev {
|
|||
u32 clk_freq_out;
|
||||
const struct geni_i2c_clk_fld *clk_fld;
|
||||
int suspended;
|
||||
void *dma_buf;
|
||||
size_t xfer_len;
|
||||
dma_addr_t dma_addr;
|
||||
};
|
||||
|
||||
struct geni_i2c_err_log {
|
||||
|
@ -348,14 +351,65 @@ static void geni_i2c_tx_fsm_rst(struct geni_i2c_dev *gi2c)
|
|||
dev_err(gi2c->se.dev, "Timeout resetting TX_FSM\n");
|
||||
}
|
||||
|
||||
static void geni_i2c_rx_msg_cleanup(struct geni_i2c_dev *gi2c,
|
||||
struct i2c_msg *cur)
|
||||
{
|
||||
gi2c->cur_rd = 0;
|
||||
if (gi2c->dma_buf) {
|
||||
if (gi2c->err)
|
||||
geni_i2c_rx_fsm_rst(gi2c);
|
||||
geni_se_rx_dma_unprep(&gi2c->se, gi2c->dma_addr, gi2c->xfer_len);
|
||||
i2c_put_dma_safe_msg_buf(gi2c->dma_buf, cur, !gi2c->err);
|
||||
}
|
||||
}
|
||||
|
||||
static void geni_i2c_tx_msg_cleanup(struct geni_i2c_dev *gi2c,
|
||||
struct i2c_msg *cur)
|
||||
{
|
||||
gi2c->cur_wr = 0;
|
||||
if (gi2c->dma_buf) {
|
||||
if (gi2c->err)
|
||||
geni_i2c_tx_fsm_rst(gi2c);
|
||||
geni_se_tx_dma_unprep(&gi2c->se, gi2c->dma_addr, gi2c->xfer_len);
|
||||
i2c_put_dma_safe_msg_buf(gi2c->dma_buf, cur, !gi2c->err);
|
||||
}
|
||||
}
|
||||
|
||||
static void geni_i2c_stop_xfer(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
int ret;
|
||||
u32 geni_status;
|
||||
struct i2c_msg *cur;
|
||||
|
||||
/* Resume device, as runtime suspend can happen anytime during transfer */
|
||||
ret = pm_runtime_get_sync(gi2c->se.dev);
|
||||
if (ret < 0) {
|
||||
dev_err(gi2c->se.dev, "Failed to resume device: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
geni_status = readl_relaxed(gi2c->se.base + SE_GENI_STATUS);
|
||||
if (geni_status & M_GENI_CMD_ACTIVE) {
|
||||
cur = gi2c->cur;
|
||||
geni_i2c_abort_xfer(gi2c);
|
||||
if (cur->flags & I2C_M_RD)
|
||||
geni_i2c_rx_msg_cleanup(gi2c, cur);
|
||||
else
|
||||
geni_i2c_tx_msg_cleanup(gi2c, cur);
|
||||
}
|
||||
|
||||
pm_runtime_put_sync_suspend(gi2c->se.dev);
|
||||
}
|
||||
|
||||
static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
||||
u32 m_param)
|
||||
{
|
||||
dma_addr_t rx_dma;
|
||||
dma_addr_t rx_dma = 0;
|
||||
unsigned long time_left;
|
||||
void *dma_buf;
|
||||
struct geni_se *se = &gi2c->se;
|
||||
size_t len = msg->len;
|
||||
struct i2c_msg *cur;
|
||||
|
||||
dma_buf = i2c_get_dma_safe_msg_buf(msg, 32);
|
||||
if (dma_buf)
|
||||
|
@ -370,19 +424,18 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
|||
geni_se_select_mode(se, GENI_SE_FIFO);
|
||||
i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
|
||||
dma_buf = NULL;
|
||||
} else {
|
||||
gi2c->xfer_len = len;
|
||||
gi2c->dma_addr = rx_dma;
|
||||
gi2c->dma_buf = dma_buf;
|
||||
}
|
||||
|
||||
cur = gi2c->cur;
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
|
||||
if (!time_left)
|
||||
geni_i2c_abort_xfer(gi2c);
|
||||
|
||||
gi2c->cur_rd = 0;
|
||||
if (dma_buf) {
|
||||
if (gi2c->err)
|
||||
geni_i2c_rx_fsm_rst(gi2c);
|
||||
geni_se_rx_dma_unprep(se, rx_dma, len);
|
||||
i2c_put_dma_safe_msg_buf(dma_buf, msg, !gi2c->err);
|
||||
}
|
||||
geni_i2c_rx_msg_cleanup(gi2c, cur);
|
||||
|
||||
return gi2c->err;
|
||||
}
|
||||
|
@ -390,11 +443,12 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
|||
static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
||||
u32 m_param)
|
||||
{
|
||||
dma_addr_t tx_dma;
|
||||
dma_addr_t tx_dma = 0;
|
||||
unsigned long time_left;
|
||||
void *dma_buf;
|
||||
struct geni_se *se = &gi2c->se;
|
||||
size_t len = msg->len;
|
||||
struct i2c_msg *cur;
|
||||
|
||||
dma_buf = i2c_get_dma_safe_msg_buf(msg, 32);
|
||||
if (dma_buf)
|
||||
|
@ -409,22 +463,21 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
|||
geni_se_select_mode(se, GENI_SE_FIFO);
|
||||
i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
|
||||
dma_buf = NULL;
|
||||
} else {
|
||||
gi2c->xfer_len = len;
|
||||
gi2c->dma_addr = tx_dma;
|
||||
gi2c->dma_buf = dma_buf;
|
||||
}
|
||||
|
||||
if (!dma_buf) /* Get FIFO IRQ */
|
||||
writel_relaxed(1, se->base + SE_GENI_TX_WATERMARK_REG);
|
||||
|
||||
cur = gi2c->cur;
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
|
||||
if (!time_left)
|
||||
geni_i2c_abort_xfer(gi2c);
|
||||
|
||||
gi2c->cur_wr = 0;
|
||||
if (dma_buf) {
|
||||
if (gi2c->err)
|
||||
geni_i2c_tx_fsm_rst(gi2c);
|
||||
geni_se_tx_dma_unprep(se, tx_dma, len);
|
||||
i2c_put_dma_safe_msg_buf(dma_buf, msg, !gi2c->err);
|
||||
}
|
||||
geni_i2c_tx_msg_cleanup(gi2c, cur);
|
||||
|
||||
return gi2c->err;
|
||||
}
|
||||
|
@ -623,6 +676,13 @@ static int geni_i2c_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void geni_i2c_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
|
||||
|
||||
geni_i2c_stop_xfer(gi2c);
|
||||
}
|
||||
|
||||
static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
@ -687,6 +747,7 @@ MODULE_DEVICE_TABLE(of, geni_i2c_dt_match);
|
|||
static struct platform_driver geni_i2c_driver = {
|
||||
.probe = geni_i2c_probe,
|
||||
.remove = geni_i2c_remove,
|
||||
.shutdown = geni_i2c_shutdown,
|
||||
.driver = {
|
||||
.name = "geni_i2c",
|
||||
.pm = &geni_i2c_pm_ops,
|
||||
|
|
|
@ -1603,7 +1603,7 @@ out:
|
|||
|
||||
static u32 qup_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
|
||||
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL_ALL & ~I2C_FUNC_SMBUS_QUICK);
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm qup_i2c_algo = {
|
||||
|
|
|
@ -91,7 +91,6 @@
|
|||
|
||||
#define RCAR_BUS_PHASE_START (MDBS | MIE | ESG)
|
||||
#define RCAR_BUS_PHASE_DATA (MDBS | MIE)
|
||||
#define RCAR_BUS_MASK_DATA (~(ESG | FSB) & 0xFF)
|
||||
#define RCAR_BUS_PHASE_STOP (MDBS | MIE | FSB)
|
||||
|
||||
#define RCAR_IRQ_SEND (MNR | MAL | MST | MAT | MDE)
|
||||
|
@ -120,6 +119,7 @@ enum rcar_i2c_type {
|
|||
};
|
||||
|
||||
struct rcar_i2c_priv {
|
||||
u32 flags;
|
||||
void __iomem *io;
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_msg *msg;
|
||||
|
@ -130,7 +130,6 @@ struct rcar_i2c_priv {
|
|||
|
||||
int pos;
|
||||
u32 icccr;
|
||||
u32 flags;
|
||||
u8 recovery_icmcr; /* protected by adapter lock */
|
||||
enum rcar_i2c_type devtype;
|
||||
struct i2c_client *slave;
|
||||
|
@ -621,27 +620,16 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
|
|||
/*
|
||||
* This driver has a lock-free design because there are IP cores (at least
|
||||
* R-Car Gen2) which have an inherent race condition in their hardware design.
|
||||
* There, we need to clear RCAR_BUS_MASK_DATA bits as soon as possible after
|
||||
* There, we need to switch to RCAR_BUS_PHASE_DATA as soon as possible after
|
||||
* the interrupt was generated, otherwise an unwanted repeated message gets
|
||||
* generated. It turned out that taking a spinlock at the beginning of the ISR
|
||||
* was already causing repeated messages. Thus, this driver was converted to
|
||||
* the now lockless behaviour. Please keep this in mind when hacking the driver.
|
||||
* R-Car Gen3 seems to have this fixed but earlier versions than R-Car Gen2 are
|
||||
* likely affected. Therefore, we have different interrupt handler entries.
|
||||
*/
|
||||
static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
|
||||
static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = ptr;
|
||||
u32 msr, val;
|
||||
|
||||
/* Clear START or STOP immediately, except for REPSTART after read */
|
||||
if (likely(!(priv->flags & ID_P_REP_AFTER_RD))) {
|
||||
val = rcar_i2c_read(priv, ICMCR);
|
||||
rcar_i2c_write(priv, ICMCR, val & RCAR_BUS_MASK_DATA);
|
||||
}
|
||||
|
||||
msr = rcar_i2c_read(priv, ICMSR);
|
||||
|
||||
/* Only handle interrupts that are currently enabled */
|
||||
msr &= rcar_i2c_read(priv, ICMIER);
|
||||
if (!msr) {
|
||||
if (rcar_i2c_slave_irq(priv))
|
||||
return IRQ_HANDLED;
|
||||
|
@ -685,6 +673,41 @@ out:
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t rcar_i2c_gen2_irq(int irq, void *ptr)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = ptr;
|
||||
u32 msr;
|
||||
|
||||
/* Clear START or STOP immediately, except for REPSTART after read */
|
||||
if (likely(!(priv->flags & ID_P_REP_AFTER_RD)))
|
||||
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA);
|
||||
|
||||
/* Only handle interrupts that are currently enabled */
|
||||
msr = rcar_i2c_read(priv, ICMSR);
|
||||
msr &= rcar_i2c_read(priv, ICMIER);
|
||||
|
||||
return rcar_i2c_irq(irq, priv, msr);
|
||||
}
|
||||
|
||||
static irqreturn_t rcar_i2c_gen3_irq(int irq, void *ptr)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = ptr;
|
||||
u32 msr;
|
||||
|
||||
/* Only handle interrupts that are currently enabled */
|
||||
msr = rcar_i2c_read(priv, ICMSR);
|
||||
msr &= rcar_i2c_read(priv, ICMIER);
|
||||
|
||||
/*
|
||||
* Clear START or STOP immediately, except for REPSTART after read or
|
||||
* if a spurious interrupt was detected.
|
||||
*/
|
||||
if (likely(!(priv->flags & ID_P_REP_AFTER_RD) && msr))
|
||||
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA);
|
||||
|
||||
return rcar_i2c_irq(irq, priv, msr);
|
||||
}
|
||||
|
||||
static struct dma_chan *rcar_i2c_request_dma_chan(struct device *dev,
|
||||
enum dma_transfer_direction dir,
|
||||
dma_addr_t port_addr)
|
||||
|
@ -931,6 +954,8 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
|||
struct rcar_i2c_priv *priv;
|
||||
struct i2c_adapter *adap;
|
||||
struct device *dev = &pdev->dev;
|
||||
unsigned long irqflags = 0;
|
||||
irqreturn_t (*irqhandler)(int irq, void *ptr) = rcar_i2c_gen3_irq;
|
||||
int ret;
|
||||
|
||||
/* Otherwise logic will break because some bytes must always use PIO */
|
||||
|
@ -979,6 +1004,11 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
rcar_i2c_write(priv, ICSAR, 0); /* Gen2: must be 0 if not using slave */
|
||||
|
||||
if (priv->devtype < I2C_RCAR_GEN3) {
|
||||
irqflags |= IRQF_NO_THREAD;
|
||||
irqhandler = rcar_i2c_gen2_irq;
|
||||
}
|
||||
|
||||
if (priv->devtype == I2C_RCAR_GEN3) {
|
||||
priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
||||
if (!IS_ERR(priv->rstc)) {
|
||||
|
@ -998,7 +1028,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
|||
priv->flags |= ID_P_HOST_NOTIFY;
|
||||
|
||||
priv->irq = platform_get_irq(pdev, 0);
|
||||
ret = devm_request_irq(dev, priv->irq, rcar_i2c_irq, 0, dev_name(dev), priv);
|
||||
ret = devm_request_irq(dev, priv->irq, irqhandler, irqflags, dev_name(dev), priv);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "cannot get irq %d\n", priv->irq);
|
||||
goto out_pm_disable;
|
||||
|
|
|
@ -781,7 +781,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
|
|||
/* declare our i2c functionality */
|
||||
static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART |
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL_ALL | I2C_FUNC_NOSTART |
|
||||
I2C_FUNC_PROTOCOL_MANGLING;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,475 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* I2C bus driver for CSR SiRFprimaII
|
||||
*
|
||||
* Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
|
||||
*/
|
||||
|
||||
#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_ERR_NOACK 1
|
||||
#define SIRFSOC_I2C_ERR_TIMEOUT 2
|
||||
|
||||
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 = SIRFSOC_I2C_ERR_NOACK;
|
||||
writel(SIRFSOC_I2C_STAT_ERR, siic->base + SIRFSOC_I2C_STATUS);
|
||||
|
||||
if (i2c_stat & SIRFSOC_I2C_STAT_NACK)
|
||||
dev_dbg(&siic->adapter.dev, "ACK not received\n");
|
||||
else
|
||||
dev_err(&siic->adapter.dev, "I2C error\n");
|
||||
|
||||
/*
|
||||
* Due to hardware ANOMALY, we need to reset I2C earlier after
|
||||
* we get NOACK while accessing non-existing clients, otherwise
|
||||
* we will get errors even we access existing clients later
|
||||
*/
|
||||
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();
|
||||
|
||||
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 = i2c_8bit_addr_from_msg(msg);
|
||||
|
||||
/* Reverse direction bit */
|
||||
if (msg->flags & I2C_M_REV_DIR_ADDR)
|
||||
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);
|
||||
|
||||
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 = SIRFSOC_I2C_ERR_TIMEOUT;
|
||||
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);
|
||||
|
||||
/* i2c control doesn't response, reset it */
|
||||
if (siic->err_status == SIRFSOC_I2C_ERR_TIMEOUT) {
|
||||
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();
|
||||
}
|
||||
return siic->err_status ? -EAGAIN : 0;
|
||||
}
|
||||
|
||||
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 i2c_sirfsoc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sirfsoc_i2c *siic;
|
||||
struct i2c_adapter *adap;
|
||||
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) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
adap = &siic->adapter;
|
||||
adap->class = I2C_CLASS_DEPRECATED;
|
||||
|
||||
siic->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(siic->base)) {
|
||||
err = PTR_ERR(siic->base);
|
||||
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->retries = 3;
|
||||
|
||||
adap->dev.of_node = pdev->dev.of_node;
|
||||
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 initialisation */
|
||||
|
||||
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 = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
|
||||
/*
|
||||
* Due to some hardware design issues, we need to tune the formula.
|
||||
* Since i2c is open drain interface that allows the slave to
|
||||
* stall the transaction by holding the SCL line at '0', the RTL
|
||||
* implementation is waiting for SCL feedback from the pin after
|
||||
* setting it to High-Z ('1'). This wait adds to the high-time
|
||||
* interval counter few cycles of the input synchronization
|
||||
* (depending on the SCL_FILTER_REG field), and also the time it
|
||||
* takes for the board pull-up resistor to rise the SCL line.
|
||||
* For slow SCL settings these additions are negligible,
|
||||
* but they start to affect the speed when clock is set to faster
|
||||
* frequencies.
|
||||
* Through the actual tests, use the different user_div value(which
|
||||
* in the divider formula 'Fio / (Fi2c * user_div)') to adapt
|
||||
* the different ranges of i2c bus clock frequency, to make the SCL
|
||||
* more accurate.
|
||||
*/
|
||||
if (bitrate <= 30000)
|
||||
regval = ctrl_speed / (bitrate * 5);
|
||||
else if (bitrate > 30000 && bitrate <= 280000)
|
||||
regval = (2 * ctrl_speed) / (bitrate * 11);
|
||||
else
|
||||
regval = ctrl_speed / (bitrate * 6);
|
||||
|
||||
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)
|
||||
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 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 i2c_adapter *adapter = dev_get_drvdata(dev);
|
||||
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 i2c_adapter *adapter = dev_get_drvdata(dev);
|
||||
struct sirfsoc_i2c *siic = adapter->algo_data;
|
||||
|
||||
clk_enable(siic->clk);
|
||||
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);
|
||||
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[] = {
|
||||
{ .compatible = "sirf,prima2-i2c", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sirfsoc_i2c_of_match);
|
||||
|
||||
static struct platform_driver i2c_sirfsoc_driver = {
|
||||
.driver = {
|
||||
.name = "sirfsoc_i2c",
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &i2c_sirfsoc_pm_ops,
|
||||
#endif
|
||||
.of_match_table = sirfsoc_i2c_of_match,
|
||||
},
|
||||
.probe = i2c_sirfsoc_probe,
|
||||
.remove = 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>");
|
||||
MODULE_AUTHOR("Xiangzhen Ye <Xiangzhen.Ye@csr.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -2035,12 +2035,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
irq_error = platform_get_irq(pdev, 1);
|
||||
if (irq_error <= 0) {
|
||||
if (irq_error != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Failed to get IRQ error: %d\n",
|
||||
irq_error);
|
||||
if (irq_error <= 0)
|
||||
return irq_error ? : -ENOENT;
|
||||
}
|
||||
|
||||
i2c_dev->wakeup_src = of_property_read_bool(pdev->dev.of_node,
|
||||
"wakeup-source");
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -550,7 +550,7 @@ static int tegra_i2c_poll_register(struct tegra_i2c_dev *i2c_dev,
|
|||
void __iomem *addr = i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg);
|
||||
u32 val;
|
||||
|
||||
if (!i2c_dev->atomic_mode && !in_irq())
|
||||
if (!i2c_dev->atomic_mode)
|
||||
return readl_relaxed_poll_timeout(addr, val, !(val & mask),
|
||||
delay_us, timeout_us);
|
||||
|
||||
|
@ -1739,9 +1739,10 @@ static int tegra_i2c_probe(struct platform_device *pdev)
|
|||
/* interrupt will be enabled during of transfer time */
|
||||
irq_set_status_flags(i2c_dev->irq, IRQ_NOAUTOEN);
|
||||
|
||||
err = devm_request_irq(i2c_dev->dev, i2c_dev->irq, tegra_i2c_isr,
|
||||
IRQF_NO_SUSPEND, dev_name(i2c_dev->dev),
|
||||
i2c_dev);
|
||||
err = devm_request_threaded_irq(i2c_dev->dev, i2c_dev->irq,
|
||||
NULL, tegra_i2c_isr,
|
||||
IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
dev_name(i2c_dev->dev), i2c_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
|
@ -1,602 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2017 Sanechips Technology Co., Ltd.
|
||||
* Copyright 2017 Linaro Ltd.
|
||||
*
|
||||
* Author: Baoyou Xie <baoyou.xie@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define REG_CMD 0x04
|
||||
#define REG_DEVADDR_H 0x0C
|
||||
#define REG_DEVADDR_L 0x10
|
||||
#define REG_CLK_DIV_FS 0x14
|
||||
#define REG_CLK_DIV_HS 0x18
|
||||
#define REG_WRCONF 0x1C
|
||||
#define REG_RDCONF 0x20
|
||||
#define REG_DATA 0x24
|
||||
#define REG_STAT 0x28
|
||||
|
||||
#define I2C_STOP 0
|
||||
#define I2C_MASTER BIT(0)
|
||||
#define I2C_ADDR_MODE_TEN BIT(1)
|
||||
#define I2C_IRQ_MSK_ENABLE BIT(3)
|
||||
#define I2C_RW_READ BIT(4)
|
||||
#define I2C_CMB_RW_EN BIT(5)
|
||||
#define I2C_START BIT(6)
|
||||
|
||||
#define I2C_ADDR_LOW_MASK GENMASK(6, 0)
|
||||
#define I2C_ADDR_LOW_SHIFT 0
|
||||
#define I2C_ADDR_HI_MASK GENMASK(2, 0)
|
||||
#define I2C_ADDR_HI_SHIFT 7
|
||||
|
||||
#define I2C_WFIFO_RESET BIT(7)
|
||||
#define I2C_RFIFO_RESET BIT(7)
|
||||
|
||||
#define I2C_IRQ_ACK_CLEAR BIT(7)
|
||||
#define I2C_INT_MASK GENMASK(6, 0)
|
||||
|
||||
#define I2C_TRANS_DONE BIT(0)
|
||||
#define I2C_SR_EDEVICE BIT(1)
|
||||
#define I2C_SR_EDATA BIT(2)
|
||||
|
||||
#define I2C_FIFO_MAX 16
|
||||
|
||||
#define I2C_TIMEOUT msecs_to_jiffies(1000)
|
||||
|
||||
#define DEV(i2c) ((i2c)->adap.dev.parent)
|
||||
|
||||
struct zx2967_i2c {
|
||||
struct i2c_adapter adap;
|
||||
struct clk *clk;
|
||||
struct completion complete;
|
||||
u32 clk_freq;
|
||||
void __iomem *reg_base;
|
||||
size_t residue;
|
||||
int irq;
|
||||
int msg_rd;
|
||||
u8 *cur_trans;
|
||||
u8 access_cnt;
|
||||
int error;
|
||||
};
|
||||
|
||||
static void zx2967_i2c_writel(struct zx2967_i2c *i2c,
|
||||
u32 val, unsigned long reg)
|
||||
{
|
||||
writel_relaxed(val, i2c->reg_base + reg);
|
||||
}
|
||||
|
||||
static u32 zx2967_i2c_readl(struct zx2967_i2c *i2c, unsigned long reg)
|
||||
{
|
||||
return readl_relaxed(i2c->reg_base + reg);
|
||||
}
|
||||
|
||||
static void zx2967_i2c_writesb(struct zx2967_i2c *i2c,
|
||||
void *data, unsigned long reg, int len)
|
||||
{
|
||||
writesb(i2c->reg_base + reg, data, len);
|
||||
}
|
||||
|
||||
static void zx2967_i2c_readsb(struct zx2967_i2c *i2c,
|
||||
void *data, unsigned long reg, int len)
|
||||
{
|
||||
readsb(i2c->reg_base + reg, data, len);
|
||||
}
|
||||
|
||||
static void zx2967_i2c_start_ctrl(struct zx2967_i2c *i2c)
|
||||
{
|
||||
u32 status;
|
||||
u32 ctl;
|
||||
|
||||
status = zx2967_i2c_readl(i2c, REG_STAT);
|
||||
status |= I2C_IRQ_ACK_CLEAR;
|
||||
zx2967_i2c_writel(i2c, status, REG_STAT);
|
||||
|
||||
ctl = zx2967_i2c_readl(i2c, REG_CMD);
|
||||
if (i2c->msg_rd)
|
||||
ctl |= I2C_RW_READ;
|
||||
else
|
||||
ctl &= ~I2C_RW_READ;
|
||||
ctl &= ~I2C_CMB_RW_EN;
|
||||
ctl |= I2C_START;
|
||||
zx2967_i2c_writel(i2c, ctl, REG_CMD);
|
||||
}
|
||||
|
||||
static void zx2967_i2c_flush_fifos(struct zx2967_i2c *i2c)
|
||||
{
|
||||
u32 offset;
|
||||
u32 val;
|
||||
|
||||
if (i2c->msg_rd) {
|
||||
offset = REG_RDCONF;
|
||||
val = I2C_RFIFO_RESET;
|
||||
} else {
|
||||
offset = REG_WRCONF;
|
||||
val = I2C_WFIFO_RESET;
|
||||
}
|
||||
|
||||
val |= zx2967_i2c_readl(i2c, offset);
|
||||
zx2967_i2c_writel(i2c, val, offset);
|
||||
}
|
||||
|
||||
static int zx2967_i2c_empty_rx_fifo(struct zx2967_i2c *i2c, u32 size)
|
||||
{
|
||||
u8 val[I2C_FIFO_MAX] = {0};
|
||||
int i;
|
||||
|
||||
if (size > I2C_FIFO_MAX) {
|
||||
dev_err(DEV(i2c), "fifo size %d over the max value %d\n",
|
||||
size, I2C_FIFO_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
zx2967_i2c_readsb(i2c, val, REG_DATA, size);
|
||||
for (i = 0; i < size; i++) {
|
||||
*i2c->cur_trans++ = val[i];
|
||||
i2c->residue--;
|
||||
}
|
||||
|
||||
barrier();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zx2967_i2c_fill_tx_fifo(struct zx2967_i2c *i2c)
|
||||
{
|
||||
size_t residue = i2c->residue;
|
||||
u8 *buf = i2c->cur_trans;
|
||||
|
||||
if (residue == 0) {
|
||||
dev_err(DEV(i2c), "residue is %d\n", (int)residue);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (residue <= I2C_FIFO_MAX) {
|
||||
zx2967_i2c_writesb(i2c, buf, REG_DATA, residue);
|
||||
|
||||
/* Again update before writing to FIFO to make sure isr sees. */
|
||||
i2c->residue = 0;
|
||||
i2c->cur_trans = NULL;
|
||||
} else {
|
||||
zx2967_i2c_writesb(i2c, buf, REG_DATA, I2C_FIFO_MAX);
|
||||
i2c->residue -= I2C_FIFO_MAX;
|
||||
i2c->cur_trans += I2C_FIFO_MAX;
|
||||
}
|
||||
|
||||
barrier();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zx2967_i2c_reset_hardware(struct zx2967_i2c *i2c)
|
||||
{
|
||||
u32 val;
|
||||
u32 clk_div;
|
||||
|
||||
val = I2C_MASTER | I2C_IRQ_MSK_ENABLE;
|
||||
zx2967_i2c_writel(i2c, val, REG_CMD);
|
||||
|
||||
clk_div = clk_get_rate(i2c->clk) / i2c->clk_freq - 1;
|
||||
zx2967_i2c_writel(i2c, clk_div, REG_CLK_DIV_FS);
|
||||
zx2967_i2c_writel(i2c, clk_div, REG_CLK_DIV_HS);
|
||||
|
||||
zx2967_i2c_writel(i2c, I2C_FIFO_MAX - 1, REG_WRCONF);
|
||||
zx2967_i2c_writel(i2c, I2C_FIFO_MAX - 1, REG_RDCONF);
|
||||
zx2967_i2c_writel(i2c, 1, REG_RDCONF);
|
||||
|
||||
zx2967_i2c_flush_fifos(i2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void zx2967_i2c_isr_clr(struct zx2967_i2c *i2c)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
status = zx2967_i2c_readl(i2c, REG_STAT);
|
||||
status |= I2C_IRQ_ACK_CLEAR;
|
||||
zx2967_i2c_writel(i2c, status, REG_STAT);
|
||||
}
|
||||
|
||||
static irqreturn_t zx2967_i2c_isr(int irq, void *dev_id)
|
||||
{
|
||||
u32 status;
|
||||
struct zx2967_i2c *i2c = (struct zx2967_i2c *)dev_id;
|
||||
|
||||
status = zx2967_i2c_readl(i2c, REG_STAT) & I2C_INT_MASK;
|
||||
zx2967_i2c_isr_clr(i2c);
|
||||
|
||||
if (status & I2C_SR_EDEVICE)
|
||||
i2c->error = -ENXIO;
|
||||
else if (status & I2C_SR_EDATA)
|
||||
i2c->error = -EIO;
|
||||
else if (status & I2C_TRANS_DONE)
|
||||
i2c->error = 0;
|
||||
else
|
||||
goto done;
|
||||
|
||||
complete(&i2c->complete);
|
||||
done:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void zx2967_set_addr(struct zx2967_i2c *i2c, u16 addr)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
val = (addr >> I2C_ADDR_LOW_SHIFT) & I2C_ADDR_LOW_MASK;
|
||||
zx2967_i2c_writel(i2c, val, REG_DEVADDR_L);
|
||||
|
||||
val = (addr >> I2C_ADDR_HI_SHIFT) & I2C_ADDR_HI_MASK;
|
||||
zx2967_i2c_writel(i2c, val, REG_DEVADDR_H);
|
||||
if (val)
|
||||
val = zx2967_i2c_readl(i2c, REG_CMD) | I2C_ADDR_MODE_TEN;
|
||||
else
|
||||
val = zx2967_i2c_readl(i2c, REG_CMD) & ~I2C_ADDR_MODE_TEN;
|
||||
zx2967_i2c_writel(i2c, val, REG_CMD);
|
||||
}
|
||||
|
||||
static int zx2967_i2c_xfer_bytes(struct zx2967_i2c *i2c, u32 bytes)
|
||||
{
|
||||
unsigned long time_left;
|
||||
int rd = i2c->msg_rd;
|
||||
int ret;
|
||||
|
||||
reinit_completion(&i2c->complete);
|
||||
|
||||
if (rd) {
|
||||
zx2967_i2c_writel(i2c, bytes - 1, REG_RDCONF);
|
||||
} else {
|
||||
ret = zx2967_i2c_fill_tx_fifo(i2c);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
zx2967_i2c_start_ctrl(i2c);
|
||||
|
||||
time_left = wait_for_completion_timeout(&i2c->complete,
|
||||
I2C_TIMEOUT);
|
||||
if (time_left == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
if (i2c->error)
|
||||
return i2c->error;
|
||||
|
||||
return rd ? zx2967_i2c_empty_rx_fifo(i2c, bytes) : 0;
|
||||
}
|
||||
|
||||
static int zx2967_i2c_xfer_msg(struct zx2967_i2c *i2c,
|
||||
struct i2c_msg *msg)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
zx2967_i2c_flush_fifos(i2c);
|
||||
|
||||
i2c->cur_trans = msg->buf;
|
||||
i2c->residue = msg->len;
|
||||
i2c->access_cnt = msg->len / I2C_FIFO_MAX;
|
||||
i2c->msg_rd = msg->flags & I2C_M_RD;
|
||||
|
||||
for (i = 0; i < i2c->access_cnt; i++) {
|
||||
ret = zx2967_i2c_xfer_bytes(i2c, I2C_FIFO_MAX);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (i2c->residue > 0) {
|
||||
ret = zx2967_i2c_xfer_bytes(i2c, i2c->residue);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c->residue = 0;
|
||||
i2c->access_cnt = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zx2967_i2c_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct zx2967_i2c *i2c = i2c_get_adapdata(adap);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
zx2967_set_addr(i2c, msgs->addr);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
ret = zx2967_i2c_xfer_msg(i2c, &msgs[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static void
|
||||
zx2967_smbus_xfer_prepare(struct zx2967_i2c *i2c, u16 addr,
|
||||
char read_write, u8 command, int size,
|
||||
union i2c_smbus_data *data)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = zx2967_i2c_readl(i2c, REG_RDCONF);
|
||||
val |= I2C_RFIFO_RESET;
|
||||
zx2967_i2c_writel(i2c, val, REG_RDCONF);
|
||||
zx2967_set_addr(i2c, addr);
|
||||
val = zx2967_i2c_readl(i2c, REG_CMD);
|
||||
val &= ~I2C_RW_READ;
|
||||
zx2967_i2c_writel(i2c, val, REG_CMD);
|
||||
|
||||
switch (size) {
|
||||
case I2C_SMBUS_BYTE:
|
||||
zx2967_i2c_writel(i2c, command, REG_DATA);
|
||||
break;
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
zx2967_i2c_writel(i2c, command, REG_DATA);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
zx2967_i2c_writel(i2c, data->byte, REG_DATA);
|
||||
break;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
zx2967_i2c_writel(i2c, command, REG_DATA);
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
zx2967_i2c_writel(i2c, (data->word >> 8), REG_DATA);
|
||||
zx2967_i2c_writel(i2c, (data->word & 0xff),
|
||||
REG_DATA);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int zx2967_smbus_xfer_read(struct zx2967_i2c *i2c, int size,
|
||||
union i2c_smbus_data *data)
|
||||
{
|
||||
unsigned long time_left;
|
||||
u8 buf[2];
|
||||
u32 val;
|
||||
|
||||
reinit_completion(&i2c->complete);
|
||||
|
||||
val = zx2967_i2c_readl(i2c, REG_CMD);
|
||||
val |= I2C_CMB_RW_EN;
|
||||
zx2967_i2c_writel(i2c, val, REG_CMD);
|
||||
|
||||
val = zx2967_i2c_readl(i2c, REG_CMD);
|
||||
val |= I2C_START;
|
||||
zx2967_i2c_writel(i2c, val, REG_CMD);
|
||||
|
||||
time_left = wait_for_completion_timeout(&i2c->complete,
|
||||
I2C_TIMEOUT);
|
||||
if (time_left == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
if (i2c->error)
|
||||
return i2c->error;
|
||||
|
||||
switch (size) {
|
||||
case I2C_SMBUS_BYTE:
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
val = zx2967_i2c_readl(i2c, REG_DATA);
|
||||
data->byte = val;
|
||||
break;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
case I2C_SMBUS_PROC_CALL:
|
||||
buf[0] = zx2967_i2c_readl(i2c, REG_DATA);
|
||||
buf[1] = zx2967_i2c_readl(i2c, REG_DATA);
|
||||
data->word = (buf[0] << 8) | buf[1];
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zx2967_smbus_xfer_write(struct zx2967_i2c *i2c)
|
||||
{
|
||||
unsigned long time_left;
|
||||
u32 val;
|
||||
|
||||
reinit_completion(&i2c->complete);
|
||||
val = zx2967_i2c_readl(i2c, REG_CMD);
|
||||
val |= I2C_START;
|
||||
zx2967_i2c_writel(i2c, val, REG_CMD);
|
||||
|
||||
time_left = wait_for_completion_timeout(&i2c->complete,
|
||||
I2C_TIMEOUT);
|
||||
if (time_left == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
if (i2c->error)
|
||||
return i2c->error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zx2967_smbus_xfer(struct i2c_adapter *adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size, union i2c_smbus_data *data)
|
||||
{
|
||||
struct zx2967_i2c *i2c = i2c_get_adapdata(adap);
|
||||
|
||||
if (size == I2C_SMBUS_QUICK)
|
||||
read_write = I2C_SMBUS_WRITE;
|
||||
|
||||
switch (size) {
|
||||
case I2C_SMBUS_QUICK:
|
||||
case I2C_SMBUS_BYTE:
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
zx2967_smbus_xfer_prepare(i2c, addr, read_write,
|
||||
command, size, data);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (read_write == I2C_SMBUS_READ)
|
||||
return zx2967_smbus_xfer_read(i2c, size, data);
|
||||
|
||||
return zx2967_smbus_xfer_write(i2c);
|
||||
}
|
||||
|
||||
static u32 zx2967_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C |
|
||||
I2C_FUNC_SMBUS_QUICK |
|
||||
I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_BLOCK_DATA |
|
||||
I2C_FUNC_SMBUS_PROC_CALL |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK;
|
||||
}
|
||||
|
||||
static int __maybe_unused zx2967_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct zx2967_i2c *i2c = dev_get_drvdata(dev);
|
||||
|
||||
i2c_mark_adapter_suspended(&i2c->adap);
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused zx2967_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct zx2967_i2c *i2c = dev_get_drvdata(dev);
|
||||
|
||||
clk_prepare_enable(i2c->clk);
|
||||
i2c_mark_adapter_resumed(&i2c->adap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(zx2967_i2c_dev_pm_ops,
|
||||
zx2967_i2c_suspend, zx2967_i2c_resume);
|
||||
|
||||
static const struct i2c_algorithm zx2967_i2c_algo = {
|
||||
.master_xfer = zx2967_i2c_xfer,
|
||||
.smbus_xfer = zx2967_smbus_xfer,
|
||||
.functionality = zx2967_i2c_func,
|
||||
};
|
||||
|
||||
static const struct i2c_adapter_quirks zx2967_i2c_quirks = {
|
||||
.flags = I2C_AQ_NO_ZERO_LEN,
|
||||
};
|
||||
|
||||
static const struct of_device_id zx2967_i2c_of_match[] = {
|
||||
{ .compatible = "zte,zx296718-i2c", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, zx2967_i2c_of_match);
|
||||
|
||||
static int zx2967_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct zx2967_i2c *i2c;
|
||||
void __iomem *reg_base;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
|
||||
if (!i2c)
|
||||
return -ENOMEM;
|
||||
|
||||
reg_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(reg_base))
|
||||
return PTR_ERR(reg_base);
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "missing controller clock");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable i2c_clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = device_property_read_u32(&pdev->dev, "clock-frequency",
|
||||
&i2c->clk_freq);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "missing clock-frequency");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
i2c->irq = ret;
|
||||
i2c->reg_base = reg_base;
|
||||
i2c->clk = clk;
|
||||
|
||||
init_completion(&i2c->complete);
|
||||
platform_set_drvdata(pdev, i2c);
|
||||
|
||||
ret = zx2967_i2c_reset_hardware(i2c);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize i2c controller\n");
|
||||
goto err_clk_unprepare;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, i2c->irq,
|
||||
zx2967_i2c_isr, 0, dev_name(&pdev->dev), i2c);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request irq %i\n", i2c->irq);
|
||||
goto err_clk_unprepare;
|
||||
}
|
||||
|
||||
i2c_set_adapdata(&i2c->adap, i2c);
|
||||
strlcpy(i2c->adap.name, "zx2967 i2c adapter",
|
||||
sizeof(i2c->adap.name));
|
||||
i2c->adap.algo = &zx2967_i2c_algo;
|
||||
i2c->adap.quirks = &zx2967_i2c_quirks;
|
||||
i2c->adap.nr = pdev->id;
|
||||
i2c->adap.dev.parent = &pdev->dev;
|
||||
i2c->adap.dev.of_node = pdev->dev.of_node;
|
||||
|
||||
ret = i2c_add_numbered_adapter(&i2c->adap);
|
||||
if (ret)
|
||||
goto err_clk_unprepare;
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk_unprepare:
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zx2967_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct zx2967_i2c *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver zx2967_i2c_driver = {
|
||||
.probe = zx2967_i2c_probe,
|
||||
.remove = zx2967_i2c_remove,
|
||||
.driver = {
|
||||
.name = "zx2967_i2c",
|
||||
.of_match_table = zx2967_i2c_of_match,
|
||||
.pm = &zx2967_i2c_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(zx2967_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
|
||||
MODULE_DESCRIPTION("ZTE ZX2967 I2C Bus Controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -225,12 +225,8 @@ static void i2c_acpi_register_device(struct i2c_adapter *adapter,
|
|||
adev->power.flags.ignore_parent = true;
|
||||
acpi_device_set_enumerated(adev);
|
||||
|
||||
if (IS_ERR(i2c_new_client_device(adapter, info))) {
|
||||
if (IS_ERR(i2c_new_client_device(adapter, info)))
|
||||
adev->power.flags.ignore_parent = false;
|
||||
dev_err(&adapter->dev,
|
||||
"failed to add I2C device %s from ACPI\n",
|
||||
dev_name(&adev->dev));
|
||||
}
|
||||
}
|
||||
|
||||
static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level,
|
||||
|
|
|
@ -323,8 +323,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
|
|||
*/
|
||||
unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
|
||||
unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
|
||||
int num = read_write == I2C_SMBUS_READ ? 2 : 1;
|
||||
int i;
|
||||
int nmsgs = read_write == I2C_SMBUS_READ ? 2 : 1;
|
||||
u8 partial_pec = 0;
|
||||
int status;
|
||||
struct i2c_msg msg[2] = {
|
||||
|
@ -340,6 +339,8 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
|
|||
.buf = msgbuf1,
|
||||
},
|
||||
};
|
||||
bool wants_pec = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
|
||||
&& size != I2C_SMBUS_I2C_BLOCK_DATA);
|
||||
|
||||
msgbuf0[0] = command;
|
||||
switch (size) {
|
||||
|
@ -348,13 +349,13 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
|
|||
/* Special case: The read/write field is used as data */
|
||||
msg[0].flags = flags | (read_write == I2C_SMBUS_READ ?
|
||||
I2C_M_RD : 0);
|
||||
num = 1;
|
||||
nmsgs = 1;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE:
|
||||
if (read_write == I2C_SMBUS_READ) {
|
||||
/* Special case: only a read! */
|
||||
msg[0].flags = I2C_M_RD | flags;
|
||||
num = 1;
|
||||
nmsgs = 1;
|
||||
}
|
||||
break;
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
|
@ -375,7 +376,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
|
|||
}
|
||||
break;
|
||||
case I2C_SMBUS_PROC_CALL:
|
||||
num = 2; /* Special case */
|
||||
nmsgs = 2; /* Special case */
|
||||
read_write = I2C_SMBUS_READ;
|
||||
msg[0].len = 3;
|
||||
msg[1].len = 2;
|
||||
|
@ -398,12 +399,11 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
|
|||
}
|
||||
|
||||
i2c_smbus_try_get_dmabuf(&msg[0], command);
|
||||
for (i = 1; i < msg[0].len; i++)
|
||||
msg[0].buf[i] = data->block[i - 1];
|
||||
memcpy(msg[0].buf + 1, data->block, msg[0].len - 1);
|
||||
}
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_PROC_CALL:
|
||||
num = 2; /* Another special case */
|
||||
nmsgs = 2; /* Another special case */
|
||||
read_write = I2C_SMBUS_READ;
|
||||
if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
|
||||
dev_err(&adapter->dev,
|
||||
|
@ -414,8 +414,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
|
|||
|
||||
msg[0].len = data->block[0] + 2;
|
||||
i2c_smbus_try_get_dmabuf(&msg[0], command);
|
||||
for (i = 1; i < msg[0].len; i++)
|
||||
msg[0].buf[i] = data->block[i - 1];
|
||||
memcpy(msg[0].buf + 1, data->block, msg[0].len - 1);
|
||||
|
||||
msg[1].flags |= I2C_M_RECV_LEN;
|
||||
msg[1].len = 1; /* block length will be added by
|
||||
|
@ -437,8 +436,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
|
|||
msg[0].len = data->block[0] + 1;
|
||||
|
||||
i2c_smbus_try_get_dmabuf(&msg[0], command);
|
||||
for (i = 1; i <= data->block[0]; i++)
|
||||
msg[0].buf[i] = data->block[i];
|
||||
memcpy(msg[0].buf + 1, data->block + 1, data->block[0]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -446,33 +444,31 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
|
||||
&& size != I2C_SMBUS_I2C_BLOCK_DATA);
|
||||
if (i) {
|
||||
if (wants_pec) {
|
||||
/* Compute PEC if first message is a write */
|
||||
if (!(msg[0].flags & I2C_M_RD)) {
|
||||
if (num == 1) /* Write only */
|
||||
if (nmsgs == 1) /* Write only */
|
||||
i2c_smbus_add_pec(&msg[0]);
|
||||
else /* Write followed by read */
|
||||
partial_pec = i2c_smbus_msg_pec(0, &msg[0]);
|
||||
}
|
||||
/* Ask for PEC if last message is a read */
|
||||
if (msg[num-1].flags & I2C_M_RD)
|
||||
msg[num-1].len++;
|
||||
if (msg[nmsgs - 1].flags & I2C_M_RD)
|
||||
msg[nmsgs - 1].len++;
|
||||
}
|
||||
|
||||
status = __i2c_transfer(adapter, msg, num);
|
||||
status = __i2c_transfer(adapter, msg, nmsgs);
|
||||
if (status < 0)
|
||||
goto cleanup;
|
||||
if (status != num) {
|
||||
if (status != nmsgs) {
|
||||
status = -EIO;
|
||||
goto cleanup;
|
||||
}
|
||||
status = 0;
|
||||
|
||||
/* Check PEC if last message is a read */
|
||||
if (i && (msg[num-1].flags & I2C_M_RD)) {
|
||||
status = i2c_smbus_check_pec(partial_pec, &msg[num-1]);
|
||||
if (wants_pec && (msg[nmsgs - 1].flags & I2C_M_RD)) {
|
||||
status = i2c_smbus_check_pec(partial_pec, &msg[nmsgs - 1]);
|
||||
if (status < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -490,8 +486,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
|
|||
data->word = msgbuf1[0] | (msgbuf1[1] << 8);
|
||||
break;
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
for (i = 0; i < data->block[0]; i++)
|
||||
data->block[i + 1] = msg[1].buf[i];
|
||||
memcpy(data->block + 1, msg[1].buf, data->block[0]);
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
case I2C_SMBUS_BLOCK_PROC_CALL:
|
||||
|
@ -502,8 +497,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
|
|||
status = -EPROTO;
|
||||
goto cleanup;
|
||||
}
|
||||
for (i = 0; i < msg[1].buf[0] + 1; i++)
|
||||
data->block[i] = msg[1].buf[i];
|
||||
memcpy(data->block, msg[1].buf, msg[1].buf[0] + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
enum testunit_cmds {
|
||||
TU_CMD_READ_BYTES = 1, /* save 0 for ABORT, RESET or similar */
|
||||
TU_CMD_HOST_NOTIFY,
|
||||
TU_CMD_SMBUS_BLOCK_PROC_CALL,
|
||||
TU_NUM_CMDS
|
||||
};
|
||||
|
||||
|
@ -88,6 +89,8 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
|
|||
enum i2c_slave_event event, u8 *val)
|
||||
{
|
||||
struct testunit_data *tu = i2c_get_clientdata(client);
|
||||
bool is_proc_call = tu->reg_idx == 3 && tu->regs[TU_REG_DATAL] == 1 &&
|
||||
tu->regs[TU_REG_CMD] == TU_CMD_SMBUS_BLOCK_PROC_CALL;
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
|
@ -118,12 +121,17 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
|
|||
fallthrough;
|
||||
|
||||
case I2C_SLAVE_WRITE_REQUESTED:
|
||||
memset(tu->regs, 0, TU_NUM_REGS);
|
||||
tu->reg_idx = 0;
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_READ_REQUESTED:
|
||||
case I2C_SLAVE_READ_PROCESSED:
|
||||
*val = TU_CUR_VERSION;
|
||||
if (is_proc_call && tu->regs[TU_REG_DATAH])
|
||||
tu->regs[TU_REG_DATAH]--;
|
||||
fallthrough;
|
||||
|
||||
case I2C_SLAVE_READ_REQUESTED:
|
||||
*val = is_proc_call ? tu->regs[TU_REG_DATAH] : TU_CUR_VERSION;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
*/
|
||||
|
||||
#define DEBUG 1
|
||||
#define pr_fmt(fmt) "i2c-stub: " fmt
|
||||
|
||||
#include <linux/errno.h>
|
||||
|
|
|
@ -49,60 +49,112 @@ static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
|
||||
struct platform_device *pdev)
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
|
||||
struct fwnode_handle *fwdev,
|
||||
unsigned int *adr)
|
||||
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *adapter_np, *child;
|
||||
struct i2c_adapter *adapter;
|
||||
unsigned *values;
|
||||
int i = 0;
|
||||
unsigned long long adr64;
|
||||
acpi_status status;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
status = acpi_evaluate_integer(ACPI_HANDLE_FWNODE(fwdev),
|
||||
METHOD_NAME__ADR,
|
||||
NULL, &adr64);
|
||||
|
||||
adapter_np = of_parse_phandle(np, "i2c-parent", 0);
|
||||
if (!adapter_np) {
|
||||
dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
|
||||
return -ENODEV;
|
||||
if (!ACPI_SUCCESS(status)) {
|
||||
dev_err(dev, "Cannot get address\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
adapter = of_find_i2c_adapter_by_node(adapter_np);
|
||||
of_node_put(adapter_np);
|
||||
|
||||
*adr = adr64;
|
||||
if (*adr != adr64) {
|
||||
dev_err(dev, "Address out of range\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
|
||||
struct fwnode_handle *fwdev,
|
||||
unsigned int *adr)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int i2c_mux_gpio_probe_fw(struct gpiomux *mux,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *adapter_np;
|
||||
struct i2c_adapter *adapter = NULL;
|
||||
struct fwnode_handle *child;
|
||||
unsigned *values;
|
||||
int rc, i = 0;
|
||||
|
||||
if (is_of_node(dev->fwnode)) {
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
adapter_np = of_parse_phandle(np, "i2c-parent", 0);
|
||||
if (!adapter_np) {
|
||||
dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
adapter = of_find_i2c_adapter_by_node(adapter_np);
|
||||
of_node_put(adapter_np);
|
||||
|
||||
} else if (is_acpi_node(dev->fwnode)) {
|
||||
/*
|
||||
* In ACPI land the mux should be a direct child of the i2c
|
||||
* bus it muxes.
|
||||
*/
|
||||
acpi_handle dev_handle = ACPI_HANDLE(dev->parent);
|
||||
|
||||
adapter = i2c_acpi_find_adapter_by_handle(dev_handle);
|
||||
}
|
||||
|
||||
if (!adapter)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
mux->data.parent = i2c_adapter_id(adapter);
|
||||
put_device(&adapter->dev);
|
||||
|
||||
mux->data.n_values = of_get_child_count(np);
|
||||
|
||||
values = devm_kcalloc(&pdev->dev,
|
||||
mux->data.n_values = device_get_child_node_count(dev);
|
||||
values = devm_kcalloc(dev,
|
||||
mux->data.n_values, sizeof(*mux->data.values),
|
||||
GFP_KERNEL);
|
||||
if (!values) {
|
||||
dev_err(&pdev->dev, "Cannot allocate values array");
|
||||
dev_err(dev, "Cannot allocate values array");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
of_property_read_u32(child, "reg", values + i);
|
||||
device_for_each_child_node(dev, child) {
|
||||
if (is_of_node(child)) {
|
||||
fwnode_property_read_u32(child, "reg", values + i);
|
||||
|
||||
} else if (is_acpi_node(child)) {
|
||||
rc = i2c_mux_gpio_get_acpi_adr(dev, child, values + i);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
mux->data.values = values;
|
||||
|
||||
if (of_property_read_u32(np, "idle-state", &mux->data.idle))
|
||||
if (fwnode_property_read_u32(dev->fwnode, "idle-state", &mux->data.idle))
|
||||
mux->data.idle = I2C_MUX_GPIO_NO_IDLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -118,7 +170,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
if (!dev_get_platdata(&pdev->dev)) {
|
||||
ret = i2c_mux_gpio_probe_dt(mux, pdev);
|
||||
ret = i2c_mux_gpio_probe_fw(mux, pdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
|
|
|
@ -1,35 +1,8 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
|
||||
/*
|
||||
* drivers/i2c/muxes/i2c-mux-mlxcpld.c
|
||||
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2016 Michael Shych <michaels@mellanox.com>
|
||||
* Mellanox i2c mux driver
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the names of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* Copyright (C) 2016-2020 Mellanox Technologies
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
|
@ -38,19 +11,19 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/x86/mlxcpld.h>
|
||||
#include <linux/platform_data/mlxcpld.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define CPLD_MUX_MAX_NCHANS 8
|
||||
|
||||
/* mlxcpld_mux - mux control structure:
|
||||
* @last_chan - last register value
|
||||
* @last_val - last selected register value or -1 if mux deselected
|
||||
* @client - I2C device client
|
||||
* @pdata: platform data
|
||||
*/
|
||||
struct mlxcpld_mux {
|
||||
u8 last_chan;
|
||||
int last_val;
|
||||
struct i2c_client *client;
|
||||
struct mlxcpld_mux_plat_data pdata;
|
||||
};
|
||||
|
||||
/* MUX logic description.
|
||||
|
@ -81,37 +54,50 @@ struct mlxcpld_mux {
|
|||
*
|
||||
*/
|
||||
|
||||
static const struct i2c_device_id mlxcpld_mux_id[] = {
|
||||
{ "mlxcpld_mux_module", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mlxcpld_mux_id);
|
||||
|
||||
/* Write to mux register. Don't use i2c_transfer() and i2c_smbus_xfer()
|
||||
* for this as they will try to lock adapter a second time.
|
||||
*/
|
||||
static int mlxcpld_mux_reg_write(struct i2c_adapter *adap,
|
||||
struct i2c_client *client, u8 val)
|
||||
struct mlxcpld_mux *mux, u32 val)
|
||||
{
|
||||
struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev);
|
||||
union i2c_smbus_data data = { .byte = val };
|
||||
struct i2c_client *client = mux->client;
|
||||
union i2c_smbus_data data;
|
||||
struct i2c_msg msg;
|
||||
u8 buf[3];
|
||||
|
||||
return __i2c_smbus_xfer(adap, client->addr, client->flags,
|
||||
I2C_SMBUS_WRITE, pdata->sel_reg_addr,
|
||||
I2C_SMBUS_BYTE_DATA, &data);
|
||||
switch (mux->pdata.reg_size) {
|
||||
case 1:
|
||||
data.byte = val;
|
||||
return __i2c_smbus_xfer(adap, client->addr, client->flags,
|
||||
I2C_SMBUS_WRITE, mux->pdata.sel_reg_addr,
|
||||
I2C_SMBUS_BYTE_DATA, &data);
|
||||
case 2:
|
||||
buf[0] = mux->pdata.sel_reg_addr >> 8;
|
||||
buf[1] = mux->pdata.sel_reg_addr;
|
||||
buf[2] = val;
|
||||
msg.addr = client->addr;
|
||||
msg.buf = buf;
|
||||
msg.len = mux->pdata.reg_size + 1;
|
||||
msg.flags = 0;
|
||||
return __i2c_transfer(adap, &msg, 1);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
|
||||
{
|
||||
struct mlxcpld_mux *data = i2c_mux_priv(muxc);
|
||||
struct i2c_client *client = data->client;
|
||||
u8 regval = chan + 1;
|
||||
struct mlxcpld_mux *mux = i2c_mux_priv(muxc);
|
||||
u32 regval = chan;
|
||||
int err = 0;
|
||||
|
||||
if (mux->pdata.reg_size == 1)
|
||||
regval += 1;
|
||||
|
||||
/* Only select the channel if its different from the last channel */
|
||||
if (data->last_chan != regval) {
|
||||
err = mlxcpld_mux_reg_write(muxc->parent, client, regval);
|
||||
data->last_chan = err < 0 ? 0 : regval;
|
||||
if (mux->last_val != regval) {
|
||||
err = mlxcpld_mux_reg_write(muxc->parent, mux, regval);
|
||||
mux->last_val = err < 0 ? -1 : regval;
|
||||
}
|
||||
|
||||
return err;
|
||||
|
@ -119,56 +105,64 @@ static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
|
|||
|
||||
static int mlxcpld_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
|
||||
{
|
||||
struct mlxcpld_mux *data = i2c_mux_priv(muxc);
|
||||
struct i2c_client *client = data->client;
|
||||
struct mlxcpld_mux *mux = i2c_mux_priv(muxc);
|
||||
|
||||
/* Deselect active channel */
|
||||
data->last_chan = 0;
|
||||
mux->last_val = -1;
|
||||
|
||||
return mlxcpld_mux_reg_write(muxc->parent, client, data->last_chan);
|
||||
return mlxcpld_mux_reg_write(muxc->parent, mux, 0);
|
||||
}
|
||||
|
||||
/* Probe/reomove functions */
|
||||
static int mlxcpld_mux_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int mlxcpld_mux_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_adapter *adap = client->adapter;
|
||||
struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct i2c_client *client = to_i2c_client(pdev->dev.parent);
|
||||
struct i2c_mux_core *muxc;
|
||||
int num, force;
|
||||
struct mlxcpld_mux *data;
|
||||
int err;
|
||||
int num, err;
|
||||
u32 func;
|
||||
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
|
||||
switch (pdata->reg_size) {
|
||||
case 1:
|
||||
func = I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
|
||||
break;
|
||||
case 2:
|
||||
func = I2C_FUNC_I2C;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, func))
|
||||
return -ENODEV;
|
||||
|
||||
muxc = i2c_mux_alloc(adap, &client->dev, CPLD_MUX_MAX_NCHANS,
|
||||
muxc = i2c_mux_alloc(client->adapter, &pdev->dev, pdata->num_adaps,
|
||||
sizeof(*data), 0, mlxcpld_mux_select_chan,
|
||||
mlxcpld_mux_deselect);
|
||||
if (!muxc)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, muxc);
|
||||
data = i2c_mux_priv(muxc);
|
||||
i2c_set_clientdata(client, muxc);
|
||||
data->client = client;
|
||||
data->last_chan = 0; /* force the first selection */
|
||||
memcpy(&data->pdata, pdata, sizeof(*pdata));
|
||||
data->last_val = -1; /* force the first selection */
|
||||
|
||||
/* Create an adapter for each channel. */
|
||||
for (num = 0; num < CPLD_MUX_MAX_NCHANS; num++) {
|
||||
if (num >= pdata->num_adaps)
|
||||
/* discard unconfigured channels */
|
||||
break;
|
||||
|
||||
force = pdata->adap_ids[num];
|
||||
|
||||
err = i2c_mux_add_adapter(muxc, force, num, 0);
|
||||
for (num = 0; num < pdata->num_adaps; num++) {
|
||||
err = i2c_mux_add_adapter(muxc, 0, pdata->chan_ids[num], 0);
|
||||
if (err)
|
||||
goto virt_reg_failed;
|
||||
}
|
||||
|
||||
/* Notify caller when all channels' adapters are created. */
|
||||
if (pdata->completion_notify)
|
||||
pdata->completion_notify(pdata->handle, muxc->parent, muxc->adapter);
|
||||
|
||||
return 0;
|
||||
|
||||
virt_reg_failed:
|
||||
|
@ -176,24 +170,23 @@ virt_reg_failed:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int mlxcpld_mux_remove(struct i2c_client *client)
|
||||
static int mlxcpld_mux_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
||||
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_mux_del_adapters(muxc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver mlxcpld_mux_driver = {
|
||||
.driver = {
|
||||
.name = "mlxcpld-mux",
|
||||
static struct platform_driver mlxcpld_mux_driver = {
|
||||
.driver = {
|
||||
.name = "i2c-mux-mlxcpld",
|
||||
},
|
||||
.probe = mlxcpld_mux_probe,
|
||||
.remove = mlxcpld_mux_remove,
|
||||
.id_table = mlxcpld_mux_id,
|
||||
.probe = mlxcpld_mux_probe,
|
||||
.remove = mlxcpld_mux_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(mlxcpld_mux_driver);
|
||||
module_platform_driver(mlxcpld_mux_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Shych (michaels@mellanox.com)");
|
||||
MODULE_DESCRIPTION("Mellanox I2C-CPLD-MUX driver");
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
|
||||
/*
|
||||
* Mellanox I2C multiplexer support in CPLD
|
||||
*
|
||||
* Copyright (C) 2016-2020 Mellanox Technologies
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_I2C_MLXCPLD_H
|
||||
#define _LINUX_I2C_MLXCPLD_H
|
||||
|
||||
/* Platform data for the CPLD I2C multiplexers */
|
||||
|
||||
/* mlxcpld_mux_plat_data - per mux data, used with i2c_register_board_info
|
||||
* @chan_ids - channels array
|
||||
* @num_adaps - number of adapters
|
||||
* @sel_reg_addr - mux select register offset in CPLD space
|
||||
* @reg_size: register size in bytes
|
||||
* @handle: handle to be passed by callback
|
||||
* @completion_notify: callback to notify when all the adapters are created
|
||||
*/
|
||||
struct mlxcpld_mux_plat_data {
|
||||
int *chan_ids;
|
||||
int num_adaps;
|
||||
int sel_reg_addr;
|
||||
u8 reg_size;
|
||||
void *handle;
|
||||
int (*completion_notify)(void *handle, struct i2c_adapter *parent,
|
||||
struct i2c_adapter *adapters[]);
|
||||
};
|
||||
|
||||
#endif /* _LINUX_I2C_MLXCPLD_H */
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* mlxcpld.h - Mellanox I2C multiplexer support in CPLD
|
||||
*
|
||||
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2016 Michael Shych <michaels@mellanox.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the names of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_I2C_MLXCPLD_H
|
||||
#define _LINUX_I2C_MLXCPLD_H
|
||||
|
||||
/* Platform data for the CPLD I2C multiplexers */
|
||||
|
||||
/* mlxcpld_mux_plat_data - per mux data, used with i2c_register_board_info
|
||||
* @adap_ids - adapter array
|
||||
* @num_adaps - number of adapters
|
||||
* @sel_reg_addr - mux select register offset in CPLD space
|
||||
*/
|
||||
struct mlxcpld_mux_plat_data {
|
||||
int *adap_ids;
|
||||
int num_adaps;
|
||||
int sel_reg_addr;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_I2C_MLXCPLD_H */
|
|
@ -1,25 +1,10 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||
/*
|
||||
i2c-dev.h - i2c-bus driver, char device interface
|
||||
|
||||
Copyright (C) 1995-97 Simon G. Vogl
|
||||
Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02110-1301 USA.
|
||||
*/
|
||||
* i2c-dev.h - I2C bus char device interface
|
||||
*
|
||||
* Copyright (C) 1995-97 Simon G. Vogl
|
||||
* Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_I2C_DEV_H
|
||||
#define _UAPI_LINUX_I2C_DEV_H
|
||||
|
|
|
@ -1,29 +1,11 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* */
|
||||
/* i2c.h - definitions for the i2c-bus interface */
|
||||
/* */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Copyright (C) 1995-2000 Simon G. Vogl
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02110-1301 USA. */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and
|
||||
Frodo Looijaard <frodol@dds.nl> */
|
||||
/*
|
||||
* i2c.h - definitions for the I2C bus interface
|
||||
*
|
||||
* Copyright (C) 1995-2000 Simon G. Vogl
|
||||
* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and
|
||||
* Frodo Looijaard <frodol@dds.nl>
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_I2C_H
|
||||
#define _UAPI_LINUX_I2C_H
|
||||
|
@ -32,18 +14,41 @@
|
|||
|
||||
/**
|
||||
* struct i2c_msg - an I2C transaction segment beginning with START
|
||||
* @addr: Slave address, either seven or ten bits. When this is a ten
|
||||
* bit address, I2C_M_TEN must be set in @flags and the adapter
|
||||
* must support I2C_FUNC_10BIT_ADDR.
|
||||
* @flags: I2C_M_RD is handled by all adapters. No other flags may be
|
||||
* provided unless the adapter exported the relevant I2C_FUNC_*
|
||||
* flags through i2c_check_functionality().
|
||||
* @len: Number of data bytes in @buf being read from or written to the
|
||||
* I2C slave address. For read transactions where I2C_M_RECV_LEN
|
||||
* is set, the caller guarantees that this buffer can hold up to
|
||||
* 32 bytes in addition to the initial length byte sent by the
|
||||
* slave (plus, if used, the SMBus PEC); and this value will be
|
||||
* incremented by the number of block data bytes received.
|
||||
*
|
||||
* @addr: Slave address, either 7 or 10 bits. When this is a 10 bit address,
|
||||
* %I2C_M_TEN must be set in @flags and the adapter must support
|
||||
* %I2C_FUNC_10BIT_ADDR.
|
||||
*
|
||||
* @flags:
|
||||
* Supported by all adapters:
|
||||
* %I2C_M_RD: read data (from slave to master). Guaranteed to be 0x0001!
|
||||
*
|
||||
* Optional:
|
||||
* %I2C_M_DMA_SAFE: the buffer of this message is DMA safe. Makes only sense
|
||||
* in kernelspace, because userspace buffers are copied anyway
|
||||
*
|
||||
* Only if I2C_FUNC_10BIT_ADDR is set:
|
||||
* %I2C_M_TEN: this is a 10 bit chip address
|
||||
*
|
||||
* Only if I2C_FUNC_SMBUS_READ_BLOCK_DATA is set:
|
||||
* %I2C_M_RECV_LEN: message length will be first received byte
|
||||
*
|
||||
* Only if I2C_FUNC_NOSTART is set:
|
||||
* %I2C_M_NOSTART: skip repeated start sequence
|
||||
|
||||
* Only if I2C_FUNC_PROTOCOL_MANGLING is set:
|
||||
* %I2C_M_NO_RD_ACK: in a read message, master ACK/NACK bit is skipped
|
||||
* %I2C_M_IGNORE_NAK: treat NACK from client as ACK
|
||||
* %I2C_M_REV_DIR_ADDR: toggles the Rd/Wr bit
|
||||
* %I2C_M_STOP: force a STOP condition after the message
|
||||
*
|
||||
* @len: Number of data bytes in @buf being read from or written to the I2C
|
||||
* slave address. For read transactions where %I2C_M_RECV_LEN is set, the
|
||||
* caller guarantees that this buffer can hold up to %I2C_SMBUS_BLOCK_MAX
|
||||
* bytes in addition to the initial length byte sent by the slave (plus,
|
||||
* if used, the SMBus PEC); and this value will be incremented by the number
|
||||
* of block data bytes received.
|
||||
*
|
||||
* @buf: The buffer into which data is read, or from which it's written.
|
||||
*
|
||||
* An i2c_msg is the low level representation of one segment of an I2C
|
||||
|
@ -60,40 +65,36 @@
|
|||
* group, it is followed by a STOP. Otherwise it is followed by the next
|
||||
* @i2c_msg transaction segment, beginning with a (repeated) START.
|
||||
*
|
||||
* Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then
|
||||
* Alternatively, when the adapter supports %I2C_FUNC_PROTOCOL_MANGLING then
|
||||
* passing certain @flags may have changed those standard protocol behaviors.
|
||||
* Those flags are only for use with broken/nonconforming slaves, and with
|
||||
* adapters which are known to support the specific mangling options they
|
||||
* need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR).
|
||||
* adapters which are known to support the specific mangling options they need.
|
||||
*/
|
||||
struct i2c_msg {
|
||||
__u16 addr; /* slave address */
|
||||
__u16 addr;
|
||||
__u16 flags;
|
||||
#define I2C_M_RD 0x0001 /* read data, from slave to master */
|
||||
/* I2C_M_RD is guaranteed to be 0x0001! */
|
||||
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
|
||||
#define I2C_M_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */
|
||||
/* makes only sense in kernelspace */
|
||||
/* userspace buffers are copied anyway */
|
||||
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
|
||||
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
|
||||
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
|
||||
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
|
||||
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
|
||||
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
|
||||
__u16 len; /* msg length */
|
||||
__u8 *buf; /* pointer to msg data */
|
||||
#define I2C_M_RD 0x0001 /* guaranteed to be 0x0001! */
|
||||
#define I2C_M_TEN 0x0010 /* use only if I2C_FUNC_10BIT_ADDR */
|
||||
#define I2C_M_DMA_SAFE 0x0200 /* use only in kernel space */
|
||||
#define I2C_M_RECV_LEN 0x0400 /* use only if I2C_FUNC_SMBUS_READ_BLOCK_DATA */
|
||||
#define I2C_M_NO_RD_ACK 0x0800 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
|
||||
#define I2C_M_IGNORE_NAK 0x1000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
|
||||
#define I2C_M_REV_DIR_ADDR 0x2000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
|
||||
#define I2C_M_NOSTART 0x4000 /* use only if I2C_FUNC_NOSTART */
|
||||
#define I2C_M_STOP 0x8000 /* use only if I2C_FUNC_PROTOCOL_MANGLING */
|
||||
__u16 len;
|
||||
__u8 *buf;
|
||||
};
|
||||
|
||||
/* To determine what functionality is present */
|
||||
|
||||
#define I2C_FUNC_I2C 0x00000001
|
||||
#define I2C_FUNC_10BIT_ADDR 0x00000002
|
||||
#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_IGNORE_NAK etc. */
|
||||
#define I2C_FUNC_10BIT_ADDR 0x00000002 /* required for I2C_M_TEN */
|
||||
#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* required for I2C_M_IGNORE_NAK etc. */
|
||||
#define I2C_FUNC_SMBUS_PEC 0x00000008
|
||||
#define I2C_FUNC_NOSTART 0x00000010 /* I2C_M_NOSTART */
|
||||
#define I2C_FUNC_NOSTART 0x00000010 /* required for I2C_M_NOSTART */
|
||||
#define I2C_FUNC_SLAVE 0x00000020
|
||||
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */
|
||||
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 or later */
|
||||
#define I2C_FUNC_SMBUS_QUICK 0x00010000
|
||||
#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000
|
||||
#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000
|
||||
|
@ -102,11 +103,11 @@ struct i2c_msg {
|
|||
#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000
|
||||
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000
|
||||
#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000
|
||||
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000
|
||||
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 /* required for I2C_M_RECV_LEN */
|
||||
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
|
||||
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */
|
||||
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
|
||||
#define I2C_FUNC_SMBUS_HOST_NOTIFY 0x10000000
|
||||
#define I2C_FUNC_SMBUS_HOST_NOTIFY 0x10000000 /* SMBus 2.0 or later */
|
||||
|
||||
#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE)
|
||||
|
@ -128,6 +129,11 @@ struct i2c_msg {
|
|||
I2C_FUNC_SMBUS_I2C_BLOCK | \
|
||||
I2C_FUNC_SMBUS_PEC)
|
||||
|
||||
/* if I2C_M_RECV_LEN is also supported */
|
||||
#define I2C_FUNC_SMBUS_EMUL_ALL (I2C_FUNC_SMBUS_EMUL | \
|
||||
I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
|
||||
I2C_FUNC_SMBUS_BLOCK_PROC_CALL)
|
||||
|
||||
/*
|
||||
* Data for SMBus Messages
|
||||
*/
|
||||
|
|
Загрузка…
Ссылка в новой задаче