mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
Merge branch 'i2c/for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: "Mostly usual driver updates and improvements. The changelog should give an idea. Standing out is the i2c-qup driver with lots of new capabilities and we also have now an i2c-demuxer. I'd especially like to welcome Peter Rosin as the i2c-mux maintainer. He has an interesting series for muxes in the queue and agreed to look after this part of the subsystem. Thank you, Peter, and welcome again! The octeon changes were applied pretty recently before the merge window. I am aware. They are the first (and relatively simple) patches of a larger overhaul to this driver. In case something goes wrong with them, they are easy to fix (or revert). The advantage I see is that they are out of the way, and I can concentrate on the next block of patches. I really would like to apply the overhaul in smaller batches to avoid regressions. And waiting a cycle for the introductory patches seemed too much of a delay for me" * 'i2c/for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (39 commits) i2c: octeon: Support I2C_M_RECV_LEN i2c: octeon: Cleanup resource allocation code i2c: octeon: Cleanup i2c-octeon driver MAINTAINERS: add Peter Rosin as i2c mux maintainer dt-bindings: i2c: Spelling s/propoerty/property/ i2c: immediately mark ourselves as registered i2c: i801: sort IDs alphabetically MAINTAINERS: Mika and me are designated reviewers for I2C DESIGNWARE i2c: octeon: Cleanup kerneldoc comments i2c: do not use internal data from driver core i2c: cadence: Fix the kernel-doc warnings i2c: imx: remove extra spaces. i2c: rcar: don't open code of_device_get_match_data() i2c: qup: Fix fifo handling after adding V2 support i2c: xiic: Implement power management i2c: piix4: Pre-shift the port number i2c: piix4: Always use the same type for port i2c: piix4: Support alternative port selection register i2c: tegra: don't open code of_device_get_match_data() i2c: riic, sh_mobile, rcar: Use ARCH_RENESAS ...
This commit is contained in:
commit
b852495908
23
Documentation/ABI/testing/sysfs-platform-i2c-demux-pinctrl
Normal file
23
Documentation/ABI/testing/sysfs-platform-i2c-demux-pinctrl
Normal file
@ -0,0 +1,23 @@
|
||||
What: /sys/devices/platform/<i2c-demux-name>/cur_master
|
||||
Date: January 2016
|
||||
KernelVersion: 4.6
|
||||
Contact: Wolfram Sang <wsa@the-dreams.de>
|
||||
Description:
|
||||
|
||||
This file selects the active I2C master for a demultiplexed bus.
|
||||
|
||||
Write 0 there for the first master, 1 for the second etc. Reading the file will
|
||||
give you a list with the active master marked. Example from a Renesas Lager
|
||||
board:
|
||||
|
||||
root@Lager:~# cat /sys/devices/platform/i2c@8/cur_master
|
||||
* 0 - /i2c@9
|
||||
1 - /i2c@e6520000
|
||||
2 - /i2c@e6530000
|
||||
|
||||
root@Lager:~# echo 2 > /sys/devices/platform/i2c@8/cur_master
|
||||
|
||||
root@Lager:~# cat /sys/devices/platform/i2c@8/cur_master
|
||||
0 - /i2c@9
|
||||
1 - /i2c@e6520000
|
||||
* 2 - /i2c@e6530000
|
135
Documentation/devicetree/bindings/i2c/i2c-demux-pinctrl.txt
Normal file
135
Documentation/devicetree/bindings/i2c/i2c-demux-pinctrl.txt
Normal file
@ -0,0 +1,135 @@
|
||||
Pinctrl-based I2C Bus DeMux
|
||||
|
||||
This binding describes an I2C bus demultiplexer that uses pin multiplexing to
|
||||
route the I2C signals, and represents the pin multiplexing configuration using
|
||||
the pinctrl device tree bindings. This may be used to select one I2C IP core at
|
||||
runtime which may have a better feature set for a given task than another I2C
|
||||
IP core on the SoC. The most simple example is to fall back to GPIO bitbanging
|
||||
if your current runtime configuration hits an errata of the internal IP core.
|
||||
|
||||
+-------------------------------+
|
||||
| SoC |
|
||||
| | +-----+ +-----+
|
||||
| +------------+ | | dev | | dev |
|
||||
| |I2C IP Core1|--\ | +-----+ +-----+
|
||||
| +------------+ \-------+ | | |
|
||||
| |Pinctrl|--|------+--------+
|
||||
| +------------+ +-------+ |
|
||||
| |I2C IP Core2|--/ |
|
||||
| +------------+ |
|
||||
| |
|
||||
+-------------------------------+
|
||||
|
||||
Required properties:
|
||||
- compatible: "i2c-demux-pinctrl"
|
||||
- i2c-parent: List of phandles of I2C masters available for selection. The first
|
||||
one will be used as default.
|
||||
- i2c-bus-name: The name of this bus. Also needed as pinctrl-name for the I2C
|
||||
parents.
|
||||
|
||||
Furthermore, I2C mux properties and child nodes. See mux.txt in this directory.
|
||||
|
||||
Example:
|
||||
|
||||
Here is a snipplet for a bus to be demuxed. It contains various i2c clients for
|
||||
HDMI, so the bus is named "i2c-hdmi":
|
||||
|
||||
i2chdmi: i2c@8 {
|
||||
|
||||
compatible = "i2c-demux-pinctrl";
|
||||
i2c-parent = <&gpioi2c>, <&iic2>, <&i2c2>;
|
||||
i2c-bus-name = "i2c-hdmi";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ak4643: sound-codec@12 {
|
||||
compatible = "asahi-kasei,ak4643";
|
||||
|
||||
#sound-dai-cells = <0>;
|
||||
reg = <0x12>;
|
||||
};
|
||||
|
||||
composite-in@20 {
|
||||
compatible = "adi,adv7180";
|
||||
reg = <0x20>;
|
||||
remote = <&vin1>;
|
||||
|
||||
port {
|
||||
adv7180: endpoint {
|
||||
bus-width = <8>;
|
||||
remote-endpoint = <&vin1ep0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
hdmi@39 {
|
||||
compatible = "adi,adv7511w";
|
||||
reg = <0x39>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <15 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
adi,input-depth = <8>;
|
||||
adi,input-colorspace = "rgb";
|
||||
adi,input-clock = "1x";
|
||||
adi,input-style = <1>;
|
||||
adi,input-justification = "evenly";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
adv7511_in: endpoint {
|
||||
remote-endpoint = <&du_out_lvds0>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
adv7511_out: endpoint {
|
||||
remote-endpoint = <&hdmi_con>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
And for clarification, here are the snipplets for the i2c-parents:
|
||||
|
||||
gpioi2c: i2c@9 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "i2c-gpio";
|
||||
status = "disabled";
|
||||
gpios = <&gpio5 6 GPIO_ACTIVE_HIGH /* sda */
|
||||
&gpio5 5 GPIO_ACTIVE_HIGH /* scl */
|
||||
>;
|
||||
i2c-gpio,delay-us = <5>;
|
||||
};
|
||||
|
||||
...
|
||||
|
||||
&i2c2 {
|
||||
pinctrl-0 = <&i2c2_pins>;
|
||||
pinctrl-names = "i2c-hdmi";
|
||||
|
||||
clock-frequency = <100000>;
|
||||
};
|
||||
|
||||
...
|
||||
|
||||
&iic2 {
|
||||
pinctrl-0 = <&iic2_pins>;
|
||||
pinctrl-names = "i2c-hdmi";
|
||||
|
||||
clock-frequency = <100000>;
|
||||
};
|
||||
|
||||
Please note:
|
||||
|
||||
- pinctrl properties for the parent I2C controllers need a pinctrl state
|
||||
with the same name as i2c-bus-name, not "default"!
|
||||
|
||||
- the i2c masters must have their status "disabled". This driver will
|
||||
enable them at runtime when needed.
|
@ -11,7 +11,7 @@ Required properties:
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz.
|
||||
The absence of the propoerty indicates the default frequency 100 kHz.
|
||||
The absence of the property indicates the default frequency 100 kHz.
|
||||
- dmas: A list of two dma specifiers, one for each entry in dma-names.
|
||||
- dma-names: should contain "tx" and "rx".
|
||||
- scl-gpios: specify the gpio related to SCL pin
|
||||
|
@ -17,7 +17,7 @@ Required properties:
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency: desired I2C bus clock frequency in Hz. The absence of this
|
||||
propoerty indicates the default frequency 100 kHz.
|
||||
property indicates the default frequency 100 kHz.
|
||||
- clocks: clock specifier.
|
||||
|
||||
- i2c-scl-falling-time-ns: see i2c.txt
|
||||
|
@ -8,7 +8,7 @@ Required properties :
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz.
|
||||
The absence of the propoerty indicates the default frequency 100 kHz.
|
||||
The absence of the property indicates the default frequency 100 kHz.
|
||||
|
||||
Examples :
|
||||
|
||||
|
@ -6,14 +6,17 @@ Required properties:
|
||||
- interrupts : IIC controller unterrupt
|
||||
- #address-cells = <1>
|
||||
- #size-cells = <0>
|
||||
- clocks: Input clock specifier. Refer to common clock bindings.
|
||||
|
||||
Optional properties:
|
||||
- Child nodes conforming to i2c bus binding
|
||||
- clock-names: Input clock name, should be 'pclk'.
|
||||
|
||||
Example:
|
||||
|
||||
axi_iic_0: i2c@40800000 {
|
||||
compatible = "xlnx,xps-iic-2.00.a";
|
||||
clocks = <&clkc 15>;
|
||||
interrupts = < 1 2 >;
|
||||
reg = < 0x40800000 0x10000 >;
|
||||
|
||||
|
14
MAINTAINERS
14
MAINTAINERS
@ -5270,6 +5270,16 @@ F: include/linux/hyperv.h
|
||||
F: tools/hv/
|
||||
F: Documentation/ABI/stable/sysfs-bus-vmbus
|
||||
|
||||
I2C MUXES
|
||||
M: Peter Rosin <peda@axentia.se>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/i2c/muxes/
|
||||
F: Documentation/devicetree/bindings/i2c/i2c-mux*
|
||||
F: drivers/i2c/i2c-mux.c
|
||||
F: drivers/i2c/muxes/
|
||||
F: include/linux/i2c-mux.h
|
||||
|
||||
I2C OVER PARALLEL PORT
|
||||
M: Jean Delvare <jdelvare@suse.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
@ -9676,9 +9686,9 @@ F: Documentation/devicetree/bindings/net/snps,dwc-qos-ethernet.txt
|
||||
F: drivers/net/ethernet/synopsys/dwc_eth_qos.c
|
||||
|
||||
SYNOPSYS DESIGNWARE I2C DRIVER
|
||||
M: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
M: Jarkko Nikula <jarkko.nikula@linux.intel.com>
|
||||
M: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
R: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/i2c/busses/i2c-designware-*
|
||||
|
@ -789,7 +789,7 @@ config I2C_QUP
|
||||
|
||||
config I2C_RIIC
|
||||
tristate "Renesas RIIC adapter"
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
Renesas RIIC I2C interface.
|
||||
@ -833,7 +833,7 @@ config I2C_SH7760
|
||||
config I2C_SH_MOBILE
|
||||
tristate "SuperH Mobile I2C Controller"
|
||||
depends on HAS_DMA
|
||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
built-in I2C interface on the Renesas SH-Mobile processor.
|
||||
@ -908,7 +908,7 @@ config I2C_TEGRA
|
||||
|
||||
config I2C_UNIPHIER
|
||||
tristate "UniPhier FIFO-less I2C controller"
|
||||
depends on ARCH_UNIPHIER
|
||||
depends on ARCH_UNIPHIER || COMPILE_TEST
|
||||
help
|
||||
If you say yes to this option, support will be included for
|
||||
the UniPhier FIFO-less I2C interface embedded in PH1-LD4, PH1-sLD8,
|
||||
@ -916,7 +916,7 @@ config I2C_UNIPHIER
|
||||
|
||||
config I2C_UNIPHIER_F
|
||||
tristate "UniPhier FIFO-builtin I2C controller"
|
||||
depends on ARCH_UNIPHIER
|
||||
depends on ARCH_UNIPHIER || COMPILE_TEST
|
||||
help
|
||||
If you say yes to this option, support will be included for
|
||||
the UniPhier FIFO-builtin I2C interface embedded in PH1-Pro4,
|
||||
@ -985,7 +985,7 @@ config I2C_XLP9XX
|
||||
|
||||
config I2C_RCAR
|
||||
tristate "Renesas R-Car I2C Controller"
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
select I2C_SLAVE
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
|
@ -58,11 +58,13 @@
|
||||
#define IE_M_RX_FIFO_FULL_SHIFT 31
|
||||
#define IE_M_RX_THLD_SHIFT 30
|
||||
#define IE_M_START_BUSY_SHIFT 28
|
||||
#define IE_M_TX_UNDERRUN_SHIFT 27
|
||||
|
||||
#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 IS_M_TX_UNDERRUN_SHIFT 27
|
||||
|
||||
#define M_TX_OFFSET 0x40
|
||||
#define M_TX_WR_STATUS_SHIFT 31
|
||||
@ -76,7 +78,7 @@
|
||||
#define M_RX_DATA_SHIFT 0
|
||||
#define M_RX_DATA_MASK 0xff
|
||||
|
||||
#define I2C_TIMEOUT_MESC 100
|
||||
#define I2C_TIMEOUT_MSEC 50000
|
||||
#define M_TX_RX_FIFO_SIZE 64
|
||||
|
||||
enum bus_speed_index {
|
||||
@ -95,12 +97,17 @@ struct bcm_iproc_i2c_dev {
|
||||
|
||||
struct completion done;
|
||||
int xfer_is_done;
|
||||
|
||||
struct i2c_msg *msg;
|
||||
|
||||
/* bytes that have been transferred */
|
||||
unsigned int tx_bytes;
|
||||
};
|
||||
|
||||
/*
|
||||
* Can be expanded in the future if more interrupt status bits are utilized
|
||||
*/
|
||||
#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT)
|
||||
#define ISR_MASK (BIT(IS_M_START_BUSY_SHIFT) | BIT(IS_M_TX_UNDERRUN_SHIFT))
|
||||
|
||||
static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
|
||||
{
|
||||
@ -112,13 +119,95 @@ static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* TX FIFO is empty and we have more data to send */
|
||||
if (status & BIT(IS_M_TX_UNDERRUN_SHIFT)) {
|
||||
struct i2c_msg *msg = iproc_i2c->msg;
|
||||
unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes;
|
||||
unsigned int i;
|
||||
u32 val;
|
||||
|
||||
/* can only fill up to the FIFO size */
|
||||
tx_bytes = min_t(unsigned int, tx_bytes, M_TX_RX_FIFO_SIZE);
|
||||
for (i = 0; i < tx_bytes; i++) {
|
||||
/* start from where we left over */
|
||||
unsigned int idx = iproc_i2c->tx_bytes + i;
|
||||
|
||||
val = msg->buf[idx];
|
||||
|
||||
/* mark the last byte */
|
||||
if (idx == msg->len - 1) {
|
||||
u32 tmp;
|
||||
|
||||
val |= BIT(M_TX_WR_STATUS_SHIFT);
|
||||
|
||||
/*
|
||||
* Since this is the last byte, we should
|
||||
* now disable TX FIFO underrun interrupt
|
||||
*/
|
||||
tmp = readl(iproc_i2c->base + IE_OFFSET);
|
||||
tmp &= ~BIT(IE_M_TX_UNDERRUN_SHIFT);
|
||||
writel(tmp, iproc_i2c->base + IE_OFFSET);
|
||||
}
|
||||
|
||||
/* load data into TX FIFO */
|
||||
writel(val, iproc_i2c->base + M_TX_OFFSET);
|
||||
}
|
||||
/* update number of transferred bytes */
|
||||
iproc_i2c->tx_bytes += tx_bytes;
|
||||
}
|
||||
|
||||
if (status & BIT(IS_M_START_BUSY_SHIFT)) {
|
||||
iproc_i2c->xfer_is_done = 1;
|
||||
complete_all(&iproc_i2c->done);
|
||||
}
|
||||
|
||||
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_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_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
struct i2c_msg *msg)
|
||||
{
|
||||
@ -149,6 +238,12 @@ static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
|
||||
default:
|
||||
dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val);
|
||||
|
||||
/* re-initialize i2c for recovery */
|
||||
bcm_iproc_i2c_enable_disable(iproc_i2c, false);
|
||||
bcm_iproc_i2c_init(iproc_i2c);
|
||||
bcm_iproc_i2c_enable_disable(iproc_i2c, true);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
@ -159,7 +254,8 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
int ret, i;
|
||||
u8 addr;
|
||||
u32 val;
|
||||
unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC);
|
||||
unsigned int tx_bytes;
|
||||
unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC);
|
||||
|
||||
/* check if bus is busy */
|
||||
if (!!(readl(iproc_i2c->base + M_CMD_OFFSET) &
|
||||
@ -168,13 +264,20 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
iproc_i2c->msg = msg;
|
||||
|
||||
/* 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 */
|
||||
/*
|
||||
* For a write transaction, load data into the TX FIFO. Only allow
|
||||
* loading up to TX FIFO size - 1 bytes of data since the first byte
|
||||
* has been used up by the slave address
|
||||
*/
|
||||
tx_bytes = min_t(unsigned int, msg->len, M_TX_RX_FIFO_SIZE - 1);
|
||||
if (!(msg->flags & I2C_M_RD)) {
|
||||
for (i = 0; i < msg->len; i++) {
|
||||
for (i = 0; i < tx_bytes; i++) {
|
||||
val = msg->buf[i];
|
||||
|
||||
/* mark the last byte */
|
||||
@ -183,6 +286,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
|
||||
writel(val, iproc_i2c->base + M_TX_OFFSET);
|
||||
}
|
||||
iproc_i2c->tx_bytes = tx_bytes;
|
||||
}
|
||||
|
||||
/* mark as incomplete before starting the transaction */
|
||||
@ -194,13 +298,24 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||
* 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);
|
||||
val = BIT(IE_M_START_BUSY_SHIFT);
|
||||
|
||||
/*
|
||||
* If TX data size is larger than the TX FIFO, need to enable TX
|
||||
* underrun interrupt, which will be triggerred when the TX FIFO is
|
||||
* empty. When that happens we can then pump more data into the FIFO
|
||||
*/
|
||||
if (!(msg->flags & I2C_M_RD) &&
|
||||
msg->len > iproc_i2c->tx_bytes)
|
||||
val |= BIT(IE_M_TX_UNDERRUN_SHIFT);
|
||||
|
||||
writel(val, 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;
|
||||
val = BIT(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);
|
||||
@ -283,7 +398,6 @@ static const struct i2c_algorithm bcm_iproc_algo = {
|
||||
static struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
|
||||
/* need to reserve one byte in the FIFO for the slave address */
|
||||
.max_read_len = M_TX_RX_FIFO_SIZE - 1,
|
||||
.max_write_len = M_TX_RX_FIFO_SIZE - 1,
|
||||
};
|
||||
|
||||
static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c)
|
||||
@ -321,49 +435,6 @@ static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c)
|
||||
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;
|
||||
|
@ -124,6 +124,8 @@
|
||||
|
||||
/**
|
||||
* struct cdns_i2c - I2C device private data structure
|
||||
*
|
||||
* @dev: Pointer to device structure
|
||||
* @membase: Base address of the I2C device
|
||||
* @adap: I2C adapter instance
|
||||
* @p_msg: Message pointer
|
||||
@ -171,7 +173,7 @@ struct cdns_platform_data {
|
||||
clk_rate_change_nb)
|
||||
|
||||
/**
|
||||
* cdns_i2c_clear_bus_hold() - Clear bus hold bit
|
||||
* cdns_i2c_clear_bus_hold - Clear bus hold bit
|
||||
* @id: Pointer to driver data struct
|
||||
*
|
||||
* Helper to clear the controller's bus hold bit.
|
||||
@ -815,8 +817,8 @@ static int cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_suspend - Suspend method for the driver
|
||||
* @_dev: Address of the platform_device structure
|
||||
* cdns_i2c_runtime_suspend - Runtime suspend method for the driver
|
||||
* @dev: Address of the platform_device structure
|
||||
*
|
||||
* Put the driver into low power mode.
|
||||
*
|
||||
@ -833,10 +835,10 @@ static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev)
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_resume - Resume from suspend
|
||||
* @_dev: Address of the platform_device structure
|
||||
* cdns_i2c_runtime_resume - Runtime resume
|
||||
* @dev: Address of the platform_device structure
|
||||
*
|
||||
* Resume operation after suspend.
|
||||
* Runtime resume callback.
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
|
@ -11,7 +11,6 @@
|
||||
* 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>
|
||||
@ -151,7 +150,3 @@ int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Baytrail I2C Semaphore driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -634,7 +634,6 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||
|
||||
dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
|
||||
reinit_completion(&dev->cmd_complete);
|
||||
@ -673,11 +672,12 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||
}
|
||||
|
||||
/*
|
||||
* We must disable the adapter before unlocking the &dev->lock mutex
|
||||
* below. Otherwise the hardware might continue generating interrupts
|
||||
* which in turn causes a race condition with the following transfer.
|
||||
* Needs some more investigation if the additional interrupts are
|
||||
* a hardware bug or this driver doesn't handle them correctly yet.
|
||||
* We must disable the adapter before returning and signaling the end
|
||||
* of the current transfer. Otherwise the hardware might continue
|
||||
* generating interrupts which in turn causes a race condition with
|
||||
* the following transfer. Needs some more investigation if the
|
||||
* additional interrupts are a hardware bug or this driver doesn't
|
||||
* handle them correctly yet.
|
||||
*/
|
||||
__i2c_dw_enable(dev, false);
|
||||
|
||||
@ -706,7 +706,6 @@ done:
|
||||
done_nolock:
|
||||
pm_runtime_mark_last_busy(dev->dev);
|
||||
pm_runtime_put_autosuspend(dev->dev);
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -860,7 +859,6 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
|
||||
int r;
|
||||
|
||||
init_completion(&dev->cmd_complete);
|
||||
mutex_init(&dev->lock);
|
||||
|
||||
r = i2c_dw_init(dev);
|
||||
if (r)
|
||||
@ -883,9 +881,17 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment PM usage count during adapter registration in order to
|
||||
* avoid possible spurious runtime suspend when adapter device is
|
||||
* registered to the device core and immediate resume in case bus has
|
||||
* registered I2C slaves that do I2C transfers in their probe.
|
||||
*/
|
||||
pm_runtime_get_noresume(dev->dev);
|
||||
r = i2c_add_numbered_adapter(adap);
|
||||
if (r)
|
||||
dev_err(dev->dev, "failure adding adapter: %d\n", r);
|
||||
pm_runtime_put_noidle(dev->dev);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -36,7 +36,6 @@
|
||||
* @dev: driver model device node
|
||||
* @base: IO registers pointer
|
||||
* @cmd_complete: tx completion indicator
|
||||
* @lock: protect this struct and IO registers
|
||||
* @clk: input reference clock
|
||||
* @cmd_err: run time hadware error code
|
||||
* @msgs: points to an array of messages currently being transfered
|
||||
@ -73,7 +72,6 @@ struct dw_i2c_dev {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct completion cmd_complete;
|
||||
struct mutex lock;
|
||||
struct clk *clk;
|
||||
u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev);
|
||||
struct dw_pci_controller *controller;
|
||||
|
@ -184,7 +184,7 @@
|
||||
|
||||
/* Older devices have their ID defined in <linux/pci_ids.h> */
|
||||
#define PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS 0x0f12
|
||||
#define PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS 0x2292
|
||||
#define PCI_DEVICE_ID_INTEL_DNV_SMBUS 0x19df
|
||||
#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22
|
||||
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22
|
||||
/* Patsburg also has three 'Integrated Device Function' SMBus controllers */
|
||||
@ -193,9 +193,11 @@
|
||||
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72
|
||||
#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22
|
||||
#define PCI_DEVICE_ID_INTEL_AVOTON_SMBUS 0x1f3c
|
||||
#define PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS 0x2292
|
||||
#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
|
||||
#define PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS 0x23b0
|
||||
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
|
||||
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
|
||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
|
||||
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS 0x8ca2
|
||||
#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS 0x8d22
|
||||
@ -204,10 +206,8 @@
|
||||
#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2 0x8d7f
|
||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22
|
||||
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS 0x9ca2
|
||||
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS 0xa123
|
||||
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS 0x9d23
|
||||
#define PCI_DEVICE_ID_INTEL_DNV_SMBUS 0x19df
|
||||
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
|
||||
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS 0xa123
|
||||
#define PCI_DEVICE_ID_INTEL_LEWISBURG_SMBUS 0xa1a3
|
||||
#define PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS 0xa223
|
||||
|
||||
|
@ -212,7 +212,7 @@ struct imx_i2c_struct {
|
||||
struct imx_i2c_dma *dma;
|
||||
};
|
||||
|
||||
static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
|
||||
static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
|
||||
.devtype = IMX1_I2C,
|
||||
.regshift = IMX_I2C_REGSHIFT,
|
||||
.clk_div = imx_i2c_clk_div,
|
||||
@ -222,7 +222,7 @@ static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
|
||||
|
||||
};
|
||||
|
||||
static const struct imx_i2c_hwdata imx21_i2c_hwdata = {
|
||||
static const struct imx_i2c_hwdata imx21_i2c_hwdata = {
|
||||
.devtype = IMX21_I2C,
|
||||
.regshift = IMX_I2C_REGSHIFT,
|
||||
.clk_div = imx_i2c_clk_div,
|
||||
@ -871,7 +871,7 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
|
||||
if ((!i) && block_data)
|
||||
msgs->buf[0] = len;
|
||||
else
|
||||
msgs->buf[i] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||
msgs->buf[i] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||
dev_dbg(&i2c_imx->adapter.dev,
|
||||
"<%s> read byte: B%d=0x%X\n",
|
||||
__func__, i, msgs->buf[i]);
|
||||
@ -916,7 +916,7 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
temp |= I2CR_RSTA;
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
result = i2c_imx_bus_busy(i2c_imx, 1);
|
||||
result = i2c_imx_bus_busy(i2c_imx, 1);
|
||||
if (result)
|
||||
goto fail0;
|
||||
}
|
||||
@ -1192,7 +1192,7 @@ static int i2c_imx_remove(struct platform_device *pdev)
|
||||
#ifdef CONFIG_PM
|
||||
static int i2c_imx_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
||||
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(i2c_imx->clk);
|
||||
|
||||
@ -1201,7 +1201,7 @@ static int i2c_imx_runtime_suspend(struct device *dev)
|
||||
|
||||
static int i2c_imx_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
||||
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(i2c_imx->clk);
|
||||
|
@ -60,6 +60,7 @@
|
||||
#define I2C_DMA_INT_FLAG_NONE 0x0000
|
||||
#define I2C_DMA_CLR_FLAG 0x0000
|
||||
#define I2C_DMA_HARD_RST 0x0002
|
||||
#define I2C_DMA_4G_MODE 0x0001
|
||||
|
||||
#define I2C_DEFAULT_SPEED 100000 /* hz */
|
||||
#define MAX_FS_MODE_SPEED 400000
|
||||
@ -88,6 +89,8 @@ enum DMA_REGS_OFFSET {
|
||||
OFFSET_RX_MEM_ADDR = 0x20,
|
||||
OFFSET_TX_LEN = 0x24,
|
||||
OFFSET_RX_LEN = 0x28,
|
||||
OFFSET_TX_4G_MODE = 0x54,
|
||||
OFFSET_RX_4G_MODE = 0x58,
|
||||
};
|
||||
|
||||
enum i2c_trans_st_rs {
|
||||
@ -133,6 +136,7 @@ struct mtk_i2c_compatible {
|
||||
unsigned char dcm: 1;
|
||||
unsigned char auto_restart: 1;
|
||||
unsigned char aux_len_reg: 1;
|
||||
unsigned char support_33bits: 1;
|
||||
};
|
||||
|
||||
struct mtk_i2c {
|
||||
@ -182,6 +186,7 @@ static const struct mtk_i2c_compatible mt6577_compat = {
|
||||
.dcm = 1,
|
||||
.auto_restart = 0,
|
||||
.aux_len_reg = 0,
|
||||
.support_33bits = 0,
|
||||
};
|
||||
|
||||
static const struct mtk_i2c_compatible mt6589_compat = {
|
||||
@ -190,6 +195,7 @@ static const struct mtk_i2c_compatible mt6589_compat = {
|
||||
.dcm = 0,
|
||||
.auto_restart = 0,
|
||||
.aux_len_reg = 0,
|
||||
.support_33bits = 0,
|
||||
};
|
||||
|
||||
static const struct mtk_i2c_compatible mt8173_compat = {
|
||||
@ -198,6 +204,7 @@ static const struct mtk_i2c_compatible mt8173_compat = {
|
||||
.dcm = 1,
|
||||
.auto_restart = 1,
|
||||
.aux_len_reg = 1,
|
||||
.support_33bits = 1,
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_i2c_of_match[] = {
|
||||
@ -366,6 +373,11 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 mtk_i2c_set_4g_mode(dma_addr_t addr)
|
||||
{
|
||||
return (addr & BIT_ULL(32)) ? I2C_DMA_4G_MODE : I2C_DMA_CLR_FLAG;
|
||||
}
|
||||
|
||||
static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
||||
int num, int left_num)
|
||||
{
|
||||
@ -373,6 +385,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
||||
u16 start_reg;
|
||||
u16 control_reg;
|
||||
u16 restart_flag = 0;
|
||||
u32 reg_4g_mode;
|
||||
dma_addr_t rpaddr = 0;
|
||||
dma_addr_t wpaddr = 0;
|
||||
int ret;
|
||||
@ -439,6 +452,12 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
||||
msgs->len, DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(i2c->dev, rpaddr))
|
||||
return -ENOMEM;
|
||||
|
||||
if (i2c->dev_comp->support_33bits) {
|
||||
reg_4g_mode = mtk_i2c_set_4g_mode(rpaddr);
|
||||
writel(reg_4g_mode, i2c->pdmabase + OFFSET_RX_4G_MODE);
|
||||
}
|
||||
|
||||
writel((u32)rpaddr, i2c->pdmabase + OFFSET_RX_MEM_ADDR);
|
||||
writel(msgs->len, i2c->pdmabase + OFFSET_RX_LEN);
|
||||
} else if (i2c->op == I2C_MASTER_WR) {
|
||||
@ -448,6 +467,12 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
||||
msgs->len, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(i2c->dev, wpaddr))
|
||||
return -ENOMEM;
|
||||
|
||||
if (i2c->dev_comp->support_33bits) {
|
||||
reg_4g_mode = mtk_i2c_set_4g_mode(wpaddr);
|
||||
writel(reg_4g_mode, i2c->pdmabase + OFFSET_TX_4G_MODE);
|
||||
}
|
||||
|
||||
writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR);
|
||||
writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN);
|
||||
} else {
|
||||
@ -465,6 +490,15 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
||||
msgs->len, DMA_TO_DEVICE);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (i2c->dev_comp->support_33bits) {
|
||||
reg_4g_mode = mtk_i2c_set_4g_mode(wpaddr);
|
||||
writel(reg_4g_mode, i2c->pdmabase + OFFSET_TX_4G_MODE);
|
||||
|
||||
reg_4g_mode = mtk_i2c_set_4g_mode(rpaddr);
|
||||
writel(reg_4g_mode, i2c->pdmabase + OFFSET_RX_4G_MODE);
|
||||
}
|
||||
|
||||
writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR);
|
||||
writel((u32)rpaddr, i2c->pdmabase + OFFSET_RX_MEM_ADDR);
|
||||
writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN);
|
||||
@ -729,6 +763,14 @@ static int mtk_i2c_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (i2c->dev_comp->support_33bits) {
|
||||
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(33));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "dma_set_mask return error.\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = mtk_i2c_clock_enable(i2c);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "clock enable failed!\n");
|
||||
|
@ -2,7 +2,7 @@
|
||||
* (C) Copyright 2009-2010
|
||||
* Nokia Siemens Networks, michael.lawnick.ext@nsn.com
|
||||
*
|
||||
* Portions Copyright (C) 2010, 2011 Cavium Networks, Inc.
|
||||
* Portions Copyright (C) 2010 - 2016 Cavium, Inc.
|
||||
*
|
||||
* This is a driver for the i2c adapter in Cavium Networks' OCTEON processors.
|
||||
*
|
||||
@ -26,39 +26,48 @@
|
||||
|
||||
#define DRV_NAME "i2c-octeon"
|
||||
|
||||
/* The previous out-of-tree version was implicitly version 1.0. */
|
||||
#define DRV_VERSION "2.0"
|
||||
|
||||
/* register offsets */
|
||||
#define SW_TWSI 0x00
|
||||
#define TWSI_INT 0x10
|
||||
/* Register offsets */
|
||||
#define SW_TWSI 0x00
|
||||
#define TWSI_INT 0x10
|
||||
|
||||
/* Controller command patterns */
|
||||
#define SW_TWSI_V 0x8000000000000000ull
|
||||
#define SW_TWSI_EOP_TWSI_DATA 0x0C00000100000000ull
|
||||
#define SW_TWSI_EOP_TWSI_CTL 0x0C00000200000000ull
|
||||
#define SW_TWSI_EOP_TWSI_CLKCTL 0x0C00000300000000ull
|
||||
#define SW_TWSI_EOP_TWSI_STAT 0x0C00000300000000ull
|
||||
#define SW_TWSI_EOP_TWSI_RST 0x0C00000700000000ull
|
||||
#define SW_TWSI_OP_TWSI_CLK 0x0800000000000000ull
|
||||
#define SW_TWSI_R 0x0100000000000000ull
|
||||
#define SW_TWSI_V BIT_ULL(63) /* Valid bit */
|
||||
#define SW_TWSI_R BIT_ULL(56) /* Result or read bit */
|
||||
|
||||
/* Controller opcode word (bits 60:57) */
|
||||
#define SW_TWSI_OP_SHIFT 57
|
||||
#define SW_TWSI_OP_TWSI_CLK (4ULL << SW_TWSI_OP_SHIFT)
|
||||
#define SW_TWSI_OP_EOP (6ULL << SW_TWSI_OP_SHIFT) /* Extended opcode */
|
||||
|
||||
/* Controller extended opcode word (bits 34:32) */
|
||||
#define SW_TWSI_EOP_SHIFT 32
|
||||
#define SW_TWSI_EOP_TWSI_DATA (SW_TWSI_OP_EOP | 1ULL << SW_TWSI_EOP_SHIFT)
|
||||
#define SW_TWSI_EOP_TWSI_CTL (SW_TWSI_OP_EOP | 2ULL << SW_TWSI_EOP_SHIFT)
|
||||
#define SW_TWSI_EOP_TWSI_CLKCTL (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT)
|
||||
#define SW_TWSI_EOP_TWSI_STAT (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT)
|
||||
#define SW_TWSI_EOP_TWSI_RST (SW_TWSI_OP_EOP | 7ULL << SW_TWSI_EOP_SHIFT)
|
||||
|
||||
/* Controller command and status bits */
|
||||
#define TWSI_CTL_CE 0x80
|
||||
#define TWSI_CTL_ENAB 0x40
|
||||
#define TWSI_CTL_STA 0x20
|
||||
#define TWSI_CTL_STP 0x10
|
||||
#define TWSI_CTL_IFLG 0x08
|
||||
#define TWSI_CTL_AAK 0x04
|
||||
#define TWSI_CTL_CE 0x80
|
||||
#define TWSI_CTL_ENAB 0x40 /* Bus enable */
|
||||
#define TWSI_CTL_STA 0x20 /* Master-mode start, HW clears when done */
|
||||
#define TWSI_CTL_STP 0x10 /* Master-mode stop, HW clears when done */
|
||||
#define TWSI_CTL_IFLG 0x08 /* HW event, SW writes 0 to ACK */
|
||||
#define TWSI_CTL_AAK 0x04 /* Assert ACK */
|
||||
|
||||
/* Some status values */
|
||||
#define STAT_START 0x08
|
||||
#define STAT_RSTART 0x10
|
||||
#define STAT_TXADDR_ACK 0x18
|
||||
#define STAT_TXDATA_ACK 0x28
|
||||
#define STAT_RXADDR_ACK 0x40
|
||||
#define STAT_RXDATA_ACK 0x50
|
||||
#define STAT_IDLE 0xF8
|
||||
#define STAT_START 0x08
|
||||
#define STAT_RSTART 0x10
|
||||
#define STAT_TXADDR_ACK 0x18
|
||||
#define STAT_TXDATA_ACK 0x28
|
||||
#define STAT_RXADDR_ACK 0x40
|
||||
#define STAT_RXDATA_ACK 0x50
|
||||
#define STAT_IDLE 0xF8
|
||||
|
||||
/* TWSI_INT values */
|
||||
#define TWSI_INT_CORE_EN BIT_ULL(6)
|
||||
#define TWSI_INT_SDA_OVR BIT_ULL(8)
|
||||
#define TWSI_INT_SCL_OVR BIT_ULL(9)
|
||||
|
||||
struct octeon_i2c {
|
||||
wait_queue_head_t queue;
|
||||
@ -66,23 +75,19 @@ struct octeon_i2c {
|
||||
int irq;
|
||||
u32 twsi_freq;
|
||||
int sys_freq;
|
||||
resource_size_t twsi_phys;
|
||||
void __iomem *twsi_base;
|
||||
resource_size_t regsize;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
/**
|
||||
* octeon_i2c_write_sw - write an I2C core register.
|
||||
* @i2c: The struct octeon_i2c.
|
||||
* @eop_reg: Register selector.
|
||||
* @data: Value to be written.
|
||||
* octeon_i2c_write_sw - write an I2C core register
|
||||
* @i2c: The struct octeon_i2c
|
||||
* @eop_reg: Register selector
|
||||
* @data: Value to be written
|
||||
*
|
||||
* The I2C core registers are accessed indirectly via the SW_TWSI CSR.
|
||||
*/
|
||||
static void octeon_i2c_write_sw(struct octeon_i2c *i2c,
|
||||
u64 eop_reg,
|
||||
u8 data)
|
||||
static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data)
|
||||
{
|
||||
u64 tmp;
|
||||
|
||||
@ -93,9 +98,9 @@ static void octeon_i2c_write_sw(struct octeon_i2c *i2c,
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_read_sw - write an I2C core register.
|
||||
* @i2c: The struct octeon_i2c.
|
||||
* @eop_reg: Register selector.
|
||||
* octeon_i2c_read_sw - read lower bits of an I2C core register
|
||||
* @i2c: The struct octeon_i2c
|
||||
* @eop_reg: Register selector
|
||||
*
|
||||
* Returns the data.
|
||||
*
|
||||
@ -115,8 +120,8 @@ static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg)
|
||||
|
||||
/**
|
||||
* octeon_i2c_write_int - write the TWSI_INT register
|
||||
* @i2c: The struct octeon_i2c.
|
||||
* @data: Value to be written.
|
||||
* @i2c: The struct octeon_i2c
|
||||
* @data: Value to be written
|
||||
*/
|
||||
static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data)
|
||||
{
|
||||
@ -125,57 +130,52 @@ static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data)
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_int_enable - enable the TS interrupt.
|
||||
* @i2c: The struct octeon_i2c.
|
||||
* octeon_i2c_int_enable - enable the CORE interrupt
|
||||
* @i2c: The struct octeon_i2c
|
||||
*
|
||||
* The interrupt will be asserted when there is non-STAT_IDLE state in
|
||||
* the SW_TWSI_EOP_TWSI_STAT register.
|
||||
*/
|
||||
static void octeon_i2c_int_enable(struct octeon_i2c *i2c)
|
||||
{
|
||||
octeon_i2c_write_int(i2c, 0x40);
|
||||
octeon_i2c_write_int(i2c, TWSI_INT_CORE_EN);
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_int_disable - disable the TS interrupt.
|
||||
* @i2c: The struct octeon_i2c.
|
||||
*/
|
||||
/* disable the CORE interrupt */
|
||||
static void octeon_i2c_int_disable(struct octeon_i2c *i2c)
|
||||
{
|
||||
/* clear TS/ST/IFLG events */
|
||||
octeon_i2c_write_int(i2c, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_unblock - unblock the bus.
|
||||
* @i2c: The struct octeon_i2c.
|
||||
* octeon_i2c_unblock - unblock the bus
|
||||
* @i2c: The struct octeon_i2c
|
||||
*
|
||||
* If there was a reset while a device was driving 0 to bus,
|
||||
* bus is blocked. We toggle it free manually by some clock
|
||||
* cycles and send a stop.
|
||||
* If there was a reset while a device was driving 0 to bus, bus is blocked.
|
||||
* We toggle it free manually by some clock cycles and send a stop.
|
||||
*/
|
||||
static void octeon_i2c_unblock(struct octeon_i2c *i2c)
|
||||
{
|
||||
int i;
|
||||
|
||||
dev_dbg(i2c->dev, "%s\n", __func__);
|
||||
|
||||
for (i = 0; i < 9; i++) {
|
||||
octeon_i2c_write_int(i2c, 0x0);
|
||||
octeon_i2c_write_int(i2c, 0);
|
||||
udelay(5);
|
||||
octeon_i2c_write_int(i2c, 0x200);
|
||||
octeon_i2c_write_int(i2c, TWSI_INT_SCL_OVR);
|
||||
udelay(5);
|
||||
}
|
||||
octeon_i2c_write_int(i2c, 0x300);
|
||||
/* hand-crank a STOP */
|
||||
octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR | TWSI_INT_SCL_OVR);
|
||||
udelay(5);
|
||||
octeon_i2c_write_int(i2c, 0x100);
|
||||
octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR);
|
||||
udelay(5);
|
||||
octeon_i2c_write_int(i2c, 0x0);
|
||||
octeon_i2c_write_int(i2c, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_isr - the interrupt service routine.
|
||||
* @int: The irq, unused.
|
||||
* @dev_id: Our struct octeon_i2c.
|
||||
*/
|
||||
/* interrupt service routine */
|
||||
static irqreturn_t octeon_i2c_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct octeon_i2c *i2c = dev_id;
|
||||
@ -193,24 +193,20 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c)
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_wait - wait for the IFLG to be set.
|
||||
* @i2c: The struct octeon_i2c.
|
||||
* octeon_i2c_wait - wait for the IFLG to be set
|
||||
* @i2c: The struct octeon_i2c
|
||||
*
|
||||
* Returns 0 on success, otherwise a negative errno.
|
||||
*/
|
||||
static int octeon_i2c_wait(struct octeon_i2c *i2c)
|
||||
{
|
||||
long result;
|
||||
long time_left;
|
||||
|
||||
octeon_i2c_int_enable(i2c);
|
||||
|
||||
result = wait_event_timeout(i2c->queue,
|
||||
octeon_i2c_test_iflg(i2c),
|
||||
i2c->adap.timeout);
|
||||
|
||||
time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c),
|
||||
i2c->adap.timeout);
|
||||
octeon_i2c_int_disable(i2c);
|
||||
|
||||
if (result == 0) {
|
||||
if (!time_left) {
|
||||
dev_dbg(i2c->dev, "%s: timeout\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
@ -219,18 +215,18 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c)
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_start - send START to the bus.
|
||||
* @i2c: The struct octeon_i2c.
|
||||
* octeon_i2c_start - send START to the bus
|
||||
* @i2c: The struct octeon_i2c
|
||||
*
|
||||
* Returns 0 on success, otherwise a negative errno.
|
||||
*/
|
||||
static int octeon_i2c_start(struct octeon_i2c *i2c)
|
||||
{
|
||||
u8 data;
|
||||
int result;
|
||||
u8 data;
|
||||
|
||||
octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL,
|
||||
TWSI_CTL_ENAB | TWSI_CTL_STA);
|
||||
TWSI_CTL_ENAB | TWSI_CTL_STA);
|
||||
|
||||
result = octeon_i2c_wait(i2c);
|
||||
if (result) {
|
||||
@ -243,7 +239,6 @@ static int octeon_i2c_start(struct octeon_i2c *i2c)
|
||||
octeon_i2c_unblock(i2c);
|
||||
octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL,
|
||||
TWSI_CTL_ENAB | TWSI_CTL_STA);
|
||||
|
||||
result = octeon_i2c_wait(i2c);
|
||||
}
|
||||
if (result)
|
||||
@ -259,34 +254,19 @@ static int octeon_i2c_start(struct octeon_i2c *i2c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_stop - send STOP to the bus.
|
||||
* @i2c: The struct octeon_i2c.
|
||||
*
|
||||
* Returns 0 on success, otherwise a negative errno.
|
||||
*/
|
||||
static int octeon_i2c_stop(struct octeon_i2c *i2c)
|
||||
/* send STOP to the bus */
|
||||
static void octeon_i2c_stop(struct octeon_i2c *i2c)
|
||||
{
|
||||
u8 data;
|
||||
|
||||
octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL,
|
||||
TWSI_CTL_ENAB | TWSI_CTL_STP);
|
||||
|
||||
data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT);
|
||||
|
||||
if (data != STAT_IDLE) {
|
||||
dev_err(i2c->dev, "%s: bad status(0x%x)\n", __func__, data);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_write - send data to the bus.
|
||||
* @i2c: The struct octeon_i2c.
|
||||
* @target: Target address.
|
||||
* @data: Pointer to the data to be sent.
|
||||
* @length: Length of the data.
|
||||
* octeon_i2c_write - send data to the bus via low-level controller
|
||||
* @i2c: The struct octeon_i2c
|
||||
* @target: Target address
|
||||
* @data: Pointer to the data to be sent
|
||||
* @length: Length of the data
|
||||
*
|
||||
* The address is sent over the bus, then the data.
|
||||
*
|
||||
@ -311,6 +291,7 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target,
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT);
|
||||
|
||||
if ((tmp != STAT_TXADDR_ACK) && (tmp != STAT_TXDATA_ACK)) {
|
||||
dev_err(i2c->dev,
|
||||
"%s: bad status before write (0x%x)\n",
|
||||
@ -330,20 +311,21 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target,
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_read - receive data from the bus.
|
||||
* @i2c: The struct octeon_i2c.
|
||||
* @target: Target address.
|
||||
* @data: Pointer to the location to store the datae .
|
||||
* @length: Length of the data.
|
||||
* octeon_i2c_read - receive data from the bus via low-level controller
|
||||
* @i2c: The struct octeon_i2c
|
||||
* @target: Target address
|
||||
* @data: Pointer to the location to store the data
|
||||
* @rlength: Length of the data
|
||||
* @recv_len: flag for length byte
|
||||
*
|
||||
* The address is sent over the bus, then the data is read.
|
||||
*
|
||||
* Returns 0 on success, otherwise a negative errno.
|
||||
*/
|
||||
static int octeon_i2c_read(struct octeon_i2c *i2c, int target,
|
||||
u8 *data, int length)
|
||||
u8 *data, u16 *rlength, bool recv_len)
|
||||
{
|
||||
int i, result;
|
||||
int i, result, length = *rlength;
|
||||
u8 tmp;
|
||||
|
||||
if (length < 1)
|
||||
@ -353,7 +335,7 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target,
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, (target<<1) | 1);
|
||||
octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, (target << 1) | 1);
|
||||
octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB);
|
||||
|
||||
result = octeon_i2c_wait(i2c);
|
||||
@ -362,6 +344,7 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target,
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT);
|
||||
|
||||
if ((tmp != STAT_RXDATA_ACK) && (tmp != STAT_RXADDR_ACK)) {
|
||||
dev_err(i2c->dev,
|
||||
"%s: bad status before read (0x%x)\n",
|
||||
@ -369,52 +352,59 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (i+1 < length)
|
||||
if (i + 1 < length)
|
||||
octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL,
|
||||
TWSI_CTL_ENAB | TWSI_CTL_AAK);
|
||||
TWSI_CTL_ENAB | TWSI_CTL_AAK);
|
||||
else
|
||||
octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL,
|
||||
TWSI_CTL_ENAB);
|
||||
TWSI_CTL_ENAB);
|
||||
|
||||
result = octeon_i2c_wait(i2c);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
data[i] = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_DATA);
|
||||
if (recv_len && i == 0) {
|
||||
if (data[i] > I2C_SMBUS_BLOCK_MAX + 1) {
|
||||
dev_err(i2c->dev,
|
||||
"%s: read len > I2C_SMBUS_BLOCK_MAX %d\n",
|
||||
__func__, data[i]);
|
||||
return -EPROTO;
|
||||
}
|
||||
length += data[i];
|
||||
}
|
||||
}
|
||||
*rlength = length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_xfer - The driver's master_xfer function.
|
||||
* @adap: Pointer to the i2c_adapter structure.
|
||||
* @msgs: Pointer to the messages to be processed.
|
||||
* @num: Length of the MSGS array.
|
||||
* octeon_i2c_xfer - The driver's master_xfer function
|
||||
* @adap: Pointer to the i2c_adapter structure
|
||||
* @msgs: Pointer to the messages to be processed
|
||||
* @num: Length of the MSGS array
|
||||
*
|
||||
* Returns the number of messages processed, or a negative errno on
|
||||
* failure.
|
||||
* Returns the number of messages processed, or a negative errno on failure.
|
||||
*/
|
||||
static int octeon_i2c_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs,
|
||||
static int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct i2c_msg *pmsg;
|
||||
int i;
|
||||
int ret = 0;
|
||||
struct octeon_i2c *i2c = i2c_get_adapdata(adap);
|
||||
int i, ret = 0;
|
||||
|
||||
for (i = 0; ret == 0 && i < num; i++) {
|
||||
pmsg = &msgs[i];
|
||||
struct i2c_msg *pmsg = &msgs[i];
|
||||
|
||||
dev_dbg(i2c->dev,
|
||||
"Doing %s %d byte(s) to/from 0x%02x - %d of %d messages\n",
|
||||
pmsg->flags & I2C_M_RD ? "read" : "write",
|
||||
pmsg->len, pmsg->addr, i + 1, num);
|
||||
if (pmsg->flags & I2C_M_RD)
|
||||
ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf,
|
||||
pmsg->len);
|
||||
&pmsg->len, pmsg->flags & I2C_M_RECV_LEN);
|
||||
else
|
||||
ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf,
|
||||
pmsg->len);
|
||||
pmsg->len);
|
||||
}
|
||||
octeon_i2c_stop(i2c);
|
||||
|
||||
@ -423,7 +413,8 @@ static int octeon_i2c_xfer(struct i2c_adapter *adap,
|
||||
|
||||
static u32 octeon_i2c_functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
|
||||
I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_SMBUS_BLOCK_PROC_CALL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm octeon_i2c_algo = {
|
||||
@ -438,10 +429,8 @@ static struct i2c_adapter octeon_i2c_ops = {
|
||||
.timeout = HZ / 50,
|
||||
};
|
||||
|
||||
/**
|
||||
* octeon_i2c_setclock - Calculate and set clock divisors.
|
||||
*/
|
||||
static int octeon_i2c_setclock(struct octeon_i2c *i2c)
|
||||
/* calculate and set clock divisors */
|
||||
static void octeon_i2c_set_clock(struct octeon_i2c *i2c)
|
||||
{
|
||||
int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff;
|
||||
int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000;
|
||||
@ -449,8 +438,7 @@ static int octeon_i2c_setclock(struct octeon_i2c *i2c)
|
||||
for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) {
|
||||
/*
|
||||
* An mdiv value of less than 2 seems to not work well
|
||||
* with ds1337 RTCs, so we constrain it to larger
|
||||
* values.
|
||||
* with ds1337 RTCs, so we constrain it to larger values.
|
||||
*/
|
||||
for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) {
|
||||
/*
|
||||
@ -460,6 +448,7 @@ static int octeon_i2c_setclock(struct octeon_i2c *i2c)
|
||||
tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10;
|
||||
tclk *= (1 << ndiv_idx);
|
||||
thp_base = (i2c->sys_freq / (tclk * 2)) - 1;
|
||||
|
||||
for (inc = 0; inc <= 1; inc++) {
|
||||
thp_idx = thp_base + inc;
|
||||
if (thp_idx < 5 || thp_idx > 0xff)
|
||||
@ -480,11 +469,9 @@ static int octeon_i2c_setclock(struct octeon_i2c *i2c)
|
||||
}
|
||||
octeon_i2c_write_sw(i2c, SW_TWSI_OP_TWSI_CLK, thp);
|
||||
octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int octeon_i2c_initlowlevel(struct octeon_i2c *i2c)
|
||||
static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c)
|
||||
{
|
||||
u8 status;
|
||||
int tries;
|
||||
@ -507,9 +494,10 @@ static int octeon_i2c_initlowlevel(struct octeon_i2c *i2c)
|
||||
|
||||
static int octeon_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
int irq, result = 0;
|
||||
struct octeon_i2c *i2c;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct resource *res_mem;
|
||||
struct octeon_i2c *i2c;
|
||||
int irq, result = 0;
|
||||
|
||||
/* All adaptors have an irq. */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
@ -518,31 +506,25 @@ static int octeon_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
|
||||
if (!i2c) {
|
||||
dev_err(&pdev->dev, "kzalloc failed\n");
|
||||
result = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
i2c->dev = &pdev->dev;
|
||||
|
||||
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
if (res_mem == NULL) {
|
||||
dev_err(i2c->dev, "found no memory resource\n");
|
||||
result = -ENXIO;
|
||||
i2c->twsi_base = devm_ioremap_resource(&pdev->dev, res_mem);
|
||||
if (IS_ERR(i2c->twsi_base)) {
|
||||
result = PTR_ERR(i2c->twsi_base);
|
||||
goto out;
|
||||
}
|
||||
i2c->twsi_phys = res_mem->start;
|
||||
i2c->regsize = resource_size(res_mem);
|
||||
|
||||
/*
|
||||
* "clock-rate" is a legacy binding, the official binding is
|
||||
* "clock-frequency". Try the official one first and then
|
||||
* fall back if it doesn't exist.
|
||||
*/
|
||||
if (of_property_read_u32(pdev->dev.of_node,
|
||||
"clock-frequency", &i2c->twsi_freq) &&
|
||||
of_property_read_u32(pdev->dev.of_node,
|
||||
"clock-rate", &i2c->twsi_freq)) {
|
||||
if (of_property_read_u32(node, "clock-frequency", &i2c->twsi_freq) &&
|
||||
of_property_read_u32(node, "clock-rate", &i2c->twsi_freq)) {
|
||||
dev_err(i2c->dev,
|
||||
"no I2C 'clock-rate' or 'clock-frequency' property\n");
|
||||
result = -ENXIO;
|
||||
@ -551,13 +533,6 @@ static int octeon_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
i2c->sys_freq = octeon_get_io_clock_rate();
|
||||
|
||||
if (!devm_request_mem_region(&pdev->dev, i2c->twsi_phys, i2c->regsize,
|
||||
res_mem->name)) {
|
||||
dev_err(i2c->dev, "request_mem_region failed\n");
|
||||
goto out;
|
||||
}
|
||||
i2c->twsi_base = devm_ioremap(&pdev->dev, i2c->twsi_phys, i2c->regsize);
|
||||
|
||||
init_waitqueue_head(&i2c->queue);
|
||||
|
||||
i2c->irq = irq;
|
||||
@ -569,21 +544,17 @@ static int octeon_i2c_probe(struct platform_device *pdev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = octeon_i2c_initlowlevel(i2c);
|
||||
result = octeon_i2c_init_lowlevel(i2c);
|
||||
if (result) {
|
||||
dev_err(i2c->dev, "init low level failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = octeon_i2c_setclock(i2c);
|
||||
if (result) {
|
||||
dev_err(i2c->dev, "clock init failed\n");
|
||||
goto out;
|
||||
}
|
||||
octeon_i2c_set_clock(i2c);
|
||||
|
||||
i2c->adap = octeon_i2c_ops;
|
||||
i2c->adap.dev.parent = &pdev->dev;
|
||||
i2c->adap.dev.of_node = pdev->dev.of_node;
|
||||
i2c->adap.dev.of_node = node;
|
||||
i2c_set_adapdata(&i2c->adap, i2c);
|
||||
platform_set_drvdata(pdev, i2c);
|
||||
|
||||
@ -592,8 +563,7 @@ static int octeon_i2c_probe(struct platform_device *pdev)
|
||||
dev_err(i2c->dev, "failed to add adapter\n");
|
||||
goto out;
|
||||
}
|
||||
dev_info(i2c->dev, "version %s\n", DRV_VERSION);
|
||||
|
||||
dev_info(i2c->dev, "probed\n");
|
||||
return 0;
|
||||
|
||||
out:
|
||||
@ -608,10 +578,8 @@ static int octeon_i2c_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
};
|
||||
|
||||
static struct of_device_id octeon_i2c_match[] = {
|
||||
{
|
||||
.compatible = "cavium,octeon-3860-twsi",
|
||||
},
|
||||
static const struct of_device_id octeon_i2c_match[] = {
|
||||
{ .compatible = "cavium,octeon-3860-twsi", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, octeon_i2c_match);
|
||||
@ -630,4 +598,3 @@ module_platform_driver(octeon_i2c_driver);
|
||||
MODULE_AUTHOR("Michael Lawnick <michael.lawnick.ext@nsn.com>");
|
||||
MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(DRV_VERSION);
|
||||
|
@ -85,8 +85,14 @@
|
||||
/* SB800 constants */
|
||||
#define SB800_PIIX4_SMB_IDX 0xcd6
|
||||
|
||||
/* SB800 port is selected by bits 2:1 of the smb_en register (0x2c) */
|
||||
/*
|
||||
* SB800 port is selected by bits 2:1 of the smb_en register (0x2c)
|
||||
* or the smb_sel register (0x2e), depending on bit 0 of register 0x2f.
|
||||
* Hudson-2/Bolton port is always selected by bits 2:1 of register 0x2f.
|
||||
*/
|
||||
#define SB800_PIIX4_PORT_IDX 0x2c
|
||||
#define SB800_PIIX4_PORT_IDX_ALT 0x2e
|
||||
#define SB800_PIIX4_PORT_IDX_SEL 0x2f
|
||||
#define SB800_PIIX4_PORT_IDX_MASK 0x06
|
||||
|
||||
/* insmod parameters */
|
||||
@ -136,8 +142,13 @@ static const struct dmi_system_id piix4_dmi_ibm[] = {
|
||||
{ },
|
||||
};
|
||||
|
||||
/* SB800 globals */
|
||||
/*
|
||||
* SB800 globals
|
||||
* piix4_mutex_sb800 protects piix4_port_sel_sb800 and the pair
|
||||
* of I/O ports at SB800_PIIX4_SMB_IDX.
|
||||
*/
|
||||
static DEFINE_MUTEX(piix4_mutex_sb800);
|
||||
static u8 piix4_port_sel_sb800;
|
||||
static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = {
|
||||
" port 0", " port 2", " port 3", " port 4"
|
||||
};
|
||||
@ -148,7 +159,7 @@ struct i2c_piix4_adapdata {
|
||||
|
||||
/* SB800 */
|
||||
bool sb800_main;
|
||||
unsigned short port;
|
||||
u8 port; /* Port number, shifted */
|
||||
};
|
||||
|
||||
static int piix4_setup(struct pci_dev *PIIX4_dev,
|
||||
@ -254,7 +265,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
|
||||
const struct pci_device_id *id, u8 aux)
|
||||
{
|
||||
unsigned short piix4_smba;
|
||||
u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status;
|
||||
u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status, port_sel;
|
||||
u8 i2ccfg, i2ccfg_offset = 0x10;
|
||||
|
||||
/* SB800 and later SMBus does not support forcing address */
|
||||
@ -334,6 +345,23 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
|
||||
"SMBus Host Controller at 0x%x, revision %d\n",
|
||||
piix4_smba, i2ccfg >> 4);
|
||||
|
||||
/* Find which register is used for port selection */
|
||||
if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD) {
|
||||
piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_ALT;
|
||||
} else {
|
||||
mutex_lock(&piix4_mutex_sb800);
|
||||
outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX);
|
||||
port_sel = inb_p(SB800_PIIX4_SMB_IDX + 1);
|
||||
piix4_port_sel_sb800 = (port_sel & 0x01) ?
|
||||
SB800_PIIX4_PORT_IDX_ALT :
|
||||
SB800_PIIX4_PORT_IDX;
|
||||
mutex_unlock(&piix4_mutex_sb800);
|
||||
}
|
||||
|
||||
dev_info(&PIIX4_dev->dev,
|
||||
"Using register 0x%02x for SMBus port selection\n",
|
||||
(unsigned int)piix4_port_sel_sb800);
|
||||
|
||||
return piix4_smba;
|
||||
}
|
||||
|
||||
@ -563,12 +591,12 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
|
||||
|
||||
mutex_lock(&piix4_mutex_sb800);
|
||||
|
||||
outb_p(SB800_PIIX4_PORT_IDX, SB800_PIIX4_SMB_IDX);
|
||||
outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX);
|
||||
smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
|
||||
|
||||
port = adapdata->port;
|
||||
if ((smba_en_lo & SB800_PIIX4_PORT_IDX_MASK) != (port << 1))
|
||||
outb_p((smba_en_lo & ~SB800_PIIX4_PORT_IDX_MASK) | (port << 1),
|
||||
if ((smba_en_lo & SB800_PIIX4_PORT_IDX_MASK) != port)
|
||||
outb_p((smba_en_lo & ~SB800_PIIX4_PORT_IDX_MASK) | port,
|
||||
SB800_PIIX4_SMB_IDX + 1);
|
||||
|
||||
retval = piix4_access(adap, addr, flags, read_write,
|
||||
@ -627,7 +655,7 @@ static struct i2c_adapter *piix4_main_adapters[PIIX4_MAX_ADAPTERS];
|
||||
static struct i2c_adapter *piix4_aux_adapter;
|
||||
|
||||
static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
|
||||
bool sb800_main, unsigned short port,
|
||||
bool sb800_main, u8 port,
|
||||
const char *name, struct i2c_adapter **padap)
|
||||
{
|
||||
struct i2c_adapter *adap;
|
||||
@ -654,7 +682,7 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
|
||||
|
||||
adapdata->smba = smba;
|
||||
adapdata->sb800_main = sb800_main;
|
||||
adapdata->port = port;
|
||||
adapdata->port = port << 1;
|
||||
|
||||
/* set up the sysfs linkage to our parent device */
|
||||
adap->dev.parent = &dev->dev;
|
||||
@ -790,7 +818,7 @@ static void piix4_adap_remove(struct i2c_adapter *adap)
|
||||
|
||||
if (adapdata->smba) {
|
||||
i2c_del_adapter(adap);
|
||||
if (adapdata->port == 0) {
|
||||
if (adapdata->port == (0 << 1)) {
|
||||
release_region(adapdata->smba, SMBIOSIZE);
|
||||
if (adapdata->sb800_main)
|
||||
release_region(SB800_PIIX4_SMB_IDX, 2);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -611,7 +611,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->io))
|
||||
return PTR_ERR(priv->io);
|
||||
|
||||
priv->devtype = (enum rcar_i2c_type)of_match_device(rcar_i2c_dt_ids, dev)->data;
|
||||
priv->devtype = (enum rcar_i2c_type)of_device_get_match_data(dev);
|
||||
init_waitqueue_head(&priv->wait);
|
||||
|
||||
adap = &priv->adap;
|
||||
|
@ -805,9 +805,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
|
||||
i2c_dev->hw = &tegra20_i2c_hw;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
const struct of_device_id *match;
|
||||
match = of_match_device(tegra_i2c_of_match, &pdev->dev);
|
||||
i2c_dev->hw = match->data;
|
||||
i2c_dev->hw = of_device_get_match_data(&pdev->dev);
|
||||
i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node,
|
||||
"nvidia,tegra20-i2c-dvc");
|
||||
} else if (pdev->id == 3) {
|
||||
|
@ -37,6 +37,8 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#define DRIVER_NAME "xiic-i2c"
|
||||
|
||||
@ -66,6 +68,7 @@ enum xiic_endian {
|
||||
* @endianness: big/little-endian byte order
|
||||
*/
|
||||
struct xiic_i2c {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
wait_queue_head_t wait;
|
||||
struct i2c_adapter adap;
|
||||
@ -77,6 +80,7 @@ struct xiic_i2c {
|
||||
struct i2c_msg *rx_msg;
|
||||
int rx_pos;
|
||||
enum xiic_endian endianness;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
|
||||
@ -164,6 +168,7 @@ struct xiic_i2c {
|
||||
|
||||
#define XIIC_RESET_MASK 0xAUL
|
||||
|
||||
#define XIIC_PM_TIMEOUT 1000 /* ms */
|
||||
/*
|
||||
* The following constant is used for the device global interrupt enable
|
||||
* register, to enable all interrupts for the device, this is the only bit
|
||||
@ -676,9 +681,13 @@ static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
dev_dbg(adap->dev.parent, "%s entry SR: 0x%x\n", __func__,
|
||||
xiic_getreg8(i2c, XIIC_SR_REG_OFFSET));
|
||||
|
||||
err = pm_runtime_get_sync(i2c->dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = xiic_busy(i2c);
|
||||
if (err)
|
||||
return err;
|
||||
goto out;
|
||||
|
||||
i2c->tx_msg = msgs;
|
||||
i2c->nmsgs = num;
|
||||
@ -686,14 +695,20 @@ static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
xiic_start_xfer(i2c);
|
||||
|
||||
if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||
|
||||
(i2c->state == STATE_DONE), HZ))
|
||||
return (i2c->state == STATE_DONE) ? num : -EIO;
|
||||
else {
|
||||
(i2c->state == STATE_DONE), HZ)) {
|
||||
err = (i2c->state == STATE_DONE) ? num : -EIO;
|
||||
goto out;
|
||||
} else {
|
||||
i2c->tx_msg = NULL;
|
||||
i2c->rx_msg = NULL;
|
||||
i2c->nmsgs = 0;
|
||||
return -ETIMEDOUT;
|
||||
err = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
pm_runtime_mark_last_busy(i2c->dev);
|
||||
pm_runtime_put_autosuspend(i2c->dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static u32 xiic_func(struct i2c_adapter *adap)
|
||||
@ -748,13 +763,28 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
||||
mutex_init(&i2c->lock);
|
||||
init_waitqueue_head(&i2c->wait);
|
||||
|
||||
i2c->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(i2c->clk)) {
|
||||
dev_err(&pdev->dev, "input clock not found.\n");
|
||||
return PTR_ERR(i2c->clk);
|
||||
}
|
||||
ret = clk_prepare_enable(i2c->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to enable clock.\n");
|
||||
return ret;
|
||||
}
|
||||
i2c->dev = &pdev->dev;
|
||||
pm_runtime_enable(i2c->dev);
|
||||
pm_runtime_set_autosuspend_delay(i2c->dev, XIIC_PM_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(i2c->dev);
|
||||
pm_runtime_set_active(i2c->dev);
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, xiic_isr,
|
||||
xiic_process, IRQF_ONESHOT,
|
||||
pdev->name, i2c);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Cannot claim IRQ\n");
|
||||
return ret;
|
||||
goto err_clk_dis;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -776,7 +806,7 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to add adapter\n");
|
||||
xiic_deinit(i2c);
|
||||
return ret;
|
||||
goto err_clk_dis;
|
||||
}
|
||||
|
||||
if (pdata) {
|
||||
@ -786,16 +816,30 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk_dis:
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xiic_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct xiic_i2c *i2c = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
/* remove adapter & data */
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
|
||||
ret = clk_prepare_enable(i2c->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to enable clock.\n");
|
||||
return ret;
|
||||
}
|
||||
xiic_deinit(i2c);
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -808,12 +852,42 @@ static const struct of_device_id xiic_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, xiic_of_match);
|
||||
#endif
|
||||
|
||||
static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct xiic_i2c *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable(i2c->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct xiic_i2c *i2c = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(i2c->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable clock.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops xiic_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(cdns_i2c_runtime_suspend,
|
||||
cdns_i2c_runtime_resume, NULL)
|
||||
};
|
||||
static struct platform_driver xiic_i2c_driver = {
|
||||
.probe = xiic_i2c_probe,
|
||||
.remove = xiic_i2c_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(xiic_of_match),
|
||||
.pm = &xiic_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -12,11 +12,11 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "i2c-core.h"
|
||||
|
||||
|
@ -28,32 +28,32 @@
|
||||
*/
|
||||
|
||||
#include <dt-bindings/i2c/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk/clk-conf.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "i2c-core.h"
|
||||
|
||||
@ -73,6 +73,7 @@ static struct device_type i2c_client_type;
|
||||
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
|
||||
|
||||
static struct static_key i2c_trace_msg = STATIC_KEY_INIT_FALSE;
|
||||
static bool is_registered;
|
||||
|
||||
void i2c_transfer_trace_reg(void)
|
||||
{
|
||||
@ -1529,7 +1530,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
||||
int res = 0;
|
||||
|
||||
/* Can't register until after driver model init */
|
||||
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
|
||||
if (WARN_ON(!is_registered)) {
|
||||
res = -EAGAIN;
|
||||
goto out_list;
|
||||
}
|
||||
@ -1926,7 +1927,7 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
|
||||
int res;
|
||||
|
||||
/* Can't register until after driver model init */
|
||||
if (unlikely(WARN_ON(!i2c_bus_type.p)))
|
||||
if (WARN_ON(!is_registered))
|
||||
return -EAGAIN;
|
||||
|
||||
/* add the driver to the list of i2c drivers in the driver core */
|
||||
@ -2104,6 +2105,9 @@ static int __init i2c_init(void)
|
||||
retval = bus_register(&i2c_bus_type);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
is_registered = true;
|
||||
|
||||
#ifdef CONFIG_I2C_COMPAT
|
||||
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
|
||||
if (!i2c_adapter_compat_class) {
|
||||
@ -2125,6 +2129,7 @@ class_err:
|
||||
class_compat_unregister(i2c_adapter_compat_class);
|
||||
bus_err:
|
||||
#endif
|
||||
is_registered = false;
|
||||
bus_unregister(&i2c_bus_type);
|
||||
return retval;
|
||||
}
|
||||
|
@ -22,17 +22,17 @@
|
||||
|
||||
/* The I2C_RDWR ioctl code is written by Kolja Waschk <waschk@telos.de> */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
/*
|
||||
|
@ -19,13 +19,13 @@
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-mux.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* multiplexer per channel data */
|
||||
struct i2c_mux_priv {
|
||||
|
@ -15,14 +15,14 @@
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-smbus.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
struct i2c_smbus_alert {
|
||||
unsigned int alert_edge_triggered:1;
|
||||
|
@ -17,13 +17,13 @@
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define MAX_CHIPS 10
|
||||
|
||||
|
@ -72,4 +72,13 @@ config I2C_MUX_REG
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-mux-reg.
|
||||
|
||||
config I2C_DEMUX_PINCTRL
|
||||
tristate "pinctrl-based I2C demultiplexer"
|
||||
depends on PINCTRL && OF
|
||||
select OF_DYNAMIC
|
||||
help
|
||||
If you say yes to this option, support will be included for an I2C
|
||||
demultiplexer that uses the pinctrl subsystem. This is useful if you
|
||||
want to change the I2C master at run-time depending on features.
|
||||
|
||||
endmenu
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
|
||||
|
||||
obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o
|
||||
|
||||
obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
|
||||
obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
|
||||
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
|
||||
|
272
drivers/i2c/muxes/i2c-demux-pinctrl.c
Normal file
272
drivers/i2c/muxes/i2c-demux-pinctrl.c
Normal file
@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Pinctrl based I2C DeMultiplexer
|
||||
*
|
||||
* Copyright (C) 2015-16 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
|
||||
* Copyright (C) 2015-16 by Renesas Electronics Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* See the bindings doc for DTS setup and the sysfs doc for usage information.
|
||||
* (look for filenames containing 'i2c-demux-pinctrl' in Documentation/)
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
struct i2c_demux_pinctrl_chan {
|
||||
struct device_node *parent_np;
|
||||
struct i2c_adapter *parent_adap;
|
||||
struct of_changeset chgset;
|
||||
};
|
||||
|
||||
struct i2c_demux_pinctrl_priv {
|
||||
int cur_chan;
|
||||
int num_chan;
|
||||
struct device *dev;
|
||||
const char *bus_name;
|
||||
struct i2c_adapter cur_adap;
|
||||
struct i2c_algorithm algo;
|
||||
struct i2c_demux_pinctrl_chan chan[];
|
||||
};
|
||||
|
||||
static struct property status_okay = { .name = "status", .length = 3, .value = "ok" };
|
||||
|
||||
static int i2c_demux_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||
{
|
||||
struct i2c_demux_pinctrl_priv *priv = adap->algo_data;
|
||||
struct i2c_adapter *parent = priv->chan[priv->cur_chan].parent_adap;
|
||||
|
||||
return __i2c_transfer(parent, msgs, num);
|
||||
}
|
||||
|
||||
static u32 i2c_demux_functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
struct i2c_demux_pinctrl_priv *priv = adap->algo_data;
|
||||
struct i2c_adapter *parent = priv->chan[priv->cur_chan].parent_adap;
|
||||
|
||||
return parent->algo->functionality(parent);
|
||||
}
|
||||
|
||||
static int i2c_demux_activate_master(struct i2c_demux_pinctrl_priv *priv, u32 new_chan)
|
||||
{
|
||||
struct i2c_adapter *adap;
|
||||
struct pinctrl *p;
|
||||
int ret;
|
||||
|
||||
ret = of_changeset_apply(&priv->chan[new_chan].chgset);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
adap = of_find_i2c_adapter_by_node(priv->chan[new_chan].parent_np);
|
||||
if (!adap) {
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
p = devm_pinctrl_get_select(adap->dev.parent, priv->bus_name);
|
||||
if (IS_ERR(p)) {
|
||||
ret = PTR_ERR(p);
|
||||
goto err_with_put;
|
||||
}
|
||||
|
||||
priv->chan[new_chan].parent_adap = adap;
|
||||
priv->cur_chan = new_chan;
|
||||
|
||||
/* Now fill out current adapter structure. cur_chan must be up to date */
|
||||
priv->algo.master_xfer = i2c_demux_master_xfer;
|
||||
priv->algo.functionality = i2c_demux_functionality;
|
||||
|
||||
snprintf(priv->cur_adap.name, sizeof(priv->cur_adap.name),
|
||||
"i2c-demux (master i2c-%d)", i2c_adapter_id(adap));
|
||||
priv->cur_adap.owner = THIS_MODULE;
|
||||
priv->cur_adap.algo = &priv->algo;
|
||||
priv->cur_adap.algo_data = priv;
|
||||
priv->cur_adap.dev.parent = priv->dev;
|
||||
priv->cur_adap.class = adap->class;
|
||||
priv->cur_adap.retries = adap->retries;
|
||||
priv->cur_adap.timeout = adap->timeout;
|
||||
priv->cur_adap.quirks = adap->quirks;
|
||||
priv->cur_adap.dev.of_node = priv->dev->of_node;
|
||||
ret = i2c_add_adapter(&priv->cur_adap);
|
||||
if (ret < 0)
|
||||
goto err_with_put;
|
||||
|
||||
return 0;
|
||||
|
||||
err_with_put:
|
||||
i2c_put_adapter(adap);
|
||||
err:
|
||||
dev_err(priv->dev, "failed to setup demux-adapter %d (%d)\n", new_chan, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_demux_deactivate_master(struct i2c_demux_pinctrl_priv *priv)
|
||||
{
|
||||
int ret, cur = priv->cur_chan;
|
||||
|
||||
if (cur < 0)
|
||||
return 0;
|
||||
|
||||
i2c_del_adapter(&priv->cur_adap);
|
||||
i2c_put_adapter(priv->chan[cur].parent_adap);
|
||||
|
||||
ret = of_changeset_revert(&priv->chan[cur].chgset);
|
||||
|
||||
priv->chan[cur].parent_adap = NULL;
|
||||
priv->cur_chan = -EINVAL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_demux_change_master(struct i2c_demux_pinctrl_priv *priv, u32 new_chan)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (new_chan == priv->cur_chan)
|
||||
return 0;
|
||||
|
||||
ret = i2c_demux_deactivate_master(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return i2c_demux_activate_master(priv, new_chan);
|
||||
}
|
||||
|
||||
static ssize_t cur_master_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_demux_pinctrl_priv *priv = dev_get_drvdata(dev);
|
||||
int count = 0, i;
|
||||
|
||||
for (i = 0; i < priv->num_chan && count < PAGE_SIZE; i++)
|
||||
count += scnprintf(buf + count, PAGE_SIZE - count, "%c %d - %s\n",
|
||||
i == priv->cur_chan ? '*' : ' ', i,
|
||||
priv->chan[i].parent_np->full_name);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t cur_master_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_demux_pinctrl_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 0, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (val >= priv->num_chan)
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_demux_change_master(priv, val);
|
||||
|
||||
return ret < 0 ? ret : count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(cur_master);
|
||||
|
||||
static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct i2c_demux_pinctrl_priv *priv;
|
||||
int num_chan, i, j, err;
|
||||
|
||||
num_chan = of_count_phandle_with_args(np, "i2c-parent", NULL);
|
||||
if (num_chan < 2) {
|
||||
dev_err(&pdev->dev, "Need at least two I2C masters to switch\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv)
|
||||
+ num_chan * sizeof(struct i2c_demux_pinctrl_chan), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
err = of_property_read_string(np, "i2c-bus-name", &priv->bus_name);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < num_chan; i++) {
|
||||
struct device_node *adap_np;
|
||||
|
||||
adap_np = of_parse_phandle(np, "i2c-parent", i);
|
||||
if (!adap_np) {
|
||||
dev_err(&pdev->dev, "can't get phandle for parent %d\n", i);
|
||||
err = -ENOENT;
|
||||
goto err_rollback;
|
||||
}
|
||||
priv->chan[i].parent_np = adap_np;
|
||||
|
||||
of_changeset_init(&priv->chan[i].chgset);
|
||||
of_changeset_update_property(&priv->chan[i].chgset, adap_np, &status_okay);
|
||||
}
|
||||
|
||||
priv->num_chan = num_chan;
|
||||
priv->dev = &pdev->dev;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
/* switch to first parent as active master */
|
||||
i2c_demux_activate_master(priv, 0);
|
||||
|
||||
err = device_create_file(&pdev->dev, &dev_attr_cur_master);
|
||||
if (err)
|
||||
goto err_rollback;
|
||||
|
||||
return 0;
|
||||
|
||||
err_rollback:
|
||||
for (j = 0; j < i; j++) {
|
||||
of_node_put(priv->chan[j].parent_np);
|
||||
of_changeset_destroy(&priv->chan[j].chgset);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int i2c_demux_pinctrl_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_demux_pinctrl_priv *priv = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
device_remove_file(&pdev->dev, &dev_attr_cur_master);
|
||||
|
||||
i2c_demux_deactivate_master(priv);
|
||||
|
||||
for (i = 0; i < priv->num_chan; i++) {
|
||||
of_node_put(priv->chan[i].parent_np);
|
||||
of_changeset_destroy(&priv->chan[i].chgset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id i2c_demux_pinctrl_of_match[] = {
|
||||
{ .compatible = "i2c-demux-pinctrl", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_demux_pinctrl_of_match);
|
||||
|
||||
static struct platform_driver i2c_demux_pinctrl_driver = {
|
||||
.driver = {
|
||||
.name = "i2c-demux-pinctrl",
|
||||
.of_match_table = i2c_demux_pinctrl_of_match,
|
||||
},
|
||||
.probe = i2c_demux_pinctrl_probe,
|
||||
.remove = i2c_demux_pinctrl_remove,
|
||||
};
|
||||
module_platform_driver(i2c_demux_pinctrl_driver);
|
||||
|
||||
MODULE_DESCRIPTION("pinctrl-based I2C demux driver");
|
||||
MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:i2c-demux-pinctrl");
|
Loading…
Reference in New Issue
Block a user