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:
Linus Torvalds 2022-01-14 16:19:38 +01:00
commit 112450df61
29 changed files with 677 additions and 1020 deletions

View file

@ -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.

View file

@ -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>;
};

View file

@ -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>;
};

View file

@ -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>;
};
};

View file

@ -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>;
};
};

View file

@ -20,7 +20,9 @@ properties:
- items:
- enum:
- fsl,imx8qxp-lpi2c
- fsl,imx8dxl-lpi2c
- fsl,imx8qm-lpi2c
- fsl,imx8ulp-lpi2c
- const: fsl,imx7ulp-lpi2c
reg:

View file

@ -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.

View file

@ -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

View file

@ -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>

View file

@ -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);

View file

@ -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

View file

@ -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 */

View file

@ -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");

View file

@ -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));

View file

@ -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

View file

@ -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 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
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++) {

View file

@ -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);

View file

@ -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)

View file

@ -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);

View file

@ -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;

View file

@ -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)

View file

@ -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;

View file

@ -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;
}

View file

@ -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,
},
};

View file

@ -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),
},
};

View file

@ -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");

View file

@ -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);

View file

@ -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;

View file

@ -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, &regmap_config);
err = at24_make_dummy_client(at24, i, client, &regmap_config);
if (err)
return err;
}