Merge branch 'i2c/for-3.20' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: "Summary: - legacy PM code removed from the core, there were no users anymore (thanks to Lars-Peter Clausen) - new driver for Broadcom iProc - bigger driver updates for designware, rk3x, cadence, ocores - a bunch of smaller updates and bugfixes" * 'i2c/for-3.20' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (31 commits) i2c: ocores: rework clk code to handle NULL cookie i2c: designware-baytrail: another fixup for proper Kconfig dependencies i2c: fix reference to functionality constants definition i2c: iproc: Add Broadcom iProc I2C Driver i2c: designware-pci: update Intel copyright line i2c: ocores: add common clock support i2c: hix5hd2: add COMPILE_TEST i2c: clarify comments about the dev_released completion i2c: ocores: fix clock-frequency binding usage i2c: tegra: Maintain CPU endianness i2c: designware-baytrail: use proper Kconfig dependencies i2c: designware: Do not calculate SCL timing parameters needlessly i2c: do not try to load modules for of-registered devices i2c: designware: Add Intel Baytrail PMIC I2C bus support i2c: designware: Add i2c bus locking support of: i2c: Add i2c-mux-idle-disconnect DT property to PCA954x mux driver i2c: designware: use {readl|writel}_relaxed instead of readl/writel i2c: designware-pci: no need to provide clk_khz i2c: designware-pci: remove Moorestown support i2c: imx: whitespace and checkpatch cleanup ...
This commit is contained in:
commit
295324556c
23 changed files with 1139 additions and 363 deletions
37
Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt
Normal file
37
Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
Broadcom iProc I2C controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- compatible:
|
||||||
|
Must be "brcm,iproc-i2c"
|
||||||
|
|
||||||
|
- reg:
|
||||||
|
Define the base and range of the I/O address space that contain the iProc
|
||||||
|
I2C controller registers
|
||||||
|
|
||||||
|
- interrupts:
|
||||||
|
Should contain the I2C interrupt
|
||||||
|
|
||||||
|
- clock-frequency:
|
||||||
|
This is the I2C bus clock. Need to be either 100000 or 400000
|
||||||
|
|
||||||
|
- #address-cells:
|
||||||
|
Always 1 (for I2C addresses)
|
||||||
|
|
||||||
|
- #size-cells:
|
||||||
|
Always 0
|
||||||
|
|
||||||
|
Example:
|
||||||
|
i2c0: i2c@18008000 {
|
||||||
|
compatible = "brcm,iproc-i2c";
|
||||||
|
reg = <0x18008000 0x100>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>;
|
||||||
|
clock-frequency = <100000>;
|
||||||
|
|
||||||
|
codec: wm8750@1a {
|
||||||
|
compatible = "wlf,wm8750";
|
||||||
|
reg = <0x1a>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -16,6 +16,9 @@ Required Properties:
|
||||||
Optional Properties:
|
Optional Properties:
|
||||||
|
|
||||||
- reset-gpios: Reference to the GPIO connected to the reset input.
|
- reset-gpios: Reference to the GPIO connected to the reset input.
|
||||||
|
- i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all
|
||||||
|
children in idle state. This is necessary for example, if there are several
|
||||||
|
multiplexers on the bus and the devices behind them use same I2C addresses.
|
||||||
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
|
@ -4,16 +4,34 @@ Required properties:
|
||||||
- compatible : "opencores,i2c-ocores" or "aeroflexgaisler,i2cmst"
|
- compatible : "opencores,i2c-ocores" or "aeroflexgaisler,i2cmst"
|
||||||
- reg : bus address start and address range size of device
|
- reg : bus address start and address range size of device
|
||||||
- interrupts : interrupt number
|
- interrupts : interrupt number
|
||||||
- clock-frequency : frequency of bus clock in Hz
|
- clocks : handle to the controller clock; see the note below.
|
||||||
|
Mutually exclusive with opencores,ip-clock-frequency
|
||||||
|
- opencores,ip-clock-frequency: frequency of the controller clock in Hz;
|
||||||
|
see the note below. Mutually exclusive with clocks
|
||||||
- #address-cells : should be <1>
|
- #address-cells : should be <1>
|
||||||
- #size-cells : should be <0>
|
- #size-cells : should be <0>
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
|
- clock-frequency : frequency of bus clock in Hz; see the note below.
|
||||||
|
Defaults to 100 KHz when the property is not specified
|
||||||
- reg-shift : device register offsets are shifted by this value
|
- reg-shift : device register offsets are shifted by this value
|
||||||
- reg-io-width : io register width in bytes (1, 2 or 4)
|
- reg-io-width : io register width in bytes (1, 2 or 4)
|
||||||
- regstep : deprecated, use reg-shift above
|
- regstep : deprecated, use reg-shift above
|
||||||
|
|
||||||
Example:
|
Note
|
||||||
|
clock-frequency property is meant to control the bus frequency for i2c bus
|
||||||
|
drivers, but it was incorrectly used to specify i2c controller input clock
|
||||||
|
frequency. So the following rules are set to fix this situation:
|
||||||
|
- if clock-frequency is present and neither opencores,ip-clock-frequency nor
|
||||||
|
clocks are, then clock-frequency specifies i2c controller clock frequency.
|
||||||
|
This is to keep backwards compatibility with setups using old DTB. i2c bus
|
||||||
|
frequency is fixed at 100 KHz.
|
||||||
|
- if clocks is present it specifies i2c controller clock. clock-frequency
|
||||||
|
property specifies i2c bus frequency.
|
||||||
|
- if opencores,ip-clock-frequency is present it specifies i2c controller
|
||||||
|
clock frequency. clock-frequency property specifies i2c bus frequency.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
i2c0: ocores@a0000000 {
|
i2c0: ocores@a0000000 {
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
|
@ -21,7 +39,25 @@ Example:
|
||||||
compatible = "opencores,i2c-ocores";
|
compatible = "opencores,i2c-ocores";
|
||||||
reg = <0xa0000000 0x8>;
|
reg = <0xa0000000 0x8>;
|
||||||
interrupts = <10>;
|
interrupts = <10>;
|
||||||
clock-frequency = <20000000>;
|
opencores,ip-clock-frequency = <20000000>;
|
||||||
|
|
||||||
|
reg-shift = <0>; /* 8 bit registers */
|
||||||
|
reg-io-width = <1>; /* 8 bit read/write */
|
||||||
|
|
||||||
|
dummy@60 {
|
||||||
|
compatible = "dummy";
|
||||||
|
reg = <0x60>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
or
|
||||||
|
i2c0: ocores@a0000000 {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
compatible = "opencores,i2c-ocores";
|
||||||
|
reg = <0xa0000000 0x8>;
|
||||||
|
interrupts = <10>;
|
||||||
|
clocks = <&osc>;
|
||||||
|
clock-frequency = <400000>; /* i2c bus frequency 400 KHz */
|
||||||
|
|
||||||
reg-shift = <0>; /* 8 bit registers */
|
reg-shift = <0>; /* 8 bit registers */
|
||||||
reg-io-width = <1>; /* 8 bit read/write */
|
reg-io-width = <1>; /* 8 bit read/write */
|
||||||
|
|
|
@ -21,6 +21,17 @@ Required on RK3066, RK3188 :
|
||||||
Optional properties :
|
Optional properties :
|
||||||
|
|
||||||
- clock-frequency : SCL frequency to use (in Hz). If omitted, 100kHz is used.
|
- clock-frequency : SCL frequency to use (in Hz). If omitted, 100kHz is used.
|
||||||
|
- i2c-scl-rising-time-ns : Number of nanoseconds the SCL signal takes to rise
|
||||||
|
(t(r) in I2C specification). If not specified this is assumed to be
|
||||||
|
the maximum the specification allows(1000 ns for Standard-mode,
|
||||||
|
300 ns for Fast-mode) which might cause slightly slower communication.
|
||||||
|
- i2c-scl-falling-time-ns : Number of nanoseconds the SCL signal takes to fall
|
||||||
|
(t(f) in the I2C specification). If not specified this is assumed to
|
||||||
|
be the maximum the specification allows (300 ns) which might cause
|
||||||
|
slightly slower communication.
|
||||||
|
- i2c-sda-falling-time-ns : Number of nanoseconds the SDA signal takes to fall
|
||||||
|
(t(f) in the I2C specification). If not specified we'll use the SCL
|
||||||
|
value since they are the same in nearly all cases.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -39,4 +50,7 @@ i2c0: i2c@2002d000 {
|
||||||
|
|
||||||
clock-names = "i2c";
|
clock-names = "i2c";
|
||||||
clocks = <&cru PCLK_I2C0>;
|
clocks = <&cru PCLK_I2C0>;
|
||||||
|
|
||||||
|
i2c-scl-rising-time-ns = <800>;
|
||||||
|
i2c-scl-falling-time-ns = <100>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,9 +61,8 @@ fsl,sgtl5000 SGTL5000: Ultra Low-Power Audio Codec
|
||||||
gmt,g751 G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
|
gmt,g751 G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
|
||||||
infineon,slb9635tt Infineon SLB9635 (Soft-) I2C TPM (old protocol, max 100khz)
|
infineon,slb9635tt Infineon SLB9635 (Soft-) I2C TPM (old protocol, max 100khz)
|
||||||
infineon,slb9645tt Infineon SLB9645 I2C TPM (new protocol, max 400khz)
|
infineon,slb9645tt Infineon SLB9645 I2C TPM (new protocol, max 400khz)
|
||||||
isl,isl12057 Intersil ISL12057 I2C RTC Chip
|
isil,isl12057 Intersil ISL12057 I2C RTC Chip
|
||||||
isil,isl29028 (deprecated, use isl)
|
isil,isl29028 Intersil ISL29028 Ambient Light and Proximity Sensor
|
||||||
isl,isl29028 Intersil ISL29028 Ambient Light and Proximity Sensor
|
|
||||||
maxim,ds1050 5 Bit Programmable, Pulse-Width Modulator
|
maxim,ds1050 5 Bit Programmable, Pulse-Width Modulator
|
||||||
maxim,max1237 Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs
|
maxim,max1237 Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs
|
||||||
maxim,max6625 9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface
|
maxim,max6625 9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface
|
||||||
|
|
|
@ -12,7 +12,7 @@ FUNCTIONALITY CONSTANTS
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
For the most up-to-date list of functionality constants, please check
|
For the most up-to-date list of functionality constants, please check
|
||||||
<linux/i2c.h>!
|
<uapi/linux/i2c.h>!
|
||||||
|
|
||||||
I2C_FUNC_I2C Plain i2c-level commands (Pure SMBus
|
I2C_FUNC_I2C Plain i2c-level commands (Pure SMBus
|
||||||
adapters typically can not do these)
|
adapters typically can not do these)
|
||||||
|
|
|
@ -79,7 +79,7 @@ config I2C_AMD8111
|
||||||
|
|
||||||
config I2C_HIX5HD2
|
config I2C_HIX5HD2
|
||||||
tristate "Hix5hd2 high-speed I2C driver"
|
tristate "Hix5hd2 high-speed I2C driver"
|
||||||
depends on ARCH_HIX5HD2
|
depends on ARCH_HIX5HD2 || COMPILE_TEST
|
||||||
help
|
help
|
||||||
Say Y here to include support for high-speed I2C controller in the
|
Say Y here to include support for high-speed I2C controller in the
|
||||||
Hisilicon based hix5hd2 SoCs.
|
Hisilicon based hix5hd2 SoCs.
|
||||||
|
@ -372,6 +372,16 @@ config I2C_BCM2835
|
||||||
This support is also available as a module. If so, the module
|
This support is also available as a module. If so, the module
|
||||||
will be called i2c-bcm2835.
|
will be called i2c-bcm2835.
|
||||||
|
|
||||||
|
config I2C_BCM_IPROC
|
||||||
|
tristate "Broadcom iProc I2C controller"
|
||||||
|
depends on ARCH_BCM_IPROC || COMPILE_TEST
|
||||||
|
default ARCH_BCM_IPROC
|
||||||
|
help
|
||||||
|
If you say yes to this option, support will be included for the
|
||||||
|
Broadcom iProc I2C controller.
|
||||||
|
|
||||||
|
If you don't know what to do here, say N.
|
||||||
|
|
||||||
config I2C_BCM_KONA
|
config I2C_BCM_KONA
|
||||||
tristate "BCM Kona I2C adapter"
|
tristate "BCM Kona I2C adapter"
|
||||||
depends on ARCH_BCM_MOBILE
|
depends on ARCH_BCM_MOBILE
|
||||||
|
@ -465,6 +475,16 @@ config I2C_DESIGNWARE_PCI
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called i2c-designware-pci.
|
will be called i2c-designware-pci.
|
||||||
|
|
||||||
|
config I2C_DESIGNWARE_BAYTRAIL
|
||||||
|
bool "Intel Baytrail I2C semaphore support"
|
||||||
|
depends on I2C_DESIGNWARE_PLATFORM && IOSF_MBI=y && ACPI
|
||||||
|
help
|
||||||
|
This driver enables managed host access to the PMIC I2C bus on select
|
||||||
|
Intel BayTrail platforms using the X-Powers AXP288 PMIC. It allows
|
||||||
|
the host to request uninterrupted access to the PMIC's I2C bus from
|
||||||
|
the platform firmware controlling it. You should say Y if running on
|
||||||
|
a BayTrail system using the AXP288.
|
||||||
|
|
||||||
config I2C_EFM32
|
config I2C_EFM32
|
||||||
tristate "EFM32 I2C controller"
|
tristate "EFM32 I2C controller"
|
||||||
depends on ARCH_EFM32 || COMPILE_TEST
|
depends on ARCH_EFM32 || COMPILE_TEST
|
||||||
|
|
|
@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o
|
||||||
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
|
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
|
||||||
obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o
|
obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o
|
||||||
obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
|
obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
|
||||||
|
obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o
|
||||||
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
|
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
|
||||||
obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o
|
obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o
|
||||||
obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o
|
obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o
|
||||||
|
@ -41,6 +42,7 @@ obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
|
||||||
obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o
|
obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o
|
||||||
obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
|
obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
|
||||||
i2c-designware-platform-objs := i2c-designware-platdrv.o
|
i2c-designware-platform-objs := i2c-designware-platdrv.o
|
||||||
|
i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-baytrail.o
|
||||||
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
|
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
|
||||||
i2c-designware-pci-objs := i2c-designware-pcidrv.o
|
i2c-designware-pci-objs := i2c-designware-pcidrv.o
|
||||||
obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o
|
obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o
|
||||||
|
|
461
drivers/i2c/busses/i2c-bcm-iproc.c
Normal file
461
drivers/i2c/busses/i2c-bcm-iproc.c
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Broadcom Corporation
|
||||||
|
*
|
||||||
|
* 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 version 2.
|
||||||
|
*
|
||||||
|
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||||
|
* kind, whether express or implied; without even the implied warranty
|
||||||
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#define CFG_OFFSET 0x00
|
||||||
|
#define CFG_RESET_SHIFT 31
|
||||||
|
#define CFG_EN_SHIFT 30
|
||||||
|
#define CFG_M_RETRY_CNT_SHIFT 16
|
||||||
|
#define CFG_M_RETRY_CNT_MASK 0x0f
|
||||||
|
|
||||||
|
#define TIM_CFG_OFFSET 0x04
|
||||||
|
#define TIM_CFG_MODE_400_SHIFT 31
|
||||||
|
|
||||||
|
#define M_FIFO_CTRL_OFFSET 0x0c
|
||||||
|
#define M_FIFO_RX_FLUSH_SHIFT 31
|
||||||
|
#define M_FIFO_TX_FLUSH_SHIFT 30
|
||||||
|
#define M_FIFO_RX_CNT_SHIFT 16
|
||||||
|
#define M_FIFO_RX_CNT_MASK 0x7f
|
||||||
|
#define M_FIFO_RX_THLD_SHIFT 8
|
||||||
|
#define M_FIFO_RX_THLD_MASK 0x3f
|
||||||
|
|
||||||
|
#define M_CMD_OFFSET 0x30
|
||||||
|
#define M_CMD_START_BUSY_SHIFT 31
|
||||||
|
#define M_CMD_STATUS_SHIFT 25
|
||||||
|
#define M_CMD_STATUS_MASK 0x07
|
||||||
|
#define M_CMD_STATUS_SUCCESS 0x0
|
||||||
|
#define M_CMD_STATUS_LOST_ARB 0x1
|
||||||
|
#define M_CMD_STATUS_NACK_ADDR 0x2
|
||||||
|
#define M_CMD_STATUS_NACK_DATA 0x3
|
||||||
|
#define M_CMD_STATUS_TIMEOUT 0x4
|
||||||
|
#define M_CMD_PROTOCOL_SHIFT 9
|
||||||
|
#define M_CMD_PROTOCOL_MASK 0xf
|
||||||
|
#define M_CMD_PROTOCOL_BLK_WR 0x7
|
||||||
|
#define M_CMD_PROTOCOL_BLK_RD 0x8
|
||||||
|
#define M_CMD_PEC_SHIFT 8
|
||||||
|
#define M_CMD_RD_CNT_SHIFT 0
|
||||||
|
#define M_CMD_RD_CNT_MASK 0xff
|
||||||
|
|
||||||
|
#define IE_OFFSET 0x38
|
||||||
|
#define IE_M_RX_FIFO_FULL_SHIFT 31
|
||||||
|
#define IE_M_RX_THLD_SHIFT 30
|
||||||
|
#define IE_M_START_BUSY_SHIFT 28
|
||||||
|
|
||||||
|
#define IS_OFFSET 0x3c
|
||||||
|
#define IS_M_RX_FIFO_FULL_SHIFT 31
|
||||||
|
#define IS_M_RX_THLD_SHIFT 30
|
||||||
|
#define IS_M_START_BUSY_SHIFT 28
|
||||||
|
|
||||||
|
#define M_TX_OFFSET 0x40
|
||||||
|
#define M_TX_WR_STATUS_SHIFT 31
|
||||||
|
#define M_TX_DATA_SHIFT 0
|
||||||
|
#define M_TX_DATA_MASK 0xff
|
||||||
|
|
||||||
|
#define M_RX_OFFSET 0x44
|
||||||
|
#define M_RX_STATUS_SHIFT 30
|
||||||
|
#define M_RX_STATUS_MASK 0x03
|
||||||
|
#define M_RX_PEC_ERR_SHIFT 29
|
||||||
|
#define M_RX_DATA_SHIFT 0
|
||||||
|
#define M_RX_DATA_MASK 0xff
|
||||||
|
|
||||||
|
#define I2C_TIMEOUT_MESC 100
|
||||||
|
#define M_TX_RX_FIFO_SIZE 64
|
||||||
|
|
||||||
|
enum bus_speed_index {
|
||||||
|
I2C_SPD_100K = 0,
|
||||||
|
I2C_SPD_400K,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bcm_iproc_i2c_dev {
|
||||||
|
struct device *device;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
void __iomem *base;
|
||||||
|
|
||||||
|
struct i2c_adapter adapter;
|
||||||
|
|
||||||
|
struct completion done;
|
||||||
|
int xfer_is_done;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Can be expanded in the future if more interrupt status bits are utilized
|
||||||
|
*/
|
||||||
|
#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT)
|
||||||
|
|
||||||
|
static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct bcm_iproc_i2c_dev *iproc_i2c = data;
|
||||||
|
u32 status = readl(iproc_i2c->base + IS_OFFSET);
|
||||||
|
|
||||||
|
status &= ISR_MASK;
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
writel(status, iproc_i2c->base + IS_OFFSET);
|
||||||
|
iproc_i2c->xfer_is_done = 1;
|
||||||
|
complete_all(&iproc_i2c->done);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||||
|
struct i2c_msg *msg)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = readl(iproc_i2c->base + M_CMD_OFFSET);
|
||||||
|
val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK;
|
||||||
|
|
||||||
|
switch (val) {
|
||||||
|
case M_CMD_STATUS_SUCCESS:
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case M_CMD_STATUS_LOST_ARB:
|
||||||
|
dev_dbg(iproc_i2c->device, "lost bus arbitration\n");
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
|
case M_CMD_STATUS_NACK_ADDR:
|
||||||
|
dev_dbg(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr);
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
case M_CMD_STATUS_NACK_DATA:
|
||||||
|
dev_dbg(iproc_i2c->device, "NAK data\n");
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
case M_CMD_STATUS_TIMEOUT:
|
||||||
|
dev_dbg(iproc_i2c->device, "bus timeout\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||||
|
struct i2c_msg *msg)
|
||||||
|
{
|
||||||
|
int ret, i;
|
||||||
|
u8 addr;
|
||||||
|
u32 val;
|
||||||
|
unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC);
|
||||||
|
|
||||||
|
/* need to reserve one byte in the FIFO for the slave address */
|
||||||
|
if (msg->len > M_TX_RX_FIFO_SIZE - 1) {
|
||||||
|
dev_err(iproc_i2c->device,
|
||||||
|
"only support data length up to %u bytes\n",
|
||||||
|
M_TX_RX_FIFO_SIZE - 1);
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if bus is busy */
|
||||||
|
if (!!(readl(iproc_i2c->base + M_CMD_OFFSET) &
|
||||||
|
BIT(M_CMD_START_BUSY_SHIFT))) {
|
||||||
|
dev_warn(iproc_i2c->device, "bus is busy\n");
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* format and load slave address into the TX FIFO */
|
||||||
|
addr = msg->addr << 1 | (msg->flags & I2C_M_RD ? 1 : 0);
|
||||||
|
writel(addr, iproc_i2c->base + M_TX_OFFSET);
|
||||||
|
|
||||||
|
/* for a write transaction, load data into the TX FIFO */
|
||||||
|
if (!(msg->flags & I2C_M_RD)) {
|
||||||
|
for (i = 0; i < msg->len; i++) {
|
||||||
|
val = msg->buf[i];
|
||||||
|
|
||||||
|
/* mark the last byte */
|
||||||
|
if (i == msg->len - 1)
|
||||||
|
val |= 1 << M_TX_WR_STATUS_SHIFT;
|
||||||
|
|
||||||
|
writel(val, iproc_i2c->base + M_TX_OFFSET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mark as incomplete before starting the transaction */
|
||||||
|
reinit_completion(&iproc_i2c->done);
|
||||||
|
iproc_i2c->xfer_is_done = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable the "start busy" interrupt, which will be triggered after the
|
||||||
|
* transaction is done, i.e., the internal start_busy bit, transitions
|
||||||
|
* from 1 to 0.
|
||||||
|
*/
|
||||||
|
writel(1 << IE_M_START_BUSY_SHIFT, iproc_i2c->base + IE_OFFSET);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we can activate the transfer. For a read operation, specify the
|
||||||
|
* number of bytes to read
|
||||||
|
*/
|
||||||
|
val = 1 << M_CMD_START_BUSY_SHIFT;
|
||||||
|
if (msg->flags & I2C_M_RD) {
|
||||||
|
val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) |
|
||||||
|
(msg->len << M_CMD_RD_CNT_SHIFT);
|
||||||
|
} else {
|
||||||
|
val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT);
|
||||||
|
}
|
||||||
|
writel(val, iproc_i2c->base + M_CMD_OFFSET);
|
||||||
|
|
||||||
|
time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left);
|
||||||
|
|
||||||
|
/* disable all interrupts */
|
||||||
|
writel(0, iproc_i2c->base + IE_OFFSET);
|
||||||
|
/* read it back to flush the write */
|
||||||
|
readl(iproc_i2c->base + IE_OFFSET);
|
||||||
|
|
||||||
|
/* make sure the interrupt handler isn't running */
|
||||||
|
synchronize_irq(iproc_i2c->irq);
|
||||||
|
|
||||||
|
if (!time_left && !iproc_i2c->xfer_is_done) {
|
||||||
|
dev_err(iproc_i2c->device, "transaction timed out\n");
|
||||||
|
|
||||||
|
/* flush FIFOs */
|
||||||
|
val = (1 << M_FIFO_RX_FLUSH_SHIFT) |
|
||||||
|
(1 << M_FIFO_TX_FLUSH_SHIFT);
|
||||||
|
writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bcm_iproc_i2c_check_status(iproc_i2c, msg);
|
||||||
|
if (ret) {
|
||||||
|
/* flush both TX/RX FIFOs */
|
||||||
|
val = (1 << M_FIFO_RX_FLUSH_SHIFT) |
|
||||||
|
(1 << M_FIFO_TX_FLUSH_SHIFT);
|
||||||
|
writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For a read operation, we now need to load the data from FIFO
|
||||||
|
* into the memory buffer
|
||||||
|
*/
|
||||||
|
if (msg->flags & I2C_M_RD) {
|
||||||
|
for (i = 0; i < msg->len; i++) {
|
||||||
|
msg->buf[i] = (readl(iproc_i2c->base + M_RX_OFFSET) >>
|
||||||
|
M_RX_DATA_SHIFT) & M_RX_DATA_MASK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
|
||||||
|
struct i2c_msg msgs[], int num)
|
||||||
|
{
|
||||||
|
struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter);
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
/* go through all messages */
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]);
|
||||||
|
if (ret) {
|
||||||
|
dev_dbg(iproc_i2c->device, "xfer failed\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_algorithm bcm_iproc_algo = {
|
||||||
|
.master_xfer = bcm_iproc_i2c_xfer,
|
||||||
|
.functionality = bcm_iproc_i2c_functionality,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c)
|
||||||
|
{
|
||||||
|
unsigned int bus_speed;
|
||||||
|
u32 val;
|
||||||
|
int ret = of_property_read_u32(iproc_i2c->device->of_node,
|
||||||
|
"clock-frequency", &bus_speed);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_info(iproc_i2c->device,
|
||||||
|
"unable to interpret clock-frequency DT property\n");
|
||||||
|
bus_speed = 100000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bus_speed < 100000) {
|
||||||
|
dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n",
|
||||||
|
bus_speed);
|
||||||
|
dev_err(iproc_i2c->device,
|
||||||
|
"valid speeds are 100khz and 400khz\n");
|
||||||
|
return -EINVAL;
|
||||||
|
} else if (bus_speed < 400000) {
|
||||||
|
bus_speed = 100000;
|
||||||
|
} else {
|
||||||
|
bus_speed = 400000;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = readl(iproc_i2c->base + TIM_CFG_OFFSET);
|
||||||
|
val &= ~(1 << TIM_CFG_MODE_400_SHIFT);
|
||||||
|
val |= (bus_speed == 400000) << TIM_CFG_MODE_400_SHIFT;
|
||||||
|
writel(val, iproc_i2c->base + TIM_CFG_OFFSET);
|
||||||
|
|
||||||
|
dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
/* put controller in reset */
|
||||||
|
val = readl(iproc_i2c->base + CFG_OFFSET);
|
||||||
|
val |= 1 << CFG_RESET_SHIFT;
|
||||||
|
val &= ~(1 << CFG_EN_SHIFT);
|
||||||
|
writel(val, iproc_i2c->base + CFG_OFFSET);
|
||||||
|
|
||||||
|
/* wait 100 usec per spec */
|
||||||
|
udelay(100);
|
||||||
|
|
||||||
|
/* bring controller out of reset */
|
||||||
|
val &= ~(1 << CFG_RESET_SHIFT);
|
||||||
|
writel(val, iproc_i2c->base + CFG_OFFSET);
|
||||||
|
|
||||||
|
/* flush TX/RX FIFOs and set RX FIFO threshold to zero */
|
||||||
|
val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT);
|
||||||
|
writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
|
||||||
|
|
||||||
|
/* disable all interrupts */
|
||||||
|
writel(0, iproc_i2c->base + IE_OFFSET);
|
||||||
|
|
||||||
|
/* clear all pending interrupts */
|
||||||
|
writel(0xffffffff, iproc_i2c->base + IS_OFFSET);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||||
|
bool enable)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = readl(iproc_i2c->base + CFG_OFFSET);
|
||||||
|
if (enable)
|
||||||
|
val |= BIT(CFG_EN_SHIFT);
|
||||||
|
else
|
||||||
|
val &= ~BIT(CFG_EN_SHIFT);
|
||||||
|
writel(val, iproc_i2c->base + CFG_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_iproc_i2c_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int irq, ret = 0;
|
||||||
|
struct bcm_iproc_i2c_dev *iproc_i2c;
|
||||||
|
struct i2c_adapter *adap;
|
||||||
|
struct resource *res;
|
||||||
|
|
||||||
|
iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!iproc_i2c)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, iproc_i2c);
|
||||||
|
iproc_i2c->device = &pdev->dev;
|
||||||
|
init_completion(&iproc_i2c->done);
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
iproc_i2c->base = devm_ioremap_resource(iproc_i2c->device, res);
|
||||||
|
if (IS_ERR(iproc_i2c->base))
|
||||||
|
return PTR_ERR(iproc_i2c->base);
|
||||||
|
|
||||||
|
ret = bcm_iproc_i2c_init(iproc_i2c);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = bcm_iproc_i2c_cfg_speed(iproc_i2c);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq <= 0) {
|
||||||
|
dev_err(iproc_i2c->device, "no irq resource\n");
|
||||||
|
return irq;
|
||||||
|
}
|
||||||
|
iproc_i2c->irq = irq;
|
||||||
|
|
||||||
|
ret = devm_request_irq(iproc_i2c->device, irq, bcm_iproc_i2c_isr, 0,
|
||||||
|
pdev->name, iproc_i2c);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(iproc_i2c->device, "unable to request irq %i\n", irq);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bcm_iproc_i2c_enable_disable(iproc_i2c, true);
|
||||||
|
|
||||||
|
adap = &iproc_i2c->adapter;
|
||||||
|
i2c_set_adapdata(adap, iproc_i2c);
|
||||||
|
strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name));
|
||||||
|
adap->algo = &bcm_iproc_algo;
|
||||||
|
adap->dev.parent = &pdev->dev;
|
||||||
|
adap->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
|
ret = i2c_add_adapter(adap);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(iproc_i2c->device, "failed to add adapter\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_iproc_i2c_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
/* make sure there's no pending interrupt when we remove the adapter */
|
||||||
|
writel(0, iproc_i2c->base + IE_OFFSET);
|
||||||
|
readl(iproc_i2c->base + IE_OFFSET);
|
||||||
|
synchronize_irq(iproc_i2c->irq);
|
||||||
|
|
||||||
|
i2c_del_adapter(&iproc_i2c->adapter);
|
||||||
|
bcm_iproc_i2c_enable_disable(iproc_i2c, false);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id bcm_iproc_i2c_of_match[] = {
|
||||||
|
{ .compatible = "brcm,iproc-i2c" },
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver bcm_iproc_i2c_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "bcm-iproc-i2c",
|
||||||
|
.of_match_table = bcm_iproc_i2c_of_match,
|
||||||
|
},
|
||||||
|
.probe = bcm_iproc_i2c_probe,
|
||||||
|
.remove = bcm_iproc_i2c_remove,
|
||||||
|
};
|
||||||
|
module_platform_driver(bcm_iproc_i2c_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
|
||||||
|
MODULE_DESCRIPTION("Broadcom iProc I2C Driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -128,6 +128,7 @@
|
||||||
* @suspended: Flag holding the device's PM status
|
* @suspended: Flag holding the device's PM status
|
||||||
* @send_count: Number of bytes still expected to send
|
* @send_count: Number of bytes still expected to send
|
||||||
* @recv_count: Number of bytes still expected to receive
|
* @recv_count: Number of bytes still expected to receive
|
||||||
|
* @curr_recv_count: Number of bytes to be received in current transfer
|
||||||
* @irq: IRQ number
|
* @irq: IRQ number
|
||||||
* @input_clk: Input clock to I2C controller
|
* @input_clk: Input clock to I2C controller
|
||||||
* @i2c_clk: Maximum I2C clock speed
|
* @i2c_clk: Maximum I2C clock speed
|
||||||
|
@ -146,6 +147,7 @@ struct cdns_i2c {
|
||||||
u8 suspended;
|
u8 suspended;
|
||||||
unsigned int send_count;
|
unsigned int send_count;
|
||||||
unsigned int recv_count;
|
unsigned int recv_count;
|
||||||
|
unsigned int curr_recv_count;
|
||||||
int irq;
|
int irq;
|
||||||
unsigned long input_clk;
|
unsigned long input_clk;
|
||||||
unsigned int i2c_clk;
|
unsigned int i2c_clk;
|
||||||
|
@ -182,14 +184,15 @@ static void cdns_i2c_clear_bus_hold(struct cdns_i2c *id)
|
||||||
*/
|
*/
|
||||||
static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
|
static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
|
||||||
{
|
{
|
||||||
unsigned int isr_status, avail_bytes;
|
unsigned int isr_status, avail_bytes, updatetx;
|
||||||
unsigned int bytes_to_recv, bytes_to_send;
|
unsigned int bytes_to_send;
|
||||||
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;
|
||||||
irqreturn_t status = IRQ_NONE;
|
irqreturn_t status = IRQ_NONE;
|
||||||
|
|
||||||
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
|
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
|
||||||
|
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
|
||||||
|
|
||||||
/* Handling nack and arbitration lost interrupt */
|
/* Handling nack and arbitration lost interrupt */
|
||||||
if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_ARB_LOST)) {
|
if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_ARB_LOST)) {
|
||||||
|
@ -197,89 +200,112 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
|
||||||
status = IRQ_HANDLED;
|
status = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handling Data interrupt */
|
/*
|
||||||
if ((isr_status & CDNS_I2C_IXR_DATA) &&
|
* Check if transfer size register needs to be updated again for a
|
||||||
(id->recv_count >= CDNS_I2C_DATA_INTR_DEPTH)) {
|
* large data receive operation.
|
||||||
/* Always read data interrupt threshold bytes */
|
*/
|
||||||
bytes_to_recv = CDNS_I2C_DATA_INTR_DEPTH;
|
updatetx = 0;
|
||||||
id->recv_count -= CDNS_I2C_DATA_INTR_DEPTH;
|
if (id->recv_count > id->curr_recv_count)
|
||||||
avail_bytes = cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
|
updatetx = 1;
|
||||||
|
|
||||||
/*
|
/* When receiving, handle data interrupt and completion interrupt */
|
||||||
* if the tranfer size register value is zero, then
|
if (id->p_recv_buf &&
|
||||||
* check for the remaining bytes and update the
|
((isr_status & CDNS_I2C_IXR_COMP) ||
|
||||||
* transfer size register.
|
(isr_status & CDNS_I2C_IXR_DATA))) {
|
||||||
*/
|
/* Read data if receive data valid is set */
|
||||||
if (!avail_bytes) {
|
while (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) &
|
||||||
if (id->recv_count > CDNS_I2C_TRANSFER_SIZE)
|
CDNS_I2C_SR_RXDV) {
|
||||||
cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
|
/*
|
||||||
CDNS_I2C_XFER_SIZE_OFFSET);
|
* Clear hold bit that was set for FIFO control if
|
||||||
else
|
* RX data left is less than FIFO depth, unless
|
||||||
cdns_i2c_writereg(id->recv_count,
|
* repeated start is selected.
|
||||||
CDNS_I2C_XFER_SIZE_OFFSET);
|
*/
|
||||||
}
|
if ((id->recv_count < CDNS_I2C_FIFO_DEPTH) &&
|
||||||
|
!id->bus_hold_flag)
|
||||||
|
cdns_i2c_clear_bus_hold(id);
|
||||||
|
|
||||||
/* Process the data received */
|
|
||||||
while (bytes_to_recv--)
|
|
||||||
*(id->p_recv_buf)++ =
|
*(id->p_recv_buf)++ =
|
||||||
cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
|
cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
|
||||||
|
id->recv_count--;
|
||||||
|
id->curr_recv_count--;
|
||||||
|
|
||||||
if (!id->bus_hold_flag &&
|
if (updatetx &&
|
||||||
(id->recv_count <= CDNS_I2C_FIFO_DEPTH))
|
(id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1))
|
||||||
cdns_i2c_clear_bus_hold(id);
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The controller sends NACK to the slave when transfer size
|
||||||
|
* register reaches zero without considering the HOLD bit.
|
||||||
|
* This workaround is implemented for large data transfers to
|
||||||
|
* maintain transfer size non-zero while performing a large
|
||||||
|
* receive operation.
|
||||||
|
*/
|
||||||
|
if (updatetx &&
|
||||||
|
(id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1)) {
|
||||||
|
/* wait while fifo is full */
|
||||||
|
while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) !=
|
||||||
|
(id->curr_recv_count - CDNS_I2C_FIFO_DEPTH))
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check number of bytes to be received against maximum
|
||||||
|
* transfer size and update register accordingly.
|
||||||
|
*/
|
||||||
|
if (((int)(id->recv_count) - CDNS_I2C_FIFO_DEPTH) >
|
||||||
|
CDNS_I2C_TRANSFER_SIZE) {
|
||||||
|
cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
|
||||||
|
CDNS_I2C_XFER_SIZE_OFFSET);
|
||||||
|
id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE +
|
||||||
|
CDNS_I2C_FIFO_DEPTH;
|
||||||
|
} else {
|
||||||
|
cdns_i2c_writereg(id->recv_count -
|
||||||
|
CDNS_I2C_FIFO_DEPTH,
|
||||||
|
CDNS_I2C_XFER_SIZE_OFFSET);
|
||||||
|
id->curr_recv_count = id->recv_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear hold (if not repeated start) and signal completion */
|
||||||
|
if ((isr_status & CDNS_I2C_IXR_COMP) && !id->recv_count) {
|
||||||
|
if (!id->bus_hold_flag)
|
||||||
|
cdns_i2c_clear_bus_hold(id);
|
||||||
|
done_flag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
status = IRQ_HANDLED;
|
status = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handling Transfer Complete interrupt */
|
/* When sending, handle transfer complete interrupt */
|
||||||
if (isr_status & CDNS_I2C_IXR_COMP) {
|
if ((isr_status & CDNS_I2C_IXR_COMP) && !id->p_recv_buf) {
|
||||||
if (!id->p_recv_buf) {
|
/*
|
||||||
/*
|
* If there is more data to be sent, calculate the
|
||||||
* If the device is sending data If there is further
|
* space available in FIFO and fill with that many bytes.
|
||||||
* data to be sent. Calculate the available space
|
*/
|
||||||
* in FIFO and fill the FIFO with that many bytes.
|
if (id->send_count) {
|
||||||
*/
|
avail_bytes = CDNS_I2C_FIFO_DEPTH -
|
||||||
if (id->send_count) {
|
cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
|
||||||
avail_bytes = CDNS_I2C_FIFO_DEPTH -
|
if (id->send_count > avail_bytes)
|
||||||
cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
|
bytes_to_send = avail_bytes;
|
||||||
if (id->send_count > avail_bytes)
|
else
|
||||||
bytes_to_send = avail_bytes;
|
bytes_to_send = id->send_count;
|
||||||
else
|
|
||||||
bytes_to_send = id->send_count;
|
|
||||||
|
|
||||||
while (bytes_to_send--) {
|
while (bytes_to_send--) {
|
||||||
cdns_i2c_writereg(
|
cdns_i2c_writereg(
|
||||||
(*(id->p_send_buf)++),
|
(*(id->p_send_buf)++),
|
||||||
CDNS_I2C_DATA_OFFSET);
|
CDNS_I2C_DATA_OFFSET);
|
||||||
id->send_count--;
|
id->send_count--;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Signal the completion of transaction and
|
|
||||||
* clear the hold bus bit if there are no
|
|
||||||
* further messages to be processed.
|
|
||||||
*/
|
|
||||||
done_flag = 1;
|
|
||||||
}
|
}
|
||||||
if (!id->send_count && !id->bus_hold_flag)
|
|
||||||
cdns_i2c_clear_bus_hold(id);
|
|
||||||
} else {
|
} else {
|
||||||
if (!id->bus_hold_flag)
|
|
||||||
cdns_i2c_clear_bus_hold(id);
|
|
||||||
/*
|
/*
|
||||||
* If the device is receiving data, then signal
|
* Signal the completion of transaction and
|
||||||
* the completion of transaction and read the data
|
* clear the hold bus bit if there are no
|
||||||
* present in the FIFO. Signal the completion of
|
* further messages to be processed.
|
||||||
* transaction.
|
|
||||||
*/
|
*/
|
||||||
while (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) &
|
|
||||||
CDNS_I2C_SR_RXDV) {
|
|
||||||
*(id->p_recv_buf)++ =
|
|
||||||
cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
|
|
||||||
id->recv_count--;
|
|
||||||
}
|
|
||||||
done_flag = 1;
|
done_flag = 1;
|
||||||
}
|
}
|
||||||
|
if (!id->send_count && !id->bus_hold_flag)
|
||||||
|
cdns_i2c_clear_bus_hold(id);
|
||||||
|
|
||||||
status = IRQ_HANDLED;
|
status = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -289,8 +315,6 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
|
||||||
if (id->err_status)
|
if (id->err_status)
|
||||||
status = IRQ_HANDLED;
|
status = IRQ_HANDLED;
|
||||||
|
|
||||||
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
|
|
||||||
|
|
||||||
if (done_flag)
|
if (done_flag)
|
||||||
complete(&id->xfer_done);
|
complete(&id->xfer_done);
|
||||||
|
|
||||||
|
@ -316,6 +340,8 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
|
||||||
if (id->p_msg->flags & I2C_M_RECV_LEN)
|
if (id->p_msg->flags & I2C_M_RECV_LEN)
|
||||||
id->recv_count = I2C_SMBUS_BLOCK_MAX + 1;
|
id->recv_count = I2C_SMBUS_BLOCK_MAX + 1;
|
||||||
|
|
||||||
|
id->curr_recv_count = id->recv_count;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for the message size against FIFO depth and set the
|
* Check for the message size against FIFO depth and set the
|
||||||
* 'hold bus' bit if it is greater than FIFO depth.
|
* 'hold bus' bit if it is greater than FIFO depth.
|
||||||
|
@ -335,11 +361,14 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
|
||||||
* receive if it is less than transfer size and transfer size if
|
* receive if it is less than transfer size and transfer size if
|
||||||
* it is more. Enable the interrupts.
|
* it is more. Enable the interrupts.
|
||||||
*/
|
*/
|
||||||
if (id->recv_count > CDNS_I2C_TRANSFER_SIZE)
|
if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) {
|
||||||
cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
|
cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
|
||||||
CDNS_I2C_XFER_SIZE_OFFSET);
|
CDNS_I2C_XFER_SIZE_OFFSET);
|
||||||
else
|
id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
|
||||||
|
} else {
|
||||||
cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
|
cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
/* Clear the bus hold flag if bytes to receive is less than FIFO size */
|
/* Clear the bus hold flag if bytes to receive is less than FIFO size */
|
||||||
if (!id->bus_hold_flag &&
|
if (!id->bus_hold_flag &&
|
||||||
((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
|
((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
|
||||||
|
@ -516,6 +545,20 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
* processed with a repeated start.
|
* processed with a repeated start.
|
||||||
*/
|
*/
|
||||||
if (num > 1) {
|
if (num > 1) {
|
||||||
|
/*
|
||||||
|
* This controller does not give completion interrupt after a
|
||||||
|
* master receive message if HOLD bit is set (repeated start),
|
||||||
|
* resulting in SW timeout. Hence, if a receive message is
|
||||||
|
* followed by any other message, an error is returned
|
||||||
|
* indicating that this sequence is not supported.
|
||||||
|
*/
|
||||||
|
for (count = 0; count < num - 1; count++) {
|
||||||
|
if (msgs[count].flags & I2C_M_RD) {
|
||||||
|
dev_warn(adap->dev.parent,
|
||||||
|
"Can't do repeated start after a receive message\n");
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
}
|
||||||
id->bus_hold_flag = 1;
|
id->bus_hold_flag = 1;
|
||||||
reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
||||||
reg |= CDNS_I2C_CR_HOLD;
|
reg |= CDNS_I2C_CR_HOLD;
|
||||||
|
|
160
drivers/i2c/busses/i2c-designware-baytrail.c
Normal file
160
drivers/i2c/busses/i2c-designware-baytrail.c
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* Intel BayTrail PMIC I2C bus semaphore implementaion
|
||||||
|
* Copyright (c) 2014, Intel Corporation.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*/
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <asm/iosf_mbi.h>
|
||||||
|
#include "i2c-designware-core.h"
|
||||||
|
|
||||||
|
#define SEMAPHORE_TIMEOUT 100
|
||||||
|
#define PUNIT_SEMAPHORE 0x7
|
||||||
|
|
||||||
|
static unsigned long acquired;
|
||||||
|
|
||||||
|
static int get_sem(struct device *dev, u32 *sem)
|
||||||
|
{
|
||||||
|
u32 reg_val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, PUNIT_SEMAPHORE,
|
||||||
|
®_val);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "iosf failed to read punit semaphore\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
*sem = reg_val & 0x1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_semaphore(struct device *dev)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
|
||||||
|
if (iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
|
||||||
|
PUNIT_SEMAPHORE, &data)) {
|
||||||
|
dev_err(dev, "iosf failed to reset punit semaphore during read\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = data & 0xfffffffe;
|
||||||
|
if (iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
|
||||||
|
PUNIT_SEMAPHORE, data))
|
||||||
|
dev_err(dev, "iosf failed to reset punit semaphore during write\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int baytrail_i2c_acquire(struct dw_i2c_dev *dev)
|
||||||
|
{
|
||||||
|
u32 sem = 0;
|
||||||
|
int ret;
|
||||||
|
unsigned long start, end;
|
||||||
|
|
||||||
|
if (!dev || !dev->dev)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (!dev->acquire_lock)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* host driver writes 0x2 to side band semaphore register */
|
||||||
|
ret = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
|
||||||
|
PUNIT_SEMAPHORE, 0x2);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev->dev, "iosf punit semaphore request failed\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* host driver waits for bit 0 to be set in semaphore register */
|
||||||
|
start = jiffies;
|
||||||
|
end = start + msecs_to_jiffies(SEMAPHORE_TIMEOUT);
|
||||||
|
while (!time_after(jiffies, end)) {
|
||||||
|
ret = get_sem(dev->dev, &sem);
|
||||||
|
if (!ret && sem) {
|
||||||
|
acquired = jiffies;
|
||||||
|
dev_dbg(dev->dev, "punit semaphore acquired after %ums\n",
|
||||||
|
jiffies_to_msecs(jiffies - start));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep_range(1000, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_err(dev->dev, "punit semaphore timed out, resetting\n");
|
||||||
|
reset_semaphore(dev->dev);
|
||||||
|
|
||||||
|
ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
|
||||||
|
PUNIT_SEMAPHORE, &sem);
|
||||||
|
if (!ret)
|
||||||
|
dev_err(dev->dev, "iosf failed to read punit semaphore\n");
|
||||||
|
else
|
||||||
|
dev_err(dev->dev, "PUNIT SEM: %d\n", sem);
|
||||||
|
|
||||||
|
WARN_ON(1);
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(baytrail_i2c_acquire);
|
||||||
|
|
||||||
|
void baytrail_i2c_release(struct dw_i2c_dev *dev)
|
||||||
|
{
|
||||||
|
if (!dev || !dev->dev)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!dev->acquire_lock)
|
||||||
|
return;
|
||||||
|
|
||||||
|
reset_semaphore(dev->dev);
|
||||||
|
dev_dbg(dev->dev, "punit semaphore held for %ums\n",
|
||||||
|
jiffies_to_msecs(jiffies - acquired));
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(baytrail_i2c_release);
|
||||||
|
|
||||||
|
int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev)
|
||||||
|
{
|
||||||
|
acpi_status status;
|
||||||
|
unsigned long long shared_host = 0;
|
||||||
|
acpi_handle handle;
|
||||||
|
|
||||||
|
if (!dev || !dev->dev)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
handle = ACPI_HANDLE(dev->dev);
|
||||||
|
if (!handle)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
|
||||||
|
|
||||||
|
if (ACPI_FAILURE(status))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (shared_host) {
|
||||||
|
dev_info(dev->dev, "I2C bus managed by PUNIT\n");
|
||||||
|
dev->acquire_lock = baytrail_i2c_acquire;
|
||||||
|
dev->release_lock = baytrail_i2c_release;
|
||||||
|
dev->pm_runtime_disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iosf_mbi_available())
|
||||||
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(i2c_dw_eval_lock_support);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
|
||||||
|
MODULE_DESCRIPTION("Baytrail I2C Semaphore driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -170,10 +170,10 @@ u32 dw_readl(struct dw_i2c_dev *dev, int offset)
|
||||||
u32 value;
|
u32 value;
|
||||||
|
|
||||||
if (dev->accessor_flags & ACCESS_16BIT)
|
if (dev->accessor_flags & ACCESS_16BIT)
|
||||||
value = readw(dev->base + offset) |
|
value = readw_relaxed(dev->base + offset) |
|
||||||
(readw(dev->base + offset + 2) << 16);
|
(readw_relaxed(dev->base + offset + 2) << 16);
|
||||||
else
|
else
|
||||||
value = readl(dev->base + offset);
|
value = readl_relaxed(dev->base + offset);
|
||||||
|
|
||||||
if (dev->accessor_flags & ACCESS_SWAP)
|
if (dev->accessor_flags & ACCESS_SWAP)
|
||||||
return swab32(value);
|
return swab32(value);
|
||||||
|
@ -187,10 +187,10 @@ void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset)
|
||||||
b = swab32(b);
|
b = swab32(b);
|
||||||
|
|
||||||
if (dev->accessor_flags & ACCESS_16BIT) {
|
if (dev->accessor_flags & ACCESS_16BIT) {
|
||||||
writew((u16)b, dev->base + offset);
|
writew_relaxed((u16)b, dev->base + offset);
|
||||||
writew((u16)(b >> 16), dev->base + offset + 2);
|
writew_relaxed((u16)(b >> 16), dev->base + offset + 2);
|
||||||
} else {
|
} else {
|
||||||
writel(b, dev->base + offset);
|
writel_relaxed(b, dev->base + offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,6 +285,15 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
|
||||||
u32 hcnt, lcnt;
|
u32 hcnt, lcnt;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
u32 sda_falling_time, scl_falling_time;
|
u32 sda_falling_time, scl_falling_time;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (dev->acquire_lock) {
|
||||||
|
ret = dev->acquire_lock(dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev->dev, "couldn't acquire bus ownership\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
input_clock_khz = dev->get_clk_rate_khz(dev);
|
input_clock_khz = dev->get_clk_rate_khz(dev);
|
||||||
|
|
||||||
|
@ -298,6 +307,8 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
|
||||||
} else if (reg != DW_IC_COMP_TYPE_VALUE) {
|
} else if (reg != DW_IC_COMP_TYPE_VALUE) {
|
||||||
dev_err(dev->dev, "Unknown Synopsys component type: "
|
dev_err(dev->dev, "Unknown Synopsys component type: "
|
||||||
"0x%08x\n", reg);
|
"0x%08x\n", reg);
|
||||||
|
if (dev->release_lock)
|
||||||
|
dev->release_lock(dev);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,40 +320,39 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
|
||||||
sda_falling_time = dev->sda_falling_time ?: 300; /* ns */
|
sda_falling_time = dev->sda_falling_time ?: 300; /* ns */
|
||||||
scl_falling_time = dev->scl_falling_time ?: 300; /* ns */
|
scl_falling_time = dev->scl_falling_time ?: 300; /* ns */
|
||||||
|
|
||||||
/* Standard-mode */
|
/* Set SCL timing parameters for standard-mode */
|
||||||
hcnt = i2c_dw_scl_hcnt(input_clock_khz,
|
|
||||||
4000, /* tHD;STA = tHIGH = 4.0 us */
|
|
||||||
sda_falling_time,
|
|
||||||
0, /* 0: DW default, 1: Ideal */
|
|
||||||
0); /* No offset */
|
|
||||||
lcnt = i2c_dw_scl_lcnt(input_clock_khz,
|
|
||||||
4700, /* tLOW = 4.7 us */
|
|
||||||
scl_falling_time,
|
|
||||||
0); /* No offset */
|
|
||||||
|
|
||||||
/* Allow platforms to specify the ideal HCNT and LCNT values */
|
|
||||||
if (dev->ss_hcnt && dev->ss_lcnt) {
|
if (dev->ss_hcnt && dev->ss_lcnt) {
|
||||||
hcnt = dev->ss_hcnt;
|
hcnt = dev->ss_hcnt;
|
||||||
lcnt = dev->ss_lcnt;
|
lcnt = dev->ss_lcnt;
|
||||||
|
} else {
|
||||||
|
hcnt = i2c_dw_scl_hcnt(input_clock_khz,
|
||||||
|
4000, /* tHD;STA = tHIGH = 4.0 us */
|
||||||
|
sda_falling_time,
|
||||||
|
0, /* 0: DW default, 1: Ideal */
|
||||||
|
0); /* No offset */
|
||||||
|
lcnt = i2c_dw_scl_lcnt(input_clock_khz,
|
||||||
|
4700, /* tLOW = 4.7 us */
|
||||||
|
scl_falling_time,
|
||||||
|
0); /* No offset */
|
||||||
}
|
}
|
||||||
dw_writel(dev, hcnt, DW_IC_SS_SCL_HCNT);
|
dw_writel(dev, hcnt, DW_IC_SS_SCL_HCNT);
|
||||||
dw_writel(dev, lcnt, DW_IC_SS_SCL_LCNT);
|
dw_writel(dev, lcnt, DW_IC_SS_SCL_LCNT);
|
||||||
dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
|
dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
|
||||||
|
|
||||||
/* Fast-mode */
|
/* Set SCL timing parameters for fast-mode */
|
||||||
hcnt = i2c_dw_scl_hcnt(input_clock_khz,
|
|
||||||
600, /* tHD;STA = tHIGH = 0.6 us */
|
|
||||||
sda_falling_time,
|
|
||||||
0, /* 0: DW default, 1: Ideal */
|
|
||||||
0); /* No offset */
|
|
||||||
lcnt = i2c_dw_scl_lcnt(input_clock_khz,
|
|
||||||
1300, /* tLOW = 1.3 us */
|
|
||||||
scl_falling_time,
|
|
||||||
0); /* No offset */
|
|
||||||
|
|
||||||
if (dev->fs_hcnt && dev->fs_lcnt) {
|
if (dev->fs_hcnt && dev->fs_lcnt) {
|
||||||
hcnt = dev->fs_hcnt;
|
hcnt = dev->fs_hcnt;
|
||||||
lcnt = dev->fs_lcnt;
|
lcnt = dev->fs_lcnt;
|
||||||
|
} else {
|
||||||
|
hcnt = i2c_dw_scl_hcnt(input_clock_khz,
|
||||||
|
600, /* tHD;STA = tHIGH = 0.6 us */
|
||||||
|
sda_falling_time,
|
||||||
|
0, /* 0: DW default, 1: Ideal */
|
||||||
|
0); /* No offset */
|
||||||
|
lcnt = i2c_dw_scl_lcnt(input_clock_khz,
|
||||||
|
1300, /* tLOW = 1.3 us */
|
||||||
|
scl_falling_time,
|
||||||
|
0); /* No offset */
|
||||||
}
|
}
|
||||||
dw_writel(dev, hcnt, DW_IC_FS_SCL_HCNT);
|
dw_writel(dev, hcnt, DW_IC_FS_SCL_HCNT);
|
||||||
dw_writel(dev, lcnt, DW_IC_FS_SCL_LCNT);
|
dw_writel(dev, lcnt, DW_IC_FS_SCL_LCNT);
|
||||||
|
@ -364,6 +374,9 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
|
||||||
|
|
||||||
/* configure the i2c master */
|
/* configure the i2c master */
|
||||||
dw_writel(dev, dev->master_cfg , DW_IC_CON);
|
dw_writel(dev, dev->master_cfg , DW_IC_CON);
|
||||||
|
|
||||||
|
if (dev->release_lock)
|
||||||
|
dev->release_lock(dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i2c_dw_init);
|
EXPORT_SYMBOL_GPL(i2c_dw_init);
|
||||||
|
@ -627,6 +640,14 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||||
dev->abort_source = 0;
|
dev->abort_source = 0;
|
||||||
dev->rx_outstanding = 0;
|
dev->rx_outstanding = 0;
|
||||||
|
|
||||||
|
if (dev->acquire_lock) {
|
||||||
|
ret = dev->acquire_lock(dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev->dev, "couldn't acquire bus ownership\n");
|
||||||
|
goto done_nolock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = i2c_dw_wait_bus_not_busy(dev);
|
ret = i2c_dw_wait_bus_not_busy(dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -672,6 +693,10 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
if (dev->release_lock)
|
||||||
|
dev->release_lock(dev);
|
||||||
|
|
||||||
|
done_nolock:
|
||||||
pm_runtime_mark_last_busy(dev->dev);
|
pm_runtime_mark_last_busy(dev->dev);
|
||||||
pm_runtime_put_autosuspend(dev->dev);
|
pm_runtime_put_autosuspend(dev->dev);
|
||||||
mutex_unlock(&dev->lock);
|
mutex_unlock(&dev->lock);
|
||||||
|
|
|
@ -61,6 +61,9 @@
|
||||||
* @ss_lcnt: standard speed LCNT value
|
* @ss_lcnt: standard speed LCNT value
|
||||||
* @fs_hcnt: fast speed HCNT value
|
* @fs_hcnt: fast speed HCNT value
|
||||||
* @fs_lcnt: fast speed LCNT value
|
* @fs_lcnt: fast speed LCNT value
|
||||||
|
* @acquire_lock: function to acquire a hardware lock on the bus
|
||||||
|
* @release_lock: function to release a hardware lock on the bus
|
||||||
|
* @pm_runtime_disabled: true if pm runtime is disabled
|
||||||
*
|
*
|
||||||
* HCNT and LCNT parameters can be used if the platform knows more accurate
|
* HCNT and LCNT parameters can be used if the platform knows more accurate
|
||||||
* values than the one computed based only on the input clock frequency.
|
* values than the one computed based only on the input clock frequency.
|
||||||
|
@ -101,6 +104,9 @@ struct dw_i2c_dev {
|
||||||
u16 ss_lcnt;
|
u16 ss_lcnt;
|
||||||
u16 fs_hcnt;
|
u16 fs_hcnt;
|
||||||
u16 fs_lcnt;
|
u16 fs_lcnt;
|
||||||
|
int (*acquire_lock)(struct dw_i2c_dev *dev);
|
||||||
|
void (*release_lock)(struct dw_i2c_dev *dev);
|
||||||
|
bool pm_runtime_disabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ACCESS_SWAP 0x00000001
|
#define ACCESS_SWAP 0x00000001
|
||||||
|
@ -119,3 +125,9 @@ extern void i2c_dw_disable(struct dw_i2c_dev *dev);
|
||||||
extern void i2c_dw_clear_int(struct dw_i2c_dev *dev);
|
extern void i2c_dw_clear_int(struct dw_i2c_dev *dev);
|
||||||
extern void i2c_dw_disable_int(struct dw_i2c_dev *dev);
|
extern void i2c_dw_disable_int(struct dw_i2c_dev *dev);
|
||||||
extern u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev);
|
extern u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev);
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL)
|
||||||
|
extern int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev);
|
||||||
|
#else
|
||||||
|
static inline int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev) { return 0; }
|
||||||
|
#endif
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Copyright (C) 2006 Texas Instruments.
|
* Copyright (C) 2006 Texas Instruments.
|
||||||
* Copyright (C) 2007 MontaVista Software Inc.
|
* Copyright (C) 2007 MontaVista Software Inc.
|
||||||
* Copyright (C) 2009 Provigent Ltd.
|
* Copyright (C) 2009 Provigent Ltd.
|
||||||
* Copyright (C) 2011 Intel corporation.
|
* Copyright (C) 2011, 2015 Intel Corporation.
|
||||||
*
|
*
|
||||||
* ----------------------------------------------------------------------------
|
* ----------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
|
@ -40,10 +40,6 @@
|
||||||
#define DRIVER_NAME "i2c-designware-pci"
|
#define DRIVER_NAME "i2c-designware-pci"
|
||||||
|
|
||||||
enum dw_pci_ctl_id_t {
|
enum dw_pci_ctl_id_t {
|
||||||
moorestown_0,
|
|
||||||
moorestown_1,
|
|
||||||
moorestown_2,
|
|
||||||
|
|
||||||
medfield_0,
|
medfield_0,
|
||||||
medfield_1,
|
medfield_1,
|
||||||
medfield_2,
|
medfield_2,
|
||||||
|
@ -101,28 +97,7 @@ static struct dw_scl_sda_cfg hsw_config = {
|
||||||
.sda_hold = 0x9,
|
.sda_hold = 0x9,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct dw_pci_controller dw_pci_controllers[] = {
|
static struct dw_pci_controller dw_pci_controllers[] = {
|
||||||
[moorestown_0] = {
|
|
||||||
.bus_num = 0,
|
|
||||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
|
||||||
.tx_fifo_depth = 32,
|
|
||||||
.rx_fifo_depth = 32,
|
|
||||||
.clk_khz = 25000,
|
|
||||||
},
|
|
||||||
[moorestown_1] = {
|
|
||||||
.bus_num = 1,
|
|
||||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
|
||||||
.tx_fifo_depth = 32,
|
|
||||||
.rx_fifo_depth = 32,
|
|
||||||
.clk_khz = 25000,
|
|
||||||
},
|
|
||||||
[moorestown_2] = {
|
|
||||||
.bus_num = 2,
|
|
||||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
|
||||||
.tx_fifo_depth = 32,
|
|
||||||
.rx_fifo_depth = 32,
|
|
||||||
.clk_khz = 25000,
|
|
||||||
},
|
|
||||||
[medfield_0] = {
|
[medfield_0] = {
|
||||||
.bus_num = 0,
|
.bus_num = 0,
|
||||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||||
|
@ -170,7 +145,6 @@ static struct dw_pci_controller dw_pci_controllers[] = {
|
||||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||||
.tx_fifo_depth = 32,
|
.tx_fifo_depth = 32,
|
||||||
.rx_fifo_depth = 32,
|
.rx_fifo_depth = 32,
|
||||||
.clk_khz = 100000,
|
|
||||||
.functionality = I2C_FUNC_10BIT_ADDR,
|
.functionality = I2C_FUNC_10BIT_ADDR,
|
||||||
.scl_sda_cfg = &byt_config,
|
.scl_sda_cfg = &byt_config,
|
||||||
},
|
},
|
||||||
|
@ -179,7 +153,6 @@ static struct dw_pci_controller dw_pci_controllers[] = {
|
||||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||||
.tx_fifo_depth = 32,
|
.tx_fifo_depth = 32,
|
||||||
.rx_fifo_depth = 32,
|
.rx_fifo_depth = 32,
|
||||||
.clk_khz = 100000,
|
|
||||||
.functionality = I2C_FUNC_10BIT_ADDR,
|
.functionality = I2C_FUNC_10BIT_ADDR,
|
||||||
.scl_sda_cfg = &hsw_config,
|
.scl_sda_cfg = &hsw_config,
|
||||||
},
|
},
|
||||||
|
@ -259,7 +232,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||||
dev->functionality = controller->functionality |
|
dev->functionality = controller->functionality |
|
||||||
DW_DEFAULT_FUNCTIONALITY;
|
DW_DEFAULT_FUNCTIONALITY;
|
||||||
|
|
||||||
dev->master_cfg = controller->bus_cfg;
|
dev->master_cfg = controller->bus_cfg;
|
||||||
if (controller->scl_sda_cfg) {
|
if (controller->scl_sda_cfg) {
|
||||||
cfg = controller->scl_sda_cfg;
|
cfg = controller->scl_sda_cfg;
|
||||||
dev->ss_hcnt = cfg->ss_hcnt;
|
dev->ss_hcnt = cfg->ss_hcnt;
|
||||||
|
@ -325,12 +298,8 @@ static void i2c_dw_pci_remove(struct pci_dev *pdev)
|
||||||
MODULE_ALIAS("i2c_designware-pci");
|
MODULE_ALIAS("i2c_designware-pci");
|
||||||
|
|
||||||
static const struct pci_device_id i2_designware_pci_ids[] = {
|
static const struct pci_device_id i2_designware_pci_ids[] = {
|
||||||
/* Moorestown */
|
|
||||||
{ PCI_VDEVICE(INTEL, 0x0802), moorestown_0 },
|
|
||||||
{ PCI_VDEVICE(INTEL, 0x0803), moorestown_1 },
|
|
||||||
{ PCI_VDEVICE(INTEL, 0x0804), moorestown_2 },
|
|
||||||
/* Medfield */
|
/* Medfield */
|
||||||
{ PCI_VDEVICE(INTEL, 0x0817), medfield_3,},
|
{ PCI_VDEVICE(INTEL, 0x0817), medfield_3 },
|
||||||
{ PCI_VDEVICE(INTEL, 0x0818), medfield_4 },
|
{ PCI_VDEVICE(INTEL, 0x0818), medfield_4 },
|
||||||
{ PCI_VDEVICE(INTEL, 0x0819), medfield_5 },
|
{ PCI_VDEVICE(INTEL, 0x0819), medfield_5 },
|
||||||
{ PCI_VDEVICE(INTEL, 0x082C), medfield_0 },
|
{ PCI_VDEVICE(INTEL, 0x082C), medfield_0 },
|
||||||
|
@ -348,7 +317,7 @@ static const struct pci_device_id i2_designware_pci_ids[] = {
|
||||||
{ PCI_VDEVICE(INTEL, 0x9c61), haswell },
|
{ PCI_VDEVICE(INTEL, 0x9c61), haswell },
|
||||||
{ PCI_VDEVICE(INTEL, 0x9c62), haswell },
|
{ PCI_VDEVICE(INTEL, 0x9c62), haswell },
|
||||||
/* Braswell / Cherrytrail */
|
/* Braswell / Cherrytrail */
|
||||||
{ PCI_VDEVICE(INTEL, 0x22C1), baytrail,},
|
{ PCI_VDEVICE(INTEL, 0x22C1), baytrail },
|
||||||
{ PCI_VDEVICE(INTEL, 0x22C2), baytrail },
|
{ PCI_VDEVICE(INTEL, 0x22C2), baytrail },
|
||||||
{ PCI_VDEVICE(INTEL, 0x22C3), baytrail },
|
{ PCI_VDEVICE(INTEL, 0x22C3), baytrail },
|
||||||
{ PCI_VDEVICE(INTEL, 0x22C4), baytrail },
|
{ PCI_VDEVICE(INTEL, 0x22C4), baytrail },
|
||||||
|
|
|
@ -195,6 +195,10 @@ static int dw_i2c_probe(struct platform_device *pdev)
|
||||||
clk_freq = pdata->i2c_scl_freq;
|
clk_freq = pdata->i2c_scl_freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r = i2c_dw_eval_lock_support(dev);
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
|
||||||
dev->functionality =
|
dev->functionality =
|
||||||
I2C_FUNC_I2C |
|
I2C_FUNC_I2C |
|
||||||
I2C_FUNC_10BIT_ADDR |
|
I2C_FUNC_10BIT_ADDR |
|
||||||
|
@ -257,10 +261,14 @@ static int dw_i2c_probe(struct platform_device *pdev)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
|
if (dev->pm_runtime_disabled) {
|
||||||
pm_runtime_use_autosuspend(&pdev->dev);
|
pm_runtime_forbid(&pdev->dev);
|
||||||
pm_runtime_set_active(&pdev->dev);
|
} else {
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
|
||||||
|
pm_runtime_use_autosuspend(&pdev->dev);
|
||||||
|
pm_runtime_set_active(&pdev->dev);
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -310,7 +318,9 @@ static int dw_i2c_resume(struct device *dev)
|
||||||
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
|
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
clk_prepare_enable(i_dev->clk);
|
clk_prepare_enable(i_dev->clk);
|
||||||
i2c_dw_init(i_dev);
|
|
||||||
|
if (!i_dev->pm_runtime_disabled)
|
||||||
|
i2c_dw_init(i_dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,7 +201,7 @@ struct imx_i2c_struct {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
wait_queue_head_t queue;
|
wait_queue_head_t queue;
|
||||||
unsigned long i2csr;
|
unsigned long i2csr;
|
||||||
unsigned int disable_delay;
|
unsigned int disable_delay;
|
||||||
int stopped;
|
int stopped;
|
||||||
unsigned int ifdr; /* IMX_I2C_IFDR */
|
unsigned int ifdr; /* IMX_I2C_IFDR */
|
||||||
unsigned int cur_clk;
|
unsigned int cur_clk;
|
||||||
|
@ -295,7 +295,6 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
|
||||||
dma->chan_tx = dma_request_slave_channel(dev, "tx");
|
dma->chan_tx = dma_request_slave_channel(dev, "tx");
|
||||||
if (!dma->chan_tx) {
|
if (!dma->chan_tx) {
|
||||||
dev_dbg(dev, "can't request DMA tx channel\n");
|
dev_dbg(dev, "can't request DMA tx channel\n");
|
||||||
ret = -ENODEV;
|
|
||||||
goto fail_al;
|
goto fail_al;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,7 +312,6 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
|
||||||
dma->chan_rx = dma_request_slave_channel(dev, "rx");
|
dma->chan_rx = dma_request_slave_channel(dev, "rx");
|
||||||
if (!dma->chan_rx) {
|
if (!dma->chan_rx) {
|
||||||
dev_dbg(dev, "can't request DMA rx channel\n");
|
dev_dbg(dev, "can't request DMA rx channel\n");
|
||||||
ret = -ENODEV;
|
|
||||||
goto fail_tx;
|
goto fail_tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,8 +479,8 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx)
|
||||||
i2c_clk_rate = clk_get_rate(i2c_imx->clk);
|
i2c_clk_rate = clk_get_rate(i2c_imx->clk);
|
||||||
if (i2c_imx->cur_clk == i2c_clk_rate)
|
if (i2c_imx->cur_clk == i2c_clk_rate)
|
||||||
return;
|
return;
|
||||||
else
|
|
||||||
i2c_imx->cur_clk = i2c_clk_rate;
|
i2c_imx->cur_clk = i2c_clk_rate;
|
||||||
|
|
||||||
div = (i2c_clk_rate + i2c_imx->bitrate - 1) / i2c_imx->bitrate;
|
div = (i2c_clk_rate + i2c_imx->bitrate - 1) / i2c_imx->bitrate;
|
||||||
if (div < i2c_clk_div[0].div)
|
if (div < i2c_clk_div[0].div)
|
||||||
|
@ -490,7 +488,8 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx)
|
||||||
else if (div > i2c_clk_div[i2c_imx->hwdata->ndivs - 1].div)
|
else if (div > i2c_clk_div[i2c_imx->hwdata->ndivs - 1].div)
|
||||||
i = i2c_imx->hwdata->ndivs - 1;
|
i = i2c_imx->hwdata->ndivs - 1;
|
||||||
else
|
else
|
||||||
for (i = 0; i2c_clk_div[i].div < div; i++);
|
for (i = 0; i2c_clk_div[i].div < div; i++)
|
||||||
|
;
|
||||||
|
|
||||||
/* Store divider value */
|
/* Store divider value */
|
||||||
i2c_imx->ifdr = i2c_clk_div[i].val;
|
i2c_imx->ifdr = i2c_clk_div[i].val;
|
||||||
|
@ -628,9 +627,9 @@ static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
|
||||||
result = wait_for_completion_timeout(
|
result = wait_for_completion_timeout(
|
||||||
&i2c_imx->dma->cmd_complete,
|
&i2c_imx->dma->cmd_complete,
|
||||||
msecs_to_jiffies(DMA_TIMEOUT));
|
msecs_to_jiffies(DMA_TIMEOUT));
|
||||||
if (result <= 0) {
|
if (result == 0) {
|
||||||
dmaengine_terminate_all(dma->chan_using);
|
dmaengine_terminate_all(dma->chan_using);
|
||||||
return result ?: -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Waiting for transfer complete. */
|
/* Waiting for transfer complete. */
|
||||||
|
@ -686,9 +685,9 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
|
||||||
result = wait_for_completion_timeout(
|
result = wait_for_completion_timeout(
|
||||||
&i2c_imx->dma->cmd_complete,
|
&i2c_imx->dma->cmd_complete,
|
||||||
msecs_to_jiffies(DMA_TIMEOUT));
|
msecs_to_jiffies(DMA_TIMEOUT));
|
||||||
if (result <= 0) {
|
if (result == 0) {
|
||||||
dmaengine_terminate_all(dma->chan_using);
|
dmaengine_terminate_all(dma->chan_using);
|
||||||
return result ?: -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* waiting for transfer complete. */
|
/* waiting for transfer complete. */
|
||||||
|
@ -822,6 +821,7 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
|
||||||
/* read data */
|
/* read data */
|
||||||
for (i = 0; i < msgs->len; i++) {
|
for (i = 0; i < msgs->len; i++) {
|
||||||
u8 len = 0;
|
u8 len = 0;
|
||||||
|
|
||||||
result = i2c_imx_trx_complete(i2c_imx);
|
result = i2c_imx_trx_complete(i2c_imx);
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
|
@ -917,15 +917,16 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
||||||
/* write/read data */
|
/* write/read data */
|
||||||
#ifdef CONFIG_I2C_DEBUG_BUS
|
#ifdef CONFIG_I2C_DEBUG_BUS
|
||||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> CONTROL: IEN=%d, IIEN=%d, "
|
dev_dbg(&i2c_imx->adapter.dev,
|
||||||
"MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n", __func__,
|
"<%s> CONTROL: IEN=%d, IIEN=%d, MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n",
|
||||||
|
__func__,
|
||||||
(temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0),
|
(temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0),
|
||||||
(temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0),
|
(temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0),
|
||||||
(temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0));
|
(temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0));
|
||||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||||
dev_dbg(&i2c_imx->adapter.dev,
|
dev_dbg(&i2c_imx->adapter.dev,
|
||||||
"<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, "
|
"<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n",
|
||||||
"IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n", __func__,
|
__func__,
|
||||||
(temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0),
|
(temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0),
|
||||||
(temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0),
|
(temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0),
|
||||||
(temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0),
|
(temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0),
|
||||||
|
@ -1004,7 +1005,7 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||||
i2c_imx->adapter.owner = THIS_MODULE;
|
i2c_imx->adapter.owner = THIS_MODULE;
|
||||||
i2c_imx->adapter.algo = &i2c_imx_algo;
|
i2c_imx->adapter.algo = &i2c_imx_algo;
|
||||||
i2c_imx->adapter.dev.parent = &pdev->dev;
|
i2c_imx->adapter.dev.parent = &pdev->dev;
|
||||||
i2c_imx->adapter.nr = pdev->id;
|
i2c_imx->adapter.nr = pdev->id;
|
||||||
i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
|
i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
|
||||||
i2c_imx->base = base;
|
i2c_imx->base = base;
|
||||||
|
|
||||||
|
@ -1063,7 +1064,7 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||||
i2c_imx->adapter.name);
|
i2c_imx->adapter.name);
|
||||||
dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
|
dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
|
||||||
|
|
||||||
/* Init DMA config if support*/
|
/* Init DMA config if supported */
|
||||||
i2c_imx_dma_request(i2c_imx, phy_addr);
|
i2c_imx_dma_request(i2c_imx, phy_addr);
|
||||||
|
|
||||||
return 0; /* Return OK */
|
return 0; /* Return OK */
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
* kind, whether express or implied.
|
* kind, whether express or implied.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -35,7 +36,9 @@ struct ocores_i2c {
|
||||||
int pos;
|
int pos;
|
||||||
int nmsgs;
|
int nmsgs;
|
||||||
int state; /* see STATE_ */
|
int state; /* see STATE_ */
|
||||||
int clock_khz;
|
struct clk *clk;
|
||||||
|
int ip_clock_khz;
|
||||||
|
int bus_clock_khz;
|
||||||
void (*setreg)(struct ocores_i2c *i2c, int reg, u8 value);
|
void (*setreg)(struct ocores_i2c *i2c, int reg, u8 value);
|
||||||
u8 (*getreg)(struct ocores_i2c *i2c, int reg);
|
u8 (*getreg)(struct ocores_i2c *i2c, int reg);
|
||||||
};
|
};
|
||||||
|
@ -215,21 +218,34 @@ static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ocores_init(struct ocores_i2c *i2c)
|
static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
|
||||||
{
|
{
|
||||||
int prescale;
|
int prescale;
|
||||||
|
int diff;
|
||||||
u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
|
u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
|
||||||
|
|
||||||
/* make sure the device is disabled */
|
/* make sure the device is disabled */
|
||||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
|
oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
|
||||||
|
|
||||||
prescale = (i2c->clock_khz / (5*100)) - 1;
|
prescale = (i2c->ip_clock_khz / (5 * i2c->bus_clock_khz)) - 1;
|
||||||
|
prescale = clamp(prescale, 0, 0xffff);
|
||||||
|
|
||||||
|
diff = i2c->ip_clock_khz / (5 * (prescale + 1)) - i2c->bus_clock_khz;
|
||||||
|
if (abs(diff) > i2c->bus_clock_khz / 10) {
|
||||||
|
dev_err(dev,
|
||||||
|
"Unsupported clock settings: core: %d KHz, bus: %d KHz\n",
|
||||||
|
i2c->ip_clock_khz, i2c->bus_clock_khz);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
oc_setreg(i2c, OCI2C_PRELOW, prescale & 0xff);
|
oc_setreg(i2c, OCI2C_PRELOW, prescale & 0xff);
|
||||||
oc_setreg(i2c, OCI2C_PREHIGH, prescale >> 8);
|
oc_setreg(i2c, OCI2C_PREHIGH, prescale >> 8);
|
||||||
|
|
||||||
/* Init the device */
|
/* Init the device */
|
||||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);
|
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);
|
||||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN | OCI2C_CTRL_EN);
|
oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN | OCI2C_CTRL_EN);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -304,6 +320,8 @@ static int ocores_i2c_of_probe(struct platform_device *pdev,
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
u32 val;
|
u32 val;
|
||||||
|
u32 clock_frequency;
|
||||||
|
bool clock_frequency_present;
|
||||||
|
|
||||||
if (of_property_read_u32(np, "reg-shift", &i2c->reg_shift)) {
|
if (of_property_read_u32(np, "reg-shift", &i2c->reg_shift)) {
|
||||||
/* no 'reg-shift', check for deprecated 'regstep' */
|
/* no 'reg-shift', check for deprecated 'regstep' */
|
||||||
|
@ -319,12 +337,42 @@ static int ocores_i2c_of_probe(struct platform_device *pdev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (of_property_read_u32(np, "clock-frequency", &val)) {
|
clock_frequency_present = !of_property_read_u32(np, "clock-frequency",
|
||||||
dev_err(&pdev->dev,
|
&clock_frequency);
|
||||||
"Missing required parameter 'clock-frequency'\n");
|
i2c->bus_clock_khz = 100;
|
||||||
return -ENODEV;
|
|
||||||
|
i2c->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
|
||||||
|
if (!IS_ERR(i2c->clk)) {
|
||||||
|
int ret = clk_prepare_enable(i2c->clk);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"clk_prepare_enable failed: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
i2c->ip_clock_khz = clk_get_rate(i2c->clk) / 1000;
|
||||||
|
if (clock_frequency_present)
|
||||||
|
i2c->bus_clock_khz = clock_frequency / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2c->ip_clock_khz == 0) {
|
||||||
|
if (of_property_read_u32(np, "opencores,ip-clock-frequency",
|
||||||
|
&val)) {
|
||||||
|
if (!clock_frequency_present) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"Missing required parameter 'opencores,ip-clock-frequency'\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
i2c->ip_clock_khz = clock_frequency / 1000;
|
||||||
|
dev_warn(&pdev->dev,
|
||||||
|
"Deprecated usage of the 'clock-frequency' property, please update to 'opencores,ip-clock-frequency'\n");
|
||||||
|
} else {
|
||||||
|
i2c->ip_clock_khz = val / 1000;
|
||||||
|
if (clock_frequency_present)
|
||||||
|
i2c->bus_clock_khz = clock_frequency / 1000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
i2c->clock_khz = val / 1000;
|
|
||||||
|
|
||||||
of_property_read_u32(pdev->dev.of_node, "reg-io-width",
|
of_property_read_u32(pdev->dev.of_node, "reg-io-width",
|
||||||
&i2c->reg_io_width);
|
&i2c->reg_io_width);
|
||||||
|
@ -368,7 +416,8 @@ static int ocores_i2c_probe(struct platform_device *pdev)
|
||||||
if (pdata) {
|
if (pdata) {
|
||||||
i2c->reg_shift = pdata->reg_shift;
|
i2c->reg_shift = pdata->reg_shift;
|
||||||
i2c->reg_io_width = pdata->reg_io_width;
|
i2c->reg_io_width = pdata->reg_io_width;
|
||||||
i2c->clock_khz = pdata->clock_khz;
|
i2c->ip_clock_khz = pdata->clock_khz;
|
||||||
|
i2c->bus_clock_khz = 100;
|
||||||
} else {
|
} else {
|
||||||
ret = ocores_i2c_of_probe(pdev, i2c);
|
ret = ocores_i2c_of_probe(pdev, i2c);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -402,7 +451,9 @@ static int ocores_i2c_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ocores_init(i2c);
|
ret = ocores_init(&pdev->dev, i2c);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
init_waitqueue_head(&i2c->wait);
|
init_waitqueue_head(&i2c->wait);
|
||||||
ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0,
|
ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0,
|
||||||
|
@ -446,6 +497,9 @@ static int ocores_i2c_remove(struct platform_device *pdev)
|
||||||
/* remove adapter & data */
|
/* remove adapter & data */
|
||||||
i2c_del_adapter(&i2c->adap);
|
i2c_del_adapter(&i2c->adap);
|
||||||
|
|
||||||
|
if (!IS_ERR(i2c->clk))
|
||||||
|
clk_disable_unprepare(i2c->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,6 +512,8 @@ static int ocores_i2c_suspend(struct device *dev)
|
||||||
/* make sure the device is disabled */
|
/* make sure the device is disabled */
|
||||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
|
oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
|
||||||
|
|
||||||
|
if (!IS_ERR(i2c->clk))
|
||||||
|
clk_disable_unprepare(i2c->clk);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,9 +521,20 @@ static int ocores_i2c_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct ocores_i2c *i2c = dev_get_drvdata(dev);
|
struct ocores_i2c *i2c = dev_get_drvdata(dev);
|
||||||
|
|
||||||
ocores_init(i2c);
|
if (!IS_ERR(i2c->clk)) {
|
||||||
|
unsigned long rate;
|
||||||
|
int ret = clk_prepare_enable(i2c->clk);
|
||||||
|
|
||||||
return 0;
|
if (ret) {
|
||||||
|
dev_err(dev,
|
||||||
|
"clk_prepare_enable failed: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
rate = clk_get_rate(i2c->clk) / 1000;
|
||||||
|
if (rate)
|
||||||
|
i2c->ip_clock_khz = rate;
|
||||||
|
}
|
||||||
|
return ocores_init(dev, i2c);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(ocores_i2c_pm, ocores_i2c_suspend, ocores_i2c_resume);
|
static SIMPLE_DEV_PM_OPS(ocores_i2c_pm, ocores_i2c_suspend, ocores_i2c_resume);
|
||||||
|
|
|
@ -148,13 +148,6 @@ static inline u32 pmcmsptwi_clock_to_reg(
|
||||||
return ((clock->filter & 0xf) << 12) | (clock->clock & 0x03ff);
|
return ((clock->filter & 0xf) << 12) | (clock->clock & 0x03ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void pmcmsptwi_reg_to_clock(
|
|
||||||
u32 reg, struct pmcmsptwi_clock *clock)
|
|
||||||
{
|
|
||||||
clock->filter = (reg >> 12) & 0xf;
|
|
||||||
clock->clock = reg & 0x03ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 pmcmsptwi_cfg_to_reg(const struct pmcmsptwi_cfg *cfg)
|
static inline u32 pmcmsptwi_cfg_to_reg(const struct pmcmsptwi_cfg *cfg)
|
||||||
{
|
{
|
||||||
return ((cfg->arbf & 0xf) << 12) |
|
return ((cfg->arbf & 0xf) << 12) |
|
||||||
|
|
|
@ -102,6 +102,9 @@ struct rk3x_i2c {
|
||||||
|
|
||||||
/* Settings */
|
/* Settings */
|
||||||
unsigned int scl_frequency;
|
unsigned int scl_frequency;
|
||||||
|
unsigned int scl_rise_ns;
|
||||||
|
unsigned int scl_fall_ns;
|
||||||
|
unsigned int sda_fall_ns;
|
||||||
|
|
||||||
/* Synchronization & notification */
|
/* Synchronization & notification */
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
@ -435,6 +438,9 @@ out:
|
||||||
*
|
*
|
||||||
* @clk_rate: I2C input clock rate
|
* @clk_rate: I2C input clock rate
|
||||||
* @scl_rate: Desired SCL rate
|
* @scl_rate: Desired SCL rate
|
||||||
|
* @scl_rise_ns: How many ns it takes for SCL to rise.
|
||||||
|
* @scl_fall_ns: How many ns it takes for SCL to fall.
|
||||||
|
* @sda_fall_ns: How many ns it takes for SDA to fall.
|
||||||
* @div_low: Divider output for low
|
* @div_low: Divider output for low
|
||||||
* @div_high: Divider output for high
|
* @div_high: Divider output for high
|
||||||
*
|
*
|
||||||
|
@ -443,11 +449,16 @@ out:
|
||||||
* too high, we silently use the highest possible rate.
|
* too high, we silently use the highest possible rate.
|
||||||
*/
|
*/
|
||||||
static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
|
static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
|
||||||
|
unsigned long scl_rise_ns,
|
||||||
|
unsigned long scl_fall_ns,
|
||||||
|
unsigned long sda_fall_ns,
|
||||||
unsigned long *div_low, unsigned long *div_high)
|
unsigned long *div_low, unsigned long *div_high)
|
||||||
{
|
{
|
||||||
unsigned long min_low_ns, min_high_ns;
|
unsigned long spec_min_low_ns, spec_min_high_ns;
|
||||||
unsigned long max_data_hold_ns;
|
unsigned long spec_setup_start, spec_max_data_hold_ns;
|
||||||
unsigned long data_hold_buffer_ns;
|
unsigned long data_hold_buffer_ns;
|
||||||
|
|
||||||
|
unsigned long min_low_ns, min_high_ns;
|
||||||
unsigned long max_low_ns, min_total_ns;
|
unsigned long max_low_ns, min_total_ns;
|
||||||
|
|
||||||
unsigned long clk_rate_khz, scl_rate_khz;
|
unsigned long clk_rate_khz, scl_rate_khz;
|
||||||
|
@ -469,29 +480,50 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
|
||||||
scl_rate = 1000;
|
scl_rate = 1000;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* min_low_ns: The minimum number of ns we need to hold low
|
* min_low_ns: The minimum number of ns we need to hold low to
|
||||||
* to meet i2c spec
|
* meet I2C specification, should include fall time.
|
||||||
* min_high_ns: The minimum number of ns we need to hold high
|
* min_high_ns: The minimum number of ns we need to hold high to
|
||||||
* to meet i2c spec
|
* meet I2C specification, should include rise time.
|
||||||
* max_low_ns: The maximum number of ns we can hold low
|
* max_low_ns: The maximum number of ns we can hold low to meet
|
||||||
* to meet i2c spec
|
* I2C specification.
|
||||||
*
|
*
|
||||||
* Note: max_low_ns should be (max data hold time * 2 - buffer)
|
* Note: max_low_ns should be (maximum data hold time * 2 - buffer)
|
||||||
* This is because the i2c host on Rockchip holds the data line
|
* This is because the i2c host on Rockchip holds the data line
|
||||||
* for half the low time.
|
* for half the low time.
|
||||||
*/
|
*/
|
||||||
if (scl_rate <= 100000) {
|
if (scl_rate <= 100000) {
|
||||||
min_low_ns = 4700;
|
/* Standard-mode */
|
||||||
min_high_ns = 4000;
|
spec_min_low_ns = 4700;
|
||||||
max_data_hold_ns = 3450;
|
spec_setup_start = 4700;
|
||||||
|
spec_min_high_ns = 4000;
|
||||||
|
spec_max_data_hold_ns = 3450;
|
||||||
data_hold_buffer_ns = 50;
|
data_hold_buffer_ns = 50;
|
||||||
} else {
|
} else {
|
||||||
min_low_ns = 1300;
|
/* Fast-mode */
|
||||||
min_high_ns = 600;
|
spec_min_low_ns = 1300;
|
||||||
max_data_hold_ns = 900;
|
spec_setup_start = 600;
|
||||||
|
spec_min_high_ns = 600;
|
||||||
|
spec_max_data_hold_ns = 900;
|
||||||
data_hold_buffer_ns = 50;
|
data_hold_buffer_ns = 50;
|
||||||
}
|
}
|
||||||
max_low_ns = max_data_hold_ns * 2 - data_hold_buffer_ns;
|
min_high_ns = scl_rise_ns + spec_min_high_ns;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Timings for repeated start:
|
||||||
|
* - controller appears to drop SDA at .875x (7/8) programmed clk high.
|
||||||
|
* - controller appears to keep SCL high for 2x programmed clk high.
|
||||||
|
*
|
||||||
|
* We need to account for those rules in picking our "high" time so
|
||||||
|
* we meet tSU;STA and tHD;STA times.
|
||||||
|
*/
|
||||||
|
min_high_ns = max(min_high_ns,
|
||||||
|
DIV_ROUND_UP((scl_rise_ns + spec_setup_start) * 1000, 875));
|
||||||
|
min_high_ns = max(min_high_ns,
|
||||||
|
DIV_ROUND_UP((scl_rise_ns + spec_setup_start +
|
||||||
|
sda_fall_ns + spec_min_high_ns), 2));
|
||||||
|
|
||||||
|
min_low_ns = scl_fall_ns + spec_min_low_ns;
|
||||||
|
max_low_ns = spec_max_data_hold_ns * 2 - data_hold_buffer_ns;
|
||||||
min_total_ns = min_low_ns + min_high_ns;
|
min_total_ns = min_low_ns + min_high_ns;
|
||||||
|
|
||||||
/* Adjust to avoid overflow */
|
/* Adjust to avoid overflow */
|
||||||
|
@ -510,8 +542,8 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
|
||||||
min_div_for_hold = (min_low_div + min_high_div);
|
min_div_for_hold = (min_low_div + min_high_div);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is the maximum divider so we don't go over the max.
|
* This is the maximum divider so we don't go over the maximum.
|
||||||
* We don't round up here (we round down) since this is a max.
|
* We don't round up here (we round down) since this is a maximum.
|
||||||
*/
|
*/
|
||||||
max_low_div = clk_rate_khz * max_low_ns / (8 * 1000000);
|
max_low_div = clk_rate_khz * max_low_ns / (8 * 1000000);
|
||||||
|
|
||||||
|
@ -544,7 +576,7 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
|
||||||
ideal_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns,
|
ideal_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns,
|
||||||
scl_rate_khz * 8 * min_total_ns);
|
scl_rate_khz * 8 * min_total_ns);
|
||||||
|
|
||||||
/* Don't allow it to go over the max */
|
/* Don't allow it to go over the maximum */
|
||||||
if (ideal_low_div > max_low_div)
|
if (ideal_low_div > max_low_div)
|
||||||
ideal_low_div = max_low_div;
|
ideal_low_div = max_low_div;
|
||||||
|
|
||||||
|
@ -588,9 +620,9 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
|
||||||
u64 t_low_ns, t_high_ns;
|
u64 t_low_ns, t_high_ns;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, &div_low,
|
ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, i2c->scl_rise_ns,
|
||||||
&div_high);
|
i2c->scl_fall_ns, i2c->sda_fall_ns,
|
||||||
|
&div_low, &div_high);
|
||||||
WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->scl_frequency);
|
WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->scl_frequency);
|
||||||
|
|
||||||
clk_enable(i2c->clk);
|
clk_enable(i2c->clk);
|
||||||
|
@ -633,9 +665,10 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case PRE_RATE_CHANGE:
|
case PRE_RATE_CHANGE:
|
||||||
if (rk3x_i2c_calc_divs(ndata->new_rate, i2c->scl_frequency,
|
if (rk3x_i2c_calc_divs(ndata->new_rate, i2c->scl_frequency,
|
||||||
&div_low, &div_high) != 0) {
|
i2c->scl_rise_ns, i2c->scl_fall_ns,
|
||||||
|
i2c->sda_fall_ns,
|
||||||
|
&div_low, &div_high) != 0)
|
||||||
return NOTIFY_STOP;
|
return NOTIFY_STOP;
|
||||||
}
|
|
||||||
|
|
||||||
/* scale up */
|
/* scale up */
|
||||||
if (ndata->new_rate > ndata->old_rate)
|
if (ndata->new_rate > ndata->old_rate)
|
||||||
|
@ -859,6 +892,24 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
|
||||||
i2c->scl_frequency = DEFAULT_SCL_RATE;
|
i2c->scl_frequency = DEFAULT_SCL_RATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read rise and fall time from device tree. If not available use
|
||||||
|
* the default maximum timing from the specification.
|
||||||
|
*/
|
||||||
|
if (of_property_read_u32(pdev->dev.of_node, "i2c-scl-rising-time-ns",
|
||||||
|
&i2c->scl_rise_ns)) {
|
||||||
|
if (i2c->scl_frequency <= 100000)
|
||||||
|
i2c->scl_rise_ns = 1000;
|
||||||
|
else
|
||||||
|
i2c->scl_rise_ns = 300;
|
||||||
|
}
|
||||||
|
if (of_property_read_u32(pdev->dev.of_node, "i2c-scl-falling-time-ns",
|
||||||
|
&i2c->scl_fall_ns))
|
||||||
|
i2c->scl_fall_ns = 300;
|
||||||
|
if (of_property_read_u32(pdev->dev.of_node, "i2c-sda-falling-time-ns",
|
||||||
|
&i2c->scl_fall_ns))
|
||||||
|
i2c->sda_fall_ns = i2c->scl_fall_ns;
|
||||||
|
|
||||||
strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name));
|
strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name));
|
||||||
i2c->adap.owner = THIS_MODULE;
|
i2c->adap.owner = THIS_MODULE;
|
||||||
i2c->adap.algo = &rk3x_i2c_algorithm;
|
i2c->adap.algo = &rk3x_i2c_algorithm;
|
||||||
|
|
|
@ -286,6 +286,7 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev)
|
||||||
if (rx_fifo_avail > 0 && buf_remaining > 0) {
|
if (rx_fifo_avail > 0 && buf_remaining > 0) {
|
||||||
BUG_ON(buf_remaining > 3);
|
BUG_ON(buf_remaining > 3);
|
||||||
val = i2c_readl(i2c_dev, I2C_RX_FIFO);
|
val = i2c_readl(i2c_dev, I2C_RX_FIFO);
|
||||||
|
val = cpu_to_le32(val);
|
||||||
memcpy(buf, &val, buf_remaining);
|
memcpy(buf, &val, buf_remaining);
|
||||||
buf_remaining = 0;
|
buf_remaining = 0;
|
||||||
rx_fifo_avail--;
|
rx_fifo_avail--;
|
||||||
|
@ -344,6 +345,7 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
|
||||||
if (tx_fifo_avail > 0 && buf_remaining > 0) {
|
if (tx_fifo_avail > 0 && buf_remaining > 0) {
|
||||||
BUG_ON(buf_remaining > 3);
|
BUG_ON(buf_remaining > 3);
|
||||||
memcpy(&val, buf, buf_remaining);
|
memcpy(&val, buf, buf_remaining);
|
||||||
|
val = le32_to_cpu(val);
|
||||||
|
|
||||||
/* Again update before writing to FIFO to make sure isr sees. */
|
/* Again update before writing to FIFO to make sure isr sees. */
|
||||||
i2c_dev->msg_buf_remaining = 0;
|
i2c_dev->msg_buf_remaining = 0;
|
||||||
|
|
|
@ -102,7 +102,7 @@ static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data)
|
||||||
struct acpi_resource_i2c_serialbus *sb;
|
struct acpi_resource_i2c_serialbus *sb;
|
||||||
|
|
||||||
sb = &ares->data.i2c_serial_bus;
|
sb = &ares->data.i2c_serial_bus;
|
||||||
if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
|
if (!info->addr && sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
|
||||||
info->addr = sb->slave_address;
|
info->addr = sb->slave_address;
|
||||||
if (sb->access_mode == ACPI_I2C_10BIT_MODE)
|
if (sb->access_mode == ACPI_I2C_10BIT_MODE)
|
||||||
info->flags |= I2C_CLIENT_TEN;
|
info->flags |= I2C_CLIENT_TEN;
|
||||||
|
@ -698,101 +698,6 @@ static void i2c_device_shutdown(struct device *dev)
|
||||||
driver->shutdown(client);
|
driver->shutdown(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
|
||||||
static int i2c_legacy_suspend(struct device *dev, pm_message_t mesg)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = i2c_verify_client(dev);
|
|
||||||
struct i2c_driver *driver;
|
|
||||||
|
|
||||||
if (!client || !dev->driver)
|
|
||||||
return 0;
|
|
||||||
driver = to_i2c_driver(dev->driver);
|
|
||||||
if (!driver->suspend)
|
|
||||||
return 0;
|
|
||||||
return driver->suspend(client, mesg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_legacy_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = i2c_verify_client(dev);
|
|
||||||
struct i2c_driver *driver;
|
|
||||||
|
|
||||||
if (!client || !dev->driver)
|
|
||||||
return 0;
|
|
||||||
driver = to_i2c_driver(dev->driver);
|
|
||||||
if (!driver->resume)
|
|
||||||
return 0;
|
|
||||||
return driver->resume(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_device_pm_suspend(struct device *dev)
|
|
||||||
{
|
|
||||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
|
||||||
|
|
||||||
if (pm)
|
|
||||||
return pm_generic_suspend(dev);
|
|
||||||
else
|
|
||||||
return i2c_legacy_suspend(dev, PMSG_SUSPEND);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_device_pm_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
|
||||||
|
|
||||||
if (pm)
|
|
||||||
return pm_generic_resume(dev);
|
|
||||||
else
|
|
||||||
return i2c_legacy_resume(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_device_pm_freeze(struct device *dev)
|
|
||||||
{
|
|
||||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
|
||||||
|
|
||||||
if (pm)
|
|
||||||
return pm_generic_freeze(dev);
|
|
||||||
else
|
|
||||||
return i2c_legacy_suspend(dev, PMSG_FREEZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_device_pm_thaw(struct device *dev)
|
|
||||||
{
|
|
||||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
|
||||||
|
|
||||||
if (pm)
|
|
||||||
return pm_generic_thaw(dev);
|
|
||||||
else
|
|
||||||
return i2c_legacy_resume(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_device_pm_poweroff(struct device *dev)
|
|
||||||
{
|
|
||||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
|
||||||
|
|
||||||
if (pm)
|
|
||||||
return pm_generic_poweroff(dev);
|
|
||||||
else
|
|
||||||
return i2c_legacy_suspend(dev, PMSG_HIBERNATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_device_pm_restore(struct device *dev)
|
|
||||||
{
|
|
||||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
|
||||||
|
|
||||||
if (pm)
|
|
||||||
return pm_generic_restore(dev);
|
|
||||||
else
|
|
||||||
return i2c_legacy_resume(dev);
|
|
||||||
}
|
|
||||||
#else /* !CONFIG_PM_SLEEP */
|
|
||||||
#define i2c_device_pm_suspend NULL
|
|
||||||
#define i2c_device_pm_resume NULL
|
|
||||||
#define i2c_device_pm_freeze NULL
|
|
||||||
#define i2c_device_pm_thaw NULL
|
|
||||||
#define i2c_device_pm_poweroff NULL
|
|
||||||
#define i2c_device_pm_restore NULL
|
|
||||||
#endif /* !CONFIG_PM_SLEEP */
|
|
||||||
|
|
||||||
static void i2c_client_dev_release(struct device *dev)
|
static void i2c_client_dev_release(struct device *dev)
|
||||||
{
|
{
|
||||||
kfree(to_i2c_client(dev));
|
kfree(to_i2c_client(dev));
|
||||||
|
@ -804,6 +709,7 @@ show_name(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
return sprintf(buf, "%s\n", dev->type == &i2c_client_type ?
|
return sprintf(buf, "%s\n", dev->type == &i2c_client_type ?
|
||||||
to_i2c_client(dev)->name : to_i2c_adapter(dev)->name);
|
to_i2c_client(dev)->name : to_i2c_adapter(dev)->name);
|
||||||
}
|
}
|
||||||
|
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
|
show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
@ -817,8 +723,6 @@ show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
|
||||||
return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name);
|
return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
|
||||||
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
|
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
|
||||||
|
|
||||||
static struct attribute *i2c_dev_attrs[] = {
|
static struct attribute *i2c_dev_attrs[] = {
|
||||||
|
@ -827,29 +731,7 @@ static struct attribute *i2c_dev_attrs[] = {
|
||||||
&dev_attr_modalias.attr,
|
&dev_attr_modalias.attr,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
ATTRIBUTE_GROUPS(i2c_dev);
|
||||||
static struct attribute_group i2c_dev_attr_group = {
|
|
||||||
.attrs = i2c_dev_attrs,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct attribute_group *i2c_dev_attr_groups[] = {
|
|
||||||
&i2c_dev_attr_group,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct dev_pm_ops i2c_device_pm_ops = {
|
|
||||||
.suspend = i2c_device_pm_suspend,
|
|
||||||
.resume = i2c_device_pm_resume,
|
|
||||||
.freeze = i2c_device_pm_freeze,
|
|
||||||
.thaw = i2c_device_pm_thaw,
|
|
||||||
.poweroff = i2c_device_pm_poweroff,
|
|
||||||
.restore = i2c_device_pm_restore,
|
|
||||||
SET_RUNTIME_PM_OPS(
|
|
||||||
pm_generic_runtime_suspend,
|
|
||||||
pm_generic_runtime_resume,
|
|
||||||
NULL
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bus_type i2c_bus_type = {
|
struct bus_type i2c_bus_type = {
|
||||||
.name = "i2c",
|
.name = "i2c",
|
||||||
|
@ -857,12 +739,11 @@ struct bus_type i2c_bus_type = {
|
||||||
.probe = i2c_device_probe,
|
.probe = i2c_device_probe,
|
||||||
.remove = i2c_device_remove,
|
.remove = i2c_device_remove,
|
||||||
.shutdown = i2c_device_shutdown,
|
.shutdown = i2c_device_shutdown,
|
||||||
.pm = &i2c_device_pm_ops,
|
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL_GPL(i2c_bus_type);
|
EXPORT_SYMBOL_GPL(i2c_bus_type);
|
||||||
|
|
||||||
static struct device_type i2c_client_type = {
|
static struct device_type i2c_client_type = {
|
||||||
.groups = i2c_dev_attr_groups,
|
.groups = i2c_dev_groups,
|
||||||
.uevent = i2c_device_uevent,
|
.uevent = i2c_device_uevent,
|
||||||
.release = i2c_client_dev_release,
|
.release = i2c_client_dev_release,
|
||||||
};
|
};
|
||||||
|
@ -1261,6 +1142,7 @@ i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* And of course let the users delete the devices they instantiated, if
|
* And of course let the users delete the devices they instantiated, if
|
||||||
|
@ -1315,8 +1197,6 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
|
||||||
"delete_device");
|
"delete_device");
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);
|
|
||||||
static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL,
|
static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL,
|
||||||
i2c_sysfs_delete_device);
|
i2c_sysfs_delete_device);
|
||||||
|
|
||||||
|
@ -1326,18 +1206,10 @@ static struct attribute *i2c_adapter_attrs[] = {
|
||||||
&dev_attr_delete_device.attr,
|
&dev_attr_delete_device.attr,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
ATTRIBUTE_GROUPS(i2c_adapter);
|
||||||
static struct attribute_group i2c_adapter_attr_group = {
|
|
||||||
.attrs = i2c_adapter_attrs,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct attribute_group *i2c_adapter_attr_groups[] = {
|
|
||||||
&i2c_adapter_attr_group,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
struct device_type i2c_adapter_type = {
|
struct device_type i2c_adapter_type = {
|
||||||
.groups = i2c_adapter_attr_groups,
|
.groups = i2c_adapter_groups,
|
||||||
.release = i2c_adapter_dev_release,
|
.release = i2c_adapter_dev_release,
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL_GPL(i2c_adapter_type);
|
EXPORT_SYMBOL_GPL(i2c_adapter_type);
|
||||||
|
@ -1419,8 +1291,6 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
|
||||||
if (of_get_property(node, "wakeup-source", NULL))
|
if (of_get_property(node, "wakeup-source", NULL))
|
||||||
info.flags |= I2C_CLIENT_WAKE;
|
info.flags |= I2C_CLIENT_WAKE;
|
||||||
|
|
||||||
request_module("%s%s", I2C_MODULE_PREFIX, info.type);
|
|
||||||
|
|
||||||
result = i2c_new_device(adap, &info);
|
result = i2c_new_device(adap, &info);
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
|
dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
|
||||||
|
@ -1796,11 +1666,15 @@ void i2c_del_adapter(struct i2c_adapter *adap)
|
||||||
/* device name is gone after device_unregister */
|
/* device name is gone after device_unregister */
|
||||||
dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);
|
dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);
|
||||||
|
|
||||||
/* clean up the sysfs representation */
|
/* wait until all references to the device are gone
|
||||||
|
*
|
||||||
|
* FIXME: This is old code and should ideally be replaced by an
|
||||||
|
* alternative which results in decoupling the lifetime of the struct
|
||||||
|
* device from the i2c_adapter, like spi or netdev do. Any solution
|
||||||
|
* should be throughly tested with DEBUG_KOBJECT_RELEASE enabled!
|
||||||
|
*/
|
||||||
init_completion(&adap->dev_released);
|
init_completion(&adap->dev_released);
|
||||||
device_unregister(&adap->dev);
|
device_unregister(&adap->dev);
|
||||||
|
|
||||||
/* wait for sysfs to drop all references */
|
|
||||||
wait_for_completion(&adap->dev_released);
|
wait_for_completion(&adap->dev_released);
|
||||||
|
|
||||||
/* free bus id */
|
/* free bus id */
|
||||||
|
@ -1859,14 +1733,6 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
|
||||||
if (res)
|
if (res)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
/* Drivers should switch to dev_pm_ops instead. */
|
|
||||||
if (driver->suspend)
|
|
||||||
pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
|
|
||||||
driver->driver.name);
|
|
||||||
if (driver->resume)
|
|
||||||
pr_warn("i2c-core: driver [%s] using legacy resume method\n",
|
|
||||||
driver->driver.name);
|
|
||||||
|
|
||||||
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
|
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&driver->clients);
|
INIT_LIST_HEAD(&driver->clients);
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include <linux/i2c-mux.h>
|
#include <linux/i2c-mux.h>
|
||||||
#include <linux/i2c/pca954x.h>
|
#include <linux/i2c/pca954x.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
@ -186,6 +187,8 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
{
|
{
|
||||||
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
|
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
|
||||||
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
|
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||||
|
struct device_node *of_node = client->dev.of_node;
|
||||||
|
bool idle_disconnect_dt;
|
||||||
struct gpio_desc *gpio;
|
struct gpio_desc *gpio;
|
||||||
int num, force, class;
|
int num, force, class;
|
||||||
struct pca954x *data;
|
struct pca954x *data;
|
||||||
|
@ -217,8 +220,13 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
data->type = id->driver_data;
|
data->type = id->driver_data;
|
||||||
data->last_chan = 0; /* force the first selection */
|
data->last_chan = 0; /* force the first selection */
|
||||||
|
|
||||||
|
idle_disconnect_dt = of_node &&
|
||||||
|
of_property_read_bool(of_node, "i2c-mux-idle-disconnect");
|
||||||
|
|
||||||
/* Now create an adapter for each channel */
|
/* Now create an adapter for each channel */
|
||||||
for (num = 0; num < chips[data->type].nchans; num++) {
|
for (num = 0; num < chips[data->type].nchans; num++) {
|
||||||
|
bool idle_disconnect_pd = false;
|
||||||
|
|
||||||
force = 0; /* dynamic adap number */
|
force = 0; /* dynamic adap number */
|
||||||
class = 0; /* no class by default */
|
class = 0; /* no class by default */
|
||||||
if (pdata) {
|
if (pdata) {
|
||||||
|
@ -229,12 +237,13 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
} else
|
} else
|
||||||
/* discard unconfigured channels */
|
/* discard unconfigured channels */
|
||||||
break;
|
break;
|
||||||
|
idle_disconnect_pd = pdata->modes[num].deselect_on_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->virt_adaps[num] =
|
data->virt_adaps[num] =
|
||||||
i2c_add_mux_adapter(adap, &client->dev, client,
|
i2c_add_mux_adapter(adap, &client->dev, client,
|
||||||
force, num, class, pca954x_select_chan,
|
force, num, class, pca954x_select_chan,
|
||||||
(pdata && pdata->modes[num].deselect_on_exit)
|
(idle_disconnect_pd || idle_disconnect_dt)
|
||||||
? pca954x_deselect_mux : NULL);
|
? pca954x_deselect_mux : NULL);
|
||||||
|
|
||||||
if (data->virt_adaps[num] == NULL) {
|
if (data->virt_adaps[num] == NULL) {
|
||||||
|
|
|
@ -130,8 +130,6 @@ extern s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client,
|
||||||
* @probe: Callback for device binding
|
* @probe: Callback for device binding
|
||||||
* @remove: Callback for device unbinding
|
* @remove: Callback for device unbinding
|
||||||
* @shutdown: Callback for device shutdown
|
* @shutdown: Callback for device shutdown
|
||||||
* @suspend: Callback for device suspend
|
|
||||||
* @resume: Callback for device resume
|
|
||||||
* @alert: Alert callback, for example for the SMBus alert protocol
|
* @alert: Alert callback, for example for the SMBus alert protocol
|
||||||
* @command: Callback for bus-wide signaling (optional)
|
* @command: Callback for bus-wide signaling (optional)
|
||||||
* @driver: Device driver model driver
|
* @driver: Device driver model driver
|
||||||
|
@ -174,8 +172,6 @@ struct i2c_driver {
|
||||||
|
|
||||||
/* driver model interfaces that don't relate to enumeration */
|
/* driver model interfaces that don't relate to enumeration */
|
||||||
void (*shutdown)(struct i2c_client *);
|
void (*shutdown)(struct i2c_client *);
|
||||||
int (*suspend)(struct i2c_client *, pm_message_t mesg);
|
|
||||||
int (*resume)(struct i2c_client *);
|
|
||||||
|
|
||||||
/* Alert callback, for example for the SMBus alert protocol.
|
/* Alert callback, for example for the SMBus alert protocol.
|
||||||
* The format and meaning of the data value depends on the protocol.
|
* The format and meaning of the data value depends on the protocol.
|
||||||
|
|
Loading…
Reference in a new issue