This commit is contained in:
Mark Brown
2026-07-31 14:50:13 +01:00
44 changed files with 1022 additions and 393 deletions
@@ -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>;
};
@@ -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
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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
+6 -2
View File
@@ -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);
@@ -333,11 +337,11 @@ static int amd_asf_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return dev_err_probe(dev, irq, "missing IRQ resources\n");
return irq;
ret = devm_request_irq(dev, irq, amd_asf_irq_handler, IRQF_SHARED, "amd_asf", asf_dev);
if (ret)
return dev_err_probe(dev, ret, "Unable to request irq: %d for use\n", irq);
return ret;
asf_dev->adap.owner = THIS_MODULE;
asf_dev->adap.algo = &amd_asf_smbus_algorithm;
+1 -2
View File
@@ -1147,8 +1147,7 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)
bcm_iproc_i2c_isr, 0, pdev->name,
iproc_i2c);
if (ret < 0)
return dev_err_probe(iproc_i2c->device, ret,
"unable to request irq %i\n", irq);
return ret;
iproc_i2c->irq = irq;
} else {
+1 -3
View File
@@ -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;
+209 -14
View File
@@ -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[] = {
+1 -1
View File
@@ -551,7 +551,7 @@ static int gxp_i2c_probe(struct platform_device *pdev)
rc = devm_request_irq(&pdev->dev, drvdata->irq, gxp_i2c_irq_handler,
IRQF_SHARED, gxp_i2c_name[drvdata->engine], drvdata);
if (rc < 0)
return dev_err_probe(&pdev->dev, rc, "irq request failed\n");
return rc;
i2c_parse_fw_timings(&pdev->dev, &drvdata->t, true);
+1 -1
View File
@@ -481,7 +481,7 @@ static int hisi_i2c_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, ctlr->irq, hisi_i2c_irq, 0, "hisi-i2c", ctlr);
if (ret)
return dev_err_probe(dev, ret, "failed to request irq handler\n");
return ret;
ctlr->clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
if (IS_ERR_OR_NULL(ctlr->clk)) {
+28 -12
View File
@@ -1510,11 +1510,6 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
if (ret)
lpi2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
ret = devm_request_irq(&pdev->dev, lpi2c_imx->irq, lpi2c_imx_isr, IRQF_NO_SUSPEND,
pdev->name, lpi2c_imx);
if (ret)
return dev_err_probe(&pdev->dev, ret, "can't claim irq %d\n", lpi2c_imx->irq);
i2c_set_adapdata(&lpi2c_imx->adapter, lpi2c_imx);
platform_set_drvdata(pdev, lpi2c_imx);
@@ -1527,14 +1522,18 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
* each transfer
*/
ret = devm_clk_rate_exclusive_get(&pdev->dev, lpi2c_imx->clks[0].clk);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"can't lock I2C peripheral clock rate\n");
if (ret) {
ret = dev_err_probe(&pdev->dev, ret,
"can't lock I2C peripheral clock rate\n");
goto clk_disable;
}
lpi2c_imx->rate_per = clk_get_rate(lpi2c_imx->clks[0].clk);
if (!lpi2c_imx->rate_per)
return dev_err_probe(&pdev->dev, -EINVAL,
"can't get I2C peripheral clock rate\n");
if (!lpi2c_imx->rate_per) {
ret = dev_err_probe(&pdev->dev, -EINVAL,
"can't get I2C peripheral clock rate\n");
goto clk_disable;
}
if (lpi2c_imx->hwdata->need_prepare_unprepare_clk)
pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_LONG_TIMEOUT_MS);
@@ -1546,6 +1545,20 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
/*
* Reset all internal controller registers of both Master and Target
* to avoid effects of previous status.
*/
writel(MCR_RST, lpi2c_imx->base + LPI2C_MCR);
writel(SCR_RST, lpi2c_imx->base + LPI2C_SCR);
writel(0, lpi2c_imx->base + LPI2C_MCR);
writel(0, lpi2c_imx->base + LPI2C_SCR);
ret = devm_request_irq(&pdev->dev, lpi2c_imx->irq, lpi2c_imx_isr, IRQF_NO_SUSPEND,
pdev->name, lpi2c_imx);
if (ret)
goto rpm_disable;
temp = readl(lpi2c_imx->base + LPI2C_PARAM);
lpi2c_imx->txfifosize = 1 << (temp & 0x0f);
lpi2c_imx->rxfifosize = 1 << ((temp >> 8) & 0x0f);
@@ -1576,8 +1589,11 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
rpm_disable:
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
clk_disable:
clk_bulk_disable_unprepare(lpi2c_imx->num_clks, lpi2c_imx->clks);
return ret;
}
+1 -1
View File
@@ -1753,7 +1753,7 @@ static int i2c_imx_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return dev_err_probe(&pdev->dev, irq, "can't get IRQ\n");
return irq;
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
+170 -17
View File
@@ -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;
@@ -721,12 +852,21 @@ static int spacemit_i2c_probe(struct platform_device *pdev)
i2c->irq = platform_get_irq(pdev, 0);
if (i2c->irq < 0)
return dev_err_probe(dev, i2c->irq, "failed to get irq resource");
return i2c->irq;
clk = devm_clk_get_enabled(dev, "func");
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);
@@ -753,7 +906,7 @@ static int spacemit_i2c_probe(struct platform_device *pdev)
ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler,
IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c);
if (ret)
return dev_err_probe(dev, ret, "failed to request irq");
return ret;
platform_set_drvdata(pdev, i2c);
+1 -1
View File
@@ -323,7 +323,7 @@ static int ls2x_i2c_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq, ls2x_i2c_isr, IRQF_SHARED, "ls2x-i2c",
priv);
if (ret < 0)
return dev_err_probe(dev, ret, "Unable to request irq %d\n", irq);
return ret;
return devm_i2c_add_adapter(dev, adap);
}
+3 -4
View File
@@ -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);
@@ -581,8 +581,7 @@ static int mchp_corei2c_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev, irq, mchp_corei2c_isr, IRQF_SHARED,
pdev->name, idev);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"failed to claim irq %d\n", irq);
return ret;
ret = clk_prepare_enable(idev->i2c_clk);
if (ret)

Some files were not shown because too many files have changed in this diff Show More