mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
i2c for v7.3
Core and helpers:
- support bus recovery with single-ended GPIOs
- acpi: clean up resource handling
- acpi: force ELAN1300 to 100 kHz
- algo-bit: allow consumers to skip the optional bus test
Drivers:
- use generic bus frequency definitions in nomadik,
octeon-core, microchip-corei2c, k1, davinci and pnx
- i2c-gpio: support multiple buses sharing the same SCL line
- qup: propagate clock enable failures
- spacemit: configure SCL timing and clean up clock handling
- amd-asf: guard against oversized firmware length
qcom-geni:
- add tracepoints for bus setup, interrupts and errors
- use dedicated completion events for abort and reset
- distinguish address and data NACK handling
- cancel transfers before falling back to abort
- simplify runtime PM and resource management
- refactor resource and serial engine initialization
DT bindings:
- convert Altera bindings to DT schema
- convert Axxia bindings to DT schema
New support:
- R-Car Gen5 and R-Car X5H
- Axiado AX3005
- Qualcomm Nord SA8797P
- Qualcomm SA8255p
-----BEGIN PGP SIGNATURE-----
iHUEABYKAB0WIQScDfrjQa34uOld1VLaeAVmJtMtbgUCaoA//QAKCRDaeAVmJtMt
bvzYAQDPhRd2O6BruFmCkxKKvlrxZ2AaHQX/ZHLNqLAq/oX4NwD8CSecgva9faf/
bC+kTGoTMBhje3FEJFufHKe2YtmJdgg=
=u6az
-----END PGP SIGNATURE-----
Merge tag 'i2c-7.3-part1' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux
Pull i2c updates from Andi Shyti:
"The main changes are support for shared SCL lines in i2c-gpio, a
larger qcom-geni update covering tracing and transfer recovery and
support for R-Car Gen5.
The rest is mostly smaller driver, core and DT binding updates.
Core and helpers:
- support bus recovery with single-ended GPIOs
- acpi: clean up resource handling
- acpi: force ELAN1300 to 100 kHz
- algo-bit: allow consumers to skip the optional bus test
Drivers:
- use generic bus frequency definitions in nomadik, octeon-core,
microchip-corei2c, k1, davinci and pnx
- i2c-gpio: support multiple buses sharing the same SCL line
- qup: propagate clock enable failures
- spacemit: configure SCL timing and clean up clock handling
- amd-asf: guard against oversized firmware length
qcom-geni:
- add tracepoints for bus setup, interrupts and errors
- use dedicated completion events for abort and reset
- distinguish address and data NACK handling
- cancel transfers before falling back to abort
- simplify runtime PM and resource management
- refactor resource and serial engine initialization
DT bindings:
- convert Altera bindings to DT schema
- convert Axxia bindings to DT schema
New support:
- R-Car Gen5 and R-Car X5H
- Axiado AX3005
- Qualcomm Nord SA8797P
- Qualcomm SA8255p"
* tag 'i2c-7.3-part1' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux: (33 commits)
i2c: core: support recovery for single-ended GPIOs
i2c: rcar: add R-Car Gen5 support
dt-bindings: i2c: rcar-i2c: Document R-Car X5H support
i2c: i2c-gpio: Enhance driver for buses with shared SCL
i2c: algo: bit: Allow to skip bit test
i2c: qcom-geni: Add trace events for Qualcomm GENI I2C driver
i2c: qcom-geni: trace: Add trace events for Qualcomm GENI I2C
i2c: qup: Propagate clock enable failures
i2c: qcom-geni: distinguish address-phase and data-phase NACK
i2c: qcom-geni: use dedicated completions for abort and reset events
i2c: qcom-geni: use cancel command before abort on transfer timeout
dt-bindings: i2c: cdns: add Axiado AX3005 I2C variant
i2c: qcom-geni: Use devm_pm_runtime_enable() for PM management
dt-bindings: i2c: qcom,sa8255p-geni-i2c: Add compatible for Nord SA8797P
i2c: nomadik: Use generic definitions for bus frequencies
i2c: octeon-core: Use generic definitions for bus frequencies
i2c: microchip-corei2c: Use generic definitions for bus frequencies
i2c: k1: Use generic definitions for bus frequencies
i2c: davinci: Use generic definitions for bus frequencies
i2c: pnx: Use generic definitions for bus frequencies
...
This commit is contained in:
commit
abea5c3493
|
|
@ -0,0 +1,62 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i2c/altr,softip-i2c-v1.0.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Altera I2C Controller
|
||||
|
||||
maintainers:
|
||||
- Thor Thayer <thor.thayer@linux.intel.com>
|
||||
- Chaitanya Sabnis <chaitanya.msabnis@gmail.com>
|
||||
|
||||
description:
|
||||
Altera's synthesizable logic block I2C Controller for use in Altera's FPGAs.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: altr,softip-i2c-v1.0
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
fifo-size:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Size of the RX and TX FIFOs in bytes.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c@80000 {
|
||||
compatible = "altr,softip-i2c-v1.0";
|
||||
reg = <0x80000 0x40>;
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <0 43 4>;
|
||||
clocks = <&clk_0>;
|
||||
clock-frequency = <100000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
fifo-size = <4>;
|
||||
|
||||
eeprom@51 {
|
||||
compatible = "atmel,24c32";
|
||||
reg = <0x51>;
|
||||
pagesize = <32>;
|
||||
};
|
||||
};
|
||||
|
|
@ -14,9 +14,13 @@ allOf:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- cdns,i2c-r1p10 # cadence i2c controller version 1.0
|
||||
- cdns,i2c-r1p14 # cadence i2c controller version 1.4
|
||||
oneOf:
|
||||
- enum:
|
||||
- cdns,i2c-r1p10 # cadence i2c controller version 1.0
|
||||
- cdns,i2c-r1p14 # cadence i2c controller version 1.4
|
||||
- items:
|
||||
- const: axiado,ax3005-i2c
|
||||
- const: cdns,i2c-r1p14
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
* Altera I2C Controller
|
||||
* This is Altera's synthesizable logic block I2C Controller for use
|
||||
* in Altera's FPGAs.
|
||||
|
||||
Required properties :
|
||||
- compatible : should be "altr,softip-i2c-v1.0"
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : <IRQ> where IRQ is the interrupt number.
|
||||
- clocks : phandle to input clock.
|
||||
- #address-cells = <1>;
|
||||
- #size-cells = <0>;
|
||||
|
||||
Recommended properties :
|
||||
- clock-frequency : desired I2C bus clock frequency in Hz.
|
||||
|
||||
Optional properties :
|
||||
- fifo-size : Size of the RX and TX FIFOs in bytes.
|
||||
- Child nodes conforming to i2c bus binding
|
||||
|
||||
Example :
|
||||
|
||||
i2c@100080000 {
|
||||
compatible = "altr,softip-i2c-v1.0";
|
||||
reg = <0x00000001 0x00080000 0x00000040>;
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <0 43 4>;
|
||||
clocks = <&clk_0>;
|
||||
clock-frequency = <100000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
fifo-size = <4>;
|
||||
|
||||
eeprom@51 {
|
||||
compatible = "atmel,24c32";
|
||||
reg = <0x51>;
|
||||
pagesize = <32>;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
LSI Axxia I2C
|
||||
|
||||
Required properties :
|
||||
- compatible : Must be "lsi,api2c"
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : the interrupt specifier
|
||||
- #address-cells : Must be <1>;
|
||||
- #size-cells : Must be <0>;
|
||||
- clock-names : Must contain "i2c".
|
||||
- clocks: Must contain an entry for each name in clock-names. See the common
|
||||
clock bindings.
|
||||
|
||||
Optional properties :
|
||||
- clock-frequency : Desired I2C bus clock frequency in Hz. If not specified,
|
||||
the default 100 kHz frequency will be used. As only Normal and Fast modes
|
||||
are supported, possible values are 100000 and 400000.
|
||||
|
||||
Example :
|
||||
|
||||
i2c@2010084000 {
|
||||
compatible = "lsi,api2c";
|
||||
device_type = "i2c";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x20 0x10084000 0x00 0x1000>;
|
||||
interrupts = <0 19 4>;
|
||||
clocks = <&clk_per>;
|
||||
clock-names = "i2c";
|
||||
clock-frequency = <400000>;
|
||||
};
|
||||
52
Documentation/devicetree/bindings/i2c/lsi,api2c.yaml
Normal file
52
Documentation/devicetree/bindings/i2c/lsi,api2c.yaml
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i2c/lsi,api2c.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: LSI Axxia I2C Controller
|
||||
|
||||
maintainers:
|
||||
- Anders Berg <anders.berg@lsi.com>
|
||||
- Chaitanya Sabnis <chaitanya.msabnis@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: lsi,api2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: i2c
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c@10084000 {
|
||||
compatible = "lsi,api2c";
|
||||
reg = <0x10084000 0x1000>;
|
||||
interrupts = <0 19 4>;
|
||||
clocks = <&clk_per>;
|
||||
clock-names = "i2c";
|
||||
clock-frequency = <400000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i2c/qcom,sa8255p-geni-i2c.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm SA8255p QUP GENI I2C Controller
|
||||
|
||||
maintainers:
|
||||
- Praveen Talari <praveen.talari@oss.qualcomm.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: qcom,sa8255p-geni-i2c
|
||||
- items:
|
||||
- const: qcom,sa8797p-geni-i2c
|
||||
- const: qcom,sa8255p-geni-i2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
maxItems: 2
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 2
|
||||
|
||||
power-domain-names:
|
||||
items:
|
||||
- const: power
|
||||
- const: perf
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- power-domains
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/dma/qcom-gpi.h>
|
||||
|
||||
i2c@a90000 {
|
||||
compatible = "qcom,sa8255p-geni-i2c";
|
||||
reg = <0xa90000 0x4000>;
|
||||
interrupts = <GIC_SPI 357 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&gpi_dma0 0 0 QCOM_GPI_I2C>,
|
||||
<&gpi_dma0 1 0 QCOM_GPI_I2C>;
|
||||
dma-names = "tx", "rx";
|
||||
power-domains = <&scmi0_pd 0>, <&scmi0_dvfs 0>;
|
||||
power-domain-names = "power", "perf";
|
||||
};
|
||||
...
|
||||
|
|
@ -56,6 +56,11 @@ properties:
|
|||
- renesas,i2c-r8a779h0 # R-Car V4M
|
||||
- const: renesas,rcar-gen4-i2c # R-Car Gen4
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,i2c-r8a78000 # R-Car X5H
|
||||
- const: renesas,rcar-gen5-i2c # R-Car Gen5
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
|
|
@ -140,6 +145,7 @@ allOf:
|
|||
- renesas,rcar-gen2-i2c
|
||||
- renesas,rcar-gen3-i2c
|
||||
- renesas,rcar-gen4-i2c
|
||||
- renesas,rcar-gen5-i2c
|
||||
then:
|
||||
required:
|
||||
- resets
|
||||
|
|
|
|||
|
|
@ -639,7 +639,7 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap,
|
|||
struct i2c_algo_bit_data *bit_adap = adap->algo_data;
|
||||
int ret;
|
||||
|
||||
if (bit_test) {
|
||||
if (bit_test && !bit_adap->skip_bit_test) {
|
||||
ret = test_bus(adap);
|
||||
if (bit_test >= 2 && ret < 0)
|
||||
return -ENODEV;
|
||||
|
|
|
|||
|
|
@ -793,7 +793,7 @@ config I2C_JZ4780
|
|||
config I2C_K1
|
||||
tristate "SpacemiT K1 I2C adapter"
|
||||
depends on ARCH_SPACEMIT || COMPILE_TEST
|
||||
depends on OF
|
||||
depends on OF && COMMON_CLK
|
||||
default ARCH_SPACEMIT
|
||||
help
|
||||
This option enables support for the I2C interface on the SpacemiT K1
|
||||
|
|
|
|||
|
|
@ -89,6 +89,10 @@ static void amd_asf_process_target(struct work_struct *work)
|
|||
outb_p(reg, ASFDATABNKSEL);
|
||||
cmd = inb_p(ASFINDEX);
|
||||
len = inb_p(ASFDATARWPTR);
|
||||
|
||||
if (len > ASF_BLOCK_MAX_BYTES)
|
||||
return;
|
||||
|
||||
for (idx = 0; idx < len; idx++)
|
||||
data[idx] = inb_p(ASFINDEX);
|
||||
|
||||
|
|
|
|||
|
|
@ -117,8 +117,6 @@
|
|||
/* timeout for pm runtime autosuspend */
|
||||
#define DAVINCI_I2C_PM_TIMEOUT 1000 /* ms */
|
||||
|
||||
#define DAVINCI_I2C_DEFAULT_BUS_FREQ 100000
|
||||
|
||||
struct davinci_i2c_dev {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
|
|
@ -760,7 +758,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
r = device_property_read_u32(&pdev->dev, "clock-frequency", &prop);
|
||||
if (r)
|
||||
prop = DAVINCI_I2C_DEFAULT_BUS_FREQ;
|
||||
prop = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
|
||||
dev->bus_freq = prop / 1000;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,21 @@
|
|||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static LIST_HEAD(i2c_gpio_scl_list);
|
||||
static DEFINE_MUTEX(i2c_gpio_scl_list_lock);
|
||||
static struct lock_class_key i2c_gpio_scl_lock_key;
|
||||
|
||||
struct i2c_gpio_scl_data {
|
||||
struct fwnode_reference_args args;
|
||||
struct gpio_desc *gpio;
|
||||
struct rt_mutex lock;
|
||||
struct list_head list;
|
||||
refcount_t ref;
|
||||
};
|
||||
|
||||
struct i2c_gpio_private_data {
|
||||
struct gpio_desc *sda;
|
||||
struct gpio_desc *scl;
|
||||
struct i2c_gpio_scl_data *scl;
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_algo_bit_data bit_data;
|
||||
struct i2c_gpio_platform_data pdata;
|
||||
|
|
@ -31,6 +43,11 @@ struct i2c_gpio_private_data {
|
|||
#endif
|
||||
};
|
||||
|
||||
static inline struct i2c_gpio_private_data *adap_to_priv(struct i2c_adapter *adap)
|
||||
{
|
||||
return container_of(adap, struct i2c_gpio_private_data, adap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Toggle SDA by changing the output value of the pin. This is only
|
||||
* valid for pins configured as open drain (i.e. setting the value
|
||||
|
|
@ -53,7 +70,7 @@ static void i2c_gpio_setscl_val(void *data, int state)
|
|||
{
|
||||
struct i2c_gpio_private_data *priv = data;
|
||||
|
||||
gpiod_set_value_cansleep(priv->scl, state);
|
||||
gpiod_set_value_cansleep(priv->scl->gpio, state);
|
||||
}
|
||||
|
||||
static int i2c_gpio_getsda(void *data)
|
||||
|
|
@ -67,9 +84,41 @@ static int i2c_gpio_getscl(void *data)
|
|||
{
|
||||
struct i2c_gpio_private_data *priv = data;
|
||||
|
||||
return gpiod_get_value_cansleep(priv->scl);
|
||||
return gpiod_get_value_cansleep(priv->scl->gpio);
|
||||
}
|
||||
|
||||
static void i2c_gpio_lock_bus(struct i2c_adapter *adap, unsigned int flags)
|
||||
{
|
||||
/* Take care about adapter lock. See i2c_adapter_lock_bus() and others. */
|
||||
rt_mutex_lock_nested(&adap->bus_lock, i2c_adapter_depth(adap));
|
||||
rt_mutex_lock(&adap_to_priv(adap)->scl->lock);
|
||||
}
|
||||
|
||||
static int i2c_gpio_trylock_bus(struct i2c_adapter *adap, unsigned int flags)
|
||||
{
|
||||
if (!rt_mutex_trylock(&adap->bus_lock))
|
||||
return 0;
|
||||
|
||||
if (!rt_mutex_trylock(&adap_to_priv(adap)->scl->lock)) {
|
||||
rt_mutex_unlock(&adap->bus_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void i2c_gpio_unlock_bus(struct i2c_adapter *adap, unsigned int flags)
|
||||
{
|
||||
rt_mutex_unlock(&adap_to_priv(adap)->scl->lock);
|
||||
rt_mutex_unlock(&adap->bus_lock);
|
||||
}
|
||||
|
||||
static const struct i2c_lock_operations i2c_gpio_lock_ops = {
|
||||
.lock_bus = i2c_gpio_lock_bus,
|
||||
.trylock_bus = i2c_gpio_trylock_bus,
|
||||
.unlock_bus = i2c_gpio_unlock_bus,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR
|
||||
|
||||
#define setsda(bd, val) ((bd)->setsda((bd)->data, val))
|
||||
|
|
@ -165,14 +214,14 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write
|
|||
static int i2c_gpio_fi_act_on_scl_irq(struct i2c_gpio_private_data *priv,
|
||||
irqreturn_t handler(int, void*))
|
||||
{
|
||||
int ret, irq = gpiod_to_irq(priv->scl);
|
||||
int ret, irq = gpiod_to_irq(priv->scl->gpio);
|
||||
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
|
||||
ret = gpiod_direction_input(priv->scl);
|
||||
ret = gpiod_direction_input(priv->scl->gpio);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
|
|
@ -187,7 +236,7 @@ static int i2c_gpio_fi_act_on_scl_irq(struct i2c_gpio_private_data *priv,
|
|||
|
||||
free_irq(irq, priv);
|
||||
output:
|
||||
ret = gpiod_direction_output(priv->scl, 1) ?: ret;
|
||||
ret = gpiod_direction_output(priv->scl->gpio, 1) ?: ret;
|
||||
unlock:
|
||||
i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
|
||||
|
|
@ -308,13 +357,17 @@ static struct gpio_desc *i2c_gpio_get_desc(struct device *dev,
|
|||
struct gpio_desc *retdesc;
|
||||
int ret;
|
||||
|
||||
retdesc = devm_gpiod_get(dev, con_id, gflags);
|
||||
/*
|
||||
* Don't use resource-managed functions. SCL may be shared across adapters and has
|
||||
* its own lifetime management. SDA uses the same path for consistency.
|
||||
*/
|
||||
retdesc = gpiod_get(dev, con_id, gflags);
|
||||
if (!IS_ERR(retdesc)) {
|
||||
dev_dbg(dev, "got GPIO from name %s\n", con_id);
|
||||
return retdesc;
|
||||
}
|
||||
|
||||
retdesc = devm_gpiod_get_index(dev, NULL, index, gflags);
|
||||
retdesc = gpiod_get_index(dev, NULL, index, gflags);
|
||||
if (!IS_ERR(retdesc)) {
|
||||
dev_dbg(dev, "got GPIO from index %u\n", index);
|
||||
return retdesc;
|
||||
|
|
@ -336,6 +389,134 @@ static struct gpio_desc *i2c_gpio_get_desc(struct device *dev,
|
|||
return retdesc;
|
||||
}
|
||||
|
||||
static struct i2c_gpio_scl_data *i2c_gpio_create_scl(struct device *dev)
|
||||
{
|
||||
struct fwnode_handle *fwnode = dev_fwnode(dev);
|
||||
struct fwnode_reference_args args;
|
||||
struct i2c_gpio_scl_data *scl;
|
||||
bool sharable = false;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* SCL gpios can be shared if they are defined in the devicetree and have at least one
|
||||
* cell in addition to the phandle. This is usually the pin. To support complex designs
|
||||
* with arbitrary of_xlate() mappings handle the node cells opaque.
|
||||
*/
|
||||
if (fwnode) {
|
||||
ret = fwnode_property_get_reference_args(fwnode, "scl-gpios",
|
||||
"#gpio-cells", 0, 0, &args);
|
||||
if (ret)
|
||||
/* try the ancient way */
|
||||
ret = fwnode_property_get_reference_args(fwnode, "gpios",
|
||||
"#gpio-cells", 0, 1, &args);
|
||||
|
||||
if (!ret) {
|
||||
if (args.nargs >= 1)
|
||||
sharable = true;
|
||||
else
|
||||
fwnode_handle_put(args.fwnode);
|
||||
}
|
||||
}
|
||||
|
||||
scl = kzalloc(sizeof(*scl), GFP_KERNEL);
|
||||
if (!scl) {
|
||||
if (sharable)
|
||||
fwnode_handle_put(args.fwnode);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
if (sharable) {
|
||||
scl->args.fwnode = args.fwnode;
|
||||
scl->args.nargs = args.nargs;
|
||||
memcpy(scl->args.args, args.args, sizeof(args.args[0]) * args.nargs);
|
||||
}
|
||||
|
||||
rt_mutex_init(&scl->lock);
|
||||
lockdep_set_class(&scl->lock, &i2c_gpio_scl_lock_key);
|
||||
refcount_set(&scl->ref, 1);
|
||||
|
||||
return scl;
|
||||
}
|
||||
|
||||
static void i2c_gpio_free_scl(struct i2c_gpio_scl_data *scl)
|
||||
{
|
||||
if (scl->args.fwnode)
|
||||
fwnode_handle_put(scl->args.fwnode);
|
||||
kfree(scl);
|
||||
}
|
||||
|
||||
static bool i2c_gpio_scl_matches(struct i2c_gpio_scl_data *a, struct i2c_gpio_scl_data *b)
|
||||
{
|
||||
if (!a->args.fwnode || !b->args.fwnode ||
|
||||
a->args.fwnode != b->args.fwnode || a->args.nargs != b->args.nargs)
|
||||
return false;
|
||||
|
||||
return memcmp(a->args.args, b->args.args, sizeof(a->args.args[0]) * a->args.nargs) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up an existing or create a new shared SCL structure. Optimistic setup sequence always
|
||||
* creates and tries to add a new entry to the list. This uses minimum locking and afterwards
|
||||
* requests the GPIO without a lock held. Concurrent probes for the same SCL pin see the entry
|
||||
* and do not race into a second gpiod_get(). Until everything is setup they terminate with
|
||||
* -EPROBE_DEFER.
|
||||
*/
|
||||
static struct i2c_gpio_scl_data *i2c_gpio_lookup_scl(struct device *dev, enum gpiod_flags gflags)
|
||||
{
|
||||
struct i2c_gpio_scl_data *scl, *new_scl;
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
new_scl = i2c_gpio_create_scl(dev);
|
||||
if (IS_ERR(new_scl))
|
||||
return new_scl;
|
||||
|
||||
scoped_guard(mutex, &i2c_gpio_scl_list_lock) {
|
||||
list_for_each_entry(scl, &i2c_gpio_scl_list, list) {
|
||||
if (!i2c_gpio_scl_matches(scl, new_scl))
|
||||
continue;
|
||||
|
||||
i2c_gpio_free_scl(new_scl);
|
||||
if (!scl->gpio)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
refcount_inc(&scl->ref);
|
||||
if (scl->args.fwnode)
|
||||
dev_dbg(dev, "reusing shared SCL (%pfwP)\n", scl->args.fwnode);
|
||||
|
||||
return scl;
|
||||
}
|
||||
list_add(&new_scl->list, &i2c_gpio_scl_list);
|
||||
}
|
||||
|
||||
gpio = i2c_gpio_get_desc(dev, "scl", 1, gflags);
|
||||
if (IS_ERR(gpio)) {
|
||||
scoped_guard(mutex, &i2c_gpio_scl_list_lock)
|
||||
list_del(&new_scl->list);
|
||||
i2c_gpio_free_scl(new_scl);
|
||||
|
||||
return ERR_CAST(gpio);
|
||||
}
|
||||
|
||||
scoped_guard(mutex, &i2c_gpio_scl_list_lock)
|
||||
new_scl->gpio = gpio;
|
||||
|
||||
if (new_scl->args.fwnode)
|
||||
dev_dbg(dev, "registered shared SCL (%pfwP)\n", new_scl->args.fwnode);
|
||||
|
||||
return new_scl;
|
||||
}
|
||||
|
||||
static void i2c_gpio_cleanup_scl(struct i2c_gpio_scl_data *scl)
|
||||
{
|
||||
if (!refcount_dec_and_mutex_lock(&scl->ref, &i2c_gpio_scl_list_lock))
|
||||
return;
|
||||
|
||||
list_del(&scl->list);
|
||||
mutex_unlock(&i2c_gpio_scl_list_lock);
|
||||
gpiod_put(scl->gpio);
|
||||
i2c_gpio_free_scl(scl);
|
||||
}
|
||||
|
||||
static int i2c_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_gpio_private_data *priv;
|
||||
|
|
@ -386,15 +567,18 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
|||
gflags = GPIOD_OUT_HIGH;
|
||||
else
|
||||
gflags = GPIOD_OUT_HIGH_OPEN_DRAIN;
|
||||
priv->scl = i2c_gpio_get_desc(dev, "scl", 1, gflags);
|
||||
if (IS_ERR(priv->scl))
|
||||
return PTR_ERR(priv->scl);
|
||||
priv->scl = i2c_gpio_lookup_scl(dev, gflags);
|
||||
if (IS_ERR(priv->scl)) {
|
||||
ret = PTR_ERR(priv->scl);
|
||||
goto err_cleanup_sda;
|
||||
}
|
||||
|
||||
if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl))
|
||||
if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl->gpio))
|
||||
dev_warn(dev, "Slow GPIO pins might wreak havoc into I2C/SMBus bus timing");
|
||||
else
|
||||
bit_data->can_do_atomic = true;
|
||||
|
||||
bit_data->skip_bit_test = !!priv->scl->args.fwnode;
|
||||
bit_data->setsda = i2c_gpio_setsda_val;
|
||||
bit_data->setscl = i2c_gpio_setscl_val;
|
||||
|
||||
|
|
@ -423,6 +607,8 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
|||
else
|
||||
snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);
|
||||
|
||||
/* Always use shared SCL aware locking */
|
||||
adap->lock_ops = &i2c_gpio_lock_ops;
|
||||
adap->algo_data = bit_data;
|
||||
adap->class = I2C_CLASS_HWMON;
|
||||
adap->dev.parent = dev;
|
||||
|
|
@ -431,7 +617,7 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
|||
adap->nr = pdev->id;
|
||||
ret = i2c_bit_add_numbered_bus(adap);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_cleanup_scl;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
|
|
@ -441,13 +627,20 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
|||
* from the descriptor, then provide that instead.
|
||||
*/
|
||||
dev_info(dev, "using lines %u (SDA) and %u (SCL%s)\n",
|
||||
desc_to_gpio(priv->sda), desc_to_gpio(priv->scl),
|
||||
desc_to_gpio(priv->sda), desc_to_gpio(priv->scl->gpio),
|
||||
pdata->scl_is_output_only
|
||||
? ", no clock stretching" : "");
|
||||
|
||||
i2c_gpio_fault_injector_init(pdev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_cleanup_scl:
|
||||
i2c_gpio_cleanup_scl(priv->scl);
|
||||
err_cleanup_sda:
|
||||
gpiod_put(priv->sda);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void i2c_gpio_remove(struct platform_device *pdev)
|
||||
|
|
@ -459,6 +652,8 @@ static void i2c_gpio_remove(struct platform_device *pdev)
|
|||
adap = &priv->adap;
|
||||
|
||||
i2c_del_adapter(adap);
|
||||
i2c_gpio_cleanup_scl(priv->scl);
|
||||
gpiod_put(priv->sda);
|
||||
}
|
||||
|
||||
static const struct of_device_id i2c_gpio_dt_ids[] = {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
|
|
@ -17,6 +19,8 @@
|
|||
#define SPACEMIT_ISR 0x4 /* Status register */
|
||||
#define SPACEMIT_IDBR 0xc /* Data buffer register */
|
||||
#define SPACEMIT_IRCR 0x18 /* Reset cycle counter */
|
||||
#define SPACEMIT_ILCR 0x10 /* Load Count Register */
|
||||
#define SPACEMIT_IWCR 0x14 /* Wait Count Register */
|
||||
#define SPACEMIT_IBMR 0x1c /* Bus monitor register */
|
||||
|
||||
/* SPACEMIT_ICR register fields */
|
||||
|
|
@ -88,12 +92,22 @@
|
|||
#define SPACEMIT_BMR_SDA BIT(0) /* SDA line level */
|
||||
#define SPACEMIT_BMR_SCL BIT(1) /* SCL line level */
|
||||
|
||||
#define SPACEMIT_LCR_LV_STANDARD_MASK GENMASK(8, 0)
|
||||
#define SPACEMIT_LCR_LV_FAST_MASK GENMASK(17, 9)
|
||||
|
||||
/* SPACEMIT_IWCR register fields */
|
||||
#define SPACEMIT_WCR_COUNT GENMASK(4, 0)
|
||||
#define SPACEMIT_WCR_HS_COUNT1 GENMASK(9, 5)
|
||||
#define SPACEMIT_WCR_HS_COUNT2 GENMASK(14, 10)
|
||||
|
||||
/* Required by I2C IP for correct SCL timing */
|
||||
#define SPACEMIT_IWCR_INIT_VALUE (FIELD_PREP(SPACEMIT_WCR_COUNT, 10) | \
|
||||
FIELD_PREP(SPACEMIT_WCR_HS_COUNT1, 1) | \
|
||||
FIELD_PREP(SPACEMIT_WCR_HS_COUNT2, 5))
|
||||
|
||||
/* i2c bus recover timeout: us */
|
||||
#define SPACEMIT_I2C_BUS_BUSY_TIMEOUT 100000
|
||||
|
||||
#define SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ 100000 /* Hz */
|
||||
#define SPACEMIT_I2C_MAX_FAST_MODE_FREQ 400000 /* Hz */
|
||||
|
||||
#define SPACEMIT_SR_ERR (SPACEMIT_SR_BED | SPACEMIT_SR_RXOV | SPACEMIT_SR_ALD)
|
||||
|
||||
#define SPACEMIT_BUS_RESET_CLK_CNT_MAX 9
|
||||
|
|
@ -109,11 +123,20 @@ enum spacemit_i2c_state {
|
|||
SPACEMIT_STATE_WRITE,
|
||||
};
|
||||
|
||||
enum spacemit_i2c_mode {
|
||||
SPACEMIT_MODE_STANDARD,
|
||||
SPACEMIT_MODE_FAST
|
||||
};
|
||||
|
||||
/* i2c-spacemit driver's main struct */
|
||||
struct spacemit_i2c_dev {
|
||||
struct device *dev;
|
||||
struct i2c_adapter adapt;
|
||||
|
||||
struct clk_hw scl_clk_hw;
|
||||
struct clk *scl_clk;
|
||||
enum spacemit_i2c_mode mode;
|
||||
|
||||
/* hardware resources */
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
|
|
@ -135,6 +158,85 @@ struct spacemit_i2c_dev {
|
|||
u32 status;
|
||||
};
|
||||
|
||||
static void spacemit_i2c_scl_clk_disable_unprepare(void *data)
|
||||
{
|
||||
clk_disable_unprepare(data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the ILCR divider value (lv) from the target SCL rate.
|
||||
*
|
||||
* Hardware timing formulas:
|
||||
* - standard mode: SCL = FCLK / (2 * SLV + 8)
|
||||
* - fast mode: SCL = FCLK / (2 * FLV + 10)
|
||||
*/
|
||||
static u32 spacemit_i2c_calc_lv(struct spacemit_i2c_dev *i2c,
|
||||
unsigned long parent_rate,
|
||||
unsigned long target_rate)
|
||||
{
|
||||
u32 offset, denom;
|
||||
|
||||
offset = (i2c->mode == SPACEMIT_MODE_STANDARD) ? 8 : 10;
|
||||
denom = DIV_ROUND_CLOSEST(parent_rate, target_rate);
|
||||
|
||||
return (denom <= offset) ? 0 : DIV_ROUND_CLOSEST(denom - offset, 2);
|
||||
}
|
||||
|
||||
static int spacemit_i2c_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct spacemit_i2c_dev *i2c = container_of(hw, struct spacemit_i2c_dev, scl_clk_hw);
|
||||
u32 lv, lcr, mask;
|
||||
|
||||
lv = spacemit_i2c_calc_lv(i2c, parent_rate, rate);
|
||||
|
||||
mask = (i2c->mode == SPACEMIT_MODE_STANDARD) ?
|
||||
SPACEMIT_LCR_LV_STANDARD_MASK : SPACEMIT_LCR_LV_FAST_MASK;
|
||||
|
||||
lcr = readl(i2c->base + SPACEMIT_ILCR);
|
||||
lcr &= ~mask;
|
||||
lcr |= field_prep(mask, lv);
|
||||
writel(lcr, i2c->base + SPACEMIT_ILCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spacemit_i2c_clk_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct spacemit_i2c_dev *i2c = container_of(hw, struct spacemit_i2c_dev, scl_clk_hw);
|
||||
u32 lv, offset;
|
||||
|
||||
lv = spacemit_i2c_calc_lv(i2c, req->best_parent_rate, req->rate);
|
||||
offset = (i2c->mode == SPACEMIT_MODE_STANDARD) ? 8 : 10;
|
||||
req->rate = DIV_ROUND_CLOSEST(req->best_parent_rate, lv * 2 + offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long spacemit_i2c_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct spacemit_i2c_dev *i2c = container_of(hw, struct spacemit_i2c_dev, scl_clk_hw);
|
||||
u32 lcr, lv = 0;
|
||||
|
||||
lcr = readl(i2c->base + SPACEMIT_ILCR);
|
||||
|
||||
if (i2c->mode == SPACEMIT_MODE_STANDARD) {
|
||||
lv = FIELD_GET(SPACEMIT_LCR_LV_STANDARD_MASK, lcr);
|
||||
return DIV_ROUND_CLOSEST(parent_rate, lv * 2 + 8);
|
||||
}
|
||||
|
||||
lv = FIELD_GET(SPACEMIT_LCR_LV_FAST_MASK, lcr);
|
||||
return DIV_ROUND_CLOSEST(parent_rate, lv * 2 + 10);
|
||||
}
|
||||
|
||||
static const struct clk_ops spacemit_i2c_clk_ops = {
|
||||
.set_rate = spacemit_i2c_clk_set_rate,
|
||||
.determine_rate = spacemit_i2c_clk_determine_rate,
|
||||
.recalc_rate = spacemit_i2c_clk_recalc_rate,
|
||||
};
|
||||
|
||||
static void spacemit_i2c_enable(struct spacemit_i2c_dev *i2c)
|
||||
{
|
||||
u32 val;
|
||||
|
|
@ -153,6 +255,28 @@ static void spacemit_i2c_disable(struct spacemit_i2c_dev *i2c)
|
|||
writel(val, i2c->base + SPACEMIT_ICR);
|
||||
}
|
||||
|
||||
static int spacemit_i2c_register_scl_clk(struct spacemit_i2c_dev *i2c)
|
||||
{
|
||||
struct clk_init_data init = {};
|
||||
char name[64];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(name, sizeof(name), "%s_scl_clk", dev_name(i2c->dev));
|
||||
if (ret >= ARRAY_SIZE(name))
|
||||
dev_warn(i2c->dev, "scl clock name truncated");
|
||||
|
||||
init.name = name;
|
||||
init.ops = &spacemit_i2c_clk_ops;
|
||||
init.parent_data = (struct clk_parent_data[]) {
|
||||
{ .fw_name = "func" },
|
||||
};
|
||||
init.num_parents = 1;
|
||||
|
||||
i2c->scl_clk_hw.init = &init;
|
||||
|
||||
return devm_clk_hw_register(i2c->dev, &i2c->scl_clk_hw);
|
||||
}
|
||||
|
||||
static void spacemit_i2c_reset(struct spacemit_i2c_dev *i2c)
|
||||
{
|
||||
writel(SPACEMIT_CR_UR, i2c->base + SPACEMIT_ICR);
|
||||
|
|
@ -286,7 +410,7 @@ static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c)
|
|||
val |= SPACEMIT_CR_MSDIE;
|
||||
}
|
||||
|
||||
if (i2c->clock_freq == SPACEMIT_I2C_MAX_FAST_MODE_FREQ)
|
||||
if (i2c->mode == SPACEMIT_MODE_FAST)
|
||||
val |= SPACEMIT_CR_MODE_FAST;
|
||||
|
||||
/* disable response to general call */
|
||||
|
|
@ -309,6 +433,14 @@ static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c)
|
|||
writel(val, i2c->base + SPACEMIT_IRCR);
|
||||
|
||||
spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK);
|
||||
|
||||
/*
|
||||
* Initialize IWCR to the value specified by the I2C IP designer.
|
||||
* The SCL frequency formulas (SCL = FCLK / (2*SLV+8) for standard
|
||||
* mode, SCL = FCLK / (2*FLV+10) for fast mode) are only valid when
|
||||
* IWCR contains this specific value.
|
||||
*/
|
||||
writel(SPACEMIT_IWCR_INIT_VALUE, i2c->base + SPACEMIT_IWCR);
|
||||
}
|
||||
|
||||
static void spacemit_i2c_start(struct spacemit_i2c_dev *i2c)
|
||||
|
|
@ -698,19 +830,18 @@ static int spacemit_i2c_probe(struct platform_device *pdev)
|
|||
if (!i2c)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_property_read_u32(of_node, "clock-frequency", &i2c->clock_freq);
|
||||
if (ret && ret != -EINVAL)
|
||||
dev_warn(dev, "failed to read clock-frequency property: %d\n", ret);
|
||||
of_property_read_u32(of_node, "clock-frequency", &i2c->clock_freq);
|
||||
|
||||
/* For now, this driver doesn't support high-speed. */
|
||||
if (!i2c->clock_freq || i2c->clock_freq > SPACEMIT_I2C_MAX_FAST_MODE_FREQ) {
|
||||
dev_warn(dev, "unsupported clock frequency %u; using %u\n",
|
||||
i2c->clock_freq, SPACEMIT_I2C_MAX_FAST_MODE_FREQ);
|
||||
i2c->clock_freq = SPACEMIT_I2C_MAX_FAST_MODE_FREQ;
|
||||
} else if (i2c->clock_freq < SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ) {
|
||||
dev_warn(dev, "unsupported clock frequency %u; using %u\n",
|
||||
i2c->clock_freq, SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ);
|
||||
i2c->clock_freq = SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ;
|
||||
if (i2c->clock_freq > I2C_MAX_STANDARD_MODE_FREQ &&
|
||||
i2c->clock_freq <= I2C_MAX_FAST_MODE_FREQ) {
|
||||
i2c->mode = SPACEMIT_MODE_FAST;
|
||||
} else if (i2c->clock_freq && i2c->clock_freq <= I2C_MAX_STANDARD_MODE_FREQ) {
|
||||
i2c->mode = SPACEMIT_MODE_STANDARD;
|
||||
} else {
|
||||
dev_info(dev, "clock-frequency not set or out of range, using fast mode\n");
|
||||
i2c->mode = SPACEMIT_MODE_FAST;
|
||||
i2c->clock_freq = I2C_MAX_FAST_MODE_FREQ;
|
||||
}
|
||||
|
||||
i2c->dev = &pdev->dev;
|
||||
|
|
@ -727,6 +858,15 @@ static int spacemit_i2c_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(clk))
|
||||
return dev_err_probe(dev, PTR_ERR(clk), "failed to enable func clock");
|
||||
|
||||
ret = spacemit_i2c_register_scl_clk(i2c);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to register scl clock\n");
|
||||
|
||||
i2c->scl_clk = devm_clk_hw_get_clk(dev, &i2c->scl_clk_hw, "scl");
|
||||
if (IS_ERR(i2c->scl_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(i2c->scl_clk),
|
||||
"failed to get scl clock\n");
|
||||
|
||||
clk = devm_clk_get_enabled(dev, "bus");
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(dev, PTR_ERR(clk), "failed to enable bus clock");
|
||||
|
|
@ -736,6 +876,19 @@ static int spacemit_i2c_probe(struct platform_device *pdev)
|
|||
return dev_err_probe(dev, PTR_ERR(rst),
|
||||
"failed to acquire deasserted reset\n");
|
||||
|
||||
ret = clk_set_rate(i2c->scl_clk, i2c->clock_freq);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to set rate for SCL clock");
|
||||
|
||||
ret = clk_prepare_enable(i2c->scl_clk);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to prepare and enable clock");
|
||||
|
||||
ret = devm_add_action_or_reset(dev, spacemit_i2c_scl_clk_disable_unprepare,
|
||||
i2c->scl_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spacemit_i2c_reset(i2c);
|
||||
|
||||
i2c_set_adapdata(&i2c->adapt, i2c);
|
||||
|
|
|
|||
|
|
@ -565,10 +565,10 @@ static int mchp_corei2c_probe(struct platform_device *pdev)
|
|||
&idev->bus_clk_rate);
|
||||
if (ret || !idev->bus_clk_rate) {
|
||||
dev_info(&pdev->dev, "default to 100kHz\n");
|
||||
idev->bus_clk_rate = 100000;
|
||||
idev->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
}
|
||||
|
||||
if (idev->bus_clk_rate > 400000)
|
||||
if (idev->bus_clk_rate > I2C_MAX_FAST_MODE_FREQ)
|
||||
return dev_err_probe(&pdev->dev, -EINVAL,
|
||||
"clock-frequency too high: %d\n",
|
||||
idev->bus_clk_rate);
|
||||
|
|
|
|||
|
|
@ -1050,9 +1050,9 @@ static int nmk_i2c_eyeq5_probe(struct nmk_i2c_dev *priv)
|
|||
if (id >= ARRAY_SIZE(nmk_i2c_eyeq5_masks))
|
||||
return -ENOENT;
|
||||
|
||||
if (priv->clk_freq <= 400000)
|
||||
if (priv->clk_freq <= I2C_MAX_FAST_MODE_FREQ)
|
||||
speed_mode = I2C_EYEQ5_SPEED_FAST;
|
||||
else if (priv->clk_freq <= 1000000)
|
||||
else if (priv->clk_freq <= I2C_MAX_FAST_MODE_PLUS_FREQ)
|
||||
speed_mode = I2C_EYEQ5_SPEED_FAST_PLUS;
|
||||
else
|
||||
speed_mode = I2C_EYEQ5_SPEED_HIGH_SPEED;
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ static inline void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data)
|
|||
octeon_i2c_writeq_flush(data, i2c->twsi_base + OCTEON_REG_TWSI_INT(i2c));
|
||||
}
|
||||
|
||||
#define IS_LS_FREQ(twsi_freq) ((twsi_freq) <= 400000)
|
||||
#define IS_LS_FREQ(twsi_freq) ((twsi_freq) <= I2C_MAX_FAST_MODE_FREQ)
|
||||
#define PCI_SUBSYS_DEVID_9XXX 0xB
|
||||
#define PCI_SUBSYS_MASK GENMASK(15, 12)
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
#include <linux/of.h>
|
||||
|
||||
#define I2C_PNX_TIMEOUT_DEFAULT 10 /* msec */
|
||||
#define I2C_PNX_SPEED_KHZ_DEFAULT 100
|
||||
#define I2C_PNX_REGION_SIZE 0x100
|
||||
|
||||
struct i2c_pnx_mif {
|
||||
|
|
@ -606,12 +605,12 @@ static DEFINE_SIMPLE_DEV_PM_OPS(i2c_pnx_pm,
|
|||
|
||||
static int i2c_pnx_probe(struct platform_device *pdev)
|
||||
{
|
||||
u32 speed = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
unsigned long tmp;
|
||||
int ret = 0;
|
||||
struct i2c_pnx_algo_data *alg_data;
|
||||
unsigned long freq;
|
||||
struct resource *res;
|
||||
u32 speed = I2C_PNX_SPEED_KHZ_DEFAULT * 1000;
|
||||
|
||||
alg_data = devm_kzalloc(&pdev->dev, sizeof(*alg_data), GFP_KERNEL);
|
||||
if (!alg_data)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/qcom_geni_i2c.h>
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dmaengine.h>
|
||||
|
|
@ -56,7 +59,8 @@
|
|||
|
||||
enum geni_i2c_err_code {
|
||||
GP_IRQ0,
|
||||
NACK,
|
||||
ADDR_NACK,
|
||||
DATA_NACK,
|
||||
GP_IRQ2,
|
||||
BUS_PROTO,
|
||||
ARB_LOST,
|
||||
|
|
@ -67,16 +71,26 @@ enum geni_i2c_err_code {
|
|||
GENI_TIMEOUT,
|
||||
};
|
||||
|
||||
#define DM_I2C_CB_ERR ((BIT(NACK) | BIT(BUS_PROTO) | BIT(ARB_LOST)) \
|
||||
#define DM_I2C_CB_ERR ((BIT(ADDR_NACK) | BIT(BUS_PROTO) | BIT(ARB_LOST)) \
|
||||
<< 5)
|
||||
|
||||
#define I2C_AUTO_SUSPEND_DELAY 250
|
||||
#define PACKING_BYTES_PW 4
|
||||
|
||||
#define ABORT_TIMEOUT HZ
|
||||
#define CANCEL_TIMEOUT HZ
|
||||
#define XFER_TIMEOUT HZ
|
||||
#define RST_TIMEOUT HZ
|
||||
|
||||
struct geni_i2c_desc {
|
||||
bool no_dma_support;
|
||||
unsigned int tx_fifo_depth;
|
||||
int (*resources_init)(struct geni_se *se);
|
||||
int (*set_rate)(struct geni_se *se, unsigned long freq);
|
||||
int (*power_on)(struct geni_se *se);
|
||||
int (*power_off)(struct geni_se *se);
|
||||
};
|
||||
|
||||
#define QCOM_I2C_MIN_NUM_OF_MSGS_MULTI_DESC 2
|
||||
|
||||
/**
|
||||
|
|
@ -103,11 +117,14 @@ struct geni_i2c_dev {
|
|||
int err;
|
||||
struct i2c_adapter adap;
|
||||
struct completion done;
|
||||
struct completion abort_done;
|
||||
struct completion cancel_done;
|
||||
struct completion tx_reset_done;
|
||||
struct completion rx_reset_done;
|
||||
struct i2c_msg *cur;
|
||||
int cur_wr;
|
||||
int cur_rd;
|
||||
spinlock_t lock;
|
||||
struct clk *core_clk;
|
||||
u32 clk_freq_out;
|
||||
const struct geni_i2c_clk_fld *clk_fld;
|
||||
void *dma_buf;
|
||||
|
|
@ -117,17 +134,10 @@ struct geni_i2c_dev {
|
|||
struct dma_chan *rx_c;
|
||||
bool no_dma;
|
||||
bool gpi_mode;
|
||||
bool abort_done;
|
||||
bool is_tx_multi_desc_xfer;
|
||||
u32 num_msgs;
|
||||
struct geni_i2c_gpi_multi_desc_xfer i2c_multi_desc_config;
|
||||
};
|
||||
|
||||
struct geni_i2c_desc {
|
||||
bool has_core_clk;
|
||||
char *icc_ddr;
|
||||
bool no_dma_support;
|
||||
unsigned int tx_fifo_depth;
|
||||
const struct geni_i2c_desc *dev_data;
|
||||
};
|
||||
|
||||
struct geni_i2c_err_log {
|
||||
|
|
@ -137,7 +147,8 @@ struct geni_i2c_err_log {
|
|||
|
||||
static const struct geni_i2c_err_log gi2c_log[] = {
|
||||
[GP_IRQ0] = {-EIO, "Unknown I2C err GP_IRQ0"},
|
||||
[NACK] = {-ENXIO, "NACK: slv unresponsive, check its power/reset-ln"},
|
||||
[ADDR_NACK] = {-ENXIO, "NACK: target device unresponsive, check its power/reset-ln"},
|
||||
[DATA_NACK] = {-EIO, "Data NACK: TX transfer NACK"},
|
||||
[GP_IRQ2] = {-EIO, "Unknown I2C err GP IRQ2"},
|
||||
[BUS_PROTO] = {-EPROTO, "Bus proto err, noisy/unexpected start/stop"},
|
||||
[ARB_LOST] = {-EAGAIN, "Bus arbitration lost, clock line undriveable"},
|
||||
|
|
@ -202,8 +213,9 @@ static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c)
|
||||
static int qcom_geni_i2c_conf(struct geni_se *se, unsigned long freq)
|
||||
{
|
||||
struct geni_i2c_dev *gi2c = dev_get_drvdata(se->dev);
|
||||
const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
|
||||
u32 val;
|
||||
|
||||
|
|
@ -216,6 +228,10 @@ static void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c)
|
|||
val |= itr->t_low_cnt << LOW_COUNTER_SHFT;
|
||||
val |= itr->t_cycle_cnt;
|
||||
writel_relaxed(val, gi2c->se.base + SE_I2C_SCL_COUNTERS);
|
||||
trace_geni_i2c_bus_setup(gi2c->se.dev, gi2c->clk_freq_out,
|
||||
itr->clk_div, itr->t_high_cnt,
|
||||
itr->t_low_cnt, itr->t_cycle_cnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void geni_i2c_err_misc(struct geni_i2c_dev *gi2c)
|
||||
|
|
@ -248,11 +264,12 @@ static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
|
|||
dev_dbg(gi2c->se.dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
|
||||
gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
|
||||
|
||||
trace_geni_i2c_err(gi2c->se.dev, gi2c_log[err].err, gi2c_log[err].msg);
|
||||
|
||||
switch (err) {
|
||||
case GENI_ABORT_DONE:
|
||||
gi2c->abort_done = true;
|
||||
break;
|
||||
case NACK:
|
||||
case ADDR_NACK:
|
||||
case DATA_NACK:
|
||||
case GENI_TIMEOUT:
|
||||
dev_dbg(gi2c->se.dev, "%s\n", gi2c_log[err].msg);
|
||||
break;
|
||||
|
|
@ -263,6 +280,14 @@ static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
|
|||
}
|
||||
}
|
||||
|
||||
static void geni_i2c_check_addr_data_nack(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
if (!readl_relaxed(gi2c->se.base + SE_GENI_M_GP_LENGTH))
|
||||
geni_i2c_err(gi2c, ADDR_NACK);
|
||||
else if (!(gi2c->cur->flags & I2C_M_RD))
|
||||
geni_i2c_err(gi2c, DATA_NACK);
|
||||
}
|
||||
|
||||
static irqreturn_t geni_i2c_irq(int irq, void *dev)
|
||||
{
|
||||
struct geni_i2c_dev *gi2c = dev;
|
||||
|
|
@ -284,11 +309,13 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev)
|
|||
dma = readl_relaxed(base + SE_GENI_DMA_MODE_EN);
|
||||
cur = gi2c->cur;
|
||||
|
||||
trace_geni_i2c_irq(gi2c->se.dev, m_stat, rx_st, dm_tx_st, dm_rx_st);
|
||||
|
||||
if (!cur ||
|
||||
m_stat & (M_CMD_FAILURE_EN | M_CMD_ABORT_EN) ||
|
||||
dm_rx_st & (DM_I2C_CB_ERR)) {
|
||||
if (m_stat & M_GP_IRQ_1_EN)
|
||||
geni_i2c_err(gi2c, NACK);
|
||||
geni_i2c_check_addr_data_nack(gi2c);
|
||||
if (m_stat & M_GP_IRQ_3_EN)
|
||||
geni_i2c_err(gi2c, BUS_PROTO);
|
||||
if (m_stat & M_GP_IRQ_4_EN)
|
||||
|
|
@ -353,10 +380,18 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev)
|
|||
writel_relaxed(dm_rx_st, base + SE_DMA_RX_IRQ_CLR);
|
||||
|
||||
/* if this is err with done-bit not set, handle that through timeout. */
|
||||
if (m_stat & M_CMD_DONE_EN || m_stat & M_CMD_ABORT_EN ||
|
||||
dm_tx_st & TX_DMA_DONE || dm_tx_st & TX_RESET_DONE ||
|
||||
dm_rx_st & RX_DMA_DONE || dm_rx_st & RX_RESET_DONE)
|
||||
if (m_stat & M_CMD_DONE_EN ||
|
||||
dm_tx_st & TX_DMA_DONE ||
|
||||
dm_rx_st & RX_DMA_DONE)
|
||||
complete(&gi2c->done);
|
||||
if (m_stat & M_CMD_CANCEL_EN)
|
||||
complete(&gi2c->cancel_done);
|
||||
if (m_stat & M_CMD_ABORT_EN)
|
||||
complete(&gi2c->abort_done);
|
||||
if (dm_tx_st & TX_RESET_DONE)
|
||||
complete(&gi2c->tx_reset_done);
|
||||
if (dm_rx_st & RX_RESET_DONE)
|
||||
complete(&gi2c->rx_reset_done);
|
||||
|
||||
spin_unlock(&gi2c->lock);
|
||||
|
||||
|
|
@ -368,48 +403,59 @@ static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c)
|
|||
unsigned long time_left = ABORT_TIMEOUT;
|
||||
unsigned long flags;
|
||||
|
||||
reinit_completion(&gi2c->abort_done);
|
||||
|
||||
spin_lock_irqsave(&gi2c->lock, flags);
|
||||
geni_i2c_err(gi2c, GENI_TIMEOUT);
|
||||
gi2c->cur = NULL;
|
||||
gi2c->abort_done = false;
|
||||
geni_se_abort_m_cmd(&gi2c->se);
|
||||
spin_unlock_irqrestore(&gi2c->lock, flags);
|
||||
|
||||
do {
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, time_left);
|
||||
} while (!gi2c->abort_done && time_left);
|
||||
|
||||
time_left = wait_for_completion_timeout(&gi2c->abort_done, time_left);
|
||||
if (!time_left)
|
||||
dev_err(gi2c->se.dev, "Timeout abort_m_cmd\n");
|
||||
}
|
||||
|
||||
static void geni_i2c_cancel_xfer(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
unsigned long time_left = msecs_to_jiffies(CANCEL_TIMEOUT);
|
||||
unsigned long flags;
|
||||
|
||||
reinit_completion(&gi2c->cancel_done);
|
||||
|
||||
spin_lock_irqsave(&gi2c->lock, flags);
|
||||
if (!gi2c->err)
|
||||
geni_i2c_err(gi2c, GENI_TIMEOUT);
|
||||
gi2c->cur = NULL;
|
||||
geni_se_cancel_m_cmd(&gi2c->se);
|
||||
spin_unlock_irqrestore(&gi2c->lock, flags);
|
||||
|
||||
time_left = wait_for_completion_timeout(&gi2c->cancel_done, time_left);
|
||||
if (!time_left) {
|
||||
dev_err(gi2c->se.dev, "Timeout cancel_m_cmd\n");
|
||||
geni_i2c_abort_xfer(gi2c);
|
||||
}
|
||||
}
|
||||
|
||||
static void geni_i2c_rx_fsm_rst(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long time_left = RST_TIMEOUT;
|
||||
|
||||
reinit_completion(&gi2c->rx_reset_done);
|
||||
writel_relaxed(1, gi2c->se.base + SE_DMA_RX_FSM_RST);
|
||||
do {
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, time_left);
|
||||
val = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
|
||||
} while (!(val & RX_RESET_DONE) && time_left);
|
||||
|
||||
if (!(val & RX_RESET_DONE))
|
||||
time_left = wait_for_completion_timeout(&gi2c->rx_reset_done, time_left);
|
||||
if (!time_left)
|
||||
dev_err(gi2c->se.dev, "Timeout resetting RX_FSM\n");
|
||||
}
|
||||
|
||||
static void geni_i2c_tx_fsm_rst(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long time_left = RST_TIMEOUT;
|
||||
|
||||
reinit_completion(&gi2c->tx_reset_done);
|
||||
writel_relaxed(1, gi2c->se.base + SE_DMA_TX_FSM_RST);
|
||||
do {
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, time_left);
|
||||
val = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
|
||||
} while (!(val & TX_RESET_DONE) && time_left);
|
||||
|
||||
if (!(val & TX_RESET_DONE))
|
||||
time_left = wait_for_completion_timeout(&gi2c->tx_reset_done, time_left);
|
||||
if (!time_left)
|
||||
dev_err(gi2c->se.dev, "Timeout resetting TX_FSM\n");
|
||||
}
|
||||
|
||||
|
|
@ -418,7 +464,7 @@ static void geni_i2c_rx_msg_cleanup(struct geni_i2c_dev *gi2c,
|
|||
{
|
||||
gi2c->cur_rd = 0;
|
||||
if (gi2c->dma_buf) {
|
||||
if (gi2c->err)
|
||||
if (gi2c->err && gi2c->err != gi2c_log[ADDR_NACK].err)
|
||||
geni_i2c_rx_fsm_rst(gi2c);
|
||||
geni_se_rx_dma_unprep(&gi2c->se, gi2c->dma_addr, gi2c->xfer_len);
|
||||
i2c_put_dma_safe_msg_buf(gi2c->dma_buf, cur, !gi2c->err);
|
||||
|
|
@ -430,7 +476,7 @@ static void geni_i2c_tx_msg_cleanup(struct geni_i2c_dev *gi2c,
|
|||
{
|
||||
gi2c->cur_wr = 0;
|
||||
if (gi2c->dma_buf) {
|
||||
if (gi2c->err)
|
||||
if (gi2c->err && gi2c->err != gi2c_log[ADDR_NACK].err)
|
||||
geni_i2c_tx_fsm_rst(gi2c);
|
||||
geni_se_tx_dma_unprep(&gi2c->se, gi2c->dma_addr, gi2c->xfer_len);
|
||||
i2c_put_dma_safe_msg_buf(gi2c->dma_buf, cur, !gi2c->err);
|
||||
|
|
@ -468,8 +514,8 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
|||
|
||||
cur = gi2c->cur;
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
|
||||
if (!time_left)
|
||||
geni_i2c_abort_xfer(gi2c);
|
||||
if (!time_left || (gi2c->err && gi2c->err != gi2c_log[ADDR_NACK].err))
|
||||
geni_i2c_cancel_xfer(gi2c);
|
||||
|
||||
geni_i2c_rx_msg_cleanup(gi2c, cur);
|
||||
|
||||
|
|
@ -510,8 +556,8 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
|||
|
||||
cur = gi2c->cur;
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
|
||||
if (!time_left)
|
||||
geni_i2c_abort_xfer(gi2c);
|
||||
if (!time_left || (gi2c->err && gi2c->err != gi2c_log[ADDR_NACK].err))
|
||||
geni_i2c_cancel_xfer(gi2c);
|
||||
|
||||
geni_i2c_tx_msg_cleanup(gi2c, cur);
|
||||
|
||||
|
|
@ -784,6 +830,10 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
|
|||
peripheral.set_config = 1;
|
||||
peripheral.multi_msg = false;
|
||||
|
||||
trace_geni_i2c_bus_setup(gi2c->se.dev, gi2c->clk_freq_out,
|
||||
itr->clk_div, itr->t_high_cnt,
|
||||
itr->t_low_cnt, itr->t_cycle_cnt);
|
||||
|
||||
gi2c->num_msgs = num;
|
||||
gi2c->is_tx_multi_desc_xfer = false;
|
||||
|
||||
|
|
@ -822,6 +872,7 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
|
|||
for (i = 0; i < num; i++) {
|
||||
gi2c->cur = &msgs[i];
|
||||
gi2c->err = 0;
|
||||
reinit_completion(&gi2c->done);
|
||||
dev_dbg(gi2c->se.dev, "msg[%d].len:%d\n", i, gi2c->cur->len);
|
||||
|
||||
peripheral.stretch = 0;
|
||||
|
|
@ -891,6 +942,8 @@ static int geni_i2c_fifo_xfer(struct geni_i2c_dev *gi2c,
|
|||
m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
|
||||
|
||||
gi2c->cur = &msgs[i];
|
||||
gi2c->err = 0;
|
||||
reinit_completion(&gi2c->done);
|
||||
if (msgs[i].flags & I2C_M_RD)
|
||||
ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
|
||||
else
|
||||
|
|
@ -910,8 +963,6 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
|
|||
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
|
||||
int ret;
|
||||
|
||||
gi2c->err = 0;
|
||||
reinit_completion(&gi2c->done);
|
||||
ret = pm_runtime_get_sync(gi2c->se.dev);
|
||||
if (ret < 0) {
|
||||
dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
|
||||
|
|
@ -921,7 +972,9 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
|
|||
return ret;
|
||||
}
|
||||
|
||||
qcom_geni_i2c_conf(gi2c);
|
||||
ret = gi2c->dev_data->set_rate(&gi2c->se, gi2c->clk_freq_out);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (gi2c->gpi_mode)
|
||||
ret = geni_i2c_gpi_xfer(gi2c, msgs, num);
|
||||
|
|
@ -944,15 +997,6 @@ static const struct i2c_algorithm geni_i2c_algo = {
|
|||
.functionality = geni_i2c_func,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id geni_i2c_acpi_match[] = {
|
||||
{ "QCOM0220"},
|
||||
{ "QCOM0411" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, geni_i2c_acpi_match);
|
||||
#endif
|
||||
|
||||
static void release_gpi_dma(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
if (gi2c->rx_c)
|
||||
|
|
@ -990,13 +1034,95 @@ static int setup_gpi_dma(struct geni_i2c_dev *gi2c)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int geni_i2c_init(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
u32 proto, tx_depth;
|
||||
bool fifo_disable;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(gi2c->se.dev);
|
||||
if (ret < 0) {
|
||||
dev_err(gi2c->se.dev, "error turning on device :%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
proto = geni_se_read_proto(&gi2c->se);
|
||||
if (proto == GENI_SE_INVALID_PROTO) {
|
||||
ret = geni_load_se_firmware(&gi2c->se, GENI_SE_I2C);
|
||||
if (ret) {
|
||||
dev_err_probe(gi2c->se.dev, ret, "i2c firmware load failed ret: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
} else if (proto != GENI_SE_I2C) {
|
||||
ret = dev_err_probe(gi2c->se.dev, -ENXIO, "Invalid proto %d\n", proto);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (gi2c->dev_data->no_dma_support) {
|
||||
fifo_disable = false;
|
||||
gi2c->no_dma = true;
|
||||
} else {
|
||||
fifo_disable = readl_relaxed(gi2c->se.base + GENI_IF_DISABLE_RO) & FIFO_IF_DISABLE;
|
||||
}
|
||||
|
||||
if (fifo_disable) {
|
||||
/* FIFO is disabled, so we can only use GPI DMA */
|
||||
gi2c->gpi_mode = true;
|
||||
ret = setup_gpi_dma(gi2c);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dev_dbg(gi2c->se.dev, "Using GPI DMA mode for I2C\n");
|
||||
} else {
|
||||
gi2c->gpi_mode = false;
|
||||
tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
|
||||
|
||||
/* I2C Master Hub Serial Elements doesn't have the HW_PARAM_0 register */
|
||||
if (!tx_depth && gi2c->se.core_clk)
|
||||
tx_depth = gi2c->dev_data->tx_fifo_depth;
|
||||
|
||||
if (!tx_depth) {
|
||||
ret = dev_err_probe(gi2c->se.dev, -EINVAL,
|
||||
"Invalid TX FIFO depth\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
gi2c->tx_wm = tx_depth - 1;
|
||||
geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
|
||||
geni_se_config_packing(&gi2c->se, BITS_PER_BYTE,
|
||||
PACKING_BYTES_PW, true, true, true);
|
||||
|
||||
dev_dbg(gi2c->se.dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
|
||||
}
|
||||
|
||||
err:
|
||||
pm_runtime_put(gi2c->se.dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int geni_i2c_resources_init(struct geni_se *se)
|
||||
{
|
||||
struct geni_i2c_dev *gi2c = dev_get_drvdata(se->dev);
|
||||
int ret;
|
||||
|
||||
ret = geni_se_resources_init(&gi2c->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = geni_i2c_clk_map_idx(gi2c);
|
||||
if (ret)
|
||||
return dev_err_probe(gi2c->se.dev, ret, "Invalid clk frequency %d Hz\n",
|
||||
gi2c->clk_freq_out);
|
||||
|
||||
return geni_icc_set_bw_ab(&gi2c->se, GENI_DEFAULT_BW, GENI_DEFAULT_BW,
|
||||
Bps_to_icc(gi2c->clk_freq_out));
|
||||
}
|
||||
|
||||
static int geni_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct geni_i2c_dev *gi2c;
|
||||
u32 proto, tx_depth, fifo_disable;
|
||||
int ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct geni_i2c_desc *desc = NULL;
|
||||
|
||||
gi2c = devm_kzalloc(dev, sizeof(*gi2c), GFP_KERNEL);
|
||||
if (!gi2c)
|
||||
|
|
@ -1008,17 +1134,9 @@ static int geni_i2c_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(gi2c->se.base))
|
||||
return PTR_ERR(gi2c->se.base);
|
||||
|
||||
desc = device_get_match_data(&pdev->dev);
|
||||
|
||||
if (desc && desc->has_core_clk) {
|
||||
gi2c->core_clk = devm_clk_get(dev, "core");
|
||||
if (IS_ERR(gi2c->core_clk))
|
||||
return PTR_ERR(gi2c->core_clk);
|
||||
}
|
||||
|
||||
gi2c->se.clk = devm_clk_get(dev, "se");
|
||||
if (IS_ERR(gi2c->se.clk) && !has_acpi_companion(dev))
|
||||
return PTR_ERR(gi2c->se.clk);
|
||||
gi2c->dev_data = device_get_match_data(&pdev->dev);
|
||||
if (!gi2c->dev_data)
|
||||
return -EINVAL;
|
||||
|
||||
ret = device_property_read_u32(dev, "clock-frequency",
|
||||
&gi2c->clk_freq_out);
|
||||
|
|
@ -1034,16 +1152,19 @@ static int geni_i2c_probe(struct platform_device *pdev)
|
|||
if (gi2c->irq < 0)
|
||||
return gi2c->irq;
|
||||
|
||||
ret = geni_i2c_clk_map_idx(gi2c);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Invalid clk frequency %d Hz\n",
|
||||
gi2c->clk_freq_out);
|
||||
|
||||
gi2c->adap.algo = &geni_i2c_algo;
|
||||
init_completion(&gi2c->done);
|
||||
init_completion(&gi2c->abort_done);
|
||||
init_completion(&gi2c->cancel_done);
|
||||
init_completion(&gi2c->tx_reset_done);
|
||||
init_completion(&gi2c->rx_reset_done);
|
||||
spin_lock_init(&gi2c->lock);
|
||||
platform_set_drvdata(pdev, gi2c);
|
||||
|
||||
ret = gi2c->dev_data->resources_init(&gi2c->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Keep interrupts disabled initially to allow for low-power modes */
|
||||
ret = devm_request_irq(dev, gi2c->irq, geni_i2c_irq, IRQF_NO_AUTOEN,
|
||||
dev_name(dev), gi2c);
|
||||
|
|
@ -1056,119 +1177,25 @@ static int geni_i2c_probe(struct platform_device *pdev)
|
|||
gi2c->adap.dev.of_node = dev->of_node;
|
||||
strscpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
|
||||
|
||||
ret = geni_icc_get(&gi2c->se, desc ? desc->icc_ddr : "qup-memory");
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* Set the bus quota for core and cpu to a reasonable value for
|
||||
* register access.
|
||||
* Set quota for DDR based on bus speed.
|
||||
*/
|
||||
gi2c->se.icc_paths[GENI_TO_CORE].avg_bw = GENI_DEFAULT_BW;
|
||||
gi2c->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW;
|
||||
if (!desc || desc->icc_ddr)
|
||||
gi2c->se.icc_paths[GENI_TO_DDR].avg_bw = Bps_to_icc(gi2c->clk_freq_out);
|
||||
pm_runtime_set_suspended(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, I2C_AUTO_SUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
|
||||
ret = geni_icc_set_bw(&gi2c->se);
|
||||
ret = devm_pm_runtime_enable(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(gi2c->core_clk);
|
||||
if (ret)
|
||||
ret = geni_i2c_init(gi2c);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = geni_se_resources_on(&gi2c->se);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "Error turning on resources\n");
|
||||
goto err_clk;
|
||||
}
|
||||
proto = geni_se_read_proto(&gi2c->se);
|
||||
if (proto == GENI_SE_INVALID_PROTO) {
|
||||
ret = geni_load_se_firmware(&gi2c->se, GENI_SE_I2C);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "i2c firmware load failed ret: %d\n", ret);
|
||||
goto err_resources;
|
||||
}
|
||||
} else if (proto != GENI_SE_I2C) {
|
||||
ret = dev_err_probe(dev, -ENXIO, "Invalid proto %d\n", proto);
|
||||
goto err_resources;
|
||||
}
|
||||
|
||||
if (desc && desc->no_dma_support) {
|
||||
fifo_disable = false;
|
||||
gi2c->no_dma = true;
|
||||
} else {
|
||||
fifo_disable = readl_relaxed(gi2c->se.base + GENI_IF_DISABLE_RO) & FIFO_IF_DISABLE;
|
||||
}
|
||||
|
||||
if (fifo_disable) {
|
||||
/* FIFO is disabled, so we can only use GPI DMA */
|
||||
gi2c->gpi_mode = true;
|
||||
ret = setup_gpi_dma(gi2c);
|
||||
if (ret)
|
||||
goto err_resources;
|
||||
|
||||
dev_dbg(dev, "Using GPI DMA mode for I2C\n");
|
||||
} else {
|
||||
gi2c->gpi_mode = false;
|
||||
tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
|
||||
|
||||
/* I2C Master Hub Serial Elements doesn't have the HW_PARAM_0 register */
|
||||
if (!tx_depth && desc)
|
||||
tx_depth = desc->tx_fifo_depth;
|
||||
|
||||
if (!tx_depth) {
|
||||
ret = dev_err_probe(dev, -EINVAL,
|
||||
"Invalid TX FIFO depth\n");
|
||||
goto err_resources;
|
||||
}
|
||||
|
||||
gi2c->tx_wm = tx_depth - 1;
|
||||
geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
|
||||
geni_se_config_packing(&gi2c->se, BITS_PER_BYTE,
|
||||
PACKING_BYTES_PW, true, true, true);
|
||||
|
||||
dev_dbg(dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
|
||||
}
|
||||
|
||||
clk_disable_unprepare(gi2c->core_clk);
|
||||
ret = geni_se_resources_off(&gi2c->se);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "Error turning off resources\n");
|
||||
goto err_dma;
|
||||
}
|
||||
|
||||
ret = geni_icc_disable(&gi2c->se);
|
||||
if (ret)
|
||||
goto err_dma;
|
||||
|
||||
pm_runtime_set_suspended(gi2c->se.dev);
|
||||
pm_runtime_set_autosuspend_delay(gi2c->se.dev, I2C_AUTO_SUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(gi2c->se.dev);
|
||||
pm_runtime_enable(gi2c->se.dev);
|
||||
|
||||
ret = i2c_add_adapter(&gi2c->adap);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "Error adding i2c adapter\n");
|
||||
pm_runtime_disable(gi2c->se.dev);
|
||||
goto err_dma;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Error adding i2c adapter\n");
|
||||
|
||||
dev_dbg(dev, "Geni-I2C adaptor successfully added\n");
|
||||
|
||||
return ret;
|
||||
|
||||
err_resources:
|
||||
geni_se_resources_off(&gi2c->se);
|
||||
err_clk:
|
||||
clk_disable_unprepare(gi2c->core_clk);
|
||||
|
||||
return ret;
|
||||
|
||||
err_dma:
|
||||
release_gpi_dma(gi2c);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void geni_i2c_remove(struct platform_device *pdev)
|
||||
|
|
@ -1177,7 +1204,6 @@ static void geni_i2c_remove(struct platform_device *pdev)
|
|||
|
||||
i2c_del_adapter(&gi2c->adap);
|
||||
release_gpi_dma(gi2c);
|
||||
pm_runtime_disable(gi2c->se.dev);
|
||||
}
|
||||
|
||||
static void geni_i2c_shutdown(struct platform_device *pdev)
|
||||
|
|
@ -1190,48 +1216,36 @@ static void geni_i2c_shutdown(struct platform_device *pdev)
|
|||
|
||||
static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
|
||||
|
||||
disable_irq(gi2c->irq);
|
||||
ret = geni_se_resources_off(&gi2c->se);
|
||||
if (ret) {
|
||||
enable_irq(gi2c->irq);
|
||||
return ret;
|
||||
|
||||
if (gi2c->dev_data->power_off) {
|
||||
ret = gi2c->dev_data->power_off(&gi2c->se);
|
||||
if (ret) {
|
||||
enable_irq(gi2c->irq);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
clk_disable_unprepare(gi2c->core_clk);
|
||||
|
||||
return geni_icc_disable(&gi2c->se);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused geni_i2c_runtime_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
|
||||
|
||||
ret = geni_icc_enable(&gi2c->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(gi2c->core_clk);
|
||||
if (ret)
|
||||
goto out_icc_disable;
|
||||
|
||||
ret = geni_se_resources_on(&gi2c->se);
|
||||
if (ret)
|
||||
goto out_clk_disable;
|
||||
if (gi2c->dev_data->power_on) {
|
||||
ret = gi2c->dev_data->power_on(&gi2c->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
enable_irq(gi2c->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
out_clk_disable:
|
||||
clk_disable_unprepare(gi2c->core_clk);
|
||||
out_icc_disable:
|
||||
geni_icc_disable(&gi2c->se);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev)
|
||||
|
|
@ -1267,16 +1281,40 @@ static const struct dev_pm_ops geni_i2c_pm_ops = {
|
|||
NULL)
|
||||
};
|
||||
|
||||
static const struct geni_i2c_desc i2c_master_hub = {
|
||||
.has_core_clk = true,
|
||||
.icc_ddr = NULL,
|
||||
.no_dma_support = true,
|
||||
.tx_fifo_depth = 16,
|
||||
static const struct geni_i2c_desc geni_i2c = {
|
||||
.resources_init = geni_i2c_resources_init,
|
||||
.set_rate = qcom_geni_i2c_conf,
|
||||
.power_on = geni_se_resources_activate,
|
||||
.power_off = geni_se_resources_deactivate,
|
||||
};
|
||||
|
||||
static const struct geni_i2c_desc i2c_master_hub = {
|
||||
.no_dma_support = true,
|
||||
.tx_fifo_depth = 16,
|
||||
.resources_init = geni_i2c_resources_init,
|
||||
.set_rate = qcom_geni_i2c_conf,
|
||||
.power_on = geni_se_resources_activate,
|
||||
.power_off = geni_se_resources_deactivate,
|
||||
};
|
||||
|
||||
static const struct geni_i2c_desc sa8255p_geni_i2c = {
|
||||
.resources_init = geni_se_domain_attach,
|
||||
.set_rate = geni_se_set_perf_opp,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id geni_i2c_acpi_match[] = {
|
||||
{ "QCOM0220", (kernel_ulong_t)&geni_i2c},
|
||||
{ "QCOM0411", (kernel_ulong_t)&geni_i2c},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, geni_i2c_acpi_match);
|
||||
#endif
|
||||
|
||||
static const struct of_device_id geni_i2c_dt_match[] = {
|
||||
{ .compatible = "qcom,geni-i2c" },
|
||||
{ .compatible = "qcom,geni-i2c", .data = &geni_i2c },
|
||||
{ .compatible = "qcom,geni-i2c-master-hub", .data = &i2c_master_hub },
|
||||
{ .compatible = "qcom,sa8255p-geni-i2c", .data = &sa8255p_geni_i2c },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, geni_i2c_dt_match);
|
||||
|
|
|
|||
|
|
@ -1657,10 +1657,21 @@ static const struct i2c_adapter_quirks qup_i2c_quirks_v2 = {
|
|||
.flags = I2C_AQ_NO_ZERO_LEN,
|
||||
};
|
||||
|
||||
static void qup_i2c_enable_clocks(struct qup_i2c_dev *qup)
|
||||
static int qup_i2c_enable_clocks(struct qup_i2c_dev *qup)
|
||||
{
|
||||
clk_prepare_enable(qup->clk);
|
||||
clk_prepare_enable(qup->pclk);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(qup->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(qup->pclk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(qup->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup)
|
||||
|
|
@ -1823,7 +1834,9 @@ static int qup_i2c_probe(struct platform_device *pdev)
|
|||
ret = PTR_ERR(qup->pclk);
|
||||
goto fail_dma;
|
||||
}
|
||||
qup_i2c_enable_clocks(qup);
|
||||
ret = qup_i2c_enable_clocks(qup);
|
||||
if (ret)
|
||||
goto fail_dma;
|
||||
src_clk_freq = clk_get_rate(qup->clk);
|
||||
}
|
||||
qup->src_clk_freq = src_clk_freq;
|
||||
|
|
@ -1975,8 +1988,7 @@ static int qup_i2c_pm_resume_runtime(struct device *device)
|
|||
struct qup_i2c_dev *qup = dev_get_drvdata(device);
|
||||
|
||||
dev_dbg(device, "pm_runtime: resuming...\n");
|
||||
qup_i2c_enable_clocks(qup);
|
||||
return 0;
|
||||
return qup_i2c_enable_clocks(qup);
|
||||
}
|
||||
|
||||
static int qup_i2c_suspend(struct device *device)
|
||||
|
|
@ -1988,7 +2000,12 @@ static int qup_i2c_suspend(struct device *device)
|
|||
|
||||
static int qup_i2c_resume(struct device *device)
|
||||
{
|
||||
qup_i2c_pm_resume_runtime(device);
|
||||
int ret;
|
||||
|
||||
ret = qup_i2c_pm_resume_runtime(device);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_request_autosuspend(device);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ enum rcar_i2c_type {
|
|||
I2C_RCAR_GEN2,
|
||||
I2C_RCAR_GEN3,
|
||||
I2C_RCAR_GEN4,
|
||||
I2C_RCAR_GEN5,
|
||||
};
|
||||
|
||||
struct rcar_i2c_priv {
|
||||
|
|
@ -900,8 +901,12 @@ static int rcar_i2c_do_reset(struct rcar_i2c_priv *priv)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return read_poll_timeout_atomic(reset_control_status, ret, ret == 0, 1,
|
||||
100, false, priv->rstc);
|
||||
/* SCMI based resets don't need to poll for success */
|
||||
if (priv->devtype < I2C_RCAR_GEN5)
|
||||
return read_poll_timeout_atomic(reset_control_status, ret, ret == 0,
|
||||
1, 100, false, priv->rstc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
||||
|
|
@ -1111,6 +1116,7 @@ static const struct of_device_id rcar_i2c_dt_ids[] = {
|
|||
{ .compatible = "renesas,rcar-gen2-i2c", .data = (void *)I2C_RCAR_GEN2 },
|
||||
{ .compatible = "renesas,rcar-gen3-i2c", .data = (void *)I2C_RCAR_GEN3 },
|
||||
{ .compatible = "renesas,rcar-gen4-i2c", .data = (void *)I2C_RCAR_GEN4 },
|
||||
{ .compatible = "renesas,rcar-gen5-i2c", .data = (void *)I2C_RCAR_GEN5 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids);
|
||||
|
|
@ -1194,9 +1200,15 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
|||
goto out_pm_put;
|
||||
}
|
||||
|
||||
ret = reset_control_status(priv->rstc);
|
||||
if (ret < 0)
|
||||
goto out_pm_put;
|
||||
/*
|
||||
* Gen5+ uses SCMI based reset which cannot report status.
|
||||
* Firmware has to ensure proper reset
|
||||
*/
|
||||
if (priv->devtype < I2C_RCAR_GEN5) {
|
||||
ret = reset_control_status(priv->rstc);
|
||||
if (ret < 0)
|
||||
goto out_pm_put;
|
||||
}
|
||||
|
||||
/* hard reset disturbs HostNotify local target, so disable it */
|
||||
priv->flags &= ~ID_P_HOST_NOTIFY;
|
||||
|
|
|
|||
|
|
@ -163,9 +163,12 @@ static int i2c_acpi_do_lookup(struct acpi_device *adev,
|
|||
INIT_LIST_HEAD(&resource_list);
|
||||
ret = acpi_dev_get_resources(adev, &resource_list,
|
||||
i2c_acpi_fill_info, lookup);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
acpi_dev_free_resource_list(&resource_list);
|
||||
|
||||
if (ret < 0 || !info->addr)
|
||||
if (!info->addr)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
|
@ -371,6 +374,7 @@ static const struct acpi_device_id i2c_acpi_force_100khz_device_ids[] = {
|
|||
{ "DLL0945", 0 },
|
||||
{ "ELAN0678", 0 },
|
||||
{ "ELAN06FA", 0 },
|
||||
{ "ELAN1300", 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -446,7 +446,8 @@ static int i2c_init_recovery(struct i2c_adapter *adap)
|
|||
bri->set_scl = set_scl_gpio_value;
|
||||
if (bri->sda_gpiod) {
|
||||
bri->get_sda = get_sda_gpio_value;
|
||||
if (gpiod_get_direction(bri->sda_gpiod) == GPIO_LINE_DIRECTION_OUT)
|
||||
if (gpiod_get_direction(bri->sda_gpiod) == GPIO_LINE_DIRECTION_OUT ||
|
||||
gpiod_is_single_ended(bri->sda_gpiod))
|
||||
bri->set_sda = set_sda_gpio_value;
|
||||
}
|
||||
} else if (bri->recover_bus == i2c_generic_scl_recovery) {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ struct i2c_algo_bit_data {
|
|||
maximum 50 us for SMBus */
|
||||
int timeout; /* in jiffies */
|
||||
bool can_do_atomic; /* callbacks don't sleep, we can be atomic */
|
||||
bool skip_bit_test; /* override bit_test module parameter */
|
||||
};
|
||||
|
||||
int i2c_bit_add_bus(struct i2c_adapter *);
|
||||
|
|
|
|||
82
include/trace/events/qcom_geni_i2c.h
Normal file
82
include/trace/events/qcom_geni_i2c.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM qcom_geni_i2c
|
||||
|
||||
#if !defined(_TRACE_QCOM_GENI_I2C_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_QCOM_GENI_I2C_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
TRACE_EVENT(geni_i2c_bus_setup,
|
||||
TP_PROTO(struct device *dev, u32 clk_freq, u8 clk_div,
|
||||
u8 t_high_cnt, u8 t_low_cnt, u8 t_cycle_cnt),
|
||||
TP_ARGS(dev, clk_freq, clk_div, t_high_cnt, t_low_cnt, t_cycle_cnt),
|
||||
|
||||
TP_STRUCT__entry(__string(name, dev_name(dev))
|
||||
__field(u32, clk_freq)
|
||||
__field(u8, clk_div)
|
||||
__field(u8, t_high_cnt)
|
||||
__field(u8, t_low_cnt)
|
||||
__field(u8, t_cycle_cnt)
|
||||
),
|
||||
|
||||
TP_fast_assign(__assign_str(name);
|
||||
__entry->clk_freq = clk_freq;
|
||||
__entry->clk_div = clk_div;
|
||||
__entry->t_high_cnt = t_high_cnt;
|
||||
__entry->t_low_cnt = t_low_cnt;
|
||||
__entry->t_cycle_cnt = t_cycle_cnt;
|
||||
),
|
||||
|
||||
TP_printk("%s: clk_freq=%u clk_div=%u t_high=%u t_low=%u t_cycle=%u",
|
||||
__get_str(name), __entry->clk_freq, __entry->clk_div,
|
||||
__entry->t_high_cnt, __entry->t_low_cnt,
|
||||
__entry->t_cycle_cnt)
|
||||
);
|
||||
|
||||
TRACE_EVENT(geni_i2c_irq,
|
||||
TP_PROTO(struct device *dev, u32 m_stat, u32 rx_st,
|
||||
u32 dm_tx_st, u32 dm_rx_st),
|
||||
TP_ARGS(dev, m_stat, rx_st, dm_tx_st, dm_rx_st),
|
||||
|
||||
TP_STRUCT__entry(__string(name, dev_name(dev))
|
||||
__field(u32, m_stat)
|
||||
__field(u32, rx_st)
|
||||
__field(u32, dm_tx_st)
|
||||
__field(u32, dm_rx_st)
|
||||
),
|
||||
|
||||
TP_fast_assign(__assign_str(name);
|
||||
__entry->m_stat = m_stat;
|
||||
__entry->rx_st = rx_st;
|
||||
__entry->dm_tx_st = dm_tx_st;
|
||||
__entry->dm_rx_st = dm_rx_st;
|
||||
),
|
||||
|
||||
TP_printk("%s: m_stat=0x%08x rx_st=0x%08x dm_tx=0x%08x dm_rx=0x%08x",
|
||||
__get_str(name), __entry->m_stat, __entry->rx_st,
|
||||
__entry->dm_tx_st, __entry->dm_rx_st)
|
||||
);
|
||||
|
||||
TRACE_EVENT(geni_i2c_err,
|
||||
TP_PROTO(struct device *dev, int err, const char *msg),
|
||||
TP_ARGS(dev, err, msg),
|
||||
|
||||
TP_STRUCT__entry(__string(name, dev_name(dev))
|
||||
__field(int, err)
|
||||
__string(msg, msg)
|
||||
),
|
||||
|
||||
TP_fast_assign(__assign_str(name);
|
||||
__entry->err = err;
|
||||
__assign_str(msg);
|
||||
),
|
||||
|
||||
TP_printk("%s: err=%d msg=%s",
|
||||
__get_str(name), __entry->err, __get_str(msg))
|
||||
);
|
||||
|
||||
#endif /* _TRACE_QCOM_GENI_I2C_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
||||
Loading…
Reference in New Issue
Block a user