forked from luck/tmp_suning_uos_patched
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
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:
|
||||
|
||||
- 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:
|
||||
|
|
|
@ -4,16 +4,34 @@ Required properties:
|
|||
- compatible : "opencores,i2c-ocores" or "aeroflexgaisler,i2cmst"
|
||||
- reg : bus address start and address range size of device
|
||||
- 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>
|
||||
- #size-cells : should be <0>
|
||||
|
||||
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-io-width : io register width in bytes (1, 2 or 4)
|
||||
- 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 {
|
||||
#address-cells = <1>;
|
||||
|
@ -21,7 +39,25 @@ Example:
|
|||
compatible = "opencores,i2c-ocores";
|
||||
reg = <0xa0000000 0x8>;
|
||||
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-io-width = <1>; /* 8 bit read/write */
|
||||
|
|
|
@ -21,6 +21,17 @@ Required on RK3066, RK3188 :
|
|||
Optional properties :
|
||||
|
||||
- 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:
|
||||
|
||||
|
@ -39,4 +50,7 @@ i2c0: i2c@2002d000 {
|
|||
|
||||
clock-names = "i2c";
|
||||
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
|
||||
infineon,slb9635tt Infineon SLB9635 (Soft-) I2C TPM (old protocol, max 100khz)
|
||||
infineon,slb9645tt Infineon SLB9645 I2C TPM (new protocol, max 400khz)
|
||||
isl,isl12057 Intersil ISL12057 I2C RTC Chip
|
||||
isil,isl29028 (deprecated, use isl)
|
||||
isl,isl29028 Intersil ISL29028 Ambient Light and Proximity Sensor
|
||||
isil,isl12057 Intersil ISL12057 I2C RTC Chip
|
||||
isil,isl29028 Intersil ISL29028 Ambient Light and Proximity Sensor
|
||||
maxim,ds1050 5 Bit Programmable, Pulse-Width Modulator
|
||||
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
|
||||
|
|
|
@ -12,7 +12,7 @@ FUNCTIONALITY CONSTANTS
|
|||
-----------------------
|
||||
|
||||
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
|
||||
adapters typically can not do these)
|
||||
|
|
|
@ -79,7 +79,7 @@ config I2C_AMD8111
|
|||
|
||||
config I2C_HIX5HD2
|
||||
tristate "Hix5hd2 high-speed I2C driver"
|
||||
depends on ARCH_HIX5HD2
|
||||
depends on ARCH_HIX5HD2 || COMPILE_TEST
|
||||
help
|
||||
Say Y here to include support for high-speed I2C controller in the
|
||||
Hisilicon based hix5hd2 SoCs.
|
||||
|
@ -372,6 +372,16 @@ config I2C_BCM2835
|
|||
This support is also available as a module. If so, the module
|
||||
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
|
||||
tristate "BCM Kona I2C adapter"
|
||||
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
|
||||
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
|
||||
tristate "EFM32 I2C controller"
|
||||
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_AXXIA) += i2c-axxia.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_CADENCE) += i2c-cadence.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_PLATFORM) += i2c-designware-platform.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
|
||||
i2c-designware-pci-objs := i2c-designware-pcidrv.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
|
||||
* @send_count: Number of bytes still expected to send
|
||||
* @recv_count: Number of bytes still expected to receive
|
||||
* @curr_recv_count: Number of bytes to be received in current transfer
|
||||
* @irq: IRQ number
|
||||
* @input_clk: Input clock to I2C controller
|
||||
* @i2c_clk: Maximum I2C clock speed
|
||||
|
@ -146,6 +147,7 @@ struct cdns_i2c {
|
|||
u8 suspended;
|
||||
unsigned int send_count;
|
||||
unsigned int recv_count;
|
||||
unsigned int curr_recv_count;
|
||||
int irq;
|
||||
unsigned long input_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)
|
||||
{
|
||||
unsigned int isr_status, avail_bytes;
|
||||
unsigned int bytes_to_recv, bytes_to_send;
|
||||
unsigned int isr_status, avail_bytes, updatetx;
|
||||
unsigned int bytes_to_send;
|
||||
struct cdns_i2c *id = ptr;
|
||||
/* Signal completion only after everything is updated */
|
||||
int done_flag = 0;
|
||||
irqreturn_t status = IRQ_NONE;
|
||||
|
||||
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
|
||||
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
|
||||
|
||||
/* Handling nack and arbitration lost interrupt */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Handling Data interrupt */
|
||||
if ((isr_status & CDNS_I2C_IXR_DATA) &&
|
||||
(id->recv_count >= CDNS_I2C_DATA_INTR_DEPTH)) {
|
||||
/* Always read data interrupt threshold bytes */
|
||||
bytes_to_recv = CDNS_I2C_DATA_INTR_DEPTH;
|
||||
id->recv_count -= CDNS_I2C_DATA_INTR_DEPTH;
|
||||
avail_bytes = cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
/*
|
||||
* Check if transfer size register needs to be updated again for a
|
||||
* large data receive operation.
|
||||
*/
|
||||
updatetx = 0;
|
||||
if (id->recv_count > id->curr_recv_count)
|
||||
updatetx = 1;
|
||||
|
||||
/*
|
||||
* if the tranfer size register value is zero, then
|
||||
* check for the remaining bytes and update the
|
||||
* transfer size register.
|
||||
*/
|
||||
if (!avail_bytes) {
|
||||
if (id->recv_count > CDNS_I2C_TRANSFER_SIZE)
|
||||
cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
|
||||
CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
else
|
||||
cdns_i2c_writereg(id->recv_count,
|
||||
CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
}
|
||||
/* When receiving, handle data interrupt and completion interrupt */
|
||||
if (id->p_recv_buf &&
|
||||
((isr_status & CDNS_I2C_IXR_COMP) ||
|
||||
(isr_status & CDNS_I2C_IXR_DATA))) {
|
||||
/* Read data if receive data valid is set */
|
||||
while (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) &
|
||||
CDNS_I2C_SR_RXDV) {
|
||||
/*
|
||||
* Clear hold bit that was set for FIFO control if
|
||||
* RX data left is less than FIFO depth, unless
|
||||
* repeated start is selected.
|
||||
*/
|
||||
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)++ =
|
||||
cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
|
||||
id->recv_count--;
|
||||
id->curr_recv_count--;
|
||||
|
||||
if (!id->bus_hold_flag &&
|
||||
(id->recv_count <= CDNS_I2C_FIFO_DEPTH))
|
||||
cdns_i2c_clear_bus_hold(id);
|
||||
if (updatetx &&
|
||||
(id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1))
|
||||
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;
|
||||
}
|
||||
|
||||
/* Handling Transfer Complete interrupt */
|
||||
if (isr_status & CDNS_I2C_IXR_COMP) {
|
||||
if (!id->p_recv_buf) {
|
||||
/*
|
||||
* If the device is sending data If there is further
|
||||
* 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 -
|
||||
cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
if (id->send_count > avail_bytes)
|
||||
bytes_to_send = avail_bytes;
|
||||
else
|
||||
bytes_to_send = id->send_count;
|
||||
/* When sending, handle transfer complete interrupt */
|
||||
if ((isr_status & CDNS_I2C_IXR_COMP) && !id->p_recv_buf) {
|
||||
/*
|
||||
* If there is more data to be sent, calculate the
|
||||
* space available in FIFO and fill with that many bytes.
|
||||
*/
|
||||
if (id->send_count) {
|
||||
avail_bytes = CDNS_I2C_FIFO_DEPTH -
|
||||
cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
if (id->send_count > avail_bytes)
|
||||
bytes_to_send = avail_bytes;
|
||||
else
|
||||
bytes_to_send = id->send_count;
|
||||
|
||||
while (bytes_to_send--) {
|
||||
cdns_i2c_writereg(
|
||||
(*(id->p_send_buf)++),
|
||||
CDNS_I2C_DATA_OFFSET);
|
||||
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;
|
||||
while (bytes_to_send--) {
|
||||
cdns_i2c_writereg(
|
||||
(*(id->p_send_buf)++),
|
||||
CDNS_I2C_DATA_OFFSET);
|
||||
id->send_count--;
|
||||
}
|
||||
if (!id->send_count && !id->bus_hold_flag)
|
||||
cdns_i2c_clear_bus_hold(id);
|
||||
} else {
|
||||
if (!id->bus_hold_flag)
|
||||
cdns_i2c_clear_bus_hold(id);
|
||||
/*
|
||||
* If the device is receiving data, then signal
|
||||
* the completion of transaction and read the data
|
||||
* present in the FIFO. Signal the completion of
|
||||
* transaction.
|
||||
* Signal the completion of transaction and
|
||||
* clear the hold bus bit if there are no
|
||||
* further messages to be processed.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
if (!id->send_count && !id->bus_hold_flag)
|
||||
cdns_i2c_clear_bus_hold(id);
|
||||
|
||||
status = IRQ_HANDLED;
|
||||
}
|
||||
|
@ -289,8 +315,6 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
|
|||
if (id->err_status)
|
||||
status = IRQ_HANDLED;
|
||||
|
||||
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
|
||||
|
||||
if (done_flag)
|
||||
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)
|
||||
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
|
||||
* '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
|
||||
* 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_XFER_SIZE_OFFSET);
|
||||
else
|
||||
id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
|
||||
} else {
|
||||
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 */
|
||||
if (!id->bus_hold_flag &&
|
||||
((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.
|
||||
*/
|
||||
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;
|
||||
reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
||||
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;
|
||||
|
||||
if (dev->accessor_flags & ACCESS_16BIT)
|
||||
value = readw(dev->base + offset) |
|
||||
(readw(dev->base + offset + 2) << 16);
|
||||
value = readw_relaxed(dev->base + offset) |
|
||||
(readw_relaxed(dev->base + offset + 2) << 16);
|
||||
else
|
||||
value = readl(dev->base + offset);
|
||||
value = readl_relaxed(dev->base + offset);
|
||||
|
||||
if (dev->accessor_flags & ACCESS_SWAP)
|
||||
return swab32(value);
|
||||
|
@ -187,10 +187,10 @@ void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset)
|
|||
b = swab32(b);
|
||||
|
||||
if (dev->accessor_flags & ACCESS_16BIT) {
|
||||
writew((u16)b, dev->base + offset);
|
||||
writew((u16)(b >> 16), dev->base + offset + 2);
|
||||
writew_relaxed((u16)b, dev->base + offset);
|
||||
writew_relaxed((u16)(b >> 16), dev->base + offset + 2);
|
||||
} 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 reg;
|
||||
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);
|
||||
|
||||
|
@ -298,6 +307,8 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
|
|||
} else if (reg != DW_IC_COMP_TYPE_VALUE) {
|
||||
dev_err(dev->dev, "Unknown Synopsys component type: "
|
||||
"0x%08x\n", reg);
|
||||
if (dev->release_lock)
|
||||
dev->release_lock(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -309,40 +320,39 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
|
|||
sda_falling_time = dev->sda_falling_time ?: 300; /* ns */
|
||||
scl_falling_time = dev->scl_falling_time ?: 300; /* ns */
|
||||
|
||||
/* 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 */
|
||||
/* Set SCL timing parameters for standard-mode */
|
||||
if (dev->ss_hcnt && dev->ss_lcnt) {
|
||||
hcnt = dev->ss_hcnt;
|
||||
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, lcnt, DW_IC_SS_SCL_LCNT);
|
||||
dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
|
||||
|
||||
/* 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 */
|
||||
|
||||
/* Set SCL timing parameters for fast-mode */
|
||||
if (dev->fs_hcnt && dev->fs_lcnt) {
|
||||
hcnt = dev->fs_hcnt;
|
||||
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, lcnt, DW_IC_FS_SCL_LCNT);
|
||||
|
@ -364,6 +374,9 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
|
|||
|
||||
/* configure the i2c master */
|
||||
dw_writel(dev, dev->master_cfg , DW_IC_CON);
|
||||
|
||||
if (dev->release_lock)
|
||||
dev->release_lock(dev);
|
||||
return 0;
|
||||
}
|
||||
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->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);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
@ -672,6 +693,10 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
ret = -EIO;
|
||||
|
||||
done:
|
||||
if (dev->release_lock)
|
||||
dev->release_lock(dev);
|
||||
|
||||
done_nolock:
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
mutex_unlock(&dev->lock);
|
||||
|
|
|
@ -61,6 +61,9 @@
|
|||
* @ss_lcnt: standard speed LCNT value
|
||||
* @fs_hcnt: fast speed HCNT 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
|
||||
* values than the one computed based only on the input clock frequency.
|
||||
|
@ -101,6 +104,9 @@ struct dw_i2c_dev {
|
|||
u16 ss_lcnt;
|
||||
u16 fs_hcnt;
|
||||
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
|
||||
|
@ -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_disable_int(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) 2007 MontaVista Software Inc.
|
||||
* 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"
|
||||
|
||||
enum dw_pci_ctl_id_t {
|
||||
moorestown_0,
|
||||
moorestown_1,
|
||||
moorestown_2,
|
||||
|
||||
medfield_0,
|
||||
medfield_1,
|
||||
medfield_2,
|
||||
|
@ -101,28 +97,7 @@ static struct dw_scl_sda_cfg hsw_config = {
|
|||
.sda_hold = 0x9,
|
||||
};
|
||||
|
||||
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,
|
||||
},
|
||||
static struct dw_pci_controller dw_pci_controllers[] = {
|
||||
[medfield_0] = {
|
||||
.bus_num = 0,
|
||||
.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,
|
||||
.tx_fifo_depth = 32,
|
||||
.rx_fifo_depth = 32,
|
||||
.clk_khz = 100000,
|
||||
.functionality = I2C_FUNC_10BIT_ADDR,
|
||||
.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,
|
||||
.tx_fifo_depth = 32,
|
||||
.rx_fifo_depth = 32,
|
||||
.clk_khz = 100000,
|
||||
.functionality = I2C_FUNC_10BIT_ADDR,
|
||||
.scl_sda_cfg = &hsw_config,
|
||||
},
|
||||
|
@ -259,7 +232,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
|||
dev->functionality = controller->functionality |
|
||||
DW_DEFAULT_FUNCTIONALITY;
|
||||
|
||||
dev->master_cfg = controller->bus_cfg;
|
||||
dev->master_cfg = controller->bus_cfg;
|
||||
if (controller->scl_sda_cfg) {
|
||||
cfg = controller->scl_sda_cfg;
|
||||
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");
|
||||
|
||||
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 */
|
||||
{ PCI_VDEVICE(INTEL, 0x0817), medfield_3,},
|
||||
{ PCI_VDEVICE(INTEL, 0x0817), medfield_3 },
|
||||
{ PCI_VDEVICE(INTEL, 0x0818), medfield_4 },
|
||||
{ PCI_VDEVICE(INTEL, 0x0819), medfield_5 },
|
||||
{ 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, 0x9c62), haswell },
|
||||
/* Braswell / Cherrytrail */
|
||||
{ PCI_VDEVICE(INTEL, 0x22C1), baytrail,},
|
||||
{ PCI_VDEVICE(INTEL, 0x22C1), baytrail },
|
||||
{ PCI_VDEVICE(INTEL, 0x22C2), baytrail },
|
||||
{ PCI_VDEVICE(INTEL, 0x22C3), 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;
|
||||
}
|
||||
|
||||
r = i2c_dw_eval_lock_support(dev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dev->functionality =
|
||||
I2C_FUNC_I2C |
|
||||
I2C_FUNC_10BIT_ADDR |
|
||||
|
@ -257,10 +261,14 @@ static int dw_i2c_probe(struct platform_device *pdev)
|
|||
return r;
|
||||
}
|
||||
|
||||
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);
|
||||
if (dev->pm_runtime_disabled) {
|
||||
pm_runtime_forbid(&pdev->dev);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
@ -310,7 +318,9 @@ static int dw_i2c_resume(struct device *dev)
|
|||
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
|
||||
|
||||
clk_prepare_enable(i_dev->clk);
|
||||
i2c_dw_init(i_dev);
|
||||
|
||||
if (!i_dev->pm_runtime_disabled)
|
||||
i2c_dw_init(i_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -201,7 +201,7 @@ struct imx_i2c_struct {
|
|||
void __iomem *base;
|
||||
wait_queue_head_t queue;
|
||||
unsigned long i2csr;
|
||||
unsigned int disable_delay;
|
||||
unsigned int disable_delay;
|
||||
int stopped;
|
||||
unsigned int ifdr; /* IMX_I2C_IFDR */
|
||||
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");
|
||||
if (!dma->chan_tx) {
|
||||
dev_dbg(dev, "can't request DMA tx channel\n");
|
||||
ret = -ENODEV;
|
||||
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");
|
||||
if (!dma->chan_rx) {
|
||||
dev_dbg(dev, "can't request DMA rx channel\n");
|
||||
ret = -ENODEV;
|
||||
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);
|
||||
if (i2c_imx->cur_clk == i2c_clk_rate)
|
||||
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;
|
||||
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)
|
||||
i = i2c_imx->hwdata->ndivs - 1;
|
||||
else
|
||||
for (i = 0; i2c_clk_div[i].div < div; i++);
|
||||
for (i = 0; i2c_clk_div[i].div < div; i++)
|
||||
;
|
||||
|
||||
/* Store divider value */
|
||||
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(
|
||||
&i2c_imx->dma->cmd_complete,
|
||||
msecs_to_jiffies(DMA_TIMEOUT));
|
||||
if (result <= 0) {
|
||||
if (result == 0) {
|
||||
dmaengine_terminate_all(dma->chan_using);
|
||||
return result ?: -ETIMEDOUT;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* 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(
|
||||
&i2c_imx->dma->cmd_complete,
|
||||
msecs_to_jiffies(DMA_TIMEOUT));
|
||||
if (result <= 0) {
|
||||
if (result == 0) {
|
||||
dmaengine_terminate_all(dma->chan_using);
|
||||
return result ?: -ETIMEDOUT;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
for (i = 0; i < msgs->len; i++) {
|
||||
u8 len = 0;
|
||||
|
||||
result = i2c_imx_trx_complete(i2c_imx);
|
||||
if (result)
|
||||
return result;
|
||||
|
@ -917,15 +917,16 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
|||
/* write/read data */
|
||||
#ifdef CONFIG_I2C_DEBUG_BUS
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> CONTROL: IEN=%d, IIEN=%d, "
|
||||
"MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n", __func__,
|
||||
dev_dbg(&i2c_imx->adapter.dev,
|
||||
"<%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_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0),
|
||||
(temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0));
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||
dev_dbg(&i2c_imx->adapter.dev,
|
||||
"<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, "
|
||||
"IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n", __func__,
|
||||
"<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n",
|
||||
__func__,
|
||||
(temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0),
|
||||
(temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 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.algo = &i2c_imx_algo;
|
||||
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->base = base;
|
||||
|
||||
|
@ -1063,7 +1064,7 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
i2c_imx->adapter.name);
|
||||
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);
|
||||
|
||||
return 0; /* Return OK */
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -35,7 +36,9 @@ struct ocores_i2c {
|
|||
int pos;
|
||||
int nmsgs;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
static void ocores_init(struct ocores_i2c *i2c)
|
||||
static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
|
||||
{
|
||||
int prescale;
|
||||
int diff;
|
||||
u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
|
||||
|
||||
/* make sure the device is disabled */
|
||||
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_PREHIGH, prescale >> 8);
|
||||
|
||||
/* Init the device */
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);
|
||||
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;
|
||||
const struct of_device_id *match;
|
||||
u32 val;
|
||||
u32 clock_frequency;
|
||||
bool clock_frequency_present;
|
||||
|
||||
if (of_property_read_u32(np, "reg-shift", &i2c->reg_shift)) {
|
||||
/* 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)) {
|
||||
dev_err(&pdev->dev,
|
||||
"Missing required parameter 'clock-frequency'\n");
|
||||
return -ENODEV;
|
||||
clock_frequency_present = !of_property_read_u32(np, "clock-frequency",
|
||||
&clock_frequency);
|
||||
i2c->bus_clock_khz = 100;
|
||||
|
||||
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",
|
||||
&i2c->reg_io_width);
|
||||
|
@ -368,7 +416,8 @@ static int ocores_i2c_probe(struct platform_device *pdev)
|
|||
if (pdata) {
|
||||
i2c->reg_shift = pdata->reg_shift;
|
||||
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 {
|
||||
ret = ocores_i2c_of_probe(pdev, i2c);
|
||||
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);
|
||||
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 */
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
|
||||
if (!IS_ERR(i2c->clk))
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -458,6 +512,8 @@ static int ocores_i2c_suspend(struct device *dev)
|
|||
/* make sure the device is disabled */
|
||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
|
||||
|
||||
if (!IS_ERR(i2c->clk))
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -465,9 +521,20 @@ static int ocores_i2c_resume(struct device *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);
|
||||
|
|
|
@ -148,13 +148,6 @@ static inline u32 pmcmsptwi_clock_to_reg(
|
|||
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)
|
||||
{
|
||||
return ((cfg->arbf & 0xf) << 12) |
|
||||
|
|
|
@ -102,6 +102,9 @@ struct rk3x_i2c {
|
|||
|
||||
/* Settings */
|
||||
unsigned int scl_frequency;
|
||||
unsigned int scl_rise_ns;
|
||||
unsigned int scl_fall_ns;
|
||||
unsigned int sda_fall_ns;
|
||||
|
||||
/* Synchronization & notification */
|
||||
spinlock_t lock;
|
||||
|
@ -435,6 +438,9 @@ static irqreturn_t rk3x_i2c_irq(int irqno, void *dev_id)
|
|||
*
|
||||
* @clk_rate: I2C input clock 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_high: Divider output for high
|
||||
*
|
||||
|
@ -443,11 +449,16 @@ static irqreturn_t rk3x_i2c_irq(int irqno, void *dev_id)
|
|||
* too high, we silently use the highest possible 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 min_low_ns, min_high_ns;
|
||||
unsigned long max_data_hold_ns;
|
||||
unsigned long spec_min_low_ns, spec_min_high_ns;
|
||||
unsigned long spec_setup_start, spec_max_data_hold_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 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;
|
||||
|
||||
/*
|
||||
* min_low_ns: The minimum number of ns we need to hold low
|
||||
* to meet i2c spec
|
||||
* min_high_ns: The minimum number of ns we need to hold high
|
||||
* to meet i2c spec
|
||||
* max_low_ns: The maximum number of ns we can hold low
|
||||
* to meet i2c spec
|
||||
* min_low_ns: The minimum number of ns we need to hold low to
|
||||
* meet I2C specification, should include fall time.
|
||||
* min_high_ns: The minimum number of ns we need to hold high to
|
||||
* meet I2C specification, should include rise time.
|
||||
* max_low_ns: The maximum number of ns we can hold low to meet
|
||||
* 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
|
||||
* for half the low time.
|
||||
*/
|
||||
if (scl_rate <= 100000) {
|
||||
min_low_ns = 4700;
|
||||
min_high_ns = 4000;
|
||||
max_data_hold_ns = 3450;
|
||||
/* Standard-mode */
|
||||
spec_min_low_ns = 4700;
|
||||
spec_setup_start = 4700;
|
||||
spec_min_high_ns = 4000;
|
||||
spec_max_data_hold_ns = 3450;
|
||||
data_hold_buffer_ns = 50;
|
||||
} else {
|
||||
min_low_ns = 1300;
|
||||
min_high_ns = 600;
|
||||
max_data_hold_ns = 900;
|
||||
/* Fast-mode */
|
||||
spec_min_low_ns = 1300;
|
||||
spec_setup_start = 600;
|
||||
spec_min_high_ns = 600;
|
||||
spec_max_data_hold_ns = 900;
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
/*
|
||||
* This is the maximum divider so we don't go over the max.
|
||||
* We don't round up here (we round down) since this is a 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 maximum.
|
||||
*/
|
||||
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,
|
||||
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)
|
||||
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;
|
||||
int ret;
|
||||
|
||||
ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, &div_low,
|
||||
&div_high);
|
||||
|
||||
ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, i2c->scl_rise_ns,
|
||||
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);
|
||||
|
||||
clk_enable(i2c->clk);
|
||||
|
@ -633,9 +665,10 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
|
|||
switch (event) {
|
||||
case PRE_RATE_CHANGE:
|
||||
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;
|
||||
}
|
||||
|
||||
/* scale up */
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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));
|
||||
i2c->adap.owner = THIS_MODULE;
|
||||
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) {
|
||||
BUG_ON(buf_remaining > 3);
|
||||
val = i2c_readl(i2c_dev, I2C_RX_FIFO);
|
||||
val = cpu_to_le32(val);
|
||||
memcpy(buf, &val, buf_remaining);
|
||||
buf_remaining = 0;
|
||||
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) {
|
||||
BUG_ON(buf_remaining > 3);
|
||||
memcpy(&val, buf, buf_remaining);
|
||||
val = le32_to_cpu(val);
|
||||
|
||||
/* Again update before writing to FIFO to make sure isr sees. */
|
||||
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;
|
||||
|
||||
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;
|
||||
if (sb->access_mode == ACPI_I2C_10BIT_MODE)
|
||||
info->flags |= I2C_CLIENT_TEN;
|
||||
|
@ -698,101 +698,6 @@ static void i2c_device_shutdown(struct device *dev)
|
|||
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)
|
||||
{
|
||||
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 ?
|
||||
to_i2c_client(dev)->name : to_i2c_adapter(dev)->name);
|
||||
}
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
||||
|
||||
static ssize_t
|
||||
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);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
||||
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
|
||||
|
||||
static struct attribute *i2c_dev_attrs[] = {
|
||||
|
@ -827,29 +731,7 @@ static struct attribute *i2c_dev_attrs[] = {
|
|||
&dev_attr_modalias.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
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
|
||||
)
|
||||
};
|
||||
ATTRIBUTE_GROUPS(i2c_dev);
|
||||
|
||||
struct bus_type i2c_bus_type = {
|
||||
.name = "i2c",
|
||||
|
@ -857,12 +739,11 @@ struct bus_type i2c_bus_type = {
|
|||
.probe = i2c_device_probe,
|
||||
.remove = i2c_device_remove,
|
||||
.shutdown = i2c_device_shutdown,
|
||||
.pm = &i2c_device_pm_ops,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(i2c_bus_type);
|
||||
|
||||
static struct device_type i2c_client_type = {
|
||||
.groups = i2c_dev_attr_groups,
|
||||
.groups = i2c_dev_groups,
|
||||
.uevent = i2c_device_uevent,
|
||||
.release = i2c_client_dev_release,
|
||||
};
|
||||
|
@ -1261,6 +1142,7 @@ i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
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
|
||||
|
@ -1315,8 +1197,6 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
|
|||
"delete_device");
|
||||
return res;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device);
|
||||
static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL,
|
||||
i2c_sysfs_delete_device);
|
||||
|
||||
|
@ -1326,18 +1206,10 @@ static struct attribute *i2c_adapter_attrs[] = {
|
|||
&dev_attr_delete_device.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
ATTRIBUTE_GROUPS(i2c_adapter);
|
||||
|
||||
struct device_type i2c_adapter_type = {
|
||||
.groups = i2c_adapter_attr_groups,
|
||||
.groups = i2c_adapter_groups,
|
||||
.release = i2c_adapter_dev_release,
|
||||
};
|
||||
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))
|
||||
info.flags |= I2C_CLIENT_WAKE;
|
||||
|
||||
request_module("%s%s", I2C_MODULE_PREFIX, info.type);
|
||||
|
||||
result = i2c_new_device(adap, &info);
|
||||
if (result == NULL) {
|
||||
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 */
|
||||
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);
|
||||
device_unregister(&adap->dev);
|
||||
|
||||
/* wait for sysfs to drop all references */
|
||||
wait_for_completion(&adap->dev_released);
|
||||
|
||||
/* free bus id */
|
||||
|
@ -1859,14 +1733,6 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
|
|||
if (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);
|
||||
|
||||
INIT_LIST_HEAD(&driver->clients);
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <linux/i2c-mux.h>
|
||||
#include <linux/i2c/pca954x.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm.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 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;
|
||||
int num, force, class;
|
||||
struct pca954x *data;
|
||||
|
@ -217,8 +220,13 @@ static int pca954x_probe(struct i2c_client *client,
|
|||
data->type = id->driver_data;
|
||||
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 */
|
||||
for (num = 0; num < chips[data->type].nchans; num++) {
|
||||
bool idle_disconnect_pd = false;
|
||||
|
||||
force = 0; /* dynamic adap number */
|
||||
class = 0; /* no class by default */
|
||||
if (pdata) {
|
||||
|
@ -229,12 +237,13 @@ static int pca954x_probe(struct i2c_client *client,
|
|||
} else
|
||||
/* discard unconfigured channels */
|
||||
break;
|
||||
idle_disconnect_pd = pdata->modes[num].deselect_on_exit;
|
||||
}
|
||||
|
||||
data->virt_adaps[num] =
|
||||
i2c_add_mux_adapter(adap, &client->dev, client,
|
||||
force, num, class, pca954x_select_chan,
|
||||
(pdata && pdata->modes[num].deselect_on_exit)
|
||||
(idle_disconnect_pd || idle_disconnect_dt)
|
||||
? pca954x_deselect_mux : 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
|
||||
* @remove: Callback for device unbinding
|
||||
* @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
|
||||
* @command: Callback for bus-wide signaling (optional)
|
||||
* @driver: Device driver model driver
|
||||
|
@ -174,8 +172,6 @@ struct i2c_driver {
|
|||
|
||||
/* driver model interfaces that don't relate to enumeration */
|
||||
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.
|
||||
* The format and meaning of the data value depends on the protocol.
|
||||
|
|
Loading…
Reference in New Issue
Block a user