mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-28 14:44:10 +08:00
Merge branch 'i2c/for-4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: - New drivers: UniPhier (with and without FIFO) - some drivers got some bigger rework: ismt, designware, img-scb (rcar had to be reverted because issues were showing up just lately) - ACPI: reworked the device scanning and added support for muxes ... and quite a lot of driver bugfixes and cleanups this time. All files touched outside of the i2c realm have proper acks. * 'i2c/for-4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (70 commits) i2c: rcar: Revert the latest refactoring series i2c: pnx: remove superfluous assignment MAINTAINERS: i2c: drop i2c-pnx maintainer MAINTAINERS: i2c: mark also subdirectories as maintained i2c: cadence: enable driver for ARM64 i2c: i801: Document Intel DNV and Broxton i2c: at91: manage unexpected RXRDY flag when starting a transfer i2c: pnx: Use setup_timer instead of open coding it i2c: add ACPI support for I2C mux ports acpi: add acpi_preset_companion() stub i2c: pxa: Add support for pxa910/988 & new configuration features i2c: au1550: Convert to devm_kzalloc and devm_ioremap_resource i2c-dev: Fix I2C_SLAVE ioctl comment i2c-dev: Fix typo in ioctl name reference i2c: sirf: tune the divider to make i2c bus freq more accurate i2c: imx: Use -ENXIO as error in the NACK case i2c: i801: Add support for Intel Broxton i2c: i801: Add support for Intel DNV i2c: mediatek: add i2c resume support i2c: imx: implement bus recovery ...
This commit is contained in:
commit
d55fc37856
58
Documentation/acpi/i2c-muxes.txt
Normal file
58
Documentation/acpi/i2c-muxes.txt
Normal file
@ -0,0 +1,58 @@
|
||||
ACPI I2C Muxes
|
||||
--------------
|
||||
|
||||
Describing an I2C device hierarchy that includes I2C muxes requires an ACPI
|
||||
Device () scope per mux channel.
|
||||
|
||||
Consider this topology:
|
||||
|
||||
+------+ +------+
|
||||
| SMB1 |-->| MUX0 |--CH00--> i2c client A (0x50)
|
||||
| | | 0x70 |--CH01--> i2c client B (0x50)
|
||||
+------+ +------+
|
||||
|
||||
which corresponds to the following ASL:
|
||||
|
||||
Device (SMB1)
|
||||
{
|
||||
Name (_HID, ...)
|
||||
Device (MUX0)
|
||||
{
|
||||
Name (_HID, ...)
|
||||
Name (_CRS, ResourceTemplate () {
|
||||
I2cSerialBus (0x70, ControllerInitiated, I2C_SPEED,
|
||||
AddressingMode7Bit, "^SMB1", 0x00,
|
||||
ResourceConsumer,,)
|
||||
}
|
||||
|
||||
Device (CH00)
|
||||
{
|
||||
Name (_ADR, 0)
|
||||
|
||||
Device (CLIA)
|
||||
{
|
||||
Name (_HID, ...)
|
||||
Name (_CRS, ResourceTemplate () {
|
||||
I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED,
|
||||
AddressingMode7Bit, "^CH00", 0x00,
|
||||
ResourceConsumer,,)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Device (CH01)
|
||||
{
|
||||
Name (_ADR, 1)
|
||||
|
||||
Device (CLIB)
|
||||
{
|
||||
Name (_HID, ...)
|
||||
Name (_CRS, ResourceTemplate () {
|
||||
I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED,
|
||||
AddressingMode7Bit, "^CH01", 0x00,
|
||||
ResourceConsumer,,)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
* Texas Instruments Davinci I2C
|
||||
* Texas Instruments Davinci/Keystone I2C
|
||||
|
||||
This file provides information, what the device node for the
|
||||
davinci i2c interface contain.
|
||||
davinci/keystone i2c interface contains.
|
||||
|
||||
Required properties:
|
||||
- compatible: "ti,davinci-i2c";
|
||||
- compatible: "ti,davinci-i2c" or "ti,keystone-i2c";
|
||||
- reg : Offset and length of the register set for the device
|
||||
|
||||
Recommended properties :
|
||||
|
@ -14,6 +14,10 @@ Optional properties:
|
||||
The absence of the propoerty 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
|
||||
- sda-gpios: specify the gpio related to SDA pin
|
||||
- pinctrl: add extra pinctrl to configure i2c pins to gpio function for i2c
|
||||
bus recovery, call it "gpio" state
|
||||
|
||||
Examples:
|
||||
|
||||
@ -37,4 +41,9 @@ i2c0: i2c@40066000 { /* i2c0 on vf610 */
|
||||
dmas = <&edma0 0 50>,
|
||||
<&edma0 0 51>;
|
||||
dma-names = "rx","tx";
|
||||
pinctrl-names = "default", "gpio";
|
||||
pinctrl-0 = <&pinctrl_i2c1>;
|
||||
pinctrl-1 = <&pinctrl_i2c1_gpio>;
|
||||
scl-gpios = <&gpio5 26 GPIO_ACTIVE_HIGH>;
|
||||
sda-gpios = <&gpio5 27 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
@ -10,6 +10,7 @@ Required properties:
|
||||
"renesas,i2c-r8a7792"
|
||||
"renesas,i2c-r8a7793"
|
||||
"renesas,i2c-r8a7794"
|
||||
"renesas,i2c-r8a7795"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: interrupt specifier.
|
||||
|
@ -10,6 +10,7 @@ Required properties:
|
||||
- "renesas,iic-r8a7792" (R-Car V2H)
|
||||
- "renesas,iic-r8a7793" (R-Car M2-N)
|
||||
- "renesas,iic-r8a7794" (R-Car E2)
|
||||
- "renesas,iic-r8a7795" (R-Car H3)
|
||||
- "renesas,iic-sh73a0" (SH-Mobile AG5)
|
||||
- reg : address start and address range size of device
|
||||
- interrupts : interrupt of device
|
||||
|
25
Documentation/devicetree/bindings/i2c/i2c-uniphier-f.txt
Normal file
25
Documentation/devicetree/bindings/i2c/i2c-uniphier-f.txt
Normal file
@ -0,0 +1,25 @@
|
||||
UniPhier I2C controller (FIFO-builtin)
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "socionext,uniphier-fi2c".
|
||||
- #address-cells: should be 1.
|
||||
- #size-cells: should be 0.
|
||||
- reg: offset and length of the register set for the device.
|
||||
- interrupts: a single interrupt specifier.
|
||||
- clocks: phandle to the input clock.
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency: desired I2C bus frequency in Hz. The maximum supported
|
||||
value is 400000. Defaults to 100000 if not specified.
|
||||
|
||||
Examples:
|
||||
|
||||
i2c0: i2c@58780000 {
|
||||
compatible = "socionext,uniphier-fi2c";
|
||||
reg = <0x58780000 0x80>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
interrupts = <0 41 4>;
|
||||
clocks = <&i2c_clk>;
|
||||
clock-frequency = <100000>;
|
||||
};
|
25
Documentation/devicetree/bindings/i2c/i2c-uniphier.txt
Normal file
25
Documentation/devicetree/bindings/i2c/i2c-uniphier.txt
Normal file
@ -0,0 +1,25 @@
|
||||
UniPhier I2C controller (FIFO-less)
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "socionext,uniphier-i2c".
|
||||
- #address-cells: should be 1.
|
||||
- #size-cells: should be 0.
|
||||
- reg: offset and length of the register set for the device.
|
||||
- interrupts: a single interrupt specifier.
|
||||
- clocks: phandle to the input clock.
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency: desired I2C bus frequency in Hz. The maximum supported
|
||||
value is 400000. Defaults to 100000 if not specified.
|
||||
|
||||
Examples:
|
||||
|
||||
i2c0: i2c@58400000 {
|
||||
compatible = "socionext,uniphier-i2c";
|
||||
reg = <0x58400000 0x40>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
interrupts = <0 41 1>;
|
||||
clocks = <&i2c_clk>;
|
||||
clock-frequency = <100000>;
|
||||
};
|
@ -30,6 +30,8 @@ Supported adapters:
|
||||
* Intel BayTrail (SOC)
|
||||
* Intel Sunrise Point-H (PCH)
|
||||
* Intel Sunrise Point-LP (PCH)
|
||||
* Intel DNV (SOC)
|
||||
* Intel Broxton (SOC)
|
||||
Datasheets: Publicly available at the Intel website
|
||||
|
||||
On Intel Patsburg and later chipsets, both the normal host SMBus controller
|
||||
|
@ -1626,6 +1626,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: arch/arm/boot/dts/uniphier*
|
||||
F: arch/arm/mach-uniphier/
|
||||
F: drivers/i2c/busses/i2c-uniphier*
|
||||
F: drivers/pinctrl/uniphier/
|
||||
F: drivers/tty/serial/8250/8250_uniphier.c
|
||||
N: uniphier
|
||||
@ -5163,6 +5164,7 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/i2c/
|
||||
F: Documentation/i2c/
|
||||
F: drivers/i2c/
|
||||
F: drivers/i2c/*/
|
||||
F: include/linux/i2c.h
|
||||
F: include/linux/i2c-*.h
|
||||
F: include/uapi/linux/i2c.h
|
||||
@ -8418,12 +8420,6 @@ M: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
|
||||
S: Maintained
|
||||
F: drivers/pnp/
|
||||
|
||||
PNXxxxx I2C DRIVER
|
||||
M: Vitaly Wool <vitalywool@gmail.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/i2c/busses/i2c-pnx.c
|
||||
|
||||
PPP PROTOCOL DRIVERS AND COMPRESSORS
|
||||
M: Paul Mackerras <paulus@samba.org>
|
||||
L: linux-ppp@vger.kernel.org
|
||||
|
@ -124,6 +124,8 @@ config I2C_I801
|
||||
BayTrail (SOC)
|
||||
Sunrise Point-H (PCH)
|
||||
Sunrise Point-LP (PCH)
|
||||
DNV (SOC)
|
||||
Broxton (SOC)
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-i801.
|
||||
@ -422,7 +424,7 @@ config I2C_BLACKFIN_TWI_CLK_KHZ
|
||||
|
||||
config I2C_CADENCE
|
||||
tristate "Cadence I2C Controller"
|
||||
depends on ARCH_ZYNQ
|
||||
depends on ARCH_ZYNQ || ARM64
|
||||
help
|
||||
Say yes here to select Cadence I2C Host Controller. This controller is
|
||||
e.g. used by Xilinx Zynq.
|
||||
@ -582,10 +584,10 @@ config I2C_IMG
|
||||
|
||||
config I2C_IMX
|
||||
tristate "IMX I2C interface"
|
||||
depends on ARCH_MXC
|
||||
depends on ARCH_MXC || ARCH_LAYERSCAPE
|
||||
help
|
||||
Say Y here if you want to use the IIC bus controller on
|
||||
the Freescale i.MX/MXC processors.
|
||||
the Freescale i.MX/MXC or Layerscape processors.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-imx.
|
||||
@ -902,6 +904,22 @@ config I2C_TEGRA
|
||||
If you say yes to this option, support will be included for the
|
||||
I2C controller embedded in NVIDIA Tegra SOCs
|
||||
|
||||
config I2C_UNIPHIER
|
||||
tristate "UniPhier FIFO-less I2C controller"
|
||||
depends on ARCH_UNIPHIER
|
||||
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,
|
||||
or older UniPhier SoCs.
|
||||
|
||||
config I2C_UNIPHIER_F
|
||||
tristate "UniPhier FIFO-builtin I2C controller"
|
||||
depends on ARCH_UNIPHIER
|
||||
help
|
||||
If you say yes to this option, support will be included for
|
||||
the UniPhier FIFO-builtin I2C interface embedded in PH1-Pro4,
|
||||
PH1-Pro5, or newer UniPhier SoCs.
|
||||
|
||||
config I2C_VERSATILE
|
||||
tristate "ARM Versatile/Realview I2C bus support"
|
||||
depends on ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS
|
||||
|
@ -87,6 +87,8 @@ obj-$(CONFIG_I2C_ST) += i2c-st.o
|
||||
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
|
||||
obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o
|
||||
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
|
||||
obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o
|
||||
obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o
|
||||
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
|
||||
obj-$(CONFIG_I2C_WMT) += i2c-wmt.o
|
||||
obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
|
||||
|
@ -347,8 +347,14 @@ error:
|
||||
|
||||
static void at91_twi_read_next_byte(struct at91_twi_dev *dev)
|
||||
{
|
||||
if (!dev->buf_len)
|
||||
/*
|
||||
* If we are in this case, it means there is garbage data in RHR, so
|
||||
* delete them.
|
||||
*/
|
||||
if (!dev->buf_len) {
|
||||
at91_twi_read(dev, AT91_TWI_RHR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 8bit read works with and without FIFO */
|
||||
*dev->buf = readb_relaxed(dev->base + AT91_TWI_RHR);
|
||||
@ -465,19 +471,73 @@ static irqreturn_t atmel_twi_interrupt(int irq, void *dev_id)
|
||||
|
||||
if (!irqstatus)
|
||||
return IRQ_NONE;
|
||||
else if (irqstatus & AT91_TWI_RXRDY)
|
||||
/*
|
||||
* In reception, the behavior of the twi device (before sama5d2) is
|
||||
* weird. There is some magic about RXRDY flag! When a data has been
|
||||
* almost received, the reception of a new one is anticipated if there
|
||||
* is no stop command to send. That is the reason why ask for sending
|
||||
* the stop command not on the last data but on the second last one.
|
||||
*
|
||||
* Unfortunately, we could still have the RXRDY flag set even if the
|
||||
* transfer is done and we have read the last data. It might happen
|
||||
* when the i2c slave device sends too quickly data after receiving the
|
||||
* ack from the master. The data has been almost received before having
|
||||
* the order to send stop. In this case, sending the stop command could
|
||||
* cause a RXRDY interrupt with a TXCOMP one. It is better to manage
|
||||
* the RXRDY interrupt first in order to not keep garbage data in the
|
||||
* Receive Holding Register for the next transfer.
|
||||
*/
|
||||
if (irqstatus & AT91_TWI_RXRDY)
|
||||
at91_twi_read_next_byte(dev);
|
||||
else if (irqstatus & AT91_TWI_TXRDY)
|
||||
at91_twi_write_next_byte(dev);
|
||||
|
||||
/* catch error flags */
|
||||
dev->transfer_status |= status;
|
||||
|
||||
/*
|
||||
* When a NACK condition is detected, the I2C controller sets the NACK,
|
||||
* TXCOMP and TXRDY bits all together in the Status Register (SR).
|
||||
*
|
||||
* 1 - Handling NACK errors with CPU write transfer.
|
||||
*
|
||||
* In such case, we should not write the next byte into the Transmit
|
||||
* Holding Register (THR) otherwise the I2C controller would start a new
|
||||
* transfer and the I2C slave is likely to reply by another NACK.
|
||||
*
|
||||
* 2 - Handling NACK errors with DMA write transfer.
|
||||
*
|
||||
* By setting the TXRDY bit in the SR, the I2C controller also triggers
|
||||
* the DMA controller to write the next data into the THR. Then the
|
||||
* result depends on the hardware version of the I2C controller.
|
||||
*
|
||||
* 2a - Without support of the Alternative Command mode.
|
||||
*
|
||||
* This is the worst case: the DMA controller is triggered to write the
|
||||
* next data into the THR, hence starting a new transfer: the I2C slave
|
||||
* is likely to reply by another NACK.
|
||||
* Concurrently, this interrupt handler is likely to be called to manage
|
||||
* the first NACK before the I2C controller detects the second NACK and
|
||||
* sets once again the NACK bit into the SR.
|
||||
* When handling the first NACK, this interrupt handler disables the I2C
|
||||
* controller interruptions, especially the NACK interrupt.
|
||||
* Hence, the NACK bit is pending into the SR. This is why we should
|
||||
* read the SR to clear all pending interrupts at the beginning of
|
||||
* at91_do_twi_transfer() before actually starting a new transfer.
|
||||
*
|
||||
* 2b - With support of the Alternative Command mode.
|
||||
*
|
||||
* When a NACK condition is detected, the I2C controller also locks the
|
||||
* THR (and sets the LOCK bit in the SR): even though the DMA controller
|
||||
* is triggered by the TXRDY bit to write the next data into the THR,
|
||||
* this data actually won't go on the I2C bus hence a second NACK is not
|
||||
* generated.
|
||||
*/
|
||||
if (irqstatus & (AT91_TWI_TXCOMP | AT91_TWI_NACK)) {
|
||||
at91_disable_twi_interrupts(dev);
|
||||
complete(&dev->cmd_complete);
|
||||
} else if (irqstatus & AT91_TWI_TXRDY) {
|
||||
at91_twi_write_next_byte(dev);
|
||||
}
|
||||
|
||||
/* catch error flags */
|
||||
dev->transfer_status |= status;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -537,6 +597,9 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
|
||||
reinit_completion(&dev->cmd_complete);
|
||||
dev->transfer_status = 0;
|
||||
|
||||
/* Clear pending interrupts, such as NACK. */
|
||||
at91_twi_read(dev, AT91_TWI_SR);
|
||||
|
||||
if (dev->fifo_size) {
|
||||
unsigned fifo_mr = at91_twi_read(dev, AT91_TWI_FMR);
|
||||
|
||||
@ -558,11 +621,6 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
|
||||
} else if (dev->msg->flags & I2C_M_RD) {
|
||||
unsigned start_flags = AT91_TWI_START;
|
||||
|
||||
if (at91_twi_read(dev, AT91_TWI_SR) & AT91_TWI_RXRDY) {
|
||||
dev_err(dev->dev, "RXRDY still set!");
|
||||
at91_twi_read(dev, AT91_TWI_RHR);
|
||||
}
|
||||
|
||||
/* if only one byte is to be read, immediately stop transfer */
|
||||
if (!has_alt_cmd && dev->buf_len <= 1 &&
|
||||
!(dev->msg->flags & I2C_M_RECV_LEN))
|
||||
|
@ -48,7 +48,6 @@ struct i2c_au1550_data {
|
||||
void __iomem *psc_base;
|
||||
int xfer_timeout;
|
||||
struct i2c_adapter adap;
|
||||
struct resource *ioarea;
|
||||
};
|
||||
|
||||
static inline void WR(struct i2c_au1550_data *a, int r, unsigned long v)
|
||||
@ -284,10 +283,10 @@ static void i2c_au1550_setup(struct i2c_au1550_data *priv)
|
||||
/* Set the protocol timer values. See Table 71 in the
|
||||
* Au1550 Data Book for standard timing values.
|
||||
*/
|
||||
WR(priv, PSC_SMBTMR, PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \
|
||||
PSC_SMBTMR_SET_PU(15) | PSC_SMBTMR_SET_SH(15) | \
|
||||
PSC_SMBTMR_SET_SU(15) | PSC_SMBTMR_SET_CL(15) | \
|
||||
PSC_SMBTMR_SET_CH(15));
|
||||
WR(priv, PSC_SMBTMR, PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(20) | \
|
||||
PSC_SMBTMR_SET_PU(20) | PSC_SMBTMR_SET_SH(20) | \
|
||||
PSC_SMBTMR_SET_SU(20) | PSC_SMBTMR_SET_CL(20) | \
|
||||
PSC_SMBTMR_SET_CH(20));
|
||||
|
||||
cfg |= PSC_SMBCFG_DE_ENABLE;
|
||||
WR(priv, PSC_SMBCFG, cfg);
|
||||
@ -315,30 +314,16 @@ i2c_au1550_probe(struct platform_device *pdev)
|
||||
struct resource *r;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct i2c_au1550_data),
|
||||
GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
priv->psc_base = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(priv->psc_base))
|
||||
return PTR_ERR(priv->psc_base);
|
||||
|
||||
priv = kzalloc(sizeof(struct i2c_au1550_data), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
priv->ioarea = request_mem_region(r->start, resource_size(r),
|
||||
pdev->name);
|
||||
if (!priv->ioarea) {
|
||||
ret = -EBUSY;
|
||||
goto out_mem;
|
||||
}
|
||||
|
||||
priv->psc_base = ioremap(r->start, resource_size(r));
|
||||
if (!priv->psc_base) {
|
||||
ret = -EIO;
|
||||
goto out_map;
|
||||
}
|
||||
priv->xfer_timeout = 200;
|
||||
|
||||
priv->adap.nr = pdev->id;
|
||||
@ -351,20 +336,13 @@ i2c_au1550_probe(struct platform_device *pdev)
|
||||
i2c_au1550_setup(priv);
|
||||
|
||||
ret = i2c_add_numbered_adapter(&priv->adap);
|
||||
if (ret == 0) {
|
||||
platform_set_drvdata(pdev, priv);
|
||||
return 0;
|
||||
if (ret) {
|
||||
i2c_au1550_disable(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_au1550_disable(priv);
|
||||
iounmap(priv->psc_base);
|
||||
out_map:
|
||||
release_resource(priv->ioarea);
|
||||
kfree(priv->ioarea);
|
||||
out_mem:
|
||||
kfree(priv);
|
||||
out:
|
||||
return ret;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_au1550_remove(struct platform_device *pdev)
|
||||
@ -373,10 +351,6 @@ static int i2c_au1550_remove(struct platform_device *pdev)
|
||||
|
||||
i2c_del_adapter(&priv->adap);
|
||||
i2c_au1550_disable(priv);
|
||||
iounmap(priv->psc_base);
|
||||
release_resource(priv->ioarea);
|
||||
kfree(priv->ioarea);
|
||||
kfree(priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -181,6 +181,7 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
|
||||
u32 clkh;
|
||||
u32 clkl;
|
||||
u32 input_clock = clk_get_rate(dev->clk);
|
||||
struct device_node *of_node = dev->dev->of_node;
|
||||
|
||||
/* NOTE: I2C Clock divider programming info
|
||||
* As per I2C specs the following formulas provide prescaler
|
||||
@ -196,6 +197,9 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
|
||||
* where if PSC == 0, d = 7,
|
||||
* if PSC == 1, d = 6
|
||||
* if PSC > 1 , d = 5
|
||||
*
|
||||
* Note:
|
||||
* d is always 6 on Keystone I2C controller
|
||||
*/
|
||||
|
||||
/* get minimum of 7 MHz clock, but max of 12 MHz */
|
||||
@ -204,6 +208,9 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
|
||||
psc++; /* better to run under spec than over */
|
||||
d = (psc >= 2) ? 5 : 7 - psc;
|
||||
|
||||
if (of_node && of_device_is_compatible(of_node, "ti,keystone-i2c"))
|
||||
d = 6;
|
||||
|
||||
clk = ((input_clock / (psc + 1)) / (pdata->bus_freq * 1000));
|
||||
/* Avoid driving the bus too fast because of rounding errors above */
|
||||
if (input_clock / (psc + 1) / clk > pdata->bus_freq * 1000)
|
||||
@ -726,6 +733,7 @@ static struct i2c_algorithm i2c_davinci_algo = {
|
||||
|
||||
static const struct of_device_id davinci_i2c_of_match[] = {
|
||||
{.compatible = "ti,davinci-i2c", },
|
||||
{.compatible = "ti,keystone-i2c", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, davinci_i2c_of_match);
|
||||
|
@ -165,7 +165,7 @@ static char *abort_sources[] = {
|
||||
"lost arbitration",
|
||||
};
|
||||
|
||||
u32 dw_readl(struct dw_i2c_dev *dev, int offset)
|
||||
static u32 dw_readl(struct dw_i2c_dev *dev, int offset)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
@ -181,7 +181,7 @@ u32 dw_readl(struct dw_i2c_dev *dev, int offset)
|
||||
return value;
|
||||
}
|
||||
|
||||
void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset)
|
||||
static void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset)
|
||||
{
|
||||
if (dev->accessor_flags & ACCESS_SWAP)
|
||||
b = swab32(b);
|
||||
@ -438,7 +438,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
|
||||
__i2c_dw_enable(dev, true);
|
||||
|
||||
/* Clear and enable interrupts */
|
||||
i2c_dw_clear_int(dev);
|
||||
dw_readl(dev, DW_IC_CLR_INTR);
|
||||
dw_writel(dev, DW_IC_INTR_DEFAULT_MASK, DW_IC_INTR_MASK);
|
||||
}
|
||||
|
||||
@ -618,7 +618,7 @@ static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
|
||||
/*
|
||||
* Prepare controller for a transaction and call i2c_dw_xfer_msg
|
||||
*/
|
||||
int
|
||||
static int
|
||||
i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||
{
|
||||
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
|
||||
@ -702,14 +702,17 @@ done_nolock:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_xfer);
|
||||
|
||||
u32 i2c_dw_func(struct i2c_adapter *adap)
|
||||
static u32 i2c_dw_func(struct i2c_adapter *adap)
|
||||
{
|
||||
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
|
||||
return dev->functionality;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_func);
|
||||
|
||||
static struct i2c_algorithm i2c_dw_algo = {
|
||||
.master_xfer = i2c_dw_xfer,
|
||||
.functionality = i2c_dw_func,
|
||||
};
|
||||
|
||||
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
||||
{
|
||||
@ -770,7 +773,7 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
||||
* Interrupt service routine. This gets called whenever an I2C interrupt
|
||||
* occurs.
|
||||
*/
|
||||
irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
|
||||
static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_id;
|
||||
u32 stat, enabled;
|
||||
@ -813,20 +816,6 @@ tx_aborted:
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_isr);
|
||||
|
||||
void i2c_dw_enable(struct dw_i2c_dev *dev)
|
||||
{
|
||||
/* Enable the adapter */
|
||||
__i2c_dw_enable(dev, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_enable);
|
||||
|
||||
u32 i2c_dw_is_enabled(struct dw_i2c_dev *dev)
|
||||
{
|
||||
return dw_readl(dev, DW_IC_ENABLE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_is_enabled);
|
||||
|
||||
void i2c_dw_disable(struct dw_i2c_dev *dev)
|
||||
{
|
||||
@ -839,12 +828,6 @@ void i2c_dw_disable(struct dw_i2c_dev *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_disable);
|
||||
|
||||
void i2c_dw_clear_int(struct dw_i2c_dev *dev)
|
||||
{
|
||||
dw_readl(dev, DW_IC_CLR_INTR);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_clear_int);
|
||||
|
||||
void i2c_dw_disable_int(struct dw_i2c_dev *dev)
|
||||
{
|
||||
dw_writel(dev, 0, DW_IC_INTR_MASK);
|
||||
@ -857,5 +840,40 @@ u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_read_comp_param);
|
||||
|
||||
int i2c_dw_probe(struct dw_i2c_dev *dev)
|
||||
{
|
||||
struct i2c_adapter *adap = &dev->adapter;
|
||||
int r;
|
||||
|
||||
init_completion(&dev->cmd_complete);
|
||||
mutex_init(&dev->lock);
|
||||
|
||||
r = i2c_dw_init(dev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
snprintf(adap->name, sizeof(adap->name),
|
||||
"Synopsys DesignWare I2C adapter");
|
||||
adap->algo = &i2c_dw_algo;
|
||||
adap->dev.parent = dev->dev;
|
||||
i2c_set_adapdata(adap, dev);
|
||||
|
||||
i2c_dw_disable_int(dev);
|
||||
r = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, IRQF_SHARED,
|
||||
dev_name(dev->dev), dev);
|
||||
if (r) {
|
||||
dev_err(dev->dev, "failure requesting irq %i: %d\n",
|
||||
dev->irq, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = i2c_add_numbered_adapter(adap);
|
||||
if (r)
|
||||
dev_err(dev->dev, "failure adding adapter: %d\n", r);
|
||||
|
||||
return r;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_probe);
|
||||
|
||||
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -112,19 +112,11 @@ struct dw_i2c_dev {
|
||||
#define ACCESS_SWAP 0x00000001
|
||||
#define ACCESS_16BIT 0x00000002
|
||||
|
||||
extern u32 dw_readl(struct dw_i2c_dev *dev, int offset);
|
||||
extern void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset);
|
||||
extern int i2c_dw_init(struct dw_i2c_dev *dev);
|
||||
extern int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
||||
int num);
|
||||
extern u32 i2c_dw_func(struct i2c_adapter *adap);
|
||||
extern irqreturn_t i2c_dw_isr(int this_irq, void *dev_id);
|
||||
extern void i2c_dw_enable(struct dw_i2c_dev *dev);
|
||||
extern u32 i2c_dw_is_enabled(struct dw_i2c_dev *dev);
|
||||
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);
|
||||
extern int i2c_dw_probe(struct dw_i2c_dev *dev);
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL)
|
||||
extern int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev);
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/acpi.h>
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
#define DRIVER_NAME "i2c-designware-pci"
|
||||
@ -158,11 +159,6 @@ static struct dw_pci_controller dw_pci_controllers[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct i2c_algorithm i2c_dw_algo = {
|
||||
.master_xfer = i2c_dw_xfer,
|
||||
.functionality = i2c_dw_func,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int i2c_dw_pci_suspend(struct device *dev)
|
||||
{
|
||||
@ -222,13 +218,12 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
init_completion(&dev->cmd_complete);
|
||||
mutex_init(&dev->lock);
|
||||
dev->clk = NULL;
|
||||
dev->controller = controller;
|
||||
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
|
||||
dev->base = pcim_iomap_table(pdev)[0];
|
||||
dev->dev = &pdev->dev;
|
||||
dev->irq = pdev->irq;
|
||||
dev->functionality = controller->functionality |
|
||||
DW_DEFAULT_FUNCTIONALITY;
|
||||
|
||||
@ -246,34 +241,16 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
|
||||
dev->tx_fifo_depth = controller->tx_fifo_depth;
|
||||
dev->rx_fifo_depth = controller->rx_fifo_depth;
|
||||
r = i2c_dw_init(dev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
adap = &dev->adapter;
|
||||
i2c_set_adapdata(adap, dev);
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = 0;
|
||||
adap->algo = &i2c_dw_algo;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
|
||||
adap->nr = controller->bus_num;
|
||||
|
||||
snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci");
|
||||
|
||||
r = devm_request_irq(&pdev->dev, pdev->irq, i2c_dw_isr,
|
||||
IRQF_SHARED | IRQF_COND_SUSPEND, adap->name, dev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
|
||||
r = i2c_dw_probe(dev);
|
||||
if (r)
|
||||
return r;
|
||||
}
|
||||
|
||||
i2c_dw_disable_int(dev);
|
||||
i2c_dw_clear_int(dev);
|
||||
r = i2c_add_numbered_adapter(adap);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failure adding adapter\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
|
@ -42,10 +42,6 @@
|
||||
#include <linux/platform_data/i2c-designware.h>
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
static struct i2c_algorithm i2c_dw_algo = {
|
||||
.master_xfer = i2c_dw_xfer,
|
||||
.functionality = i2c_dw_func,
|
||||
};
|
||||
static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
||||
{
|
||||
return clk_get_rate(dev->clk)/1000;
|
||||
@ -97,7 +93,6 @@ static void dw_i2c_acpi_params(struct platform_device *pdev, char method[],
|
||||
static int dw_i2c_acpi_configure(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
||||
const struct acpi_device_id *id;
|
||||
|
||||
dev->adapter.nr = -1;
|
||||
dev->tx_fifo_depth = 32;
|
||||
@ -111,29 +106,9 @@ static int dw_i2c_acpi_configure(struct platform_device *pdev)
|
||||
dw_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt,
|
||||
&dev->sda_hold_time);
|
||||
|
||||
/*
|
||||
* Provide a way for Designware I2C host controllers that are not
|
||||
* based on Intel LPSS to specify their input clock frequency via
|
||||
* id->driver_data.
|
||||
*/
|
||||
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
|
||||
if (id && id->driver_data)
|
||||
clk_register_fixed_rate(&pdev->dev, dev_name(&pdev->dev), NULL,
|
||||
CLK_IS_ROOT, id->driver_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dw_i2c_acpi_unconfigure(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
||||
const struct acpi_device_id *id;
|
||||
|
||||
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
|
||||
if (id && id->driver_data)
|
||||
clk_unregister(dev->clk);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id dw_i2c_acpi_match[] = {
|
||||
{ "INT33C2", 0 },
|
||||
{ "INT33C3", 0 },
|
||||
@ -141,7 +116,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = {
|
||||
{ "INT3433", 0 },
|
||||
{ "80860F41", 0 },
|
||||
{ "808622C1", 0 },
|
||||
{ "AMD0010", 133 * 1000 * 1000 },
|
||||
{ "AMD0010", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);
|
||||
@ -150,10 +125,9 @@ static inline int dw_i2c_acpi_configure(struct platform_device *pdev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline void dw_i2c_acpi_unconfigure(struct platform_device *pdev) { }
|
||||
#endif
|
||||
|
||||
static int dw_i2c_probe(struct platform_device *pdev)
|
||||
static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_i2c_dev *dev;
|
||||
struct i2c_adapter *adap;
|
||||
@ -175,8 +149,6 @@ static int dw_i2c_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(dev->base))
|
||||
return PTR_ERR(dev->base);
|
||||
|
||||
init_completion(&dev->cmd_complete);
|
||||
mutex_init(&dev->lock);
|
||||
dev->dev = &pdev->dev;
|
||||
dev->irq = irq;
|
||||
platform_set_drvdata(pdev, dev);
|
||||
@ -251,26 +223,11 @@ static int dw_i2c_probe(struct platform_device *pdev)
|
||||
dev->rx_fifo_depth = ((param1 >> 8) & 0xff) + 1;
|
||||
dev->adapter.nr = pdev->id;
|
||||
}
|
||||
r = i2c_dw_init(dev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
i2c_dw_disable_int(dev);
|
||||
r = devm_request_irq(&pdev->dev, dev->irq, i2c_dw_isr, IRQF_SHARED,
|
||||
pdev->name, dev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
|
||||
return r;
|
||||
}
|
||||
|
||||
adap = &dev->adapter;
|
||||
i2c_set_adapdata(adap, dev);
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = I2C_CLASS_DEPRECATED;
|
||||
strlcpy(adap->name, "Synopsys DesignWare I2C adapter",
|
||||
sizeof(adap->name));
|
||||
adap->algo = &i2c_dw_algo;
|
||||
adap->dev.parent = &pdev->dev;
|
||||
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
|
||||
adap->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
if (dev->pm_runtime_disabled) {
|
||||
@ -282,9 +239,8 @@ static int dw_i2c_probe(struct platform_device *pdev)
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
}
|
||||
|
||||
r = i2c_add_numbered_adapter(adap);
|
||||
r = i2c_dw_probe(dev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "failure adding adapter\n");
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return r;
|
||||
}
|
||||
@ -292,7 +248,7 @@ static int dw_i2c_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2c_remove(struct platform_device *pdev)
|
||||
static int dw_i2c_plat_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
||||
|
||||
@ -306,9 +262,6 @@ static int dw_i2c_remove(struct platform_device *pdev)
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
if (has_acpi_companion(&pdev->dev))
|
||||
dw_i2c_acpi_unconfigure(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -321,23 +274,23 @@ MODULE_DEVICE_TABLE(of, dw_i2c_of_match);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dw_i2c_prepare(struct device *dev)
|
||||
static int dw_i2c_plat_prepare(struct device *dev)
|
||||
{
|
||||
return pm_runtime_suspended(dev);
|
||||
}
|
||||
|
||||
static void dw_i2c_complete(struct device *dev)
|
||||
static void dw_i2c_plat_complete(struct device *dev)
|
||||
{
|
||||
if (dev->power.direct_complete)
|
||||
pm_request_resume(dev);
|
||||
}
|
||||
#else
|
||||
#define dw_i2c_prepare NULL
|
||||
#define dw_i2c_complete NULL
|
||||
#define dw_i2c_plat_prepare NULL
|
||||
#define dw_i2c_plat_complete NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int dw_i2c_suspend(struct device *dev)
|
||||
static int dw_i2c_plat_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
|
||||
@ -348,7 +301,7 @@ static int dw_i2c_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2c_resume(struct device *dev)
|
||||
static int dw_i2c_plat_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
|
||||
@ -362,10 +315,10 @@ static int dw_i2c_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
|
||||
.prepare = dw_i2c_prepare,
|
||||
.complete = dw_i2c_complete,
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dw_i2c_suspend, dw_i2c_resume)
|
||||
SET_RUNTIME_PM_OPS(dw_i2c_suspend, dw_i2c_resume, NULL)
|
||||
.prepare = dw_i2c_plat_prepare,
|
||||
.complete = dw_i2c_plat_complete,
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
|
||||
SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL)
|
||||
};
|
||||
|
||||
#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
|
||||
@ -377,8 +330,8 @@ static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
|
||||
MODULE_ALIAS("platform:i2c_designware");
|
||||
|
||||
static struct platform_driver dw_i2c_driver = {
|
||||
.probe = dw_i2c_probe,
|
||||
.remove = dw_i2c_remove,
|
||||
.probe = dw_i2c_plat_probe,
|
||||
.remove = dw_i2c_plat_remove,
|
||||
.driver = {
|
||||
.name = "i2c_designware",
|
||||
.of_match_table = of_match_ptr(dw_i2c_of_match),
|
||||
|
@ -60,6 +60,8 @@
|
||||
* BayTrail (SOC) 0x0f12 32 hard yes yes yes
|
||||
* Sunrise Point-H (PCH) 0xa123 32 hard yes yes yes
|
||||
* Sunrise Point-LP (PCH) 0x9d23 32 hard yes yes yes
|
||||
* DNV (SOC) 0x19df 32 hard yes yes yes
|
||||
* Broxton (SOC) 0x5ad4 32 hard yes yes yes
|
||||
*
|
||||
* Features supported by this driver:
|
||||
* Software PEC no
|
||||
@ -202,6 +204,8 @@
|
||||
#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
|
||||
|
||||
struct i801_mux_config {
|
||||
char *gpio_chip;
|
||||
@ -863,6 +867,8 @@ static const struct pci_device_id i801_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DNV_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BROXTON_SMBUS) },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
@ -1251,11 +1257,15 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
priv->adapter.owner = THIS_MODULE;
|
||||
priv->adapter.class = i801_get_adapter_class(priv);
|
||||
priv->adapter.algo = &smbus_algorithm;
|
||||
priv->adapter.dev.parent = &dev->dev;
|
||||
ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&dev->dev));
|
||||
priv->adapter.retries = 3;
|
||||
|
||||
priv->pci_dev = dev;
|
||||
switch (dev->device) {
|
||||
case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_DNV_SMBUS:
|
||||
priv->features |= FEATURE_I2C_BLOCK_READ;
|
||||
priv->features |= FEATURE_IRQ;
|
||||
priv->features |= FEATURE_SMBUS_PEC;
|
||||
@ -1381,12 +1391,6 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
|
||||
i801_add_tco(priv);
|
||||
|
||||
/* set up the sysfs linkage to our parent device */
|
||||
priv->adapter.dev.parent = &dev->dev;
|
||||
|
||||
/* Retry up to 3 times on lost arbitration */
|
||||
priv->adapter.retries = 3;
|
||||
|
||||
snprintf(priv->adapter.name, sizeof(priv->adapter.name),
|
||||
"SMBus I801 adapter at %04lx", priv->smba);
|
||||
err = i2c_add_adapter(&priv->adapter);
|
||||
|
@ -798,6 +798,7 @@ static const struct of_device_id ibm_iic_match[] = {
|
||||
{ .compatible = "ibm,iic", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ibm_iic_match);
|
||||
|
||||
static struct platform_driver ibm_iic_driver = {
|
||||
.driver = {
|
||||
|
@ -278,8 +278,6 @@
|
||||
#define ISR_COMPLETE(err) (ISR_COMPLETE_M | (ISR_STATUS_M & (err)))
|
||||
#define ISR_FATAL(err) (ISR_COMPLETE(err) | ISR_FATAL_M)
|
||||
|
||||
#define REL_SOC_IP_SCB_2_2_1 0x00020201
|
||||
|
||||
enum img_i2c_mode {
|
||||
MODE_INACTIVE,
|
||||
MODE_RAW,
|
||||
@ -536,6 +534,7 @@ static void img_i2c_read_fifo(struct img_i2c *i2c)
|
||||
u32 fifo_status;
|
||||
u8 data;
|
||||
|
||||
img_i2c_wr_rd_fence(i2c);
|
||||
fifo_status = img_i2c_readl(i2c, SCB_FIFO_STATUS_REG);
|
||||
if (fifo_status & FIFO_READ_EMPTY)
|
||||
break;
|
||||
@ -544,7 +543,6 @@ static void img_i2c_read_fifo(struct img_i2c *i2c)
|
||||
*i2c->msg.buf = data;
|
||||
|
||||
img_i2c_writel(i2c, SCB_READ_FIFO_REG, 0xff);
|
||||
img_i2c_wr_rd_fence(i2c);
|
||||
i2c->msg.len--;
|
||||
i2c->msg.buf++;
|
||||
}
|
||||
@ -556,12 +554,12 @@ static void img_i2c_write_fifo(struct img_i2c *i2c)
|
||||
while (i2c->msg.len) {
|
||||
u32 fifo_status;
|
||||
|
||||
img_i2c_wr_rd_fence(i2c);
|
||||
fifo_status = img_i2c_readl(i2c, SCB_FIFO_STATUS_REG);
|
||||
if (fifo_status & FIFO_WRITE_FULL)
|
||||
break;
|
||||
|
||||
img_i2c_writel(i2c, SCB_WRITE_DATA_REG, *i2c->msg.buf);
|
||||
img_i2c_wr_rd_fence(i2c);
|
||||
i2c->msg.len--;
|
||||
i2c->msg.buf++;
|
||||
}
|
||||
@ -859,7 +857,7 @@ static unsigned int img_i2c_auto(struct img_i2c *i2c,
|
||||
}
|
||||
|
||||
/* Enable transaction halt on start bit */
|
||||
if (!i2c->last_msg && i2c->line_status & LINESTAT_START_BIT_DET) {
|
||||
if (!i2c->last_msg && line_status & LINESTAT_START_BIT_DET) {
|
||||
img_i2c_transaction_halt(i2c, true);
|
||||
/* we're no longer interested in the slave event */
|
||||
i2c->int_enable &= ~INT_SLAVE_EVENT;
|
||||
@ -1062,6 +1060,15 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
i2c->last_msg = (i == num - 1);
|
||||
reinit_completion(&i2c->msg_complete);
|
||||
|
||||
/*
|
||||
* Clear line status and all interrupts before starting a
|
||||
* transfer, as we may have unserviced interrupts from
|
||||
* previous transfers that might be handled in the context
|
||||
* of the new transfer.
|
||||
*/
|
||||
img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0);
|
||||
img_i2c_writel(i2c, SCB_CLEAR_REG, ~0);
|
||||
|
||||
if (atomic)
|
||||
img_i2c_atomic_start(i2c);
|
||||
else if (msg->flags & I2C_M_RD)
|
||||
@ -1120,13 +1127,8 @@ static int img_i2c_init(struct img_i2c *i2c)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rev == REL_SOC_IP_SCB_2_2_1) {
|
||||
i2c->need_wr_rd_fence = true;
|
||||
dev_info(i2c->adap.dev.parent, "fence quirk enabled");
|
||||
}
|
||||
|
||||
bitrate_khz = i2c->bitrate / 1000;
|
||||
clk_khz = clk_get_rate(i2c->scb_clk) / 1000;
|
||||
/* Fencing enabled by default. */
|
||||
i2c->need_wr_rd_fence = true;
|
||||
|
||||
/* Determine what mode we're in from the bitrate */
|
||||
timing = timings[0];
|
||||
@ -1136,6 +1138,17 @@ static int img_i2c_init(struct img_i2c *i2c)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i2c->bitrate > timings[ARRAY_SIZE(timings) - 1].max_bitrate) {
|
||||
dev_warn(i2c->adap.dev.parent,
|
||||
"requested bitrate (%u) is higher than the max bitrate supported (%u)\n",
|
||||
i2c->bitrate,
|
||||
timings[ARRAY_SIZE(timings) - 1].max_bitrate);
|
||||
timing = timings[ARRAY_SIZE(timings) - 1];
|
||||
i2c->bitrate = timing.max_bitrate;
|
||||
}
|
||||
|
||||
bitrate_khz = i2c->bitrate / 1000;
|
||||
clk_khz = clk_get_rate(i2c->scb_clk) / 1000;
|
||||
|
||||
/* Find the prescale that would give us that inc (approx delay = 0) */
|
||||
prescale = SCB_OPT_INC * clk_khz / (256 * 16 * bitrate_khz);
|
||||
@ -1182,32 +1195,32 @@ static int img_i2c_init(struct img_i2c *i2c)
|
||||
((bitrate_khz * clk_period) / 2))
|
||||
int_bitrate++;
|
||||
|
||||
/* Setup TCKH value */
|
||||
tckh = timing.tckh / clk_period;
|
||||
if (timing.tckh % clk_period)
|
||||
tckh++;
|
||||
|
||||
if (tckh > 0)
|
||||
data = tckh - 1;
|
||||
else
|
||||
data = 0;
|
||||
|
||||
img_i2c_writel(i2c, SCB_TIME_TCKH_REG, data);
|
||||
|
||||
/* Setup TCKL value */
|
||||
/*
|
||||
* Setup clock duty cycle, start with 50% and adjust TCKH and TCKL
|
||||
* values from there if they don't meet minimum timing requirements
|
||||
*/
|
||||
tckh = int_bitrate / 2;
|
||||
tckl = int_bitrate - tckh;
|
||||
|
||||
if (tckl > 0)
|
||||
data = tckl - 1;
|
||||
else
|
||||
data = 0;
|
||||
/* Adjust TCKH and TCKL values */
|
||||
data = DIV_ROUND_UP(timing.tckl, clk_period);
|
||||
|
||||
img_i2c_writel(i2c, SCB_TIME_TCKL_REG, data);
|
||||
if (tckl < data) {
|
||||
tckl = data;
|
||||
tckh = int_bitrate - tckl;
|
||||
}
|
||||
|
||||
if (tckh > 0)
|
||||
--tckh;
|
||||
|
||||
if (tckl > 0)
|
||||
--tckl;
|
||||
|
||||
img_i2c_writel(i2c, SCB_TIME_TCKH_REG, tckh);
|
||||
img_i2c_writel(i2c, SCB_TIME_TCKL_REG, tckl);
|
||||
|
||||
/* Setup TSDH value */
|
||||
tsdh = timing.tsdh / clk_period;
|
||||
if (timing.tsdh % clk_period)
|
||||
tsdh++;
|
||||
tsdh = DIV_ROUND_UP(timing.tsdh, clk_period);
|
||||
|
||||
if (tsdh > 1)
|
||||
data = tsdh - 1;
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_data/i2c-imx.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
@ -207,6 +208,11 @@ struct imx_i2c_struct {
|
||||
unsigned int cur_clk;
|
||||
unsigned int bitrate;
|
||||
const struct imx_i2c_hwdata *hwdata;
|
||||
struct i2c_bus_recovery_info rinfo;
|
||||
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pinctrl_pins_default;
|
||||
struct pinctrl_state *pinctrl_pins_gpio;
|
||||
|
||||
struct imx_i2c_dma *dma;
|
||||
};
|
||||
@ -461,7 +467,7 @@ static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
if (imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR) & I2SR_RXAK) {
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> No ACK\n", __func__);
|
||||
return -EIO; /* No ACK */
|
||||
return -ENXIO; /* No ACK */
|
||||
}
|
||||
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> ACK received\n", __func__);
|
||||
@ -896,6 +902,13 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
||||
|
||||
/* Start I2C transfer */
|
||||
result = i2c_imx_start(i2c_imx);
|
||||
if (result) {
|
||||
if (i2c_imx->adapter.bus_recovery_info) {
|
||||
i2c_recover_bus(&i2c_imx->adapter);
|
||||
result = i2c_imx_start(i2c_imx);
|
||||
}
|
||||
}
|
||||
|
||||
if (result)
|
||||
goto fail0;
|
||||
|
||||
@ -956,6 +969,55 @@ fail0:
|
||||
return (result < 0) ? result : num;
|
||||
}
|
||||
|
||||
static void i2c_imx_prepare_recovery(struct i2c_adapter *adap)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx;
|
||||
|
||||
i2c_imx = container_of(adap, struct imx_i2c_struct, adapter);
|
||||
|
||||
pinctrl_select_state(i2c_imx->pinctrl, i2c_imx->pinctrl_pins_gpio);
|
||||
}
|
||||
|
||||
static void i2c_imx_unprepare_recovery(struct i2c_adapter *adap)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx;
|
||||
|
||||
i2c_imx = container_of(adap, struct imx_i2c_struct, adapter);
|
||||
|
||||
pinctrl_select_state(i2c_imx->pinctrl, i2c_imx->pinctrl_pins_default);
|
||||
}
|
||||
|
||||
static void i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_bus_recovery_info *rinfo = &i2c_imx->rinfo;
|
||||
|
||||
i2c_imx->pinctrl_pins_default = pinctrl_lookup_state(i2c_imx->pinctrl,
|
||||
PINCTRL_STATE_DEFAULT);
|
||||
i2c_imx->pinctrl_pins_gpio = pinctrl_lookup_state(i2c_imx->pinctrl,
|
||||
"gpio");
|
||||
rinfo->sda_gpio = of_get_named_gpio_flags(pdev->dev.of_node,
|
||||
"sda-gpios", 0, NULL);
|
||||
rinfo->scl_gpio = of_get_named_gpio_flags(pdev->dev.of_node,
|
||||
"scl-gpios", 0, NULL);
|
||||
|
||||
if (!gpio_is_valid(rinfo->sda_gpio) ||
|
||||
!gpio_is_valid(rinfo->scl_gpio) ||
|
||||
IS_ERR(i2c_imx->pinctrl_pins_default) ||
|
||||
IS_ERR(i2c_imx->pinctrl_pins_gpio)) {
|
||||
dev_dbg(&pdev->dev, "recovery information incomplete\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev, "using scl-gpio %d and sda-gpio %d for recovery\n",
|
||||
rinfo->sda_gpio, rinfo->scl_gpio);
|
||||
|
||||
rinfo->prepare_recovery = i2c_imx_prepare_recovery;
|
||||
rinfo->unprepare_recovery = i2c_imx_unprepare_recovery;
|
||||
rinfo->recover_bus = i2c_generic_gpio_recovery;
|
||||
i2c_imx->adapter.bus_recovery_info = rinfo;
|
||||
}
|
||||
|
||||
static u32 i2c_imx_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
|
||||
@ -1023,6 +1085,13 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "can't enable I2C clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_imx->pinctrl = devm_pinctrl_get(&pdev->dev);
|
||||
if (IS_ERR(i2c_imx->pinctrl)) {
|
||||
ret = PTR_ERR(i2c_imx->pinctrl);
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
/* Request IRQ */
|
||||
ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0,
|
||||
pdev->name, i2c_imx);
|
||||
@ -1056,6 +1125,8 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
i2c_imx_init_recovery_info(i2c_imx, pdev);
|
||||
|
||||
/* Set up platform driver data */
|
||||
platform_set_drvdata(pdev, i2c_imx);
|
||||
clk_disable_unprepare(i2c_imx->clk);
|
||||
|
@ -165,14 +165,13 @@ struct ismt_desc {
|
||||
|
||||
struct ismt_priv {
|
||||
struct i2c_adapter adapter;
|
||||
void *smba; /* PCI BAR */
|
||||
void __iomem *smba; /* PCI BAR */
|
||||
struct pci_dev *pci_dev;
|
||||
struct ismt_desc *hw; /* descriptor virt base addr */
|
||||
dma_addr_t io_rng_dma; /* descriptor HW base addr */
|
||||
u8 head; /* ring buffer head pointer */
|
||||
struct completion cmp; /* interrupt completion */
|
||||
u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* temp R/W data buffer */
|
||||
bool using_msi; /* type of interrupt flag */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -398,7 +397,7 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr,
|
||||
desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, read_write);
|
||||
|
||||
/* Initialize common control bits */
|
||||
if (likely(priv->using_msi))
|
||||
if (likely(pci_dev_msi_enabled(priv->pci_dev)))
|
||||
desc->control = ISMT_DESC_INT | ISMT_DESC_FAIR;
|
||||
else
|
||||
desc->control = ISMT_DESC_FAIR;
|
||||
@ -789,11 +788,8 @@ static int ismt_int_init(struct ismt_priv *priv)
|
||||
|
||||
/* Try using MSI interrupts */
|
||||
err = pci_enable_msi(priv->pci_dev);
|
||||
if (err) {
|
||||
dev_warn(&priv->pci_dev->dev,
|
||||
"Unable to use MSI interrupts, falling back to legacy\n");
|
||||
if (err)
|
||||
goto intx;
|
||||
}
|
||||
|
||||
err = devm_request_irq(&priv->pci_dev->dev,
|
||||
priv->pci_dev->irq,
|
||||
@ -806,11 +802,13 @@ static int ismt_int_init(struct ismt_priv *priv)
|
||||
goto intx;
|
||||
}
|
||||
|
||||
priv->using_msi = true;
|
||||
goto done;
|
||||
return 0;
|
||||
|
||||
/* Try using legacy interrupts */
|
||||
intx:
|
||||
dev_warn(&priv->pci_dev->dev,
|
||||
"Unable to use MSI interrupts, falling back to legacy\n");
|
||||
|
||||
err = devm_request_irq(&priv->pci_dev->dev,
|
||||
priv->pci_dev->irq,
|
||||
ismt_do_interrupt,
|
||||
@ -819,12 +817,9 @@ intx:
|
||||
priv);
|
||||
if (err) {
|
||||
dev_err(&priv->pci_dev->dev, "no usable interrupts\n");
|
||||
return -ENODEV;
|
||||
return err;
|
||||
}
|
||||
|
||||
priv->using_msi = false;
|
||||
|
||||
done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -847,17 +842,13 @@ ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
return -ENOMEM;
|
||||
|
||||
pci_set_drvdata(pdev, priv);
|
||||
|
||||
i2c_set_adapdata(&priv->adapter, priv);
|
||||
priv->adapter.owner = THIS_MODULE;
|
||||
|
||||
priv->adapter.class = I2C_CLASS_HWMON;
|
||||
|
||||
priv->adapter.algo = &smbus_algorithm;
|
||||
|
||||
/* set up the sysfs linkage to our parent device */
|
||||
priv->adapter.dev.parent = &pdev->dev;
|
||||
|
||||
/* number of retries on lost arbitration */
|
||||
ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&pdev->dev));
|
||||
priv->adapter.retries = ISMT_MAX_RETRIES;
|
||||
|
||||
priv->pci_dev = pdev;
|
||||
@ -904,8 +895,7 @@ ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
priv->smba = pcim_iomap(pdev, SMBBAR, len);
|
||||
if (!priv->smba) {
|
||||
dev_err(&pdev->dev, "Unable to ioremap SMBus BAR\n");
|
||||
err = -ENODEV;
|
||||
goto fail;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) ||
|
||||
@ -915,32 +905,26 @@ ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
DMA_BIT_MASK(32)) != 0)) {
|
||||
dev_err(&pdev->dev, "pci_set_dma_mask fail %p\n",
|
||||
pdev);
|
||||
err = -ENODEV;
|
||||
goto fail;
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
err = ismt_dev_init(priv);
|
||||
if (err)
|
||||
goto fail;
|
||||
return err;
|
||||
|
||||
ismt_hw_init(priv);
|
||||
|
||||
err = ismt_int_init(priv);
|
||||
if (err)
|
||||
goto fail;
|
||||
return err;
|
||||
|
||||
err = i2c_add_adapter(&priv->adapter);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to add SMBus iSMT adapter\n");
|
||||
err = -ENODEV;
|
||||
goto fail;
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
pci_release_region(pdev, SMBBAR);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -952,47 +936,13 @@ static void ismt_remove(struct pci_dev *pdev)
|
||||
struct ismt_priv *priv = pci_get_drvdata(pdev);
|
||||
|
||||
i2c_del_adapter(&priv->adapter);
|
||||
pci_release_region(pdev, SMBBAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* ismt_suspend() - place the device in suspend
|
||||
* @pdev: PCI-Express device
|
||||
* @mesg: PM message
|
||||
*/
|
||||
#ifdef CONFIG_PM
|
||||
static int ismt_suspend(struct pci_dev *pdev, pm_message_t mesg)
|
||||
{
|
||||
pci_save_state(pdev);
|
||||
pci_set_power_state(pdev, pci_choose_state(pdev, mesg));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ismt_resume() - PCI resume code
|
||||
* @pdev: PCI-Express device
|
||||
*/
|
||||
static int ismt_resume(struct pci_dev *pdev)
|
||||
{
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
pci_restore_state(pdev);
|
||||
return pci_enable_device(pdev);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define ismt_suspend NULL
|
||||
#define ismt_resume NULL
|
||||
|
||||
#endif
|
||||
|
||||
static struct pci_driver ismt_driver = {
|
||||
.name = "ismt_smbus",
|
||||
.id_table = ismt_ids,
|
||||
.probe = ismt_probe,
|
||||
.remove = ismt_remove,
|
||||
.suspend = ismt_suspend,
|
||||
.resume = ismt_resume,
|
||||
};
|
||||
|
||||
module_pci_driver(ismt_driver);
|
||||
|
@ -475,6 +475,7 @@ static const struct of_device_id meson_i2c_match[] = {
|
||||
{ .compatible = "amlogic,meson6-i2c" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, meson_i2c_match);
|
||||
|
||||
static struct platform_driver meson_i2c_driver = {
|
||||
.probe = meson_i2c_probe,
|
||||
|
@ -728,11 +728,27 @@ static int mtk_i2c_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mtk_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct mtk_i2c *i2c = dev_get_drvdata(dev);
|
||||
|
||||
mtk_i2c_init_hw(i2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops mtk_i2c_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(NULL, mtk_i2c_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver mtk_i2c_driver = {
|
||||
.probe = mtk_i2c_probe,
|
||||
.remove = mtk_i2c_remove,
|
||||
.driver = {
|
||||
.name = I2C_DRV_NAME,
|
||||
.pm = &mtk_i2c_pm,
|
||||
.of_match_table = of_match_ptr(mtk_i2c_of_match),
|
||||
},
|
||||
};
|
||||
|
@ -92,6 +92,16 @@ static void oc_setreg_32(struct ocores_i2c *i2c, int reg, u8 value)
|
||||
iowrite32(value, i2c->base + (reg << i2c->reg_shift));
|
||||
}
|
||||
|
||||
static void oc_setreg_16be(struct ocores_i2c *i2c, int reg, u8 value)
|
||||
{
|
||||
iowrite16be(value, i2c->base + (reg << i2c->reg_shift));
|
||||
}
|
||||
|
||||
static void oc_setreg_32be(struct ocores_i2c *i2c, int reg, u8 value)
|
||||
{
|
||||
iowrite32be(value, i2c->base + (reg << i2c->reg_shift));
|
||||
}
|
||||
|
||||
static inline u8 oc_getreg_8(struct ocores_i2c *i2c, int reg)
|
||||
{
|
||||
return ioread8(i2c->base + (reg << i2c->reg_shift));
|
||||
@ -107,6 +117,16 @@ static inline u8 oc_getreg_32(struct ocores_i2c *i2c, int reg)
|
||||
return ioread32(i2c->base + (reg << i2c->reg_shift));
|
||||
}
|
||||
|
||||
static inline u8 oc_getreg_16be(struct ocores_i2c *i2c, int reg)
|
||||
{
|
||||
return ioread16be(i2c->base + (reg << i2c->reg_shift));
|
||||
}
|
||||
|
||||
static inline u8 oc_getreg_32be(struct ocores_i2c *i2c, int reg)
|
||||
{
|
||||
return ioread32be(i2c->base + (reg << i2c->reg_shift));
|
||||
}
|
||||
|
||||
static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value)
|
||||
{
|
||||
i2c->setreg(i2c, reg, value);
|
||||
@ -428,6 +448,9 @@ static int ocores_i2c_probe(struct platform_device *pdev)
|
||||
i2c->reg_io_width = 1; /* Set to default value */
|
||||
|
||||
if (!i2c->setreg || !i2c->getreg) {
|
||||
bool be = pdata ? pdata->big_endian :
|
||||
of_device_is_big_endian(pdev->dev.of_node);
|
||||
|
||||
switch (i2c->reg_io_width) {
|
||||
case 1:
|
||||
i2c->setreg = oc_setreg_8;
|
||||
@ -435,13 +458,13 @@ static int ocores_i2c_probe(struct platform_device *pdev)
|
||||
break;
|
||||
|
||||
case 2:
|
||||
i2c->setreg = oc_setreg_16;
|
||||
i2c->getreg = oc_getreg_16;
|
||||
i2c->setreg = be ? oc_setreg_16be : oc_setreg_16;
|
||||
i2c->getreg = be ? oc_getreg_16be : oc_getreg_16;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
i2c->setreg = oc_setreg_32;
|
||||
i2c->getreg = oc_getreg_32;
|
||||
i2c->setreg = be ? oc_setreg_32be : oc_setreg_32;
|
||||
i2c->getreg = be ? oc_getreg_32be : oc_getreg_32;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -496,7 +496,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
struct i2c_msg *pmsg;
|
||||
int rc = 0, completed = 0, i;
|
||||
struct i2c_pnx_algo_data *alg_data = adap->algo_data;
|
||||
u32 stat = ioread32(I2C_REG_STS(alg_data));
|
||||
u32 stat;
|
||||
|
||||
dev_dbg(&alg_data->adapter.dev,
|
||||
"%s(): entering: %d messages, stat = %04x.\n",
|
||||
@ -659,9 +659,8 @@ static int i2c_pnx_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(alg_data->clk))
|
||||
return PTR_ERR(alg_data->clk);
|
||||
|
||||
init_timer(&alg_data->mif.timer);
|
||||
alg_data->mif.timer.function = i2c_pnx_timeout;
|
||||
alg_data->mif.timer.data = (unsigned long)alg_data;
|
||||
setup_timer(&alg_data->mif.timer, i2c_pnx_timeout,
|
||||
(unsigned long)alg_data);
|
||||
|
||||
snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name),
|
||||
"%s", pdev->name);
|
||||
|
@ -46,12 +46,15 @@ struct pxa_reg_layout {
|
||||
u32 icr;
|
||||
u32 isr;
|
||||
u32 isar;
|
||||
u32 ilcr;
|
||||
u32 iwcr;
|
||||
};
|
||||
|
||||
enum pxa_i2c_types {
|
||||
REGS_PXA2XX,
|
||||
REGS_PXA3XX,
|
||||
REGS_CE4100,
|
||||
REGS_PXA910,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -79,12 +82,22 @@ static struct pxa_reg_layout pxa_reg_layout[] = {
|
||||
.isr = 0x04,
|
||||
/* no isar register */
|
||||
},
|
||||
[REGS_PXA910] = {
|
||||
.ibmr = 0x00,
|
||||
.idbr = 0x08,
|
||||
.icr = 0x10,
|
||||
.isr = 0x18,
|
||||
.isar = 0x20,
|
||||
.ilcr = 0x28,
|
||||
.iwcr = 0x30,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct platform_device_id i2c_pxa_id_table[] = {
|
||||
{ "pxa2xx-i2c", REGS_PXA2XX },
|
||||
{ "pxa3xx-pwri2c", REGS_PXA3XX },
|
||||
{ "ce4100-i2c", REGS_CE4100 },
|
||||
{ "pxa910-i2c", REGS_PXA910 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table);
|
||||
@ -124,6 +137,23 @@ MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table);
|
||||
#define ISR_SAD (1 << 9) /* slave address detected */
|
||||
#define ISR_BED (1 << 10) /* bus error no ACK/NAK */
|
||||
|
||||
/* bit field shift & mask */
|
||||
#define ILCR_SLV_SHIFT 0
|
||||
#define ILCR_SLV_MASK (0x1FF << ILCR_SLV_SHIFT)
|
||||
#define ILCR_FLV_SHIFT 9
|
||||
#define ILCR_FLV_MASK (0x1FF << ILCR_FLV_SHIFT)
|
||||
#define ILCR_HLVL_SHIFT 18
|
||||
#define ILCR_HLVL_MASK (0x1FF << ILCR_HLVL_SHIFT)
|
||||
#define ILCR_HLVH_SHIFT 27
|
||||
#define ILCR_HLVH_MASK (0x1F << ILCR_HLVH_SHIFT)
|
||||
|
||||
#define IWCR_CNT_SHIFT 0
|
||||
#define IWCR_CNT_MASK (0x1F << IWCR_CNT_SHIFT)
|
||||
#define IWCR_HS_CNT1_SHIFT 5
|
||||
#define IWCR_HS_CNT1_MASK (0x1F << IWCR_HS_CNT1_SHIFT)
|
||||
#define IWCR_HS_CNT2_SHIFT 10
|
||||
#define IWCR_HS_CNT2_MASK (0x1F << IWCR_HS_CNT2_SHIFT)
|
||||
|
||||
struct pxa_i2c {
|
||||
spinlock_t lock;
|
||||
wait_queue_head_t wait;
|
||||
@ -150,6 +180,8 @@ struct pxa_i2c {
|
||||
void __iomem *reg_icr;
|
||||
void __iomem *reg_isr;
|
||||
void __iomem *reg_isar;
|
||||
void __iomem *reg_ilcr;
|
||||
void __iomem *reg_iwcr;
|
||||
|
||||
unsigned long iobase;
|
||||
unsigned long iosize;
|
||||
@ -168,6 +200,8 @@ struct pxa_i2c {
|
||||
#define _ICR(i2c) ((i2c)->reg_icr)
|
||||
#define _ISR(i2c) ((i2c)->reg_isr)
|
||||
#define _ISAR(i2c) ((i2c)->reg_isar)
|
||||
#define _ILCR(i2c) ((i2c)->reg_ilcr)
|
||||
#define _IWCR(i2c) ((i2c)->reg_iwcr)
|
||||
|
||||
/*
|
||||
* I2C Slave mode address
|
||||
@ -1102,7 +1136,7 @@ static const struct i2c_algorithm i2c_pxa_pio_algorithm = {
|
||||
static const struct of_device_id i2c_pxa_dt_ids[] = {
|
||||
{ .compatible = "mrvl,pxa-i2c", .data = (void *)REGS_PXA2XX },
|
||||
{ .compatible = "mrvl,pwri2c", .data = (void *)REGS_PXA3XX },
|
||||
{ .compatible = "mrvl,mmp-twsi", .data = (void *)REGS_PXA2XX },
|
||||
{ .compatible = "mrvl,mmp-twsi", .data = (void *)REGS_PXA910 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids);
|
||||
@ -1203,6 +1237,11 @@ static int i2c_pxa_probe(struct platform_device *dev)
|
||||
if (i2c_type != REGS_CE4100)
|
||||
i2c->reg_isar = i2c->reg_base + pxa_reg_layout[i2c_type].isar;
|
||||
|
||||
if (i2c_type == REGS_PXA910) {
|
||||
i2c->reg_ilcr = i2c->reg_base + pxa_reg_layout[i2c_type].ilcr;
|
||||
i2c->reg_iwcr = i2c->reg_base + pxa_reg_layout[i2c_type].iwcr;
|
||||
}
|
||||
|
||||
i2c->iobase = res->start;
|
||||
i2c->iosize = resource_size(res);
|
||||
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/i2c-rcar.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
@ -103,6 +102,7 @@
|
||||
enum rcar_i2c_type {
|
||||
I2C_RCAR_GEN1,
|
||||
I2C_RCAR_GEN2,
|
||||
I2C_RCAR_GEN3,
|
||||
};
|
||||
|
||||
struct rcar_i2c_priv {
|
||||
@ -178,6 +178,7 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
|
||||
cdf_width = 2;
|
||||
break;
|
||||
case I2C_RCAR_GEN2:
|
||||
case I2C_RCAR_GEN3:
|
||||
cdf_width = 3;
|
||||
break;
|
||||
default:
|
||||
@ -625,13 +626,13 @@ static const struct of_device_id rcar_i2c_dt_ids[] = {
|
||||
{ .compatible = "renesas,i2c-r8a7792", .data = (void *)I2C_RCAR_GEN2 },
|
||||
{ .compatible = "renesas,i2c-r8a7793", .data = (void *)I2C_RCAR_GEN2 },
|
||||
{ .compatible = "renesas,i2c-r8a7794", .data = (void *)I2C_RCAR_GEN2 },
|
||||
{ .compatible = "renesas,i2c-r8a7795", .data = (void *)I2C_RCAR_GEN3 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids);
|
||||
|
||||
static int rcar_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_rcar_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct rcar_i2c_priv *priv;
|
||||
struct i2c_adapter *adap;
|
||||
struct resource *res;
|
||||
@ -650,15 +651,9 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
bus_speed = 100000; /* default 100 kHz */
|
||||
ret = of_property_read_u32(dev->of_node, "clock-frequency", &bus_speed);
|
||||
if (ret < 0 && pdata && pdata->bus_speed)
|
||||
bus_speed = pdata->bus_speed;
|
||||
of_property_read_u32(dev->of_node, "clock-frequency", &bus_speed);
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
priv->devtype = (long)of_match_device(rcar_i2c_dt_ids,
|
||||
dev)->data;
|
||||
else
|
||||
priv->devtype = platform_get_device_id(pdev)->driver_data;
|
||||
priv->devtype = (enum rcar_i2c_type)of_match_device(rcar_i2c_dt_ids, dev)->data;
|
||||
|
||||
ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
|
||||
if (ret < 0)
|
||||
@ -716,14 +711,6 @@ static int rcar_i2c_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id rcar_i2c_id_table[] = {
|
||||
{ "i2c-rcar", I2C_RCAR_GEN1 },
|
||||
{ "i2c-rcar_gen1", I2C_RCAR_GEN1 },
|
||||
{ "i2c-rcar_gen2", I2C_RCAR_GEN2 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, rcar_i2c_id_table);
|
||||
|
||||
static struct platform_driver rcar_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "i2c-rcar",
|
||||
@ -731,7 +718,6 @@ static struct platform_driver rcar_i2c_driver = {
|
||||
},
|
||||
.probe = rcar_i2c_probe,
|
||||
.remove = rcar_i2c_remove,
|
||||
.id_table = rcar_i2c_id_table,
|
||||
};
|
||||
|
||||
module_platform_driver(rcar_i2c_driver);
|
||||
|
@ -858,6 +858,7 @@ static const struct of_device_id rk3x_i2c_match[] = {
|
||||
{ .compatible = "rockchip,rk3288-i2c", .data = (void *)&soc_data[2] },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rk3x_i2c_match);
|
||||
|
||||
static int rk3x_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -836,6 +836,7 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = {
|
||||
{ .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7795", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config },
|
||||
{},
|
||||
};
|
||||
|
@ -358,11 +358,29 @@ static int i2c_sirfsoc_probe(struct platform_device *pdev)
|
||||
if (err < 0)
|
||||
bitrate = SIRFSOC_I2C_DEFAULT_SPEED;
|
||||
|
||||
if (bitrate < 100000)
|
||||
regval =
|
||||
(2 * ctrl_speed) / (bitrate * 11);
|
||||
else
|
||||
/*
|
||||
* Due to some hardware design issues, we need to tune the formula.
|
||||
* Since i2c is open drain interface that allows the slave to
|
||||
* stall the transaction by holding the SCL line at '0', the RTL
|
||||
* implementation is waiting for SCL feedback from the pin after
|
||||
* setting it to High-Z ('1'). This wait adds to the high-time
|
||||
* interval counter few cycles of the input synchronization
|
||||
* (depending on the SCL_FILTER_REG field), and also the time it
|
||||
* takes for the board pull-up resistor to rise the SCL line.
|
||||
* For slow SCL settings these additions are negligible,
|
||||
* but they start to affect the speed when clock is set to faster
|
||||
* frequencies.
|
||||
* Through the actual tests, use the different user_div value(which
|
||||
* in the divider formular 'Fio / (Fi2c * user_div)') to adapt
|
||||
* the different ranges of i2c bus clock frequency, to make the SCL
|
||||
* more accurate.
|
||||
*/
|
||||
if (bitrate <= 30000)
|
||||
regval = ctrl_speed / (bitrate * 5);
|
||||
else if (bitrate > 30000 && bitrate <= 280000)
|
||||
regval = (2 * ctrl_speed) / (bitrate * 11);
|
||||
else
|
||||
regval = ctrl_speed / (bitrate * 6);
|
||||
|
||||
writel(regval, siic->base + SIRFSOC_I2C_CLK_CTRL);
|
||||
if (regval > 0xFF)
|
||||
|
@ -977,6 +977,7 @@ static const struct of_device_id stu300_dt_match[] = {
|
||||
{ .compatible = "st,ddci2c" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stu300_dt_match);
|
||||
|
||||
static struct platform_driver stu300_i2c_driver = {
|
||||
.driver = {
|
||||
|
@ -873,7 +873,6 @@ static int tegra_i2c_probe(struct platform_device *pdev)
|
||||
i2c_dev->adapter.class = I2C_CLASS_DEPRECATED;
|
||||
strlcpy(i2c_dev->adapter.name, "Tegra I2C adapter",
|
||||
sizeof(i2c_dev->adapter.name));
|
||||
i2c_dev->adapter.algo = &tegra_i2c_algo;
|
||||
i2c_dev->adapter.dev.parent = &pdev->dev;
|
||||
i2c_dev->adapter.nr = pdev->id;
|
||||
i2c_dev->adapter.dev.of_node = pdev->dev.of_node;
|
||||
|
584
drivers/i2c/busses/i2c-uniphier-f.c
Normal file
584
drivers/i2c/busses/i2c-uniphier-f.c
Normal file
@ -0,0 +1,584 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that 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/clk.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define UNIPHIER_FI2C_CR 0x00 /* control register */
|
||||
#define UNIPHIER_FI2C_CR_MST BIT(3) /* master mode */
|
||||
#define UNIPHIER_FI2C_CR_STA BIT(2) /* start condition */
|
||||
#define UNIPHIER_FI2C_CR_STO BIT(1) /* stop condition */
|
||||
#define UNIPHIER_FI2C_CR_NACK BIT(0) /* do not return ACK */
|
||||
#define UNIPHIER_FI2C_DTTX 0x04 /* TX FIFO */
|
||||
#define UNIPHIER_FI2C_DTTX_CMD BIT(8) /* send command (slave addr) */
|
||||
#define UNIPHIER_FI2C_DTTX_RD BIT(0) /* read transaction */
|
||||
#define UNIPHIER_FI2C_DTRX 0x04 /* RX FIFO */
|
||||
#define UNIPHIER_FI2C_SLAD 0x0c /* slave address */
|
||||
#define UNIPHIER_FI2C_CYC 0x10 /* clock cycle control */
|
||||
#define UNIPHIER_FI2C_LCTL 0x14 /* clock low period control */
|
||||
#define UNIPHIER_FI2C_SSUT 0x18 /* restart/stop setup time control */
|
||||
#define UNIPHIER_FI2C_DSUT 0x1c /* data setup time control */
|
||||
#define UNIPHIER_FI2C_INT 0x20 /* interrupt status */
|
||||
#define UNIPHIER_FI2C_IE 0x24 /* interrupt enable */
|
||||
#define UNIPHIER_FI2C_IC 0x28 /* interrupt clear */
|
||||
#define UNIPHIER_FI2C_INT_TE BIT(9) /* TX FIFO empty */
|
||||
#define UNIPHIER_FI2C_INT_RF BIT(8) /* RX FIFO full */
|
||||
#define UNIPHIER_FI2C_INT_TC BIT(7) /* send complete (STOP) */
|
||||
#define UNIPHIER_FI2C_INT_RC BIT(6) /* receive complete (STOP) */
|
||||
#define UNIPHIER_FI2C_INT_TB BIT(5) /* sent specified bytes */
|
||||
#define UNIPHIER_FI2C_INT_RB BIT(4) /* received specified bytes */
|
||||
#define UNIPHIER_FI2C_INT_NA BIT(2) /* no ACK */
|
||||
#define UNIPHIER_FI2C_INT_AL BIT(1) /* arbitration lost */
|
||||
#define UNIPHIER_FI2C_SR 0x2c /* status register */
|
||||
#define UNIPHIER_FI2C_SR_DB BIT(12) /* device busy */
|
||||
#define UNIPHIER_FI2C_SR_STS BIT(11) /* stop condition detected */
|
||||
#define UNIPHIER_FI2C_SR_BB BIT(8) /* bus busy */
|
||||
#define UNIPHIER_FI2C_SR_RFF BIT(3) /* RX FIFO full */
|
||||
#define UNIPHIER_FI2C_SR_RNE BIT(2) /* RX FIFO not empty */
|
||||
#define UNIPHIER_FI2C_SR_TNF BIT(1) /* TX FIFO not full */
|
||||
#define UNIPHIER_FI2C_SR_TFE BIT(0) /* TX FIFO empty */
|
||||
#define UNIPHIER_FI2C_RST 0x34 /* reset control */
|
||||
#define UNIPHIER_FI2C_RST_TBRST BIT(2) /* clear TX FIFO */
|
||||
#define UNIPHIER_FI2C_RST_RBRST BIT(1) /* clear RX FIFO */
|
||||
#define UNIPHIER_FI2C_RST_RST BIT(0) /* forcible bus reset */
|
||||
#define UNIPHIER_FI2C_BM 0x38 /* bus monitor */
|
||||
#define UNIPHIER_FI2C_BM_SDAO BIT(3) /* output for SDA line */
|
||||
#define UNIPHIER_FI2C_BM_SDAS BIT(2) /* readback of SDA line */
|
||||
#define UNIPHIER_FI2C_BM_SCLO BIT(1) /* output for SCL line */
|
||||
#define UNIPHIER_FI2C_BM_SCLS BIT(0) /* readback of SCL line */
|
||||
#define UNIPHIER_FI2C_NOISE 0x3c /* noise filter control */
|
||||
#define UNIPHIER_FI2C_TBC 0x40 /* TX byte count setting */
|
||||
#define UNIPHIER_FI2C_RBC 0x44 /* RX byte count setting */
|
||||
#define UNIPHIER_FI2C_TBCM 0x48 /* TX byte count monitor */
|
||||
#define UNIPHIER_FI2C_RBCM 0x4c /* RX byte count monitor */
|
||||
#define UNIPHIER_FI2C_BRST 0x50 /* bus reset */
|
||||
#define UNIPHIER_FI2C_BRST_FOEN BIT(1) /* normal operation */
|
||||
#define UNIPHIER_FI2C_BRST_RSCL BIT(0) /* release SCL */
|
||||
|
||||
#define UNIPHIER_FI2C_INT_FAULTS \
|
||||
(UNIPHIER_FI2C_INT_NA | UNIPHIER_FI2C_INT_AL)
|
||||
#define UNIPHIER_FI2C_INT_STOP \
|
||||
(UNIPHIER_FI2C_INT_TC | UNIPHIER_FI2C_INT_RC)
|
||||
|
||||
#define UNIPHIER_FI2C_RD BIT(0)
|
||||
#define UNIPHIER_FI2C_STOP BIT(1)
|
||||
#define UNIPHIER_FI2C_MANUAL_NACK BIT(2)
|
||||
#define UNIPHIER_FI2C_BYTE_WISE BIT(3)
|
||||
#define UNIPHIER_FI2C_DEFER_STOP_COMP BIT(4)
|
||||
|
||||
#define UNIPHIER_FI2C_DEFAULT_SPEED 100000
|
||||
#define UNIPHIER_FI2C_MAX_SPEED 400000
|
||||
#define UNIPHIER_FI2C_FIFO_SIZE 8
|
||||
|
||||
struct uniphier_fi2c_priv {
|
||||
struct completion comp;
|
||||
struct i2c_adapter adap;
|
||||
void __iomem *membase;
|
||||
struct clk *clk;
|
||||
unsigned int len;
|
||||
u8 *buf;
|
||||
u32 enabled_irqs;
|
||||
int error;
|
||||
unsigned int flags;
|
||||
unsigned int busy_cnt;
|
||||
};
|
||||
|
||||
static void uniphier_fi2c_fill_txfifo(struct uniphier_fi2c_priv *priv,
|
||||
bool first)
|
||||
{
|
||||
int fifo_space = UNIPHIER_FI2C_FIFO_SIZE;
|
||||
|
||||
/*
|
||||
* TX-FIFO stores slave address in it for the first access.
|
||||
* Decrement the counter.
|
||||
*/
|
||||
if (first)
|
||||
fifo_space--;
|
||||
|
||||
while (priv->len) {
|
||||
if (fifo_space-- <= 0)
|
||||
break;
|
||||
|
||||
dev_dbg(&priv->adap.dev, "write data: %02x\n", *priv->buf);
|
||||
writel(*priv->buf++, priv->membase + UNIPHIER_FI2C_DTTX);
|
||||
priv->len--;
|
||||
}
|
||||
}
|
||||
|
||||
static void uniphier_fi2c_drain_rxfifo(struct uniphier_fi2c_priv *priv)
|
||||
{
|
||||
int fifo_left = priv->flags & UNIPHIER_FI2C_BYTE_WISE ?
|
||||
1 : UNIPHIER_FI2C_FIFO_SIZE;
|
||||
|
||||
while (priv->len) {
|
||||
if (fifo_left-- <= 0)
|
||||
break;
|
||||
|
||||
*priv->buf++ = readl(priv->membase + UNIPHIER_FI2C_DTRX);
|
||||
dev_dbg(&priv->adap.dev, "read data: %02x\n", priv->buf[-1]);
|
||||
priv->len--;
|
||||
}
|
||||
}
|
||||
|
||||
static void uniphier_fi2c_set_irqs(struct uniphier_fi2c_priv *priv)
|
||||
{
|
||||
writel(priv->enabled_irqs, priv->membase + UNIPHIER_FI2C_IE);
|
||||
}
|
||||
|
||||
static void uniphier_fi2c_clear_irqs(struct uniphier_fi2c_priv *priv)
|
||||
{
|
||||
writel(-1, priv->membase + UNIPHIER_FI2C_IC);
|
||||
}
|
||||
|
||||
static void uniphier_fi2c_stop(struct uniphier_fi2c_priv *priv)
|
||||
{
|
||||
dev_dbg(&priv->adap.dev, "stop condition\n");
|
||||
|
||||
priv->enabled_irqs |= UNIPHIER_FI2C_INT_STOP;
|
||||
uniphier_fi2c_set_irqs(priv);
|
||||
writel(UNIPHIER_FI2C_CR_MST | UNIPHIER_FI2C_CR_STO,
|
||||
priv->membase + UNIPHIER_FI2C_CR);
|
||||
}
|
||||
|
||||
static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct uniphier_fi2c_priv *priv = dev_id;
|
||||
u32 irq_status;
|
||||
|
||||
irq_status = readl(priv->membase + UNIPHIER_FI2C_INT);
|
||||
|
||||
dev_dbg(&priv->adap.dev,
|
||||
"interrupt: enabled_irqs=%04x, irq_status=%04x\n",
|
||||
priv->enabled_irqs, irq_status);
|
||||
|
||||
if (irq_status & UNIPHIER_FI2C_INT_STOP)
|
||||
goto complete;
|
||||
|
||||
if (unlikely(irq_status & UNIPHIER_FI2C_INT_AL)) {
|
||||
dev_dbg(&priv->adap.dev, "arbitration lost\n");
|
||||
priv->error = -EAGAIN;
|
||||
goto complete;
|
||||
}
|
||||
|
||||
if (unlikely(irq_status & UNIPHIER_FI2C_INT_NA)) {
|
||||
dev_dbg(&priv->adap.dev, "could not get ACK\n");
|
||||
priv->error = -ENXIO;
|
||||
if (priv->flags & UNIPHIER_FI2C_RD) {
|
||||
/*
|
||||
* work around a hardware bug:
|
||||
* The receive-completed interrupt is never set even if
|
||||
* STOP condition is detected after the address phase
|
||||
* of read transaction fails to get ACK.
|
||||
* To avoid time-out error, we issue STOP here,
|
||||
* but do not wait for its completion.
|
||||
* It should be checked after exiting this handler.
|
||||
*/
|
||||
uniphier_fi2c_stop(priv);
|
||||
priv->flags |= UNIPHIER_FI2C_DEFER_STOP_COMP;
|
||||
goto complete;
|
||||
}
|
||||
goto stop;
|
||||
}
|
||||
|
||||
if (irq_status & UNIPHIER_FI2C_INT_TE) {
|
||||
if (!priv->len)
|
||||
goto data_done;
|
||||
|
||||
uniphier_fi2c_fill_txfifo(priv, false);
|
||||
goto handled;
|
||||
}
|
||||
|
||||
if (irq_status & (UNIPHIER_FI2C_INT_RF | UNIPHIER_FI2C_INT_RB)) {
|
||||
uniphier_fi2c_drain_rxfifo(priv);
|
||||
if (!priv->len)
|
||||
goto data_done;
|
||||
|
||||
if (unlikely(priv->flags & UNIPHIER_FI2C_MANUAL_NACK)) {
|
||||
if (priv->len <= UNIPHIER_FI2C_FIFO_SIZE &&
|
||||
!(priv->flags & UNIPHIER_FI2C_BYTE_WISE)) {
|
||||
dev_dbg(&priv->adap.dev,
|
||||
"enable read byte count IRQ\n");
|
||||
priv->enabled_irqs |= UNIPHIER_FI2C_INT_RB;
|
||||
uniphier_fi2c_set_irqs(priv);
|
||||
priv->flags |= UNIPHIER_FI2C_BYTE_WISE;
|
||||
}
|
||||
if (priv->len <= 1) {
|
||||
dev_dbg(&priv->adap.dev, "set NACK\n");
|
||||
writel(UNIPHIER_FI2C_CR_MST |
|
||||
UNIPHIER_FI2C_CR_NACK,
|
||||
priv->membase + UNIPHIER_FI2C_CR);
|
||||
}
|
||||
}
|
||||
|
||||
goto handled;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
|
||||
data_done:
|
||||
if (priv->flags & UNIPHIER_FI2C_STOP) {
|
||||
stop:
|
||||
uniphier_fi2c_stop(priv);
|
||||
} else {
|
||||
complete:
|
||||
priv->enabled_irqs = 0;
|
||||
uniphier_fi2c_set_irqs(priv);
|
||||
complete(&priv->comp);
|
||||
}
|
||||
|
||||
handled:
|
||||
uniphier_fi2c_clear_irqs(priv);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void uniphier_fi2c_tx_init(struct uniphier_fi2c_priv *priv, u16 addr)
|
||||
{
|
||||
priv->enabled_irqs |= UNIPHIER_FI2C_INT_TE;
|
||||
/* do not use TX byte counter */
|
||||
writel(0, priv->membase + UNIPHIER_FI2C_TBC);
|
||||
/* set slave address */
|
||||
writel(UNIPHIER_FI2C_DTTX_CMD | addr << 1,
|
||||
priv->membase + UNIPHIER_FI2C_DTTX);
|
||||
/* first chunk of data */
|
||||
uniphier_fi2c_fill_txfifo(priv, true);
|
||||
}
|
||||
|
||||
static void uniphier_fi2c_rx_init(struct uniphier_fi2c_priv *priv, u16 addr)
|
||||
{
|
||||
priv->flags |= UNIPHIER_FI2C_RD;
|
||||
|
||||
if (likely(priv->len < 256)) {
|
||||
/*
|
||||
* If possible, use RX byte counter.
|
||||
* It can automatically handle NACK for the last byte.
|
||||
*/
|
||||
writel(priv->len, priv->membase + UNIPHIER_FI2C_RBC);
|
||||
priv->enabled_irqs |= UNIPHIER_FI2C_INT_RF |
|
||||
UNIPHIER_FI2C_INT_RB;
|
||||
} else {
|
||||
/*
|
||||
* The byte counter can not count over 256. In this case,
|
||||
* do not use it at all. Drain data when FIFO gets full,
|
||||
* but treat the last portion as a special case.
|
||||
*/
|
||||
writel(0, priv->membase + UNIPHIER_FI2C_RBC);
|
||||
priv->flags |= UNIPHIER_FI2C_MANUAL_NACK;
|
||||
priv->enabled_irqs |= UNIPHIER_FI2C_INT_RF;
|
||||
}
|
||||
|
||||
/* set slave address with RD bit */
|
||||
writel(UNIPHIER_FI2C_DTTX_CMD | UNIPHIER_FI2C_DTTX_RD | addr << 1,
|
||||
priv->membase + UNIPHIER_FI2C_DTTX);
|
||||
}
|
||||
|
||||
static void uniphier_fi2c_reset(struct uniphier_fi2c_priv *priv)
|
||||
{
|
||||
writel(UNIPHIER_FI2C_RST_RST, priv->membase + UNIPHIER_FI2C_RST);
|
||||
}
|
||||
|
||||
static void uniphier_fi2c_prepare_operation(struct uniphier_fi2c_priv *priv)
|
||||
{
|
||||
writel(UNIPHIER_FI2C_BRST_FOEN | UNIPHIER_FI2C_BRST_RSCL,
|
||||
priv->membase + UNIPHIER_FI2C_BRST);
|
||||
}
|
||||
|
||||
static void uniphier_fi2c_recover(struct uniphier_fi2c_priv *priv)
|
||||
{
|
||||
uniphier_fi2c_reset(priv);
|
||||
i2c_recover_bus(&priv->adap);
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msg, bool stop)
|
||||
{
|
||||
struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap);
|
||||
bool is_read = msg->flags & I2C_M_RD;
|
||||
unsigned long time_left;
|
||||
|
||||
dev_dbg(&adap->dev, "%s: addr=0x%02x, len=%d, stop=%d\n",
|
||||
is_read ? "receive" : "transmit", msg->addr, msg->len, stop);
|
||||
|
||||
priv->len = msg->len;
|
||||
priv->buf = msg->buf;
|
||||
priv->enabled_irqs = UNIPHIER_FI2C_INT_FAULTS;
|
||||
priv->error = 0;
|
||||
priv->flags = 0;
|
||||
|
||||
if (stop)
|
||||
priv->flags |= UNIPHIER_FI2C_STOP;
|
||||
|
||||
reinit_completion(&priv->comp);
|
||||
uniphier_fi2c_clear_irqs(priv);
|
||||
writel(UNIPHIER_FI2C_RST_TBRST | UNIPHIER_FI2C_RST_RBRST,
|
||||
priv->membase + UNIPHIER_FI2C_RST); /* reset TX/RX FIFO */
|
||||
|
||||
if (is_read)
|
||||
uniphier_fi2c_rx_init(priv, msg->addr);
|
||||
else
|
||||
uniphier_fi2c_tx_init(priv, msg->addr);
|
||||
|
||||
uniphier_fi2c_set_irqs(priv);
|
||||
|
||||
dev_dbg(&adap->dev, "start condition\n");
|
||||
writel(UNIPHIER_FI2C_CR_MST | UNIPHIER_FI2C_CR_STA,
|
||||
priv->membase + UNIPHIER_FI2C_CR);
|
||||
|
||||
time_left = wait_for_completion_timeout(&priv->comp, adap->timeout);
|
||||
if (!time_left) {
|
||||
dev_err(&adap->dev, "transaction timeout.\n");
|
||||
uniphier_fi2c_recover(priv);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
dev_dbg(&adap->dev, "complete\n");
|
||||
|
||||
if (unlikely(priv->flags & UNIPHIER_FI2C_DEFER_STOP_COMP)) {
|
||||
u32 status = readl(priv->membase + UNIPHIER_FI2C_SR);
|
||||
|
||||
if (!(status & UNIPHIER_FI2C_SR_STS) ||
|
||||
status & UNIPHIER_FI2C_SR_BB) {
|
||||
dev_err(&adap->dev,
|
||||
"stop condition was not completed.\n");
|
||||
uniphier_fi2c_recover(priv);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
return priv->error;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_check_bus_busy(struct i2c_adapter *adap)
|
||||
{
|
||||
struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap);
|
||||
|
||||
if (readl(priv->membase + UNIPHIER_FI2C_SR) & UNIPHIER_FI2C_SR_DB) {
|
||||
if (priv->busy_cnt++ > 3) {
|
||||
/*
|
||||
* If bus busy continues too long, it is probably
|
||||
* in a wrong state. Try bus recovery.
|
||||
*/
|
||||
uniphier_fi2c_recover(priv);
|
||||
priv->busy_cnt = 0;
|
||||
}
|
||||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
priv->busy_cnt = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_master_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct i2c_msg *msg, *emsg = msgs + num;
|
||||
int ret;
|
||||
|
||||
ret = uniphier_fi2c_check_bus_busy(adap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (msg = msgs; msg < emsg; msg++) {
|
||||
/* If next message is read, skip the stop condition */
|
||||
bool stop = !(msg + 1 < emsg && msg[1].flags & I2C_M_RD);
|
||||
/* but, force it if I2C_M_STOP is set */
|
||||
if (msg->flags & I2C_M_STOP)
|
||||
stop = true;
|
||||
|
||||
ret = uniphier_fi2c_master_xfer_one(adap, msg, stop);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static u32 uniphier_fi2c_functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm uniphier_fi2c_algo = {
|
||||
.master_xfer = uniphier_fi2c_master_xfer,
|
||||
.functionality = uniphier_fi2c_functionality,
|
||||
};
|
||||
|
||||
static int uniphier_fi2c_get_scl(struct i2c_adapter *adap)
|
||||
{
|
||||
struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap);
|
||||
|
||||
return !!(readl(priv->membase + UNIPHIER_FI2C_BM) &
|
||||
UNIPHIER_FI2C_BM_SCLS);
|
||||
}
|
||||
|
||||
static void uniphier_fi2c_set_scl(struct i2c_adapter *adap, int val)
|
||||
{
|
||||
struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap);
|
||||
|
||||
writel(val ? UNIPHIER_FI2C_BRST_RSCL : 0,
|
||||
priv->membase + UNIPHIER_FI2C_BRST);
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_get_sda(struct i2c_adapter *adap)
|
||||
{
|
||||
struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap);
|
||||
|
||||
return !!(readl(priv->membase + UNIPHIER_FI2C_BM) &
|
||||
UNIPHIER_FI2C_BM_SDAS);
|
||||
}
|
||||
|
||||
static void uniphier_fi2c_unprepare_recovery(struct i2c_adapter *adap)
|
||||
{
|
||||
uniphier_fi2c_prepare_operation(i2c_get_adapdata(adap));
|
||||
}
|
||||
|
||||
static struct i2c_bus_recovery_info uniphier_fi2c_bus_recovery_info = {
|
||||
.recover_bus = i2c_generic_scl_recovery,
|
||||
.get_scl = uniphier_fi2c_get_scl,
|
||||
.set_scl = uniphier_fi2c_set_scl,
|
||||
.get_sda = uniphier_fi2c_get_sda,
|
||||
.unprepare_recovery = uniphier_fi2c_unprepare_recovery,
|
||||
};
|
||||
|
||||
static int uniphier_fi2c_clk_init(struct device *dev,
|
||||
struct uniphier_fi2c_priv *priv)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
unsigned long clk_rate;
|
||||
u32 bus_speed, clk_count;
|
||||
int ret;
|
||||
|
||||
if (of_property_read_u32(np, "clock-frequency", &bus_speed))
|
||||
bus_speed = UNIPHIER_FI2C_DEFAULT_SPEED;
|
||||
|
||||
if (bus_speed > UNIPHIER_FI2C_MAX_SPEED)
|
||||
bus_speed = UNIPHIER_FI2C_MAX_SPEED;
|
||||
|
||||
/* Get input clk rate through clk driver */
|
||||
priv->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
dev_err(dev, "failed to get clock\n");
|
||||
return PTR_ERR(priv->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_rate = clk_get_rate(priv->clk);
|
||||
|
||||
uniphier_fi2c_reset(priv);
|
||||
|
||||
clk_count = clk_rate / bus_speed;
|
||||
|
||||
writel(clk_count, priv->membase + UNIPHIER_FI2C_CYC);
|
||||
writel(clk_count / 2, priv->membase + UNIPHIER_FI2C_LCTL);
|
||||
writel(clk_count / 2, priv->membase + UNIPHIER_FI2C_SSUT);
|
||||
writel(clk_count / 16, priv->membase + UNIPHIER_FI2C_DSUT);
|
||||
|
||||
uniphier_fi2c_prepare_operation(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct uniphier_fi2c_priv *priv;
|
||||
struct resource *regs;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->membase = devm_ioremap_resource(dev, regs);
|
||||
if (IS_ERR(priv->membase))
|
||||
return PTR_ERR(priv->membase);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "failed to get IRQ number");
|
||||
return irq;
|
||||
}
|
||||
|
||||
init_completion(&priv->comp);
|
||||
priv->adap.owner = THIS_MODULE;
|
||||
priv->adap.algo = &uniphier_fi2c_algo;
|
||||
priv->adap.dev.parent = dev;
|
||||
priv->adap.dev.of_node = dev->of_node;
|
||||
strlcpy(priv->adap.name, "UniPhier FI2C", sizeof(priv->adap.name));
|
||||
priv->adap.bus_recovery_info = &uniphier_fi2c_bus_recovery_info;
|
||||
i2c_set_adapdata(&priv->adap, priv);
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = uniphier_fi2c_clk_init(dev, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_irq(dev, irq, uniphier_fi2c_interrupt, 0,
|
||||
pdev->name, priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq %d\n", irq);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = i2c_add_adapter(&priv->adap);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add I2C adapter\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
err:
|
||||
if (ret)
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_fi2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct uniphier_fi2c_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_del_adapter(&priv->adap);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id uniphier_fi2c_match[] = {
|
||||
{ .compatible = "socionext,uniphier-fi2c" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, uniphier_fi2c_match);
|
||||
|
||||
static struct platform_driver uniphier_fi2c_drv = {
|
||||
.probe = uniphier_fi2c_probe,
|
||||
.remove = uniphier_fi2c_remove,
|
||||
.driver = {
|
||||
.name = "uniphier-fi2c",
|
||||
.of_match_table = uniphier_fi2c_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(uniphier_fi2c_drv);
|
||||
|
||||
MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
|
||||
MODULE_DESCRIPTION("UniPhier FIFO-builtin I2C bus driver");
|
||||
MODULE_LICENSE("GPL");
|
441
drivers/i2c/busses/i2c-uniphier.c
Normal file
441
drivers/i2c/busses/i2c-uniphier.c
Normal file
@ -0,0 +1,441 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that 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/clk.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define UNIPHIER_I2C_DTRM 0x00 /* TX register */
|
||||
#define UNIPHIER_I2C_DTRM_IRQEN BIT(11) /* enable interrupt */
|
||||
#define UNIPHIER_I2C_DTRM_STA BIT(10) /* start condition */
|
||||
#define UNIPHIER_I2C_DTRM_STO BIT(9) /* stop condition */
|
||||
#define UNIPHIER_I2C_DTRM_NACK BIT(8) /* do not return ACK */
|
||||
#define UNIPHIER_I2C_DTRM_RD BIT(0) /* read transaction */
|
||||
#define UNIPHIER_I2C_DREC 0x04 /* RX register */
|
||||
#define UNIPHIER_I2C_DREC_MST BIT(14) /* 1 = master, 0 = slave */
|
||||
#define UNIPHIER_I2C_DREC_TX BIT(13) /* 1 = transmit, 0 = receive */
|
||||
#define UNIPHIER_I2C_DREC_STS BIT(12) /* stop condition detected */
|
||||
#define UNIPHIER_I2C_DREC_LRB BIT(11) /* no ACK */
|
||||
#define UNIPHIER_I2C_DREC_LAB BIT(9) /* arbitration lost */
|
||||
#define UNIPHIER_I2C_DREC_BBN BIT(8) /* bus not busy */
|
||||
#define UNIPHIER_I2C_MYAD 0x08 /* slave address */
|
||||
#define UNIPHIER_I2C_CLK 0x0c /* clock frequency control */
|
||||
#define UNIPHIER_I2C_BRST 0x10 /* bus reset */
|
||||
#define UNIPHIER_I2C_BRST_FOEN BIT(1) /* normal operation */
|
||||
#define UNIPHIER_I2C_BRST_RSCL BIT(0) /* release SCL */
|
||||
#define UNIPHIER_I2C_HOLD 0x14 /* hold time control */
|
||||
#define UNIPHIER_I2C_BSTS 0x18 /* bus status monitor */
|
||||
#define UNIPHIER_I2C_BSTS_SDA BIT(1) /* readback of SDA line */
|
||||
#define UNIPHIER_I2C_BSTS_SCL BIT(0) /* readback of SCL line */
|
||||
#define UNIPHIER_I2C_NOISE 0x1c /* noise filter control */
|
||||
#define UNIPHIER_I2C_SETUP 0x20 /* setup time control */
|
||||
|
||||
#define UNIPHIER_I2C_DEFAULT_SPEED 100000
|
||||
#define UNIPHIER_I2C_MAX_SPEED 400000
|
||||
|
||||
struct uniphier_i2c_priv {
|
||||
struct completion comp;
|
||||
struct i2c_adapter adap;
|
||||
void __iomem *membase;
|
||||
struct clk *clk;
|
||||
unsigned int busy_cnt;
|
||||
};
|
||||
|
||||
static irqreturn_t uniphier_i2c_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct uniphier_i2c_priv *priv = dev_id;
|
||||
|
||||
/*
|
||||
* This hardware uses edge triggered interrupt. Do not touch the
|
||||
* hardware registers in this handler to make sure to catch the next
|
||||
* interrupt edge. Just send a complete signal and return.
|
||||
*/
|
||||
complete(&priv->comp);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_xfer_byte(struct i2c_adapter *adap, u32 txdata,
|
||||
u32 *rxdatap)
|
||||
{
|
||||
struct uniphier_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||
unsigned long time_left;
|
||||
u32 rxdata;
|
||||
|
||||
reinit_completion(&priv->comp);
|
||||
|
||||
txdata |= UNIPHIER_I2C_DTRM_IRQEN;
|
||||
dev_dbg(&adap->dev, "write data: 0x%04x\n", txdata);
|
||||
writel(txdata, priv->membase + UNIPHIER_I2C_DTRM);
|
||||
|
||||
time_left = wait_for_completion_timeout(&priv->comp, adap->timeout);
|
||||
if (unlikely(!time_left)) {
|
||||
dev_err(&adap->dev, "transaction timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
rxdata = readl(priv->membase + UNIPHIER_I2C_DREC);
|
||||
dev_dbg(&adap->dev, "read data: 0x%04x\n", rxdata);
|
||||
|
||||
if (rxdatap)
|
||||
*rxdatap = rxdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_send_byte(struct i2c_adapter *adap, u32 txdata)
|
||||
{
|
||||
u32 rxdata;
|
||||
int ret;
|
||||
|
||||
ret = uniphier_i2c_xfer_byte(adap, txdata, &rxdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (unlikely(rxdata & UNIPHIER_I2C_DREC_LAB)) {
|
||||
dev_dbg(&adap->dev, "arbitration lost\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (unlikely(rxdata & UNIPHIER_I2C_DREC_LRB)) {
|
||||
dev_dbg(&adap->dev, "could not get ACK\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_tx(struct i2c_adapter *adap, u16 addr, u16 len,
|
||||
const u8 *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(&adap->dev, "start condition\n");
|
||||
ret = uniphier_i2c_send_byte(adap, addr << 1 |
|
||||
UNIPHIER_I2C_DTRM_STA |
|
||||
UNIPHIER_I2C_DTRM_NACK);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (len--) {
|
||||
ret = uniphier_i2c_send_byte(adap,
|
||||
UNIPHIER_I2C_DTRM_NACK | *buf++);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_rx(struct i2c_adapter *adap, u16 addr, u16 len,
|
||||
u8 *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(&adap->dev, "start condition\n");
|
||||
ret = uniphier_i2c_send_byte(adap, addr << 1 |
|
||||
UNIPHIER_I2C_DTRM_STA |
|
||||
UNIPHIER_I2C_DTRM_NACK |
|
||||
UNIPHIER_I2C_DTRM_RD);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (len--) {
|
||||
u32 rxdata;
|
||||
|
||||
ret = uniphier_i2c_xfer_byte(adap,
|
||||
len ? 0 : UNIPHIER_I2C_DTRM_NACK,
|
||||
&rxdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
*buf++ = rxdata;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_stop(struct i2c_adapter *adap)
|
||||
{
|
||||
dev_dbg(&adap->dev, "stop condition\n");
|
||||
return uniphier_i2c_send_byte(adap, UNIPHIER_I2C_DTRM_STO |
|
||||
UNIPHIER_I2C_DTRM_NACK);
|
||||
}
|
||||
|
||||
static int uniphier_i2c_master_xfer_one(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msg, bool stop)
|
||||
{
|
||||
bool is_read = msg->flags & I2C_M_RD;
|
||||
bool recovery = false;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&adap->dev, "%s: addr=0x%02x, len=%d, stop=%d\n",
|
||||
is_read ? "receive" : "transmit", msg->addr, msg->len, stop);
|
||||
|
||||
if (is_read)
|
||||
ret = uniphier_i2c_rx(adap, msg->addr, msg->len, msg->buf);
|
||||
else
|
||||
ret = uniphier_i2c_tx(adap, msg->addr, msg->len, msg->buf);
|
||||
|
||||
if (ret == -EAGAIN) /* could not acquire bus. bail out without STOP */
|
||||
return ret;
|
||||
|
||||
if (ret == -ETIMEDOUT) {
|
||||
/* This error is fatal. Needs recovery. */
|
||||
stop = false;
|
||||
recovery = true;
|
||||
}
|
||||
|
||||
if (stop) {
|
||||
int ret2 = uniphier_i2c_stop(adap);
|
||||
|
||||
if (ret2) {
|
||||
/* Failed to issue STOP. The bus needs recovery. */
|
||||
recovery = true;
|
||||
ret = ret ?: ret2;
|
||||
}
|
||||
}
|
||||
|
||||
if (recovery)
|
||||
i2c_recover_bus(adap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_check_bus_busy(struct i2c_adapter *adap)
|
||||
{
|
||||
struct uniphier_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||
|
||||
if (!(readl(priv->membase + UNIPHIER_I2C_DREC) &
|
||||
UNIPHIER_I2C_DREC_BBN)) {
|
||||
if (priv->busy_cnt++ > 3) {
|
||||
/*
|
||||
* If bus busy continues too long, it is probably
|
||||
* in a wrong state. Try bus recovery.
|
||||
*/
|
||||
i2c_recover_bus(adap);
|
||||
priv->busy_cnt = 0;
|
||||
}
|
||||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
priv->busy_cnt = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_master_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct i2c_msg *msg, *emsg = msgs + num;
|
||||
int ret;
|
||||
|
||||
ret = uniphier_i2c_check_bus_busy(adap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (msg = msgs; msg < emsg; msg++) {
|
||||
/* If next message is read, skip the stop condition */
|
||||
bool stop = !(msg + 1 < emsg && msg[1].flags & I2C_M_RD);
|
||||
/* but, force it if I2C_M_STOP is set */
|
||||
if (msg->flags & I2C_M_STOP)
|
||||
stop = true;
|
||||
|
||||
ret = uniphier_i2c_master_xfer_one(adap, msg, stop);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static u32 uniphier_i2c_functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm uniphier_i2c_algo = {
|
||||
.master_xfer = uniphier_i2c_master_xfer,
|
||||
.functionality = uniphier_i2c_functionality,
|
||||
};
|
||||
|
||||
static void uniphier_i2c_reset(struct uniphier_i2c_priv *priv, bool reset_on)
|
||||
{
|
||||
u32 val = UNIPHIER_I2C_BRST_RSCL;
|
||||
|
||||
val |= reset_on ? 0 : UNIPHIER_I2C_BRST_FOEN;
|
||||
writel(val, priv->membase + UNIPHIER_I2C_BRST);
|
||||
}
|
||||
|
||||
static int uniphier_i2c_get_scl(struct i2c_adapter *adap)
|
||||
{
|
||||
struct uniphier_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||
|
||||
return !!(readl(priv->membase + UNIPHIER_I2C_BSTS) &
|
||||
UNIPHIER_I2C_BSTS_SCL);
|
||||
}
|
||||
|
||||
static void uniphier_i2c_set_scl(struct i2c_adapter *adap, int val)
|
||||
{
|
||||
struct uniphier_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||
|
||||
writel(val ? UNIPHIER_I2C_BRST_RSCL : 0,
|
||||
priv->membase + UNIPHIER_I2C_BRST);
|
||||
}
|
||||
|
||||
static int uniphier_i2c_get_sda(struct i2c_adapter *adap)
|
||||
{
|
||||
struct uniphier_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||
|
||||
return !!(readl(priv->membase + UNIPHIER_I2C_BSTS) &
|
||||
UNIPHIER_I2C_BSTS_SDA);
|
||||
}
|
||||
|
||||
static void uniphier_i2c_unprepare_recovery(struct i2c_adapter *adap)
|
||||
{
|
||||
uniphier_i2c_reset(i2c_get_adapdata(adap), false);
|
||||
}
|
||||
|
||||
static struct i2c_bus_recovery_info uniphier_i2c_bus_recovery_info = {
|
||||
.recover_bus = i2c_generic_scl_recovery,
|
||||
.get_scl = uniphier_i2c_get_scl,
|
||||
.set_scl = uniphier_i2c_set_scl,
|
||||
.get_sda = uniphier_i2c_get_sda,
|
||||
.unprepare_recovery = uniphier_i2c_unprepare_recovery,
|
||||
};
|
||||
|
||||
static int uniphier_i2c_clk_init(struct device *dev,
|
||||
struct uniphier_i2c_priv *priv)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
unsigned long clk_rate;
|
||||
u32 bus_speed;
|
||||
int ret;
|
||||
|
||||
if (of_property_read_u32(np, "clock-frequency", &bus_speed))
|
||||
bus_speed = UNIPHIER_I2C_DEFAULT_SPEED;
|
||||
|
||||
if (bus_speed > UNIPHIER_I2C_MAX_SPEED)
|
||||
bus_speed = UNIPHIER_I2C_MAX_SPEED;
|
||||
|
||||
/* Get input clk rate through clk driver */
|
||||
priv->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
dev_err(dev, "failed to get clock\n");
|
||||
return PTR_ERR(priv->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_rate = clk_get_rate(priv->clk);
|
||||
|
||||
uniphier_i2c_reset(priv, true);
|
||||
|
||||
writel((clk_rate / bus_speed / 2 << 16) | (clk_rate / bus_speed),
|
||||
priv->membase + UNIPHIER_I2C_CLK);
|
||||
|
||||
uniphier_i2c_reset(priv, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct uniphier_i2c_priv *priv;
|
||||
struct resource *regs;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->membase = devm_ioremap_resource(dev, regs);
|
||||
if (IS_ERR(priv->membase))
|
||||
return PTR_ERR(priv->membase);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "failed to get IRQ number");
|
||||
return irq;
|
||||
}
|
||||
|
||||
init_completion(&priv->comp);
|
||||
priv->adap.owner = THIS_MODULE;
|
||||
priv->adap.algo = &uniphier_i2c_algo;
|
||||
priv->adap.dev.parent = dev;
|
||||
priv->adap.dev.of_node = dev->of_node;
|
||||
strlcpy(priv->adap.name, "UniPhier I2C", sizeof(priv->adap.name));
|
||||
priv->adap.bus_recovery_info = &uniphier_i2c_bus_recovery_info;
|
||||
i2c_set_adapdata(&priv->adap, priv);
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = uniphier_i2c_clk_init(dev, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_irq(dev, irq, uniphier_i2c_interrupt, 0, pdev->name,
|
||||
priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq %d\n", irq);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = i2c_add_adapter(&priv->adap);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add I2C adapter\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
err:
|
||||
if (ret)
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uniphier_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct uniphier_i2c_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_del_adapter(&priv->adap);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id uniphier_i2c_match[] = {
|
||||
{ .compatible = "socionext,uniphier-i2c" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, uniphier_i2c_match);
|
||||
|
||||
static struct platform_driver uniphier_i2c_drv = {
|
||||
.probe = uniphier_i2c_probe,
|
||||
.remove = uniphier_i2c_remove,
|
||||
.driver = {
|
||||
.name = "uniphier-i2c",
|
||||
.of_match_table = uniphier_i2c_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(uniphier_i2c_drv);
|
||||
|
||||
MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
|
||||
MODULE_DESCRIPTION("UniPhier I2C bus driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -99,27 +99,40 @@ struct gsb_buffer {
|
||||
};
|
||||
} __packed;
|
||||
|
||||
static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data)
|
||||
struct acpi_i2c_lookup {
|
||||
struct i2c_board_info *info;
|
||||
acpi_handle adapter_handle;
|
||||
acpi_handle device_handle;
|
||||
};
|
||||
|
||||
static int acpi_i2c_find_address(struct acpi_resource *ares, void *data)
|
||||
{
|
||||
struct i2c_board_info *info = data;
|
||||
struct acpi_i2c_lookup *lookup = data;
|
||||
struct i2c_board_info *info = lookup->info;
|
||||
struct acpi_resource_i2c_serialbus *sb;
|
||||
acpi_handle adapter_handle;
|
||||
acpi_status status;
|
||||
|
||||
if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
|
||||
struct acpi_resource_i2c_serialbus *sb;
|
||||
if (info->addr || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
|
||||
return 1;
|
||||
|
||||
sb = &ares->data.i2c_serial_bus;
|
||||
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;
|
||||
}
|
||||
} else if (!info->irq) {
|
||||
struct resource r;
|
||||
sb = &ares->data.i2c_serial_bus;
|
||||
if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C)
|
||||
return 1;
|
||||
|
||||
if (acpi_dev_resource_interrupt(ares, 0, &r))
|
||||
info->irq = r.start;
|
||||
/*
|
||||
* Extract the ResourceSource and make sure that the handle matches
|
||||
* with the I2C adapter handle.
|
||||
*/
|
||||
status = acpi_get_handle(lookup->device_handle,
|
||||
sb->resource_source.string_ptr,
|
||||
&adapter_handle);
|
||||
if (ACPI_SUCCESS(status) && adapter_handle == lookup->adapter_handle) {
|
||||
info->addr = sb->slave_address;
|
||||
if (sb->access_mode == ACPI_I2C_10BIT_MODE)
|
||||
info->flags |= I2C_CLIENT_TEN;
|
||||
}
|
||||
|
||||
/* Tell the ACPI core to skip this resource */
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -128,6 +141,8 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
|
||||
{
|
||||
struct i2c_adapter *adapter = data;
|
||||
struct list_head resource_list;
|
||||
struct acpi_i2c_lookup lookup;
|
||||
struct resource_entry *entry;
|
||||
struct i2c_board_info info;
|
||||
struct acpi_device *adev;
|
||||
int ret;
|
||||
@ -140,14 +155,37 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.fwnode = acpi_fwnode_handle(adev);
|
||||
|
||||
memset(&lookup, 0, sizeof(lookup));
|
||||
lookup.adapter_handle = ACPI_HANDLE(&adapter->dev);
|
||||
lookup.device_handle = handle;
|
||||
lookup.info = &info;
|
||||
|
||||
/*
|
||||
* Look up for I2cSerialBus resource with ResourceSource that
|
||||
* matches with this adapter.
|
||||
*/
|
||||
INIT_LIST_HEAD(&resource_list);
|
||||
ret = acpi_dev_get_resources(adev, &resource_list,
|
||||
acpi_i2c_add_resource, &info);
|
||||
acpi_i2c_find_address, &lookup);
|
||||
acpi_dev_free_resource_list(&resource_list);
|
||||
|
||||
if (ret < 0 || !info.addr)
|
||||
return AE_OK;
|
||||
|
||||
/* Then fill IRQ number if any */
|
||||
ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
|
||||
if (ret < 0)
|
||||
return AE_OK;
|
||||
|
||||
resource_list_for_each_entry(entry, &resource_list) {
|
||||
if (resource_type(entry->res) == IORESOURCE_IRQ) {
|
||||
info.irq = entry->res->start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
acpi_dev_free_resource_list(&resource_list);
|
||||
|
||||
adev->power.flags.ignore_parent = true;
|
||||
strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type));
|
||||
if (!i2c_new_device(adapter, &info)) {
|
||||
@ -160,6 +198,8 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
#define ACPI_I2C_MAX_SCAN_DEPTH 32
|
||||
|
||||
/**
|
||||
* acpi_i2c_register_devices - enumerate I2C slave devices behind adapter
|
||||
* @adap: pointer to adapter
|
||||
@ -170,17 +210,13 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
|
||||
*/
|
||||
static void acpi_i2c_register_devices(struct i2c_adapter *adap)
|
||||
{
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
|
||||
if (!adap->dev.parent)
|
||||
if (!has_acpi_companion(&adap->dev))
|
||||
return;
|
||||
|
||||
handle = ACPI_HANDLE(adap->dev.parent);
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_I2C_MAX_SCAN_DEPTH,
|
||||
acpi_i2c_add_device, NULL,
|
||||
adap, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
|
@ -235,7 +235,7 @@ static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr)
|
||||
return result;
|
||||
}
|
||||
|
||||
static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
|
||||
static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct i2c_rdwr_ioctl_data rdwr_arg;
|
||||
@ -250,7 +250,7 @@ static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
|
||||
|
||||
/* Put an arbitrary limit on the number of messages that can
|
||||
* be sent at once */
|
||||
if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
|
||||
if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS)
|
||||
return -EINVAL;
|
||||
|
||||
rdwr_pa = memdup_user(rdwr_arg.msgs,
|
||||
@ -421,16 +421,6 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
switch (cmd) {
|
||||
case I2C_SLAVE:
|
||||
case I2C_SLAVE_FORCE:
|
||||
/* NOTE: devices set up to work with "new style" drivers
|
||||
* can't use I2C_SLAVE, even when the device node is not
|
||||
* bound to a driver. Only I2C_SLAVE_FORCE will work.
|
||||
*
|
||||
* Setting the PEC flag here won't affect kernel drivers,
|
||||
* which will be using the i2c_client node registered with
|
||||
* the driver model core. Likewise, when that client has
|
||||
* the PEC flag already set, the i2c-dev driver won't see
|
||||
* (or use) this setting.
|
||||
*/
|
||||
if ((arg > 0x3ff) ||
|
||||
(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
|
||||
return -EINVAL;
|
||||
@ -446,6 +436,13 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
client->flags &= ~I2C_M_TEN;
|
||||
return 0;
|
||||
case I2C_PEC:
|
||||
/*
|
||||
* Setting the PEC flag here won't affect kernel drivers,
|
||||
* which will be using the i2c_client node registered with
|
||||
* the driver model core. Likewise, when that client has
|
||||
* the PEC flag already set, the i2c-dev driver won't see
|
||||
* (or use) this setting.
|
||||
*/
|
||||
if (arg)
|
||||
client->flags |= I2C_CLIENT_PEC;
|
||||
else
|
||||
@ -456,7 +453,7 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
return put_user(funcs, (unsigned long __user *)arg);
|
||||
|
||||
case I2C_RDWR:
|
||||
return i2cdev_ioctl_rdrw(client, arg);
|
||||
return i2cdev_ioctl_rdwr(client, arg);
|
||||
|
||||
case I2C_SMBUS:
|
||||
return i2cdev_ioctl_smbus(client, arg);
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-mux.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
/* multiplexer per channel data */
|
||||
struct i2c_mux_priv {
|
||||
@ -173,6 +174,13 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Associate the mux channel with an ACPI node.
|
||||
*/
|
||||
if (has_acpi_companion(mux_dev))
|
||||
acpi_preset_companion(&priv->adap.dev, ACPI_COMPANION(mux_dev),
|
||||
chan_id);
|
||||
|
||||
if (force_nr) {
|
||||
priv->adap.nr = force_nr;
|
||||
ret = i2c_add_numbered_adapter(&priv->adap);
|
||||
|
@ -686,7 +686,7 @@ static int do_i2c_rdwr_ioctl(unsigned int fd, unsigned int cmd,
|
||||
|
||||
if (get_user(nmsgs, &udata->nmsgs))
|
||||
return -EFAULT;
|
||||
if (nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
|
||||
if (nmsgs > I2C_RDWR_IOCTL_MAX_MSGS)
|
||||
return -EINVAL;
|
||||
|
||||
if (get_user(datap, &udata->msgs))
|
||||
|
@ -514,6 +514,11 @@ static inline bool has_acpi_companion(struct device *dev)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void acpi_preset_companion(struct device *dev,
|
||||
struct acpi_device *parent, u64 addr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline const char *acpi_dev_name(struct acpi_device *adev)
|
||||
{
|
||||
return NULL;
|
||||
|
@ -15,6 +15,7 @@ struct ocores_i2c_platform_data {
|
||||
u32 reg_shift; /* register offset shift value */
|
||||
u32 reg_io_width; /* register io read/write width */
|
||||
u32 clock_khz; /* input clock in kHz */
|
||||
bool big_endian; /* registers are big endian */
|
||||
u8 num_devices; /* number of devices in the devices list */
|
||||
struct i2c_board_info const *devices; /* devices connected to the bus */
|
||||
};
|
||||
|
@ -1,10 +0,0 @@
|
||||
#ifndef __I2C_R_CAR_H__
|
||||
#define __I2C_R_CAR_H__
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct i2c_rcar_platform_data {
|
||||
u32 bus_speed;
|
||||
};
|
||||
|
||||
#endif /* __I2C_R_CAR_H__ */
|
@ -66,7 +66,9 @@ struct i2c_rdwr_ioctl_data {
|
||||
__u32 nmsgs; /* number of i2c_msgs */
|
||||
};
|
||||
|
||||
#define I2C_RDRW_IOCTL_MAX_MSGS 42
|
||||
#define I2C_RDWR_IOCTL_MAX_MSGS 42
|
||||
/* Originally defined with a typo, keep it for compatibility */
|
||||
#define I2C_RDRW_IOCTL_MAX_MSGS I2C_RDWR_IOCTL_MAX_MSGS
|
||||
|
||||
|
||||
#endif /* _UAPI_LINUX_I2C_DEV_H */
|
||||
|
Loading…
Reference in New Issue
Block a user