mirror of
https://github.com/torvalds/linux.git
synced 2024-11-29 23:51:37 +00:00
Merge branch 'i2c/for-4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: "Features: - new drivers: Renesas EMEV2, register based MUX, NXP LPC2xxx - core: scans DT and assigns wakeup interrupts. no driver changes needed. - core: some refcouting issues fixed and better API for that - core: new helper function for best effort block read emulation - slave framework: proper DT bindings and userspace instantiation - some bigger work for xiic, pxa, omap drivers .. and quite a number of smaller driver fixes, cleanups, improvements" * 'i2c/for-4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (65 commits) i2c: mux: reg Change ioread endianness for readback i2c: mux: reg: fix compilation warnings i2c: mux: reg: simplify register size checking i2c: muxes: fix leaked i2c adapter device node references i2c: allow specifying separate wakeup interrupt in device tree of/irq: export of_get_irq_byname() i2c: xgene-slimpro: dma_mapping_error() doesn't return an error code i2c: Replace I2C_CROS_EC_TUNNEL dependency eeprom: at24: use i2c_smbus_read_i2c_block_data_or_emulated i2c: core: Add support for best effort block read emulation i2c: lpc2k: add driver i2c: mux: Add register-based mux i2c-mux-reg i2c: dt: describe generic bindings i2c: slave: print warning if slave flag not set i2c: support 10 bit and slave addresses in sysfs 'new_device' i2c: take address space into account when checking for used addresses i2c: apply DT flags when probing i2c: make address check indpendent from client struct i2c: rename address check functions i2c: apply address offset for slaves, too ...
This commit is contained in:
commit
acceba598e
@ -2,7 +2,11 @@ Binding for the Cadence I2C controller
|
|||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- reg: Physical base address and size of the controller's register area.
|
- reg: Physical base address and size of the controller's register area.
|
||||||
- compatible: Compatibility string. Must be 'cdns,i2c-r1p10'.
|
- compatible: Should contain one of:
|
||||||
|
* "cdns,i2c-r1p10"
|
||||||
|
Note: Use this when cadence i2c controller version 1.0 is used.
|
||||||
|
* "cdns,i2c-r1p14"
|
||||||
|
Note: Use this when cadence i2c controller version 1.4 is used.
|
||||||
- clocks: Input clock specifier. Refer to common clock bindings.
|
- clocks: Input clock specifier. Refer to common clock bindings.
|
||||||
- interrupts: Interrupt specifier. Refer to interrupt bindings.
|
- interrupts: Interrupt specifier. Refer to interrupt bindings.
|
||||||
- #address-cells: Should be 1.
|
- #address-cells: Should be 1.
|
||||||
|
22
Documentation/devicetree/bindings/i2c/i2c-emev2.txt
Normal file
22
Documentation/devicetree/bindings/i2c/i2c-emev2.txt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Device tree configuration for Renesas EMEV2 IIC controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : "renesas,iic-emev2"
|
||||||
|
- reg : address start and address range size of device
|
||||||
|
- interrupts : specifier for the IIC controller interrupt
|
||||||
|
- clocks : phandle to the IP core SCLK
|
||||||
|
- clock-names : must be "sclk"
|
||||||
|
- #address-cells : should be <1>
|
||||||
|
- #size-cells : should be <0>
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
iic0: i2c@e0070000 {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
compatible = "renesas,iic-emev2";
|
||||||
|
reg = <0xe0070000 0x28>;
|
||||||
|
interrupts = <0 32 IRQ_TYPE_EDGE_RISING>;
|
||||||
|
clocks = <&iic0_sclk>;
|
||||||
|
clock-names = "sclk";
|
||||||
|
};
|
33
Documentation/devicetree/bindings/i2c/i2c-lpc2k.txt
Normal file
33
Documentation/devicetree/bindings/i2c/i2c-lpc2k.txt
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
NXP I2C controller for LPC2xxx/178x/18xx/43xx
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: must be "nxp,lpc1788-i2c"
|
||||||
|
- reg: physical address and length of the device registers
|
||||||
|
- interrupts: a single interrupt specifier
|
||||||
|
- clocks: clock for the device
|
||||||
|
- #address-cells: should be <1>
|
||||||
|
- #size-cells: should be <0>
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- clock-frequency: the desired I2C bus clock frequency in Hz; in
|
||||||
|
absence of this property the default value is used (100 kHz).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
i2c0: i2c@400a1000 {
|
||||||
|
compatible = "nxp,lpc1788-i2c";
|
||||||
|
reg = <0x400a1000 0x1000>;
|
||||||
|
interrupts = <18>;
|
||||||
|
clocks = <&ccu1 CLK_APB1_I2C0>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
&i2c0 {
|
||||||
|
clock-frequency = <400000>;
|
||||||
|
|
||||||
|
lm75@48 {
|
||||||
|
compatible = "nxp,lm75";
|
||||||
|
reg = <0x48>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
74
Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt
Normal file
74
Documentation/devicetree/bindings/i2c/i2c-mux-reg.txt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
Register-based I2C Bus Mux
|
||||||
|
|
||||||
|
This binding describes an I2C bus multiplexer that uses a single register
|
||||||
|
to route the I2C signals.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: i2c-mux-reg
|
||||||
|
- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side
|
||||||
|
port is connected to.
|
||||||
|
* Standard I2C mux properties. See mux.txt in this directory.
|
||||||
|
* I2C child bus nodes. See mux.txt in this directory.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- reg: this pair of <offset size> specifies the register to control the mux.
|
||||||
|
The <offset size> depends on its parent node. It can be any memory-mapped
|
||||||
|
address. The size must be either 1, 2, or 4 bytes. If reg is omitted, the
|
||||||
|
resource of this device will be used.
|
||||||
|
- little-endian: The existence indicates the register is in little endian.
|
||||||
|
- big-endian: The existence indicates the register is in big endian.
|
||||||
|
If both little-endian and big-endian are omitted, the endianness of the
|
||||||
|
CPU will be used.
|
||||||
|
- write-only: The existence indicates the register is write-only.
|
||||||
|
- idle-state: value to set the muxer to when idle. When no value is
|
||||||
|
given, it defaults to the last value used.
|
||||||
|
|
||||||
|
Whenever an access is made to a device on a child bus, the value set
|
||||||
|
in the revelant node's reg property will be output to the register.
|
||||||
|
|
||||||
|
If an idle state is defined, using the idle-state (optional) property,
|
||||||
|
whenever an access is not being made to a device on a child bus, the
|
||||||
|
register will be set according to the idle value.
|
||||||
|
|
||||||
|
If an idle state is not defined, the most recently used value will be
|
||||||
|
left programmed into the register.
|
||||||
|
|
||||||
|
Example of a mux on PCIe card, the host is a powerpc SoC (big endian):
|
||||||
|
|
||||||
|
i2c-mux {
|
||||||
|
/* the <offset size> depends on the address translation
|
||||||
|
* of the parent device. If omitted, device resource
|
||||||
|
* will be used instead. The size is to determine
|
||||||
|
* whether iowrite32, iowrite16, or iowrite8 will be used.
|
||||||
|
*/
|
||||||
|
reg = <0x6028 0x4>;
|
||||||
|
little-endian; /* little endian register on PCIe */
|
||||||
|
compatible = "i2c-mux-reg";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
i2c-parent = <&i2c1>;
|
||||||
|
i2c@0 {
|
||||||
|
reg = <0>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
si5338: clock-generator@70 {
|
||||||
|
compatible = "silabs,si5338";
|
||||||
|
reg = <0x70>;
|
||||||
|
/* other stuff */
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c@1 {
|
||||||
|
/* data is written using iowrite32 */
|
||||||
|
reg = <1>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
si5338: clock-generator@70 {
|
||||||
|
compatible = "silabs,si5338";
|
||||||
|
reg = <0x70>;
|
||||||
|
/* other stuff */
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
45
Documentation/devicetree/bindings/i2c/i2c.txt
Normal file
45
Documentation/devicetree/bindings/i2c/i2c.txt
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
Generic device tree bindings for I2C busses
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
This document describes generic bindings which can be used to describe I2C
|
||||||
|
busses in a device tree.
|
||||||
|
|
||||||
|
Required properties
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
- #address-cells - should be <1>. Read more about addresses below.
|
||||||
|
- #size-cells - should be <0>.
|
||||||
|
- compatible - name of I2C bus controller following generic names
|
||||||
|
recommended practice.
|
||||||
|
|
||||||
|
For other required properties e.g. to describe register sets,
|
||||||
|
clocks, etc. check the binding documentation of the specific driver.
|
||||||
|
|
||||||
|
The cells properties above define that an address of children of an I2C bus
|
||||||
|
are described by a single value. This is usually a 7 bit address. However,
|
||||||
|
flags can be attached to the address. I2C_TEN_BIT_ADDRESS is used to mark a 10
|
||||||
|
bit address. It is needed to avoid the ambiguity between e.g. a 7 bit address
|
||||||
|
of 0x50 and a 10 bit address of 0x050 which, in theory, can be on the same bus.
|
||||||
|
Another flag is I2C_OWN_SLAVE_ADDRESS to mark addresses on which we listen to
|
||||||
|
be devices ourselves.
|
||||||
|
|
||||||
|
Optional properties
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
These properties may not be supported by all drivers. However, if a driver
|
||||||
|
wants to support one of the below features, it should adapt the bindings below.
|
||||||
|
|
||||||
|
- clock-frequency - frequency of bus clock in Hz.
|
||||||
|
- wakeup-source - device can be used as a wakeup source.
|
||||||
|
|
||||||
|
- interrupts - interrupts used by the device.
|
||||||
|
- interrupt-names - "irq" and "wakeup" names are recognized by I2C core,
|
||||||
|
other names are left to individual drivers.
|
||||||
|
|
||||||
|
Binding may contain optional "interrupts" property, describing interrupts
|
||||||
|
used by the device. I2C core will assign "irq" interrupt (or the very first
|
||||||
|
interrupt if not using interrupt names) as primary interrupt for the slave.
|
||||||
|
|
||||||
|
Also, if device is marked as a wakeup source, I2C core will set up "wakeup"
|
||||||
|
interrupt for the device. If "wakeup" interrupt name is not present in the
|
||||||
|
binding, then primary interrupt will be used as wakeup interrupt.
|
@ -95,6 +95,8 @@ stm,m41t00 Serial Access TIMEKEEPER
|
|||||||
stm,m41t62 Serial real-time clock (RTC) with alarm
|
stm,m41t62 Serial real-time clock (RTC) with alarm
|
||||||
stm,m41t80 M41T80 - SERIAL ACCESS RTC WITH ALARMS
|
stm,m41t80 M41T80 - SERIAL ACCESS RTC WITH ALARMS
|
||||||
taos,tsl2550 Ambient Light Sensor with SMBUS/Two Wire Serial Interface
|
taos,tsl2550 Ambient Light Sensor with SMBUS/Two Wire Serial Interface
|
||||||
|
ti,ads7828 8-Channels, 12-bit ADC
|
||||||
|
ti,ads7830 8-Channels, 8-bit ADC
|
||||||
ti,tsc2003 I2C Touch-Screen Controller
|
ti,tsc2003 I2C Touch-Screen Controller
|
||||||
ti,tmp102 Low Power Digital Temperature Sensor with SMBUS/Two Wire Serial Interface
|
ti,tmp102 Low Power Digital Temperature Sensor with SMBUS/Two Wire Serial Interface
|
||||||
ti,tmp103 Low Power Digital Temperature Sensor with SMBUS/Two Wire Serial Interface
|
ti,tmp103 Low Power Digital Temperature Sensor with SMBUS/Two Wire Serial Interface
|
||||||
|
@ -20,6 +20,7 @@ It currently supports the following devices:
|
|||||||
* (type=5) Analog Devices evaluation boards: ADM1025, ADM1030, ADM1031
|
* (type=5) Analog Devices evaluation boards: ADM1025, ADM1030, ADM1031
|
||||||
* (type=6) Barco LPT->DVI (K5800236) adapter
|
* (type=6) Barco LPT->DVI (K5800236) adapter
|
||||||
* (type=7) One For All JP1 parallel port adapter
|
* (type=7) One For All JP1 parallel port adapter
|
||||||
|
* (type=8) VCT-jig
|
||||||
|
|
||||||
These devices use different pinout configurations, so you have to tell
|
These devices use different pinout configurations, so you have to tell
|
||||||
the driver what you have, using the type module parameter. There is no
|
the driver what you have, using the type module parameter. There is no
|
||||||
|
@ -31,10 +31,13 @@ User manual
|
|||||||
===========
|
===========
|
||||||
|
|
||||||
I2C slave backends behave like standard I2C clients. So, you can instantiate
|
I2C slave backends behave like standard I2C clients. So, you can instantiate
|
||||||
them as described in the document 'instantiating-devices'. A quick example for
|
them as described in the document 'instantiating-devices'. The only difference
|
||||||
instantiating the slave-eeprom driver from userspace at address 0x64 on bus 1:
|
is that i2c slave backends have their own address space. So, you have to add
|
||||||
|
0x1000 to the address you would originally request. An example for
|
||||||
|
instantiating the slave-eeprom driver from userspace at the 7 bit address 0x64
|
||||||
|
on bus 1:
|
||||||
|
|
||||||
# echo slave-24c02 0x64 > /sys/bus/i2c/devices/i2c-1/new_device
|
# echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-1/new_device
|
||||||
|
|
||||||
Each backend should come with separate documentation to describe its specific
|
Each backend should come with separate documentation to describe its specific
|
||||||
behaviour and setup.
|
behaviour and setup.
|
||||||
|
@ -2,6 +2,10 @@ The I2C protocol knows about two kinds of device addresses: normal 7 bit
|
|||||||
addresses, and an extended set of 10 bit addresses. The sets of addresses
|
addresses, and an extended set of 10 bit addresses. The sets of addresses
|
||||||
do not intersect: the 7 bit address 0x10 is not the same as the 10 bit
|
do not intersect: the 7 bit address 0x10 is not the same as the 10 bit
|
||||||
address 0x10 (though a single device could respond to both of them).
|
address 0x10 (though a single device could respond to both of them).
|
||||||
|
To avoid ambiguity, the user sees 10 bit addresses mapped to a different
|
||||||
|
address space, namely 0xa000-0xa3ff. The leading 0xa (= 10) represents the
|
||||||
|
10 bit mode. This is used for creating device names in sysfs. It is also
|
||||||
|
needed when instantiating 10 bit devices via the new_device file in sysfs.
|
||||||
|
|
||||||
I2C messages to and from 10-bit address devices have a different format.
|
I2C messages to and from 10-bit address devices have a different format.
|
||||||
See the I2C specification for the details.
|
See the I2C specification for the details.
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
#define USIBU1_RSTCTRL 0x0ac
|
#define USIBU1_RSTCTRL 0x0ac
|
||||||
#define USIBU2_RSTCTRL 0x0b0
|
#define USIBU2_RSTCTRL 0x0b0
|
||||||
#define USIBU3_RSTCTRL 0x0b4
|
#define USIBU3_RSTCTRL 0x0b4
|
||||||
|
#define IIC0_RSTCTRL 0x0dc
|
||||||
|
#define IIC1_RSTCTRL 0x0e0
|
||||||
#define STI_RSTCTRL 0x124
|
#define STI_RSTCTRL 0x124
|
||||||
#define STI_CLKSEL 0x688
|
#define STI_CLKSEL 0x688
|
||||||
|
|
||||||
@ -66,6 +68,10 @@ static void __init emev2_smu_init(void)
|
|||||||
emev2_smu_write(2, USIBU1_RSTCTRL);
|
emev2_smu_write(2, USIBU1_RSTCTRL);
|
||||||
emev2_smu_write(2, USIBU2_RSTCTRL);
|
emev2_smu_write(2, USIBU2_RSTCTRL);
|
||||||
emev2_smu_write(2, USIBU3_RSTCTRL);
|
emev2_smu_write(2, USIBU3_RSTCTRL);
|
||||||
|
|
||||||
|
/* deassert reset for IIC0->IIC1 */
|
||||||
|
emev2_smu_write(1, IIC0_RSTCTRL);
|
||||||
|
emev2_smu_write(1, IIC1_RSTCTRL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init emev2_smu_clkdiv_init(struct device_node *np)
|
static void __init emev2_smu_clkdiv_init(struct device_node *np)
|
||||||
|
@ -526,6 +526,13 @@ config I2C_EG20T
|
|||||||
ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
|
ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
|
||||||
ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
|
ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
|
||||||
|
|
||||||
|
config I2C_EMEV2
|
||||||
|
tristate "EMMA Mobile series I2C adapter"
|
||||||
|
depends on HAVE_CLK
|
||||||
|
help
|
||||||
|
If you say yes to this option, support will be included for the
|
||||||
|
I2C interface on the Renesas Electronics EM/EV family of processors.
|
||||||
|
|
||||||
config I2C_EXYNOS5
|
config I2C_EXYNOS5
|
||||||
tristate "Exynos5 high-speed I2C driver"
|
tristate "Exynos5 high-speed I2C driver"
|
||||||
depends on ARCH_EXYNOS && OF
|
depends on ARCH_EXYNOS && OF
|
||||||
@ -612,6 +619,16 @@ config I2C_KEMPLD
|
|||||||
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-kempld.
|
will be called i2c-kempld.
|
||||||
|
|
||||||
|
config I2C_LPC2K
|
||||||
|
tristate "I2C bus support for NXP LPC2K/LPC178x/18xx/43xx"
|
||||||
|
depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
|
||||||
|
help
|
||||||
|
This driver supports the I2C interface found several NXP
|
||||||
|
devices including LPC2xxx, LPC178x/7x and LPC18xx/43xx.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module
|
||||||
|
will be called i2c-lpc2k.
|
||||||
|
|
||||||
config I2C_MESON
|
config I2C_MESON
|
||||||
tristate "Amlogic Meson I2C controller"
|
tristate "Amlogic Meson I2C controller"
|
||||||
depends on ARCH_MESON
|
depends on ARCH_MESON
|
||||||
@ -1123,7 +1140,7 @@ config I2C_SIBYTE
|
|||||||
|
|
||||||
config I2C_CROS_EC_TUNNEL
|
config I2C_CROS_EC_TUNNEL
|
||||||
tristate "ChromeOS EC tunnel I2C bus"
|
tristate "ChromeOS EC tunnel I2C bus"
|
||||||
depends on CROS_EC_PROTO
|
depends on MFD_CROS_EC
|
||||||
help
|
help
|
||||||
If you say yes here you get an I2C bus that will tunnel i2c commands
|
If you say yes here you get an I2C bus that will tunnel i2c commands
|
||||||
through to the other side of the ChromeOS EC to the i2c bus
|
through to the other side of the ChromeOS EC to the i2c bus
|
||||||
|
@ -48,6 +48,7 @@ i2c-designware-pci-objs := i2c-designware-pcidrv.o
|
|||||||
obj-$(CONFIG_I2C_DIGICOLOR) += i2c-digicolor.o
|
obj-$(CONFIG_I2C_DIGICOLOR) += i2c-digicolor.o
|
||||||
obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o
|
obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o
|
||||||
obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
|
obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
|
||||||
|
obj-$(CONFIG_I2C_EMEV2) += i2c-emev2.o
|
||||||
obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o
|
obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o
|
||||||
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
|
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
|
||||||
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
|
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
|
||||||
@ -58,6 +59,7 @@ obj-$(CONFIG_I2C_IMX) += i2c-imx.o
|
|||||||
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
|
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
|
||||||
obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o
|
obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o
|
||||||
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
|
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
|
||||||
|
obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o
|
||||||
obj-$(CONFIG_I2C_MESON) += i2c-meson.o
|
obj-$(CONFIG_I2C_MESON) += i2c-meson.o
|
||||||
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
|
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
|
||||||
obj-$(CONFIG_I2C_MT65XX) += i2c-mt65xx.o
|
obj-$(CONFIG_I2C_MT65XX) += i2c-mt65xx.o
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
/* Register offsets for the I2C device. */
|
/* Register offsets for the I2C device. */
|
||||||
#define CDNS_I2C_CR_OFFSET 0x00 /* Control Register, RW */
|
#define CDNS_I2C_CR_OFFSET 0x00 /* Control Register, RW */
|
||||||
@ -113,6 +114,8 @@
|
|||||||
|
|
||||||
#define CDNS_I2C_TIMEOUT_MAX 0xFF
|
#define CDNS_I2C_TIMEOUT_MAX 0xFF
|
||||||
|
|
||||||
|
#define CDNS_I2C_BROKEN_HOLD_BIT BIT(0)
|
||||||
|
|
||||||
#define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset)
|
#define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset)
|
||||||
#define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset)
|
#define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset)
|
||||||
|
|
||||||
@ -135,6 +138,7 @@
|
|||||||
* @bus_hold_flag: Flag used in repeated start for clearing HOLD bit
|
* @bus_hold_flag: Flag used in repeated start for clearing HOLD bit
|
||||||
* @clk: Pointer to struct clk
|
* @clk: Pointer to struct clk
|
||||||
* @clk_rate_change_nb: Notifier block for clock rate changes
|
* @clk_rate_change_nb: Notifier block for clock rate changes
|
||||||
|
* @quirks: flag for broken hold bit usage in r1p10
|
||||||
*/
|
*/
|
||||||
struct cdns_i2c {
|
struct cdns_i2c {
|
||||||
void __iomem *membase;
|
void __iomem *membase;
|
||||||
@ -154,6 +158,11 @@ struct cdns_i2c {
|
|||||||
unsigned int bus_hold_flag;
|
unsigned int bus_hold_flag;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
struct notifier_block clk_rate_change_nb;
|
struct notifier_block clk_rate_change_nb;
|
||||||
|
u32 quirks;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cdns_platform_data {
|
||||||
|
u32 quirks;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_cdns_i2c(_nb) container_of(_nb, struct cdns_i2c, \
|
#define to_cdns_i2c(_nb) container_of(_nb, struct cdns_i2c, \
|
||||||
@ -172,6 +181,12 @@ static void cdns_i2c_clear_bus_hold(struct cdns_i2c *id)
|
|||||||
cdns_i2c_writereg(reg & ~CDNS_I2C_CR_HOLD, CDNS_I2C_CR_OFFSET);
|
cdns_i2c_writereg(reg & ~CDNS_I2C_CR_HOLD, CDNS_I2C_CR_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool cdns_is_holdquirk(struct cdns_i2c *id, bool hold_wrkaround)
|
||||||
|
{
|
||||||
|
return (hold_wrkaround &&
|
||||||
|
(id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cdns_i2c_isr - Interrupt handler for the I2C device
|
* cdns_i2c_isr - Interrupt handler for the I2C device
|
||||||
* @irq: irq number for the I2C device
|
* @irq: irq number for the I2C device
|
||||||
@ -186,6 +201,7 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
|
|||||||
{
|
{
|
||||||
unsigned int isr_status, avail_bytes, updatetx;
|
unsigned int isr_status, avail_bytes, updatetx;
|
||||||
unsigned int bytes_to_send;
|
unsigned int bytes_to_send;
|
||||||
|
bool hold_quirk;
|
||||||
struct cdns_i2c *id = ptr;
|
struct cdns_i2c *id = ptr;
|
||||||
/* Signal completion only after everything is updated */
|
/* Signal completion only after everything is updated */
|
||||||
int done_flag = 0;
|
int done_flag = 0;
|
||||||
@ -208,6 +224,8 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
|
|||||||
if (id->recv_count > id->curr_recv_count)
|
if (id->recv_count > id->curr_recv_count)
|
||||||
updatetx = 1;
|
updatetx = 1;
|
||||||
|
|
||||||
|
hold_quirk = (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT) && updatetx;
|
||||||
|
|
||||||
/* When receiving, handle data interrupt and completion interrupt */
|
/* When receiving, handle data interrupt and completion interrupt */
|
||||||
if (id->p_recv_buf &&
|
if (id->p_recv_buf &&
|
||||||
((isr_status & CDNS_I2C_IXR_COMP) ||
|
((isr_status & CDNS_I2C_IXR_COMP) ||
|
||||||
@ -229,8 +247,7 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
|
|||||||
id->recv_count--;
|
id->recv_count--;
|
||||||
id->curr_recv_count--;
|
id->curr_recv_count--;
|
||||||
|
|
||||||
if (updatetx &&
|
if (cdns_is_holdquirk(id, hold_quirk))
|
||||||
(id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1))
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,8 +258,7 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
|
|||||||
* maintain transfer size non-zero while performing a large
|
* maintain transfer size non-zero while performing a large
|
||||||
* receive operation.
|
* receive operation.
|
||||||
*/
|
*/
|
||||||
if (updatetx &&
|
if (cdns_is_holdquirk(id, hold_quirk)) {
|
||||||
(id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1)) {
|
|
||||||
/* wait while fifo is full */
|
/* wait while fifo is full */
|
||||||
while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) !=
|
while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) !=
|
||||||
(id->curr_recv_count - CDNS_I2C_FIFO_DEPTH))
|
(id->curr_recv_count - CDNS_I2C_FIFO_DEPTH))
|
||||||
@ -264,6 +280,22 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
|
|||||||
CDNS_I2C_XFER_SIZE_OFFSET);
|
CDNS_I2C_XFER_SIZE_OFFSET);
|
||||||
id->curr_recv_count = id->recv_count;
|
id->curr_recv_count = id->recv_count;
|
||||||
}
|
}
|
||||||
|
} else if (id->recv_count && !hold_quirk &&
|
||||||
|
!id->curr_recv_count) {
|
||||||
|
|
||||||
|
/* Set the slave address in address register*/
|
||||||
|
cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
|
||||||
|
CDNS_I2C_ADDR_OFFSET);
|
||||||
|
|
||||||
|
if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) {
|
||||||
|
cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
|
||||||
|
CDNS_I2C_XFER_SIZE_OFFSET);
|
||||||
|
id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
|
||||||
|
} else {
|
||||||
|
cdns_i2c_writereg(id->recv_count,
|
||||||
|
CDNS_I2C_XFER_SIZE_OFFSET);
|
||||||
|
id->curr_recv_count = id->recv_count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear hold (if not repeated start) and signal completion */
|
/* Clear hold (if not repeated start) and signal completion */
|
||||||
@ -535,11 +567,13 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
|||||||
int ret, count;
|
int ret, count;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
struct cdns_i2c *id = adap->algo_data;
|
struct cdns_i2c *id = adap->algo_data;
|
||||||
|
bool hold_quirk;
|
||||||
|
|
||||||
/* Check if the bus is free */
|
/* Check if the bus is free */
|
||||||
if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA)
|
if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA)
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
|
hold_quirk = !!(id->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
|
||||||
/*
|
/*
|
||||||
* Set the flag to one when multiple messages are to be
|
* Set the flag to one when multiple messages are to be
|
||||||
* processed with a repeated start.
|
* processed with a repeated start.
|
||||||
@ -552,7 +586,7 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
|||||||
* followed by any other message, an error is returned
|
* followed by any other message, an error is returned
|
||||||
* indicating that this sequence is not supported.
|
* indicating that this sequence is not supported.
|
||||||
*/
|
*/
|
||||||
for (count = 0; count < num - 1; count++) {
|
for (count = 0; (count < num - 1 && hold_quirk); count++) {
|
||||||
if (msgs[count].flags & I2C_M_RD) {
|
if (msgs[count].flags & I2C_M_RD) {
|
||||||
dev_warn(adap->dev.parent,
|
dev_warn(adap->dev.parent,
|
||||||
"Can't do repeated start after a receive message\n");
|
"Can't do repeated start after a receive message\n");
|
||||||
@ -815,6 +849,17 @@ static int __maybe_unused cdns_i2c_resume(struct device *_dev)
|
|||||||
static SIMPLE_DEV_PM_OPS(cdns_i2c_dev_pm_ops, cdns_i2c_suspend,
|
static SIMPLE_DEV_PM_OPS(cdns_i2c_dev_pm_ops, cdns_i2c_suspend,
|
||||||
cdns_i2c_resume);
|
cdns_i2c_resume);
|
||||||
|
|
||||||
|
static const struct cdns_platform_data r1p10_i2c_def = {
|
||||||
|
.quirks = CDNS_I2C_BROKEN_HOLD_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id cdns_i2c_of_match[] = {
|
||||||
|
{ .compatible = "cdns,i2c-r1p10", .data = &r1p10_i2c_def },
|
||||||
|
{ .compatible = "cdns,i2c-r1p14",},
|
||||||
|
{ /* end of table */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, cdns_i2c_of_match);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cdns_i2c_probe - Platform registration call
|
* cdns_i2c_probe - Platform registration call
|
||||||
* @pdev: Handle to the platform device structure
|
* @pdev: Handle to the platform device structure
|
||||||
@ -830,6 +875,7 @@ static int cdns_i2c_probe(struct platform_device *pdev)
|
|||||||
struct resource *r_mem;
|
struct resource *r_mem;
|
||||||
struct cdns_i2c *id;
|
struct cdns_i2c *id;
|
||||||
int ret;
|
int ret;
|
||||||
|
const struct of_device_id *match;
|
||||||
|
|
||||||
id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL);
|
id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL);
|
||||||
if (!id)
|
if (!id)
|
||||||
@ -837,6 +883,12 @@ static int cdns_i2c_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
platform_set_drvdata(pdev, id);
|
platform_set_drvdata(pdev, id);
|
||||||
|
|
||||||
|
match = of_match_node(cdns_i2c_of_match, pdev->dev.of_node);
|
||||||
|
if (match && match->data) {
|
||||||
|
const struct cdns_platform_data *data = match->data;
|
||||||
|
id->quirks = data->quirks;
|
||||||
|
}
|
||||||
|
|
||||||
r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
id->membase = devm_ioremap_resource(&pdev->dev, r_mem);
|
id->membase = devm_ioremap_resource(&pdev->dev, r_mem);
|
||||||
if (IS_ERR(id->membase))
|
if (IS_ERR(id->membase))
|
||||||
@ -844,6 +896,7 @@ static int cdns_i2c_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
id->irq = platform_get_irq(pdev, 0);
|
id->irq = platform_get_irq(pdev, 0);
|
||||||
|
|
||||||
|
id->adap.owner = THIS_MODULE;
|
||||||
id->adap.dev.of_node = pdev->dev.of_node;
|
id->adap.dev.of_node = pdev->dev.of_node;
|
||||||
id->adap.algo = &cdns_i2c_algo;
|
id->adap.algo = &cdns_i2c_algo;
|
||||||
id->adap.timeout = CDNS_I2C_TIMEOUT;
|
id->adap.timeout = CDNS_I2C_TIMEOUT;
|
||||||
@ -935,12 +988,6 @@ static int cdns_i2c_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id cdns_i2c_of_match[] = {
|
|
||||||
{ .compatible = "cdns,i2c-r1p10", },
|
|
||||||
{ /* end of table */ }
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, cdns_i2c_of_match);
|
|
||||||
|
|
||||||
static struct platform_driver cdns_i2c_drv = {
|
static struct platform_driver cdns_i2c_drv = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
|
@ -777,8 +777,7 @@ irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
|
|||||||
|
|
||||||
enabled = dw_readl(dev, DW_IC_ENABLE);
|
enabled = dw_readl(dev, DW_IC_ENABLE);
|
||||||
stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
|
stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
|
||||||
dev_dbg(dev->dev, "%s: %s enabled= 0x%x stat=0x%x\n", __func__,
|
dev_dbg(dev->dev, "%s: enabled=%#x stat=%#x\n", __func__, enabled, stat);
|
||||||
dev->adapter.name, enabled, stat);
|
|
||||||
if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))
|
if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
@ -260,8 +260,8 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
|||||||
|
|
||||||
snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci");
|
snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci");
|
||||||
|
|
||||||
r = devm_request_irq(&pdev->dev, pdev->irq, i2c_dw_isr, IRQF_SHARED,
|
r = devm_request_irq(&pdev->dev, pdev->irq, i2c_dw_isr,
|
||||||
adap->name, dev);
|
IRQF_SHARED | IRQF_COND_SUSPEND, adap->name, dev);
|
||||||
if (r) {
|
if (r) {
|
||||||
dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
|
dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
|
||||||
return r;
|
return r;
|
||||||
|
332
drivers/i2c/busses/i2c-emev2.c
Normal file
332
drivers/i2c/busses/i2c-emev2.c
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
/*
|
||||||
|
* I2C driver for the Renesas EMEV2 SoC
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Wolfram Sang <wsa@sang-engineering.com>
|
||||||
|
* Copyright 2013 Codethink Ltd.
|
||||||
|
* Copyright 2010-2015 Renesas Electronics Corporation
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2
|
||||||
|
* as published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/completion.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
|
||||||
|
/* I2C Registers */
|
||||||
|
#define I2C_OFS_IICACT0 0x00 /* start */
|
||||||
|
#define I2C_OFS_IIC0 0x04 /* shift */
|
||||||
|
#define I2C_OFS_IICC0 0x08 /* control */
|
||||||
|
#define I2C_OFS_SVA0 0x0c /* slave address */
|
||||||
|
#define I2C_OFS_IICCL0 0x10 /* clock select */
|
||||||
|
#define I2C_OFS_IICX0 0x14 /* extension */
|
||||||
|
#define I2C_OFS_IICS0 0x18 /* status */
|
||||||
|
#define I2C_OFS_IICSE0 0x1c /* status For emulation */
|
||||||
|
#define I2C_OFS_IICF0 0x20 /* IIC flag */
|
||||||
|
|
||||||
|
/* I2C IICACT0 Masks */
|
||||||
|
#define I2C_BIT_IICE0 0x0001
|
||||||
|
|
||||||
|
/* I2C IICC0 Masks */
|
||||||
|
#define I2C_BIT_LREL0 0x0040
|
||||||
|
#define I2C_BIT_WREL0 0x0020
|
||||||
|
#define I2C_BIT_SPIE0 0x0010
|
||||||
|
#define I2C_BIT_WTIM0 0x0008
|
||||||
|
#define I2C_BIT_ACKE0 0x0004
|
||||||
|
#define I2C_BIT_STT0 0x0002
|
||||||
|
#define I2C_BIT_SPT0 0x0001
|
||||||
|
|
||||||
|
/* I2C IICCL0 Masks */
|
||||||
|
#define I2C_BIT_SMC0 0x0008
|
||||||
|
#define I2C_BIT_DFC0 0x0004
|
||||||
|
|
||||||
|
/* I2C IICSE0 Masks */
|
||||||
|
#define I2C_BIT_MSTS0 0x0080
|
||||||
|
#define I2C_BIT_ALD0 0x0040
|
||||||
|
#define I2C_BIT_EXC0 0x0020
|
||||||
|
#define I2C_BIT_COI0 0x0010
|
||||||
|
#define I2C_BIT_TRC0 0x0008
|
||||||
|
#define I2C_BIT_ACKD0 0x0004
|
||||||
|
#define I2C_BIT_STD0 0x0002
|
||||||
|
#define I2C_BIT_SPD0 0x0001
|
||||||
|
|
||||||
|
/* I2C IICF0 Masks */
|
||||||
|
#define I2C_BIT_STCF 0x0080
|
||||||
|
#define I2C_BIT_IICBSY 0x0040
|
||||||
|
#define I2C_BIT_STCEN 0x0002
|
||||||
|
#define I2C_BIT_IICRSV 0x0001
|
||||||
|
|
||||||
|
struct em_i2c_device {
|
||||||
|
void __iomem *base;
|
||||||
|
struct i2c_adapter adap;
|
||||||
|
struct completion msg_done;
|
||||||
|
struct clk *sclk;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void em_clear_set_bit(struct em_i2c_device *priv, u8 clear, u8 set, u8 reg)
|
||||||
|
{
|
||||||
|
writeb((readb(priv->base + reg) & ~clear) | set, priv->base + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int em_i2c_wait_for_event(struct em_i2c_device *priv)
|
||||||
|
{
|
||||||
|
unsigned long time_left;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
reinit_completion(&priv->msg_done);
|
||||||
|
|
||||||
|
time_left = wait_for_completion_timeout(&priv->msg_done, priv->adap.timeout);
|
||||||
|
|
||||||
|
if (!time_left)
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
|
||||||
|
status = readb(priv->base + I2C_OFS_IICSE0);
|
||||||
|
return status & I2C_BIT_ALD0 ? -EAGAIN : status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void em_i2c_stop(struct em_i2c_device *priv)
|
||||||
|
{
|
||||||
|
/* Send Stop condition */
|
||||||
|
em_clear_set_bit(priv, 0, I2C_BIT_SPT0 | I2C_BIT_SPIE0, I2C_OFS_IICC0);
|
||||||
|
|
||||||
|
/* Wait for stop condition */
|
||||||
|
em_i2c_wait_for_event(priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void em_i2c_reset(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
struct em_i2c_device *priv = i2c_get_adapdata(adap);
|
||||||
|
int retr;
|
||||||
|
|
||||||
|
/* If I2C active */
|
||||||
|
if (readb(priv->base + I2C_OFS_IICACT0) & I2C_BIT_IICE0) {
|
||||||
|
/* Disable I2C operation */
|
||||||
|
writeb(0, priv->base + I2C_OFS_IICACT0);
|
||||||
|
|
||||||
|
retr = 1000;
|
||||||
|
while (readb(priv->base + I2C_OFS_IICACT0) == 1 && retr)
|
||||||
|
retr--;
|
||||||
|
WARN_ON(retr == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Transfer mode set */
|
||||||
|
writeb(I2C_BIT_DFC0, priv->base + I2C_OFS_IICCL0);
|
||||||
|
|
||||||
|
/* Can Issue start without detecting a stop, Reservation disabled. */
|
||||||
|
writeb(I2C_BIT_STCEN | I2C_BIT_IICRSV, priv->base + I2C_OFS_IICF0);
|
||||||
|
|
||||||
|
/* I2C enable, 9 bit interrupt mode */
|
||||||
|
writeb(I2C_BIT_WTIM0, priv->base + I2C_OFS_IICC0);
|
||||||
|
|
||||||
|
/* Enable I2C operation */
|
||||||
|
writeb(I2C_BIT_IICE0, priv->base + I2C_OFS_IICACT0);
|
||||||
|
|
||||||
|
retr = 1000;
|
||||||
|
while (readb(priv->base + I2C_OFS_IICACT0) == 0 && retr)
|
||||||
|
retr--;
|
||||||
|
WARN_ON(retr == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __em_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
|
||||||
|
int stop)
|
||||||
|
{
|
||||||
|
struct em_i2c_device *priv = i2c_get_adapdata(adap);
|
||||||
|
int count, status, read = !!(msg->flags & I2C_M_RD);
|
||||||
|
|
||||||
|
/* Send start condition */
|
||||||
|
em_clear_set_bit(priv, 0, I2C_BIT_ACKE0 | I2C_BIT_WTIM0, I2C_OFS_IICC0);
|
||||||
|
em_clear_set_bit(priv, 0, I2C_BIT_STT0, I2C_OFS_IICC0);
|
||||||
|
|
||||||
|
/* Send slave address and R/W type */
|
||||||
|
writeb((msg->addr << 1) | read, priv->base + I2C_OFS_IIC0);
|
||||||
|
|
||||||
|
/* Wait for transaction */
|
||||||
|
status = em_i2c_wait_for_event(priv);
|
||||||
|
if (status < 0)
|
||||||
|
goto out_reset;
|
||||||
|
|
||||||
|
/* Received NACK (result of setting slave address and R/W) */
|
||||||
|
if (!(status & I2C_BIT_ACKD0)) {
|
||||||
|
em_i2c_stop(priv);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extra setup for read transactions */
|
||||||
|
if (read) {
|
||||||
|
/* 8 bit interrupt mode */
|
||||||
|
em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_ACKE0, I2C_OFS_IICC0);
|
||||||
|
em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_WREL0, I2C_OFS_IICC0);
|
||||||
|
|
||||||
|
/* Wait for transaction */
|
||||||
|
status = em_i2c_wait_for_event(priv);
|
||||||
|
if (status < 0)
|
||||||
|
goto out_reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send / receive data */
|
||||||
|
for (count = 0; count < msg->len; count++) {
|
||||||
|
if (read) { /* Read transaction */
|
||||||
|
msg->buf[count] = readb(priv->base + I2C_OFS_IIC0);
|
||||||
|
em_clear_set_bit(priv, 0, I2C_BIT_WREL0, I2C_OFS_IICC0);
|
||||||
|
|
||||||
|
} else { /* Write transaction */
|
||||||
|
/* Received NACK */
|
||||||
|
if (!(status & I2C_BIT_ACKD0)) {
|
||||||
|
em_i2c_stop(priv);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write data */
|
||||||
|
writeb(msg->buf[count], priv->base + I2C_OFS_IIC0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for R/W transaction */
|
||||||
|
status = em_i2c_wait_for_event(priv);
|
||||||
|
if (status < 0)
|
||||||
|
goto out_reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stop)
|
||||||
|
em_i2c_stop(priv);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
|
||||||
|
out_reset:
|
||||||
|
em_i2c_reset(adap);
|
||||||
|
out:
|
||||||
|
return status < 0 ? status : -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int em_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
|
int num)
|
||||||
|
{
|
||||||
|
struct em_i2c_device *priv = i2c_get_adapdata(adap);
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
if (readb(priv->base + I2C_OFS_IICF0) & I2C_BIT_IICBSY)
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
ret = __em_i2c_xfer(adap, &msgs[i], (i == (num - 1)));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* I2C transfer completed */
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t em_i2c_irq_handler(int this_irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct em_i2c_device *priv = dev_id;
|
||||||
|
|
||||||
|
complete(&priv->msg_done);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 em_i2c_func(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct i2c_algorithm em_i2c_algo = {
|
||||||
|
.master_xfer = em_i2c_xfer,
|
||||||
|
.functionality = em_i2c_func,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int em_i2c_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct em_i2c_device *priv;
|
||||||
|
struct resource *r;
|
||||||
|
int irq, ret;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
priv->base = devm_ioremap_resource(&pdev->dev, r);
|
||||||
|
if (IS_ERR(priv->base))
|
||||||
|
return PTR_ERR(priv->base);
|
||||||
|
|
||||||
|
strlcpy(priv->adap.name, "EMEV2 I2C", sizeof(priv->adap.name));
|
||||||
|
|
||||||
|
priv->sclk = devm_clk_get(&pdev->dev, "sclk");
|
||||||
|
if (IS_ERR(priv->sclk))
|
||||||
|
return PTR_ERR(priv->sclk);
|
||||||
|
|
||||||
|
clk_prepare_enable(priv->sclk);
|
||||||
|
|
||||||
|
priv->adap.timeout = msecs_to_jiffies(100);
|
||||||
|
priv->adap.retries = 5;
|
||||||
|
priv->adap.dev.parent = &pdev->dev;
|
||||||
|
priv->adap.algo = &em_i2c_algo;
|
||||||
|
priv->adap.owner = THIS_MODULE;
|
||||||
|
priv->adap.dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
|
init_completion(&priv->msg_done);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, priv);
|
||||||
|
i2c_set_adapdata(&priv->adap, priv);
|
||||||
|
|
||||||
|
em_i2c_reset(&priv->adap);
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
ret = devm_request_irq(&pdev->dev, irq, em_i2c_irq_handler, 0,
|
||||||
|
"em_i2c", priv);
|
||||||
|
if (ret)
|
||||||
|
goto err_clk;
|
||||||
|
|
||||||
|
ret = i2c_add_adapter(&priv->adap);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
goto err_clk;
|
||||||
|
|
||||||
|
dev_info(&pdev->dev, "Added i2c controller %d, irq %d\n", priv->adap.nr, irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_clk:
|
||||||
|
clk_disable_unprepare(priv->sclk);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int em_i2c_remove(struct platform_device *dev)
|
||||||
|
{
|
||||||
|
struct em_i2c_device *priv = platform_get_drvdata(dev);
|
||||||
|
|
||||||
|
i2c_del_adapter(&priv->adap);
|
||||||
|
clk_disable_unprepare(priv->sclk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id em_i2c_ids[] = {
|
||||||
|
{ .compatible = "renesas,iic-emev2", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver em_i2c_driver = {
|
||||||
|
.probe = em_i2c_probe,
|
||||||
|
.remove = em_i2c_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "em-i2c",
|
||||||
|
.of_match_table = em_i2c_ids,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
module_platform_driver(em_i2c_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("EMEV2 I2C bus driver");
|
||||||
|
MODULE_AUTHOR("Ian Molton and Wolfram Sang <wsa@sang-engineering.com>");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_DEVICE_TABLE(of, em_i2c_ids);
|
513
drivers/i2c/busses/i2c-lpc2k.c
Normal file
513
drivers/i2c/busses/i2c-lpc2k.c
Normal file
@ -0,0 +1,513 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 NXP Semiconductors
|
||||||
|
*
|
||||||
|
* Code portions referenced from the i2x-pxa and i2c-pnx drivers
|
||||||
|
*
|
||||||
|
* Make SMBus byte and word transactions work on LPC178x/7x
|
||||||
|
* Copyright (c) 2012
|
||||||
|
* Alexander Potashev, Emcraft Systems, aspotashev@emcraft.com
|
||||||
|
* Anton Protopopov, Emcraft Systems, antonp@emcraft.com
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/time.h>
|
||||||
|
|
||||||
|
/* LPC24xx register offsets and bits */
|
||||||
|
#define LPC24XX_I2CONSET 0x00
|
||||||
|
#define LPC24XX_I2STAT 0x04
|
||||||
|
#define LPC24XX_I2DAT 0x08
|
||||||
|
#define LPC24XX_I2ADDR 0x0c
|
||||||
|
#define LPC24XX_I2SCLH 0x10
|
||||||
|
#define LPC24XX_I2SCLL 0x14
|
||||||
|
#define LPC24XX_I2CONCLR 0x18
|
||||||
|
|
||||||
|
#define LPC24XX_AA BIT(2)
|
||||||
|
#define LPC24XX_SI BIT(3)
|
||||||
|
#define LPC24XX_STO BIT(4)
|
||||||
|
#define LPC24XX_STA BIT(5)
|
||||||
|
#define LPC24XX_I2EN BIT(6)
|
||||||
|
|
||||||
|
#define LPC24XX_STO_AA (LPC24XX_STO | LPC24XX_AA)
|
||||||
|
#define LPC24XX_CLEAR_ALL (LPC24XX_AA | LPC24XX_SI | LPC24XX_STO | \
|
||||||
|
LPC24XX_STA | LPC24XX_I2EN)
|
||||||
|
|
||||||
|
/* I2C SCL clock has different duty cycle depending on mode */
|
||||||
|
#define I2C_STD_MODE_DUTY 46
|
||||||
|
#define I2C_FAST_MODE_DUTY 36
|
||||||
|
#define I2C_FAST_MODE_PLUS_DUTY 38
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 26 possible I2C status codes, but codes applicable only
|
||||||
|
* to master are listed here and used in this driver
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
M_BUS_ERROR = 0x00,
|
||||||
|
M_START = 0x08,
|
||||||
|
M_REPSTART = 0x10,
|
||||||
|
MX_ADDR_W_ACK = 0x18,
|
||||||
|
MX_ADDR_W_NACK = 0x20,
|
||||||
|
MX_DATA_W_ACK = 0x28,
|
||||||
|
MX_DATA_W_NACK = 0x30,
|
||||||
|
M_DATA_ARB_LOST = 0x38,
|
||||||
|
MR_ADDR_R_ACK = 0x40,
|
||||||
|
MR_ADDR_R_NACK = 0x48,
|
||||||
|
MR_DATA_R_ACK = 0x50,
|
||||||
|
MR_DATA_R_NACK = 0x58,
|
||||||
|
M_I2C_IDLE = 0xf8,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lpc2k_i2c {
|
||||||
|
void __iomem *base;
|
||||||
|
struct clk *clk;
|
||||||
|
int irq;
|
||||||
|
wait_queue_head_t wait;
|
||||||
|
struct i2c_adapter adap;
|
||||||
|
struct i2c_msg *msg;
|
||||||
|
int msg_idx;
|
||||||
|
int msg_status;
|
||||||
|
int is_last;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void i2c_lpc2k_reset(struct lpc2k_i2c *i2c)
|
||||||
|
{
|
||||||
|
/* Will force clear all statuses */
|
||||||
|
writel(LPC24XX_CLEAR_ALL, i2c->base + LPC24XX_I2CONCLR);
|
||||||
|
writel(0, i2c->base + LPC24XX_I2ADDR);
|
||||||
|
writel(LPC24XX_I2EN, i2c->base + LPC24XX_I2CONSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_lpc2k_clear_arb(struct lpc2k_i2c *i2c)
|
||||||
|
{
|
||||||
|
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the transfer needs to abort for some reason, we'll try to
|
||||||
|
* force a stop condition to clear any pending bus conditions
|
||||||
|
*/
|
||||||
|
writel(LPC24XX_STO, i2c->base + LPC24XX_I2CONSET);
|
||||||
|
|
||||||
|
/* Wait for status change */
|
||||||
|
while (readl(i2c->base + LPC24XX_I2STAT) != M_I2C_IDLE) {
|
||||||
|
if (time_after(jiffies, timeout)) {
|
||||||
|
/* Bus was not idle, try to reset adapter */
|
||||||
|
i2c_lpc2k_reset(i2c);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_relax();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2c_lpc2k_pump_msg(struct lpc2k_i2c *i2c)
|
||||||
|
{
|
||||||
|
unsigned char data;
|
||||||
|
u32 status;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* I2C in the LPC2xxx series is basically a state machine.
|
||||||
|
* Just run through the steps based on the current status.
|
||||||
|
*/
|
||||||
|
status = readl(i2c->base + LPC24XX_I2STAT);
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case M_START:
|
||||||
|
case M_REPSTART:
|
||||||
|
/* Start bit was just sent out, send out addr and dir */
|
||||||
|
data = i2c->msg->addr << 1;
|
||||||
|
if (i2c->msg->flags & I2C_M_RD)
|
||||||
|
data |= 1;
|
||||||
|
|
||||||
|
writel(data, i2c->base + LPC24XX_I2DAT);
|
||||||
|
writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONCLR);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MX_ADDR_W_ACK:
|
||||||
|
case MX_DATA_W_ACK:
|
||||||
|
/*
|
||||||
|
* Address or data was sent out with an ACK. If there is more
|
||||||
|
* data to send, send it now
|
||||||
|
*/
|
||||||
|
if (i2c->msg_idx < i2c->msg->len) {
|
||||||
|
writel(i2c->msg->buf[i2c->msg_idx],
|
||||||
|
i2c->base + LPC24XX_I2DAT);
|
||||||
|
} else if (i2c->is_last) {
|
||||||
|
/* Last message, send stop */
|
||||||
|
writel(LPC24XX_STO_AA, i2c->base + LPC24XX_I2CONSET);
|
||||||
|
writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR);
|
||||||
|
i2c->msg_status = 0;
|
||||||
|
disable_irq_nosync(i2c->irq);
|
||||||
|
} else {
|
||||||
|
i2c->msg_status = 0;
|
||||||
|
disable_irq_nosync(i2c->irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c->msg_idx++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MR_ADDR_R_ACK:
|
||||||
|
/* Receive first byte from slave */
|
||||||
|
if (i2c->msg->len == 1) {
|
||||||
|
/* Last byte, return NACK */
|
||||||
|
writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONCLR);
|
||||||
|
} else {
|
||||||
|
/* Not last byte, return ACK */
|
||||||
|
writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONCLR);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MR_DATA_R_NACK:
|
||||||
|
/*
|
||||||
|
* The I2C shows NACK status on reads, so we need to accept
|
||||||
|
* the NACK as an ACK here. This should be ok, as the real
|
||||||
|
* BACK would of been caught on the address write.
|
||||||
|
*/
|
||||||
|
case MR_DATA_R_ACK:
|
||||||
|
/* Data was received */
|
||||||
|
if (i2c->msg_idx < i2c->msg->len) {
|
||||||
|
i2c->msg->buf[i2c->msg_idx] =
|
||||||
|
readl(i2c->base + LPC24XX_I2DAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If transfer is done, send STOP */
|
||||||
|
if (i2c->msg_idx >= i2c->msg->len - 1 && i2c->is_last) {
|
||||||
|
writel(LPC24XX_STO_AA, i2c->base + LPC24XX_I2CONSET);
|
||||||
|
writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR);
|
||||||
|
i2c->msg_status = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Message is done */
|
||||||
|
if (i2c->msg_idx >= i2c->msg->len - 1) {
|
||||||
|
i2c->msg_status = 0;
|
||||||
|
disable_irq_nosync(i2c->irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* One pre-last data input, send NACK to tell the slave that
|
||||||
|
* this is going to be the last data byte to be transferred.
|
||||||
|
*/
|
||||||
|
if (i2c->msg_idx >= i2c->msg->len - 2) {
|
||||||
|
/* One byte left to receive - NACK */
|
||||||
|
writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONCLR);
|
||||||
|
} else {
|
||||||
|
/* More than one byte left to receive - ACK */
|
||||||
|
writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONCLR);
|
||||||
|
i2c->msg_idx++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MX_ADDR_W_NACK:
|
||||||
|
case MX_DATA_W_NACK:
|
||||||
|
case MR_ADDR_R_NACK:
|
||||||
|
/* NACK processing is done */
|
||||||
|
writel(LPC24XX_STO_AA, i2c->base + LPC24XX_I2CONSET);
|
||||||
|
i2c->msg_status = -ENXIO;
|
||||||
|
disable_irq_nosync(i2c->irq);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case M_DATA_ARB_LOST:
|
||||||
|
/* Arbitration lost */
|
||||||
|
i2c->msg_status = -EAGAIN;
|
||||||
|
|
||||||
|
/* Release the I2C bus */
|
||||||
|
writel(LPC24XX_STA | LPC24XX_STO, i2c->base + LPC24XX_I2CONCLR);
|
||||||
|
disable_irq_nosync(i2c->irq);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* Unexpected statuses */
|
||||||
|
i2c->msg_status = -EIO;
|
||||||
|
disable_irq_nosync(i2c->irq);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exit on failure or all bytes transferred */
|
||||||
|
if (i2c->msg_status != -EBUSY)
|
||||||
|
wake_up(&i2c->wait);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If `msg_status` is zero, then `lpc2k_process_msg()`
|
||||||
|
* is responsible for clearing the SI flag.
|
||||||
|
*/
|
||||||
|
if (i2c->msg_status != 0)
|
||||||
|
writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lpc2k_process_msg(struct lpc2k_i2c *i2c, int msgidx)
|
||||||
|
{
|
||||||
|
/* A new transfer is kicked off by initiating a start condition */
|
||||||
|
if (!msgidx) {
|
||||||
|
writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONSET);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* A multi-message I2C transfer continues where the
|
||||||
|
* previous I2C transfer left off and uses the
|
||||||
|
* current condition of the I2C adapter.
|
||||||
|
*/
|
||||||
|
if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) {
|
||||||
|
WARN_ON(i2c->msg->len == 0);
|
||||||
|
|
||||||
|
if (!(i2c->msg->flags & I2C_M_RD)) {
|
||||||
|
/* Start transmit of data */
|
||||||
|
writel(i2c->msg->buf[0],
|
||||||
|
i2c->base + LPC24XX_I2DAT);
|
||||||
|
i2c->msg_idx++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Start or repeated start */
|
||||||
|
writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR);
|
||||||
|
}
|
||||||
|
|
||||||
|
enable_irq(i2c->irq);
|
||||||
|
|
||||||
|
/* Wait for transfer completion */
|
||||||
|
if (wait_event_timeout(i2c->wait, i2c->msg_status != -EBUSY,
|
||||||
|
msecs_to_jiffies(1000)) == 0) {
|
||||||
|
disable_irq_nosync(i2c->irq);
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i2c->msg_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_lpc2k_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
|
int msg_num)
|
||||||
|
{
|
||||||
|
struct lpc2k_i2c *i2c = i2c_get_adapdata(adap);
|
||||||
|
int ret, i;
|
||||||
|
u32 stat;
|
||||||
|
|
||||||
|
/* Check for bus idle condition */
|
||||||
|
stat = readl(i2c->base + LPC24XX_I2STAT);
|
||||||
|
if (stat != M_I2C_IDLE) {
|
||||||
|
/* Something is holding the bus, try to clear it */
|
||||||
|
return i2c_lpc2k_clear_arb(i2c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process a single message at a time */
|
||||||
|
for (i = 0; i < msg_num; i++) {
|
||||||
|
/* Save message pointer and current message data index */
|
||||||
|
i2c->msg = &msgs[i];
|
||||||
|
i2c->msg_idx = 0;
|
||||||
|
i2c->msg_status = -EBUSY;
|
||||||
|
i2c->is_last = (i == (msg_num - 1));
|
||||||
|
|
||||||
|
ret = lpc2k_process_msg(i2c, i);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t i2c_lpc2k_handler(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct lpc2k_i2c *i2c = dev_id;
|
||||||
|
|
||||||
|
if (readl(i2c->base + LPC24XX_I2CONSET) & LPC24XX_SI) {
|
||||||
|
i2c_lpc2k_pump_msg(i2c);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 i2c_lpc2k_functionality(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
/* Only emulated SMBus for now */
|
||||||
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_algorithm i2c_lpc2k_algorithm = {
|
||||||
|
.master_xfer = i2c_lpc2k_xfer,
|
||||||
|
.functionality = i2c_lpc2k_functionality,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int i2c_lpc2k_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct lpc2k_i2c *i2c;
|
||||||
|
struct resource *res;
|
||||||
|
u32 bus_clk_rate;
|
||||||
|
u32 scl_high;
|
||||||
|
u32 clkrate;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
|
||||||
|
if (!i2c)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
i2c->base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(i2c->base))
|
||||||
|
return PTR_ERR(i2c->base);
|
||||||
|
|
||||||
|
i2c->irq = platform_get_irq(pdev, 0);
|
||||||
|
if (i2c->irq < 0) {
|
||||||
|
dev_err(&pdev->dev, "can't get interrupt resource\n");
|
||||||
|
return i2c->irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_waitqueue_head(&i2c->wait);
|
||||||
|
|
||||||
|
i2c->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(i2c->clk)) {
|
||||||
|
dev_err(&pdev->dev, "error getting clock\n");
|
||||||
|
return PTR_ERR(i2c->clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(i2c->clk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "unable to enable clock.\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_irq(&pdev->dev, i2c->irq, i2c_lpc2k_handler, 0,
|
||||||
|
dev_name(&pdev->dev), i2c);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "can't request interrupt.\n");
|
||||||
|
goto fail_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
disable_irq_nosync(i2c->irq);
|
||||||
|
|
||||||
|
/* Place controller is a known state */
|
||||||
|
i2c_lpc2k_reset(i2c);
|
||||||
|
|
||||||
|
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
|
||||||
|
&bus_clk_rate);
|
||||||
|
if (ret)
|
||||||
|
bus_clk_rate = 100000; /* 100 kHz default clock rate */
|
||||||
|
|
||||||
|
clkrate = clk_get_rate(i2c->clk);
|
||||||
|
if (clkrate == 0) {
|
||||||
|
dev_err(&pdev->dev, "can't get I2C base clock\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup I2C dividers to generate clock with proper duty cycle */
|
||||||
|
clkrate = clkrate / bus_clk_rate;
|
||||||
|
if (bus_clk_rate <= 100000)
|
||||||
|
scl_high = (clkrate * I2C_STD_MODE_DUTY) / 100;
|
||||||
|
else if (bus_clk_rate <= 400000)
|
||||||
|
scl_high = (clkrate * I2C_FAST_MODE_DUTY) / 100;
|
||||||
|
else
|
||||||
|
scl_high = (clkrate * I2C_FAST_MODE_PLUS_DUTY) / 100;
|
||||||
|
|
||||||
|
writel(scl_high, i2c->base + LPC24XX_I2SCLH);
|
||||||
|
writel(clkrate - scl_high, i2c->base + LPC24XX_I2SCLL);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, i2c);
|
||||||
|
|
||||||
|
i2c_set_adapdata(&i2c->adap, i2c);
|
||||||
|
i2c->adap.owner = THIS_MODULE;
|
||||||
|
strlcpy(i2c->adap.name, "LPC2K I2C adapter", sizeof(i2c->adap.name));
|
||||||
|
i2c->adap.algo = &i2c_lpc2k_algorithm;
|
||||||
|
i2c->adap.dev.parent = &pdev->dev;
|
||||||
|
i2c->adap.dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
|
ret = i2c_add_adapter(&i2c->adap);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "failed to add adapter!\n");
|
||||||
|
goto fail_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(&pdev->dev, "LPC2K I2C adapter\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail_clk:
|
||||||
|
clk_disable_unprepare(i2c->clk);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_lpc2k_remove(struct platform_device *dev)
|
||||||
|
{
|
||||||
|
struct lpc2k_i2c *i2c = platform_get_drvdata(dev);
|
||||||
|
|
||||||
|
i2c_del_adapter(&i2c->adap);
|
||||||
|
clk_disable_unprepare(i2c->clk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int i2c_lpc2k_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
struct lpc2k_i2c *i2c = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
clk_disable(i2c->clk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_lpc2k_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
struct lpc2k_i2c *i2c = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
clk_enable(i2c->clk);
|
||||||
|
i2c_lpc2k_reset(i2c);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops i2c_lpc2k_dev_pm_ops = {
|
||||||
|
.suspend_noirq = i2c_lpc2k_suspend,
|
||||||
|
.resume_noirq = i2c_lpc2k_resume,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define I2C_LPC2K_DEV_PM_OPS (&i2c_lpc2k_dev_pm_ops)
|
||||||
|
#else
|
||||||
|
#define I2C_LPC2K_DEV_PM_OPS NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct of_device_id lpc2k_i2c_match[] = {
|
||||||
|
{ .compatible = "nxp,lpc1788-i2c" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, lpc2k_i2c_match);
|
||||||
|
|
||||||
|
static struct platform_driver i2c_lpc2k_driver = {
|
||||||
|
.probe = i2c_lpc2k_probe,
|
||||||
|
.remove = i2c_lpc2k_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "lpc2k-i2c",
|
||||||
|
.pm = I2C_LPC2K_DEV_PM_OPS,
|
||||||
|
.of_match_table = lpc2k_i2c_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(i2c_lpc2k_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
|
||||||
|
MODULE_DESCRIPTION("I2C driver for LPC2xxx devices");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("platform:lpc2k-i2c");
|
@ -59,6 +59,7 @@
|
|||||||
#define I2C_DMA_START_EN 0x0001
|
#define I2C_DMA_START_EN 0x0001
|
||||||
#define I2C_DMA_INT_FLAG_NONE 0x0000
|
#define I2C_DMA_INT_FLAG_NONE 0x0000
|
||||||
#define I2C_DMA_CLR_FLAG 0x0000
|
#define I2C_DMA_CLR_FLAG 0x0000
|
||||||
|
#define I2C_DMA_HARD_RST 0x0002
|
||||||
|
|
||||||
#define I2C_DEFAULT_SPEED 100000 /* hz */
|
#define I2C_DEFAULT_SPEED 100000 /* hz */
|
||||||
#define MAX_FS_MODE_SPEED 400000
|
#define MAX_FS_MODE_SPEED 400000
|
||||||
@ -81,6 +82,7 @@ enum DMA_REGS_OFFSET {
|
|||||||
OFFSET_INT_FLAG = 0x0,
|
OFFSET_INT_FLAG = 0x0,
|
||||||
OFFSET_INT_EN = 0x04,
|
OFFSET_INT_EN = 0x04,
|
||||||
OFFSET_EN = 0x08,
|
OFFSET_EN = 0x08,
|
||||||
|
OFFSET_RST = 0x0c,
|
||||||
OFFSET_CON = 0x18,
|
OFFSET_CON = 0x18,
|
||||||
OFFSET_TX_MEM_ADDR = 0x1c,
|
OFFSET_TX_MEM_ADDR = 0x1c,
|
||||||
OFFSET_RX_MEM_ADDR = 0x20,
|
OFFSET_RX_MEM_ADDR = 0x20,
|
||||||
@ -262,6 +264,10 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c)
|
|||||||
I2C_CONTROL_CLK_EXT_EN | I2C_CONTROL_DMA_EN;
|
I2C_CONTROL_CLK_EXT_EN | I2C_CONTROL_DMA_EN;
|
||||||
writew(control_reg, i2c->base + OFFSET_CONTROL);
|
writew(control_reg, i2c->base + OFFSET_CONTROL);
|
||||||
writew(I2C_DELAY_LEN, i2c->base + OFFSET_DELAY_LEN);
|
writew(I2C_DELAY_LEN, i2c->base + OFFSET_DELAY_LEN);
|
||||||
|
|
||||||
|
writel(I2C_DMA_HARD_RST, i2c->pdmabase + OFFSET_RST);
|
||||||
|
udelay(50);
|
||||||
|
writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -551,15 +557,22 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
|
|||||||
{
|
{
|
||||||
struct mtk_i2c *i2c = dev_id;
|
struct mtk_i2c *i2c = dev_id;
|
||||||
u16 restart_flag = 0;
|
u16 restart_flag = 0;
|
||||||
|
u16 intr_stat;
|
||||||
|
|
||||||
if (i2c->dev_comp->auto_restart)
|
if (i2c->dev_comp->auto_restart)
|
||||||
restart_flag = I2C_RS_TRANSFER;
|
restart_flag = I2C_RS_TRANSFER;
|
||||||
|
|
||||||
i2c->irq_stat = readw(i2c->base + OFFSET_INTR_STAT);
|
intr_stat = readw(i2c->base + OFFSET_INTR_STAT);
|
||||||
writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR
|
writew(intr_stat, i2c->base + OFFSET_INTR_STAT);
|
||||||
| I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_STAT);
|
|
||||||
|
|
||||||
complete(&i2c->msg_complete);
|
/*
|
||||||
|
* when occurs ack error, i2c controller generate two interrupts
|
||||||
|
* first is the ack error interrupt, then the complete interrupt
|
||||||
|
* i2c->irq_stat need keep the two interrupt value.
|
||||||
|
*/
|
||||||
|
i2c->irq_stat |= intr_stat;
|
||||||
|
if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag))
|
||||||
|
complete(&i2c->msg_complete);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,8 @@
|
|||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
* ------------------------------------------------------------------------ */
|
* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "i2c-parport: " fmt
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
@ -176,26 +178,24 @@ static void i2c_parport_attach(struct parport *port)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (i == MAX_DEVICE) {
|
if (i == MAX_DEVICE) {
|
||||||
pr_debug("i2c-parport: Not using parport%d.\n", port->number);
|
pr_debug("Not using parport%d.\n", port->number);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
adapter = kzalloc(sizeof(struct i2c_par), GFP_KERNEL);
|
adapter = kzalloc(sizeof(struct i2c_par), GFP_KERNEL);
|
||||||
if (adapter == NULL) {
|
if (!adapter)
|
||||||
printk(KERN_ERR "i2c-parport: Failed to kzalloc\n");
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
memset(&i2c_parport_cb, 0, sizeof(i2c_parport_cb));
|
memset(&i2c_parport_cb, 0, sizeof(i2c_parport_cb));
|
||||||
i2c_parport_cb.flags = PARPORT_FLAG_EXCL;
|
i2c_parport_cb.flags = PARPORT_FLAG_EXCL;
|
||||||
i2c_parport_cb.irq_func = i2c_parport_irq;
|
i2c_parport_cb.irq_func = i2c_parport_irq;
|
||||||
i2c_parport_cb.private = adapter;
|
i2c_parport_cb.private = adapter;
|
||||||
|
|
||||||
pr_debug("i2c-parport: attaching to %s\n", port->name);
|
pr_debug("attaching to %s\n", port->name);
|
||||||
parport_disable_irq(port);
|
parport_disable_irq(port);
|
||||||
adapter->pdev = parport_register_dev_model(port, "i2c-parport",
|
adapter->pdev = parport_register_dev_model(port, "i2c-parport",
|
||||||
&i2c_parport_cb, i);
|
&i2c_parport_cb, i);
|
||||||
if (!adapter->pdev) {
|
if (!adapter->pdev) {
|
||||||
printk(KERN_ERR "i2c-parport: Unable to register with parport\n");
|
pr_err("Unable to register with parport\n");
|
||||||
goto err_free;
|
goto err_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +215,8 @@ static void i2c_parport_attach(struct parport *port)
|
|||||||
adapter->adapter.dev.parent = port->physport->dev;
|
adapter->adapter.dev.parent = port->physport->dev;
|
||||||
|
|
||||||
if (parport_claim_or_block(adapter->pdev) < 0) {
|
if (parport_claim_or_block(adapter->pdev) < 0) {
|
||||||
printk(KERN_ERR "i2c-parport: Could not claim parallel port\n");
|
dev_err(&adapter->pdev->dev,
|
||||||
|
"Could not claim parallel port\n");
|
||||||
goto err_unregister;
|
goto err_unregister;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +231,7 @@ static void i2c_parport_attach(struct parport *port)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (i2c_bit_add_bus(&adapter->adapter) < 0) {
|
if (i2c_bit_add_bus(&adapter->adapter) < 0) {
|
||||||
printk(KERN_ERR "i2c-parport: Unable to register with I2C\n");
|
dev_err(&adapter->pdev->dev, "Unable to register with I2C\n");
|
||||||
goto err_unregister;
|
goto err_unregister;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,8 +243,8 @@ static void i2c_parport_attach(struct parport *port)
|
|||||||
if (adapter->ara)
|
if (adapter->ara)
|
||||||
parport_enable_irq(port);
|
parport_enable_irq(port);
|
||||||
else
|
else
|
||||||
printk(KERN_WARNING "i2c-parport: Failed to register "
|
dev_warn(&adapter->pdev->dev,
|
||||||
"ARA client\n");
|
"Failed to register ARA client\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add the new adapter to the list */
|
/* Add the new adapter to the list */
|
||||||
@ -298,12 +299,12 @@ static struct parport_driver i2c_parport_driver = {
|
|||||||
static int __init i2c_parport_init(void)
|
static int __init i2c_parport_init(void)
|
||||||
{
|
{
|
||||||
if (type < 0) {
|
if (type < 0) {
|
||||||
printk(KERN_WARNING "i2c-parport: adapter type unspecified\n");
|
pr_warn("adapter type unspecified\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type >= ARRAY_SIZE(adapter_parm)) {
|
if (type >= ARRAY_SIZE(adapter_parm)) {
|
||||||
printk(KERN_WARNING "i2c-parport: invalid type (%d)\n", type);
|
pr_warn("invalid type (%d)\n", type);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +89,13 @@ static const struct adapter_parm adapter_parm[] = {
|
|||||||
.getsda = { 0x80, PORT_STAT, 1 },
|
.getsda = { 0x80, PORT_STAT, 1 },
|
||||||
.init = { 0x04, PORT_DATA, 1 },
|
.init = { 0x04, PORT_DATA, 1 },
|
||||||
},
|
},
|
||||||
|
/* type 8: VCT-jig */
|
||||||
|
{
|
||||||
|
.setsda = { 0x04, PORT_DATA, 1 },
|
||||||
|
.setscl = { 0x01, PORT_DATA, 1 },
|
||||||
|
.getsda = { 0x40, PORT_STAT, 0 },
|
||||||
|
.getscl = { 0x80, PORT_STAT, 1 },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int type = -1;
|
static int type = -1;
|
||||||
@ -103,4 +110,5 @@ MODULE_PARM_DESC(type,
|
|||||||
" 5 = ADM1025, ADM1030 and ADM1031 evaluation boards\n"
|
" 5 = ADM1025, ADM1030 and ADM1031 evaluation boards\n"
|
||||||
" 6 = Barco LPT->DVI (K5800236) adapter\n"
|
" 6 = Barco LPT->DVI (K5800236) adapter\n"
|
||||||
" 7 = One For All JP1 parallel port adapter\n"
|
" 7 = One For All JP1 parallel port adapter\n"
|
||||||
|
" 8 = VCT-jig\n"
|
||||||
);
|
);
|
||||||
|
@ -132,6 +132,7 @@ struct pxa_i2c {
|
|||||||
unsigned int msg_idx;
|
unsigned int msg_idx;
|
||||||
unsigned int msg_ptr;
|
unsigned int msg_ptr;
|
||||||
unsigned int slave_addr;
|
unsigned int slave_addr;
|
||||||
|
unsigned int req_slave_addr;
|
||||||
|
|
||||||
struct i2c_adapter adap;
|
struct i2c_adapter adap;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
@ -253,15 +254,20 @@ static void i2c_pxa_show_state(struct pxa_i2c *i2c, int lno, const char *fname)
|
|||||||
static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why)
|
static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
printk(KERN_ERR "i2c: error: %s\n", why);
|
struct device *dev = &i2c->adap.dev;
|
||||||
printk(KERN_ERR "i2c: msg_num: %d msg_idx: %d msg_ptr: %d\n",
|
|
||||||
|
dev_err(dev, "slave_0x%x error: %s\n",
|
||||||
|
i2c->req_slave_addr >> 1, why);
|
||||||
|
dev_err(dev, "msg_num: %d msg_idx: %d msg_ptr: %d\n",
|
||||||
i2c->msg_num, i2c->msg_idx, i2c->msg_ptr);
|
i2c->msg_num, i2c->msg_idx, i2c->msg_ptr);
|
||||||
printk(KERN_ERR "i2c: ICR: %08x ISR: %08x\n",
|
dev_err(dev, "IBMR: %08x IDBR: %08x ICR: %08x ISR: %08x\n",
|
||||||
readl(_ICR(i2c)), readl(_ISR(i2c)));
|
readl(_IBMR(i2c)), readl(_IDBR(i2c)), readl(_ICR(i2c)),
|
||||||
printk(KERN_DEBUG "i2c: log: ");
|
readl(_ISR(i2c)));
|
||||||
|
dev_dbg(dev, "log: ");
|
||||||
for (i = 0; i < i2c->irqlogidx; i++)
|
for (i = 0; i < i2c->irqlogidx; i++)
|
||||||
printk("[%08x:%08x] ", i2c->isrlog[i], i2c->icrlog[i]);
|
pr_debug("[%08x:%08x] ", i2c->isrlog[i], i2c->icrlog[i]);
|
||||||
printk("\n");
|
|
||||||
|
pr_debug("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /* ifdef DEBUG */
|
#else /* ifdef DEBUG */
|
||||||
@ -459,7 +465,7 @@ static void i2c_pxa_reset(struct pxa_i2c *i2c)
|
|||||||
writel(I2C_ISR_INIT, _ISR(i2c));
|
writel(I2C_ISR_INIT, _ISR(i2c));
|
||||||
writel(readl(_ICR(i2c)) & ~ICR_UR, _ICR(i2c));
|
writel(readl(_ICR(i2c)) & ~ICR_UR, _ICR(i2c));
|
||||||
|
|
||||||
if (i2c->reg_isar)
|
if (i2c->reg_isar && IS_ENABLED(CONFIG_I2C_PXA_SLAVE))
|
||||||
writel(i2c->slave_addr, _ISAR(i2c));
|
writel(i2c->slave_addr, _ISAR(i2c));
|
||||||
|
|
||||||
/* set control register values */
|
/* set control register values */
|
||||||
@ -638,6 +644,7 @@ static inline void i2c_pxa_start_message(struct pxa_i2c *i2c)
|
|||||||
* Step 1: target slave address into IDBR
|
* Step 1: target slave address into IDBR
|
||||||
*/
|
*/
|
||||||
writel(i2c_pxa_addr_byte(i2c->msg), _IDBR(i2c));
|
writel(i2c_pxa_addr_byte(i2c->msg), _IDBR(i2c));
|
||||||
|
i2c->req_slave_addr = i2c_pxa_addr_byte(i2c->msg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Step 2: initiate the write.
|
* Step 2: initiate the write.
|
||||||
@ -745,8 +752,10 @@ static int i2c_pxa_do_pio_xfer(struct pxa_i2c *i2c,
|
|||||||
ret = i2c->msg_idx;
|
ret = i2c->msg_idx;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (timeout == 0)
|
if (timeout == 0) {
|
||||||
i2c_pxa_scream_blue_murder(i2c, "timeout");
|
i2c_pxa_scream_blue_murder(i2c, "timeout");
|
||||||
|
ret = I2C_RETRY;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -949,6 +958,7 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr)
|
|||||||
* Write the next address.
|
* Write the next address.
|
||||||
*/
|
*/
|
||||||
writel(i2c_pxa_addr_byte(i2c->msg), _IDBR(i2c));
|
writel(i2c_pxa_addr_byte(i2c->msg), _IDBR(i2c));
|
||||||
|
i2c->req_slave_addr = i2c_pxa_addr_byte(i2c->msg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* And trigger a repeated start, and send the byte.
|
* And trigger a repeated start, and send the byte.
|
||||||
@ -1114,7 +1124,9 @@ static int i2c_pxa_probe_dt(struct platform_device *pdev, struct pxa_i2c *i2c,
|
|||||||
i2c->use_pio = 1;
|
i2c->use_pio = 1;
|
||||||
if (of_get_property(np, "mrvl,i2c-fast-mode", NULL))
|
if (of_get_property(np, "mrvl,i2c-fast-mode", NULL))
|
||||||
i2c->fast_mode = 1;
|
i2c->fast_mode = 1;
|
||||||
*i2c_types = (u32)(of_id->data);
|
|
||||||
|
*i2c_types = (enum pxa_i2c_types)(of_id->data);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1146,10 +1158,19 @@ static int i2c_pxa_probe(struct platform_device *dev)
|
|||||||
struct resource *res = NULL;
|
struct resource *res = NULL;
|
||||||
int ret, irq;
|
int ret, irq;
|
||||||
|
|
||||||
i2c = kzalloc(sizeof(struct pxa_i2c), GFP_KERNEL);
|
i2c = devm_kzalloc(&dev->dev, sizeof(struct pxa_i2c), GFP_KERNEL);
|
||||||
if (!i2c) {
|
if (!i2c)
|
||||||
ret = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto emalloc;
|
|
||||||
|
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||||
|
i2c->reg_base = devm_ioremap_resource(&dev->dev, res);
|
||||||
|
if (IS_ERR(i2c->reg_base))
|
||||||
|
return PTR_ERR(i2c->reg_base);
|
||||||
|
|
||||||
|
irq = platform_get_irq(dev, 0);
|
||||||
|
if (irq < 0) {
|
||||||
|
dev_err(&dev->dev, "no irq resource: %d\n", irq);
|
||||||
|
return irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Default adapter num to device id; i2c_pxa_probe_dt can override. */
|
/* Default adapter num to device id; i2c_pxa_probe_dt can override. */
|
||||||
@ -1159,19 +1180,7 @@ static int i2c_pxa_probe(struct platform_device *dev)
|
|||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
ret = i2c_pxa_probe_pdata(dev, i2c, &i2c_type);
|
ret = i2c_pxa_probe_pdata(dev, i2c, &i2c_type);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto eclk;
|
return ret;
|
||||||
|
|
||||||
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
|
||||||
irq = platform_get_irq(dev, 0);
|
|
||||||
if (res == NULL || irq < 0) {
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto eclk;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!request_mem_region(res->start, resource_size(res), res->name)) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto eclk;
|
|
||||||
}
|
|
||||||
|
|
||||||
i2c->adap.owner = THIS_MODULE;
|
i2c->adap.owner = THIS_MODULE;
|
||||||
i2c->adap.retries = 5;
|
i2c->adap.retries = 5;
|
||||||
@ -1181,16 +1190,10 @@ static int i2c_pxa_probe(struct platform_device *dev)
|
|||||||
|
|
||||||
strlcpy(i2c->adap.name, "pxa_i2c-i2c", sizeof(i2c->adap.name));
|
strlcpy(i2c->adap.name, "pxa_i2c-i2c", sizeof(i2c->adap.name));
|
||||||
|
|
||||||
i2c->clk = clk_get(&dev->dev, NULL);
|
i2c->clk = devm_clk_get(&dev->dev, NULL);
|
||||||
if (IS_ERR(i2c->clk)) {
|
if (IS_ERR(i2c->clk)) {
|
||||||
ret = PTR_ERR(i2c->clk);
|
dev_err(&dev->dev, "failed to get the clk: %ld\n", PTR_ERR(i2c->clk));
|
||||||
goto eclk;
|
return PTR_ERR(i2c->clk);
|
||||||
}
|
|
||||||
|
|
||||||
i2c->reg_base = ioremap(res->start, resource_size(res));
|
|
||||||
if (!i2c->reg_base) {
|
|
||||||
ret = -EIO;
|
|
||||||
goto eremap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c->reg_ibmr = i2c->reg_base + pxa_reg_layout[i2c_type].ibmr;
|
i2c->reg_ibmr = i2c->reg_base + pxa_reg_layout[i2c_type].ibmr;
|
||||||
@ -1232,10 +1235,13 @@ static int i2c_pxa_probe(struct platform_device *dev)
|
|||||||
i2c->adap.algo = &i2c_pxa_pio_algorithm;
|
i2c->adap.algo = &i2c_pxa_pio_algorithm;
|
||||||
} else {
|
} else {
|
||||||
i2c->adap.algo = &i2c_pxa_algorithm;
|
i2c->adap.algo = &i2c_pxa_algorithm;
|
||||||
ret = request_irq(irq, i2c_pxa_handler, IRQF_SHARED,
|
ret = devm_request_irq(&dev->dev, irq, i2c_pxa_handler,
|
||||||
dev_name(&dev->dev), i2c);
|
IRQF_SHARED | IRQF_NO_SUSPEND,
|
||||||
if (ret)
|
dev_name(&dev->dev), i2c);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&dev->dev, "failed to request irq: %d\n", ret);
|
||||||
goto ereqirq;
|
goto ereqirq;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c_pxa_reset(i2c);
|
i2c_pxa_reset(i2c);
|
||||||
@ -1248,33 +1254,22 @@ static int i2c_pxa_probe(struct platform_device *dev)
|
|||||||
|
|
||||||
ret = i2c_add_numbered_adapter(&i2c->adap);
|
ret = i2c_add_numbered_adapter(&i2c->adap);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
printk(KERN_INFO "I2C: Failed to add bus\n");
|
dev_err(&dev->dev, "failed to add bus: %d\n", ret);
|
||||||
goto eadapt;
|
goto ereqirq;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(dev, i2c);
|
platform_set_drvdata(dev, i2c);
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_PXA_SLAVE
|
#ifdef CONFIG_I2C_PXA_SLAVE
|
||||||
printk(KERN_INFO "I2C: %s: PXA I2C adapter, slave address %d\n",
|
dev_info(&i2c->adap.dev, " PXA I2C adapter, slave address %d\n",
|
||||||
dev_name(&i2c->adap.dev), i2c->slave_addr);
|
i2c->slave_addr);
|
||||||
#else
|
#else
|
||||||
printk(KERN_INFO "I2C: %s: PXA I2C adapter\n",
|
dev_info(&i2c->adap.dev, " PXA I2C adapter\n");
|
||||||
dev_name(&i2c->adap.dev));
|
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
eadapt:
|
|
||||||
if (!i2c->use_pio)
|
|
||||||
free_irq(irq, i2c);
|
|
||||||
ereqirq:
|
ereqirq:
|
||||||
clk_disable_unprepare(i2c->clk);
|
clk_disable_unprepare(i2c->clk);
|
||||||
iounmap(i2c->reg_base);
|
|
||||||
eremap:
|
|
||||||
clk_put(i2c->clk);
|
|
||||||
eclk:
|
|
||||||
kfree(i2c);
|
|
||||||
emalloc:
|
|
||||||
release_mem_region(res->start, resource_size(res));
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1283,15 +1278,8 @@ static int i2c_pxa_remove(struct platform_device *dev)
|
|||||||
struct pxa_i2c *i2c = platform_get_drvdata(dev);
|
struct pxa_i2c *i2c = platform_get_drvdata(dev);
|
||||||
|
|
||||||
i2c_del_adapter(&i2c->adap);
|
i2c_del_adapter(&i2c->adap);
|
||||||
if (!i2c->use_pio)
|
|
||||||
free_irq(i2c->irq, i2c);
|
|
||||||
|
|
||||||
clk_disable_unprepare(i2c->clk);
|
clk_disable_unprepare(i2c->clk);
|
||||||
clk_put(i2c->clk);
|
|
||||||
|
|
||||||
iounmap(i2c->reg_base);
|
|
||||||
release_mem_region(i2c->iobase, i2c->iosize);
|
|
||||||
kfree(i2c);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -100,6 +100,12 @@
|
|||||||
#define I2C_HEADER_CONTINUE_XFER (1<<15)
|
#define I2C_HEADER_CONTINUE_XFER (1<<15)
|
||||||
#define I2C_HEADER_MASTER_ADDR_SHIFT 12
|
#define I2C_HEADER_MASTER_ADDR_SHIFT 12
|
||||||
#define I2C_HEADER_SLAVE_ADDR_SHIFT 1
|
#define I2C_HEADER_SLAVE_ADDR_SHIFT 1
|
||||||
|
|
||||||
|
#define I2C_CONFIG_LOAD 0x08C
|
||||||
|
#define I2C_MSTR_CONFIG_LOAD (1 << 0)
|
||||||
|
#define I2C_SLV_CONFIG_LOAD (1 << 1)
|
||||||
|
#define I2C_TIMEOUT_CONFIG_LOAD (1 << 2)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* msg_end_type: The bus control which need to be send at end of transfer.
|
* msg_end_type: The bus control which need to be send at end of transfer.
|
||||||
* @MSG_END_STOP: Send stop pulse at end of transfer.
|
* @MSG_END_STOP: Send stop pulse at end of transfer.
|
||||||
@ -121,6 +127,8 @@ enum msg_end_type {
|
|||||||
* @has_single_clk_source: The i2c controller has single clock source. Tegra30
|
* @has_single_clk_source: The i2c controller has single clock source. Tegra30
|
||||||
* and earlier Socs has two clock sources i.e. div-clk and
|
* and earlier Socs has two clock sources i.e. div-clk and
|
||||||
* fast-clk.
|
* fast-clk.
|
||||||
|
* @has_config_load_reg: Has the config load register to load the new
|
||||||
|
* configuration.
|
||||||
* @clk_divisor_hs_mode: Clock divisor in HS mode.
|
* @clk_divisor_hs_mode: Clock divisor in HS mode.
|
||||||
* @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is
|
* @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is
|
||||||
* applicable if there is no fast clock source i.e. single clock
|
* applicable if there is no fast clock source i.e. single clock
|
||||||
@ -131,8 +139,10 @@ struct tegra_i2c_hw_feature {
|
|||||||
bool has_continue_xfer_support;
|
bool has_continue_xfer_support;
|
||||||
bool has_per_pkt_xfer_complete_irq;
|
bool has_per_pkt_xfer_complete_irq;
|
||||||
bool has_single_clk_source;
|
bool has_single_clk_source;
|
||||||
|
bool has_config_load_reg;
|
||||||
int clk_divisor_hs_mode;
|
int clk_divisor_hs_mode;
|
||||||
int clk_divisor_std_fast_mode;
|
int clk_divisor_std_fast_mode;
|
||||||
|
u16 clk_divisor_fast_plus_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -172,6 +182,7 @@ struct tegra_i2c_dev {
|
|||||||
size_t msg_buf_remaining;
|
size_t msg_buf_remaining;
|
||||||
int msg_read;
|
int msg_read;
|
||||||
u32 bus_clk_rate;
|
u32 bus_clk_rate;
|
||||||
|
u16 clk_divisor_non_hs_mode;
|
||||||
bool is_suspended;
|
bool is_suspended;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -410,6 +421,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
|||||||
u32 val;
|
u32 val;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
u32 clk_divisor;
|
u32 clk_divisor;
|
||||||
|
unsigned long timeout = jiffies + HZ;
|
||||||
|
|
||||||
err = tegra_i2c_clock_enable(i2c_dev);
|
err = tegra_i2c_clock_enable(i2c_dev);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
@ -431,7 +443,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
|||||||
|
|
||||||
/* Make sure clock divisor programmed correctly */
|
/* Make sure clock divisor programmed correctly */
|
||||||
clk_divisor = i2c_dev->hw->clk_divisor_hs_mode;
|
clk_divisor = i2c_dev->hw->clk_divisor_hs_mode;
|
||||||
clk_divisor |= i2c_dev->hw->clk_divisor_std_fast_mode <<
|
clk_divisor |= i2c_dev->clk_divisor_non_hs_mode <<
|
||||||
I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT;
|
I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT;
|
||||||
i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR);
|
i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR);
|
||||||
|
|
||||||
@ -451,6 +463,18 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
|||||||
if (tegra_i2c_flush_fifos(i2c_dev))
|
if (tegra_i2c_flush_fifos(i2c_dev))
|
||||||
err = -ETIMEDOUT;
|
err = -ETIMEDOUT;
|
||||||
|
|
||||||
|
if (i2c_dev->hw->has_config_load_reg) {
|
||||||
|
i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD, I2C_CONFIG_LOAD);
|
||||||
|
while (i2c_readl(i2c_dev, I2C_CONFIG_LOAD) != 0) {
|
||||||
|
if (time_after(jiffies, timeout)) {
|
||||||
|
dev_warn(i2c_dev->dev,
|
||||||
|
"timeout waiting for config load\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
msleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tegra_i2c_clock_disable(i2c_dev);
|
tegra_i2c_clock_disable(i2c_dev);
|
||||||
|
|
||||||
if (i2c_dev->irq_disabled) {
|
if (i2c_dev->irq_disabled) {
|
||||||
@ -681,6 +705,8 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
|
|||||||
.has_single_clk_source = false,
|
.has_single_clk_source = false,
|
||||||
.clk_divisor_hs_mode = 3,
|
.clk_divisor_hs_mode = 3,
|
||||||
.clk_divisor_std_fast_mode = 0,
|
.clk_divisor_std_fast_mode = 0,
|
||||||
|
.clk_divisor_fast_plus_mode = 0,
|
||||||
|
.has_config_load_reg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
|
static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
|
||||||
@ -689,6 +715,8 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
|
|||||||
.has_single_clk_source = false,
|
.has_single_clk_source = false,
|
||||||
.clk_divisor_hs_mode = 3,
|
.clk_divisor_hs_mode = 3,
|
||||||
.clk_divisor_std_fast_mode = 0,
|
.clk_divisor_std_fast_mode = 0,
|
||||||
|
.clk_divisor_fast_plus_mode = 0,
|
||||||
|
.has_config_load_reg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
|
static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
|
||||||
@ -697,10 +725,23 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
|
|||||||
.has_single_clk_source = true,
|
.has_single_clk_source = true,
|
||||||
.clk_divisor_hs_mode = 1,
|
.clk_divisor_hs_mode = 1,
|
||||||
.clk_divisor_std_fast_mode = 0x19,
|
.clk_divisor_std_fast_mode = 0x19,
|
||||||
|
.clk_divisor_fast_plus_mode = 0x10,
|
||||||
|
.has_config_load_reg = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
|
||||||
|
.has_continue_xfer_support = true,
|
||||||
|
.has_per_pkt_xfer_complete_irq = true,
|
||||||
|
.has_single_clk_source = true,
|
||||||
|
.clk_divisor_hs_mode = 1,
|
||||||
|
.clk_divisor_std_fast_mode = 0x19,
|
||||||
|
.clk_divisor_fast_plus_mode = 0x10,
|
||||||
|
.has_config_load_reg = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Match table for of_platform binding */
|
/* Match table for of_platform binding */
|
||||||
static const struct of_device_id tegra_i2c_of_match[] = {
|
static const struct of_device_id tegra_i2c_of_match[] = {
|
||||||
|
{ .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, },
|
||||||
{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
|
{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
|
||||||
{ .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, },
|
{ .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, },
|
||||||
{ .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_hw, },
|
{ .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_hw, },
|
||||||
@ -793,7 +834,14 @@ static int tegra_i2c_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_multiplier *= (i2c_dev->hw->clk_divisor_std_fast_mode + 1);
|
i2c_dev->clk_divisor_non_hs_mode =
|
||||||
|
i2c_dev->hw->clk_divisor_std_fast_mode;
|
||||||
|
if (i2c_dev->hw->clk_divisor_fast_plus_mode &&
|
||||||
|
(i2c_dev->bus_clk_rate == 1000000))
|
||||||
|
i2c_dev->clk_divisor_non_hs_mode =
|
||||||
|
i2c_dev->hw->clk_divisor_fast_plus_mode;
|
||||||
|
|
||||||
|
clk_multiplier *= (i2c_dev->clk_divisor_non_hs_mode + 1);
|
||||||
ret = clk_set_rate(i2c_dev->div_clk,
|
ret = clk_set_rate(i2c_dev->div_clk,
|
||||||
i2c_dev->bus_clk_rate * clk_multiplier);
|
i2c_dev->bus_clk_rate * clk_multiplier);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -391,11 +391,11 @@ static int vprbrd_i2c_probe(struct platform_device *pdev)
|
|||||||
VPRBRD_USB_REQUEST_I2C_FREQ, VPRBRD_USB_TYPE_OUT,
|
VPRBRD_USB_REQUEST_I2C_FREQ, VPRBRD_USB_TYPE_OUT,
|
||||||
0x0000, 0x0000, &vb_i2c->bus_freq_param, 1,
|
0x0000, 0x0000, &vb_i2c->bus_freq_param, 1,
|
||||||
VPRBRD_USB_TIMEOUT_MS);
|
VPRBRD_USB_TIMEOUT_MS);
|
||||||
if (ret != 1) {
|
if (ret != 1) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev, "failure setting i2c_bus_freq to %d\n",
|
||||||
"failure setting i2c_bus_freq to %d\n", i2c_bus_freq);
|
i2c_bus_freq);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"invalid i2c_bus_freq setting:%d\n", i2c_bus_freq);
|
"invalid i2c_bus_freq setting:%d\n", i2c_bus_freq);
|
||||||
|
@ -198,10 +198,10 @@ static int slimpro_i2c_blkrd(struct slimpro_i2c_dev *ctx, u32 chip, u32 addr,
|
|||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
paddr = dma_map_single(ctx->dev, ctx->dma_buffer, readlen, DMA_FROM_DEVICE);
|
paddr = dma_map_single(ctx->dev, ctx->dma_buffer, readlen, DMA_FROM_DEVICE);
|
||||||
rc = dma_mapping_error(ctx->dev, paddr);
|
if (dma_mapping_error(ctx->dev, paddr)) {
|
||||||
if (rc) {
|
|
||||||
dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n",
|
dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n",
|
||||||
ctx->dma_buffer);
|
ctx->dma_buffer);
|
||||||
|
rc = -ENOMEM;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,10 +241,10 @@ static int slimpro_i2c_blkwr(struct slimpro_i2c_dev *ctx, u32 chip,
|
|||||||
memcpy(ctx->dma_buffer, data, writelen);
|
memcpy(ctx->dma_buffer, data, writelen);
|
||||||
paddr = dma_map_single(ctx->dev, ctx->dma_buffer, writelen,
|
paddr = dma_map_single(ctx->dev, ctx->dma_buffer, writelen,
|
||||||
DMA_TO_DEVICE);
|
DMA_TO_DEVICE);
|
||||||
rc = dma_mapping_error(ctx->dev, paddr);
|
if (dma_mapping_error(ctx->dev, paddr)) {
|
||||||
if (rc) {
|
|
||||||
dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n",
|
dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n",
|
||||||
ctx->dma_buffer);
|
ctx->dma_buffer);
|
||||||
|
rc = -ENOMEM;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +283,7 @@ static void xiic_reinit(struct xiic_i2c *i2c)
|
|||||||
/* Enable interrupts */
|
/* Enable interrupts */
|
||||||
xiic_setreg32(i2c, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK);
|
xiic_setreg32(i2c, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK);
|
||||||
|
|
||||||
xiic_irq_clr_en(i2c, XIIC_INTR_AAS_MASK | XIIC_INTR_ARB_LOST_MASK);
|
xiic_irq_clr_en(i2c, XIIC_INTR_ARB_LOST_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xiic_deinit(struct xiic_i2c *i2c)
|
static void xiic_deinit(struct xiic_i2c *i2c)
|
||||||
@ -358,8 +358,9 @@ static void xiic_wakeup(struct xiic_i2c *i2c, int code)
|
|||||||
wake_up(&i2c->wait);
|
wake_up(&i2c->wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xiic_process(struct xiic_i2c *i2c)
|
static irqreturn_t xiic_process(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
|
struct xiic_i2c *i2c = dev_id;
|
||||||
u32 pend, isr, ier;
|
u32 pend, isr, ier;
|
||||||
u32 clr = 0;
|
u32 clr = 0;
|
||||||
|
|
||||||
@ -368,6 +369,7 @@ static void xiic_process(struct xiic_i2c *i2c)
|
|||||||
* To find which interrupts are pending; AND interrupts pending with
|
* To find which interrupts are pending; AND interrupts pending with
|
||||||
* interrupts masked.
|
* interrupts masked.
|
||||||
*/
|
*/
|
||||||
|
spin_lock(&i2c->lock);
|
||||||
isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
|
isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
|
||||||
ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET);
|
ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET);
|
||||||
pend = isr & ier;
|
pend = isr & ier;
|
||||||
@ -378,11 +380,6 @@ static void xiic_process(struct xiic_i2c *i2c)
|
|||||||
__func__, xiic_getreg8(i2c, XIIC_SR_REG_OFFSET),
|
__func__, xiic_getreg8(i2c, XIIC_SR_REG_OFFSET),
|
||||||
i2c->tx_msg, i2c->nmsgs);
|
i2c->tx_msg, i2c->nmsgs);
|
||||||
|
|
||||||
/* Do not processes a devices interrupts if the device has no
|
|
||||||
* interrupts pending
|
|
||||||
*/
|
|
||||||
if (!pend)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Service requesting interrupt */
|
/* Service requesting interrupt */
|
||||||
if ((pend & XIIC_INTR_ARB_LOST_MASK) ||
|
if ((pend & XIIC_INTR_ARB_LOST_MASK) ||
|
||||||
@ -402,13 +399,15 @@ static void xiic_process(struct xiic_i2c *i2c)
|
|||||||
*/
|
*/
|
||||||
xiic_reinit(i2c);
|
xiic_reinit(i2c);
|
||||||
|
|
||||||
|
if (i2c->rx_msg)
|
||||||
|
xiic_wakeup(i2c, STATE_ERROR);
|
||||||
if (i2c->tx_msg)
|
if (i2c->tx_msg)
|
||||||
xiic_wakeup(i2c, STATE_ERROR);
|
xiic_wakeup(i2c, STATE_ERROR);
|
||||||
|
}
|
||||||
} else if (pend & XIIC_INTR_RX_FULL_MASK) {
|
if (pend & XIIC_INTR_RX_FULL_MASK) {
|
||||||
/* Receive register/FIFO is full */
|
/* Receive register/FIFO is full */
|
||||||
|
|
||||||
clr = XIIC_INTR_RX_FULL_MASK;
|
clr |= XIIC_INTR_RX_FULL_MASK;
|
||||||
if (!i2c->rx_msg) {
|
if (!i2c->rx_msg) {
|
||||||
dev_dbg(i2c->adap.dev.parent,
|
dev_dbg(i2c->adap.dev.parent,
|
||||||
"%s unexpexted RX IRQ\n", __func__);
|
"%s unexpexted RX IRQ\n", __func__);
|
||||||
@ -441,9 +440,10 @@ static void xiic_process(struct xiic_i2c *i2c)
|
|||||||
__xiic_start_xfer(i2c);
|
__xiic_start_xfer(i2c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (pend & XIIC_INTR_BNB_MASK) {
|
}
|
||||||
|
if (pend & XIIC_INTR_BNB_MASK) {
|
||||||
/* IIC bus has transitioned to not busy */
|
/* IIC bus has transitioned to not busy */
|
||||||
clr = XIIC_INTR_BNB_MASK;
|
clr |= XIIC_INTR_BNB_MASK;
|
||||||
|
|
||||||
/* The bus is not busy, disable BusNotBusy interrupt */
|
/* The bus is not busy, disable BusNotBusy interrupt */
|
||||||
xiic_irq_dis(i2c, XIIC_INTR_BNB_MASK);
|
xiic_irq_dis(i2c, XIIC_INTR_BNB_MASK);
|
||||||
@ -456,12 +456,12 @@ static void xiic_process(struct xiic_i2c *i2c)
|
|||||||
xiic_wakeup(i2c, STATE_DONE);
|
xiic_wakeup(i2c, STATE_DONE);
|
||||||
else
|
else
|
||||||
xiic_wakeup(i2c, STATE_ERROR);
|
xiic_wakeup(i2c, STATE_ERROR);
|
||||||
|
}
|
||||||
} else if (pend & (XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK)) {
|
if (pend & (XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK)) {
|
||||||
/* Transmit register/FIFO is empty or ½ empty */
|
/* Transmit register/FIFO is empty or ½ empty */
|
||||||
|
|
||||||
clr = pend &
|
clr |= (pend &
|
||||||
(XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK);
|
(XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_HALF_MASK));
|
||||||
|
|
||||||
if (!i2c->tx_msg) {
|
if (!i2c->tx_msg) {
|
||||||
dev_dbg(i2c->adap.dev.parent,
|
dev_dbg(i2c->adap.dev.parent,
|
||||||
@ -492,16 +492,13 @@ static void xiic_process(struct xiic_i2c *i2c)
|
|||||||
* make sure to disable tx half
|
* make sure to disable tx half
|
||||||
*/
|
*/
|
||||||
xiic_irq_dis(i2c, XIIC_INTR_TX_HALF_MASK);
|
xiic_irq_dis(i2c, XIIC_INTR_TX_HALF_MASK);
|
||||||
} else {
|
|
||||||
/* got IRQ which is not acked */
|
|
||||||
dev_err(i2c->adap.dev.parent, "%s Got unexpected IRQ\n",
|
|
||||||
__func__);
|
|
||||||
clr = pend;
|
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
dev_dbg(i2c->adap.dev.parent, "%s clr: 0x%x\n", __func__, clr);
|
dev_dbg(i2c->adap.dev.parent, "%s clr: 0x%x\n", __func__, clr);
|
||||||
|
|
||||||
xiic_setreg32(i2c, XIIC_IISR_OFFSET, clr);
|
xiic_setreg32(i2c, XIIC_IISR_OFFSET, clr);
|
||||||
|
spin_unlock(&i2c->lock);
|
||||||
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xiic_bus_busy(struct xiic_i2c *i2c)
|
static int xiic_bus_busy(struct xiic_i2c *i2c)
|
||||||
@ -525,7 +522,7 @@ static int xiic_busy(struct xiic_i2c *i2c)
|
|||||||
*/
|
*/
|
||||||
err = xiic_bus_busy(i2c);
|
err = xiic_bus_busy(i2c);
|
||||||
while (err && tries--) {
|
while (err && tries--) {
|
||||||
mdelay(1);
|
msleep(1);
|
||||||
err = xiic_bus_busy(i2c);
|
err = xiic_bus_busy(i2c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,19 +599,21 @@ static void xiic_start_send(struct xiic_i2c *i2c)
|
|||||||
static irqreturn_t xiic_isr(int irq, void *dev_id)
|
static irqreturn_t xiic_isr(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct xiic_i2c *i2c = dev_id;
|
struct xiic_i2c *i2c = dev_id;
|
||||||
|
u32 pend, isr, ier;
|
||||||
spin_lock(&i2c->lock);
|
irqreturn_t ret = IRQ_NONE;
|
||||||
/* disable interrupts globally */
|
/* Do not processes a devices interrupts if the device has no
|
||||||
xiic_setreg32(i2c, XIIC_DGIER_OFFSET, 0);
|
* interrupts pending
|
||||||
|
*/
|
||||||
|
|
||||||
dev_dbg(i2c->adap.dev.parent, "%s entry\n", __func__);
|
dev_dbg(i2c->adap.dev.parent, "%s entry\n", __func__);
|
||||||
|
|
||||||
xiic_process(i2c);
|
isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
|
||||||
|
ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET);
|
||||||
|
pend = isr & ier;
|
||||||
|
if (pend)
|
||||||
|
ret = IRQ_WAKE_THREAD;
|
||||||
|
|
||||||
xiic_setreg32(i2c, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK);
|
return ret;
|
||||||
spin_unlock(&i2c->lock);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __xiic_start_xfer(struct xiic_i2c *i2c)
|
static void __xiic_start_xfer(struct xiic_i2c *i2c)
|
||||||
@ -663,16 +662,8 @@ static void __xiic_start_xfer(struct xiic_i2c *i2c)
|
|||||||
|
|
||||||
static void xiic_start_xfer(struct xiic_i2c *i2c)
|
static void xiic_start_xfer(struct xiic_i2c *i2c)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&i2c->lock, flags);
|
|
||||||
xiic_reinit(i2c);
|
|
||||||
/* disable interrupts globally */
|
|
||||||
xiic_setreg32(i2c, XIIC_DGIER_OFFSET, 0);
|
|
||||||
spin_unlock_irqrestore(&i2c->lock, flags);
|
|
||||||
|
|
||||||
__xiic_start_xfer(i2c);
|
__xiic_start_xfer(i2c);
|
||||||
xiic_setreg32(i2c, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||||
@ -755,7 +746,10 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
|||||||
spin_lock_init(&i2c->lock);
|
spin_lock_init(&i2c->lock);
|
||||||
init_waitqueue_head(&i2c->wait);
|
init_waitqueue_head(&i2c->wait);
|
||||||
|
|
||||||
ret = devm_request_irq(&pdev->dev, irq, xiic_isr, 0, pdev->name, i2c);
|
ret = devm_request_threaded_irq(&pdev->dev, irq, xiic_isr,
|
||||||
|
xiic_process, IRQF_ONESHOT,
|
||||||
|
pdev->name, i2c);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Cannot claim IRQ\n");
|
dev_err(&pdev->dev, "Cannot claim IRQ\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
I2C slave support (c) 2014 by Wolfram Sang <wsa@sang-engineering.com>
|
I2C slave support (c) 2014 by Wolfram Sang <wsa@sang-engineering.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <dt-bindings/i2c/i2c.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
@ -47,6 +48,7 @@
|
|||||||
#include <linux/rwsem.h>
|
#include <linux/rwsem.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/pm_domain.h>
|
#include <linux/pm_domain.h>
|
||||||
|
#include <linux/pm_wakeirq.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/jump_label.h>
|
#include <linux/jump_label.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
@ -57,6 +59,9 @@
|
|||||||
#define CREATE_TRACE_POINTS
|
#define CREATE_TRACE_POINTS
|
||||||
#include <trace/events/i2c.h>
|
#include <trace/events/i2c.h>
|
||||||
|
|
||||||
|
#define I2C_ADDR_OFFSET_TEN_BIT 0xa000
|
||||||
|
#define I2C_ADDR_OFFSET_SLAVE 0x1000
|
||||||
|
|
||||||
/* core_lock protects i2c_adapter_idr, and guarantees
|
/* core_lock protects i2c_adapter_idr, and guarantees
|
||||||
that device detection, deletion of detected devices, and attach_adapter
|
that device detection, deletion of detected devices, and attach_adapter
|
||||||
calls are serialized */
|
calls are serialized */
|
||||||
@ -641,11 +646,13 @@ static int i2c_device_probe(struct device *dev)
|
|||||||
if (!client->irq) {
|
if (!client->irq) {
|
||||||
int irq = -ENOENT;
|
int irq = -ENOENT;
|
||||||
|
|
||||||
if (dev->of_node)
|
if (dev->of_node) {
|
||||||
irq = of_irq_get(dev->of_node, 0);
|
irq = of_irq_get_byname(dev->of_node, "irq");
|
||||||
else if (ACPI_COMPANION(dev))
|
if (irq == -EINVAL || irq == -ENODATA)
|
||||||
|
irq = of_irq_get(dev->of_node, 0);
|
||||||
|
} else if (ACPI_COMPANION(dev)) {
|
||||||
irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
|
irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
|
||||||
|
}
|
||||||
if (irq == -EPROBE_DEFER)
|
if (irq == -EPROBE_DEFER)
|
||||||
return irq;
|
return irq;
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
@ -658,23 +665,49 @@ static int i2c_device_probe(struct device *dev)
|
|||||||
if (!driver->probe || !driver->id_table)
|
if (!driver->probe || !driver->id_table)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
if (!device_can_wakeup(&client->dev))
|
if (client->flags & I2C_CLIENT_WAKE) {
|
||||||
device_init_wakeup(&client->dev,
|
int wakeirq = -ENOENT;
|
||||||
client->flags & I2C_CLIENT_WAKE);
|
|
||||||
|
if (dev->of_node) {
|
||||||
|
wakeirq = of_irq_get_byname(dev->of_node, "wakeup");
|
||||||
|
if (wakeirq == -EPROBE_DEFER)
|
||||||
|
return wakeirq;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_init_wakeup(&client->dev, true);
|
||||||
|
|
||||||
|
if (wakeirq > 0 && wakeirq != client->irq)
|
||||||
|
status = dev_pm_set_dedicated_wake_irq(dev, wakeirq);
|
||||||
|
else if (client->irq > 0)
|
||||||
|
status = dev_pm_set_wake_irq(dev, wakeirq);
|
||||||
|
else
|
||||||
|
status = 0;
|
||||||
|
|
||||||
|
if (status)
|
||||||
|
dev_warn(&client->dev, "failed to set up wakeup irq");
|
||||||
|
}
|
||||||
|
|
||||||
dev_dbg(dev, "probe\n");
|
dev_dbg(dev, "probe\n");
|
||||||
|
|
||||||
status = of_clk_set_defaults(dev->of_node, false);
|
status = of_clk_set_defaults(dev->of_node, false);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return status;
|
goto err_clear_wakeup_irq;
|
||||||
|
|
||||||
status = dev_pm_domain_attach(&client->dev, true);
|
status = dev_pm_domain_attach(&client->dev, true);
|
||||||
if (status != -EPROBE_DEFER) {
|
if (status != -EPROBE_DEFER) {
|
||||||
status = driver->probe(client, i2c_match_id(driver->id_table,
|
status = driver->probe(client, i2c_match_id(driver->id_table,
|
||||||
client));
|
client));
|
||||||
if (status)
|
if (status)
|
||||||
dev_pm_domain_detach(&client->dev, true);
|
goto err_detach_pm_domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_detach_pm_domain:
|
||||||
|
dev_pm_domain_detach(&client->dev, true);
|
||||||
|
err_clear_wakeup_irq:
|
||||||
|
dev_pm_clear_wake_irq(&client->dev);
|
||||||
|
device_init_wakeup(&client->dev, false);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -694,6 +727,10 @@ static int i2c_device_remove(struct device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
dev_pm_domain_detach(&client->dev, true);
|
dev_pm_domain_detach(&client->dev, true);
|
||||||
|
|
||||||
|
dev_pm_clear_wake_irq(&client->dev);
|
||||||
|
device_init_wakeup(&client->dev, false);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -778,17 +815,32 @@ struct i2c_client *i2c_verify_client(struct device *dev)
|
|||||||
EXPORT_SYMBOL(i2c_verify_client);
|
EXPORT_SYMBOL(i2c_verify_client);
|
||||||
|
|
||||||
|
|
||||||
|
/* Return a unique address which takes the flags of the client into account */
|
||||||
|
static unsigned short i2c_encode_flags_to_addr(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
unsigned short addr = client->addr;
|
||||||
|
|
||||||
|
/* For some client flags, add an arbitrary offset to avoid collisions */
|
||||||
|
if (client->flags & I2C_CLIENT_TEN)
|
||||||
|
addr |= I2C_ADDR_OFFSET_TEN_BIT;
|
||||||
|
|
||||||
|
if (client->flags & I2C_CLIENT_SLAVE)
|
||||||
|
addr |= I2C_ADDR_OFFSET_SLAVE;
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
/* This is a permissive address validity check, I2C address map constraints
|
/* This is a permissive address validity check, I2C address map constraints
|
||||||
* are purposely not enforced, except for the general call address. */
|
* are purposely not enforced, except for the general call address. */
|
||||||
static int i2c_check_client_addr_validity(const struct i2c_client *client)
|
static int i2c_check_addr_validity(unsigned addr, unsigned short flags)
|
||||||
{
|
{
|
||||||
if (client->flags & I2C_CLIENT_TEN) {
|
if (flags & I2C_CLIENT_TEN) {
|
||||||
/* 10-bit address, all values are valid */
|
/* 10-bit address, all values are valid */
|
||||||
if (client->addr > 0x3ff)
|
if (addr > 0x3ff)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
} else {
|
} else {
|
||||||
/* 7-bit address, reject the general call address */
|
/* 7-bit address, reject the general call address */
|
||||||
if (client->addr == 0x00 || client->addr > 0x7f)
|
if (addr == 0x00 || addr > 0x7f)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -798,7 +850,7 @@ static int i2c_check_client_addr_validity(const struct i2c_client *client)
|
|||||||
* device uses a reserved address, then it shouldn't be probed. 7-bit
|
* device uses a reserved address, then it shouldn't be probed. 7-bit
|
||||||
* addressing is assumed, 10-bit address devices are rare and should be
|
* addressing is assumed, 10-bit address devices are rare and should be
|
||||||
* explicitly enumerated. */
|
* explicitly enumerated. */
|
||||||
static int i2c_check_addr_validity(unsigned short addr)
|
static int i2c_check_7bit_addr_validity_strict(unsigned short addr)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Reserved addresses per I2C specification:
|
* Reserved addresses per I2C specification:
|
||||||
@ -820,7 +872,7 @@ static int __i2c_check_addr_busy(struct device *dev, void *addrp)
|
|||||||
struct i2c_client *client = i2c_verify_client(dev);
|
struct i2c_client *client = i2c_verify_client(dev);
|
||||||
int addr = *(int *)addrp;
|
int addr = *(int *)addrp;
|
||||||
|
|
||||||
if (client && client->addr == addr)
|
if (client && i2c_encode_flags_to_addr(client) == addr)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -923,10 +975,8 @@ static void i2c_dev_set_name(struct i2c_adapter *adap,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For 10-bit clients, add an arbitrary offset to avoid collisions */
|
|
||||||
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
|
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
|
||||||
client->addr | ((client->flags & I2C_CLIENT_TEN)
|
i2c_encode_flags_to_addr(client));
|
||||||
? 0xa000 : 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -968,8 +1018,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
|
|||||||
|
|
||||||
strlcpy(client->name, info->type, sizeof(client->name));
|
strlcpy(client->name, info->type, sizeof(client->name));
|
||||||
|
|
||||||
/* Check for address validity */
|
status = i2c_check_addr_validity(client->addr, client->flags);
|
||||||
status = i2c_check_client_addr_validity(client);
|
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
|
dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
|
||||||
client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
|
client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
|
||||||
@ -977,7 +1026,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check for address business */
|
/* Check for address business */
|
||||||
status = i2c_check_addr_busy(adap, client->addr);
|
status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client));
|
||||||
if (status)
|
if (status)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
@ -1142,6 +1191,16 @@ i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((info.addr & I2C_ADDR_OFFSET_TEN_BIT) == I2C_ADDR_OFFSET_TEN_BIT) {
|
||||||
|
info.addr &= ~I2C_ADDR_OFFSET_TEN_BIT;
|
||||||
|
info.flags |= I2C_CLIENT_TEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.addr & I2C_ADDR_OFFSET_SLAVE) {
|
||||||
|
info.addr &= ~I2C_ADDR_OFFSET_SLAVE;
|
||||||
|
info.flags |= I2C_CLIENT_SLAVE;
|
||||||
|
}
|
||||||
|
|
||||||
client = i2c_new_device(adap, &info);
|
client = i2c_new_device(adap, &info);
|
||||||
if (!client)
|
if (!client)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -1193,7 +1252,7 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
|
|||||||
i2c_adapter_depth(adap));
|
i2c_adapter_depth(adap));
|
||||||
list_for_each_entry_safe(client, next, &adap->userspace_clients,
|
list_for_each_entry_safe(client, next, &adap->userspace_clients,
|
||||||
detected) {
|
detected) {
|
||||||
if (client->addr == addr) {
|
if (i2c_encode_flags_to_addr(client) == addr) {
|
||||||
dev_info(dev, "%s: Deleting device %s at 0x%02hx\n",
|
dev_info(dev, "%s: Deleting device %s at 0x%02hx\n",
|
||||||
"delete_device", client->name, client->addr);
|
"delete_device", client->name, client->addr);
|
||||||
|
|
||||||
@ -1273,7 +1332,8 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
|
|||||||
struct i2c_client *result;
|
struct i2c_client *result;
|
||||||
struct i2c_board_info info = {};
|
struct i2c_board_info info = {};
|
||||||
struct dev_archdata dev_ad = {};
|
struct dev_archdata dev_ad = {};
|
||||||
const __be32 *addr;
|
const __be32 *addr_be;
|
||||||
|
u32 addr;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
|
dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
|
||||||
@ -1284,20 +1344,31 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
|
|||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
addr = of_get_property(node, "reg", &len);
|
addr_be = of_get_property(node, "reg", &len);
|
||||||
if (!addr || (len < sizeof(*addr))) {
|
if (!addr_be || (len < sizeof(*addr_be))) {
|
||||||
dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
|
dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
|
||||||
node->full_name);
|
node->full_name);
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
info.addr = be32_to_cpup(addr);
|
addr = be32_to_cpup(addr_be);
|
||||||
if (info.addr > (1 << 10) - 1) {
|
if (addr & I2C_TEN_BIT_ADDRESS) {
|
||||||
|
addr &= ~I2C_TEN_BIT_ADDRESS;
|
||||||
|
info.flags |= I2C_CLIENT_TEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr & I2C_OWN_SLAVE_ADDRESS) {
|
||||||
|
addr &= ~I2C_OWN_SLAVE_ADDRESS;
|
||||||
|
info.flags |= I2C_CLIENT_SLAVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2c_check_addr_validity(addr, info.flags)) {
|
||||||
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
|
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
|
||||||
info.addr, node->full_name);
|
info.addr, node->full_name);
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info.addr = addr;
|
||||||
info.of_node = of_node_get(node);
|
info.of_node = of_node_get(node);
|
||||||
info.archdata = &dev_ad;
|
info.archdata = &dev_ad;
|
||||||
|
|
||||||
@ -1371,6 +1442,24 @@ struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
|
|||||||
return adapter;
|
return adapter;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(of_find_i2c_adapter_by_node);
|
EXPORT_SYMBOL(of_find_i2c_adapter_by_node);
|
||||||
|
|
||||||
|
/* must call i2c_put_adapter() when done with returned i2c_adapter device */
|
||||||
|
struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node)
|
||||||
|
{
|
||||||
|
struct i2c_adapter *adapter;
|
||||||
|
|
||||||
|
adapter = of_find_i2c_adapter_by_node(node);
|
||||||
|
if (!adapter)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!try_module_get(adapter->owner)) {
|
||||||
|
put_device(&adapter->dev);
|
||||||
|
adapter = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return adapter;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(of_get_i2c_adapter_by_node);
|
||||||
#else
|
#else
|
||||||
static void of_i2c_register_devices(struct i2c_adapter *adap) { }
|
static void of_i2c_register_devices(struct i2c_adapter *adap) { }
|
||||||
#endif /* CONFIG_OF */
|
#endif /* CONFIG_OF */
|
||||||
@ -2262,14 +2351,14 @@ static int i2c_detect_address(struct i2c_client *temp_client,
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* Make sure the address is valid */
|
/* Make sure the address is valid */
|
||||||
err = i2c_check_addr_validity(addr);
|
err = i2c_check_7bit_addr_validity_strict(addr);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
|
dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
|
||||||
addr);
|
addr);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Skip if already in use */
|
/* Skip if already in use (7 bit, no need to encode flags) */
|
||||||
if (i2c_check_addr_busy(adapter, addr))
|
if (i2c_check_addr_busy(adapter, addr))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -2379,13 +2468,13 @@ i2c_new_probed_device(struct i2c_adapter *adap,
|
|||||||
|
|
||||||
for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {
|
for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {
|
||||||
/* Check address validity */
|
/* Check address validity */
|
||||||
if (i2c_check_addr_validity(addr_list[i]) < 0) {
|
if (i2c_check_7bit_addr_validity_strict(addr_list[i]) < 0) {
|
||||||
dev_warn(&adap->dev, "Invalid 7-bit address "
|
dev_warn(&adap->dev, "Invalid 7-bit address "
|
||||||
"0x%02x\n", addr_list[i]);
|
"0x%02x\n", addr_list[i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check address availability */
|
/* Check address availability (7 bit, no need to encode flags) */
|
||||||
if (i2c_check_addr_busy(adap, addr_list[i])) {
|
if (i2c_check_addr_busy(adap, addr_list[i])) {
|
||||||
dev_dbg(&adap->dev, "Address 0x%02x already in "
|
dev_dbg(&adap->dev, "Address 0x%02x already in "
|
||||||
"use, not probing\n", addr_list[i]);
|
"use, not probing\n", addr_list[i]);
|
||||||
@ -2413,9 +2502,15 @@ struct i2c_adapter *i2c_get_adapter(int nr)
|
|||||||
|
|
||||||
mutex_lock(&core_lock);
|
mutex_lock(&core_lock);
|
||||||
adapter = idr_find(&i2c_adapter_idr, nr);
|
adapter = idr_find(&i2c_adapter_idr, nr);
|
||||||
if (adapter && !try_module_get(adapter->owner))
|
if (!adapter)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
if (try_module_get(adapter->owner))
|
||||||
|
get_device(&adapter->dev);
|
||||||
|
else
|
||||||
adapter = NULL;
|
adapter = NULL;
|
||||||
|
|
||||||
|
exit:
|
||||||
mutex_unlock(&core_lock);
|
mutex_unlock(&core_lock);
|
||||||
return adapter;
|
return adapter;
|
||||||
}
|
}
|
||||||
@ -2423,8 +2518,11 @@ EXPORT_SYMBOL(i2c_get_adapter);
|
|||||||
|
|
||||||
void i2c_put_adapter(struct i2c_adapter *adap)
|
void i2c_put_adapter(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
if (adap)
|
if (!adap)
|
||||||
module_put(adap->owner);
|
return;
|
||||||
|
|
||||||
|
put_device(&adap->dev);
|
||||||
|
module_put(adap->owner);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(i2c_put_adapter);
|
EXPORT_SYMBOL(i2c_put_adapter);
|
||||||
|
|
||||||
@ -2942,6 +3040,63 @@ trace:
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(i2c_smbus_xfer);
|
EXPORT_SYMBOL(i2c_smbus_xfer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i2c_smbus_read_i2c_block_data_or_emulated - read block or emulate
|
||||||
|
* @client: Handle to slave device
|
||||||
|
* @command: Byte interpreted by slave
|
||||||
|
* @length: Size of data block; SMBus allows at most I2C_SMBUS_BLOCK_MAX bytes
|
||||||
|
* @values: Byte array into which data will be read; big enough to hold
|
||||||
|
* the data returned by the slave. SMBus allows at most
|
||||||
|
* I2C_SMBUS_BLOCK_MAX bytes.
|
||||||
|
*
|
||||||
|
* This executes the SMBus "block read" protocol if supported by the adapter.
|
||||||
|
* If block read is not supported, it emulates it using either word or byte
|
||||||
|
* read protocols depending on availability.
|
||||||
|
*
|
||||||
|
* The addresses of the I2C slave device that are accessed with this function
|
||||||
|
* must be mapped to a linear region, so that a block read will have the same
|
||||||
|
* effect as a byte read. Before using this function you must double-check
|
||||||
|
* if the I2C slave does support exchanging a block transfer with a byte
|
||||||
|
* transfer.
|
||||||
|
*/
|
||||||
|
s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client,
|
||||||
|
u8 command, u8 length, u8 *values)
|
||||||
|
{
|
||||||
|
u8 i = 0;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (length > I2C_SMBUS_BLOCK_MAX)
|
||||||
|
length = I2C_SMBUS_BLOCK_MAX;
|
||||||
|
|
||||||
|
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
|
||||||
|
return i2c_smbus_read_i2c_block_data(client, command, length, values);
|
||||||
|
|
||||||
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) {
|
||||||
|
while ((i + 2) <= length) {
|
||||||
|
status = i2c_smbus_read_word_data(client, command + i);
|
||||||
|
if (status < 0)
|
||||||
|
return status;
|
||||||
|
values[i] = status & 0xff;
|
||||||
|
values[i + 1] = status >> 8;
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i < length) {
|
||||||
|
status = i2c_smbus_read_byte_data(client, command + i);
|
||||||
|
if (status < 0)
|
||||||
|
return status;
|
||||||
|
values[i] = status;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||||
int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
|
int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
|
||||||
{
|
{
|
||||||
@ -2952,9 +3107,13 @@ int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(client->flags & I2C_CLIENT_SLAVE))
|
||||||
|
dev_warn(&client->dev, "%s: client slave flag not set. You might see address collisions\n",
|
||||||
|
__func__);
|
||||||
|
|
||||||
if (!(client->flags & I2C_CLIENT_TEN)) {
|
if (!(client->flags & I2C_CLIENT_TEN)) {
|
||||||
/* Enforce stricter address checking */
|
/* Enforce stricter address checking */
|
||||||
ret = i2c_check_addr_validity(client->addr);
|
ret = i2c_check_7bit_addr_validity_strict(client->addr);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&client->dev, "%s: invalid address\n", __func__);
|
dev_err(&client->dev, "%s: invalid address\n", __func__);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -157,7 +157,6 @@ MODULE_DEVICE_TABLE(i2c, i2c_slave_eeprom_id);
|
|||||||
static struct i2c_driver i2c_slave_eeprom_driver = {
|
static struct i2c_driver i2c_slave_eeprom_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "i2c-slave-eeprom",
|
.name = "i2c-slave-eeprom",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = i2c_slave_eeprom_probe,
|
.probe = i2c_slave_eeprom_probe,
|
||||||
.remove = i2c_slave_eeprom_remove,
|
.remove = i2c_slave_eeprom_remove,
|
||||||
|
@ -61,4 +61,15 @@ config I2C_MUX_PINCTRL
|
|||||||
This driver can also be built as a module. If so, the module will be
|
This driver can also be built as a module. If so, the module will be
|
||||||
called pinctrl-i2cmux.
|
called pinctrl-i2cmux.
|
||||||
|
|
||||||
|
config I2C_MUX_REG
|
||||||
|
tristate "Register-based I2C multiplexer"
|
||||||
|
help
|
||||||
|
If you say yes to this option, support will be included for a
|
||||||
|
register based I2C multiplexer. This driver provides access to
|
||||||
|
I2C busses connected through a MUX, which is controlled
|
||||||
|
by a single register.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module
|
||||||
|
will be called i2c-mux-reg.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
@ -7,5 +7,6 @@ obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
|
|||||||
obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
|
obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
|
||||||
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
|
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
|
||||||
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
|
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
|
||||||
|
obj-$(CONFIG_I2C_MUX_REG) += i2c-mux-reg.o
|
||||||
|
|
||||||
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
|
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
|
||||||
|
@ -196,7 +196,8 @@ static int i2c_arbitrator_probe(struct platform_device *pdev)
|
|||||||
dev_err(dev, "Cannot parse i2c-parent\n");
|
dev_err(dev, "Cannot parse i2c-parent\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
arb->parent = of_find_i2c_adapter_by_node(parent_np);
|
arb->parent = of_get_i2c_adapter_by_node(parent_np);
|
||||||
|
of_node_put(parent_np);
|
||||||
if (!arb->parent) {
|
if (!arb->parent) {
|
||||||
dev_err(dev, "Cannot find parent bus\n");
|
dev_err(dev, "Cannot find parent bus\n");
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
|
@ -76,6 +76,7 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
adapter = of_find_i2c_adapter_by_node(adapter_np);
|
adapter = of_find_i2c_adapter_by_node(adapter_np);
|
||||||
|
of_node_put(adapter_np);
|
||||||
if (!adapter)
|
if (!adapter)
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
|
@ -386,7 +386,6 @@ static int pca9541_remove(struct i2c_client *client)
|
|||||||
static struct i2c_driver pca9541_driver = {
|
static struct i2c_driver pca9541_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "pca9541",
|
.name = "pca9541",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = pca9541_probe,
|
.probe = pca9541_probe,
|
||||||
.remove = pca9541_remove,
|
.remove = pca9541_remove,
|
||||||
|
@ -300,7 +300,6 @@ static struct i2c_driver pca954x_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "pca954x",
|
.name = "pca954x",
|
||||||
.pm = &pca954x_pm,
|
.pm = &pca954x_pm,
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = pca954x_probe,
|
.probe = pca954x_probe,
|
||||||
.remove = pca954x_remove,
|
.remove = pca954x_remove,
|
||||||
|
@ -111,6 +111,7 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
adapter = of_find_i2c_adapter_by_node(adapter_np);
|
adapter = of_find_i2c_adapter_by_node(adapter_np);
|
||||||
|
of_node_put(adapter_np);
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
dev_err(mux->dev, "Cannot find parent bus\n");
|
dev_err(mux->dev, "Cannot find parent bus\n");
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
|
290
drivers/i2c/muxes/i2c-mux-reg.c
Normal file
290
drivers/i2c/muxes/i2c-mux-reg.c
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
/*
|
||||||
|
* I2C multiplexer using a single register
|
||||||
|
*
|
||||||
|
* Copyright 2015 Freescale Semiconductor
|
||||||
|
* York Sun <yorksun@freescale.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c-mux.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/platform_data/i2c-mux-reg.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
struct regmux {
|
||||||
|
struct i2c_adapter *parent;
|
||||||
|
struct i2c_adapter **adap; /* child busses */
|
||||||
|
struct i2c_mux_reg_platform_data data;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int i2c_mux_reg_set(const struct regmux *mux, unsigned int chan_id)
|
||||||
|
{
|
||||||
|
if (!mux->data.reg)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write to the register, followed by a read to ensure the write is
|
||||||
|
* completed on a "posted" bus, for example PCI or write buffers.
|
||||||
|
* The endianness of reading doesn't matter and the return data
|
||||||
|
* is not used.
|
||||||
|
*/
|
||||||
|
switch (mux->data.reg_size) {
|
||||||
|
case 4:
|
||||||
|
if (mux->data.little_endian)
|
||||||
|
iowrite32(chan_id, mux->data.reg);
|
||||||
|
else
|
||||||
|
iowrite32be(chan_id, mux->data.reg);
|
||||||
|
if (!mux->data.write_only)
|
||||||
|
ioread32(mux->data.reg);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (mux->data.little_endian)
|
||||||
|
iowrite16(chan_id, mux->data.reg);
|
||||||
|
else
|
||||||
|
iowrite16be(chan_id, mux->data.reg);
|
||||||
|
if (!mux->data.write_only)
|
||||||
|
ioread16(mux->data.reg);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
iowrite8(chan_id, mux->data.reg);
|
||||||
|
if (!mux->data.write_only)
|
||||||
|
ioread8(mux->data.reg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_mux_reg_select(struct i2c_adapter *adap, void *data,
|
||||||
|
unsigned int chan)
|
||||||
|
{
|
||||||
|
struct regmux *mux = data;
|
||||||
|
|
||||||
|
return i2c_mux_reg_set(mux, chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_mux_reg_deselect(struct i2c_adapter *adap, void *data,
|
||||||
|
unsigned int chan)
|
||||||
|
{
|
||||||
|
struct regmux *mux = data;
|
||||||
|
|
||||||
|
if (mux->data.idle_in_use)
|
||||||
|
return i2c_mux_reg_set(mux, mux->data.idle);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static int i2c_mux_reg_probe_dt(struct regmux *mux,
|
||||||
|
struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct device_node *adapter_np, *child;
|
||||||
|
struct i2c_adapter *adapter;
|
||||||
|
struct resource res;
|
||||||
|
unsigned *values;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (!np)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
adapter_np = of_parse_phandle(np, "i2c-parent", 0);
|
||||||
|
if (!adapter_np) {
|
||||||
|
dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
adapter = of_find_i2c_adapter_by_node(adapter_np);
|
||||||
|
of_node_put(adapter_np);
|
||||||
|
if (!adapter)
|
||||||
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
|
mux->parent = adapter;
|
||||||
|
mux->data.parent = i2c_adapter_id(adapter);
|
||||||
|
put_device(&adapter->dev);
|
||||||
|
|
||||||
|
mux->data.n_values = of_get_child_count(np);
|
||||||
|
if (of_find_property(np, "little-endian", NULL)) {
|
||||||
|
mux->data.little_endian = true;
|
||||||
|
} else if (of_find_property(np, "big-endian", NULL)) {
|
||||||
|
mux->data.little_endian = false;
|
||||||
|
} else {
|
||||||
|
#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : \
|
||||||
|
defined(__LITTLE_ENDIAN)
|
||||||
|
mux->data.little_endian = true;
|
||||||
|
#elif defined(__BYTE_ORDER) ? __BYTE_ORDER == __BIG_ENDIAN : \
|
||||||
|
defined(__BIG_ENDIAN)
|
||||||
|
mux->data.little_endian = false;
|
||||||
|
#else
|
||||||
|
#error Endianness not defined?
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (of_find_property(np, "write-only", NULL))
|
||||||
|
mux->data.write_only = true;
|
||||||
|
else
|
||||||
|
mux->data.write_only = false;
|
||||||
|
|
||||||
|
values = devm_kzalloc(&pdev->dev,
|
||||||
|
sizeof(*mux->data.values) * mux->data.n_values,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!values) {
|
||||||
|
dev_err(&pdev->dev, "Cannot allocate values array");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
for_each_child_of_node(np, child) {
|
||||||
|
of_property_read_u32(child, "reg", values + i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
mux->data.values = values;
|
||||||
|
|
||||||
|
if (!of_property_read_u32(np, "idle-state", &mux->data.idle))
|
||||||
|
mux->data.idle_in_use = true;
|
||||||
|
|
||||||
|
/* map address from "reg" if exists */
|
||||||
|
if (of_address_to_resource(np, 0, &res)) {
|
||||||
|
mux->data.reg_size = resource_size(&res);
|
||||||
|
mux->data.reg = devm_ioremap_resource(&pdev->dev, &res);
|
||||||
|
if (IS_ERR(mux->data.reg))
|
||||||
|
return PTR_ERR(mux->data.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int i2c_mux_reg_probe_dt(struct regmux *mux,
|
||||||
|
struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int i2c_mux_reg_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct regmux *mux;
|
||||||
|
struct i2c_adapter *parent;
|
||||||
|
struct resource *res;
|
||||||
|
int (*deselect)(struct i2c_adapter *, void *, u32);
|
||||||
|
unsigned int class;
|
||||||
|
int i, ret, nr;
|
||||||
|
|
||||||
|
mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
|
||||||
|
if (!mux)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, mux);
|
||||||
|
|
||||||
|
if (dev_get_platdata(&pdev->dev)) {
|
||||||
|
memcpy(&mux->data, dev_get_platdata(&pdev->dev),
|
||||||
|
sizeof(mux->data));
|
||||||
|
|
||||||
|
parent = i2c_get_adapter(mux->data.parent);
|
||||||
|
if (!parent)
|
||||||
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
|
mux->parent = parent;
|
||||||
|
} else {
|
||||||
|
ret = i2c_mux_reg_probe_dt(mux, pdev);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "Error parsing device tree");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mux->data.reg) {
|
||||||
|
dev_info(&pdev->dev,
|
||||||
|
"Register not set, using platform resource\n");
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
mux->data.reg_size = resource_size(res);
|
||||||
|
mux->data.reg = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(mux->data.reg))
|
||||||
|
return PTR_ERR(mux->data.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mux->data.reg_size != 4 && mux->data.reg_size != 2 &&
|
||||||
|
mux->data.reg_size != 1) {
|
||||||
|
dev_err(&pdev->dev, "Invalid register size\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mux->adap = devm_kzalloc(&pdev->dev,
|
||||||
|
sizeof(*mux->adap) * mux->data.n_values,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!mux->adap) {
|
||||||
|
dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mux->data.idle_in_use)
|
||||||
|
deselect = i2c_mux_reg_deselect;
|
||||||
|
else
|
||||||
|
deselect = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < mux->data.n_values; i++) {
|
||||||
|
nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
|
||||||
|
class = mux->data.classes ? mux->data.classes[i] : 0;
|
||||||
|
|
||||||
|
mux->adap[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev, mux,
|
||||||
|
nr, mux->data.values[i],
|
||||||
|
class, i2c_mux_reg_select,
|
||||||
|
deselect);
|
||||||
|
if (!mux->adap[i]) {
|
||||||
|
ret = -ENODEV;
|
||||||
|
dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
|
||||||
|
goto add_adapter_failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "%d port mux on %s adapter\n",
|
||||||
|
mux->data.n_values, mux->parent->name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
add_adapter_failed:
|
||||||
|
for (; i > 0; i--)
|
||||||
|
i2c_del_mux_adapter(mux->adap[i - 1]);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_mux_reg_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct regmux *mux = platform_get_drvdata(pdev);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < mux->data.n_values; i++)
|
||||||
|
i2c_del_mux_adapter(mux->adap[i]);
|
||||||
|
|
||||||
|
i2c_put_adapter(mux->parent);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id i2c_mux_reg_of_match[] = {
|
||||||
|
{ .compatible = "i2c-mux-reg", },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, i2c_mux_reg_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver i2c_mux_reg_driver = {
|
||||||
|
.probe = i2c_mux_reg_probe,
|
||||||
|
.remove = i2c_mux_reg_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "i2c-mux-reg",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(i2c_mux_reg_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Register-based I2C multiplexer driver");
|
||||||
|
MODULE_AUTHOR("York Sun <yorksun@freescale.com>");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("platform:i2c-mux-reg");
|
@ -186,19 +186,11 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
|
|||||||
if (count > io_limit)
|
if (count > io_limit)
|
||||||
count = io_limit;
|
count = io_limit;
|
||||||
|
|
||||||
switch (at24->use_smbus) {
|
if (at24->use_smbus) {
|
||||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
|
||||||
/* Smaller eeproms can work given some SMBus extension calls */
|
/* Smaller eeproms can work given some SMBus extension calls */
|
||||||
if (count > I2C_SMBUS_BLOCK_MAX)
|
if (count > I2C_SMBUS_BLOCK_MAX)
|
||||||
count = I2C_SMBUS_BLOCK_MAX;
|
count = I2C_SMBUS_BLOCK_MAX;
|
||||||
break;
|
} else {
|
||||||
case I2C_SMBUS_WORD_DATA:
|
|
||||||
count = 2;
|
|
||||||
break;
|
|
||||||
case I2C_SMBUS_BYTE_DATA:
|
|
||||||
count = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/*
|
/*
|
||||||
* When we have a better choice than SMBus calls, use a
|
* When we have a better choice than SMBus calls, use a
|
||||||
* combined I2C message. Write address; then read up to
|
* combined I2C message. Write address; then read up to
|
||||||
@ -229,27 +221,10 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
|
|||||||
timeout = jiffies + msecs_to_jiffies(write_timeout);
|
timeout = jiffies + msecs_to_jiffies(write_timeout);
|
||||||
do {
|
do {
|
||||||
read_time = jiffies;
|
read_time = jiffies;
|
||||||
switch (at24->use_smbus) {
|
if (at24->use_smbus) {
|
||||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
status = i2c_smbus_read_i2c_block_data_or_emulated(client, offset,
|
||||||
status = i2c_smbus_read_i2c_block_data(client, offset,
|
count, buf);
|
||||||
count, buf);
|
} else {
|
||||||
break;
|
|
||||||
case I2C_SMBUS_WORD_DATA:
|
|
||||||
status = i2c_smbus_read_word_data(client, offset);
|
|
||||||
if (status >= 0) {
|
|
||||||
buf[0] = status & 0xff;
|
|
||||||
buf[1] = status >> 8;
|
|
||||||
status = count;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case I2C_SMBUS_BYTE_DATA:
|
|
||||||
status = i2c_smbus_read_byte_data(client, offset);
|
|
||||||
if (status >= 0) {
|
|
||||||
buf[0] = status;
|
|
||||||
status = count;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status = i2c_transfer(client->adapter, msg, 2);
|
status = i2c_transfer(client->adapter, msg, 2);
|
||||||
if (status == 2)
|
if (status == 2)
|
||||||
status = count;
|
status = count;
|
||||||
|
@ -191,6 +191,7 @@ static const struct i2c_device_id max6875_id[] = {
|
|||||||
{ "max6875", 0 },
|
{ "max6875", 0 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, max6875_id);
|
||||||
|
|
||||||
static struct i2c_driver max6875_driver = {
|
static struct i2c_driver max6875_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
|
@ -432,6 +432,7 @@ int of_irq_get_byname(struct device_node *dev, const char *name)
|
|||||||
|
|
||||||
return of_irq_get(dev, index);
|
return of_irq_get(dev, index);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(of_irq_get_byname);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* of_irq_count - Count the number of IRQs a node uses
|
* of_irq_count - Count the number of IRQs a node uses
|
||||||
|
18
include/dt-bindings/i2c/i2c.h
Normal file
18
include/dt-bindings/i2c/i2c.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* This header provides constants for I2C bindings
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 by Sang Engineering
|
||||||
|
* Copyright (C) 2015 by Renesas Electronics Corporation
|
||||||
|
*
|
||||||
|
* Wolfram Sang <wsa@sang-engineering.com>
|
||||||
|
*
|
||||||
|
* GPLv2 only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DT_BINDINGS_I2C_I2C_H
|
||||||
|
#define _DT_BINDINGS_I2C_I2C_H
|
||||||
|
|
||||||
|
#define I2C_TEN_BIT_ADDRESS (1 << 31)
|
||||||
|
#define I2C_OWN_SLAVE_ADDRESS (1 << 30)
|
||||||
|
|
||||||
|
#endif
|
@ -121,6 +121,9 @@ extern s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client,
|
|||||||
extern s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client,
|
extern s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client,
|
||||||
u8 command, u8 length,
|
u8 command, u8 length,
|
||||||
const u8 *values);
|
const u8 *values);
|
||||||
|
extern s32
|
||||||
|
i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client,
|
||||||
|
u8 command, u8 length, u8 *values);
|
||||||
#endif /* I2C */
|
#endif /* I2C */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -550,11 +553,12 @@ void i2c_lock_adapter(struct i2c_adapter *);
|
|||||||
void i2c_unlock_adapter(struct i2c_adapter *);
|
void i2c_unlock_adapter(struct i2c_adapter *);
|
||||||
|
|
||||||
/*flags for the client struct: */
|
/*flags for the client struct: */
|
||||||
#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
|
#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
|
||||||
#define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */
|
#define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */
|
||||||
/* Must equal I2C_M_TEN below */
|
/* Must equal I2C_M_TEN below */
|
||||||
#define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */
|
#define I2C_CLIENT_SLAVE 0x20 /* we are the slave */
|
||||||
#define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */
|
#define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */
|
||||||
|
#define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */
|
||||||
/* Must match I2C_M_STOP|IGNORE_NAK */
|
/* Must match I2C_M_STOP|IGNORE_NAK */
|
||||||
|
|
||||||
/* i2c adapter classes (bitmask) */
|
/* i2c adapter classes (bitmask) */
|
||||||
@ -638,6 +642,8 @@ extern struct i2c_client *of_find_i2c_device_by_node(struct device_node *node);
|
|||||||
/* must call put_device() when done with returned i2c_adapter device */
|
/* must call put_device() when done with returned i2c_adapter device */
|
||||||
extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node);
|
extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node);
|
||||||
|
|
||||||
|
/* must call i2c_put_adapter() when done with returned i2c_adapter device */
|
||||||
|
struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
|
static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
|
||||||
@ -649,6 +655,11 @@ static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node
|
|||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
#endif /* CONFIG_OF */
|
#endif /* CONFIG_OF */
|
||||||
|
|
||||||
#endif /* _LINUX_I2C_H */
|
#endif /* _LINUX_I2C_H */
|
||||||
|
44
include/linux/platform_data/i2c-mux-reg.h
Normal file
44
include/linux/platform_data/i2c-mux-reg.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* I2C multiplexer using a single register
|
||||||
|
*
|
||||||
|
* Copyright 2015 Freescale Semiconductor
|
||||||
|
* York Sun <yorksun@freescale.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LINUX_PLATFORM_DATA_I2C_MUX_REG_H
|
||||||
|
#define __LINUX_PLATFORM_DATA_I2C_MUX_REG_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct i2c_mux_reg_platform_data - Platform-dependent data for i2c-mux-reg
|
||||||
|
* @parent: Parent I2C bus adapter number
|
||||||
|
* @base_nr: Base I2C bus number to number adapters from or zero for dynamic
|
||||||
|
* @values: Array of value for each channel
|
||||||
|
* @n_values: Number of multiplexer channels
|
||||||
|
* @little_endian: Indicating if the register is in little endian
|
||||||
|
* @write_only: Reading the register is not allowed by hardware
|
||||||
|
* @classes: Optional I2C auto-detection classes
|
||||||
|
* @idle: Value to write to mux when idle
|
||||||
|
* @idle_in_use: indicate if idle value is in use
|
||||||
|
* @reg: Virtual address of the register to switch channel
|
||||||
|
* @reg_size: register size in bytes
|
||||||
|
*/
|
||||||
|
struct i2c_mux_reg_platform_data {
|
||||||
|
int parent;
|
||||||
|
int base_nr;
|
||||||
|
const unsigned int *values;
|
||||||
|
int n_values;
|
||||||
|
bool little_endian;
|
||||||
|
bool write_only;
|
||||||
|
const unsigned int *classes;
|
||||||
|
u32 idle;
|
||||||
|
bool idle_in_use;
|
||||||
|
void __iomem *reg;
|
||||||
|
resource_size_t reg_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __LINUX_PLATFORM_DATA_I2C_MUX_REG_H */
|
Loading…
Reference in New Issue
Block a user