Merge branch 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: "Mostly driver updates and refactorization. The removal of the XLR driver and the i801 refactoring stand out a little. In the core, we enabled async suspend/resume for I2C controllers and their clients. No issues were reported during the test phase in -next. We will see how this goes for mainline" * 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (54 commits) i2c: sh_mobile: remove unneeded semicolon i2c: riic: Use platform_get_irq() to get the interrupt i2c: sh_mobile: Use platform_get_irq_optional() to get the interrupt i2c: bcm2835: Use platform_get_irq() to get the interrupt i2c: aspeed: Remove unused includes dt-bindings: i2c: aspeed: Drop stray '#interrupt-cells' i2c: sh_mobile: update to new DMAENGINE API when terminating i2c: rcar: update to new DMAENGINE API when terminating i2c: exynos5: Fix getting the optional clock i2c: designware-pci: Convert to use dev_err_probe() i2c: designware-pci: use __maybe_unused for PM functions i2c: designware-pci: Group MODULE_*() macros i2c: designware-pci: Add a note about struct dw_scl_sda_cfg usage i2c: designware-pci: Fix to change data types of hcnt and lcnt parameters i2c: designware: Do not complete i2c read without RX_FULL interrupt eeprom: at24: Add support for 24c1025 EEPROM dt-bindings: at24: add at24c1025 i2c: tegra: use i2c_timings for bus clock freq dt-bindings: at24: Rework special case compatible handling i2c: i801: Don't clear status flags twice in interrupt mode ...
This commit is contained in:
commit
112450df61
|
@ -86,6 +86,10 @@ properties:
|
|||
pattern: c1024$
|
||||
- items:
|
||||
pattern: cs1024$
|
||||
- items:
|
||||
pattern: c1025$
|
||||
- items:
|
||||
pattern: cs1025$
|
||||
- items:
|
||||
pattern: c2048$
|
||||
- items:
|
||||
|
@ -95,17 +99,20 @@ properties:
|
|||
# These are special cases that don't conform to the above pattern.
|
||||
# Each requires a standard at24 model as fallback.
|
||||
- items:
|
||||
- const: nxp,se97b
|
||||
- enum:
|
||||
- rohm,br24g01
|
||||
- rohm,br24t01
|
||||
- const: atmel,24c01
|
||||
- items:
|
||||
- enum:
|
||||
- nxp,se97b
|
||||
- renesas,r1ex24002
|
||||
- const: atmel,24c02
|
||||
- items:
|
||||
- const: onnn,cat24c04
|
||||
- enum:
|
||||
- onnn,cat24c04
|
||||
- onnn,cat24c05
|
||||
- const: atmel,24c04
|
||||
- items:
|
||||
- const: onnn,cat24c05
|
||||
- const: atmel,24c04
|
||||
- items:
|
||||
- const: renesas,r1ex24002
|
||||
- const: atmel,24c02
|
||||
- items:
|
||||
- const: renesas,r1ex24016
|
||||
- const: atmel,24c16
|
||||
|
@ -115,12 +122,6 @@ properties:
|
|||
- items:
|
||||
- const: renesas,r1ex24128
|
||||
- const: atmel,24c128
|
||||
- items:
|
||||
- const: rohm,br24g01
|
||||
- const: atmel,24c01
|
||||
- items:
|
||||
- const: rohm,br24t01
|
||||
- const: atmel,24c01
|
||||
|
||||
label:
|
||||
description: Descriptive name of the EEPROM.
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
Broadcom BCM2835 I2C controller
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be one of:
|
||||
"brcm,bcm2711-i2c"
|
||||
"brcm,bcm2835-i2c"
|
||||
- reg: Should contain register location and length.
|
||||
- interrupts: Should contain interrupt.
|
||||
- clocks : The clock feeding the I2C controller.
|
||||
|
||||
Recommended properties:
|
||||
- clock-frequency : desired I2C bus clock frequency in Hz.
|
||||
|
||||
Example:
|
||||
|
||||
i2c@7e205000 {
|
||||
compatible = "brcm,bcm2835-i2c";
|
||||
reg = <0x7e205000 0x1000>;
|
||||
interrupts = <2 21>;
|
||||
clocks = <&clk_i2c>;
|
||||
clock-frequency = <100000>;
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i2c/brcm,bcm2835-i2c.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom BCM2835 I2C controller
|
||||
|
||||
maintainers:
|
||||
- Stephen Warren <swarren@wwwdotorg.org>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- brcm,bcm2835-i2c
|
||||
- items:
|
||||
- const: brcm,bcm2711-i2c
|
||||
- const: brcm,bcm2835-i2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-frequency: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c@7e205000 {
|
||||
compatible = "brcm,bcm2835-i2c";
|
||||
reg = <0x7e205000 0x1000>;
|
||||
interrupts = <2 21>;
|
||||
clocks = <&clk_i2c>;
|
||||
clock-frequency = <100000>;
|
||||
};
|
|
@ -1,53 +0,0 @@
|
|||
* Samsung's High Speed I2C controller
|
||||
|
||||
The Samsung's High Speed I2C controller is used to interface with I2C devices
|
||||
at various speeds ranging from 100khz to 3.4Mhz.
|
||||
|
||||
Required properties:
|
||||
- compatible: value should be.
|
||||
-> "samsung,exynos5-hsi2c", (DEPRECATED)
|
||||
for i2c compatible with HSI2C available
|
||||
on Exynos5250 and Exynos5420 SoCs.
|
||||
-> "samsung,exynos5250-hsi2c", for i2c compatible with HSI2C available
|
||||
on Exynos5250 and Exynos5420 SoCs.
|
||||
-> "samsung,exynos5260-hsi2c", for i2c compatible with HSI2C available
|
||||
on Exynos5260 SoCs.
|
||||
-> "samsung,exynos7-hsi2c", for i2c compatible with HSI2C available
|
||||
on Exynos7 SoCs.
|
||||
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: interrupt number to the cpu.
|
||||
- #address-cells: always 1 (for i2c addresses)
|
||||
- #size-cells: always 0
|
||||
|
||||
- Pinctrl:
|
||||
- pinctrl-0: Pin control group to be used for this controller.
|
||||
- pinctrl-names: Should contain only one value - "default".
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency: Desired operating frequency in Hz of the bus.
|
||||
-> If not specified, the bus operates in fast-speed mode at
|
||||
at 100khz.
|
||||
-> If specified, the bus operates in high-speed mode only if the
|
||||
clock-frequency is >= 1Mhz.
|
||||
|
||||
Example:
|
||||
|
||||
hsi2c@12ca0000 {
|
||||
compatible = "samsung,exynos5250-hsi2c";
|
||||
reg = <0x12ca0000 0x100>;
|
||||
interrupts = <56>;
|
||||
clock-frequency = <100000>;
|
||||
|
||||
pinctrl-0 = <&i2c4_bus>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
s2mps11_pmic@66 {
|
||||
compatible = "samsung,s2mps11-pmic";
|
||||
reg = <0x66>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,133 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i2c/i2c-exynos5.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Samsung's High Speed I2C controller
|
||||
|
||||
maintainers:
|
||||
- Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
|
||||
|
||||
description: |
|
||||
The Samsung's High Speed I2C controller is used to interface with I2C devices
|
||||
at various speeds ranging from 100kHz to 3.4MHz.
|
||||
|
||||
In case the HSI2C controller is encapsulated within USI block (it's the case
|
||||
e.g. for Exynos850 and Exynos Auto V9 SoCs), it might be also necessary to
|
||||
define USI node in device tree file, choosing "i2c" configuration. Please see
|
||||
Documentation/devicetree/bindings/soc/samsung/exynos-usi.yaml for details.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- samsung,exynos5250-hsi2c # Exynos5250 and Exynos5420
|
||||
- samsung,exynos5260-hsi2c # Exynos5260
|
||||
- samsung,exynos7-hsi2c # Exynos7
|
||||
- samsung,exynosautov9-hsi2c # ExynosAutoV9 and Exynos850
|
||||
- const: samsung,exynos5-hsi2c # Exynos5250 and Exynos5420
|
||||
deprecated: true
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clock-frequency:
|
||||
default: 100000
|
||||
description:
|
||||
Desired operating frequency in Hz of the bus.
|
||||
|
||||
If not specified, the bus operates in fast-speed mode at 100kHz.
|
||||
|
||||
If specified, the bus operates in high-speed mode only if the
|
||||
clock-frequency is >= 1MHz.
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: I2C operating clock
|
||||
- description: Bus clock (APB)
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: hsi2c
|
||||
- const: hsi2c_pclk
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- samsung,exynosautov9-hsi2c
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
|
||||
clock-names:
|
||||
minItems: 2
|
||||
|
||||
required:
|
||||
- clock-names
|
||||
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/exynos5420.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
hsi2c_8: i2c@12e00000 {
|
||||
compatible = "samsung,exynos5250-hsi2c";
|
||||
reg = <0x12e00000 0x1000>;
|
||||
interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clock-frequency = <100000>;
|
||||
clocks = <&clock CLK_USI4>;
|
||||
clock-names = "hsi2c";
|
||||
|
||||
pmic@66 {
|
||||
/* compatible = "samsung,s2mps11-pmic"; */
|
||||
reg = <0x66>;
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/clock/exynos850.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
hsi2c_2: i2c@138c0000 {
|
||||
compatible = "samsung,exynosautov9-hsi2c";
|
||||
reg = <0x138c0000 0xc0>;
|
||||
interrupts = <GIC_SPI 195 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&cmu_peri CLK_GOUT_HSI2C2_IPCLK>,
|
||||
<&cmu_peri CLK_GOUT_HSI2C2_PCLK>;
|
||||
clock-names = "hsi2c", "hsi2c_pclk";
|
||||
|
||||
pmic@66 {
|
||||
/* compatible = "samsung,s2mps11-pmic"; */
|
||||
reg = <0x66>;
|
||||
};
|
||||
};
|
|
@ -20,7 +20,9 @@ properties:
|
|||
- items:
|
||||
- enum:
|
||||
- fsl,imx8qxp-lpi2c
|
||||
- fsl,imx8dxl-lpi2c
|
||||
- fsl,imx8qm-lpi2c
|
||||
- fsl,imx8ulp-lpi2c
|
||||
- const: fsl,imx7ulp-lpi2c
|
||||
|
||||
reg:
|
||||
|
|
|
@ -617,7 +617,7 @@ config I2C_EXYNOS5
|
|||
help
|
||||
High-speed I2C controller on Samsung Exynos5 and newer Samsung SoCs:
|
||||
Exynos5250, Exynos5260, Exynos5410, Exynos542x, Exynos5800,
|
||||
Exynos5433 and Exynos7.
|
||||
Exynos5433, Exynos7, Exynos850 and ExynosAutoV9.
|
||||
Choose Y here only if you build for such Samsung SoC.
|
||||
|
||||
config I2C_GPIO
|
||||
|
@ -1153,22 +1153,12 @@ config I2C_XILINX
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called xilinx_i2c.
|
||||
|
||||
config I2C_XLR
|
||||
tristate "Netlogic XLR I2C support"
|
||||
depends on CPU_XLR || COMPILE_TEST
|
||||
help
|
||||
This driver enables support for the on-chip I2C interface of
|
||||
the Netlogic XLR/XLS MIPS processors and Sigma Designs SOCs.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-xlr.
|
||||
|
||||
config I2C_XLP9XX
|
||||
tristate "XLP9XX I2C support"
|
||||
depends on CPU_XLP || ARCH_THUNDER2 || COMPILE_TEST
|
||||
tristate "Cavium ThunderX2 I2C support"
|
||||
depends on ARCH_THUNDER2 || COMPILE_TEST
|
||||
help
|
||||
This driver enables support for the on-chip I2C interface of
|
||||
the Broadcom XLP9xx/XLP5xx MIPS and Vulcan ARM64 processors.
|
||||
the Cavium ThunderX2 processors. (Originally on Netlogic XLP SoCs.)
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called i2c-xlp9xx.
|
||||
|
|
|
@ -119,7 +119,6 @@ obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
|
|||
i2c-thunderx-objs := i2c-octeon-core.o i2c-thunderx-pcidrv.o
|
||||
obj-$(CONFIG_I2C_THUNDERX) += i2c-thunderx.o
|
||||
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
|
||||
obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
|
||||
obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
|
||||
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
|
||||
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
|
|
|
@ -402,7 +402,7 @@ static const struct i2c_adapter_quirks bcm2835_i2c_quirks = {
|
|||
static int bcm2835_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bcm2835_i2c_dev *i2c_dev;
|
||||
struct resource *mem, *irq;
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
struct i2c_adapter *adap;
|
||||
struct clk *mclk;
|
||||
|
@ -452,12 +452,9 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!irq) {
|
||||
dev_err(&pdev->dev, "No IRQ resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
i2c_dev->irq = irq->start;
|
||||
i2c_dev->irq = platform_get_irq(pdev, 0);
|
||||
if (i2c_dev->irq < 0)
|
||||
return i2c_dev->irq;
|
||||
|
||||
ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED,
|
||||
dev_name(&pdev->dev), i2c_dev);
|
||||
|
|
|
@ -191,23 +191,26 @@ struct reset_control;
|
|||
* @cmd_complete: tx completion indicator
|
||||
* @clk: input reference clock
|
||||
* @pclk: clock required to access the registers
|
||||
* @rst: optional reset for the controller
|
||||
* @slave: represent an I2C slave device
|
||||
* @get_clk_rate_khz: callback to retrieve IP specific bus speed
|
||||
* @cmd_err: run time hadware error code
|
||||
* @msgs: points to an array of messages currently being transferred
|
||||
* @msgs_num: the number of elements in msgs
|
||||
* @msg_write_idx: the element index of the current tx message in the msgs
|
||||
* array
|
||||
* @msg_write_idx: the element index of the current tx message in the msgs array
|
||||
* @tx_buf_len: the length of the current tx buffer
|
||||
* @tx_buf: the current tx buffer
|
||||
* @msg_read_idx: the element index of the current rx message in the msgs
|
||||
* array
|
||||
* @msg_read_idx: the element index of the current rx message in the msgs array
|
||||
* @rx_buf_len: the length of the current rx buffer
|
||||
* @rx_buf: the current rx buffer
|
||||
* @msg_err: error status of the current transfer
|
||||
* @status: i2c master status, one of STATUS_*
|
||||
* @abort_source: copy of the TX_ABRT_SOURCE register
|
||||
* @irq: interrupt number for the i2c master
|
||||
* @flags: platform specific flags like type of IO accessors or model
|
||||
* @adapter: i2c subsystem adapter node
|
||||
* @functionality: I2C_FUNC_* ORed bits to reflect what controller does support
|
||||
* @master_cfg: configuration for the master device
|
||||
* @slave_cfg: configuration for the slave device
|
||||
* @tx_fifo_depth: depth of the hardware tx fifo
|
||||
* @rx_fifo_depth: depth of the hardware rx fifo
|
||||
|
@ -228,7 +231,9 @@ struct reset_control;
|
|||
* @disable: function to disable the controller
|
||||
* @disable_int: function to disable all interrupts
|
||||
* @init: function to initialize the I2C hardware
|
||||
* @set_sda_hold_time: callback to retrieve IP specific SDA hold timing
|
||||
* @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE
|
||||
* @rinfo: I²C GPIO recovery information
|
||||
* @suspended: set to true if the controller is suspended
|
||||
*
|
||||
* HCNT and LCNT parameters can be used if the platform knows more accurate
|
||||
|
|
|
@ -701,7 +701,8 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
|||
regmap_read(dev->map, DW_IC_CLR_RX_DONE, &dummy);
|
||||
if (stat & DW_IC_INTR_ACTIVITY)
|
||||
regmap_read(dev->map, DW_IC_CLR_ACTIVITY, &dummy);
|
||||
if (stat & DW_IC_INTR_STOP_DET)
|
||||
if ((stat & DW_IC_INTR_STOP_DET) &&
|
||||
((dev->rx_outstanding == 0) || (stat & DW_IC_INTR_RX_FULL)))
|
||||
regmap_read(dev->map, DW_IC_CLR_STOP_DET, &dummy);
|
||||
if (stat & DW_IC_INTR_START_DET)
|
||||
regmap_read(dev->map, DW_IC_CLR_START_DET, &dummy);
|
||||
|
@ -723,6 +724,7 @@ static int i2c_dw_irq_handler_master(struct dw_i2c_dev *dev)
|
|||
if (stat & DW_IC_INTR_TX_ABRT) {
|
||||
dev->cmd_err |= DW_IC_ERR_TX_ABRT;
|
||||
dev->status = STATUS_IDLE;
|
||||
dev->rx_outstanding = 0;
|
||||
|
||||
/*
|
||||
* Anytime TX_ABRT is set, the contents of the tx/rx
|
||||
|
@ -745,7 +747,8 @@ static int i2c_dw_irq_handler_master(struct dw_i2c_dev *dev)
|
|||
*/
|
||||
|
||||
tx_aborted:
|
||||
if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err)
|
||||
if (((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err) &&
|
||||
(dev->rx_outstanding == 0))
|
||||
complete(&dev->cmd_complete);
|
||||
else if (unlikely(dev->flags & ACCESS_INTR_MASK)) {
|
||||
/* Workaround to trigger pending interrupt */
|
||||
|
|
|
@ -38,11 +38,18 @@ enum dw_pci_ctl_id_t {
|
|||
navi_amd,
|
||||
};
|
||||
|
||||
/*
|
||||
* This is a legacy structure to describe the hardware counters
|
||||
* to configure signal timings on the bus. For Device Tree platforms
|
||||
* one should use the respective properties and for ACPI there is
|
||||
* a set of ACPI methods that provide these counters. No new
|
||||
* platform should use this structure.
|
||||
*/
|
||||
struct dw_scl_sda_cfg {
|
||||
u32 ss_hcnt;
|
||||
u32 fs_hcnt;
|
||||
u32 ss_lcnt;
|
||||
u32 fs_lcnt;
|
||||
u16 ss_hcnt;
|
||||
u16 fs_hcnt;
|
||||
u16 ss_lcnt;
|
||||
u16 fs_lcnt;
|
||||
u32 sda_hold;
|
||||
};
|
||||
|
||||
|
@ -206,8 +213,7 @@ static struct dw_pci_controller dw_pci_controllers[] = {
|
|||
},
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int i2c_dw_pci_suspend(struct device *dev)
|
||||
static int __maybe_unused i2c_dw_pci_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
|
@ -217,7 +223,7 @@ static int i2c_dw_pci_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_dw_pci_resume(struct device *dev)
|
||||
static int __maybe_unused i2c_dw_pci_resume(struct device *dev)
|
||||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
@ -227,7 +233,6 @@ static int i2c_dw_pci_resume(struct device *dev)
|
|||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static UNIVERSAL_DEV_PM_OPS(i2c_dw_pm_ops, i2c_dw_pci_suspend,
|
||||
i2c_dw_pci_resume, NULL);
|
||||
|
@ -241,28 +246,24 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
|||
struct dw_pci_controller *controller;
|
||||
struct dw_scl_sda_cfg *cfg;
|
||||
|
||||
if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) {
|
||||
dev_err(&pdev->dev, "%s: invalid driver data %ld\n", __func__,
|
||||
id->driver_data);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers))
|
||||
return dev_err_probe(&pdev->dev, -EINVAL,
|
||||
"Invalid driver data %ld\n",
|
||||
id->driver_data);
|
||||
|
||||
controller = &dw_pci_controllers[id->driver_data];
|
||||
|
||||
r = pcim_enable_device(pdev);
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "Failed to enable I2C PCI device (%d)\n",
|
||||
r);
|
||||
return r;
|
||||
}
|
||||
if (r)
|
||||
return dev_err_probe(&pdev->dev, r,
|
||||
"Failed to enable I2C PCI device\n");
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
r = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
|
||||
if (r) {
|
||||
dev_err(&pdev->dev, "I/O memory remapping failed\n");
|
||||
return r;
|
||||
}
|
||||
if (r)
|
||||
return dev_err_probe(&pdev->dev, r,
|
||||
"I/O memory remapping failed\n");
|
||||
|
||||
dev = devm_kzalloc(&pdev->dev, sizeof(struct dw_i2c_dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
|
@ -352,9 +353,6 @@ static void i2c_dw_pci_remove(struct pci_dev *pdev)
|
|||
pci_free_irq_vectors(pdev);
|
||||
}
|
||||
|
||||
/* work with hotplug and coldplug */
|
||||
MODULE_ALIAS("i2c_designware-pci");
|
||||
|
||||
static const struct pci_device_id i2_designware_pci_ids[] = {
|
||||
/* Medfield */
|
||||
{ PCI_VDEVICE(INTEL, 0x0817), medfield },
|
||||
|
@ -411,9 +409,10 @@ static struct pci_driver dw_i2c_driver = {
|
|||
.pm = &i2c_dw_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_pci_driver(dw_i2c_driver);
|
||||
|
||||
/* Work with hotplug and coldplug */
|
||||
MODULE_ALIAS("i2c_designware-pci");
|
||||
MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
|
||||
MODULE_DESCRIPTION("Synopsys DesignWare PCI I2C bus adapter");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -293,6 +293,8 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
|||
DPM_FLAG_MAY_SKIP_RESUME);
|
||||
}
|
||||
|
||||
device_enable_async_suspend(&pdev->dev);
|
||||
|
||||
/* The code below assumes runtime PM to be disabled. */
|
||||
WARN_ON(pm_runtime_enabled(&pdev->dev));
|
||||
|
||||
|
|
|
@ -169,6 +169,7 @@
|
|||
enum i2c_type_exynos {
|
||||
I2C_TYPE_EXYNOS5,
|
||||
I2C_TYPE_EXYNOS7,
|
||||
I2C_TYPE_EXYNOSAUTOV9,
|
||||
};
|
||||
|
||||
struct exynos5_i2c {
|
||||
|
@ -181,7 +182,8 @@ struct exynos5_i2c {
|
|||
unsigned int irq;
|
||||
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
struct clk *clk; /* operating clock */
|
||||
struct clk *pclk; /* bus clock */
|
||||
struct device *dev;
|
||||
int state;
|
||||
|
||||
|
@ -230,6 +232,11 @@ static const struct exynos_hsi2c_variant exynos7_hsi2c_data = {
|
|||
.hw = I2C_TYPE_EXYNOS7,
|
||||
};
|
||||
|
||||
static const struct exynos_hsi2c_variant exynosautov9_hsi2c_data = {
|
||||
.fifo_depth = 64,
|
||||
.hw = I2C_TYPE_EXYNOSAUTOV9,
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos5_i2c_match[] = {
|
||||
{
|
||||
.compatible = "samsung,exynos5-hsi2c",
|
||||
|
@ -243,6 +250,9 @@ static const struct of_device_id exynos5_i2c_match[] = {
|
|||
}, {
|
||||
.compatible = "samsung,exynos7-hsi2c",
|
||||
.data = &exynos7_hsi2c_data
|
||||
}, {
|
||||
.compatible = "samsung,exynosautov9-hsi2c",
|
||||
.data = &exynosautov9_hsi2c_data
|
||||
}, {},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
|
||||
|
@ -281,6 +291,31 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, bool hs_timings)
|
|||
i2c->op_clock;
|
||||
int div, clk_cycle, temp;
|
||||
|
||||
/*
|
||||
* In case of HSI2C controllers in ExynosAutoV9:
|
||||
*
|
||||
* FSCL = IPCLK / ((CLK_DIV + 1) * 16)
|
||||
* T_SCL_LOW = IPCLK * (CLK_DIV + 1) * (N + M)
|
||||
* [N : number of 0's in the TSCL_H_HS]
|
||||
* [M : number of 0's in the TSCL_L_HS]
|
||||
* T_SCL_HIGH = IPCLK * (CLK_DIV + 1) * (N + M)
|
||||
* [N : number of 1's in the TSCL_H_HS]
|
||||
* [M : number of 1's in the TSCL_L_HS]
|
||||
*
|
||||
* Result of (N + M) is always 8.
|
||||
* In general case, we don't need to control timing_s1 and timing_s2.
|
||||
*/
|
||||
if (i2c->variant->hw == I2C_TYPE_EXYNOSAUTOV9) {
|
||||
div = ((clkin / (16 * i2c->op_clock)) - 1);
|
||||
i2c_timing_s3 = div << 16;
|
||||
if (hs_timings)
|
||||
writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_HS3);
|
||||
else
|
||||
writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_FS3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* In case of HSI2C controller in Exynos5 series
|
||||
* FPCLK / FI2C =
|
||||
|
@ -422,7 +457,10 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
|
|||
writel(int_status, i2c->regs + HSI2C_INT_STATUS);
|
||||
|
||||
/* handle interrupt related to the transfer status */
|
||||
if (i2c->variant->hw == I2C_TYPE_EXYNOS7) {
|
||||
switch (i2c->variant->hw) {
|
||||
case I2C_TYPE_EXYNOSAUTOV9:
|
||||
fallthrough;
|
||||
case I2C_TYPE_EXYNOS7:
|
||||
if (int_status & HSI2C_INT_TRANS_DONE) {
|
||||
i2c->trans_done = 1;
|
||||
i2c->state = 0;
|
||||
|
@ -443,7 +481,12 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
|
|||
i2c->state = -ETIMEDOUT;
|
||||
goto stop;
|
||||
}
|
||||
} else if (int_status & HSI2C_INT_I2C) {
|
||||
|
||||
break;
|
||||
case I2C_TYPE_EXYNOS5:
|
||||
if (!(int_status & HSI2C_INT_I2C))
|
||||
break;
|
||||
|
||||
trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
|
||||
if (trans_status & HSI2C_NO_DEV_ACK) {
|
||||
dev_dbg(i2c->dev, "No ACK from device\n");
|
||||
|
@ -465,6 +508,8 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
|
|||
i2c->trans_done = 1;
|
||||
i2c->state = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ((i2c->msg->flags & I2C_M_RD) && (int_status &
|
||||
|
@ -569,13 +614,13 @@ static void exynos5_i2c_bus_check(struct exynos5_i2c *i2c)
|
|||
{
|
||||
unsigned long timeout;
|
||||
|
||||
if (i2c->variant->hw != I2C_TYPE_EXYNOS7)
|
||||
if (i2c->variant->hw == I2C_TYPE_EXYNOS5)
|
||||
return;
|
||||
|
||||
/*
|
||||
* HSI2C_MASTER_ST_LOSE state in EXYNOS7 variant before transaction
|
||||
* indicates that bus is stuck (SDA is low). In such case bus recovery
|
||||
* can be performed.
|
||||
* HSI2C_MASTER_ST_LOSE state (in Exynos7 and ExynosAutoV9 variants)
|
||||
* before transaction indicates that bus is stuck (SDA is low).
|
||||
* In such case bus recovery can be performed.
|
||||
*/
|
||||
timeout = jiffies + msecs_to_jiffies(100);
|
||||
for (;;) {
|
||||
|
@ -611,10 +656,10 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
|
|||
unsigned long flags;
|
||||
unsigned short trig_lvl;
|
||||
|
||||
if (i2c->variant->hw == I2C_TYPE_EXYNOS7)
|
||||
int_en |= HSI2C_INT_I2C_TRANS;
|
||||
else
|
||||
if (i2c->variant->hw == I2C_TYPE_EXYNOS5)
|
||||
int_en |= HSI2C_INT_I2C;
|
||||
else
|
||||
int_en |= HSI2C_INT_I2C_TRANS;
|
||||
|
||||
i2c_ctl = readl(i2c->regs + HSI2C_CTL);
|
||||
i2c_ctl &= ~(HSI2C_TXCHON | HSI2C_RXCHON);
|
||||
|
@ -713,10 +758,14 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap,
|
|||
struct exynos5_i2c *i2c = adap->algo_data;
|
||||
int i, ret;
|
||||
|
||||
ret = clk_enable(i2c->clk);
|
||||
ret = clk_enable(i2c->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(i2c->clk);
|
||||
if (ret)
|
||||
goto err_pclk;
|
||||
|
||||
for (i = 0; i < num; ++i) {
|
||||
ret = exynos5_i2c_xfer_msg(i2c, msgs + i, i + 1 == num);
|
||||
if (ret)
|
||||
|
@ -724,6 +773,8 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap,
|
|||
}
|
||||
|
||||
clk_disable(i2c->clk);
|
||||
err_pclk:
|
||||
clk_disable(i2c->pclk);
|
||||
|
||||
return ret ?: num;
|
||||
}
|
||||
|
@ -763,10 +814,20 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(i2c->clk);
|
||||
i2c->pclk = devm_clk_get_optional(&pdev->dev, "hsi2c_pclk");
|
||||
if (IS_ERR(i2c->pclk)) {
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(i2c->pclk),
|
||||
"cannot get pclk");
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(i2c->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(i2c->clk);
|
||||
if (ret)
|
||||
goto err_pclk;
|
||||
|
||||
i2c->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(i2c->regs)) {
|
||||
ret = PTR_ERR(i2c->regs);
|
||||
|
@ -809,11 +870,15 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, i2c);
|
||||
|
||||
clk_disable(i2c->clk);
|
||||
clk_disable(i2c->pclk);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
|
||||
err_pclk:
|
||||
clk_disable_unprepare(i2c->pclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -824,6 +889,7 @@ static int exynos5_i2c_remove(struct platform_device *pdev)
|
|||
i2c_del_adapter(&i2c->adap);
|
||||
|
||||
clk_unprepare(i2c->clk);
|
||||
clk_unprepare(i2c->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -835,6 +901,7 @@ static int exynos5_i2c_suspend_noirq(struct device *dev)
|
|||
|
||||
i2c_mark_adapter_suspended(&i2c->adap);
|
||||
clk_unprepare(i2c->clk);
|
||||
clk_unprepare(i2c->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -844,21 +911,30 @@ static int exynos5_i2c_resume_noirq(struct device *dev)
|
|||
struct exynos5_i2c *i2c = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
ret = clk_prepare_enable(i2c->clk);
|
||||
ret = clk_prepare_enable(i2c->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(i2c->clk);
|
||||
if (ret)
|
||||
goto err_pclk;
|
||||
|
||||
ret = exynos5_hsi2c_clock_setup(i2c);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
exynos5_i2c_init(i2c);
|
||||
clk_disable(i2c->clk);
|
||||
clk_disable(i2c->pclk);
|
||||
i2c_mark_adapter_resumed(&i2c->adap);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
err_pclk:
|
||||
clk_disable_unprepare(i2c->pclk);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -328,22 +328,14 @@ static int i801_check_pre(struct i801_priv *priv)
|
|||
|
||||
status = inb_p(SMBHSTSTS(priv));
|
||||
if (status & SMBHSTSTS_HOST_BUSY) {
|
||||
dev_err(&priv->pci_dev->dev, "SMBus is busy, can't use it!\n");
|
||||
pci_err(priv->pci_dev, "SMBus is busy, can't use it!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
status &= STATUS_FLAGS;
|
||||
if (status) {
|
||||
dev_dbg(&priv->pci_dev->dev, "Clearing status flags (%02x)\n",
|
||||
status);
|
||||
pci_dbg(priv->pci_dev, "Clearing status flags (%02x)\n", status);
|
||||
outb_p(status, SMBHSTSTS(priv));
|
||||
status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS;
|
||||
if (status) {
|
||||
dev_err(&priv->pci_dev->dev,
|
||||
"Failed clearing status flags (%02x)\n",
|
||||
status);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -356,27 +348,14 @@ static int i801_check_pre(struct i801_priv *priv)
|
|||
if (priv->features & FEATURE_SMBUS_PEC) {
|
||||
status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE;
|
||||
if (status) {
|
||||
dev_dbg(&priv->pci_dev->dev,
|
||||
"Clearing aux status flags (%02x)\n", status);
|
||||
pci_dbg(priv->pci_dev, "Clearing aux status flags (%02x)\n", status);
|
||||
outb_p(status, SMBAUXSTS(priv));
|
||||
status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE;
|
||||
if (status) {
|
||||
dev_err(&priv->pci_dev->dev,
|
||||
"Failed clearing aux status flags (%02x)\n",
|
||||
status);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the status register to an error code, and clear it.
|
||||
* Note that status only contains the bits we want to clear, not the
|
||||
* actual register value.
|
||||
*/
|
||||
static int i801_check_post(struct i801_priv *priv, int status)
|
||||
{
|
||||
int result = 0;
|
||||
|
@ -401,7 +380,6 @@ static int i801_check_post(struct i801_priv *priv, int status)
|
|||
!(status & SMBHSTSTS_FAILED))
|
||||
dev_err(&priv->pci_dev->dev,
|
||||
"Failed terminating the transaction\n");
|
||||
outb_p(STATUS_FLAGS, SMBHSTSTS(priv));
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
|
@ -440,9 +418,6 @@ static int i801_check_post(struct i801_priv *priv, int status)
|
|||
dev_dbg(&priv->pci_dev->dev, "Lost arbitration\n");
|
||||
}
|
||||
|
||||
/* Clear status flags except BYTE_DONE, to be cleared by caller */
|
||||
outb_p(status, SMBHSTSTS(priv));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -523,9 +498,11 @@ static int i801_block_transaction_by_block(struct i801_priv *priv,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Set block buffer mode */
|
||||
outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_E32B, SMBAUXCTL(priv));
|
||||
|
||||
inb_p(SMBHSTCNT(priv)); /* reset the data buffer index */
|
||||
|
||||
/* Use 32-byte buffer to process this transaction */
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
len = data->block[0];
|
||||
outb_p(len, SMBHSTDAT0(priv));
|
||||
|
@ -760,14 +737,6 @@ exit:
|
|||
return i801_check_post(priv, status);
|
||||
}
|
||||
|
||||
static int i801_set_block_buffer_mode(struct i801_priv *priv)
|
||||
{
|
||||
outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_E32B, SMBAUXCTL(priv));
|
||||
if ((inb_p(SMBAUXCTL(priv)) & SMBAUXCTL_E32B) == 0)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Block transaction function */
|
||||
static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *data,
|
||||
char read_write, int command)
|
||||
|
@ -775,6 +744,11 @@ static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *
|
|||
int result = 0;
|
||||
unsigned char hostc;
|
||||
|
||||
if (read_write == I2C_SMBUS_READ && command == I2C_SMBUS_BLOCK_DATA)
|
||||
data->block[0] = I2C_SMBUS_BLOCK_MAX;
|
||||
else if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
|
||||
return -EPROTO;
|
||||
|
||||
if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
/* set I2C_EN bit in configuration register */
|
||||
|
@ -788,22 +762,11 @@ static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *
|
|||
}
|
||||
}
|
||||
|
||||
if (read_write == I2C_SMBUS_WRITE
|
||||
|| command == I2C_SMBUS_I2C_BLOCK_DATA) {
|
||||
if (data->block[0] < 1)
|
||||
data->block[0] = 1;
|
||||
if (data->block[0] > I2C_SMBUS_BLOCK_MAX)
|
||||
data->block[0] = I2C_SMBUS_BLOCK_MAX;
|
||||
} else {
|
||||
data->block[0] = 32; /* max for SMBus block reads */
|
||||
}
|
||||
|
||||
/* Experience has shown that the block buffer can only be used for
|
||||
SMBus (not I2C) block transactions, even though the datasheet
|
||||
doesn't mention this limitation. */
|
||||
if ((priv->features & FEATURE_BLOCK_BUFFER)
|
||||
&& command != I2C_SMBUS_I2C_BLOCK_DATA
|
||||
&& i801_set_block_buffer_mode(priv) == 0)
|
||||
if ((priv->features & FEATURE_BLOCK_BUFFER) &&
|
||||
command != I2C_SMBUS_I2C_BLOCK_DATA)
|
||||
result = i801_block_transaction_by_block(priv, data,
|
||||
read_write,
|
||||
command);
|
||||
|
@ -951,8 +914,11 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
|
|||
}
|
||||
|
||||
out:
|
||||
/* Unlock the SMBus device for use by BIOS/ACPI */
|
||||
outb_p(SMBHSTSTS_INUSE_STS, SMBHSTSTS(priv));
|
||||
/*
|
||||
* Unlock the SMBus device for use by BIOS/ACPI,
|
||||
* and clear status flags if not done already.
|
||||
*/
|
||||
outb_p(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv));
|
||||
|
||||
pm_runtime_mark_last_busy(&priv->pci_dev->dev);
|
||||
pm_runtime_put_autosuspend(&priv->pci_dev->dev);
|
||||
|
@ -1009,66 +975,72 @@ static const struct i2c_algorithm smbus_algorithm = {
|
|||
.functionality = i801_func,
|
||||
};
|
||||
|
||||
#define FEATURES_ICH5 (FEATURE_BLOCK_PROC | FEATURE_I2C_BLOCK_READ | \
|
||||
FEATURE_IRQ | FEATURE_SMBUS_PEC | \
|
||||
FEATURE_BLOCK_BUFFER | FEATURE_HOST_NOTIFY)
|
||||
#define FEATURES_ICH4 (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER | \
|
||||
FEATURE_HOST_NOTIFY)
|
||||
|
||||
static const struct pci_device_id i801_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_3) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_3) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_2) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_3) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_3) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_3) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_4) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_16) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_17) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_17) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_5) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_6) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EP80579_1) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_4) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_5) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GEMINILAKE_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS) },
|
||||
{ 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_CDF_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DNV_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EBG_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BROXTON_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LEWISBURG_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICELAKE_N_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COMETLAKE_H_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COMETLAKE_V_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TIGERLAKE_LP_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS) },
|
||||
{ PCI_DEVICE_DATA(INTEL, 82801AA_3, 0) },
|
||||
{ PCI_DEVICE_DATA(INTEL, 82801AB_3, 0) },
|
||||
{ PCI_DEVICE_DATA(INTEL, 82801BA_2, 0) },
|
||||
{ PCI_DEVICE_DATA(INTEL, 82801CA_3, FEATURE_HOST_NOTIFY) },
|
||||
{ PCI_DEVICE_DATA(INTEL, 82801DB_3, FEATURES_ICH4) },
|
||||
{ PCI_DEVICE_DATA(INTEL, 82801EB_3, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ESB_4, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ICH6_16, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ICH7_17, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ESB2_17, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ICH8_5, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ICH9_6, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, EP80579_1, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ICH10_4, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ICH10_5, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, 5_3400_SERIES_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, COUGARPOINT_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, PATSBURG_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, PATSBURG_SMBUS_IDF0, FEATURES_ICH5 | FEATURE_IDF) },
|
||||
{ PCI_DEVICE_DATA(INTEL, PATSBURG_SMBUS_IDF1, FEATURES_ICH5 | FEATURE_IDF) },
|
||||
{ PCI_DEVICE_DATA(INTEL, PATSBURG_SMBUS_IDF2, FEATURES_ICH5 | FEATURE_IDF) },
|
||||
{ PCI_DEVICE_DATA(INTEL, DH89XXCC_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, PANTHERPOINT_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, LYNXPOINT_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, LYNXPOINT_LP_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, AVOTON_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, WELLSBURG_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, WELLSBURG_SMBUS_MS0, FEATURES_ICH5 | FEATURE_IDF) },
|
||||
{ PCI_DEVICE_DATA(INTEL, WELLSBURG_SMBUS_MS1, FEATURES_ICH5 | FEATURE_IDF) },
|
||||
{ PCI_DEVICE_DATA(INTEL, WELLSBURG_SMBUS_MS2, FEATURES_ICH5 | FEATURE_IDF) },
|
||||
{ PCI_DEVICE_DATA(INTEL, COLETOCREEK_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, GEMINILAKE_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, WILDCATPOINT_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, WILDCATPOINT_LP_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, BAYTRAIL_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, BRASWELL_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, SUNRISEPOINT_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
|
||||
{ PCI_DEVICE_DATA(INTEL, SUNRISEPOINT_LP_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
|
||||
{ PCI_DEVICE_DATA(INTEL, CDF_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, DNV_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
|
||||
{ PCI_DEVICE_DATA(INTEL, EBG_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, BROXTON_SMBUS, FEATURES_ICH5) },
|
||||
{ PCI_DEVICE_DATA(INTEL, LEWISBURG_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
|
||||
{ PCI_DEVICE_DATA(INTEL, LEWISBURG_SSKU_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
|
||||
{ PCI_DEVICE_DATA(INTEL, KABYLAKE_PCH_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
|
||||
{ PCI_DEVICE_DATA(INTEL, CANNONLAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, CANNONLAKE_LP_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ICELAKE_LP_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ICELAKE_N_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, COMETLAKE_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, COMETLAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, COMETLAKE_V_SMBUS, FEATURES_ICH5 | FEATURE_TCO_SPT) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ELKHART_LAKE_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, TIGERLAKE_LP_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, TIGERLAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, JASPER_LAKE_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ALDER_LAKE_S_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ALDER_LAKE_P_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ALDER_LAKE_M_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
|
@ -1493,15 +1465,14 @@ static inline unsigned int i801_get_adapter_class(struct i801_priv *priv)
|
|||
}
|
||||
#endif
|
||||
|
||||
static const struct itco_wdt_platform_data spt_tco_platform_data = {
|
||||
.name = "Intel PCH",
|
||||
.version = 4,
|
||||
};
|
||||
|
||||
static struct platform_device *
|
||||
i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev,
|
||||
struct resource *tco_res)
|
||||
{
|
||||
static const struct itco_wdt_platform_data pldata = {
|
||||
.name = "Intel PCH",
|
||||
.version = 4,
|
||||
};
|
||||
struct resource *res;
|
||||
unsigned int devfn;
|
||||
u64 base64_addr;
|
||||
|
@ -1544,22 +1515,20 @@ i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev,
|
|||
res->flags = IORESOURCE_MEM;
|
||||
|
||||
return platform_device_register_resndata(&pci_dev->dev, "iTCO_wdt", -1,
|
||||
tco_res, 2, &spt_tco_platform_data,
|
||||
sizeof(spt_tco_platform_data));
|
||||
tco_res, 2, &pldata, sizeof(pldata));
|
||||
}
|
||||
|
||||
static const struct itco_wdt_platform_data cnl_tco_platform_data = {
|
||||
.name = "Intel PCH",
|
||||
.version = 6,
|
||||
};
|
||||
|
||||
static struct platform_device *
|
||||
i801_add_tco_cnl(struct i801_priv *priv, struct pci_dev *pci_dev,
|
||||
struct resource *tco_res)
|
||||
{
|
||||
return platform_device_register_resndata(&pci_dev->dev,
|
||||
"iTCO_wdt", -1, tco_res, 1, &cnl_tco_platform_data,
|
||||
sizeof(cnl_tco_platform_data));
|
||||
static const struct itco_wdt_platform_data pldata = {
|
||||
.name = "Intel PCH",
|
||||
.version = 6,
|
||||
};
|
||||
|
||||
return platform_device_register_resndata(&pci_dev->dev, "iTCO_wdt", -1,
|
||||
tco_res, 1, &pldata, sizeof(pldata));
|
||||
}
|
||||
|
||||
static void i801_add_tco(struct i801_priv *priv)
|
||||
|
@ -1697,72 +1666,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
mutex_init(&priv->acpi_lock);
|
||||
|
||||
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_LEWISBURG_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_LEWISBURG_SSKU_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_DNV_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_COMETLAKE_V_SMBUS:
|
||||
priv->features |= FEATURE_BLOCK_PROC;
|
||||
priv->features |= FEATURE_I2C_BLOCK_READ;
|
||||
priv->features |= FEATURE_IRQ;
|
||||
priv->features |= FEATURE_SMBUS_PEC;
|
||||
priv->features |= FEATURE_BLOCK_BUFFER;
|
||||
priv->features |= FEATURE_TCO_SPT;
|
||||
priv->features |= FEATURE_HOST_NOTIFY;
|
||||
break;
|
||||
|
||||
case PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_CDF_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_ICELAKE_N_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_COMETLAKE_H_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_TIGERLAKE_LP_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_EBG_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS:
|
||||
priv->features |= FEATURE_BLOCK_PROC;
|
||||
priv->features |= FEATURE_I2C_BLOCK_READ;
|
||||
priv->features |= FEATURE_IRQ;
|
||||
priv->features |= FEATURE_SMBUS_PEC;
|
||||
priv->features |= FEATURE_BLOCK_BUFFER;
|
||||
priv->features |= FEATURE_TCO_CNL;
|
||||
priv->features |= FEATURE_HOST_NOTIFY;
|
||||
break;
|
||||
|
||||
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0:
|
||||
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1:
|
||||
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2:
|
||||
case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0:
|
||||
case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1:
|
||||
case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2:
|
||||
priv->features |= FEATURE_IDF;
|
||||
fallthrough;
|
||||
default:
|
||||
priv->features |= FEATURE_BLOCK_PROC;
|
||||
priv->features |= FEATURE_I2C_BLOCK_READ;
|
||||
priv->features |= FEATURE_IRQ;
|
||||
fallthrough;
|
||||
case PCI_DEVICE_ID_INTEL_82801DB_3:
|
||||
priv->features |= FEATURE_SMBUS_PEC;
|
||||
priv->features |= FEATURE_BLOCK_BUFFER;
|
||||
fallthrough;
|
||||
case PCI_DEVICE_ID_INTEL_82801CA_3:
|
||||
priv->features |= FEATURE_HOST_NOTIFY;
|
||||
fallthrough;
|
||||
case PCI_DEVICE_ID_INTEL_82801BA_2:
|
||||
case PCI_DEVICE_ID_INTEL_82801AB_3:
|
||||
case PCI_DEVICE_ID_INTEL_82801AA_3:
|
||||
break;
|
||||
}
|
||||
priv->features = id->driver_data;
|
||||
|
||||
/* Disable features on user request */
|
||||
for (i = 0; i < ARRAY_SIZE(i801_feature_names); i++) {
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
@ -51,6 +53,8 @@
|
|||
/* This will be the driver name the kernel reports */
|
||||
#define DRIVER_NAME "imx-i2c"
|
||||
|
||||
#define I2C_IMX_CHECK_DELAY 30000 /* Time to check for bus idle, in NS */
|
||||
|
||||
/*
|
||||
* Enable DMA if transfer byte size is bigger than this threshold.
|
||||
* As the hardware request, it must bigger than 4 bytes.\
|
||||
|
@ -210,6 +214,10 @@ struct imx_i2c_struct {
|
|||
struct imx_i2c_dma *dma;
|
||||
struct i2c_client *slave;
|
||||
enum i2c_slave_event last_slave_event;
|
||||
|
||||
/* For checking slave events. */
|
||||
spinlock_t slave_lock;
|
||||
struct hrtimer slave_timer;
|
||||
};
|
||||
|
||||
static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
|
||||
|
@ -680,7 +688,7 @@ static void i2c_imx_slave_event(struct imx_i2c_struct *i2c_imx,
|
|||
|
||||
static void i2c_imx_slave_finish_op(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
u8 val;
|
||||
u8 val = 0;
|
||||
|
||||
while (i2c_imx->last_slave_event != I2C_SLAVE_STOP) {
|
||||
switch (i2c_imx->last_slave_event) {
|
||||
|
@ -701,10 +709,11 @@ static void i2c_imx_slave_finish_op(struct imx_i2c_struct *i2c_imx)
|
|||
}
|
||||
}
|
||||
|
||||
static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
|
||||
unsigned int status, unsigned int ctl)
|
||||
/* Returns true if the timer should be restarted, false if not. */
|
||||
static irqreturn_t i2c_imx_slave_handle(struct imx_i2c_struct *i2c_imx,
|
||||
unsigned int status, unsigned int ctl)
|
||||
{
|
||||
u8 value;
|
||||
u8 value = 0;
|
||||
|
||||
if (status & I2SR_IAL) { /* Arbitration lost */
|
||||
i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
|
||||
|
@ -712,6 +721,16 @@ static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (!(status & I2SR_IBB)) {
|
||||
/* No master on the bus, that could mean a stop condition. */
|
||||
i2c_imx_slave_finish_op(i2c_imx);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (!(status & I2SR_ICF))
|
||||
/* Data transfer still in progress, ignore this. */
|
||||
goto out;
|
||||
|
||||
if (status & I2SR_IAAS) { /* Addressed as a slave */
|
||||
i2c_imx_slave_finish_op(i2c_imx);
|
||||
if (status & I2SR_SRW) { /* Master wants to read from us*/
|
||||
|
@ -737,16 +756,9 @@ static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
|
|||
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||
}
|
||||
} else if (!(ctl & I2CR_MTX)) { /* Receive mode */
|
||||
if (status & I2SR_IBB) { /* No STOP signal detected */
|
||||
value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||
i2c_imx_slave_event(i2c_imx,
|
||||
I2C_SLAVE_WRITE_RECEIVED, &value);
|
||||
} else { /* STOP signal is detected */
|
||||
dev_dbg(&i2c_imx->adapter.dev,
|
||||
"STOP signal detected");
|
||||
i2c_imx_slave_event(i2c_imx,
|
||||
I2C_SLAVE_STOP, &value);
|
||||
}
|
||||
value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||
i2c_imx_slave_event(i2c_imx,
|
||||
I2C_SLAVE_WRITE_RECEIVED, &value);
|
||||
} else if (!(status & I2SR_RXAK)) { /* Transmit mode received ACK */
|
||||
ctl |= I2CR_MTX;
|
||||
imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
|
||||
|
@ -755,15 +767,43 @@ static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
|
|||
I2C_SLAVE_READ_PROCESSED, &value);
|
||||
|
||||
imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
|
||||
} else { /* Transmit mode received NAK */
|
||||
} else { /* Transmit mode received NAK, operation is done */
|
||||
ctl &= ~I2CR_MTX;
|
||||
imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
|
||||
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||
i2c_imx_slave_finish_op(i2c_imx);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
out:
|
||||
/*
|
||||
* No need to check the return value here. If it returns 0 or
|
||||
* 1, then everything is fine. If it returns -1, then the
|
||||
* timer is running in the handler. This will still work,
|
||||
* though it may be redone (or already have been done) by the
|
||||
* timer function.
|
||||
*/
|
||||
hrtimer_try_to_cancel(&i2c_imx->slave_timer);
|
||||
hrtimer_forward_now(&i2c_imx->slave_timer, I2C_IMX_CHECK_DELAY);
|
||||
hrtimer_restart(&i2c_imx->slave_timer);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart i2c_imx_slave_timeout(struct hrtimer *t)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx = container_of(t, struct imx_i2c_struct,
|
||||
slave_timer);
|
||||
unsigned int ctl, status;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&i2c_imx->slave_lock, flags);
|
||||
status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||
ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
i2c_imx_slave_handle(i2c_imx, status, ctl);
|
||||
spin_unlock_irqrestore(&i2c_imx->slave_lock, flags);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static void i2c_imx_slave_init(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
int temp;
|
||||
|
@ -843,7 +883,9 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
|
|||
{
|
||||
struct imx_i2c_struct *i2c_imx = dev_id;
|
||||
unsigned int ctl, status;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&i2c_imx->slave_lock, flags);
|
||||
status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||
ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
|
||||
|
@ -851,14 +893,20 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
|
|||
i2c_imx_clear_irq(i2c_imx, I2SR_IIF);
|
||||
if (i2c_imx->slave) {
|
||||
if (!(ctl & I2CR_MSTA)) {
|
||||
return i2c_imx_slave_isr(i2c_imx, status, ctl);
|
||||
} else if (i2c_imx->last_slave_event !=
|
||||
I2C_SLAVE_STOP) {
|
||||
i2c_imx_slave_finish_op(i2c_imx);
|
||||
irqreturn_t ret;
|
||||
|
||||
ret = i2c_imx_slave_handle(i2c_imx,
|
||||
status, ctl);
|
||||
spin_unlock_irqrestore(&i2c_imx->slave_lock,
|
||||
flags);
|
||||
return ret;
|
||||
}
|
||||
i2c_imx_slave_finish_op(i2c_imx);
|
||||
}
|
||||
spin_unlock_irqrestore(&i2c_imx->slave_lock, flags);
|
||||
return i2c_imx_master_isr(i2c_imx, status);
|
||||
}
|
||||
spin_unlock_irqrestore(&i2c_imx->slave_lock, flags);
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
@ -1378,6 +1426,10 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
if (!i2c_imx)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&i2c_imx->slave_lock);
|
||||
hrtimer_init(&i2c_imx->slave_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
i2c_imx->slave_timer.function = i2c_imx_slave_timeout;
|
||||
|
||||
match = device_get_match_data(&pdev->dev);
|
||||
if (match)
|
||||
i2c_imx->hwdata = match;
|
||||
|
@ -1491,6 +1543,8 @@ static int i2c_imx_remove(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
hrtimer_cancel(&i2c_imx->slave_timer);
|
||||
|
||||
/* remove adapter */
|
||||
dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
|
||||
i2c_del_adapter(&i2c_imx->adapter);
|
||||
|
|
|
@ -119,23 +119,30 @@ static inline void writeccr(struct mpc_i2c *i2c, u32 x)
|
|||
/* Sometimes 9th clock pulse isn't generated, and slave doesn't release
|
||||
* the bus, because it wants to send ACK.
|
||||
* Following sequence of enabling/disabling and sending start/stop generates
|
||||
* the 9 pulses, so it's all OK.
|
||||
* the 9 pulses, each with a START then ending with STOP, so it's all OK.
|
||||
*/
|
||||
static void mpc_i2c_fixup(struct mpc_i2c *i2c)
|
||||
{
|
||||
int k;
|
||||
u32 delay_val = 1000000 / i2c->real_clk + 1;
|
||||
|
||||
if (delay_val < 2)
|
||||
delay_val = 2;
|
||||
unsigned long flags;
|
||||
|
||||
for (k = 9; k; k--) {
|
||||
writeccr(i2c, 0);
|
||||
writeccr(i2c, CCR_MSTA | CCR_MTX | CCR_MEN);
|
||||
writeb(0, i2c->base + MPC_I2C_SR); /* clear any status bits */
|
||||
writeccr(i2c, CCR_MEN | CCR_MSTA); /* START */
|
||||
readb(i2c->base + MPC_I2C_DR); /* init xfer */
|
||||
udelay(15); /* let it hit the bus */
|
||||
local_irq_save(flags); /* should not be delayed further */
|
||||
writeccr(i2c, CCR_MEN | CCR_MSTA | CCR_RSTA); /* delay SDA */
|
||||
readb(i2c->base + MPC_I2C_DR);
|
||||
writeccr(i2c, CCR_MEN);
|
||||
udelay(delay_val << 1);
|
||||
if (k != 1)
|
||||
udelay(5);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
writeccr(i2c, CCR_MEN); /* Initiate STOP */
|
||||
readb(i2c->base + MPC_I2C_DR);
|
||||
udelay(15); /* Let STOP propagate */
|
||||
writeccr(i2c, 0);
|
||||
}
|
||||
|
||||
static int i2c_mpc_wait_sr(struct mpc_i2c *i2c, int mask)
|
||||
|
|
|
@ -367,11 +367,15 @@ static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv)
|
|||
rcar_i2c_prepare_msg(priv);
|
||||
}
|
||||
|
||||
static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv)
|
||||
static void rcar_i2c_cleanup_dma(struct rcar_i2c_priv *priv, bool terminate)
|
||||
{
|
||||
struct dma_chan *chan = priv->dma_direction == DMA_FROM_DEVICE
|
||||
? priv->dma_rx : priv->dma_tx;
|
||||
|
||||
/* only allowed from thread context! */
|
||||
if (terminate)
|
||||
dmaengine_terminate_sync(chan);
|
||||
|
||||
dma_unmap_single(chan->device->dev, sg_dma_address(&priv->sg),
|
||||
sg_dma_len(&priv->sg), priv->dma_direction);
|
||||
|
||||
|
@ -386,25 +390,13 @@ static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv)
|
|||
rcar_i2c_write(priv, ICDMAER, 0);
|
||||
}
|
||||
|
||||
static void rcar_i2c_cleanup_dma(struct rcar_i2c_priv *priv)
|
||||
{
|
||||
if (priv->dma_direction == DMA_NONE)
|
||||
return;
|
||||
else if (priv->dma_direction == DMA_FROM_DEVICE)
|
||||
dmaengine_terminate_all(priv->dma_rx);
|
||||
else if (priv->dma_direction == DMA_TO_DEVICE)
|
||||
dmaengine_terminate_all(priv->dma_tx);
|
||||
|
||||
rcar_i2c_dma_unmap(priv);
|
||||
}
|
||||
|
||||
static void rcar_i2c_dma_callback(void *data)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = data;
|
||||
|
||||
priv->pos += sg_dma_len(&priv->sg);
|
||||
|
||||
rcar_i2c_dma_unmap(priv);
|
||||
rcar_i2c_cleanup_dma(priv, false);
|
||||
}
|
||||
|
||||
static bool rcar_i2c_dma(struct rcar_i2c_priv *priv)
|
||||
|
@ -456,7 +448,7 @@ static bool rcar_i2c_dma(struct rcar_i2c_priv *priv)
|
|||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!txdesc) {
|
||||
dev_dbg(dev, "dma prep slave sg failed, using PIO\n");
|
||||
rcar_i2c_cleanup_dma(priv);
|
||||
rcar_i2c_cleanup_dma(priv, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -466,7 +458,7 @@ static bool rcar_i2c_dma(struct rcar_i2c_priv *priv)
|
|||
cookie = dmaengine_submit(txdesc);
|
||||
if (dma_submit_error(cookie)) {
|
||||
dev_dbg(dev, "submitting dma failed, using PIO\n");
|
||||
rcar_i2c_cleanup_dma(priv);
|
||||
rcar_i2c_cleanup_dma(priv, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -846,7 +838,7 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
|||
|
||||
/* cleanup DMA if it couldn't complete properly due to an error */
|
||||
if (priv->dma_direction != DMA_NONE)
|
||||
rcar_i2c_cleanup_dma(priv);
|
||||
rcar_i2c_cleanup_dma(priv, true);
|
||||
|
||||
if (!time_left) {
|
||||
rcar_i2c_init(priv);
|
||||
|
|
|
@ -433,12 +433,12 @@ static int riic_i2c_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(riic_irqs); i++) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, riic_irqs[i].res_num);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
ret = platform_get_irq(pdev, riic_irqs[i].res_num);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, res->start, riic_irqs[i].isr,
|
||||
0, riic_irqs[i].name, riic);
|
||||
ret = devm_request_irq(&pdev->dev, ret, riic_irqs[i].isr,
|
||||
0, riic_irqs[i].name, riic);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request irq %s\n", riic_irqs[i].name);
|
||||
return ret;
|
||||
|
|
|
@ -1338,8 +1338,15 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
|
|||
goto err_pclk;
|
||||
}
|
||||
|
||||
ret = clk_enable(i2c->clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Can't enable bus clk: %d\n", ret);
|
||||
goto err_clk_notifier;
|
||||
}
|
||||
|
||||
clk_rate = clk_get_rate(i2c->clk);
|
||||
rk3x_i2c_adapt_div(i2c, clk_rate);
|
||||
clk_disable(i2c->clk);
|
||||
|
||||
ret = i2c_add_adapter(&i2c->adap);
|
||||
if (ret < 0)
|
||||
|
|
|
@ -442,34 +442,26 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void sh_mobile_i2c_dma_unmap(struct sh_mobile_i2c_data *pd)
|
||||
static void sh_mobile_i2c_cleanup_dma(struct sh_mobile_i2c_data *pd, bool terminate)
|
||||
{
|
||||
struct dma_chan *chan = pd->dma_direction == DMA_FROM_DEVICE
|
||||
? pd->dma_rx : pd->dma_tx;
|
||||
|
||||
/* only allowed from thread context! */
|
||||
if (terminate)
|
||||
dmaengine_terminate_sync(chan);
|
||||
|
||||
dma_unmap_single(chan->device->dev, sg_dma_address(&pd->sg),
|
||||
pd->msg->len, pd->dma_direction);
|
||||
|
||||
pd->dma_direction = DMA_NONE;
|
||||
}
|
||||
|
||||
static void sh_mobile_i2c_cleanup_dma(struct sh_mobile_i2c_data *pd)
|
||||
{
|
||||
if (pd->dma_direction == DMA_NONE)
|
||||
return;
|
||||
else if (pd->dma_direction == DMA_FROM_DEVICE)
|
||||
dmaengine_terminate_sync(pd->dma_rx);
|
||||
else if (pd->dma_direction == DMA_TO_DEVICE)
|
||||
dmaengine_terminate_sync(pd->dma_tx);
|
||||
|
||||
sh_mobile_i2c_dma_unmap(pd);
|
||||
}
|
||||
|
||||
static void sh_mobile_i2c_dma_callback(void *data)
|
||||
{
|
||||
struct sh_mobile_i2c_data *pd = data;
|
||||
|
||||
sh_mobile_i2c_dma_unmap(pd);
|
||||
sh_mobile_i2c_cleanup_dma(pd, false);
|
||||
pd->pos = pd->msg->len;
|
||||
pd->stop_after_dma = true;
|
||||
|
||||
|
@ -549,7 +541,7 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)
|
|||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!txdesc) {
|
||||
dev_dbg(pd->dev, "dma prep slave sg failed, using PIO\n");
|
||||
sh_mobile_i2c_cleanup_dma(pd);
|
||||
sh_mobile_i2c_cleanup_dma(pd, false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -559,7 +551,7 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)
|
|||
cookie = dmaengine_submit(txdesc);
|
||||
if (dma_submit_error(cookie)) {
|
||||
dev_dbg(pd->dev, "submitting dma failed, using PIO\n");
|
||||
sh_mobile_i2c_cleanup_dma(pd);
|
||||
sh_mobile_i2c_cleanup_dma(pd, false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -698,7 +690,7 @@ static int sh_mobile_xfer(struct sh_mobile_i2c_data *pd,
|
|||
if (!time_left) {
|
||||
dev_err(pd->dev, "Transfer request timed out\n");
|
||||
if (pd->dma_direction != DMA_NONE)
|
||||
sh_mobile_i2c_cleanup_dma(pd);
|
||||
sh_mobile_i2c_cleanup_dma(pd, true);
|
||||
|
||||
err = -ETIMEDOUT;
|
||||
break;
|
||||
|
@ -838,20 +830,38 @@ static void sh_mobile_i2c_release_dma(struct sh_mobile_i2c_data *pd)
|
|||
|
||||
static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, struct sh_mobile_i2c_data *pd)
|
||||
{
|
||||
struct resource *res;
|
||||
resource_size_t n;
|
||||
struct device_node *np = dev_of_node(&dev->dev);
|
||||
int k = 0, ret;
|
||||
|
||||
while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) {
|
||||
for (n = res->start; n <= res->end; n++) {
|
||||
ret = devm_request_irq(&dev->dev, n, sh_mobile_i2c_isr,
|
||||
0, dev_name(&dev->dev), pd);
|
||||
if (np) {
|
||||
int irq;
|
||||
|
||||
while ((irq = platform_get_irq_optional(dev, k)) != -ENXIO) {
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
ret = devm_request_irq(&dev->dev, irq, sh_mobile_i2c_isr,
|
||||
0, dev_name(&dev->dev), pd);
|
||||
if (ret) {
|
||||
dev_err(&dev->dev, "cannot request IRQ %pa\n", &n);
|
||||
dev_err(&dev->dev, "cannot request IRQ %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
} else {
|
||||
struct resource *res;
|
||||
resource_size_t n;
|
||||
|
||||
while ((res = platform_get_resource(dev, IORESOURCE_IRQ, k))) {
|
||||
for (n = res->start; n <= res->end; n++) {
|
||||
ret = devm_request_irq(&dev->dev, n, sh_mobile_i2c_isr,
|
||||
0, dev_name(&dev->dev), pd);
|
||||
if (ret) {
|
||||
dev_err(&dev->dev, "cannot request IRQ %pa\n", &n);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
k++;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
|
||||
return k > 0 ? 0 : -ENOENT;
|
||||
|
|
|
@ -828,18 +828,14 @@ static void stm32f7_i2c_smbus_reload(struct stm32f7_i2c_dev *i2c_dev)
|
|||
writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2);
|
||||
}
|
||||
|
||||
static int stm32f7_i2c_release_bus(struct i2c_adapter *i2c_adap)
|
||||
static void stm32f7_i2c_release_bus(struct i2c_adapter *i2c_adap)
|
||||
{
|
||||
struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
|
||||
|
||||
dev_info(i2c_dev->dev, "Trying to recover bus\n");
|
||||
|
||||
stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
|
||||
STM32F7_I2C_CR1_PE);
|
||||
|
||||
stm32f7_i2c_hw_config(i2c_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev)
|
||||
|
@ -854,13 +850,7 @@ static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev)
|
|||
if (!ret)
|
||||
return 0;
|
||||
|
||||
dev_info(i2c_dev->dev, "bus busy\n");
|
||||
|
||||
ret = stm32f7_i2c_release_bus(&i2c_dev->adap);
|
||||
if (ret) {
|
||||
dev_err(i2c_dev->dev, "Failed to recover the bus (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
stm32f7_i2c_release_bus(&i2c_dev->adap);
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Author: Colin Cross <ccross@android.com>
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -245,7 +246,7 @@ struct tegra_i2c_hw_feature {
|
|||
* @msg_buf: pointer to current message data
|
||||
* @msg_buf_remaining: size of unsent data in the message buffer
|
||||
* @msg_read: indicates that the transfer is a read access
|
||||
* @bus_clk_rate: current I2C bus clock rate
|
||||
* @timings: i2c timings information like bus frequency
|
||||
* @multimaster_mode: indicates that I2C controller is in multi-master mode
|
||||
* @tx_dma_chan: DMA transmit channel
|
||||
* @rx_dma_chan: DMA receive channel
|
||||
|
@ -272,7 +273,7 @@ struct tegra_i2c_dev {
|
|||
unsigned int nclocks;
|
||||
|
||||
struct clk *div_clk;
|
||||
u32 bus_clk_rate;
|
||||
struct i2c_timings timings;
|
||||
|
||||
struct completion msg_complete;
|
||||
size_t msg_buf_remaining;
|
||||
|
@ -608,6 +609,8 @@ static int tegra_i2c_wait_for_config_load(struct tegra_i2c_dev *i2c_dev)
|
|||
static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
||||
{
|
||||
u32 val, clk_divisor, clk_multiplier, tsu_thd, tlow, thigh, non_hs_mode;
|
||||
acpi_handle handle = ACPI_HANDLE(i2c_dev->dev);
|
||||
struct i2c_timings *t = &i2c_dev->timings;
|
||||
int err;
|
||||
|
||||
/*
|
||||
|
@ -618,7 +621,11 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
|||
* emit a noisy warning on error, which won't stay unnoticed and
|
||||
* won't hose machine entirely.
|
||||
*/
|
||||
err = reset_control_reset(i2c_dev->rst);
|
||||
if (handle)
|
||||
err = acpi_evaluate_object(handle, "_RST", NULL, NULL);
|
||||
else
|
||||
err = reset_control_reset(i2c_dev->rst);
|
||||
|
||||
WARN_ON_ONCE(err);
|
||||
|
||||
if (i2c_dev->is_dvc)
|
||||
|
@ -636,14 +643,14 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
|||
if (i2c_dev->is_vi)
|
||||
tegra_i2c_vi_init(i2c_dev);
|
||||
|
||||
switch (i2c_dev->bus_clk_rate) {
|
||||
switch (t->bus_freq_hz) {
|
||||
case I2C_MAX_STANDARD_MODE_FREQ + 1 ... I2C_MAX_FAST_MODE_PLUS_FREQ:
|
||||
default:
|
||||
tlow = i2c_dev->hw->tlow_fast_fastplus_mode;
|
||||
thigh = i2c_dev->hw->thigh_fast_fastplus_mode;
|
||||
tsu_thd = i2c_dev->hw->setup_hold_time_fast_fast_plus_mode;
|
||||
|
||||
if (i2c_dev->bus_clk_rate > I2C_MAX_FAST_MODE_FREQ)
|
||||
if (t->bus_freq_hz > I2C_MAX_FAST_MODE_FREQ)
|
||||
non_hs_mode = i2c_dev->hw->clk_divisor_fast_plus_mode;
|
||||
else
|
||||
non_hs_mode = i2c_dev->hw->clk_divisor_fast_mode;
|
||||
|
@ -679,7 +686,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
|||
clk_multiplier = (tlow + thigh + 2) * (non_hs_mode + 1);
|
||||
|
||||
err = clk_set_rate(i2c_dev->div_clk,
|
||||
i2c_dev->bus_clk_rate * clk_multiplier);
|
||||
t->bus_freq_hz * clk_multiplier);
|
||||
if (err) {
|
||||
dev_err(i2c_dev->dev, "failed to set div-clk rate: %d\n", err);
|
||||
return err;
|
||||
|
@ -718,7 +725,7 @@ static int tegra_i2c_disable_packet_mode(struct tegra_i2c_dev *i2c_dev)
|
|||
* before disabling the controller so that the STOP condition has
|
||||
* been delivered properly.
|
||||
*/
|
||||
udelay(DIV_ROUND_UP(2 * 1000000, i2c_dev->bus_clk_rate));
|
||||
udelay(DIV_ROUND_UP(2 * 1000000, i2c_dev->timings.bus_freq_hz));
|
||||
|
||||
cnfg = i2c_readl(i2c_dev, I2C_CNFG);
|
||||
if (cnfg & I2C_CNFG_PACKET_MODE_EN)
|
||||
|
@ -1248,7 +1255,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
|
|||
* Total bits = 9 bits per byte (including ACK bit) + Start & stop bits
|
||||
*/
|
||||
xfer_time += DIV_ROUND_CLOSEST(((xfer_size * 9) + 2) * MSEC_PER_SEC,
|
||||
i2c_dev->bus_clk_rate);
|
||||
i2c_dev->timings.bus_freq_hz);
|
||||
|
||||
int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
|
||||
tegra_i2c_unmask_irq(i2c_dev, int_mask);
|
||||
|
@ -1625,14 +1632,10 @@ static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev)
|
|||
{
|
||||
struct device_node *np = i2c_dev->dev->of_node;
|
||||
bool multi_mode;
|
||||
int err;
|
||||
|
||||
err = of_property_read_u32(np, "clock-frequency",
|
||||
&i2c_dev->bus_clk_rate);
|
||||
if (err)
|
||||
i2c_dev->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
i2c_parse_fw_timings(i2c_dev->dev, &i2c_dev->timings, true);
|
||||
|
||||
multi_mode = of_property_read_bool(np, "multi-master");
|
||||
multi_mode = device_property_read_bool(i2c_dev->dev, "multi-master");
|
||||
i2c_dev->multimaster_mode = multi_mode;
|
||||
|
||||
if (of_device_is_compatible(np, "nvidia,tegra20-i2c-dvc"))
|
||||
|
@ -1642,10 +1645,26 @@ static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev)
|
|||
i2c_dev->is_vi = true;
|
||||
}
|
||||
|
||||
static int tegra_i2c_init_reset(struct tegra_i2c_dev *i2c_dev)
|
||||
{
|
||||
if (ACPI_HANDLE(i2c_dev->dev))
|
||||
return 0;
|
||||
|
||||
i2c_dev->rst = devm_reset_control_get_exclusive(i2c_dev->dev, "i2c");
|
||||
if (IS_ERR(i2c_dev->rst))
|
||||
return dev_err_probe(i2c_dev->dev, PTR_ERR(i2c_dev->rst),
|
||||
"failed to get reset control\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_i2c_init_clocks(struct tegra_i2c_dev *i2c_dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (ACPI_HANDLE(i2c_dev->dev))
|
||||
return 0;
|
||||
|
||||
i2c_dev->clocks[i2c_dev->nclocks++].id = "div-clk";
|
||||
|
||||
if (i2c_dev->hw == &tegra20_i2c_hw || i2c_dev->hw == &tegra30_i2c_hw)
|
||||
|
@ -1720,7 +1739,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
|
|||
init_completion(&i2c_dev->msg_complete);
|
||||
init_completion(&i2c_dev->dma_complete);
|
||||
|
||||
i2c_dev->hw = of_device_get_match_data(&pdev->dev);
|
||||
i2c_dev->hw = device_get_match_data(&pdev->dev);
|
||||
i2c_dev->cont_id = pdev->id;
|
||||
i2c_dev->dev = &pdev->dev;
|
||||
|
||||
|
@ -1746,15 +1765,12 @@ static int tegra_i2c_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
i2c_dev->rst = devm_reset_control_get_exclusive(i2c_dev->dev, "i2c");
|
||||
if (IS_ERR(i2c_dev->rst)) {
|
||||
dev_err_probe(i2c_dev->dev, PTR_ERR(i2c_dev->rst),
|
||||
"failed to get reset control\n");
|
||||
return PTR_ERR(i2c_dev->rst);
|
||||
}
|
||||
|
||||
tegra_i2c_parse_dt(i2c_dev);
|
||||
|
||||
err = tegra_i2c_init_reset(i2c_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tegra_i2c_init_clocks(i2c_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -1923,12 +1939,21 @@ static const struct dev_pm_ops tegra_i2c_pm = {
|
|||
NULL)
|
||||
};
|
||||
|
||||
static const struct acpi_device_id tegra_i2c_acpi_match[] = {
|
||||
{.id = "NVDA0101", .driver_data = (kernel_ulong_t)&tegra210_i2c_hw},
|
||||
{.id = "NVDA0201", .driver_data = (kernel_ulong_t)&tegra186_i2c_hw},
|
||||
{.id = "NVDA0301", .driver_data = (kernel_ulong_t)&tegra194_i2c_hw},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, tegra_i2c_acpi_match);
|
||||
|
||||
static struct platform_driver tegra_i2c_driver = {
|
||||
.probe = tegra_i2c_probe,
|
||||
.remove = tegra_i2c_remove,
|
||||
.driver = {
|
||||
.name = "tegra-i2c",
|
||||
.of_match_table = tegra_i2c_of_match,
|
||||
.acpi_match_table = tegra_i2c_acpi_match,
|
||||
.pm = &tegra_i2c_pm,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -572,12 +572,6 @@ static int xlp9xx_i2c_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id xlp9xx_i2c_of_match[] = {
|
||||
{ .compatible = "netlogic,xlp980-i2c", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, xlp9xx_i2c_of_match);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id xlp9xx_i2c_acpi_ids[] = {
|
||||
{"BRCM9007", 0},
|
||||
|
@ -592,7 +586,6 @@ static struct platform_driver xlp9xx_i2c_driver = {
|
|||
.remove = xlp9xx_i2c_remove,
|
||||
.driver = {
|
||||
.name = "xlp9xx-i2c",
|
||||
.of_match_table = xlp9xx_i2c_of_match,
|
||||
.acpi_match_table = ACPI_PTR(xlp9xx_i2c_acpi_ids),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,470 +0,0 @@
|
|||
/*
|
||||
* Copyright 2011, Netlogic Microsystems Inc.
|
||||
* Copyright 2004, Matt Porter <mporter@kernel.crashing.org>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
/* XLR I2C REGISTERS */
|
||||
#define XLR_I2C_CFG 0x00
|
||||
#define XLR_I2C_CLKDIV 0x01
|
||||
#define XLR_I2C_DEVADDR 0x02
|
||||
#define XLR_I2C_ADDR 0x03
|
||||
#define XLR_I2C_DATAOUT 0x04
|
||||
#define XLR_I2C_DATAIN 0x05
|
||||
#define XLR_I2C_STATUS 0x06
|
||||
#define XLR_I2C_STARTXFR 0x07
|
||||
#define XLR_I2C_BYTECNT 0x08
|
||||
#define XLR_I2C_HDSTATIM 0x09
|
||||
|
||||
/* Sigma Designs additional registers */
|
||||
#define XLR_I2C_INT_EN 0x09
|
||||
#define XLR_I2C_INT_STAT 0x0a
|
||||
|
||||
/* XLR I2C REGISTERS FLAGS */
|
||||
#define XLR_I2C_BUS_BUSY 0x01
|
||||
#define XLR_I2C_SDOEMPTY 0x02
|
||||
#define XLR_I2C_RXRDY 0x04
|
||||
#define XLR_I2C_ACK_ERR 0x08
|
||||
#define XLR_I2C_ARB_STARTERR 0x30
|
||||
|
||||
/* Register Values */
|
||||
#define XLR_I2C_CFG_ADDR 0xF8
|
||||
#define XLR_I2C_CFG_NOADDR 0xFA
|
||||
#define XLR_I2C_STARTXFR_ND 0x02 /* No Data */
|
||||
#define XLR_I2C_STARTXFR_RD 0x01 /* Read */
|
||||
#define XLR_I2C_STARTXFR_WR 0x00 /* Write */
|
||||
|
||||
#define XLR_I2C_TIMEOUT 10 /* timeout per byte in msec */
|
||||
|
||||
/*
|
||||
* On XLR/XLS, we need to use __raw_ IO to read the I2C registers
|
||||
* because they are in the big-endian MMIO area on the SoC.
|
||||
*
|
||||
* The readl/writel implementation on XLR/XLS byteswaps, because
|
||||
* those are for its little-endian PCI space (see arch/mips/Kconfig).
|
||||
*/
|
||||
static inline void xlr_i2c_wreg(u32 __iomem *base, unsigned int reg, u32 val)
|
||||
{
|
||||
__raw_writel(val, base + reg);
|
||||
}
|
||||
|
||||
static inline u32 xlr_i2c_rdreg(u32 __iomem *base, unsigned int reg)
|
||||
{
|
||||
return __raw_readl(base + reg);
|
||||
}
|
||||
|
||||
#define XLR_I2C_FLAG_IRQ 1
|
||||
|
||||
struct xlr_i2c_config {
|
||||
u32 flags; /* optional feature support */
|
||||
u32 status_busy; /* value of STATUS[0] when busy */
|
||||
u32 cfg_extra; /* extra CFG bits to set */
|
||||
};
|
||||
|
||||
struct xlr_i2c_private {
|
||||
struct i2c_adapter adap;
|
||||
u32 __iomem *iobase;
|
||||
int irq;
|
||||
int pos;
|
||||
struct i2c_msg *msg;
|
||||
const struct xlr_i2c_config *cfg;
|
||||
wait_queue_head_t wait;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static int xlr_i2c_busy(struct xlr_i2c_private *priv, u32 status)
|
||||
{
|
||||
return (status & XLR_I2C_BUS_BUSY) == priv->cfg->status_busy;
|
||||
}
|
||||
|
||||
static int xlr_i2c_idle(struct xlr_i2c_private *priv)
|
||||
{
|
||||
return !xlr_i2c_busy(priv, xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS));
|
||||
}
|
||||
|
||||
static int xlr_i2c_wait(struct xlr_i2c_private *priv, unsigned long timeout)
|
||||
{
|
||||
int status;
|
||||
int t;
|
||||
|
||||
t = wait_event_timeout(priv->wait, xlr_i2c_idle(priv),
|
||||
msecs_to_jiffies(timeout));
|
||||
if (!t)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
|
||||
|
||||
return status & XLR_I2C_ACK_ERR ? -EIO : 0;
|
||||
}
|
||||
|
||||
static void xlr_i2c_tx_irq(struct xlr_i2c_private *priv, u32 status)
|
||||
{
|
||||
struct i2c_msg *msg = priv->msg;
|
||||
|
||||
if (status & XLR_I2C_SDOEMPTY)
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT,
|
||||
msg->buf[priv->pos++]);
|
||||
}
|
||||
|
||||
static void xlr_i2c_rx_irq(struct xlr_i2c_private *priv, u32 status)
|
||||
{
|
||||
struct i2c_msg *msg = priv->msg;
|
||||
|
||||
if (status & XLR_I2C_RXRDY)
|
||||
msg->buf[priv->pos++] =
|
||||
xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
|
||||
}
|
||||
|
||||
static irqreturn_t xlr_i2c_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct xlr_i2c_private *priv = dev_id;
|
||||
struct i2c_msg *msg = priv->msg;
|
||||
u32 int_stat, status;
|
||||
|
||||
int_stat = xlr_i2c_rdreg(priv->iobase, XLR_I2C_INT_STAT);
|
||||
if (!int_stat)
|
||||
return IRQ_NONE;
|
||||
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_STAT, int_stat);
|
||||
|
||||
if (!msg)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
|
||||
|
||||
if (priv->pos < msg->len) {
|
||||
if (msg->flags & I2C_M_RD)
|
||||
xlr_i2c_rx_irq(priv, status);
|
||||
else
|
||||
xlr_i2c_tx_irq(priv, status);
|
||||
}
|
||||
|
||||
if (!xlr_i2c_busy(priv, status))
|
||||
wake_up(&priv->wait);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
|
||||
u8 *buf, u16 addr)
|
||||
{
|
||||
struct i2c_adapter *adap = &priv->adap;
|
||||
unsigned long timeout, stoptime, checktime;
|
||||
u32 i2c_status;
|
||||
int pos, timedout;
|
||||
u8 offset;
|
||||
u32 xfer;
|
||||
|
||||
offset = buf[0];
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
|
||||
XLR_I2C_CFG_ADDR | priv->cfg->cfg_extra);
|
||||
|
||||
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
|
||||
stoptime = jiffies + timeout;
|
||||
timedout = 0;
|
||||
|
||||
if (len == 1) {
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
|
||||
xfer = XLR_I2C_STARTXFR_ND;
|
||||
pos = 1;
|
||||
} else {
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 2);
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[1]);
|
||||
xfer = XLR_I2C_STARTXFR_WR;
|
||||
pos = 2;
|
||||
}
|
||||
|
||||
priv->pos = pos;
|
||||
|
||||
retry:
|
||||
/* retry can only happen on the first byte */
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, xfer);
|
||||
|
||||
if (priv->irq > 0)
|
||||
return xlr_i2c_wait(priv, XLR_I2C_TIMEOUT * len);
|
||||
|
||||
while (!timedout) {
|
||||
checktime = jiffies;
|
||||
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
|
||||
|
||||
if ((i2c_status & XLR_I2C_SDOEMPTY) && pos < len) {
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos++]);
|
||||
|
||||
/* reset timeout on successful xmit */
|
||||
stoptime = jiffies + timeout;
|
||||
}
|
||||
timedout = time_after(checktime, stoptime);
|
||||
|
||||
if (i2c_status & XLR_I2C_ARB_STARTERR) {
|
||||
if (timedout)
|
||||
break;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (i2c_status & XLR_I2C_ACK_ERR)
|
||||
return -EIO;
|
||||
|
||||
if (!xlr_i2c_busy(priv, i2c_status) && pos >= len)
|
||||
return 0;
|
||||
}
|
||||
dev_err(&adap->dev, "I2C transmit timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
|
||||
{
|
||||
struct i2c_adapter *adap = &priv->adap;
|
||||
u32 i2c_status;
|
||||
unsigned long timeout, stoptime, checktime;
|
||||
int nbytes, timedout;
|
||||
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
|
||||
XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra);
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
|
||||
|
||||
priv->pos = 0;
|
||||
|
||||
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
|
||||
stoptime = jiffies + timeout;
|
||||
timedout = 0;
|
||||
nbytes = 0;
|
||||
retry:
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, XLR_I2C_STARTXFR_RD);
|
||||
|
||||
if (priv->irq > 0)
|
||||
return xlr_i2c_wait(priv, XLR_I2C_TIMEOUT * len);
|
||||
|
||||
while (!timedout) {
|
||||
checktime = jiffies;
|
||||
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
|
||||
if (i2c_status & XLR_I2C_RXRDY) {
|
||||
if (nbytes >= len)
|
||||
return -EIO; /* should not happen */
|
||||
|
||||
buf[nbytes++] =
|
||||
xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
|
||||
|
||||
/* reset timeout on successful read */
|
||||
stoptime = jiffies + timeout;
|
||||
}
|
||||
|
||||
timedout = time_after(checktime, stoptime);
|
||||
if (i2c_status & XLR_I2C_ARB_STARTERR) {
|
||||
if (timedout)
|
||||
break;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (i2c_status & XLR_I2C_ACK_ERR)
|
||||
return -EIO;
|
||||
|
||||
if (!xlr_i2c_busy(priv, i2c_status))
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_err(&adap->dev, "I2C receive timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int xlr_i2c_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct i2c_msg *msg;
|
||||
int i;
|
||||
int ret = 0;
|
||||
struct xlr_i2c_private *priv = i2c_get_adapdata(adap);
|
||||
|
||||
ret = clk_enable(priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->irq)
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_EN, 0xf);
|
||||
|
||||
|
||||
for (i = 0; ret == 0 && i < num; i++) {
|
||||
msg = &msgs[i];
|
||||
priv->msg = msg;
|
||||
if (msg->flags & I2C_M_RD)
|
||||
ret = xlr_i2c_rx(priv, msg->len, &msg->buf[0],
|
||||
msg->addr);
|
||||
else
|
||||
ret = xlr_i2c_tx(priv, msg->len, &msg->buf[0],
|
||||
msg->addr);
|
||||
}
|
||||
|
||||
if (priv->irq)
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_EN, 0);
|
||||
|
||||
clk_disable(priv->clk);
|
||||
priv->msg = NULL;
|
||||
|
||||
return (ret != 0) ? ret : num;
|
||||
}
|
||||
|
||||
static u32 xlr_func(struct i2c_adapter *adap)
|
||||
{
|
||||
/* Emulate SMBUS over I2C */
|
||||
return (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | I2C_FUNC_I2C;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm xlr_i2c_algo = {
|
||||
.master_xfer = xlr_i2c_xfer,
|
||||
.functionality = xlr_func,
|
||||
};
|
||||
|
||||
static const struct i2c_adapter_quirks xlr_i2c_quirks = {
|
||||
.flags = I2C_AQ_NO_ZERO_LEN,
|
||||
};
|
||||
|
||||
static const struct xlr_i2c_config xlr_i2c_config_default = {
|
||||
.status_busy = XLR_I2C_BUS_BUSY,
|
||||
.cfg_extra = 0,
|
||||
};
|
||||
|
||||
static const struct xlr_i2c_config xlr_i2c_config_tangox = {
|
||||
.flags = XLR_I2C_FLAG_IRQ,
|
||||
.status_busy = 0,
|
||||
.cfg_extra = 1 << 8,
|
||||
};
|
||||
|
||||
static const struct of_device_id xlr_i2c_dt_ids[] = {
|
||||
{
|
||||
.compatible = "sigma,smp8642-i2c",
|
||||
.data = &xlr_i2c_config_tangox,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, xlr_i2c_dt_ids);
|
||||
|
||||
static int xlr_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct xlr_i2c_private *priv;
|
||||
struct clk *clk;
|
||||
unsigned long clk_rate;
|
||||
unsigned long clk_div;
|
||||
u32 busfreq;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
match = of_match_device(xlr_i2c_dt_ids, &pdev->dev);
|
||||
if (match)
|
||||
priv->cfg = match->data;
|
||||
else
|
||||
priv->cfg = &xlr_i2c_config_default;
|
||||
|
||||
priv->iobase = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->iobase))
|
||||
return PTR_ERR(priv->iobase);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (irq > 0 && (priv->cfg->flags & XLR_I2C_FLAG_IRQ)) {
|
||||
priv->irq = irq;
|
||||
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_EN, 0);
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_STAT, 0xf);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, priv->irq, xlr_i2c_irq,
|
||||
IRQF_SHARED, dev_name(&pdev->dev),
|
||||
priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
init_waitqueue_head(&priv->wait);
|
||||
}
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
|
||||
&busfreq))
|
||||
busfreq = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (!IS_ERR(clk)) {
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_rate = clk_get_rate(clk);
|
||||
clk_div = DIV_ROUND_UP(clk_rate, 2 * busfreq);
|
||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_CLKDIV, clk_div);
|
||||
|
||||
clk_disable(clk);
|
||||
priv->clk = clk;
|
||||
}
|
||||
|
||||
priv->adap.dev.parent = &pdev->dev;
|
||||
priv->adap.dev.of_node = pdev->dev.of_node;
|
||||
priv->adap.owner = THIS_MODULE;
|
||||
priv->adap.algo_data = priv;
|
||||
priv->adap.algo = &xlr_i2c_algo;
|
||||
priv->adap.quirks = &xlr_i2c_quirks;
|
||||
priv->adap.nr = pdev->id;
|
||||
priv->adap.class = I2C_CLASS_HWMON;
|
||||
snprintf(priv->adap.name, sizeof(priv->adap.name), "xlr-i2c");
|
||||
|
||||
i2c_set_adapdata(&priv->adap, priv);
|
||||
ret = i2c_add_numbered_adapter(&priv->adap);
|
||||
if (ret < 0)
|
||||
goto err_unprepare_clk;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
dev_info(&priv->adap.dev, "Added I2C Bus.\n");
|
||||
return 0;
|
||||
|
||||
err_unprepare_clk:
|
||||
clk_unprepare(clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xlr_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct xlr_i2c_private *priv;
|
||||
|
||||
priv = platform_get_drvdata(pdev);
|
||||
i2c_del_adapter(&priv->adap);
|
||||
clk_unprepare(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver xlr_i2c_driver = {
|
||||
.probe = xlr_i2c_probe,
|
||||
.remove = xlr_i2c_remove,
|
||||
.driver = {
|
||||
.name = "xlr-i2cbus",
|
||||
.of_match_table = xlr_i2c_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(xlr_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Ganesan Ramalingam <ganesanr@netlogicmicro.com>");
|
||||
MODULE_DESCRIPTION("XLR/XLS SoC I2C Controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:xlr-i2cbus");
|
|
@ -953,6 +953,7 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
|
|||
client->dev.of_node = of_node_get(info->of_node);
|
||||
client->dev.fwnode = info->fwnode;
|
||||
|
||||
device_enable_async_suspend(&client->dev);
|
||||
i2c_dev_set_name(adap, client, info);
|
||||
|
||||
if (info->swnode) {
|
||||
|
@ -1482,6 +1483,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
|||
if (res)
|
||||
goto out_reg;
|
||||
|
||||
device_enable_async_suspend(&adap->dev);
|
||||
pm_runtime_no_callbacks(&adap->dev);
|
||||
pm_suspend_ignore_children(&adap->dev, true);
|
||||
pm_runtime_enable(&adap->dev);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-mux.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/platform_data/i2c-mux-gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -49,49 +50,11 @@ static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
|
||||
struct fwnode_handle *fwdev,
|
||||
unsigned int *adr)
|
||||
|
||||
{
|
||||
unsigned long long adr64;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(ACPI_HANDLE_FWNODE(fwdev),
|
||||
METHOD_NAME__ADR,
|
||||
NULL, &adr64);
|
||||
|
||||
if (!ACPI_SUCCESS(status)) {
|
||||
dev_err(dev, "Cannot get address\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*adr = adr64;
|
||||
if (*adr != adr64) {
|
||||
dev_err(dev, "Address out of range\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
|
||||
struct fwnode_handle *fwdev,
|
||||
unsigned int *adr)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int i2c_mux_gpio_probe_fw(struct gpiomux *mux,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct fwnode_handle *fwnode = dev_fwnode(dev);
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *adapter_np;
|
||||
struct i2c_adapter *adapter = NULL;
|
||||
|
@ -99,7 +62,7 @@ static int i2c_mux_gpio_probe_fw(struct gpiomux *mux,
|
|||
unsigned *values;
|
||||
int rc, i = 0;
|
||||
|
||||
if (is_of_node(dev->fwnode)) {
|
||||
if (is_of_node(fwnode)) {
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
|
@ -111,7 +74,7 @@ static int i2c_mux_gpio_probe_fw(struct gpiomux *mux,
|
|||
adapter = of_find_i2c_adapter_by_node(adapter_np);
|
||||
of_node_put(adapter_np);
|
||||
|
||||
} else if (is_acpi_node(dev->fwnode)) {
|
||||
} else if (is_acpi_node(fwnode)) {
|
||||
/*
|
||||
* In ACPI land the mux should be a direct child of the i2c
|
||||
* bus it muxes.
|
||||
|
@ -141,16 +104,16 @@ static int i2c_mux_gpio_probe_fw(struct gpiomux *mux,
|
|||
fwnode_property_read_u32(child, "reg", values + i);
|
||||
|
||||
} else if (is_acpi_node(child)) {
|
||||
rc = i2c_mux_gpio_get_acpi_adr(dev, child, values + i);
|
||||
rc = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), values + i);
|
||||
if (rc)
|
||||
return rc;
|
||||
return dev_err_probe(dev, rc, "Cannot get address\n");
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
mux->data.values = values;
|
||||
|
||||
if (fwnode_property_read_u32(dev->fwnode, "idle-state", &mux->data.idle))
|
||||
if (device_property_read_u32(dev, "idle-state", &mux->data.idle))
|
||||
mux->data.idle = I2C_MUX_GPIO_NO_IDLE;
|
||||
|
||||
return 0;
|
||||
|
@ -190,7 +153,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
|||
return -EPROBE_DEFER;
|
||||
|
||||
muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
|
||||
ngpios * sizeof(*mux->gpios), 0,
|
||||
array_size(ngpios, sizeof(*mux->gpios)), 0,
|
||||
i2c_mux_gpio_select, NULL);
|
||||
if (!muxc) {
|
||||
ret = -ENOMEM;
|
||||
|
|
|
@ -68,11 +68,6 @@
|
|||
* which won't work on pure SMBus systems.
|
||||
*/
|
||||
|
||||
struct at24_client {
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
struct at24_data {
|
||||
/*
|
||||
* Lock protects against activities from other Linux tasks,
|
||||
|
@ -94,9 +89,10 @@ struct at24_data {
|
|||
|
||||
/*
|
||||
* Some chips tie up multiple I2C addresses; dummy devices reserve
|
||||
* them for us, and we'll use them with SMBus calls.
|
||||
* them for us.
|
||||
*/
|
||||
struct at24_client client[];
|
||||
u8 bank_addr_shift;
|
||||
struct regmap *client_regmaps[];
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -123,6 +119,7 @@ MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)");
|
|||
struct at24_chip_data {
|
||||
u32 byte_len;
|
||||
u8 flags;
|
||||
u8 bank_addr_shift;
|
||||
void (*read_post)(unsigned int off, char *buf, size_t count);
|
||||
};
|
||||
|
||||
|
@ -137,6 +134,12 @@ struct at24_chip_data {
|
|||
.read_post = _read_post, \
|
||||
}
|
||||
|
||||
#define AT24_CHIP_DATA_BS(_name, _len, _flags, _bank_addr_shift) \
|
||||
static const struct at24_chip_data _name = { \
|
||||
.byte_len = _len, .flags = _flags, \
|
||||
.bank_addr_shift = _bank_addr_shift \
|
||||
}
|
||||
|
||||
static void at24_read_post_vaio(unsigned int off, char *buf, size_t count)
|
||||
{
|
||||
int i;
|
||||
|
@ -197,6 +200,7 @@ AT24_CHIP_DATA(at24_data_24c128, 131072 / 8, AT24_FLAG_ADDR16);
|
|||
AT24_CHIP_DATA(at24_data_24c256, 262144 / 8, AT24_FLAG_ADDR16);
|
||||
AT24_CHIP_DATA(at24_data_24c512, 524288 / 8, AT24_FLAG_ADDR16);
|
||||
AT24_CHIP_DATA(at24_data_24c1024, 1048576 / 8, AT24_FLAG_ADDR16);
|
||||
AT24_CHIP_DATA_BS(at24_data_24c1025, 1048576 / 8, AT24_FLAG_ADDR16, 2);
|
||||
AT24_CHIP_DATA(at24_data_24c2048, 2097152 / 8, AT24_FLAG_ADDR16);
|
||||
/* identical to 24c08 ? */
|
||||
AT24_CHIP_DATA(at24_data_INT3499, 8192 / 8, 0);
|
||||
|
@ -225,6 +229,7 @@ static const struct i2c_device_id at24_ids[] = {
|
|||
{ "24c256", (kernel_ulong_t)&at24_data_24c256 },
|
||||
{ "24c512", (kernel_ulong_t)&at24_data_24c512 },
|
||||
{ "24c1024", (kernel_ulong_t)&at24_data_24c1024 },
|
||||
{ "24c1025", (kernel_ulong_t)&at24_data_24c1025 },
|
||||
{ "24c2048", (kernel_ulong_t)&at24_data_24c2048 },
|
||||
{ "at24", 0 },
|
||||
{ /* END OF LIST */ }
|
||||
|
@ -254,6 +259,7 @@ static const struct of_device_id at24_of_match[] = {
|
|||
{ .compatible = "atmel,24c256", .data = &at24_data_24c256 },
|
||||
{ .compatible = "atmel,24c512", .data = &at24_data_24c512 },
|
||||
{ .compatible = "atmel,24c1024", .data = &at24_data_24c1024 },
|
||||
{ .compatible = "atmel,24c1025", .data = &at24_data_24c1025 },
|
||||
{ .compatible = "atmel,24c2048", .data = &at24_data_24c2048 },
|
||||
{ /* END OF LIST */ },
|
||||
};
|
||||
|
@ -275,8 +281,8 @@ MODULE_DEVICE_TABLE(acpi, at24_acpi_ids);
|
|||
* set the byte address; on a multi-master board, another master
|
||||
* may have changed the chip's "current" address pointer.
|
||||
*/
|
||||
static struct at24_client *at24_translate_offset(struct at24_data *at24,
|
||||
unsigned int *offset)
|
||||
static struct regmap *at24_translate_offset(struct at24_data *at24,
|
||||
unsigned int *offset)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
|
@ -288,12 +294,12 @@ static struct at24_client *at24_translate_offset(struct at24_data *at24,
|
|||
*offset &= 0xff;
|
||||
}
|
||||
|
||||
return &at24->client[i];
|
||||
return at24->client_regmaps[i];
|
||||
}
|
||||
|
||||
static struct device *at24_base_client_dev(struct at24_data *at24)
|
||||
{
|
||||
return &at24->client[0].client->dev;
|
||||
return regmap_get_device(at24->client_regmaps[0]);
|
||||
}
|
||||
|
||||
static size_t at24_adjust_read_count(struct at24_data *at24,
|
||||
|
@ -324,14 +330,10 @@ static ssize_t at24_regmap_read(struct at24_data *at24, char *buf,
|
|||
unsigned int offset, size_t count)
|
||||
{
|
||||
unsigned long timeout, read_time;
|
||||
struct at24_client *at24_client;
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
at24_client = at24_translate_offset(at24, &offset);
|
||||
regmap = at24_client->regmap;
|
||||
client = at24_client->client;
|
||||
regmap = at24_translate_offset(at24, &offset);
|
||||
count = at24_adjust_read_count(at24, offset, count);
|
||||
|
||||
/* adjust offset for mac and serial read ops */
|
||||
|
@ -346,7 +348,7 @@ static ssize_t at24_regmap_read(struct at24_data *at24, char *buf,
|
|||
read_time = jiffies;
|
||||
|
||||
ret = regmap_bulk_read(regmap, offset, buf, count);
|
||||
dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
|
||||
dev_dbg(regmap_get_device(regmap), "read %zu@%d --> %d (%ld)\n",
|
||||
count, offset, ret, jiffies);
|
||||
if (!ret)
|
||||
return count;
|
||||
|
@ -387,14 +389,10 @@ static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf,
|
|||
unsigned int offset, size_t count)
|
||||
{
|
||||
unsigned long timeout, write_time;
|
||||
struct at24_client *at24_client;
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
at24_client = at24_translate_offset(at24, &offset);
|
||||
regmap = at24_client->regmap;
|
||||
client = at24_client->client;
|
||||
regmap = at24_translate_offset(at24, &offset);
|
||||
count = at24_adjust_write_count(at24, offset, count);
|
||||
timeout = jiffies + msecs_to_jiffies(at24_write_timeout);
|
||||
|
||||
|
@ -406,7 +404,7 @@ static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf,
|
|||
write_time = jiffies;
|
||||
|
||||
ret = regmap_bulk_write(regmap, offset, buf, count);
|
||||
dev_dbg(&client->dev, "write %zu@%d --> %d (%ld)\n",
|
||||
dev_dbg(regmap_get_device(regmap), "write %zu@%d --> %d (%ld)\n",
|
||||
count, offset, ret, jiffies);
|
||||
if (!ret)
|
||||
return count;
|
||||
|
@ -538,17 +536,16 @@ static const struct at24_chip_data *at24_get_chip_data(struct device *dev)
|
|||
}
|
||||
|
||||
static int at24_make_dummy_client(struct at24_data *at24, unsigned int index,
|
||||
struct i2c_client *base_client,
|
||||
struct regmap_config *regmap_config)
|
||||
{
|
||||
struct i2c_client *base_client, *dummy_client;
|
||||
struct i2c_client *dummy_client;
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
|
||||
base_client = at24->client[0].client;
|
||||
dev = &base_client->dev;
|
||||
|
||||
dummy_client = devm_i2c_new_dummy_device(dev, base_client->adapter,
|
||||
base_client->addr + index);
|
||||
dummy_client = devm_i2c_new_dummy_device(&base_client->dev,
|
||||
base_client->adapter,
|
||||
base_client->addr +
|
||||
(index << at24->bank_addr_shift));
|
||||
if (IS_ERR(dummy_client))
|
||||
return PTR_ERR(dummy_client);
|
||||
|
||||
|
@ -556,8 +553,7 @@ static int at24_make_dummy_client(struct at24_data *at24, unsigned int index,
|
|||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
at24->client[index].client = dummy_client;
|
||||
at24->client[index].regmap = regmap;
|
||||
at24->client_regmaps[index] = regmap;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -680,7 +676,7 @@ static int at24_probe(struct i2c_client *client)
|
|||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
at24 = devm_kzalloc(dev, struct_size(at24, client, num_addresses),
|
||||
at24 = devm_kzalloc(dev, struct_size(at24, client_regmaps, num_addresses),
|
||||
GFP_KERNEL);
|
||||
if (!at24)
|
||||
return -ENOMEM;
|
||||
|
@ -690,10 +686,10 @@ static int at24_probe(struct i2c_client *client)
|
|||
at24->page_size = page_size;
|
||||
at24->flags = flags;
|
||||
at24->read_post = cdata->read_post;
|
||||
at24->bank_addr_shift = cdata->bank_addr_shift;
|
||||
at24->num_addresses = num_addresses;
|
||||
at24->offset_adj = at24_get_offset_adj(flags, byte_len);
|
||||
at24->client[0].client = client;
|
||||
at24->client[0].regmap = regmap;
|
||||
at24->client_regmaps[0] = regmap;
|
||||
|
||||
at24->vcc_reg = devm_regulator_get(dev, "vcc");
|
||||
if (IS_ERR(at24->vcc_reg))
|
||||
|
@ -709,7 +705,7 @@ static int at24_probe(struct i2c_client *client)
|
|||
|
||||
/* use dummy devices for multiple-address chips */
|
||||
for (i = 1; i < num_addresses; i++) {
|
||||
err = at24_make_dummy_client(at24, i, ®map_config);
|
||||
err = at24_make_dummy_client(at24, i, client, ®map_config);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue