Merge branch 'i2c/for-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: "I2C has for you two new drivers (Tegra BPMP and STM32F4), interrupt support for pca954x muxes, and a bunch of driver bugfixes and improvements. Nothing really special this cycle. A few commits have been added to my tree just recently. Those are the Tegra BPMP driver and a few straightforward bugfixes or cleanups which I prefer to have upstream rather soonish. The rest had proper linux-next exposure" * 'i2c/for-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (25 commits) i2c: thunderx: Replace pci_enable_msix() i2c: exynos5: fix arbitration lost handling i2c: exynos5: disable fifo-almost-empty irq signal when necessary i2c: at91: ensure state is restored after suspending i2c: bcm2835: Avoid possible NULL ptr dereference i2c: Add Tegra BPMP I2C proxy driver dt-bindings: Add Tegra186 BPMP I2C binding misc: eeprom: at24: use device_property_*() functions instead of of_get_property() i2c: mux: pca954x: Add interrupt controller support dt: bindings: i2c-mux-pca954x: Add documentation for interrupt controller i2c: mux: pca954x: Add missing pca9542 definition to chip_desc i2c: riic: correctly finish transfers i2c: i801: Add support for Intel Gemini Lake i2c: mux: pca9541: Export OF device ID table as module aliases i2c: mux: pca954x: Export OF device ID table as module aliases i2c: mux: mlxcpld: remove unused including <linux/version.h> i2c: busses: constify i2c_algorithm structures i2c: i2c-mux-gpio: rename i2c-gpio-mux to i2c-mux-gpio i2c: sh_mobile: document support for r8a7796 (R-Car M3-W) i2c: i2c-cros-ec-tunnel: Reduce logging noise ...
This commit is contained in:
Коммит
7067739df2
|
@ -19,7 +19,14 @@ Optional Properties:
|
||||||
- i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all
|
- i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all
|
||||||
children in idle state. This is necessary for example, if there are several
|
children in idle state. This is necessary for example, if there are several
|
||||||
multiplexers on the bus and the devices behind them use same I2C addresses.
|
multiplexers on the bus and the devices behind them use same I2C addresses.
|
||||||
|
- interrupt-parent: Phandle for the interrupt controller that services
|
||||||
|
interrupts for this device.
|
||||||
|
- interrupts: Interrupt mapping for IRQ.
|
||||||
|
- interrupt-controller: Marks the device node as an interrupt controller.
|
||||||
|
- #interrupt-cells : Should be two.
|
||||||
|
- first cell is the pin number
|
||||||
|
- second cell is used to specify flags.
|
||||||
|
See also Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -29,6 +36,11 @@ Example:
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
reg = <0x74>;
|
reg = <0x74>;
|
||||||
|
|
||||||
|
interrupt-parent = <&ipic>;
|
||||||
|
interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <2>;
|
||||||
|
|
||||||
i2c@2 {
|
i2c@2 {
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
|
|
|
@ -10,6 +10,7 @@ Required properties:
|
||||||
- "renesas,iic-r8a7793" (R-Car M2-N)
|
- "renesas,iic-r8a7793" (R-Car M2-N)
|
||||||
- "renesas,iic-r8a7794" (R-Car E2)
|
- "renesas,iic-r8a7794" (R-Car E2)
|
||||||
- "renesas,iic-r8a7795" (R-Car H3)
|
- "renesas,iic-r8a7795" (R-Car H3)
|
||||||
|
- "renesas,iic-r8a7796" (R-Car M3-W)
|
||||||
- "renesas,iic-sh73a0" (SH-Mobile AG5)
|
- "renesas,iic-sh73a0" (SH-Mobile AG5)
|
||||||
- "renesas,rcar-gen2-iic" (generic R-Car Gen2 compatible device)
|
- "renesas,rcar-gen2-iic" (generic R-Car Gen2 compatible device)
|
||||||
- "renesas,rcar-gen3-iic" (generic R-Car Gen3 compatible device)
|
- "renesas,rcar-gen3-iic" (generic R-Car Gen3 compatible device)
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
* I2C controller embedded in STMicroelectronics STM32 I2C platform
|
||||||
|
|
||||||
|
Required properties :
|
||||||
|
- compatible : Must be "st,stm32f4-i2c"
|
||||||
|
- reg : Offset and length of the register set for the device
|
||||||
|
- interrupts : Must contain the interrupt id for I2C event and then the
|
||||||
|
interrupt id for I2C error.
|
||||||
|
- resets: Must contain the phandle to the reset controller.
|
||||||
|
- clocks: Must contain the input clock of the I2C instance.
|
||||||
|
- A pinctrl state named "default" must be defined to set pins in mode of
|
||||||
|
operation for I2C transfer
|
||||||
|
- #address-cells = <1>;
|
||||||
|
- #size-cells = <0>;
|
||||||
|
|
||||||
|
Optional properties :
|
||||||
|
- clock-frequency : Desired I2C bus clock frequency in Hz. If not specified,
|
||||||
|
the default 100 kHz frequency will be used. As only Normal and Fast modes
|
||||||
|
are supported, possible values are 100000 and 400000.
|
||||||
|
|
||||||
|
Example :
|
||||||
|
|
||||||
|
i2c@40005400 {
|
||||||
|
compatible = "st,stm32f4-i2c";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
reg = <0x40005400 0x400>;
|
||||||
|
interrupts = <31>,
|
||||||
|
<32>;
|
||||||
|
resets = <&rcc 277>;
|
||||||
|
clocks = <&rcc 0 149>;
|
||||||
|
pinctrl-0 = <&i2c1_sda_pin>, <&i2c1_scl_pin>;
|
||||||
|
pinctrl-names = "default";
|
||||||
|
};
|
|
@ -0,0 +1,42 @@
|
||||||
|
NVIDIA Tegra186 BPMP I2C controller
|
||||||
|
|
||||||
|
In Tegra186, the BPMP (Boot and Power Management Processor) owns certain HW
|
||||||
|
devices, such as the I2C controller for the power management I2C bus. Software
|
||||||
|
running on other CPUs must perform IPC to the BPMP in order to execute
|
||||||
|
transactions on that I2C bus. This binding describes an I2C bus that is
|
||||||
|
accessed in such a fashion.
|
||||||
|
|
||||||
|
The BPMP I2C node must be located directly inside the main BPMP node. See
|
||||||
|
../firmware/nvidia,tegra186-bpmp.txt for details of the BPMP binding.
|
||||||
|
|
||||||
|
This node represents an I2C controller. See ../i2c/i2c.txt for details of the
|
||||||
|
core I2C binding.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible:
|
||||||
|
Array of strings.
|
||||||
|
One of:
|
||||||
|
- "nvidia,tegra186-bpmp-i2c".
|
||||||
|
- #address-cells: Address cells for I2C device address.
|
||||||
|
Single-cell integer.
|
||||||
|
Must be <1>.
|
||||||
|
- #size-cells:
|
||||||
|
Single-cell integer.
|
||||||
|
Must be <0>.
|
||||||
|
- nvidia,bpmp-bus-id:
|
||||||
|
Single-cell integer.
|
||||||
|
Indicates the I2C bus number this DT node represent, as defined by the
|
||||||
|
BPMP firmware.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
bpmp {
|
||||||
|
...
|
||||||
|
|
||||||
|
i2c {
|
||||||
|
compatible = "nvidia,tegra186-bpmp-i2c";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
nvidia,bpmp-bus-id = <5>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -33,6 +33,7 @@ Supported adapters:
|
||||||
* Intel DNV (SOC)
|
* Intel DNV (SOC)
|
||||||
* Intel Broxton (SOC)
|
* Intel Broxton (SOC)
|
||||||
* Intel Lewisburg (PCH)
|
* Intel Lewisburg (PCH)
|
||||||
|
* Intel Gemini Lake (SOC)
|
||||||
Datasheets: Publicly available at the Intel website
|
Datasheets: Publicly available at the Intel website
|
||||||
|
|
||||||
On Intel Patsburg and later chipsets, both the normal host SMBus controller
|
On Intel Patsburg and later chipsets, both the normal host SMBus controller
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
Kernel driver i2c-gpio-mux
|
Kernel driver i2c-mux-gpio
|
||||||
|
|
||||||
Author: Peter Korsgaard <peter.korsgaard@barco.com>
|
Author: Peter Korsgaard <peter.korsgaard@barco.com>
|
||||||
|
|
||||||
Description
|
Description
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
i2c-gpio-mux is an i2c mux driver providing access to I2C bus segments
|
i2c-mux-gpio is an i2c mux driver providing access to I2C bus segments
|
||||||
from a master I2C bus and a hardware MUX controlled through GPIO pins.
|
from a master I2C bus and a hardware MUX controlled through GPIO pins.
|
||||||
|
|
||||||
E.G.:
|
E.G.:
|
||||||
|
@ -26,16 +26,16 @@ according to the settings of the GPIO pins 1..N.
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
i2c-gpio-mux uses the platform bus, so you need to provide a struct
|
i2c-mux-gpio uses the platform bus, so you need to provide a struct
|
||||||
platform_device with the platform_data pointing to a struct
|
platform_device with the platform_data pointing to a struct
|
||||||
gpio_i2cmux_platform_data with the I2C adapter number of the master
|
i2c_mux_gpio_platform_data with the I2C adapter number of the master
|
||||||
bus, the number of bus segments to create and the GPIO pins used
|
bus, the number of bus segments to create and the GPIO pins used
|
||||||
to control it. See include/linux/i2c-gpio-mux.h for details.
|
to control it. See include/linux/i2c-mux-gpio.h for details.
|
||||||
|
|
||||||
E.G. something like this for a MUX providing 4 bus segments
|
E.G. something like this for a MUX providing 4 bus segments
|
||||||
controlled through 3 GPIO pins:
|
controlled through 3 GPIO pins:
|
||||||
|
|
||||||
#include <linux/i2c-gpio-mux.h>
|
#include <linux/i2c-mux-gpio.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
static const unsigned myboard_gpiomux_gpios[] = {
|
static const unsigned myboard_gpiomux_gpios[] = {
|
||||||
|
@ -46,7 +46,7 @@ static const unsigned myboard_gpiomux_values[] = {
|
||||||
0, 1, 2, 3
|
0, 1, 2, 3
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct gpio_i2cmux_platform_data myboard_i2cmux_data = {
|
static struct i2c_mux_gpio_platform_data myboard_i2cmux_data = {
|
||||||
.parent = 1,
|
.parent = 1,
|
||||||
.base_nr = 2, /* optional */
|
.base_nr = 2, /* optional */
|
||||||
.values = myboard_gpiomux_values,
|
.values = myboard_gpiomux_values,
|
||||||
|
@ -57,7 +57,7 @@ static struct gpio_i2cmux_platform_data myboard_i2cmux_data = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device myboard_i2cmux = {
|
static struct platform_device myboard_i2cmux = {
|
||||||
.name = "i2c-gpio-mux",
|
.name = "i2c-mux-gpio",
|
||||||
.id = 0,
|
.id = 0,
|
||||||
.dev = {
|
.dev = {
|
||||||
.platform_data = &myboard_i2cmux_data,
|
.platform_data = &myboard_i2cmux_data,
|
||||||
|
@ -66,14 +66,14 @@ static struct platform_device myboard_i2cmux = {
|
||||||
|
|
||||||
If you don't know the absolute GPIO pin numbers at registration time,
|
If you don't know the absolute GPIO pin numbers at registration time,
|
||||||
you can instead provide a chip name (.chip_name) and relative GPIO pin
|
you can instead provide a chip name (.chip_name) and relative GPIO pin
|
||||||
numbers, and the i2c-gpio-mux driver will do the work for you,
|
numbers, and the i2c-mux-gpio driver will do the work for you,
|
||||||
including deferred probing if the GPIO chip isn't immediately
|
including deferred probing if the GPIO chip isn't immediately
|
||||||
available.
|
available.
|
||||||
|
|
||||||
Device Registration
|
Device Registration
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
When registering your i2c-gpio-mux device, you should pass the number
|
When registering your i2c-mux-gpio device, you should pass the number
|
||||||
of any GPIO pin it uses as the device ID. This guarantees that every
|
of any GPIO pin it uses as the device ID. This guarantees that every
|
||||||
instance has a different ID.
|
instance has a different ID.
|
||||||
|
|
||||||
|
|
|
@ -128,6 +128,7 @@ config I2C_I801
|
||||||
DNV (SOC)
|
DNV (SOC)
|
||||||
Broxton (SOC)
|
Broxton (SOC)
|
||||||
Lewisburg (PCH)
|
Lewisburg (PCH)
|
||||||
|
Gemini Lake (SOC)
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called i2c-i801.
|
will be called i2c-i801.
|
||||||
|
@ -886,6 +887,16 @@ config I2C_ST
|
||||||
This driver can also be built as module. If so, the module
|
This driver can also be built as module. If so, the module
|
||||||
will be called i2c-st.
|
will be called i2c-st.
|
||||||
|
|
||||||
|
config I2C_STM32F4
|
||||||
|
tristate "STMicroelectronics STM32F4 I2C support"
|
||||||
|
depends on ARCH_STM32 || COMPILE_TEST
|
||||||
|
help
|
||||||
|
Enable this option to add support for STM32 I2C controller embedded
|
||||||
|
in STM32F4 SoCs.
|
||||||
|
|
||||||
|
This driver can also be built as module. If so, the module
|
||||||
|
will be called i2c-stm32f4.
|
||||||
|
|
||||||
config I2C_STU300
|
config I2C_STU300
|
||||||
tristate "ST Microelectronics DDC I2C interface"
|
tristate "ST Microelectronics DDC I2C interface"
|
||||||
depends on MACH_U300
|
depends on MACH_U300
|
||||||
|
@ -919,6 +930,17 @@ config I2C_TEGRA
|
||||||
If you say yes to this option, support will be included for the
|
If you say yes to this option, support will be included for the
|
||||||
I2C controller embedded in NVIDIA Tegra SOCs
|
I2C controller embedded in NVIDIA Tegra SOCs
|
||||||
|
|
||||||
|
config I2C_TEGRA_BPMP
|
||||||
|
tristate "NVIDIA Tegra BPMP I2C controller"
|
||||||
|
depends on TEGRA_BPMP
|
||||||
|
help
|
||||||
|
If you say yes to this option, support will be included for the I2C
|
||||||
|
controller embedded in NVIDIA Tegra SoCs accessed via the BPMP.
|
||||||
|
|
||||||
|
This I2C driver is a 'virtual' I2C driver. The real driver is part
|
||||||
|
of the BPMP firmware, and this driver merely communicates with that
|
||||||
|
real driver.
|
||||||
|
|
||||||
config I2C_UNIPHIER
|
config I2C_UNIPHIER
|
||||||
tristate "UniPhier FIFO-less I2C controller"
|
tristate "UniPhier FIFO-less I2C controller"
|
||||||
depends on ARCH_UNIPHIER || COMPILE_TEST
|
depends on ARCH_UNIPHIER || COMPILE_TEST
|
||||||
|
|
|
@ -85,9 +85,11 @@ obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
|
||||||
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
|
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
|
||||||
obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o
|
obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o
|
||||||
obj-$(CONFIG_I2C_ST) += i2c-st.o
|
obj-$(CONFIG_I2C_ST) += i2c-st.o
|
||||||
|
obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o
|
||||||
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
|
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
|
||||||
obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o
|
obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o
|
||||||
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
|
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
|
||||||
|
obj-$(CONFIG_I2C_TEGRA_BPMP) += i2c-tegra-bpmp.o
|
||||||
obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o
|
obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o
|
||||||
obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o
|
obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o
|
||||||
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
|
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
|
||||||
|
|
|
@ -820,7 +820,7 @@ static u32 at91_twi_func(struct i2c_adapter *adapter)
|
||||||
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm at91_twi_algorithm = {
|
static const struct i2c_algorithm at91_twi_algorithm = {
|
||||||
.master_xfer = at91_twi_xfer,
|
.master_xfer = at91_twi_xfer,
|
||||||
.functionality = at91_twi_func,
|
.functionality = at91_twi_func,
|
||||||
};
|
};
|
||||||
|
@ -1180,6 +1180,7 @@ static int at91_twi_suspend_noirq(struct device *dev)
|
||||||
|
|
||||||
static int at91_twi_resume_noirq(struct device *dev)
|
static int at91_twi_resume_noirq(struct device *dev)
|
||||||
{
|
{
|
||||||
|
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!pm_runtime_status_suspended(dev)) {
|
if (!pm_runtime_status_suspended(dev)) {
|
||||||
|
@ -1191,6 +1192,8 @@ static int at91_twi_resume_noirq(struct device *dev)
|
||||||
pm_runtime_mark_last_busy(dev);
|
pm_runtime_mark_last_busy(dev);
|
||||||
pm_request_autosuspend(dev);
|
pm_request_autosuspend(dev);
|
||||||
|
|
||||||
|
at91_init_twi_bus(twi_dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -195,7 +195,9 @@ static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val & BCM2835_I2C_S_DONE) {
|
if (val & BCM2835_I2C_S_DONE) {
|
||||||
if (i2c_dev->curr_msg->flags & I2C_M_RD) {
|
if (!i2c_dev->curr_msg) {
|
||||||
|
dev_err(i2c_dev->dev, "Got unexpected interrupt (from firmware?)\n");
|
||||||
|
} else if (i2c_dev->curr_msg->flags & I2C_M_RD) {
|
||||||
bcm2835_drain_rxfifo(i2c_dev);
|
bcm2835_drain_rxfifo(i2c_dev);
|
||||||
val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
|
val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
|
||||||
}
|
}
|
||||||
|
|
|
@ -563,7 +563,7 @@ static u32 bfin_twi_functionality(struct i2c_adapter *adap)
|
||||||
I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK;
|
I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm bfin_twi_algorithm = {
|
static const struct i2c_algorithm bfin_twi_algorithm = {
|
||||||
.master_xfer = bfin_twi_master_xfer,
|
.master_xfer = bfin_twi_master_xfer,
|
||||||
.smbus_xfer = bfin_twi_smbus_xfer,
|
.smbus_xfer = bfin_twi_smbus_xfer,
|
||||||
.functionality = bfin_twi_functionality,
|
.functionality = bfin_twi_functionality,
|
||||||
|
|
|
@ -154,8 +154,10 @@ static int ec_i2c_parse_response(const u8 *buf, struct i2c_msg i2c_msgs[],
|
||||||
resp = (const struct ec_response_i2c_passthru *)buf;
|
resp = (const struct ec_response_i2c_passthru *)buf;
|
||||||
if (resp->i2c_status & EC_I2C_STATUS_TIMEOUT)
|
if (resp->i2c_status & EC_I2C_STATUS_TIMEOUT)
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
|
else if (resp->i2c_status & EC_I2C_STATUS_NAK)
|
||||||
|
return -ENXIO;
|
||||||
else if (resp->i2c_status & EC_I2C_STATUS_ERROR)
|
else if (resp->i2c_status & EC_I2C_STATUS_ERROR)
|
||||||
return -EREMOTEIO;
|
return -EIO;
|
||||||
|
|
||||||
/* Other side could send us back fewer messages, but not more */
|
/* Other side could send us back fewer messages, but not more */
|
||||||
if (resp->num_msgs > *num)
|
if (resp->num_msgs > *num)
|
||||||
|
@ -222,10 +224,8 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
|
||||||
}
|
}
|
||||||
|
|
||||||
result = ec_i2c_parse_response(msg->data, i2c_msgs, &num);
|
result = ec_i2c_parse_response(msg->data, i2c_msgs, &num);
|
||||||
if (result < 0) {
|
if (result < 0)
|
||||||
dev_err(dev, "Error parsing EC i2c message %d\n", result);
|
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
|
||||||
|
|
||||||
/* Indicate success by saying how many messages were sent */
|
/* Indicate success by saying how many messages were sent */
|
||||||
result = num;
|
result = num;
|
||||||
|
|
|
@ -820,7 +820,7 @@ static u32 i2c_dw_func(struct i2c_adapter *adap)
|
||||||
return dev->functionality;
|
return dev->functionality;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm i2c_dw_algo = {
|
static const struct i2c_algorithm i2c_dw_algo = {
|
||||||
.master_xfer = i2c_dw_xfer,
|
.master_xfer = i2c_dw_xfer,
|
||||||
.functionality = i2c_dw_func,
|
.functionality = i2c_dw_func,
|
||||||
};
|
};
|
||||||
|
|
|
@ -715,7 +715,7 @@ static u32 pch_i2c_func(struct i2c_adapter *adap)
|
||||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm pch_algorithm = {
|
static const struct i2c_algorithm pch_algorithm = {
|
||||||
.master_xfer = pch_i2c_xfer,
|
.master_xfer = pch_i2c_xfer,
|
||||||
.functionality = pch_i2c_func
|
.functionality = pch_i2c_func
|
||||||
};
|
};
|
||||||
|
|
|
@ -347,7 +347,7 @@ static int em_i2c_unreg_slave(struct i2c_client *slave)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm em_i2c_algo = {
|
static const struct i2c_algorithm em_i2c_algo = {
|
||||||
.master_xfer = em_i2c_xfer,
|
.master_xfer = em_i2c_xfer,
|
||||||
.functionality = em_i2c_func,
|
.functionality = em_i2c_func,
|
||||||
.reg_slave = em_i2c_reg_slave,
|
.reg_slave = em_i2c_reg_slave,
|
||||||
|
|
|
@ -130,12 +130,32 @@
|
||||||
/* I2C_TRANS_STATUS register bits */
|
/* I2C_TRANS_STATUS register bits */
|
||||||
#define HSI2C_MASTER_BUSY (1u << 17)
|
#define HSI2C_MASTER_BUSY (1u << 17)
|
||||||
#define HSI2C_SLAVE_BUSY (1u << 16)
|
#define HSI2C_SLAVE_BUSY (1u << 16)
|
||||||
|
|
||||||
|
/* I2C_TRANS_STATUS register bits for Exynos5 variant */
|
||||||
#define HSI2C_TIMEOUT_AUTO (1u << 4)
|
#define HSI2C_TIMEOUT_AUTO (1u << 4)
|
||||||
#define HSI2C_NO_DEV (1u << 3)
|
#define HSI2C_NO_DEV (1u << 3)
|
||||||
#define HSI2C_NO_DEV_ACK (1u << 2)
|
#define HSI2C_NO_DEV_ACK (1u << 2)
|
||||||
#define HSI2C_TRANS_ABORT (1u << 1)
|
#define HSI2C_TRANS_ABORT (1u << 1)
|
||||||
#define HSI2C_TRANS_DONE (1u << 0)
|
#define HSI2C_TRANS_DONE (1u << 0)
|
||||||
|
|
||||||
|
/* I2C_TRANS_STATUS register bits for Exynos7 variant */
|
||||||
|
#define HSI2C_MASTER_ST_MASK 0xf
|
||||||
|
#define HSI2C_MASTER_ST_IDLE 0x0
|
||||||
|
#define HSI2C_MASTER_ST_START 0x1
|
||||||
|
#define HSI2C_MASTER_ST_RESTART 0x2
|
||||||
|
#define HSI2C_MASTER_ST_STOP 0x3
|
||||||
|
#define HSI2C_MASTER_ST_MASTER_ID 0x4
|
||||||
|
#define HSI2C_MASTER_ST_ADDR0 0x5
|
||||||
|
#define HSI2C_MASTER_ST_ADDR1 0x6
|
||||||
|
#define HSI2C_MASTER_ST_ADDR2 0x7
|
||||||
|
#define HSI2C_MASTER_ST_ADDR_SR 0x8
|
||||||
|
#define HSI2C_MASTER_ST_READ 0x9
|
||||||
|
#define HSI2C_MASTER_ST_WRITE 0xa
|
||||||
|
#define HSI2C_MASTER_ST_NO_ACK 0xb
|
||||||
|
#define HSI2C_MASTER_ST_LOSE 0xc
|
||||||
|
#define HSI2C_MASTER_ST_WAIT 0xd
|
||||||
|
#define HSI2C_MASTER_ST_WAIT_CMD 0xe
|
||||||
|
|
||||||
/* I2C_ADDR register bits */
|
/* I2C_ADDR register bits */
|
||||||
#define HSI2C_SLV_ADDR_SLV(x) ((x & 0x3ff) << 0)
|
#define HSI2C_SLV_ADDR_SLV(x) ((x & 0x3ff) << 0)
|
||||||
#define HSI2C_SLV_ADDR_MAS(x) ((x & 0x3ff) << 10)
|
#define HSI2C_SLV_ADDR_MAS(x) ((x & 0x3ff) << 10)
|
||||||
|
@ -437,6 +457,7 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
|
||||||
|
|
||||||
int_status = readl(i2c->regs + HSI2C_INT_STATUS);
|
int_status = readl(i2c->regs + HSI2C_INT_STATUS);
|
||||||
writel(int_status, i2c->regs + HSI2C_INT_STATUS);
|
writel(int_status, i2c->regs + HSI2C_INT_STATUS);
|
||||||
|
trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
|
||||||
|
|
||||||
/* handle interrupt related to the transfer status */
|
/* handle interrupt related to the transfer status */
|
||||||
if (i2c->variant->hw == HSI2C_EXYNOS7) {
|
if (i2c->variant->hw == HSI2C_EXYNOS7) {
|
||||||
|
@ -460,8 +481,12 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
|
||||||
i2c->state = -ETIMEDOUT;
|
i2c->state = -ETIMEDOUT;
|
||||||
goto stop;
|
goto stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((trans_status & HSI2C_MASTER_ST_MASK) == HSI2C_MASTER_ST_LOSE) {
|
||||||
|
i2c->state = -EAGAIN;
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
} else if (int_status & HSI2C_INT_I2C) {
|
} else if (int_status & HSI2C_INT_I2C) {
|
||||||
trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
|
|
||||||
if (trans_status & HSI2C_NO_DEV_ACK) {
|
if (trans_status & HSI2C_NO_DEV_ACK) {
|
||||||
dev_dbg(i2c->dev, "No ACK from device\n");
|
dev_dbg(i2c->dev, "No ACK from device\n");
|
||||||
i2c->state = -ENXIO;
|
i2c->state = -ENXIO;
|
||||||
|
@ -502,8 +527,13 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
|
||||||
fifo_level = HSI2C_TX_FIFO_LVL(fifo_status);
|
fifo_level = HSI2C_TX_FIFO_LVL(fifo_status);
|
||||||
|
|
||||||
len = i2c->variant->fifo_depth - fifo_level;
|
len = i2c->variant->fifo_depth - fifo_level;
|
||||||
if (len > (i2c->msg->len - i2c->msg_ptr))
|
if (len > (i2c->msg->len - i2c->msg_ptr)) {
|
||||||
|
u32 int_en = readl(i2c->regs + HSI2C_INT_ENABLE);
|
||||||
|
|
||||||
|
int_en &= ~HSI2C_INT_TX_ALMOSTEMPTY_EN;
|
||||||
|
writel(int_en, i2c->regs + HSI2C_INT_ENABLE);
|
||||||
len = i2c->msg->len - i2c->msg_ptr;
|
len = i2c->msg->len - i2c->msg_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
byte = i2c->msg->buf[i2c->msg_ptr++];
|
byte = i2c->msg->buf[i2c->msg_ptr++];
|
||||||
|
|
|
@ -65,6 +65,7 @@
|
||||||
* Lewisburg (PCH) 0xa1a3 32 hard yes yes yes
|
* Lewisburg (PCH) 0xa1a3 32 hard yes yes yes
|
||||||
* Lewisburg Supersku (PCH) 0xa223 32 hard yes yes yes
|
* Lewisburg Supersku (PCH) 0xa223 32 hard yes yes yes
|
||||||
* Kaby Lake PCH-H (PCH) 0xa2a3 32 hard yes yes yes
|
* Kaby Lake PCH-H (PCH) 0xa2a3 32 hard yes yes yes
|
||||||
|
* Gemini Lake (SOC) 0x31d4 32 hard yes yes yes
|
||||||
*
|
*
|
||||||
* Features supported by this driver:
|
* Features supported by this driver:
|
||||||
* Software PEC no
|
* Software PEC no
|
||||||
|
@ -213,6 +214,7 @@
|
||||||
#define PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS 0x2292
|
#define PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS 0x2292
|
||||||
#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
|
#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
|
||||||
#define PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS 0x23b0
|
#define PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS 0x23b0
|
||||||
|
#define PCI_DEVICE_ID_INTEL_GEMINILAKE_SMBUS 0x31d4
|
||||||
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
|
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
|
||||||
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
|
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
|
||||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
|
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
|
||||||
|
@ -1012,6 +1014,7 @@ static const struct pci_device_id i801_ids[] = {
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS) },
|
||||||
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GEMINILAKE_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS) },
|
||||||
|
|
|
@ -538,7 +538,7 @@ static u32 lpi2c_imx_func(struct i2c_adapter *adapter)
|
||||||
I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm lpi2c_imx_algo = {
|
static const struct i2c_algorithm lpi2c_imx_algo = {
|
||||||
.master_xfer = lpi2c_imx_xfer,
|
.master_xfer = lpi2c_imx_xfer,
|
||||||
.functionality = lpi2c_imx_func,
|
.functionality = lpi2c_imx_func,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1037,7 +1037,7 @@ static u32 i2c_imx_func(struct i2c_adapter *adapter)
|
||||||
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm i2c_imx_algo = {
|
static const struct i2c_algorithm i2c_imx_algo = {
|
||||||
.master_xfer = i2c_imx_xfer,
|
.master_xfer = i2c_imx_xfer,
|
||||||
.functionality = i2c_imx_func,
|
.functionality = i2c_imx_func,
|
||||||
};
|
};
|
||||||
|
|
|
@ -977,11 +977,32 @@ mv64xxx_i2c_remove(struct platform_device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int mv64xxx_i2c_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
mv64xxx_i2c_hw_init(drv_data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops mv64xxx_i2c_pm = {
|
||||||
|
.resume = mv64xxx_i2c_resume,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define mv64xxx_i2c_pm_ops (&mv64xxx_i2c_pm)
|
||||||
|
#else
|
||||||
|
#define mv64xxx_i2c_pm_ops NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct platform_driver mv64xxx_i2c_driver = {
|
static struct platform_driver mv64xxx_i2c_driver = {
|
||||||
.probe = mv64xxx_i2c_probe,
|
.probe = mv64xxx_i2c_probe,
|
||||||
.remove = mv64xxx_i2c_remove,
|
.remove = mv64xxx_i2c_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = MV64XXX_I2C_CTLR_NAME,
|
.name = MV64XXX_I2C_CTLR_NAME,
|
||||||
|
.pm = mv64xxx_i2c_pm_ops,
|
||||||
.of_match_table = mv64xxx_i2c_of_match_table,
|
.of_match_table = mv64xxx_i2c_of_match_table,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -296,7 +296,7 @@ static u32 nforce2_func(struct i2c_adapter *adapter)
|
||||||
I2C_FUNC_SMBUS_BLOCK_DATA : 0);
|
I2C_FUNC_SMBUS_BLOCK_DATA : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm smbus_algorithm = {
|
static const struct i2c_algorithm smbus_algorithm = {
|
||||||
.smbus_xfer = nforce2_access,
|
.smbus_xfer = nforce2_access,
|
||||||
.functionality = nforce2_func,
|
.functionality = nforce2_func,
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#include <linux/i2c-smbus.h>
|
#include <linux/i2c-smbus.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/pci.h>
|
|
||||||
|
|
||||||
/* Controller command patterns */
|
/* Controller command patterns */
|
||||||
#define SW_TWSI_V BIT_ULL(63) /* Valid bit */
|
#define SW_TWSI_V BIT_ULL(63) /* Valid bit */
|
||||||
|
@ -118,9 +117,6 @@ struct octeon_i2c {
|
||||||
void (*hlc_int_disable)(struct octeon_i2c *);
|
void (*hlc_int_disable)(struct octeon_i2c *);
|
||||||
atomic_t int_enable_cnt;
|
atomic_t int_enable_cnt;
|
||||||
atomic_t hlc_int_enable_cnt;
|
atomic_t hlc_int_enable_cnt;
|
||||||
#if IS_ENABLED(CONFIG_I2C_THUNDERX)
|
|
||||||
struct msix_entry i2c_msix;
|
|
||||||
#endif
|
|
||||||
struct i2c_smbus_alert_setup alert_data;
|
struct i2c_smbus_alert_setup alert_data;
|
||||||
struct i2c_client *ara;
|
struct i2c_client *ara;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1504,7 +1504,7 @@ static int omap_i2c_runtime_resume(struct device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dev_pm_ops omap_i2c_pm_ops = {
|
static const struct dev_pm_ops omap_i2c_pm_ops = {
|
||||||
SET_RUNTIME_PM_OPS(omap_i2c_runtime_suspend,
|
SET_RUNTIME_PM_OPS(omap_i2c_runtime_suspend,
|
||||||
omap_i2c_runtime_resume, NULL)
|
omap_i2c_runtime_resume, NULL)
|
||||||
};
|
};
|
||||||
|
|
|
@ -80,6 +80,7 @@
|
||||||
#define ICIER_TEIE 0x40
|
#define ICIER_TEIE 0x40
|
||||||
#define ICIER_RIE 0x20
|
#define ICIER_RIE 0x20
|
||||||
#define ICIER_NAKIE 0x10
|
#define ICIER_NAKIE 0x10
|
||||||
|
#define ICIER_SPIE 0x08
|
||||||
|
|
||||||
#define ICSR2_NACKF 0x10
|
#define ICSR2_NACKF 0x10
|
||||||
|
|
||||||
|
@ -216,11 +217,10 @@ static irqreturn_t riic_tend_isr(int irq, void *data)
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (riic->is_last || riic->err)
|
if (riic->is_last || riic->err) {
|
||||||
|
riic_clear_set_bit(riic, 0, ICIER_SPIE, RIIC_ICIER);
|
||||||
writeb(ICCR2_SP, riic->base + RIIC_ICCR2);
|
writeb(ICCR2_SP, riic->base + RIIC_ICCR2);
|
||||||
|
}
|
||||||
writeb(0, riic->base + RIIC_ICIER);
|
|
||||||
complete(&riic->msg_done);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -240,13 +240,13 @@ static irqreturn_t riic_rdrf_isr(int irq, void *data)
|
||||||
|
|
||||||
if (riic->bytes_left == 1) {
|
if (riic->bytes_left == 1) {
|
||||||
/* STOP must come before we set ACKBT! */
|
/* STOP must come before we set ACKBT! */
|
||||||
if (riic->is_last)
|
if (riic->is_last) {
|
||||||
|
riic_clear_set_bit(riic, 0, ICIER_SPIE, RIIC_ICIER);
|
||||||
writeb(ICCR2_SP, riic->base + RIIC_ICCR2);
|
writeb(ICCR2_SP, riic->base + RIIC_ICCR2);
|
||||||
|
}
|
||||||
|
|
||||||
riic_clear_set_bit(riic, 0, ICMR3_ACKBT, RIIC_ICMR3);
|
riic_clear_set_bit(riic, 0, ICMR3_ACKBT, RIIC_ICMR3);
|
||||||
|
|
||||||
writeb(0, riic->base + RIIC_ICIER);
|
|
||||||
complete(&riic->msg_done);
|
|
||||||
} else {
|
} else {
|
||||||
riic_clear_set_bit(riic, ICMR3_ACKBT, 0, RIIC_ICMR3);
|
riic_clear_set_bit(riic, ICMR3_ACKBT, 0, RIIC_ICMR3);
|
||||||
}
|
}
|
||||||
|
@ -259,6 +259,21 @@ static irqreturn_t riic_rdrf_isr(int irq, void *data)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static irqreturn_t riic_stop_isr(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct riic_dev *riic = data;
|
||||||
|
|
||||||
|
/* read back registers to confirm writes have fully propagated */
|
||||||
|
writeb(0, riic->base + RIIC_ICSR2);
|
||||||
|
readb(riic->base + RIIC_ICSR2);
|
||||||
|
writeb(0, riic->base + RIIC_ICIER);
|
||||||
|
readb(riic->base + RIIC_ICIER);
|
||||||
|
|
||||||
|
complete(&riic->msg_done);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
static u32 riic_func(struct i2c_adapter *adap)
|
static u32 riic_func(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||||
|
@ -326,6 +341,7 @@ static struct riic_irq_desc riic_irqs[] = {
|
||||||
{ .res_num = 0, .isr = riic_tend_isr, .name = "riic-tend" },
|
{ .res_num = 0, .isr = riic_tend_isr, .name = "riic-tend" },
|
||||||
{ .res_num = 1, .isr = riic_rdrf_isr, .name = "riic-rdrf" },
|
{ .res_num = 1, .isr = riic_rdrf_isr, .name = "riic-rdrf" },
|
||||||
{ .res_num = 2, .isr = riic_tdre_isr, .name = "riic-tdre" },
|
{ .res_num = 2, .isr = riic_tdre_isr, .name = "riic-tdre" },
|
||||||
|
{ .res_num = 3, .isr = riic_stop_isr, .name = "riic-stop" },
|
||||||
{ .res_num = 5, .isr = riic_tend_isr, .name = "riic-nack" },
|
{ .res_num = 5, .isr = riic_tend_isr, .name = "riic-nack" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,7 @@ static u32 osif_func(struct i2c_adapter *adapter)
|
||||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm osif_algorithm = {
|
static const struct i2c_algorithm osif_algorithm = {
|
||||||
.master_xfer = osif_xfer,
|
.master_xfer = osif_xfer,
|
||||||
.functionality = osif_func,
|
.functionality = osif_func,
|
||||||
};
|
};
|
||||||
|
|
|
@ -781,7 +781,7 @@ static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter)
|
||||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm sh_mobile_i2c_algorithm = {
|
static const struct i2c_algorithm sh_mobile_i2c_algorithm = {
|
||||||
.functionality = sh_mobile_i2c_func,
|
.functionality = sh_mobile_i2c_func,
|
||||||
.master_xfer = sh_mobile_i2c_xfer,
|
.master_xfer = sh_mobile_i2c_xfer,
|
||||||
};
|
};
|
||||||
|
|
|
@ -776,7 +776,7 @@ static u32 st_i2c_func(struct i2c_adapter *adap)
|
||||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm st_i2c_algo = {
|
static const struct i2c_algorithm st_i2c_algo = {
|
||||||
.master_xfer = st_i2c_xfer,
|
.master_xfer = st_i2c_xfer,
|
||||||
.functionality = st_i2c_func,
|
.functionality = st_i2c_func,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,897 @@
|
||||||
|
/*
|
||||||
|
* Driver for STMicroelectronics STM32 I2C controller
|
||||||
|
*
|
||||||
|
* This I2C controller is described in the STM32F429/439 Soc reference manual.
|
||||||
|
* Please see below a link to the documentation:
|
||||||
|
* http://www.st.com/resource/en/reference_manual/DM00031020.pdf
|
||||||
|
*
|
||||||
|
* Copyright (C) M'boumba Cedric Madianga 2016
|
||||||
|
* Author: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
|
||||||
|
*
|
||||||
|
* This driver is based on i2c-st.c
|
||||||
|
*
|
||||||
|
* License terms: GNU General Public License (GPL), version 2
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/reset.h>
|
||||||
|
|
||||||
|
/* STM32F4 I2C offset registers */
|
||||||
|
#define STM32F4_I2C_CR1 0x00
|
||||||
|
#define STM32F4_I2C_CR2 0x04
|
||||||
|
#define STM32F4_I2C_DR 0x10
|
||||||
|
#define STM32F4_I2C_SR1 0x14
|
||||||
|
#define STM32F4_I2C_SR2 0x18
|
||||||
|
#define STM32F4_I2C_CCR 0x1C
|
||||||
|
#define STM32F4_I2C_TRISE 0x20
|
||||||
|
#define STM32F4_I2C_FLTR 0x24
|
||||||
|
|
||||||
|
/* STM32F4 I2C control 1*/
|
||||||
|
#define STM32F4_I2C_CR1_POS BIT(11)
|
||||||
|
#define STM32F4_I2C_CR1_ACK BIT(10)
|
||||||
|
#define STM32F4_I2C_CR1_STOP BIT(9)
|
||||||
|
#define STM32F4_I2C_CR1_START BIT(8)
|
||||||
|
#define STM32F4_I2C_CR1_PE BIT(0)
|
||||||
|
|
||||||
|
/* STM32F4 I2C control 2 */
|
||||||
|
#define STM32F4_I2C_CR2_FREQ_MASK GENMASK(5, 0)
|
||||||
|
#define STM32F4_I2C_CR2_FREQ(n) ((n) & STM32F4_I2C_CR2_FREQ_MASK)
|
||||||
|
#define STM32F4_I2C_CR2_ITBUFEN BIT(10)
|
||||||
|
#define STM32F4_I2C_CR2_ITEVTEN BIT(9)
|
||||||
|
#define STM32F4_I2C_CR2_ITERREN BIT(8)
|
||||||
|
#define STM32F4_I2C_CR2_IRQ_MASK (STM32F4_I2C_CR2_ITBUFEN | \
|
||||||
|
STM32F4_I2C_CR2_ITEVTEN | \
|
||||||
|
STM32F4_I2C_CR2_ITERREN)
|
||||||
|
|
||||||
|
/* STM32F4 I2C Status 1 */
|
||||||
|
#define STM32F4_I2C_SR1_AF BIT(10)
|
||||||
|
#define STM32F4_I2C_SR1_ARLO BIT(9)
|
||||||
|
#define STM32F4_I2C_SR1_BERR BIT(8)
|
||||||
|
#define STM32F4_I2C_SR1_TXE BIT(7)
|
||||||
|
#define STM32F4_I2C_SR1_RXNE BIT(6)
|
||||||
|
#define STM32F4_I2C_SR1_BTF BIT(2)
|
||||||
|
#define STM32F4_I2C_SR1_ADDR BIT(1)
|
||||||
|
#define STM32F4_I2C_SR1_SB BIT(0)
|
||||||
|
#define STM32F4_I2C_SR1_ITEVTEN_MASK (STM32F4_I2C_SR1_BTF | \
|
||||||
|
STM32F4_I2C_SR1_ADDR | \
|
||||||
|
STM32F4_I2C_SR1_SB)
|
||||||
|
#define STM32F4_I2C_SR1_ITBUFEN_MASK (STM32F4_I2C_SR1_TXE | \
|
||||||
|
STM32F4_I2C_SR1_RXNE)
|
||||||
|
#define STM32F4_I2C_SR1_ITERREN_MASK (STM32F4_I2C_SR1_AF | \
|
||||||
|
STM32F4_I2C_SR1_ARLO | \
|
||||||
|
STM32F4_I2C_SR1_BERR)
|
||||||
|
|
||||||
|
/* STM32F4 I2C Status 2 */
|
||||||
|
#define STM32F4_I2C_SR2_BUSY BIT(1)
|
||||||
|
|
||||||
|
/* STM32F4 I2C Control Clock */
|
||||||
|
#define STM32F4_I2C_CCR_CCR_MASK GENMASK(11, 0)
|
||||||
|
#define STM32F4_I2C_CCR_CCR(n) ((n) & STM32F4_I2C_CCR_CCR_MASK)
|
||||||
|
#define STM32F4_I2C_CCR_FS BIT(15)
|
||||||
|
#define STM32F4_I2C_CCR_DUTY BIT(14)
|
||||||
|
|
||||||
|
/* STM32F4 I2C Trise */
|
||||||
|
#define STM32F4_I2C_TRISE_VALUE_MASK GENMASK(5, 0)
|
||||||
|
#define STM32F4_I2C_TRISE_VALUE(n) ((n) & STM32F4_I2C_TRISE_VALUE_MASK)
|
||||||
|
|
||||||
|
#define STM32F4_I2C_MIN_STANDARD_FREQ 2U
|
||||||
|
#define STM32F4_I2C_MIN_FAST_FREQ 6U
|
||||||
|
#define STM32F4_I2C_MAX_FREQ 46U
|
||||||
|
#define HZ_TO_MHZ 1000000
|
||||||
|
|
||||||
|
enum stm32f4_i2c_speed {
|
||||||
|
STM32F4_I2C_SPEED_STANDARD, /* 100 kHz */
|
||||||
|
STM32F4_I2C_SPEED_FAST, /* 400 kHz */
|
||||||
|
STM32F4_I2C_SPEED_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct stm32f4_i2c_msg - client specific data
|
||||||
|
* @addr: 8-bit slave addr, including r/w bit
|
||||||
|
* @count: number of bytes to be transferred
|
||||||
|
* @buf: data buffer
|
||||||
|
* @result: result of the transfer
|
||||||
|
* @stop: last I2C msg to be sent, i.e. STOP to be generated
|
||||||
|
*/
|
||||||
|
struct stm32f4_i2c_msg {
|
||||||
|
u8 addr;
|
||||||
|
u32 count;
|
||||||
|
u8 *buf;
|
||||||
|
int result;
|
||||||
|
bool stop;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct stm32f4_i2c_dev - private data of the controller
|
||||||
|
* @adap: I2C adapter for this controller
|
||||||
|
* @dev: device for this controller
|
||||||
|
* @base: virtual memory area
|
||||||
|
* @complete: completion of I2C message
|
||||||
|
* @clk: hw i2c clock
|
||||||
|
* @speed: I2C clock frequency of the controller. Standard or Fast are supported
|
||||||
|
* @parent_rate: I2C clock parent rate in MHz
|
||||||
|
* @msg: I2C transfer information
|
||||||
|
*/
|
||||||
|
struct stm32f4_i2c_dev {
|
||||||
|
struct i2c_adapter adap;
|
||||||
|
struct device *dev;
|
||||||
|
void __iomem *base;
|
||||||
|
struct completion complete;
|
||||||
|
struct clk *clk;
|
||||||
|
int speed;
|
||||||
|
int parent_rate;
|
||||||
|
struct stm32f4_i2c_msg msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void stm32f4_i2c_set_bits(void __iomem *reg, u32 mask)
|
||||||
|
{
|
||||||
|
writel_relaxed(readl_relaxed(reg) | mask, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void stm32f4_i2c_clr_bits(void __iomem *reg, u32 mask)
|
||||||
|
{
|
||||||
|
writel_relaxed(readl_relaxed(reg) & ~mask, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stm32f4_i2c_disable_irq(struct stm32f4_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2;
|
||||||
|
|
||||||
|
stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_IRQ_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32f4_i2c_set_periph_clk_freq(struct stm32f4_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
u32 freq;
|
||||||
|
u32 cr2 = 0;
|
||||||
|
|
||||||
|
i2c_dev->parent_rate = clk_get_rate(i2c_dev->clk);
|
||||||
|
freq = DIV_ROUND_UP(i2c_dev->parent_rate, HZ_TO_MHZ);
|
||||||
|
|
||||||
|
if (i2c_dev->speed == STM32F4_I2C_SPEED_STANDARD) {
|
||||||
|
/*
|
||||||
|
* To reach 100 kHz, the parent clk frequency should be between
|
||||||
|
* a minimum value of 2 MHz and a maximum value of 46 MHz due
|
||||||
|
* to hardware limitation
|
||||||
|
*/
|
||||||
|
if (freq < STM32F4_I2C_MIN_STANDARD_FREQ ||
|
||||||
|
freq > STM32F4_I2C_MAX_FREQ) {
|
||||||
|
dev_err(i2c_dev->dev,
|
||||||
|
"bad parent clk freq for standard mode\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* To be as close as possible to 400 kHz, the parent clk
|
||||||
|
* frequency should be between a minimum value of 6 MHz and a
|
||||||
|
* maximum value of 46 MHz due to hardware limitation
|
||||||
|
*/
|
||||||
|
if (freq < STM32F4_I2C_MIN_FAST_FREQ ||
|
||||||
|
freq > STM32F4_I2C_MAX_FREQ) {
|
||||||
|
dev_err(i2c_dev->dev,
|
||||||
|
"bad parent clk freq for fast mode\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cr2 |= STM32F4_I2C_CR2_FREQ(freq);
|
||||||
|
writel_relaxed(cr2, i2c_dev->base + STM32F4_I2C_CR2);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stm32f4_i2c_set_rise_time(struct stm32f4_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
u32 freq = DIV_ROUND_UP(i2c_dev->parent_rate, HZ_TO_MHZ);
|
||||||
|
u32 trise;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These bits must be programmed with the maximum SCL rise time given in
|
||||||
|
* the I2C bus specification, incremented by 1.
|
||||||
|
*
|
||||||
|
* In standard mode, the maximum allowed SCL rise time is 1000 ns.
|
||||||
|
* If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to
|
||||||
|
* 0x08 so period = 125 ns therefore the TRISE[5:0] bits must be
|
||||||
|
* programmed with 0x9. (1000 ns / 125 ns + 1)
|
||||||
|
* So, for I2C standard mode TRISE = FREQ[5:0] + 1
|
||||||
|
*
|
||||||
|
* In fast mode, the maximum allowed SCL rise time is 300 ns.
|
||||||
|
* If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to
|
||||||
|
* 0x08 so period = 125 ns therefore the TRISE[5:0] bits must be
|
||||||
|
* programmed with 0x3. (300 ns / 125 ns + 1)
|
||||||
|
* So, for I2C fast mode TRISE = FREQ[5:0] * 300 / 1000 + 1
|
||||||
|
*
|
||||||
|
* Function stm32f4_i2c_set_periph_clk_freq made sure that parent rate
|
||||||
|
* is not higher than 46 MHz . As a result trise is at most 4 bits wide
|
||||||
|
* and so fits into the TRISE bits [5:0].
|
||||||
|
*/
|
||||||
|
if (i2c_dev->speed == STM32F4_I2C_SPEED_STANDARD)
|
||||||
|
trise = freq + 1;
|
||||||
|
else
|
||||||
|
trise = freq * 3 / 10 + 1;
|
||||||
|
|
||||||
|
writel_relaxed(STM32F4_I2C_TRISE_VALUE(trise),
|
||||||
|
i2c_dev->base + STM32F4_I2C_TRISE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stm32f4_i2c_set_speed_mode(struct stm32f4_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
u32 ccr = 0;
|
||||||
|
|
||||||
|
if (i2c_dev->speed == STM32F4_I2C_SPEED_STANDARD) {
|
||||||
|
/*
|
||||||
|
* In standard mode:
|
||||||
|
* t_scl_high = t_scl_low = CCR * I2C parent clk period
|
||||||
|
* So to reach 100 kHz, we have:
|
||||||
|
* CCR = I2C parent rate / 100 kHz >> 1
|
||||||
|
*
|
||||||
|
* For example with parent rate = 2 MHz:
|
||||||
|
* CCR = 2000000 / (100000 << 1) = 10
|
||||||
|
* t_scl_high = t_scl_low = 10 * (1 / 2000000) = 5000 ns
|
||||||
|
* t_scl_high + t_scl_low = 10000 ns so 100 kHz is reached
|
||||||
|
*
|
||||||
|
* Function stm32f4_i2c_set_periph_clk_freq made sure that
|
||||||
|
* parent rate is not higher than 46 MHz . As a result val
|
||||||
|
* is at most 8 bits wide and so fits into the CCR bits [11:0].
|
||||||
|
*/
|
||||||
|
val = i2c_dev->parent_rate / (100000 << 1);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* In fast mode, we compute CCR with duty = 0 as with low
|
||||||
|
* frequencies we are not able to reach 400 kHz.
|
||||||
|
* In that case:
|
||||||
|
* t_scl_high = CCR * I2C parent clk period
|
||||||
|
* t_scl_low = 2 * CCR * I2C parent clk period
|
||||||
|
* So, CCR = I2C parent rate / (400 kHz * 3)
|
||||||
|
*
|
||||||
|
* For example with parent rate = 6 MHz:
|
||||||
|
* CCR = 6000000 / (400000 * 3) = 5
|
||||||
|
* t_scl_high = 5 * (1 / 6000000) = 833 ns > 600 ns
|
||||||
|
* t_scl_low = 2 * 5 * (1 / 6000000) = 1667 ns > 1300 ns
|
||||||
|
* t_scl_high + t_scl_low = 2500 ns so 400 kHz is reached
|
||||||
|
*
|
||||||
|
* Function stm32f4_i2c_set_periph_clk_freq made sure that
|
||||||
|
* parent rate is not higher than 46 MHz . As a result val
|
||||||
|
* is at most 6 bits wide and so fits into the CCR bits [11:0].
|
||||||
|
*/
|
||||||
|
val = DIV_ROUND_UP(i2c_dev->parent_rate, 400000 * 3);
|
||||||
|
|
||||||
|
/* Select Fast mode */
|
||||||
|
ccr |= STM32F4_I2C_CCR_FS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ccr |= STM32F4_I2C_CCR_CCR(val);
|
||||||
|
writel_relaxed(ccr, i2c_dev->base + STM32F4_I2C_CCR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stm32f4_i2c_hw_config() - Prepare I2C block
|
||||||
|
* @i2c_dev: Controller's private data
|
||||||
|
*/
|
||||||
|
static int stm32f4_i2c_hw_config(struct stm32f4_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = stm32f4_i2c_set_periph_clk_freq(i2c_dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
stm32f4_i2c_set_rise_time(i2c_dev);
|
||||||
|
|
||||||
|
stm32f4_i2c_set_speed_mode(i2c_dev);
|
||||||
|
|
||||||
|
/* Enable I2C */
|
||||||
|
writel_relaxed(STM32F4_I2C_CR1_PE, i2c_dev->base + STM32F4_I2C_CR1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32f4_i2c_wait_free_bus(struct stm32f4_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
u32 status;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = readl_relaxed_poll_timeout(i2c_dev->base + STM32F4_I2C_SR2,
|
||||||
|
status,
|
||||||
|
!(status & STM32F4_I2C_SR2_BUSY),
|
||||||
|
10, 1000);
|
||||||
|
if (ret) {
|
||||||
|
dev_dbg(i2c_dev->dev, "bus not free\n");
|
||||||
|
ret = -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stm32f4_i2c_write_ byte() - Write a byte in the data register
|
||||||
|
* @i2c_dev: Controller's private data
|
||||||
|
* @byte: Data to write in the register
|
||||||
|
*/
|
||||||
|
static void stm32f4_i2c_write_byte(struct stm32f4_i2c_dev *i2c_dev, u8 byte)
|
||||||
|
{
|
||||||
|
writel_relaxed(byte, i2c_dev->base + STM32F4_I2C_DR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stm32f4_i2c_write_msg() - Fill the data register in write mode
|
||||||
|
* @i2c_dev: Controller's private data
|
||||||
|
*
|
||||||
|
* This function fills the data register with I2C transfer buffer
|
||||||
|
*/
|
||||||
|
static void stm32f4_i2c_write_msg(struct stm32f4_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
|
||||||
|
|
||||||
|
stm32f4_i2c_write_byte(i2c_dev, *msg->buf++);
|
||||||
|
msg->count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stm32f4_i2c_read_msg(struct stm32f4_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
|
||||||
|
u32 rbuf;
|
||||||
|
|
||||||
|
rbuf = readl_relaxed(i2c_dev->base + STM32F4_I2C_DR);
|
||||||
|
*msg->buf++ = rbuf;
|
||||||
|
msg->count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stm32f4_i2c_terminate_xfer(struct stm32f4_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
|
||||||
|
void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2;
|
||||||
|
|
||||||
|
stm32f4_i2c_disable_irq(i2c_dev);
|
||||||
|
|
||||||
|
reg = i2c_dev->base + STM32F4_I2C_CR1;
|
||||||
|
if (msg->stop)
|
||||||
|
stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP);
|
||||||
|
else
|
||||||
|
stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START);
|
||||||
|
|
||||||
|
complete(&i2c_dev->complete);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stm32f4_i2c_handle_write() - Handle FIFO empty interrupt in case of write
|
||||||
|
* @i2c_dev: Controller's private data
|
||||||
|
*/
|
||||||
|
static void stm32f4_i2c_handle_write(struct stm32f4_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
|
||||||
|
void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2;
|
||||||
|
|
||||||
|
if (msg->count) {
|
||||||
|
stm32f4_i2c_write_msg(i2c_dev);
|
||||||
|
if (!msg->count) {
|
||||||
|
/*
|
||||||
|
* Disable buffer interrupts for RX not empty and TX
|
||||||
|
* empty events
|
||||||
|
*/
|
||||||
|
stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_ITBUFEN);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stm32f4_i2c_terminate_xfer(i2c_dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stm32f4_i2c_handle_read() - Handle FIFO empty interrupt in case of read
|
||||||
|
* @i2c_dev: Controller's private data
|
||||||
|
*
|
||||||
|
* This function is called when a new data is received in data register
|
||||||
|
*/
|
||||||
|
static void stm32f4_i2c_handle_read(struct stm32f4_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
|
||||||
|
void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2;
|
||||||
|
|
||||||
|
switch (msg->count) {
|
||||||
|
case 1:
|
||||||
|
stm32f4_i2c_disable_irq(i2c_dev);
|
||||||
|
stm32f4_i2c_read_msg(i2c_dev);
|
||||||
|
complete(&i2c_dev->complete);
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
* For 2-byte reception, 3-byte reception and for Data N-2, N-1 and N
|
||||||
|
* for N-byte reception with N > 3, we do not have to read the data
|
||||||
|
* register when RX not empty event occurs as we have to wait for byte
|
||||||
|
* transferred finished event before reading data.
|
||||||
|
* So, here we just disable buffer interrupt in order to avoid another
|
||||||
|
* system preemption due to RX not empty event.
|
||||||
|
*/
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_ITBUFEN);
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
* For N byte reception with N > 3 we directly read data register
|
||||||
|
* until N-2 data.
|
||||||
|
*/
|
||||||
|
default:
|
||||||
|
stm32f4_i2c_read_msg(i2c_dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stm32f4_i2c_handle_rx_done() - Handle byte transfer finished interrupt
|
||||||
|
* in case of read
|
||||||
|
* @i2c_dev: Controller's private data
|
||||||
|
*
|
||||||
|
* This function is called when a new data is received in the shift register
|
||||||
|
* but data register has not been read yet.
|
||||||
|
*/
|
||||||
|
static void stm32f4_i2c_handle_rx_done(struct stm32f4_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
|
||||||
|
void __iomem *reg;
|
||||||
|
u32 mask;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
switch (msg->count) {
|
||||||
|
case 2:
|
||||||
|
/*
|
||||||
|
* In order to correctly send the Stop or Repeated Start
|
||||||
|
* condition on the I2C bus, the STOP/START bit has to be set
|
||||||
|
* before reading the last two bytes (data N-1 and N).
|
||||||
|
* After that, we could read the last two bytes, disable
|
||||||
|
* remaining interrupts and notify the end of xfer to the
|
||||||
|
* client
|
||||||
|
*/
|
||||||
|
reg = i2c_dev->base + STM32F4_I2C_CR1;
|
||||||
|
if (msg->stop)
|
||||||
|
stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP);
|
||||||
|
else
|
||||||
|
stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START);
|
||||||
|
|
||||||
|
for (i = 2; i > 0; i--)
|
||||||
|
stm32f4_i2c_read_msg(i2c_dev);
|
||||||
|
|
||||||
|
reg = i2c_dev->base + STM32F4_I2C_CR2;
|
||||||
|
mask = STM32F4_I2C_CR2_ITEVTEN | STM32F4_I2C_CR2_ITERREN;
|
||||||
|
stm32f4_i2c_clr_bits(reg, mask);
|
||||||
|
|
||||||
|
complete(&i2c_dev->complete);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
/*
|
||||||
|
* In order to correctly generate the NACK pulse after the last
|
||||||
|
* received data byte, we have to enable NACK before reading N-2
|
||||||
|
* data
|
||||||
|
*/
|
||||||
|
reg = i2c_dev->base + STM32F4_I2C_CR1;
|
||||||
|
stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR1_ACK);
|
||||||
|
stm32f4_i2c_read_msg(i2c_dev);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
stm32f4_i2c_read_msg(i2c_dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stm32f4_i2c_handle_rx_addr() - Handle address matched interrupt in case of
|
||||||
|
* master receiver
|
||||||
|
* @i2c_dev: Controller's private data
|
||||||
|
*/
|
||||||
|
static void stm32f4_i2c_handle_rx_addr(struct stm32f4_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
|
||||||
|
u32 cr1;
|
||||||
|
|
||||||
|
switch (msg->count) {
|
||||||
|
case 0:
|
||||||
|
stm32f4_i2c_terminate_xfer(i2c_dev);
|
||||||
|
|
||||||
|
/* Clear ADDR flag */
|
||||||
|
readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/*
|
||||||
|
* Single byte reception:
|
||||||
|
* Enable NACK and reset POS (Acknowledge position).
|
||||||
|
* Then, clear ADDR flag and set STOP or RepSTART.
|
||||||
|
* In that way, the NACK and STOP or RepStart pulses will be
|
||||||
|
* sent as soon as the byte will be received in shift register
|
||||||
|
*/
|
||||||
|
cr1 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR1);
|
||||||
|
cr1 &= ~(STM32F4_I2C_CR1_ACK | STM32F4_I2C_CR1_POS);
|
||||||
|
writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1);
|
||||||
|
|
||||||
|
readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2);
|
||||||
|
|
||||||
|
if (msg->stop)
|
||||||
|
cr1 |= STM32F4_I2C_CR1_STOP;
|
||||||
|
else
|
||||||
|
cr1 |= STM32F4_I2C_CR1_START;
|
||||||
|
writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
/*
|
||||||
|
* 2-byte reception:
|
||||||
|
* Enable NACK, set POS (NACK position) and clear ADDR flag.
|
||||||
|
* In that way, NACK will be sent for the next byte which will
|
||||||
|
* be received in the shift register instead of the current
|
||||||
|
* one.
|
||||||
|
*/
|
||||||
|
cr1 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR1);
|
||||||
|
cr1 &= ~STM32F4_I2C_CR1_ACK;
|
||||||
|
cr1 |= STM32F4_I2C_CR1_POS;
|
||||||
|
writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1);
|
||||||
|
|
||||||
|
readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
* N-byte reception:
|
||||||
|
* Enable ACK, reset POS (ACK postion) and clear ADDR flag.
|
||||||
|
* In that way, ACK will be sent as soon as the current byte
|
||||||
|
* will be received in the shift register
|
||||||
|
*/
|
||||||
|
cr1 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR1);
|
||||||
|
cr1 |= STM32F4_I2C_CR1_ACK;
|
||||||
|
cr1 &= ~STM32F4_I2C_CR1_POS;
|
||||||
|
writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1);
|
||||||
|
|
||||||
|
readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stm32f4_i2c_isr_event() - Interrupt routine for I2C bus event
|
||||||
|
* @irq: interrupt number
|
||||||
|
* @data: Controller's private data
|
||||||
|
*/
|
||||||
|
static irqreturn_t stm32f4_i2c_isr_event(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct stm32f4_i2c_dev *i2c_dev = data;
|
||||||
|
struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
|
||||||
|
u32 possible_status = STM32F4_I2C_SR1_ITEVTEN_MASK;
|
||||||
|
u32 status, ien, event, cr2;
|
||||||
|
|
||||||
|
cr2 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR2);
|
||||||
|
ien = cr2 & STM32F4_I2C_CR2_IRQ_MASK;
|
||||||
|
|
||||||
|
/* Update possible_status if buffer interrupt is enabled */
|
||||||
|
if (ien & STM32F4_I2C_CR2_ITBUFEN)
|
||||||
|
possible_status |= STM32F4_I2C_SR1_ITBUFEN_MASK;
|
||||||
|
|
||||||
|
status = readl_relaxed(i2c_dev->base + STM32F4_I2C_SR1);
|
||||||
|
event = status & possible_status;
|
||||||
|
if (!event) {
|
||||||
|
dev_dbg(i2c_dev->dev,
|
||||||
|
"spurious evt irq (status=0x%08x, ien=0x%08x)\n",
|
||||||
|
status, ien);
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start condition generated */
|
||||||
|
if (event & STM32F4_I2C_SR1_SB)
|
||||||
|
stm32f4_i2c_write_byte(i2c_dev, msg->addr);
|
||||||
|
|
||||||
|
/* I2C Address sent */
|
||||||
|
if (event & STM32F4_I2C_SR1_ADDR) {
|
||||||
|
if (msg->addr & I2C_M_RD)
|
||||||
|
stm32f4_i2c_handle_rx_addr(i2c_dev);
|
||||||
|
else
|
||||||
|
readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable buffer interrupts for RX not empty and TX empty
|
||||||
|
* events
|
||||||
|
*/
|
||||||
|
cr2 |= STM32F4_I2C_CR2_ITBUFEN;
|
||||||
|
writel_relaxed(cr2, i2c_dev->base + STM32F4_I2C_CR2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TX empty */
|
||||||
|
if ((event & STM32F4_I2C_SR1_TXE) && !(msg->addr & I2C_M_RD))
|
||||||
|
stm32f4_i2c_handle_write(i2c_dev);
|
||||||
|
|
||||||
|
/* RX not empty */
|
||||||
|
if ((event & STM32F4_I2C_SR1_RXNE) && (msg->addr & I2C_M_RD))
|
||||||
|
stm32f4_i2c_handle_read(i2c_dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The BTF (Byte Transfer finished) event occurs when:
|
||||||
|
* - in reception : a new byte is received in the shift register
|
||||||
|
* but the previous byte has not been read yet from data register
|
||||||
|
* - in transmission: a new byte should be sent but the data register
|
||||||
|
* has not been written yet
|
||||||
|
*/
|
||||||
|
if (event & STM32F4_I2C_SR1_BTF) {
|
||||||
|
if (msg->addr & I2C_M_RD)
|
||||||
|
stm32f4_i2c_handle_rx_done(i2c_dev);
|
||||||
|
else
|
||||||
|
stm32f4_i2c_handle_write(i2c_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stm32f4_i2c_isr_error() - Interrupt routine for I2C bus error
|
||||||
|
* @irq: interrupt number
|
||||||
|
* @data: Controller's private data
|
||||||
|
*/
|
||||||
|
static irqreturn_t stm32f4_i2c_isr_error(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct stm32f4_i2c_dev *i2c_dev = data;
|
||||||
|
struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
|
||||||
|
void __iomem *reg;
|
||||||
|
u32 status;
|
||||||
|
|
||||||
|
status = readl_relaxed(i2c_dev->base + STM32F4_I2C_SR1);
|
||||||
|
|
||||||
|
/* Arbitration lost */
|
||||||
|
if (status & STM32F4_I2C_SR1_ARLO) {
|
||||||
|
status &= ~STM32F4_I2C_SR1_ARLO;
|
||||||
|
writel_relaxed(status, i2c_dev->base + STM32F4_I2C_SR1);
|
||||||
|
msg->result = -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Acknowledge failure:
|
||||||
|
* In master transmitter mode a Stop must be generated by software
|
||||||
|
*/
|
||||||
|
if (status & STM32F4_I2C_SR1_AF) {
|
||||||
|
if (!(msg->addr & I2C_M_RD)) {
|
||||||
|
reg = i2c_dev->base + STM32F4_I2C_CR1;
|
||||||
|
stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP);
|
||||||
|
}
|
||||||
|
status &= ~STM32F4_I2C_SR1_AF;
|
||||||
|
writel_relaxed(status, i2c_dev->base + STM32F4_I2C_SR1);
|
||||||
|
msg->result = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bus error */
|
||||||
|
if (status & STM32F4_I2C_SR1_BERR) {
|
||||||
|
status &= ~STM32F4_I2C_SR1_BERR;
|
||||||
|
writel_relaxed(status, i2c_dev->base + STM32F4_I2C_SR1);
|
||||||
|
msg->result = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
stm32f4_i2c_disable_irq(i2c_dev);
|
||||||
|
complete(&i2c_dev->complete);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stm32f4_i2c_xfer_msg() - Transfer a single I2C message
|
||||||
|
* @i2c_dev: Controller's private data
|
||||||
|
* @msg: I2C message to transfer
|
||||||
|
* @is_first: first message of the sequence
|
||||||
|
* @is_last: last message of the sequence
|
||||||
|
*/
|
||||||
|
static int stm32f4_i2c_xfer_msg(struct stm32f4_i2c_dev *i2c_dev,
|
||||||
|
struct i2c_msg *msg, bool is_first,
|
||||||
|
bool is_last)
|
||||||
|
{
|
||||||
|
struct stm32f4_i2c_msg *f4_msg = &i2c_dev->msg;
|
||||||
|
void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR1;
|
||||||
|
unsigned long timeout;
|
||||||
|
u32 mask;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
f4_msg->addr = i2c_8bit_addr_from_msg(msg);
|
||||||
|
f4_msg->buf = msg->buf;
|
||||||
|
f4_msg->count = msg->len;
|
||||||
|
f4_msg->result = 0;
|
||||||
|
f4_msg->stop = is_last;
|
||||||
|
|
||||||
|
reinit_completion(&i2c_dev->complete);
|
||||||
|
|
||||||
|
/* Enable events and errors interrupts */
|
||||||
|
mask = STM32F4_I2C_CR2_ITEVTEN | STM32F4_I2C_CR2_ITERREN;
|
||||||
|
stm32f4_i2c_set_bits(i2c_dev->base + STM32F4_I2C_CR2, mask);
|
||||||
|
|
||||||
|
if (is_first) {
|
||||||
|
ret = stm32f4_i2c_wait_free_bus(i2c_dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* START generation */
|
||||||
|
stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout = wait_for_completion_timeout(&i2c_dev->complete,
|
||||||
|
i2c_dev->adap.timeout);
|
||||||
|
ret = f4_msg->result;
|
||||||
|
|
||||||
|
if (!timeout)
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stm32f4_i2c_xfer() - Transfer combined I2C message
|
||||||
|
* @i2c_adap: Adapter pointer to the controller
|
||||||
|
* @msgs: Pointer to data to be written.
|
||||||
|
* @num: Number of messages to be executed
|
||||||
|
*/
|
||||||
|
static int stm32f4_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[],
|
||||||
|
int num)
|
||||||
|
{
|
||||||
|
struct stm32f4_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
ret = clk_enable(i2c_dev->clk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(i2c_dev->dev, "Failed to enable clock\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < num && !ret; i++)
|
||||||
|
ret = stm32f4_i2c_xfer_msg(i2c_dev, &msgs[i], i == 0,
|
||||||
|
i == num - 1);
|
||||||
|
|
||||||
|
clk_disable(i2c_dev->clk);
|
||||||
|
|
||||||
|
return (ret < 0) ? ret : num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 stm32f4_i2c_func(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct i2c_algorithm stm32f4_i2c_algo = {
|
||||||
|
.master_xfer = stm32f4_i2c_xfer,
|
||||||
|
.functionality = stm32f4_i2c_func,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stm32f4_i2c_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct stm32f4_i2c_dev *i2c_dev;
|
||||||
|
struct resource *res;
|
||||||
|
u32 irq_event, irq_error, clk_rate;
|
||||||
|
struct i2c_adapter *adap;
|
||||||
|
struct reset_control *rst;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
|
||||||
|
if (!i2c_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
i2c_dev->base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(i2c_dev->base))
|
||||||
|
return PTR_ERR(i2c_dev->base);
|
||||||
|
|
||||||
|
irq_event = irq_of_parse_and_map(np, 0);
|
||||||
|
if (!irq_event) {
|
||||||
|
dev_err(&pdev->dev, "IRQ event missing or invalid\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_error = irq_of_parse_and_map(np, 1);
|
||||||
|
if (!irq_error) {
|
||||||
|
dev_err(&pdev->dev, "IRQ error missing or invalid\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(i2c_dev->clk)) {
|
||||||
|
dev_err(&pdev->dev, "Error: Missing controller clock\n");
|
||||||
|
return PTR_ERR(i2c_dev->clk);
|
||||||
|
}
|
||||||
|
ret = clk_prepare_enable(i2c_dev->clk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(i2c_dev->dev, "Failed to prepare_enable clock\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
rst = devm_reset_control_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(rst)) {
|
||||||
|
dev_err(&pdev->dev, "Error: Missing controller reset\n");
|
||||||
|
ret = PTR_ERR(rst);
|
||||||
|
goto clk_free;
|
||||||
|
}
|
||||||
|
reset_control_assert(rst);
|
||||||
|
udelay(2);
|
||||||
|
reset_control_deassert(rst);
|
||||||
|
|
||||||
|
i2c_dev->speed = STM32F4_I2C_SPEED_STANDARD;
|
||||||
|
ret = of_property_read_u32(np, "clock-frequency", &clk_rate);
|
||||||
|
if (!ret && clk_rate >= 400000)
|
||||||
|
i2c_dev->speed = STM32F4_I2C_SPEED_FAST;
|
||||||
|
|
||||||
|
i2c_dev->dev = &pdev->dev;
|
||||||
|
|
||||||
|
ret = devm_request_irq(&pdev->dev, irq_event, stm32f4_i2c_isr_event, 0,
|
||||||
|
pdev->name, i2c_dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Failed to request irq event %i\n",
|
||||||
|
irq_event);
|
||||||
|
goto clk_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_irq(&pdev->dev, irq_error, stm32f4_i2c_isr_error, 0,
|
||||||
|
pdev->name, i2c_dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Failed to request irq error %i\n",
|
||||||
|
irq_error);
|
||||||
|
goto clk_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = stm32f4_i2c_hw_config(i2c_dev);
|
||||||
|
if (ret)
|
||||||
|
goto clk_free;
|
||||||
|
|
||||||
|
adap = &i2c_dev->adap;
|
||||||
|
i2c_set_adapdata(adap, i2c_dev);
|
||||||
|
snprintf(adap->name, sizeof(adap->name), "STM32 I2C(%pa)", &res->start);
|
||||||
|
adap->owner = THIS_MODULE;
|
||||||
|
adap->timeout = 2 * HZ;
|
||||||
|
adap->retries = 0;
|
||||||
|
adap->algo = &stm32f4_i2c_algo;
|
||||||
|
adap->dev.parent = &pdev->dev;
|
||||||
|
adap->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
|
init_completion(&i2c_dev->complete);
|
||||||
|
|
||||||
|
ret = i2c_add_adapter(adap);
|
||||||
|
if (ret)
|
||||||
|
goto clk_free;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, i2c_dev);
|
||||||
|
|
||||||
|
clk_disable(i2c_dev->clk);
|
||||||
|
|
||||||
|
dev_info(i2c_dev->dev, "STM32F4 I2C driver registered\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
clk_free:
|
||||||
|
clk_disable_unprepare(i2c_dev->clk);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32f4_i2c_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct stm32f4_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
i2c_del_adapter(&i2c_dev->adap);
|
||||||
|
|
||||||
|
clk_unprepare(i2c_dev->clk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id stm32f4_i2c_match[] = {
|
||||||
|
{ .compatible = "st,stm32f4-i2c", },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, stm32f4_i2c_match);
|
||||||
|
|
||||||
|
static struct platform_driver stm32f4_i2c_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "stm32f4-i2c",
|
||||||
|
.of_match_table = stm32f4_i2c_match,
|
||||||
|
},
|
||||||
|
.probe = stm32f4_i2c_probe,
|
||||||
|
.remove = stm32f4_i2c_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(stm32f4_i2c_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("M'boumba Cedric Madianga <cedric.madianga@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("STMicroelectronics STM32F4 I2C driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,346 @@
|
||||||
|
/*
|
||||||
|
* drivers/i2c/busses/i2c-tegra-bpmp.c
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 NVIDIA Corporation. All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Shardar Shariff Md <smohammed@nvidia.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
|
#include <soc/tegra/bpmp-abi.h>
|
||||||
|
#include <soc/tegra/bpmp.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Serialized I2C message header size is 6 bytes and includes address, flags
|
||||||
|
* and length
|
||||||
|
*/
|
||||||
|
#define SERIALI2C_HDR_SIZE 6
|
||||||
|
|
||||||
|
struct tegra_bpmp_i2c {
|
||||||
|
struct i2c_adapter adapter;
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
|
struct tegra_bpmp *bpmp;
|
||||||
|
unsigned int bus;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Linux flags are translated to BPMP defined I2C flags that are used in BPMP
|
||||||
|
* firmware I2C driver to avoid any issues in future if Linux I2C flags are
|
||||||
|
* changed.
|
||||||
|
*/
|
||||||
|
static int tegra_bpmp_xlate_flags(u16 flags, u16 *out)
|
||||||
|
{
|
||||||
|
if (flags & I2C_M_TEN) {
|
||||||
|
*out |= SERIALI2C_TEN;
|
||||||
|
flags &= ~I2C_M_TEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & I2C_M_RD) {
|
||||||
|
*out |= SERIALI2C_RD;
|
||||||
|
flags &= ~I2C_M_RD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & I2C_M_STOP) {
|
||||||
|
*out |= SERIALI2C_STOP;
|
||||||
|
flags &= ~I2C_M_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & I2C_M_NOSTART) {
|
||||||
|
*out |= SERIALI2C_NOSTART;
|
||||||
|
flags &= ~I2C_M_NOSTART;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & I2C_M_REV_DIR_ADDR) {
|
||||||
|
*out |= SERIALI2C_REV_DIR_ADDR;
|
||||||
|
flags &= ~I2C_M_REV_DIR_ADDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & I2C_M_IGNORE_NAK) {
|
||||||
|
*out |= SERIALI2C_IGNORE_NAK;
|
||||||
|
flags &= ~I2C_M_IGNORE_NAK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & I2C_M_NO_RD_ACK) {
|
||||||
|
*out |= SERIALI2C_NO_RD_ACK;
|
||||||
|
flags &= ~I2C_M_NO_RD_ACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & I2C_M_RECV_LEN) {
|
||||||
|
*out |= SERIALI2C_RECV_LEN;
|
||||||
|
flags &= ~I2C_M_RECV_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (flags != 0) ? -EINVAL : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The serialized I2C format is simply the following:
|
||||||
|
* [addr little-endian][flags little-endian][len little-endian][data if write]
|
||||||
|
* [addr little-endian][flags little-endian][len little-endian][data if write]
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* The flags are translated from Linux kernel representation to seriali2c
|
||||||
|
* representation. Any undefined flag being set causes an error.
|
||||||
|
*
|
||||||
|
* The data is there only for writes. Reads have the data transferred in the
|
||||||
|
* other direction, and thus data is not present.
|
||||||
|
*
|
||||||
|
* See deserialize_i2c documentation for the data format in the other direction.
|
||||||
|
*/
|
||||||
|
static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
|
||||||
|
struct mrq_i2c_request *request,
|
||||||
|
struct i2c_msg *msgs,
|
||||||
|
unsigned int num)
|
||||||
|
{
|
||||||
|
char *buf = request->xfer.data_buf;
|
||||||
|
unsigned int i, j, pos = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
struct i2c_msg *msg = &msgs[i];
|
||||||
|
u16 flags = 0;
|
||||||
|
|
||||||
|
err = tegra_bpmp_xlate_flags(msg->flags, &flags);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
buf[pos++] = msg->addr & 0xff;
|
||||||
|
buf[pos++] = (msg->addr & 0xff00) >> 8;
|
||||||
|
buf[pos++] = flags & 0xff;
|
||||||
|
buf[pos++] = (flags & 0xff00) >> 8;
|
||||||
|
buf[pos++] = msg->len & 0xff;
|
||||||
|
buf[pos++] = (msg->len & 0xff00) >> 8;
|
||||||
|
|
||||||
|
if ((flags & SERIALI2C_RD) == 0) {
|
||||||
|
for (j = 0; j < msg->len; j++)
|
||||||
|
buf[pos++] = msg->buf[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request->xfer.data_size = pos;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data in the BPMP -> CPU direction is composed of sequential blocks for
|
||||||
|
* those messages that have I2C_M_RD. So, for example, if you have:
|
||||||
|
*
|
||||||
|
* - !I2C_M_RD, len == 5, data == a0 01 02 03 04
|
||||||
|
* - !I2C_M_RD, len == 1, data == a0
|
||||||
|
* - I2C_M_RD, len == 2, data == [uninitialized buffer 1]
|
||||||
|
* - !I2C_M_RD, len == 1, data == a2
|
||||||
|
* - I2C_M_RD, len == 2, data == [uninitialized buffer 2]
|
||||||
|
*
|
||||||
|
* ...then the data in the BPMP -> CPU direction would be 4 bytes total, and
|
||||||
|
* would contain 2 bytes that will go to uninitialized buffer 1, and 2 bytes
|
||||||
|
* that will go to uninitialized buffer 2.
|
||||||
|
*/
|
||||||
|
static int tegra_bpmp_i2c_deserialize(struct tegra_bpmp_i2c *i2c,
|
||||||
|
struct mrq_i2c_response *response,
|
||||||
|
struct i2c_msg *msgs,
|
||||||
|
unsigned int num)
|
||||||
|
{
|
||||||
|
size_t size = response->xfer.data_size, len = 0, pos = 0;
|
||||||
|
char *buf = response->xfer.data_buf;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++)
|
||||||
|
if (msgs[i].flags & I2C_M_RD)
|
||||||
|
len += msgs[i].len;
|
||||||
|
|
||||||
|
if (len != size)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
if (msgs[i].flags & I2C_M_RD) {
|
||||||
|
memcpy(msgs[i].buf, buf + pos, msgs[i].len);
|
||||||
|
pos += msgs[i].len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_bpmp_i2c_msg_len_check(struct i2c_msg *msgs, unsigned int num)
|
||||||
|
{
|
||||||
|
size_t tx_len = 0, rx_len = 0;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++)
|
||||||
|
if (!(msgs[i].flags & I2C_M_RD))
|
||||||
|
tx_len += SERIALI2C_HDR_SIZE + msgs[i].len;
|
||||||
|
|
||||||
|
if (tx_len > TEGRA_I2C_IPC_MAX_IN_BUF_SIZE)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++)
|
||||||
|
if ((msgs[i].flags & I2C_M_RD))
|
||||||
|
rx_len += msgs[i].len;
|
||||||
|
|
||||||
|
if (rx_len > TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
|
||||||
|
struct mrq_i2c_request *request,
|
||||||
|
struct mrq_i2c_response *response)
|
||||||
|
{
|
||||||
|
struct tegra_bpmp_message msg;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
request->cmd = CMD_I2C_XFER;
|
||||||
|
request->xfer.bus_id = i2c->bus;
|
||||||
|
|
||||||
|
memset(&msg, 0, sizeof(msg));
|
||||||
|
msg.mrq = MRQ_I2C;
|
||||||
|
msg.tx.data = request;
|
||||||
|
msg.tx.size = sizeof(*request);
|
||||||
|
msg.rx.data = response;
|
||||||
|
msg.rx.size = sizeof(*response);
|
||||||
|
|
||||||
|
if (irqs_disabled())
|
||||||
|
err = tegra_bpmp_transfer_atomic(i2c->bpmp, &msg);
|
||||||
|
else
|
||||||
|
err = tegra_bpmp_transfer(i2c->bpmp, &msg);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
|
||||||
|
struct i2c_msg *msgs, int num)
|
||||||
|
{
|
||||||
|
struct tegra_bpmp_i2c *i2c = i2c_get_adapdata(adapter);
|
||||||
|
struct mrq_i2c_response response;
|
||||||
|
struct mrq_i2c_request request;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = tegra_bpmp_i2c_msg_len_check(msgs, num);
|
||||||
|
if (err < 0) {
|
||||||
|
dev_err(i2c->dev, "unsupported message length\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&request, 0, sizeof(request));
|
||||||
|
memset(&response, 0, sizeof(response));
|
||||||
|
|
||||||
|
err = tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num);
|
||||||
|
if (err < 0) {
|
||||||
|
dev_err(i2c->dev, "failed to serialize message: %d\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response);
|
||||||
|
if (err < 0) {
|
||||||
|
dev_err(i2c->dev, "failed to transfer message: %d\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tegra_bpmp_i2c_deserialize(i2c, &response, msgs, num);
|
||||||
|
if (err < 0) {
|
||||||
|
dev_err(i2c->dev, "failed to deserialize message: %d\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter)
|
||||||
|
{
|
||||||
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
|
||||||
|
I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_algorithm tegra_bpmp_i2c_algo = {
|
||||||
|
.master_xfer = tegra_bpmp_i2c_xfer,
|
||||||
|
.functionality = tegra_bpmp_i2c_func,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int tegra_bpmp_i2c_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct tegra_bpmp_i2c *i2c;
|
||||||
|
u32 value;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
|
||||||
|
if (!i2c)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
i2c->dev = &pdev->dev;
|
||||||
|
|
||||||
|
i2c->bpmp = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
if (!i2c->bpmp)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
err = of_property_read_u32(pdev->dev.of_node, "nvidia,bpmp-bus-id",
|
||||||
|
&value);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
i2c->bus = value;
|
||||||
|
|
||||||
|
i2c_set_adapdata(&i2c->adapter, i2c);
|
||||||
|
i2c->adapter.owner = THIS_MODULE;
|
||||||
|
strlcpy(i2c->adapter.name, "Tegra BPMP I2C adapter",
|
||||||
|
sizeof(i2c->adapter.name));
|
||||||
|
i2c->adapter.algo = &tegra_bpmp_i2c_algo;
|
||||||
|
i2c->adapter.dev.parent = &pdev->dev;
|
||||||
|
i2c->adapter.dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, i2c);
|
||||||
|
|
||||||
|
return i2c_add_adapter(&i2c->adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_bpmp_i2c_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct tegra_bpmp_i2c *i2c = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
i2c_del_adapter(&i2c->adapter);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id tegra_bpmp_i2c_of_match[] = {
|
||||||
|
{ .compatible = "nvidia,tegra186-bpmp-i2c", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, tegra_bpmp_i2c_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver tegra_bpmp_i2c_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "tegra-bpmp-i2c",
|
||||||
|
.of_match_table = tegra_bpmp_i2c_of_match,
|
||||||
|
},
|
||||||
|
.probe = tegra_bpmp_i2c_probe,
|
||||||
|
.remove = tegra_bpmp_i2c_remove,
|
||||||
|
};
|
||||||
|
module_platform_driver(tegra_bpmp_i2c_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("NVIDIA Tegra BPMP I2C bus contoller driver");
|
||||||
|
MODULE_AUTHOR("Shardar Shariff Md <smohammed@nvidia.com>");
|
||||||
|
MODULE_AUTHOR("Juha-Matti Tilli");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -188,11 +188,11 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev,
|
||||||
i2c->hlc_int_enable = thunder_i2c_hlc_int_enable;
|
i2c->hlc_int_enable = thunder_i2c_hlc_int_enable;
|
||||||
i2c->hlc_int_disable = thunder_i2c_hlc_int_disable;
|
i2c->hlc_int_disable = thunder_i2c_hlc_int_disable;
|
||||||
|
|
||||||
ret = pci_enable_msix(pdev, &i2c->i2c_msix, 1);
|
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
|
||||||
if (ret)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
ret = devm_request_irq(dev, i2c->i2c_msix.vector, octeon_i2c_isr, 0,
|
ret = devm_request_irq(dev, pci_irq_vector(pdev, 0), octeon_i2c_isr, 0,
|
||||||
DRV_NAME, i2c);
|
DRV_NAME, i2c);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
|
@ -372,7 +372,7 @@ static u32 xgene_slimpro_i2c_func(struct i2c_adapter *adapter)
|
||||||
I2C_FUNC_SMBUS_I2C_BLOCK;
|
I2C_FUNC_SMBUS_I2C_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm xgene_slimpro_i2c_algorithm = {
|
static const struct i2c_algorithm xgene_slimpro_i2c_algorithm = {
|
||||||
.smbus_xfer = xgene_slimpro_i2c_xfer,
|
.smbus_xfer = xgene_slimpro_i2c_xfer,
|
||||||
.functionality = xgene_slimpro_i2c_func,
|
.functionality = xgene_slimpro_i2c_func,
|
||||||
};
|
};
|
||||||
|
|
|
@ -334,7 +334,7 @@ static u32 xlp9xx_i2c_functionality(struct i2c_adapter *adapter)
|
||||||
I2C_FUNC_10BIT_ADDR;
|
I2C_FUNC_10BIT_ADDR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm xlp9xx_i2c_algo = {
|
static const struct i2c_algorithm xlp9xx_i2c_algo = {
|
||||||
.master_xfer = xlp9xx_i2c_xfer,
|
.master_xfer = xlp9xx_i2c_xfer,
|
||||||
.functionality = xlp9xx_i2c_functionality,
|
.functionality = xlp9xx_i2c_functionality,
|
||||||
};
|
};
|
||||||
|
|
|
@ -335,7 +335,7 @@ static u32 xlr_func(struct i2c_adapter *adap)
|
||||||
return (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | I2C_FUNC_I2C;
|
return (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | I2C_FUNC_I2C;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm xlr_i2c_algo = {
|
static const struct i2c_algorithm xlr_i2c_algo = {
|
||||||
.master_xfer = xlr_i2c_xfer,
|
.master_xfer = xlr_i2c_xfer,
|
||||||
.functionality = xlr_func,
|
.functionality = xlr_func,
|
||||||
};
|
};
|
||||||
|
|
|
@ -3705,6 +3705,39 @@ int i2c_slave_unregister(struct i2c_client *client)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i2c_slave_unregister);
|
EXPORT_SYMBOL_GPL(i2c_slave_unregister);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i2c_detect_slave_mode - detect operation mode
|
||||||
|
* @dev: The device owning the bus
|
||||||
|
*
|
||||||
|
* This checks the device nodes for an I2C slave by checking the address
|
||||||
|
* used in the reg property. If the address match the I2C_OWN_SLAVE_ADDRESS
|
||||||
|
* flag this means the device is configured to act as a I2C slave and it will
|
||||||
|
* be listening at that address.
|
||||||
|
*
|
||||||
|
* Returns true if an I2C own slave address is detected, otherwise returns
|
||||||
|
* false.
|
||||||
|
*/
|
||||||
|
bool i2c_detect_slave_mode(struct device *dev)
|
||||||
|
{
|
||||||
|
if (IS_BUILTIN(CONFIG_OF) && dev->of_node) {
|
||||||
|
struct device_node *child;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
for_each_child_of_node(dev->of_node, child) {
|
||||||
|
of_property_read_u32(child, "reg", ®);
|
||||||
|
if (reg & I2C_OWN_SLAVE_ADDRESS) {
|
||||||
|
of_node_put(child);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (IS_BUILTIN(CONFIG_ACPI) && ACPI_HANDLE(dev)) {
|
||||||
|
dev_dbg(dev, "ACPI slave is not supported yet\n");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(i2c_detect_slave_mode);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
|
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
|
||||||
|
|
|
@ -40,7 +40,6 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/version.h>
|
|
||||||
#include <linux/i2c/mlxcpld.h>
|
#include <linux/i2c/mlxcpld.h>
|
||||||
|
|
||||||
#define CPLD_MUX_MAX_NCHANS 8
|
#define CPLD_MUX_MAX_NCHANS 8
|
||||||
|
|
|
@ -90,6 +90,7 @@ static const struct of_device_id pca9541_of_match[] = {
|
||||||
{ .compatible = "nxp,pca9541" },
|
{ .compatible = "nxp,pca9541" },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, pca9541_of_match);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -41,14 +41,20 @@
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/i2c-mux.h>
|
#include <linux/i2c-mux.h>
|
||||||
#include <linux/i2c/pca954x.h>
|
#include <linux/i2c/pca954x.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
#define PCA954X_MAX_NCHANS 8
|
#define PCA954X_MAX_NCHANS 8
|
||||||
|
|
||||||
|
#define PCA954X_IRQ_OFFSET 4
|
||||||
|
|
||||||
enum pca_type {
|
enum pca_type {
|
||||||
pca_9540,
|
pca_9540,
|
||||||
pca_9542,
|
pca_9542,
|
||||||
|
@ -63,6 +69,7 @@ enum pca_type {
|
||||||
struct chip_desc {
|
struct chip_desc {
|
||||||
u8 nchans;
|
u8 nchans;
|
||||||
u8 enable; /* used for muxes only */
|
u8 enable; /* used for muxes only */
|
||||||
|
u8 has_irq;
|
||||||
enum muxtype {
|
enum muxtype {
|
||||||
pca954x_ismux = 0,
|
pca954x_ismux = 0,
|
||||||
pca954x_isswi
|
pca954x_isswi
|
||||||
|
@ -75,6 +82,10 @@ struct pca954x {
|
||||||
u8 last_chan; /* last register value */
|
u8 last_chan; /* last register value */
|
||||||
u8 deselect;
|
u8 deselect;
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
|
|
||||||
|
struct irq_domain *irq;
|
||||||
|
unsigned int irq_mask;
|
||||||
|
spinlock_t lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Provide specs for the PCA954x types we know about */
|
/* Provide specs for the PCA954x types we know about */
|
||||||
|
@ -84,17 +95,26 @@ static const struct chip_desc chips[] = {
|
||||||
.enable = 0x4,
|
.enable = 0x4,
|
||||||
.muxtype = pca954x_ismux,
|
.muxtype = pca954x_ismux,
|
||||||
},
|
},
|
||||||
|
[pca_9542] = {
|
||||||
|
.nchans = 2,
|
||||||
|
.enable = 0x4,
|
||||||
|
.has_irq = 1,
|
||||||
|
.muxtype = pca954x_ismux,
|
||||||
|
},
|
||||||
[pca_9543] = {
|
[pca_9543] = {
|
||||||
.nchans = 2,
|
.nchans = 2,
|
||||||
|
.has_irq = 1,
|
||||||
.muxtype = pca954x_isswi,
|
.muxtype = pca954x_isswi,
|
||||||
},
|
},
|
||||||
[pca_9544] = {
|
[pca_9544] = {
|
||||||
.nchans = 4,
|
.nchans = 4,
|
||||||
.enable = 0x4,
|
.enable = 0x4,
|
||||||
|
.has_irq = 1,
|
||||||
.muxtype = pca954x_ismux,
|
.muxtype = pca954x_ismux,
|
||||||
},
|
},
|
||||||
[pca_9545] = {
|
[pca_9545] = {
|
||||||
.nchans = 4,
|
.nchans = 4,
|
||||||
|
.has_irq = 1,
|
||||||
.muxtype = pca954x_isswi,
|
.muxtype = pca954x_isswi,
|
||||||
},
|
},
|
||||||
[pca_9547] = {
|
[pca_9547] = {
|
||||||
|
@ -110,7 +130,7 @@ static const struct chip_desc chips[] = {
|
||||||
|
|
||||||
static const struct i2c_device_id pca954x_id[] = {
|
static const struct i2c_device_id pca954x_id[] = {
|
||||||
{ "pca9540", pca_9540 },
|
{ "pca9540", pca_9540 },
|
||||||
{ "pca9542", pca_9540 },
|
{ "pca9542", pca_9542 },
|
||||||
{ "pca9543", pca_9543 },
|
{ "pca9543", pca_9543 },
|
||||||
{ "pca9544", pca_9544 },
|
{ "pca9544", pca_9544 },
|
||||||
{ "pca9545", pca_9545 },
|
{ "pca9545", pca_9545 },
|
||||||
|
@ -124,7 +144,7 @@ MODULE_DEVICE_TABLE(i2c, pca954x_id);
|
||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
static const struct acpi_device_id pca954x_acpi_ids[] = {
|
static const struct acpi_device_id pca954x_acpi_ids[] = {
|
||||||
{ .id = "PCA9540", .driver_data = pca_9540 },
|
{ .id = "PCA9540", .driver_data = pca_9540 },
|
||||||
{ .id = "PCA9542", .driver_data = pca_9540 },
|
{ .id = "PCA9542", .driver_data = pca_9542 },
|
||||||
{ .id = "PCA9543", .driver_data = pca_9543 },
|
{ .id = "PCA9543", .driver_data = pca_9543 },
|
||||||
{ .id = "PCA9544", .driver_data = pca_9544 },
|
{ .id = "PCA9544", .driver_data = pca_9544 },
|
||||||
{ .id = "PCA9545", .driver_data = pca_9545 },
|
{ .id = "PCA9545", .driver_data = pca_9545 },
|
||||||
|
@ -148,6 +168,7 @@ static const struct of_device_id pca954x_of_match[] = {
|
||||||
{ .compatible = "nxp,pca9548", .data = &chips[pca_9548] },
|
{ .compatible = "nxp,pca9548", .data = &chips[pca_9548] },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, pca954x_of_match);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer()
|
/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer()
|
||||||
|
@ -217,6 +238,114 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
|
||||||
return pca954x_reg_write(muxc->parent, client, data->last_chan);
|
return pca954x_reg_write(muxc->parent, client, data->last_chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct pca954x *data = dev_id;
|
||||||
|
unsigned int child_irq;
|
||||||
|
int ret, i, handled = 0;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_byte(data->client);
|
||||||
|
if (ret < 0)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
for (i = 0; i < data->chip->nchans; i++) {
|
||||||
|
if (ret & BIT(PCA954X_IRQ_OFFSET + i)) {
|
||||||
|
child_irq = irq_linear_revmap(data->irq, i);
|
||||||
|
handle_nested_irq(child_irq);
|
||||||
|
handled++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handled ? IRQ_HANDLED : IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pca954x_irq_mask(struct irq_data *idata)
|
||||||
|
{
|
||||||
|
struct pca954x *data = irq_data_get_irq_chip_data(idata);
|
||||||
|
unsigned int pos = idata->hwirq;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&data->lock, flags);
|
||||||
|
|
||||||
|
data->irq_mask &= ~BIT(pos);
|
||||||
|
if (!data->irq_mask)
|
||||||
|
disable_irq(data->client->irq);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&data->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pca954x_irq_unmask(struct irq_data *idata)
|
||||||
|
{
|
||||||
|
struct pca954x *data = irq_data_get_irq_chip_data(idata);
|
||||||
|
unsigned int pos = idata->hwirq;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&data->lock, flags);
|
||||||
|
|
||||||
|
if (!data->irq_mask)
|
||||||
|
enable_irq(data->client->irq);
|
||||||
|
data->irq_mask |= BIT(pos);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&data->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type)
|
||||||
|
{
|
||||||
|
if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_LOW)
|
||||||
|
return -EINVAL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_chip pca954x_irq_chip = {
|
||||||
|
.name = "i2c-mux-pca954x",
|
||||||
|
.irq_mask = pca954x_irq_mask,
|
||||||
|
.irq_unmask = pca954x_irq_unmask,
|
||||||
|
.irq_set_type = pca954x_irq_set_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int pca954x_irq_setup(struct i2c_mux_core *muxc)
|
||||||
|
{
|
||||||
|
struct pca954x *data = i2c_mux_priv(muxc);
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
|
int c, err, irq;
|
||||||
|
|
||||||
|
if (!data->chip->has_irq || client->irq <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
spin_lock_init(&data->lock);
|
||||||
|
|
||||||
|
data->irq = irq_domain_add_linear(client->dev.of_node,
|
||||||
|
data->chip->nchans,
|
||||||
|
&irq_domain_simple_ops, data);
|
||||||
|
if (!data->irq)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
for (c = 0; c < data->chip->nchans; c++) {
|
||||||
|
irq = irq_create_mapping(data->irq, c);
|
||||||
|
irq_set_chip_data(irq, data);
|
||||||
|
irq_set_chip_and_handler(irq, &pca954x_irq_chip,
|
||||||
|
handle_simple_irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = devm_request_threaded_irq(&client->dev, data->client->irq, NULL,
|
||||||
|
pca954x_irq_handler,
|
||||||
|
IRQF_ONESHOT | IRQF_SHARED,
|
||||||
|
"pca954x", data);
|
||||||
|
if (err)
|
||||||
|
goto err_req_irq;
|
||||||
|
|
||||||
|
disable_irq(data->client->irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err_req_irq:
|
||||||
|
for (c = 0; c < data->chip->nchans; c++) {
|
||||||
|
irq = irq_find_mapping(data->irq, c);
|
||||||
|
irq_dispose_mapping(irq);
|
||||||
|
}
|
||||||
|
irq_domain_remove(data->irq);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* I2C init/probing/exit functions
|
* I2C init/probing/exit functions
|
||||||
*/
|
*/
|
||||||
|
@ -281,6 +410,10 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
idle_disconnect_dt = of_node &&
|
idle_disconnect_dt = of_node &&
|
||||||
of_property_read_bool(of_node, "i2c-mux-idle-disconnect");
|
of_property_read_bool(of_node, "i2c-mux-idle-disconnect");
|
||||||
|
|
||||||
|
ret = pca954x_irq_setup(muxc);
|
||||||
|
if (ret)
|
||||||
|
goto fail_del_adapters;
|
||||||
|
|
||||||
/* Now create an adapter for each channel */
|
/* Now create an adapter for each channel */
|
||||||
for (num = 0; num < data->chip->nchans; num++) {
|
for (num = 0; num < data->chip->nchans; num++) {
|
||||||
bool idle_disconnect_pd = false;
|
bool idle_disconnect_pd = false;
|
||||||
|
@ -306,7 +439,7 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
dev_err(&client->dev,
|
dev_err(&client->dev,
|
||||||
"failed to register multiplexed adapter"
|
"failed to register multiplexed adapter"
|
||||||
" %d as bus %d\n", num, force);
|
" %d as bus %d\n", num, force);
|
||||||
goto virt_reg_failed;
|
goto fail_del_adapters;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,7 +450,7 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
virt_reg_failed:
|
fail_del_adapters:
|
||||||
i2c_mux_del_adapters(muxc);
|
i2c_mux_del_adapters(muxc);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -325,6 +458,16 @@ virt_reg_failed:
|
||||||
static int pca954x_remove(struct i2c_client *client)
|
static int pca954x_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
||||||
|
struct pca954x *data = i2c_mux_priv(muxc);
|
||||||
|
int c, irq;
|
||||||
|
|
||||||
|
if (data->irq) {
|
||||||
|
for (c = 0; c < data->chip->nchans; c++) {
|
||||||
|
irq = irq_find_mapping(data->irq, c);
|
||||||
|
irq_dispose_mapping(irq);
|
||||||
|
}
|
||||||
|
irq_domain_remove(data->irq);
|
||||||
|
}
|
||||||
|
|
||||||
i2c_mux_del_adapters(muxc);
|
i2c_mux_del_adapters(muxc);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include <linux/log2.h>
|
#include <linux/log2.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
#include <linux/of.h>
|
#include <linux/property.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/nvmem-provider.h>
|
#include <linux/nvmem-provider.h>
|
||||||
|
@ -562,26 +562,26 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip)
|
||||||
static void at24_get_ofdata(struct i2c_client *client,
|
|
||||||
struct at24_platform_data *chip)
|
|
||||||
{
|
{
|
||||||
const __be32 *val;
|
int err;
|
||||||
struct device_node *node = client->dev.of_node;
|
u32 val;
|
||||||
|
|
||||||
if (node) {
|
if (device_property_present(dev, "read-only"))
|
||||||
if (of_get_property(node, "read-only", NULL))
|
chip->flags |= AT24_FLAG_READONLY;
|
||||||
chip->flags |= AT24_FLAG_READONLY;
|
|
||||||
val = of_get_property(node, "pagesize", NULL);
|
err = device_property_read_u32(dev, "pagesize", &val);
|
||||||
if (val)
|
if (!err) {
|
||||||
chip->page_size = be32_to_cpup(val);
|
chip->page_size = val;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* This is slow, but we can't know all eeproms, so we better
|
||||||
|
* play safe. Specifying custom eeprom-types via platform_data
|
||||||
|
* is recommended anyhow.
|
||||||
|
*/
|
||||||
|
chip->page_size = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
static void at24_get_ofdata(struct i2c_client *client,
|
|
||||||
struct at24_platform_data *chip)
|
|
||||||
{ }
|
|
||||||
#endif /* CONFIG_OF */
|
|
||||||
|
|
||||||
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
@ -613,15 +613,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||||
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
|
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
|
||||||
magic >>= AT24_SIZE_BYTELEN;
|
magic >>= AT24_SIZE_BYTELEN;
|
||||||
chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
|
chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
|
||||||
/*
|
|
||||||
* This is slow, but we can't know all eeproms, so we better
|
|
||||||
* play safe. Specifying custom eeprom-types via platform_data
|
|
||||||
* is recommended anyhow.
|
|
||||||
*/
|
|
||||||
chip.page_size = 1;
|
|
||||||
|
|
||||||
/* update chipdata if OF is present */
|
at24_get_pdata(&client->dev, &chip);
|
||||||
at24_get_ofdata(client, &chip);
|
|
||||||
|
|
||||||
chip.setup = NULL;
|
chip.setup = NULL;
|
||||||
chip.context = NULL;
|
chip.context = NULL;
|
||||||
|
|
|
@ -283,6 +283,7 @@ enum i2c_slave_event {
|
||||||
|
|
||||||
extern int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb);
|
extern int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb);
|
||||||
extern int i2c_slave_unregister(struct i2c_client *client);
|
extern int i2c_slave_unregister(struct i2c_client *client);
|
||||||
|
extern bool i2c_detect_slave_mode(struct device *dev);
|
||||||
|
|
||||||
static inline int i2c_slave_event(struct i2c_client *client,
|
static inline int i2c_slave_event(struct i2c_client *client,
|
||||||
enum i2c_slave_event event, u8 *val)
|
enum i2c_slave_event event, u8 *val)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче