summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-riic.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-09-23 14:34:19 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2024-09-23 14:34:19 -0700
commit4e2c9cd7dce6c7480f236c3ead196ff4e92ed597 (patch)
treeed92a4f5febdd0e22bf9511b85f77d500aa00fa6 /drivers/i2c/busses/i2c-riic.c
parent00b43f85f287f4b661f1a2485bed1a476d308427 (diff)
parentc24999e61b2187578fe4256b7bc6190a046c4b93 (diff)
Merge tag 'i2c-for-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: "I2C core: - finally remove the I2C_COMPAT symbol after 15 years of deprecation - lock client addresses during initialization to prevent race conditions between different kinds of instantiation - use scoped foreach OF child loops - testunit cleanups and documentation improvements, as well as two new tests, one for repeated start and one for triggering SMBusAlert interrupts I2C host drivers: - DesignWare and Renesas I2C driver updates. The first has has undergone through a series of cleanups that have been sent to the mailing list a year ago for the first time and finally get merged in this pull request. They are many, from typos (e.g. i2/i2c), to cosmetics, to refactoring (e.g. move inline functions to librarieas) and many others. - all the DesignWare Kconfig options have been grouped under the I2C_DESIGNWARE_CORE and this required some adaptation in many of the kernel configuration files for different arm and mips boards Cleanups: - improve the exit path in the runtime resume function for the Qualcomm Geni platform - get rid of the unused "target_addr" parameter in the Intel LJCA driver - intialize the restart_flag in the MediaTek controller in one single place - constify a few global data structures in the virtio driver - simplify the bus speed handling in the Renesas driver init function making it more readable - improved probe function of the Renesas R-Car driver - switch the iMX/MXC driver to use RUNTIME_PM_OPS() instead of SET_RUNTIME_PM_OPS() - iMX/MXC driver cleanups - use devm_clk_get_enabled() to simplify the Renesas EMEV2, Ingenic and MPC drivers Refactoring: - Fix a potential out of boundary array access in the Nuvoton driver. This is not a bug fix because the issue could never occur due to hardware not having the properties listed in the array. The change makes the driver more future proof and, at the same time, silences code analyzers. Improvements: - several patches improving the runtime power management handling of the Renesas I2C (riic) driver - use a more descriptive adapter name in the Intel i801 driver to show the presence of the IDF feature - kill pending transactions when irq's can't complete their handling in the Intel Denverton (ismt) driver, triggering a timeout New Feature: - support fast mode plus in the Renesas I2C (riic) driver New support: - Added support for: - Renesas R9A08G045 - Rockchip RK3576 - KEBA I2C - Theobroma Systems Mule Multiplexer. - new i2c-keba.c driver - new driver for The Mule i2c multiplexer Core I2C framework: - move runtime PM functions in order to allow them to be accessed during device add Devicetree: - nVidia and Qualcomm binding improvements - get rid of redundant "multi-master" property in the aspeed binding - convert i2c-sprd binding to YAML AT24 updates: - document a new model from giantec in DT bindings" * tag 'i2c-for-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (69 commits) i2c: designware: Use pci_get_drvdata() i2c: designware: Propagate firmware node i2c: designware: Uninline i2c_dw_probe() i2c: ljca: Remove unused "target_addr" parameter i2c: keba: Add KEBA I2C controller support i2c: i801: Use a different adapter-name for IDF adapters i2c: core: Setup i2c_adapter runtime-pm before calling device_add() dt-bindings: i2c: i2c-sprd: convert to YAML i2c: ismt: kill transaction in hardware on timeout i2c: designware: Group all DesignWare drivers under a single option net: txgbe: Fix I2C Kconfig dependencies RISC-V: configs: enable I2C_DESIGNWARE_CORE with I2C_DESIGNWARE_PLATFORM mips: configs: enable I2C_DESIGNWARE_CORE with I2C_DESIGNWARE_PLATFORM arm64: defconfig: enable I2C_DESIGNWARE_CORE with I2C_DESIGNWARE_PLATFORM ARM: configs: enable I2C_DESIGNWARE_CORE with I2C_DESIGNWARE_PLATFORM ARC: configs: enable I2C_DESIGNWARE_CORE with I2C_DESIGNWARE_PLATFORM i2c: virtio: Constify struct i2c_algorithm and struct virtio_device_id i2c: rcar: tidyup priv->devtype handling on rcar_i2c_probe() i2c: imx: Convert comma to semicolon i2c: jz4780: Use devm_clk_get_enabled() helpers ...
Diffstat (limited to 'drivers/i2c/busses/i2c-riic.c')
-rw-r--r--drivers/i2c/busses/i2c-riic.c228
1 files changed, 157 insertions, 71 deletions
diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
index d6f585cdb7e5..c7f3a4c02470 100644
--- a/drivers/i2c/busses/i2c-riic.c
+++ b/drivers/i2c/busses/i2c-riic.c
@@ -63,6 +63,8 @@
#define ICMR3_ACKWP 0x10
#define ICMR3_ACKBT 0x08
+#define ICFER_FMPE 0x80
+
#define ICIER_TIE 0x80
#define ICIER_TEIE 0x40
#define ICIER_RIE 0x20
@@ -80,6 +82,7 @@ enum riic_reg_list {
RIIC_ICCR2,
RIIC_ICMR1,
RIIC_ICMR3,
+ RIIC_ICFER,
RIIC_ICSER,
RIIC_ICIER,
RIIC_ICSR2,
@@ -91,7 +94,8 @@ enum riic_reg_list {
};
struct riic_of_data {
- u8 regs[RIIC_REG_END];
+ const u8 *regs;
+ bool fast_mode_plus;
};
struct riic_dev {
@@ -105,6 +109,8 @@ struct riic_dev {
struct completion msg_done;
struct i2c_adapter adapter;
struct clk *clk;
+ struct reset_control *rstc;
+ struct i2c_timings i2c_t;
};
struct riic_irq_desc {
@@ -131,11 +137,14 @@ static inline void riic_clear_set_bit(struct riic_dev *riic, u8 clear, u8 set, u
static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct riic_dev *riic = i2c_get_adapdata(adap);
+ struct device *dev = adap->dev.parent;
unsigned long time_left;
- int i;
+ int i, ret;
u8 start_bit;
- pm_runtime_get_sync(adap->dev.parent);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
if (riic_readb(riic, RIIC_ICCR2) & ICCR2_BBSY) {
riic->err = -EBUSY;
@@ -168,7 +177,8 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
}
out:
- pm_runtime_put(adap->dev.parent);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return riic->err ?: num;
}
@@ -298,21 +308,21 @@ static const struct i2c_algorithm riic_algo = {
.functionality = riic_func,
};
-static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
+static int riic_init_hw(struct riic_dev *riic)
{
- int ret = 0;
+ int ret;
unsigned long rate;
int total_ticks, cks, brl, brh;
+ struct i2c_timings *t = &riic->i2c_t;
+ struct device *dev = riic->adapter.dev.parent;
+ bool fast_mode_plus = riic->info->fast_mode_plus;
+ u32 max_freq = fast_mode_plus ? I2C_MAX_FAST_MODE_PLUS_FREQ
+ : I2C_MAX_FAST_MODE_FREQ;
- pm_runtime_get_sync(riic->adapter.dev.parent);
-
- if (t->bus_freq_hz > I2C_MAX_FAST_MODE_FREQ) {
- dev_err(&riic->adapter.dev,
- "unsupported bus speed (%dHz). %d max\n",
- t->bus_freq_hz, I2C_MAX_FAST_MODE_FREQ);
- ret = -EINVAL;
- goto out;
- }
+ if (t->bus_freq_hz > max_freq)
+ return dev_err_probe(&riic->adapter.dev, -EINVAL,
+ "unsupported bus speed %uHz (%u max)\n",
+ t->bus_freq_hz, max_freq);
rate = clk_get_rate(riic->clk);
@@ -349,8 +359,7 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
if (brl > (0x1F + 3)) {
dev_err(&riic->adapter.dev, "invalid speed (%lu). Too slow.\n",
(unsigned long)t->bus_freq_hz);
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
brh = total_ticks - brl;
@@ -382,6 +391,10 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
t->scl_fall_ns / (1000000000 / rate),
t->scl_rise_ns / (1000000000 / rate), cks, brl, brh);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
/* Changing the order of accessing IICRST and ICE may break things! */
riic_writeb(riic, ICCR1_IICRST | ICCR1_SOWP, RIIC_ICCR1);
riic_clear_set_bit(riic, 0, ICCR1_ICE, RIIC_ICCR1);
@@ -393,11 +406,14 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
riic_writeb(riic, 0, RIIC_ICSER);
riic_writeb(riic, ICMR3_ACKWP | ICMR3_RDRFS, RIIC_ICMR3);
+ if (fast_mode_plus && t->bus_freq_hz > I2C_MAX_FAST_MODE_FREQ)
+ riic_clear_set_bit(riic, 0, ICFER_FMPE, RIIC_ICFER);
+
riic_clear_set_bit(riic, ICCR1_IICRST, 0, RIIC_ICCR1);
-out:
- pm_runtime_put(riic->adapter.dev.parent);
- return ret;
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ return 0;
}
static struct riic_irq_desc riic_irqs[] = {
@@ -415,13 +431,12 @@ static void riic_reset_control_assert(void *data)
static int riic_i2c_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct riic_dev *riic;
struct i2c_adapter *adap;
- struct i2c_timings i2c_t;
- struct reset_control *rstc;
int i, ret;
- riic = devm_kzalloc(&pdev->dev, sizeof(*riic), GFP_KERNEL);
+ riic = devm_kzalloc(dev, sizeof(*riic), GFP_KERNEL);
if (!riic)
return -ENOMEM;
@@ -429,22 +444,22 @@ static int riic_i2c_probe(struct platform_device *pdev)
if (IS_ERR(riic->base))
return PTR_ERR(riic->base);
- riic->clk = devm_clk_get(&pdev->dev, NULL);
+ riic->clk = devm_clk_get(dev, NULL);
if (IS_ERR(riic->clk)) {
- dev_err(&pdev->dev, "missing controller clock");
+ dev_err(dev, "missing controller clock");
return PTR_ERR(riic->clk);
}
- rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
- if (IS_ERR(rstc))
- return dev_err_probe(&pdev->dev, PTR_ERR(rstc),
+ riic->rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (IS_ERR(riic->rstc))
+ return dev_err_probe(dev, PTR_ERR(riic->rstc),
"Error: missing reset ctrl\n");
- ret = reset_control_deassert(rstc);
+ ret = reset_control_deassert(riic->rstc);
if (ret)
return ret;
- ret = devm_add_action_or_reset(&pdev->dev, riic_reset_control_assert, rstc);
+ ret = devm_add_action_or_reset(dev, riic_reset_control_assert, riic->rstc);
if (ret)
return ret;
@@ -453,31 +468,34 @@ static int riic_i2c_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- ret = devm_request_irq(&pdev->dev, ret, riic_irqs[i].isr,
+ ret = devm_request_irq(dev, ret, riic_irqs[i].isr,
0, riic_irqs[i].name, riic);
if (ret) {
- dev_err(&pdev->dev, "failed to request irq %s\n", riic_irqs[i].name);
+ dev_err(dev, "failed to request irq %s\n", riic_irqs[i].name);
return ret;
}
}
- riic->info = of_device_get_match_data(&pdev->dev);
+ riic->info = of_device_get_match_data(dev);
adap = &riic->adapter;
i2c_set_adapdata(adap, riic);
strscpy(adap->name, "Renesas RIIC adapter", sizeof(adap->name));
adap->owner = THIS_MODULE;
adap->algo = &riic_algo;
- adap->dev.parent = &pdev->dev;
- adap->dev.of_node = pdev->dev.of_node;
+ adap->dev.parent = dev;
+ adap->dev.of_node = dev->of_node;
init_completion(&riic->msg_done);
- i2c_parse_fw_timings(&pdev->dev, &i2c_t, true);
+ i2c_parse_fw_timings(dev, &riic->i2c_t, true);
- pm_runtime_enable(&pdev->dev);
+ /* Default 0 to save power. Can be overridden via sysfs for lower latency. */
+ pm_runtime_set_autosuspend_delay(dev, 0);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
- ret = riic_init_hw(riic, &i2c_t);
+ ret = riic_init_hw(riic);
if (ret)
goto out;
@@ -487,60 +505,127 @@ static int riic_i2c_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, riic);
- dev_info(&pdev->dev, "registered with %dHz bus speed\n",
- i2c_t.bus_freq_hz);
+ dev_info(dev, "registered with %dHz bus speed\n", riic->i2c_t.bus_freq_hz);
return 0;
out:
- pm_runtime_disable(&pdev->dev);
+ pm_runtime_disable(dev);
+ pm_runtime_dont_use_autosuspend(dev);
return ret;
}
static void riic_i2c_remove(struct platform_device *pdev)
{
struct riic_dev *riic = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ int ret;
- pm_runtime_get_sync(&pdev->dev);
- riic_writeb(riic, 0, RIIC_ICIER);
- pm_runtime_put(&pdev->dev);
+ ret = pm_runtime_resume_and_get(dev);
+ if (!ret) {
+ riic_writeb(riic, 0, RIIC_ICIER);
+ pm_runtime_put(dev);
+ }
i2c_del_adapter(&riic->adapter);
- pm_runtime_disable(&pdev->dev);
+ pm_runtime_disable(dev);
+ pm_runtime_dont_use_autosuspend(dev);
}
+static const u8 riic_rz_a_regs[RIIC_REG_END] = {
+ [RIIC_ICCR1] = 0x00,
+ [RIIC_ICCR2] = 0x04,
+ [RIIC_ICMR1] = 0x08,
+ [RIIC_ICMR3] = 0x10,
+ [RIIC_ICFER] = 0x14,
+ [RIIC_ICSER] = 0x18,
+ [RIIC_ICIER] = 0x1c,
+ [RIIC_ICSR2] = 0x24,
+ [RIIC_ICBRL] = 0x34,
+ [RIIC_ICBRH] = 0x38,
+ [RIIC_ICDRT] = 0x3c,
+ [RIIC_ICDRR] = 0x40,
+};
+
static const struct riic_of_data riic_rz_a_info = {
- .regs = {
- [RIIC_ICCR1] = 0x00,
- [RIIC_ICCR2] = 0x04,
- [RIIC_ICMR1] = 0x08,
- [RIIC_ICMR3] = 0x10,
- [RIIC_ICSER] = 0x18,
- [RIIC_ICIER] = 0x1c,
- [RIIC_ICSR2] = 0x24,
- [RIIC_ICBRL] = 0x34,
- [RIIC_ICBRH] = 0x38,
- [RIIC_ICDRT] = 0x3c,
- [RIIC_ICDRR] = 0x40,
- },
+ .regs = riic_rz_a_regs,
+ .fast_mode_plus = true,
+};
+
+static const struct riic_of_data riic_rz_a1h_info = {
+ .regs = riic_rz_a_regs,
+};
+
+static const u8 riic_rz_v2h_regs[RIIC_REG_END] = {
+ [RIIC_ICCR1] = 0x00,
+ [RIIC_ICCR2] = 0x01,
+ [RIIC_ICMR1] = 0x02,
+ [RIIC_ICMR3] = 0x04,
+ [RIIC_ICFER] = 0x05,
+ [RIIC_ICSER] = 0x06,
+ [RIIC_ICIER] = 0x07,
+ [RIIC_ICSR2] = 0x09,
+ [RIIC_ICBRL] = 0x10,
+ [RIIC_ICBRH] = 0x11,
+ [RIIC_ICDRT] = 0x12,
+ [RIIC_ICDRR] = 0x13,
};
static const struct riic_of_data riic_rz_v2h_info = {
- .regs = {
- [RIIC_ICCR1] = 0x00,
- [RIIC_ICCR2] = 0x01,
- [RIIC_ICMR1] = 0x02,
- [RIIC_ICMR3] = 0x04,
- [RIIC_ICSER] = 0x06,
- [RIIC_ICIER] = 0x07,
- [RIIC_ICSR2] = 0x09,
- [RIIC_ICBRL] = 0x10,
- [RIIC_ICBRH] = 0x11,
- [RIIC_ICDRT] = 0x12,
- [RIIC_ICDRR] = 0x13,
- },
+ .regs = riic_rz_v2h_regs,
+ .fast_mode_plus = true,
+};
+
+static int riic_i2c_suspend(struct device *dev)
+{
+ struct riic_dev *riic = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ i2c_mark_adapter_suspended(&riic->adapter);
+
+ /* Disable output on SDA, SCL pins. */
+ riic_clear_set_bit(riic, ICCR1_ICE, 0, RIIC_ICCR1);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_sync(dev);
+
+ return reset_control_assert(riic->rstc);
+}
+
+static int riic_i2c_resume(struct device *dev)
+{
+ struct riic_dev *riic = dev_get_drvdata(dev);
+ int ret;
+
+ ret = reset_control_deassert(riic->rstc);
+ if (ret)
+ return ret;
+
+ ret = riic_init_hw(riic);
+ if (ret) {
+ /*
+ * In case this happens there is no way to recover from this
+ * state. The driver will remain loaded. We want to avoid
+ * keeping the reset line de-asserted for no reason.
+ */
+ reset_control_assert(riic->rstc);
+ return ret;
+ }
+
+ i2c_mark_adapter_resumed(&riic->adapter);
+
+ return 0;
+}
+
+static const struct dev_pm_ops riic_i2c_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(riic_i2c_suspend, riic_i2c_resume)
};
static const struct of_device_id riic_i2c_dt_ids[] = {
{ .compatible = "renesas,riic-rz", .data = &riic_rz_a_info },
+ { .compatible = "renesas,riic-r7s72100", .data = &riic_rz_a1h_info, },
{ .compatible = "renesas,riic-r9a09g057", .data = &riic_rz_v2h_info },
{ /* Sentinel */ },
};
@@ -551,6 +636,7 @@ static struct platform_driver riic_i2c_driver = {
.driver = {
.name = "i2c-riic",
.of_match_table = riic_i2c_dt_ids,
+ .pm = pm_ptr(&riic_i2c_pm_ops),
},
};