From 83c3408f7b9c6b0aa8441204801a15a6c1c0665c Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Fri, 5 Feb 2021 09:51:41 +0100 Subject: i2c: stm32f7: support DT binding i2c-analog-filter Replace driver internally coded enabling/disabling of the analog-filter with the DT binding "i2c-analog-filter". Signed-off-by: Alain Volmat Reviewed-by: Pierre-Yves MORDRET Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-stm32f7.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index c62c815b88eb..140b1078146b 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -164,7 +164,6 @@ enum { #define STM32F7_I2C_DNF_DEFAULT 0 #define STM32F7_I2C_DNF_MAX 15 -#define STM32F7_I2C_ANALOG_FILTER_ENABLE 1 #define STM32F7_I2C_ANALOG_FILTER_DELAY_MIN 50 /* ns */ #define STM32F7_I2C_ANALOG_FILTER_DELAY_MAX 260 /* ns */ @@ -224,7 +223,6 @@ struct stm32f7_i2c_spec { * @rise_time: Rise time (ns) * @fall_time: Fall time (ns) * @dnf: Digital filter coefficient (0-16) - * @analog_filter: Analog filter delay (On/Off) * @fmp_clr_offset: Fast Mode Plus clear register offset from set register */ struct stm32f7_i2c_setup { @@ -233,7 +231,6 @@ struct stm32f7_i2c_setup { u32 rise_time; u32 fall_time; u8 dnf; - bool analog_filter; u32 fmp_clr_offset; }; @@ -312,6 +309,7 @@ struct stm32f7_i2c_msg { * @wakeup_src: boolean to know if the device is a wakeup source * @smbus_mode: states that the controller is configured in SMBus mode * @host_notify_client: SMBus host-notify client + * @analog_filter: boolean to indicate enabling of the analog filter */ struct stm32f7_i2c_dev { struct i2c_adapter adap; @@ -340,6 +338,7 @@ struct stm32f7_i2c_dev { bool wakeup_src; bool smbus_mode; struct i2c_client *host_notify_client; + bool analog_filter; }; /* @@ -386,14 +385,12 @@ static const struct stm32f7_i2c_setup stm32f7_setup = { .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT, .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT, .dnf = STM32F7_I2C_DNF_DEFAULT, - .analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE, }; static const struct stm32f7_i2c_setup stm32mp15_setup = { .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT, .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT, .dnf = STM32F7_I2C_DNF_DEFAULT, - .analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE, .fmp_clr_offset = 0x40, }; @@ -471,10 +468,10 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, /* Analog and Digital Filters */ af_delay_min = - (setup->analog_filter ? + (i2c_dev->analog_filter ? STM32F7_I2C_ANALOG_FILTER_DELAY_MIN : 0); af_delay_max = - (setup->analog_filter ? + (i2c_dev->analog_filter ? STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0); dnf_delay = setup->dnf * i2cclk; @@ -676,12 +673,15 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, return ret; } + i2c_dev->analog_filter = of_property_read_bool(i2c_dev->dev->of_node, + "i2c-analog-filter"); + dev_dbg(i2c_dev->dev, "I2C Speed(%i), Clk Source(%i)\n", setup->speed_freq, setup->clock_src); dev_dbg(i2c_dev->dev, "I2C Rise(%i) and Fall(%i) Time\n", setup->rise_time, setup->fall_time); dev_dbg(i2c_dev->dev, "I2C Analog Filter(%s), DNF(%i)\n", - (setup->analog_filter ? "On" : "Off"), setup->dnf); + (i2c_dev->analog_filter ? "On" : "Off"), setup->dnf); i2c_dev->bus_rate = setup->speed_freq; @@ -720,8 +720,8 @@ static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev) timing |= STM32F7_I2C_TIMINGR_SCLL(t->scll); writel_relaxed(timing, i2c_dev->base + STM32F7_I2C_TIMINGR); - /* Enable I2C */ - if (i2c_dev->setup.analog_filter) + /* Configure the Analog Filter */ + if (i2c_dev->analog_filter) stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_ANFOFF); else -- cgit v1.2.3 From 9449a558549978c1fe9af340164b17fa7d12af16 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Fri, 5 Feb 2021 09:51:42 +0100 Subject: i2c: stm32f7: add support for DNF i2c-digital-filter binding Add the support for the i2c-digital-filter binding, allowing to enable the digital filter via the device-tree and indicate its value in the DT. Signed-off-by: Alain Volmat Reviewed-by: Pierre-Yves MORDRET Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-stm32f7.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 140b1078146b..c019548e9ded 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -222,7 +222,6 @@ struct stm32f7_i2c_spec { * @clock_src: I2C clock source frequency (Hz) * @rise_time: Rise time (ns) * @fall_time: Fall time (ns) - * @dnf: Digital filter coefficient (0-16) * @fmp_clr_offset: Fast Mode Plus clear register offset from set register */ struct stm32f7_i2c_setup { @@ -230,7 +229,6 @@ struct stm32f7_i2c_setup { u32 clock_src; u32 rise_time; u32 fall_time; - u8 dnf; u32 fmp_clr_offset; }; @@ -310,6 +308,8 @@ struct stm32f7_i2c_msg { * @smbus_mode: states that the controller is configured in SMBus mode * @host_notify_client: SMBus host-notify client * @analog_filter: boolean to indicate enabling of the analog filter + * @dnf_dt: value of digital filter requested via dt + * @dnf: value of digital filter to apply */ struct stm32f7_i2c_dev { struct i2c_adapter adap; @@ -339,6 +339,8 @@ struct stm32f7_i2c_dev { bool smbus_mode; struct i2c_client *host_notify_client; bool analog_filter; + u32 dnf_dt; + u32 dnf; }; /* @@ -384,13 +386,11 @@ static struct stm32f7_i2c_spec stm32f7_i2c_specs[] = { static const struct stm32f7_i2c_setup stm32f7_setup = { .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT, .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT, - .dnf = STM32F7_I2C_DNF_DEFAULT, }; static const struct stm32f7_i2c_setup stm32mp15_setup = { .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT, .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT, - .dnf = STM32F7_I2C_DNF_DEFAULT, .fmp_clr_offset = 0x40, }; @@ -459,10 +459,11 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, return -EINVAL; } - if (setup->dnf > STM32F7_I2C_DNF_MAX) { + i2c_dev->dnf = DIV_ROUND_CLOSEST(i2c_dev->dnf_dt, i2cclk); + if (i2c_dev->dnf > STM32F7_I2C_DNF_MAX) { dev_err(i2c_dev->dev, "DNF out of bound %d/%d\n", - setup->dnf, STM32F7_I2C_DNF_MAX); + i2c_dev->dnf * i2cclk, STM32F7_I2C_DNF_MAX * i2cclk); return -EINVAL; } @@ -473,13 +474,13 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, af_delay_max = (i2c_dev->analog_filter ? STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0); - dnf_delay = setup->dnf * i2cclk; + dnf_delay = i2c_dev->dnf * i2cclk; sdadel_min = specs->hddat_min + setup->fall_time - - af_delay_min - (setup->dnf + 3) * i2cclk; + af_delay_min - (i2c_dev->dnf + 3) * i2cclk; sdadel_max = specs->vddat_max - setup->rise_time - - af_delay_max - (setup->dnf + 4) * i2cclk; + af_delay_max - (i2c_dev->dnf + 4) * i2cclk; scldel_min = setup->rise_time + specs->sudat_min; @@ -645,6 +646,7 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, setup->speed_freq = t->bus_freq_hz; i2c_dev->setup.rise_time = t->scl_rise_ns; i2c_dev->setup.fall_time = t->scl_fall_ns; + i2c_dev->dnf_dt = t->digital_filter_width_ns; setup->clock_src = clk_get_rate(i2c_dev->clk); if (!setup->clock_src) { @@ -652,6 +654,9 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, return -EINVAL; } + if (!of_property_read_bool(i2c_dev->dev->of_node, "i2c-digital-filter")) + i2c_dev->dnf_dt = STM32F7_I2C_DNF_DEFAULT; + do { ret = stm32f7_i2c_compute_timing(i2c_dev, setup, &i2c_dev->timing); @@ -681,7 +686,7 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, dev_dbg(i2c_dev->dev, "I2C Rise(%i) and Fall(%i) Time\n", setup->rise_time, setup->fall_time); dev_dbg(i2c_dev->dev, "I2C Analog Filter(%s), DNF(%i)\n", - (i2c_dev->analog_filter ? "On" : "Off"), setup->dnf); + (i2c_dev->analog_filter ? "On" : "Off"), i2c_dev->dnf); i2c_dev->bus_rate = setup->speed_freq; @@ -732,7 +737,7 @@ static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev) stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_DNF_MASK); stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1, - STM32F7_I2C_CR1_DNF(i2c_dev->setup.dnf)); + STM32F7_I2C_CR1_DNF(i2c_dev->dnf)); stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_PE); -- cgit v1.2.3 From b87752528fe562f01034168b7e4c476b200bc456 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Fri, 5 Feb 2021 09:51:44 +0100 Subject: i2c: stm32f7: indicate the address being accessed on errors To help debugging issues, add the address of the slave being accessed when getting an error. Signed-off-by: Alain Volmat Signed-off-by: Pierre-Yves MORDRET Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-stm32f7.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index c019548e9ded..162a3b069261 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -1602,7 +1602,8 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data) /* Bus error */ if (status & STM32F7_I2C_ISR_BERR) { - dev_err(dev, "<%s>: Bus error\n", __func__); + dev_err(dev, "<%s>: Bus error accessing addr 0x%x\n", + __func__, f7_msg->addr); writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR); stm32f7_i2c_release_bus(&i2c_dev->adap); f7_msg->result = -EIO; @@ -1610,13 +1611,15 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data) /* Arbitration loss */ if (status & STM32F7_I2C_ISR_ARLO) { - dev_dbg(dev, "<%s>: Arbitration loss\n", __func__); + dev_dbg(dev, "<%s>: Arbitration loss accessing addr 0x%x\n", + __func__, f7_msg->addr); writel_relaxed(STM32F7_I2C_ICR_ARLOCF, base + STM32F7_I2C_ICR); f7_msg->result = -EAGAIN; } if (status & STM32F7_I2C_ISR_PECERR) { - dev_err(dev, "<%s>: PEC error in reception\n", __func__); + dev_err(dev, "<%s>: PEC error in reception accessing addr 0x%x\n", + __func__, f7_msg->addr); writel_relaxed(STM32F7_I2C_ICR_PECCF, base + STM32F7_I2C_ICR); f7_msg->result = -EINVAL; } -- cgit v1.2.3 From 82531dfdf163319cbeaa969c734ebf8d84456810 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Fri, 12 Feb 2021 17:45:41 +0100 Subject: i2c: rcar: implement atomic transfers Implements atomic transfers. Tested by rebooting an r8a7790 Lager board after connecting the i2c-rcar controller to the PMIC in arch/arm/boot/dts/r8a7790-lager.dts like so: compatible = "i2c-demux-pinctrl"; pinctrl-names = "default"; pinctrl-0 = <&pmic_irq_pins>; - i2c-parent = <&iic3>, <&i2c3>; + i2c-parent = <&i2c3>, <&iic3>; i2c-bus-name = "i2c-pwr"; #address-cells = <1>; #size-cells = <0>; Signed-off-by: Ulrich Hecht Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 84 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 12f6d452c0f7..1f0c164f671a 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -141,6 +141,7 @@ struct rcar_i2c_priv { enum dma_data_direction dma_direction; struct reset_control *rstc; + bool atomic_xfer; int irq; struct i2c_client *host_notify_client; @@ -353,7 +354,9 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv) rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START); rcar_i2c_write(priv, ICMSR, 0); } - rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND); + + if (!priv->atomic_xfer) + rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND); } static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv) @@ -418,7 +421,7 @@ static bool rcar_i2c_dma(struct rcar_i2c_priv *priv) int len; /* Do various checks to see if DMA is feasible at all */ - if (IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN || + if (priv->atomic_xfer || IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN || !(msg->flags & I2C_M_DMA_SAFE) || (read && priv->flags & ID_P_NO_RXDMA)) return false; @@ -646,7 +649,8 @@ static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr) /* Nack */ if (msr & MNR) { /* HW automatically sends STOP after received NACK */ - rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP); + if (!priv->atomic_xfer) + rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP); priv->flags |= ID_NACK; goto out; } @@ -667,7 +671,8 @@ out: if (priv->flags & ID_DONE) { rcar_i2c_write(priv, ICMIER, 0); rcar_i2c_write(priv, ICMSR, 0); - wake_up(&priv->wait); + if (!priv->atomic_xfer) + wake_up(&priv->wait); } return IRQ_HANDLED; @@ -684,7 +689,8 @@ static irqreturn_t rcar_i2c_gen2_irq(int irq, void *ptr) /* Only handle interrupts that are currently enabled */ msr = rcar_i2c_read(priv, ICMSR); - msr &= rcar_i2c_read(priv, ICMIER); + if (!priv->atomic_xfer) + msr &= rcar_i2c_read(priv, ICMIER); return rcar_i2c_irq(irq, priv, msr); } @@ -696,7 +702,8 @@ static irqreturn_t rcar_i2c_gen3_irq(int irq, void *ptr) /* Only handle interrupts that are currently enabled */ msr = rcar_i2c_read(priv, ICMSR); - msr &= rcar_i2c_read(priv, ICMIER); + if (!priv->atomic_xfer) + msr &= rcar_i2c_read(priv, ICMIER); /* * Clear START or STOP immediately, except for REPSTART after read or @@ -804,6 +811,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, int i, ret; long time_left; + priv->atomic_xfer = false; + pm_runtime_get_sync(dev); /* Check bus state before init otherwise bus busy info will be lost */ @@ -858,6 +867,68 @@ out: return ret; } +static int rcar_i2c_master_xfer_atomic(struct i2c_adapter *adap, + struct i2c_msg *msgs, + int num) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap); + struct device *dev = rcar_i2c_priv_to_dev(priv); + unsigned long j; + bool time_left; + int ret; + + priv->atomic_xfer = true; + + pm_runtime_get_sync(dev); + + /* Check bus state before init otherwise bus busy info will be lost */ + ret = rcar_i2c_bus_barrier(priv); + if (ret < 0) + goto out; + + rcar_i2c_init(priv); + + /* init first message */ + priv->msg = msgs; + priv->msgs_left = num; + priv->flags = (priv->flags & ID_P_MASK) | ID_FIRST_MSG; + rcar_i2c_prepare_msg(priv); + + j = jiffies + num * adap->timeout; + do { + u32 msr = rcar_i2c_read(priv, ICMSR); + + msr &= (rcar_i2c_is_recv(priv) ? RCAR_IRQ_RECV : RCAR_IRQ_SEND) | RCAR_IRQ_STOP; + + if (msr) { + if (priv->devtype < I2C_RCAR_GEN3) + rcar_i2c_gen2_irq(0, priv); + else + rcar_i2c_gen3_irq(0, priv); + } + + time_left = time_before_eq(jiffies, j); + } while (!(priv->flags & ID_DONE) && time_left); + + if (!time_left) { + rcar_i2c_init(priv); + ret = -ETIMEDOUT; + } else if (priv->flags & ID_NACK) { + ret = -ENXIO; + } else if (priv->flags & ID_ARBLOST) { + ret = -EAGAIN; + } else { + ret = num - priv->msgs_left; /* The number of transfer */ + } +out: + pm_runtime_put(dev); + + if (ret < 0 && ret != -ENXIO) + dev_err(dev, "error %d : %x\n", ret, priv->flags); + + return ret; +} + static int rcar_reg_slave(struct i2c_client *slave) { struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter); @@ -922,6 +993,7 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap) static const struct i2c_algorithm rcar_i2c_algo = { .master_xfer = rcar_i2c_master_xfer, + .master_xfer_atomic = rcar_i2c_master_xfer_atomic, .functionality = rcar_i2c_func, .reg_slave = rcar_reg_slave, .unreg_slave = rcar_unreg_slave, -- cgit v1.2.3 From f1e1bf76bc20f617945dc44226067a07ccf02bea Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 12 Feb 2021 18:03:50 +0100 Subject: i2c: powermac: remove uncertainty about SMBUS_BLOCK transfers The driver does the correct thing, so no need to question it. Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-powermac.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 3e38e114948b..5241e6f414e9 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -76,11 +76,6 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap, * but I think the current API makes no sense and I don't want * any driver that I haven't verified for correctness to go * anywhere near a pmac i2c bus anyway ... - * - * I'm also not completely sure what kind of phases to do between - * the actual command and the data (what I am _supposed_ to do that - * is). For now, I assume writes are a single stream and reads have - * a repeat start/addr phase (but not stop in between) */ case I2C_SMBUS_BLOCK_DATA: buf = data->block; -- cgit v1.2.3 From fd6c3f45bf300b57e45b1b87786907bcb61f61e1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 5 Mar 2021 19:28:30 +0100 Subject: i2c: i2c-scmi: Drop unused ACPI_MODULE_NAME definition The ACPI_MODULE_NAME() definition is only used by the message printing macros from ACPICA that are not used by the code in question, so it is redundant. Drop it. No functional impact. Signed-off-by: Rafael J. Wysocki Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-scmi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c index 1dc387392e74..6746aa46d96c 100644 --- a/drivers/i2c/busses/i2c-scmi.c +++ b/drivers/i2c/busses/i2c-scmi.c @@ -18,8 +18,6 @@ /* SMBUS HID definition as supported by Microsoft Windows */ #define ACPI_SMBUS_MS_HID "SMB0001" -ACPI_MODULE_NAME("smbus_cmi"); - struct smbus_methods_t { char *mt_info; char *mt_sbr; -- cgit v1.2.3 From 71581562ee36032d2d574a9b23ad4af6d6a64cf7 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 12 Mar 2021 12:57:34 +0100 Subject: i2c: bail out early when RDWR parameters are wrong The buggy parameters currently get caught later, but emit a noisy WARN. Userspace should not be able to trigger this, so add similar checks much earlier. Also avoids some unneeded code paths, of course. Apply kernel coding stlye to a comment while here. Reported-by: syzbot+ffb0b3ffa6cfbc7d7b3f@syzkaller.appspotmail.com Tested-by: syzbot+ffb0b3ffa6cfbc7d7b3f@syzkaller.appspotmail.com Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-dev.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 6ceb11cc4be1..6ef38a8ee95c 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -440,8 +440,13 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) sizeof(rdwr_arg))) return -EFAULT; - /* Put an arbitrary limit on the number of messages that can - * be sent at once */ + if (!rdwr_arg.msgs || rdwr_arg.nmsgs == 0) + return -EINVAL; + + /* + * Put an arbitrary limit on the number of messages that can + * be sent at once + */ if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS) return -EINVAL; -- cgit v1.2.3 From 1a0e240d09413ac6c90ce6247afdfc424c84103f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 23 Mar 2021 16:57:13 +0100 Subject: i2c: tegra-bpmp: Implement better error handling Inspect a message's return value upon successful IVC transaction to determine if the I2C transaction on the BPMP side was successful. Heavily based on work by Timo Alho . Signed-off-by: Thierry Reding Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra-bpmp.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-tegra-bpmp.c b/drivers/i2c/busses/i2c-tegra-bpmp.c index c0c7d01473f2..c934d636f625 100644 --- a/drivers/i2c/busses/i2c-tegra-bpmp.c +++ b/drivers/i2c/busses/i2c-tegra-bpmp.c @@ -217,7 +217,32 @@ static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c, else err = tegra_bpmp_transfer(i2c->bpmp, &msg); - return err; + if (err < 0) { + dev_err(i2c->dev, "failed to transfer message: %d\n", err); + return err; + } + + if (msg.rx.ret != 0) { + if (msg.rx.ret == -BPMP_EAGAIN) { + dev_dbg(i2c->dev, "arbitration lost\n"); + return -EAGAIN; + } + + if (msg.rx.ret == -BPMP_ETIMEDOUT) { + dev_dbg(i2c->dev, "timeout\n"); + return -ETIMEDOUT; + } + + if (msg.rx.ret == -BPMP_ENXIO) { + dev_dbg(i2c->dev, "NAK\n"); + return -ENXIO; + } + + dev_err(i2c->dev, "transaction failed: %d\n", msg.rx.ret); + return -EIO; + } + + return 0; } static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter, -- cgit v1.2.3 From 8f66edb25c4e10f8380c88f4c097158ff611fd10 Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Thu, 25 Mar 2021 20:04:32 +0800 Subject: i2c: stm32f7: Remove useless error message Fix the following coccicheck report: drivers/i2c/busses/i2c-stm32f7.c:2032:3-10 : platform_get_irq : line 2032 is redundant because platform_get_irq() already prints an error Remove dev_err() messages after platform_get_irq() failures. Signed-off-by: Tian Tao Signed-off-by: Zihao Tang Reviewed-by: Alain Volmat Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-stm32f7.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 162a3b069261..9fbcbd1807df 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -2035,12 +2035,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) phy_addr = (dma_addr_t)res->start; irq_event = platform_get_irq(pdev, 0); - if (irq_event <= 0) { - if (irq_event != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to get IRQ event: %d\n", - irq_event); + if (irq_event <= 0) return irq_event ? : -ENOENT; - } irq_error = platform_get_irq(pdev, 1); if (irq_error <= 0) -- cgit v1.2.3 From c126f7c3b8c41f5ca146e52e70ae927e3be30060 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 29 Mar 2021 22:55:30 +0300 Subject: i2c: Make i2c_recover_bus() to return -EBUSY if bus recovery unimplemented The i2c_recover_bus() returns -EOPNOTSUPP if bus recovery isn't wired up by the bus driver, which the case for Tegra I2C driver for example. This error code is then propagated to I2C client and might be confusing, thus make i2c_recover_bus() to return -EBUSY instead. Suggested-by: Wolfram Sang Signed-off-by: Dmitry Osipenko Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 63ebf722a424..e7be127eeee4 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -249,7 +249,7 @@ EXPORT_SYMBOL_GPL(i2c_generic_scl_recovery); int i2c_recover_bus(struct i2c_adapter *adap) { if (!adap->bus_recovery_info) - return -EOPNOTSUPP; + return -EBUSY; dev_dbg(&adap->dev, "Trying i2c bus recovery\n"); return adap->bus_recovery_info->recover_bus(adap); -- cgit v1.2.3 From aca01415e076aa96cca0f801f4420ee5c10c660d Mon Sep 17 00:00:00 2001 From: Bence Csókás Date: Wed, 31 Mar 2021 19:19:20 +0000 Subject: i2c: Add I2C_AQ_NO_REP_START adapter quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This quirk signifies that the adapter cannot do a repeated START, it always issues a STOP condition after transfers. Suggested-by: Wolfram Sang Signed-off-by: Bence Csókás Signed-off-by: Wolfram Sang --- include/linux/i2c.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 56622658b215..a670ae129f4b 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -687,6 +687,8 @@ struct i2c_adapter_quirks { #define I2C_AQ_NO_ZERO_LEN_READ BIT(5) #define I2C_AQ_NO_ZERO_LEN_WRITE BIT(6) #define I2C_AQ_NO_ZERO_LEN (I2C_AQ_NO_ZERO_LEN_READ | I2C_AQ_NO_ZERO_LEN_WRITE) +/* adapter cannot do repeated START */ +#define I2C_AQ_NO_REP_START BIT(7) /* * i2c_adapter is the structure used to identify a physical i2c bus along -- cgit v1.2.3 From 4a7695429eade517b07ea72f9ec366130e81a076 Mon Sep 17 00:00:00 2001 From: Bence Csókás Date: Wed, 31 Mar 2021 19:19:21 +0000 Subject: i2c: cp2615: add i2c driver for Silicon Labs' CP2615 Digital Audio Bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create an i2c_adapter for CP2615's I2C master interface Signed-off-by: Bence Csókás [wsa: switched to '__packed', added some 'static' and an include] Signed-off-by: Wolfram Sang --- MAINTAINERS | 5 + drivers/i2c/busses/Kconfig | 10 ++ drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-cp2615.c | 330 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 346 insertions(+) create mode 100644 drivers/i2c/busses/i2c-cp2615.c diff --git a/MAINTAINERS b/MAINTAINERS index aa84121c5611..bffcf6a77ae9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4585,6 +4585,11 @@ F: drivers/counter/ F: include/linux/counter.h F: include/linux/counter_enum.h +CP2615 I2C DRIVER +M: Bence Csókás +S: Maintained +F: drivers/i2c/busses/i2c-cp2615.c + CPMAC ETHERNET DRIVER M: Florian Fainelli L: netdev@vger.kernel.org diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 05ebf7546e3f..338a306ec39e 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1199,6 +1199,16 @@ config I2C_DLN2 This driver can also be built as a module. If so, the module will be called i2c-dln2. +config I2C_CP2615 + tristate "Silicon Labs CP2615 USB sound card and I2C adapter" + depends on USB + help + If you say yes to this option, support will be included for Silicon + Labs CP2615's I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-cp2615. + config I2C_PARPORT tristate "Parallel port adapter" depends on PARPORT diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 615f35e3e31f..45cd53515c77 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -123,6 +123,7 @@ obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o # External I2C/SMBus adapter drivers obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o +obj-$(CONFIG_I2C_CP2615) += i2c-cp2615.o obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o diff --git a/drivers/i2c/busses/i2c-cp2615.c b/drivers/i2c/busses/i2c-cp2615.c new file mode 100644 index 000000000000..78cfecd1ea76 --- /dev/null +++ b/drivers/i2c/busses/i2c-cp2615.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * i2c support for Silicon Labs' CP2615 Digital Audio Bridge + * + * (c) 2021, Bence Csókás + */ + +#include +#include +#include +#include +#include +#include + +/** CP2615 I/O Protocol implementation */ + +#define CP2615_VID 0x10c4 +#define CP2615_PID 0xeac1 + +#define IOP_EP_IN 0x82 +#define IOP_EP_OUT 0x02 +#define IOP_IFN 1 +#define IOP_ALTSETTING 2 + +#define MAX_IOP_SIZE 64 +#define MAX_IOP_PAYLOAD_SIZE (MAX_IOP_SIZE - 6) +#define MAX_I2C_SIZE (MAX_IOP_PAYLOAD_SIZE - 4) + +enum cp2615_iop_msg_type { + iop_GetAccessoryInfo = 0xD100, + iop_AccessoryInfo = 0xA100, + iop_GetPortConfiguration = 0xD203, + iop_PortConfiguration = 0xA203, + iop_DoI2cTransfer = 0xD400, + iop_I2cTransferResult = 0xA400, + iop_GetSerialState = 0xD501, + iop_SerialState = 0xA501 +}; + +struct __packed cp2615_iop_msg { + __be16 preamble, length, msg; + u8 data[MAX_IOP_PAYLOAD_SIZE]; +}; + +#define PART_ID_A01 0x1400 +#define PART_ID_A02 0x1500 + +struct __packed cp2615_iop_accessory_info { + __be16 part_id, option_id, proto_ver; +}; + +struct __packed cp2615_i2c_transfer { + u8 tag, i2caddr, read_len, write_len; + u8 data[MAX_I2C_SIZE]; +}; + +/* Possible values for struct cp2615_i2c_transfer_result.status */ +enum cp2615_i2c_status { + /* Writing to the internal EEPROM failed, because it is locked */ + CP2615_CFG_LOCKED = -6, + /* read_len or write_len out of range */ + CP2615_INVALID_PARAM = -4, + /* I2C slave did not ACK in time */ + CP2615_TIMEOUT, + /* I2C bus busy */ + CP2615_BUS_BUSY, + /* I2C bus error (ie. device NAK'd the request) */ + CP2615_BUS_ERROR, + CP2615_SUCCESS +}; + +struct __packed cp2615_i2c_transfer_result { + u8 tag, i2caddr; + s8 status; + u8 read_len; + u8 data[MAX_I2C_SIZE]; +}; + +static int cp2615_init_iop_msg(struct cp2615_iop_msg *ret, enum cp2615_iop_msg_type msg, + const void *data, size_t data_len) +{ + if (data_len > MAX_IOP_PAYLOAD_SIZE) + return -EFBIG; + + if (!ret) + return -EINVAL; + + ret->preamble = 0x2A2A; + ret->length = htons(data_len + 6); + ret->msg = htons(msg); + if (data && data_len) + memcpy(&ret->data, data, data_len); + return 0; +} + +static int cp2615_init_i2c_msg(struct cp2615_iop_msg *ret, const struct cp2615_i2c_transfer *data) +{ + return cp2615_init_iop_msg(ret, iop_DoI2cTransfer, data, 4 + data->write_len); +} + +/* Translates status codes to Linux errno's */ +static int cp2615_check_status(enum cp2615_i2c_status status) +{ + switch (status) { + case CP2615_SUCCESS: + return 0; + case CP2615_BUS_ERROR: + return -ENXIO; + case CP2615_BUS_BUSY: + return -EAGAIN; + case CP2615_TIMEOUT: + return -ETIMEDOUT; + case CP2615_INVALID_PARAM: + return -EINVAL; + case CP2615_CFG_LOCKED: + return -EPERM; + } + /* Unknown error code */ + return -EPROTO; +} + +/** Driver code */ + +static int +cp2615_i2c_send(struct usb_interface *usbif, struct cp2615_i2c_transfer *i2c_w) +{ + struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + struct usb_device *usbdev = interface_to_usbdev(usbif); + int res = cp2615_init_i2c_msg(msg, i2c_w); + + if (!res) + res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT), + msg, ntohs(msg->length), NULL, 0); + kfree(msg); + return res; +} + +static int +cp2615_i2c_recv(struct usb_interface *usbif, unsigned char tag, void *buf) +{ + struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + struct cp2615_i2c_transfer_result *i2c_r = (struct cp2615_i2c_transfer_result *)&msg->data; + struct usb_device *usbdev = interface_to_usbdev(usbif); + int res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN), + msg, sizeof(struct cp2615_iop_msg), NULL, 0); + + if (res < 0) { + kfree(msg); + return res; + } + + if (msg->msg != htons(iop_I2cTransferResult) || i2c_r->tag != tag) { + kfree(msg); + return -EIO; + } + + res = cp2615_check_status(i2c_r->status); + if (!res) + memcpy(buf, &i2c_r->data, i2c_r->read_len); + + kfree(msg); + return res; +} + +/* Checks if the IOP is functional by querying the part's ID */ +static int cp2615_check_iop(struct usb_interface *usbif) +{ + struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + struct cp2615_iop_accessory_info *info = (struct cp2615_iop_accessory_info *)&msg->data; + struct usb_device *usbdev = interface_to_usbdev(usbif); + int res = cp2615_init_iop_msg(msg, iop_GetAccessoryInfo, NULL, 0); + + if (res) + goto out; + + res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT), + msg, ntohs(msg->length), NULL, 0); + if (res) + goto out; + + res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN), + msg, sizeof(struct cp2615_iop_msg), NULL, 0); + if (res) + goto out; + + if (msg->msg != htons(iop_AccessoryInfo)) { + res = -EIO; + goto out; + } + + switch (ntohs(info->part_id)) { + case PART_ID_A01: + dev_dbg(&usbif->dev, "Found A01 part. (WARNING: errata exists!)\n"); + break; + case PART_ID_A02: + dev_dbg(&usbif->dev, "Found good A02 part.\n"); + break; + default: + dev_warn(&usbif->dev, "Unknown part ID %04X\n", ntohs(info->part_id)); + } + +out: + kfree(msg); + return res; +} + +static int +cp2615_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct usb_interface *usbif = adap->algo_data; + int i = 0, ret = 0; + struct i2c_msg *msg; + struct cp2615_i2c_transfer i2c_w = {0}; + + dev_dbg(&usbif->dev, "Doing %d I2C transactions\n", num); + + for (; !ret && i < num; i++) { + msg = &msgs[i]; + + i2c_w.tag = 0xdd; + i2c_w.i2caddr = i2c_8bit_addr_from_msg(msg); + if (msg->flags & I2C_M_RD) { + i2c_w.read_len = msg->len; + i2c_w.write_len = 0; + } else { + i2c_w.read_len = 0; + i2c_w.write_len = msg->len; + memcpy(&i2c_w.data, msg->buf, i2c_w.write_len); + } + ret = cp2615_i2c_send(usbif, &i2c_w); + if (ret) + break; + ret = cp2615_i2c_recv(usbif, i2c_w.tag, msg->buf); + } + if (ret < 0) + return ret; + return i; +} + +static u32 +cp2615_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm cp2615_i2c_algo = { + .master_xfer = cp2615_i2c_master_xfer, + .functionality = cp2615_i2c_func, +}; + +/* + * This chip has some limitations: one is that the USB endpoint + * can only receive 64 bytes/transfer, that leaves 54 bytes for + * the I2C transfer. On top of that, EITHER read_len OR write_len + * may be zero, but not both. If both are non-zero, the adapter + * issues a write followed by a read. And the chip does not + * support repeated START between the write and read phases. + */ +static struct i2c_adapter_quirks cp2615_i2c_quirks = { + .max_write_len = MAX_I2C_SIZE, + .max_read_len = MAX_I2C_SIZE, + .flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN | I2C_AQ_NO_REP_START, + .max_comb_1st_msg_len = MAX_I2C_SIZE, + .max_comb_2nd_msg_len = MAX_I2C_SIZE +}; + +static void +cp2615_i2c_remove(struct usb_interface *usbif) +{ + struct i2c_adapter *adap = usb_get_intfdata(usbif); + + usb_set_intfdata(usbif, NULL); + i2c_del_adapter(adap); +} + +static int +cp2615_i2c_probe(struct usb_interface *usbif, const struct usb_device_id *id) +{ + int ret = 0; + struct i2c_adapter *adap; + struct usb_device *usbdev = interface_to_usbdev(usbif); + + ret = usb_set_interface(usbdev, IOP_IFN, IOP_ALTSETTING); + if (ret) + return ret; + + ret = cp2615_check_iop(usbif); + if (ret) + return ret; + + adap = devm_kzalloc(&usbif->dev, sizeof(struct i2c_adapter), GFP_KERNEL); + if (!adap) + return -ENOMEM; + + strncpy(adap->name, usbdev->serial, sizeof(adap->name) - 1); + adap->owner = THIS_MODULE; + adap->dev.parent = &usbif->dev; + adap->dev.of_node = usbif->dev.of_node; + adap->timeout = HZ; + adap->algo = &cp2615_i2c_algo; + adap->quirks = &cp2615_i2c_quirks; + adap->algo_data = usbif; + + ret = i2c_add_adapter(adap); + if (ret) + return ret; + + usb_set_intfdata(usbif, adap); + return 0; +} + +static const struct usb_device_id id_table[] = { + { USB_DEVICE_INTERFACE_NUMBER(CP2615_VID, CP2615_PID, IOP_IFN) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, id_table); + +static struct usb_driver cp2615_i2c_driver = { + .name = "i2c-cp2615", + .probe = cp2615_i2c_probe, + .disconnect = cp2615_i2c_remove, + .id_table = id_table, +}; + +module_usb_driver(cp2615_i2c_driver); + +MODULE_AUTHOR("Bence Csókás "); +MODULE_DESCRIPTION("CP2615 I2C bus driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 17631e8ca2d3421090e54b39d9a1402091019ba1 Mon Sep 17 00:00:00 2001 From: Sanket Goswami Date: Wed, 31 Mar 2021 19:37:30 +0530 Subject: i2c: designware: Add driver support for AMD NAVI GPU The Latest AMD NAVI GPU card has an integrated Type-C controller and Designware I2C with PCI Interface. The PD controller for USB Type-C can be accessed over I2C. The client driver is part of the USB Type-C UCSI driver. Also, there exists a couple of notable IP limitations that are dealt as workarounds: - I2C transaction work on a polling mode as IP does not generate interrupt. - I2C read command sent twice to address the IP issues. - AMD NAVI GPU based products are already in the commercial market, hence some of the I2C parameters are statically programmed as they can not be part of the ACPI table. Reviewed-by: Shyam Sundar S K Co-developed-by: Nehal Bakulchandra Shah Signed-off-by: Nehal Bakulchandra Shah Signed-off-by: Sanket Goswami Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-common.c | 3 + drivers/i2c/busses/i2c-designware-core.h | 8 ++ drivers/i2c/busses/i2c-designware-master.c | 133 +++++++++++++++++++++++++++++ drivers/i2c/busses/i2c-designware-pcidrv.c | 61 +++++++++++++ 4 files changed, 205 insertions(+) diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index 3c19aada4b30..fdc34d9e3702 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -150,6 +150,9 @@ int i2c_dw_init_regmap(struct dw_i2c_dev *dev) reg = readl(dev->base + DW_IC_COMP_TYPE); i2c_dw_release_lock(dev); + if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) + map_cfg.max_register = AMD_UCSI_INTR_REG; + if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) { map_cfg.reg_read = dw_reg_read_swab; map_cfg.reg_write = dw_reg_write_swab; diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 5392b82f68a4..6a53f75abf7c 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -295,8 +295,16 @@ struct dw_i2c_dev { #define MODEL_MSCC_OCELOT BIT(8) #define MODEL_BAIKAL_BT1 BIT(9) +#define MODEL_AMD_NAVI_GPU BIT(10) #define MODEL_MASK GENMASK(11, 8) +/* + * Enable UCSI interrupt by writing 0xd at register + * offset 0x474 specified in hardware specification. + */ +#define AMD_UCSI_INTR_REG 0x474 +#define AMD_UCSI_INTR_EN 0xd + int i2c_dw_init_regmap(struct dw_i2c_dev *dev); u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset); u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset); diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index dd27b9dbe931..e288b654cb47 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -23,6 +23,10 @@ #include "i2c-designware-core.h" +#define AMD_TIMEOUT_MIN_US 25 +#define AMD_TIMEOUT_MAX_US 250 +#define AMD_MASTERCFG_MASK GENMASK(15, 0) + static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev) { /* Configure Tx/Rx FIFO threshold levels */ @@ -259,6 +263,108 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_MASTER_MASK); } +static int i2c_dw_check_stopbit(struct dw_i2c_dev *dev) +{ + u32 val; + int ret; + + ret = regmap_read_poll_timeout(dev->map, DW_IC_INTR_STAT, val, + !(val & DW_IC_INTR_STOP_DET), + 1100, 20000); + if (ret) + dev_err(dev->dev, "i2c timeout error %d\n", ret); + + return ret; +} + +static int i2c_dw_status(struct dw_i2c_dev *dev) +{ + int status; + + status = i2c_dw_wait_bus_not_busy(dev); + if (status) + return status; + + return i2c_dw_check_stopbit(dev); +} + +/* + * Initiate and continue master read/write transaction with polling + * based transfer routine afterward write messages into the Tx buffer. + */ +static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs, int num_msgs) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + int msg_wrt_idx, msg_itr_lmt, buf_len, data_idx; + int cmd = 0, status; + u8 *tx_buf; + u32 val; + + /* + * In order to enable the interrupt for UCSI i.e. AMD NAVI GPU card, + * it is mandatory to set the right value in specific register + * (offset:0x474) as per the hardware IP specification. + */ + regmap_write(dev->map, AMD_UCSI_INTR_REG, AMD_UCSI_INTR_EN); + + dev->msgs = msgs; + dev->msgs_num = num_msgs; + i2c_dw_xfer_init(dev); + i2c_dw_disable_int(dev); + + /* Initiate messages read/write transaction */ + for (msg_wrt_idx = 0; msg_wrt_idx < num_msgs; msg_wrt_idx++) { + tx_buf = msgs[msg_wrt_idx].buf; + buf_len = msgs[msg_wrt_idx].len; + + if (!(msgs[msg_wrt_idx].flags & I2C_M_RD)) + regmap_write(dev->map, DW_IC_TX_TL, buf_len - 1); + /* + * Initiate the i2c read/write transaction of buffer length, + * and poll for bus busy status. For the last message transfer, + * update the command with stopbit enable. + */ + for (msg_itr_lmt = buf_len; msg_itr_lmt > 0; msg_itr_lmt--) { + if (msg_wrt_idx == num_msgs - 1 && msg_itr_lmt == 1) + cmd |= BIT(9); + + if (msgs[msg_wrt_idx].flags & I2C_M_RD) { + /* Due to hardware bug, need to write the same command twice. */ + regmap_write(dev->map, DW_IC_DATA_CMD, 0x100); + regmap_write(dev->map, DW_IC_DATA_CMD, 0x100 | cmd); + if (cmd) { + regmap_write(dev->map, DW_IC_TX_TL, 2 * (buf_len - 1)); + regmap_write(dev->map, DW_IC_RX_TL, 2 * (buf_len - 1)); + /* + * Need to check the stop bit. However, it cannot be + * detected from the registers so we check it always + * when read/write the last byte. + */ + status = i2c_dw_status(dev); + if (status) + return status; + + for (data_idx = 0; data_idx < buf_len; data_idx++) { + regmap_read(dev->map, DW_IC_DATA_CMD, &val); + tx_buf[data_idx] = val; + } + status = i2c_dw_check_stopbit(dev); + if (status) + return status; + } + } else { + regmap_write(dev->map, DW_IC_DATA_CMD, *tx_buf++ | cmd); + usleep_range(AMD_TIMEOUT_MIN_US, AMD_TIMEOUT_MAX_US); + } + } + status = i2c_dw_check_stopbit(dev); + if (status) + return status; + } + + return 0; +} + /* * Initiate (and continue) low level master read/write transaction. * This function is only called from i2c_dw_isr, and pumping i2c_msg @@ -462,6 +568,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) pm_runtime_get_sync(dev->dev); + /* + * Initiate I2C message transfer when AMD NAVI GPU card is enabled, + * As it is polling based transfer mechanism, which does not support + * interrupt based functionalities of existing DesignWare driver. + */ + if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) { + ret = amd_i2c_dw_xfer_quirk(adap, msgs, num); + goto done_nolock; + } + if (dev_WARN_ONCE(dev->dev, dev->suspended, "Transfer while suspended\n")) { ret = -ESHUTDOWN; goto done_nolock; @@ -738,6 +854,20 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev) return 0; } +static int amd_i2c_adap_quirk(struct dw_i2c_dev *dev) +{ + struct i2c_adapter *adap = &dev->adapter; + int ret; + + pm_runtime_get_noresume(dev->dev); + ret = i2c_add_numbered_adapter(adap); + if (ret) + dev_err(dev->dev, "Failed to add adapter: %d\n", ret); + pm_runtime_put_noidle(dev->dev); + + return ret; +} + int i2c_dw_probe_master(struct dw_i2c_dev *dev) { struct i2c_adapter *adap = &dev->adapter; @@ -774,6 +904,9 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev) adap->dev.parent = dev->dev; i2c_set_adapdata(adap, dev); + if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) + return amd_i2c_adap_quirk(dev); + if (dev->flags & ACCESS_NO_IRQ_SUSPEND) { irq_flags = IRQF_NO_SUSPEND; } else { diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 55c83a7a24f3..7ca0017883a6 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -26,6 +26,7 @@ #include "i2c-designware-core.h" #define DRIVER_NAME "i2c-designware-pci" +#define AMD_CLK_RATE_HZ 100000 enum dw_pci_ctl_id_t { medfield, @@ -34,6 +35,7 @@ enum dw_pci_ctl_id_t { cherrytrail, haswell, elkhartlake, + navi_amd, }; struct dw_scl_sda_cfg { @@ -78,11 +80,23 @@ static struct dw_scl_sda_cfg hsw_config = { .sda_hold = 0x9, }; +/* NAVI-AMD HCNT/LCNT/SDA hold time */ +static struct dw_scl_sda_cfg navi_amd_config = { + .ss_hcnt = 0x1ae, + .ss_lcnt = 0x23a, + .sda_hold = 0x9, +}; + static u32 mfld_get_clk_rate_khz(struct dw_i2c_dev *dev) { return 25000; } +static u32 navi_amd_get_clk_rate_khz(struct dw_i2c_dev *dev) +{ + return AMD_CLK_RATE_HZ; +} + static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c) { struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev); @@ -104,6 +118,35 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c) return -ENODEV; } + /* + * TODO find a better way how to deduplicate instantiation + * of USB PD slave device from nVidia GPU driver. + */ +static int navi_amd_register_client(struct dw_i2c_dev *dev) +{ + struct i2c_board_info info; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strscpy(info.type, "ccgx-ucsi", I2C_NAME_SIZE); + info.addr = 0x08; + info.irq = dev->irq; + + dev->slave = i2c_new_client_device(&dev->adapter, &info); + if (!dev->slave) + return -ENODEV; + + return 0; +} + +static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c) +{ + struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev); + + dev->flags |= MODEL_AMD_NAVI_GPU; + dev->timings.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ; + return 0; +} + static int mrfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c) { /* @@ -155,6 +198,12 @@ static struct dw_pci_controller dw_pci_controllers[] = { .bus_num = -1, .get_clk_rate_khz = ehl_get_clk_rate_khz, }, + [navi_amd] = { + .bus_num = -1, + .scl_sda_cfg = &navi_amd_config, + .setup = navi_amd_setup, + .get_clk_rate_khz = navi_amd_get_clk_rate_khz, + }, }; #ifdef CONFIG_PM @@ -274,6 +323,14 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev, return r; } + if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) { + r = navi_amd_register_client(dev); + if (r) { + dev_err(dev->dev, "register client failed with %d\n", r); + return r; + } + } + pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); @@ -337,6 +394,10 @@ static const struct pci_device_id i2_designware_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x4bbe), elkhartlake }, { PCI_VDEVICE(INTEL, 0x4bbf), elkhartlake }, { PCI_VDEVICE(INTEL, 0x4bc0), elkhartlake }, + { PCI_VDEVICE(ATI, 0x7314), navi_amd }, + { PCI_VDEVICE(ATI, 0x73a4), navi_amd }, + { PCI_VDEVICE(ATI, 0x73e4), navi_amd }, + { PCI_VDEVICE(ATI, 0x73c4), navi_amd }, { 0,} }; MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids); -- cgit v1.2.3 From fd6ddaa0f50a1d5235989262ef666713d2903678 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 31 Mar 2021 13:46:22 +0300 Subject: i2c: drivers: Use generic definitions for bus frequencies (part 2) Since we have generic definitions for bus frequencies, let's use them. Signed-off-by: Andy Shevchenko Acked-by: Khalil Blaiech Reviewed-by: Robert Foss Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-master.c | 2 +- drivers/i2c/busses/i2c-mlxbf.c | 14 ++++---------- drivers/i2c/busses/i2c-qcom-cci.c | 4 ++-- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index e288b654cb47..8a59470a82ad 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -82,7 +82,7 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) * difference is the timing parameter values since the registers are * the same. */ - if (t->bus_freq_hz == 1000000) { + if (t->bus_freq_hz == I2C_MAX_FAST_MODE_PLUS_FREQ) { /* * Check are Fast Mode Plus parameters available. Calculate * SCL timing parameters for Fast Mode Plus if not set. diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c index 2fb0532d8a16..80ab831df349 100644 --- a/drivers/i2c/busses/i2c-mlxbf.c +++ b/drivers/i2c/busses/i2c-mlxbf.c @@ -172,12 +172,6 @@ #define MLXBF_I2C_SMBUS_THIGH_MAX_TBUF 0x14 #define MLXBF_I2C_SMBUS_SCL_LOW_TIMEOUT 0x18 -enum { - MLXBF_I2C_TIMING_100KHZ = 100000, - MLXBF_I2C_TIMING_400KHZ = 400000, - MLXBF_I2C_TIMING_1000KHZ = 1000000, -}; - /* * Defines SMBus operating frequency and core clock frequency. * According to ADB files, default values are compliant to 100KHz SMBus @@ -1202,7 +1196,7 @@ static int mlxbf_i2c_init_timings(struct platform_device *pdev, ret = device_property_read_u32(dev, "clock-frequency", &config_khz); if (ret < 0) - config_khz = MLXBF_I2C_TIMING_100KHZ; + config_khz = I2C_MAX_STANDARD_MODE_FREQ; switch (config_khz) { default: @@ -1210,15 +1204,15 @@ static int mlxbf_i2c_init_timings(struct platform_device *pdev, pr_warn("Illegal value %d: defaulting to 100 KHz\n", config_khz); fallthrough; - case MLXBF_I2C_TIMING_100KHZ: + case I2C_MAX_STANDARD_MODE_FREQ: config_idx = MLXBF_I2C_TIMING_CONFIG_100KHZ; break; - case MLXBF_I2C_TIMING_400KHZ: + case I2C_MAX_FAST_MODE_FREQ: config_idx = MLXBF_I2C_TIMING_CONFIG_400KHZ; break; - case MLXBF_I2C_TIMING_1000KHZ: + case I2C_MAX_FAST_MODE_PLUS_FREQ: config_idx = MLXBF_I2C_TIMING_CONFIG_1000KHZ; break; } diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 1c259b5188de..c63d5545fc2a 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -569,9 +569,9 @@ static int cci_probe(struct platform_device *pdev) cci->master[idx].mode = I2C_MODE_STANDARD; ret = of_property_read_u32(child, "clock-frequency", &val); if (!ret) { - if (val == 400000) + if (val == I2C_MAX_FAST_MODE_FREQ) cci->master[idx].mode = I2C_MODE_FAST; - else if (val == 1000000) + else if (val == I2C_MAX_FAST_MODE_PLUS_FREQ) cci->master[idx].mode = I2C_MODE_FAST_PLUS; } -- cgit v1.2.3 From 22e06b30f94c9b014dfba029d84395542e274885 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 31 Mar 2021 09:51:40 +0200 Subject: i2c: tegra-bpmp: don't modify input variable in xlate_flags Since commit bc1c2048abbe ("i2c: bpmp-tegra: Ignore unknown I2C_M flags") we don't need to mask out flags and can keep the input variable as is to save quite some lines. Signed-off-by: Wolfram Sang Acked-by: Thierry Reding Tested-by: Thierry Reding Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra-bpmp.c | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra-bpmp.c b/drivers/i2c/busses/i2c-tegra-bpmp.c index c934d636f625..295286ad6d6c 100644 --- a/drivers/i2c/busses/i2c-tegra-bpmp.c +++ b/drivers/i2c/busses/i2c-tegra-bpmp.c @@ -40,45 +40,29 @@ struct tegra_bpmp_i2c { */ static int tegra_bpmp_xlate_flags(u16 flags, u16 *out) { - if (flags & I2C_M_TEN) { + if (flags & I2C_M_TEN) *out |= SERIALI2C_TEN; - flags &= ~I2C_M_TEN; - } - if (flags & I2C_M_RD) { + if (flags & I2C_M_RD) *out |= SERIALI2C_RD; - flags &= ~I2C_M_RD; - } - if (flags & I2C_M_STOP) { + if (flags & I2C_M_STOP) *out |= SERIALI2C_STOP; - flags &= ~I2C_M_STOP; - } - if (flags & I2C_M_NOSTART) { + if (flags & I2C_M_NOSTART) *out |= SERIALI2C_NOSTART; - flags &= ~I2C_M_NOSTART; - } - if (flags & I2C_M_REV_DIR_ADDR) { + if (flags & I2C_M_REV_DIR_ADDR) *out |= SERIALI2C_REV_DIR_ADDR; - flags &= ~I2C_M_REV_DIR_ADDR; - } - if (flags & I2C_M_IGNORE_NAK) { + if (flags & I2C_M_IGNORE_NAK) *out |= SERIALI2C_IGNORE_NAK; - flags &= ~I2C_M_IGNORE_NAK; - } - if (flags & I2C_M_NO_RD_ACK) { + if (flags & I2C_M_NO_RD_ACK) *out |= SERIALI2C_NO_RD_ACK; - flags &= ~I2C_M_NO_RD_ACK; - } - if (flags & I2C_M_RECV_LEN) { + if (flags & I2C_M_RECV_LEN) *out |= SERIALI2C_RECV_LEN; - flags &= ~I2C_M_RECV_LEN; - } return 0; } -- cgit v1.2.3 From 40357058f859563149c976fe90728436d6795227 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 31 Mar 2021 09:51:41 +0200 Subject: i2c: tegra-bpmp: make some functions void They return 0 always, so save some lines and code. Signed-off-by: Wolfram Sang Acked-by: Thierry Reding Tested-by: Thierry Reding Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra-bpmp.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra-bpmp.c b/drivers/i2c/busses/i2c-tegra-bpmp.c index 295286ad6d6c..3680d608698b 100644 --- a/drivers/i2c/busses/i2c-tegra-bpmp.c +++ b/drivers/i2c/busses/i2c-tegra-bpmp.c @@ -38,7 +38,7 @@ struct tegra_bpmp_i2c { * firmware I2C driver to avoid any issues in future if Linux I2C flags are * changed. */ -static int tegra_bpmp_xlate_flags(u16 flags, u16 *out) +static void tegra_bpmp_xlate_flags(u16 flags, u16 *out) { if (flags & I2C_M_TEN) *out |= SERIALI2C_TEN; @@ -63,8 +63,6 @@ static int tegra_bpmp_xlate_flags(u16 flags, u16 *out) if (flags & I2C_M_RECV_LEN) *out |= SERIALI2C_RECV_LEN; - - return 0; } /** @@ -81,22 +79,19 @@ static int tegra_bpmp_xlate_flags(u16 flags, u16 *out) * * See deserialize_i2c documentation for the data format in the other direction. */ -static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c, +static void tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c, struct mrq_i2c_request *request, struct i2c_msg *msgs, unsigned int num) { char *buf = request->xfer.data_buf; unsigned int i, j, pos = 0; - int err; for (i = 0; i < num; i++) { struct i2c_msg *msg = &msgs[i]; u16 flags = 0; - err = tegra_bpmp_xlate_flags(msg->flags, &flags); - if (err < 0) - return err; + tegra_bpmp_xlate_flags(msg->flags, &flags); buf[pos++] = msg->addr & 0xff; buf[pos++] = (msg->addr & 0xff00) >> 8; @@ -112,8 +107,6 @@ static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c, } request->xfer.data_size = pos; - - return 0; } /** @@ -247,12 +240,7 @@ static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter, memset(&request, 0, sizeof(request)); memset(&response, 0, sizeof(response)); - err = tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num); - if (err < 0) { - dev_err(i2c->dev, "failed to serialize message: %d\n", err); - return err; - } - + tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num); err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response, atomic); if (err < 0) { dev_err(i2c->dev, "failed to transfer message: %d\n", err); -- cgit v1.2.3 From 010e32ab205bfee3a3e860d0366c1516c3e298f9 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Fri, 12 Mar 2021 12:53:27 +0100 Subject: i2c: stm32f7: avoid ifdef CONFIG_PM_SLEEP for pm callbacks Avoid CONFIG_PM preprocessor check for pm suspend/resume callbacks and identify the functions with __maybe_unused. Signed-off-by: Alain Volmat Reviewed-by: Pierre-Yves MORDRET Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-stm32f7.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 9fbcbd1807df..7e06e6b79b21 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -2271,8 +2271,7 @@ static int __maybe_unused stm32f7_i2c_runtime_resume(struct device *dev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev) +static int __maybe_unused stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev) { int ret; struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs; @@ -2293,7 +2292,7 @@ static int stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev) return ret; } -static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev) +static int __maybe_unused stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev) { u32 cr1; int ret; @@ -2324,7 +2323,7 @@ static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev) return ret; } -static int stm32f7_i2c_suspend(struct device *dev) +static int __maybe_unused stm32f7_i2c_suspend(struct device *dev) { struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); int ret; @@ -2345,7 +2344,7 @@ static int stm32f7_i2c_suspend(struct device *dev) return 0; } -static int stm32f7_i2c_resume(struct device *dev) +static int __maybe_unused stm32f7_i2c_resume(struct device *dev) { struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); int ret; @@ -2365,7 +2364,6 @@ static int stm32f7_i2c_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops stm32f7_i2c_pm_ops = { SET_RUNTIME_PM_OPS(stm32f7_i2c_runtime_suspend, -- cgit v1.2.3 From 3ab4ce2daf09b62e38934a7afe3e9fc3f3cbcdec Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 7 Apr 2021 03:11:37 +0000 Subject: i2c: designware: Fix return value check in navi_amd_register_client() In case of error, the function i2c_new_client_device() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Fixes: 17631e8ca2d3 ("i2c: designware: Add driver support for AMD NAVI GPU") Reported-by: Hulk Robot Signed-off-by: Wei Yongjun Acked-by: Jarkko Nikula Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-pcidrv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 7ca0017883a6..0f409a4c2da0 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -132,8 +132,8 @@ static int navi_amd_register_client(struct dw_i2c_dev *dev) info.irq = dev->irq; dev->slave = i2c_new_client_device(&dev->adapter, &info); - if (!dev->slave) - return -ENODEV; + if (IS_ERR(dev->slave)) + return PTR_ERR(dev->slave); return 0; } -- cgit v1.2.3 From bb7f086b8404e70ac2891ba539f959805f1684b0 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 8 Apr 2021 20:17:40 +0800 Subject: i2c: core: simplify devm_i2c_new_dummy_device() Use devm_add_action_or_reset() instead of devres_alloc() and devres_add(), which works the same. This will simplify the code. There is no functional change. Signed-off-by: Yicong Yang Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index e7be127eeee4..15977add74bc 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1016,15 +1016,9 @@ struct i2c_client *i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address } EXPORT_SYMBOL_GPL(i2c_new_dummy_device); -struct i2c_dummy_devres { - struct i2c_client *client; -}; - -static void devm_i2c_release_dummy(struct device *dev, void *res) +static void devm_i2c_release_dummy(void *client) { - struct i2c_dummy_devres *this = res; - - i2c_unregister_device(this->client); + i2c_unregister_device(client); } /** @@ -1041,20 +1035,16 @@ struct i2c_client *devm_i2c_new_dummy_device(struct device *dev, struct i2c_adapter *adapter, u16 address) { - struct i2c_dummy_devres *dr; struct i2c_client *client; - - dr = devres_alloc(devm_i2c_release_dummy, sizeof(*dr), GFP_KERNEL); - if (!dr) - return ERR_PTR(-ENOMEM); + int ret; client = i2c_new_dummy_device(adapter, address); - if (IS_ERR(client)) { - devres_free(dr); - } else { - dr->client = client; - devres_add(dev, dr); - } + if (IS_ERR(client)) + return client; + + ret = devm_add_action_or_reset(dev, devm_i2c_release_dummy, client); + if (ret) + return ERR_PTR(ret); return client; } -- cgit v1.2.3 From 660f58b6d290417828680f417f43d7b810fa9138 Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Thu, 1 Apr 2021 21:37:47 +0800 Subject: i2c: remove unused 'version.h' include in drivers Signed-off-by: Tian Tao Signed-off-by: Zhiqi Song Acked-by: Florian Fainelli # for brcmstb Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-brcmstb.c | 1 - drivers/i2c/busses/i2c-xgene-slimpro.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c index ba766d24219e..490ee3962645 100644 --- a/drivers/i2c/busses/i2c-brcmstb.c +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -22,7 +22,6 @@ #include #include #include -#include #define N_DATA_REGS 8 diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c index 63cbb9c7c1b0..bba08cbce6e1 100644 --- a/drivers/i2c/busses/i2c-xgene-slimpro.c +++ b/drivers/i2c/busses/i2c-xgene-slimpro.c @@ -19,7 +19,6 @@ #include #include #include -#include #define MAILBOX_OP_TIMEOUT 1000 /* Operation time out in ms */ #define MAILBOX_I2C_INDEX 0 -- cgit v1.2.3 From 714638e02d94fa28c9e030d13d03e663fe24925e Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 29 Mar 2021 13:50:36 +0300 Subject: i2c: Add support for software nodes This makes it possible for the drivers to assign complete software fwnodes to the devices instead of only the device properties in those nodes. Signed-off-by: Heikki Krogerus Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 15 ++++++++++++++- include/linux/i2c.h | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 63ebf722a424..266b2013b1f1 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -919,15 +919,27 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf } } + if (info->swnode) { + status = device_add_software_node(&client->dev, info->swnode); + if (status) { + dev_err(&adap->dev, + "Failed to add software node to client %s: %d\n", + client->name, status); + goto out_free_props; + } + } + status = device_register(&client->dev); if (status) - goto out_free_props; + goto out_remove_swnode; dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n", client->name, dev_name(&client->dev)); return client; +out_remove_swnode: + device_remove_software_node(&client->dev); out_free_props: if (info->properties) device_remove_properties(&client->dev); @@ -960,6 +972,7 @@ void i2c_unregister_device(struct i2c_client *client) if (ACPI_COMPANION(&client->dev)) acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev)); + device_remove_software_node(&client->dev); device_unregister(&client->dev); } EXPORT_SYMBOL_GPL(i2c_unregister_device); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 56622658b215..cb1f882a3e88 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -391,7 +391,8 @@ static inline bool i2c_detect_slave_mode(struct device *dev) { return false; } * @platform_data: stored in i2c_client.dev.platform_data * @of_node: pointer to OpenFirmware device node * @fwnode: device node supplied by the platform firmware - * @properties: additional device properties for the device + * @properties: Deprecated - use swnode instead + * @swnode: software node for the device * @resources: resources associated with the device * @num_resources: number of resources in the @resources array * @irq: stored in i2c_client.irq @@ -416,6 +417,7 @@ struct i2c_board_info { struct device_node *of_node; struct fwnode_handle *fwnode; const struct property_entry *properties; + const struct software_node *swnode; const struct resource *resources; unsigned int num_resources; int irq; -- cgit v1.2.3 From 95e272dc364a6bb70d461a42014a0142ab25ea82 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 29 Mar 2021 13:50:37 +0300 Subject: ARM: davinci: Constify the software nodes Additional device properties are always just a part of a software fwnode. If the device properties are constant, the software node can also be constant. Signed-off-by: Heikki Krogerus Acked-by: Bartosz Golaszewski Signed-off-by: Wolfram Sang --- arch/arm/mach-davinci/board-da830-evm.c | 6 +++++- arch/arm/mach-davinci/board-dm365-evm.c | 6 +++++- arch/arm/mach-davinci/board-dm644x-evm.c | 6 +++++- arch/arm/mach-davinci/board-dm646x-evm.c | 6 +++++- arch/arm/mach-davinci/board-mityomapl138.c | 6 +++++- arch/arm/mach-davinci/board-sffsdr.c | 6 +++++- 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index a20ba12d876c..823c9cc98f18 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -454,6 +454,10 @@ static const struct property_entry da830_evm_i2c_eeprom_properties[] = { { } }; +static const struct software_node da830_evm_i2c_eeprom_node = { + .properties = da830_evm_i2c_eeprom_properties, +}; + static int __init da830_evm_ui_expander_setup(struct i2c_client *client, int gpio, unsigned ngpio, void *context) { @@ -485,7 +489,7 @@ static struct pcf857x_platform_data __initdata da830_evm_ui_expander_info = { static struct i2c_board_info __initdata da830_evm_i2c_devices[] = { { I2C_BOARD_INFO("24c256", 0x50), - .properties = da830_evm_i2c_eeprom_properties, + .swnode = &da830_evm_i2c_eeprom_node, }, { I2C_BOARD_INFO("tlv320aic3x", 0x18), diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index bdf31eb77620..b3bef74c982a 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c @@ -232,10 +232,14 @@ static const struct property_entry eeprom_properties[] = { { } }; +static const struct software_node eeprom_node = { + .properties = eeprom_properties, +}; + static struct i2c_board_info i2c_info[] = { { I2C_BOARD_INFO("24c256", 0x50), - .properties = eeprom_properties, + .swnode = &eeprom_node, }, { I2C_BOARD_INFO("tlv320aic3x", 0x18), diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 7755cccec550..cce3a621eb20 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -541,6 +541,10 @@ static const struct property_entry eeprom_properties[] = { { } }; +static const struct software_node eeprom_node = { + .properties = eeprom_properties, +}; + /* * MSP430 supports RTC, card detection, input from IR remote, and * a bit more. It triggers interrupts on GPIO(7) from pressing @@ -647,7 +651,7 @@ static struct i2c_board_info __initdata i2c_info[] = { }, { I2C_BOARD_INFO("24c256", 0x50), - .properties = eeprom_properties, + .swnode = &eeprom_node, }, { I2C_BOARD_INFO("tlv320aic33", 0x1b), diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index 952ddabc743e..ee91d81ebbfd 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -362,6 +362,10 @@ static const struct property_entry eeprom_properties[] = { PROPERTY_ENTRY_U32("pagesize", 64), { } }; + +static const struct software_node eeprom_node = { + .properties = eeprom_properties, +}; #endif static u8 dm646x_iis_serializer_direction[] = { @@ -430,7 +434,7 @@ static void evm_init_cpld(void) static struct i2c_board_info __initdata i2c_info[] = { { I2C_BOARD_INFO("24c256", 0x50), - .properties = eeprom_properties, + .swnode = &eeprom_node, }, { I2C_BOARD_INFO("pcf8574a", 0x38), diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c index 5205008c8061..2127969beb96 100644 --- a/arch/arm/mach-davinci/board-mityomapl138.c +++ b/arch/arm/mach-davinci/board-mityomapl138.c @@ -197,6 +197,10 @@ static const struct property_entry mityomapl138_fd_chip_properties[] = { { } }; +static const struct software_node mityomapl138_fd_chip_node = { + .properties = mityomapl138_fd_chip_properties, +}; + static struct davinci_i2c_platform_data mityomap_i2c_0_pdata = { .bus_freq = 100, /* kHz */ .bus_delay = 0, /* usec */ @@ -323,7 +327,7 @@ static struct i2c_board_info __initdata mityomap_tps65023_info[] = { }, { I2C_BOARD_INFO("24c02", 0x50), - .properties = mityomapl138_fd_chip_properties, + .swnode = &mityomapl138_fd_chip_node, }, }; diff --git a/arch/arm/mach-davinci/board-sffsdr.c b/arch/arm/mach-davinci/board-sffsdr.c index 79b47958e992..6930b2f485d1 100644 --- a/arch/arm/mach-davinci/board-sffsdr.c +++ b/arch/arm/mach-davinci/board-sffsdr.c @@ -84,10 +84,14 @@ static const struct property_entry eeprom_properties[] = { { } }; +static const struct software_node eeprom_node = { + .properties = eeprom_properties, +}; + static struct i2c_board_info __initdata i2c_info[] = { { I2C_BOARD_INFO("24c64", 0x50), - .properties = eeprom_properties, + .swnode = &eeprom_node, }, /* Other I2C devices: * MSP430, addr 0x23 (not used) -- cgit v1.2.3 From 74031824cac501b1c6db18ee1e49a2197e3b67bd Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 29 Mar 2021 13:50:38 +0300 Subject: ARM: omap1: osk: Constify the software node Additional device properties are always just a part of a software fwnode. If the device properties are constant, the software node can also be constant. Signed-off-by: Heikki Krogerus Acked-by: Tony Lindgren Signed-off-by: Wolfram Sang --- arch/arm/mach-omap1/board-osk.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c index 0a4c9b0b13b0..e18b6f13300e 100644 --- a/arch/arm/mach-omap1/board-osk.c +++ b/arch/arm/mach-omap1/board-osk.c @@ -332,11 +332,15 @@ static const struct property_entry mistral_at24_properties[] = { { } }; +static const struct software_node mistral_at24_node = { + .properties = mistral_at24_properties, +}; + static struct i2c_board_info __initdata mistral_i2c_board_info[] = { { /* NOTE: powered from LCD supply */ I2C_BOARD_INFO("24c04", 0x50), - .properties = mistral_at24_properties, + .swnode = &mistral_at24_node, }, /* TODO when driver support is ready: * - optionally ov9640 camera sensor at 0x30 -- cgit v1.2.3 From 4335168600ef7f4aae9d70c7785d5dcdf09c0f52 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 29 Mar 2021 13:50:39 +0300 Subject: ARM: pxa: stargate2: Constify the software node Additional device properties are always just a part of a software fwnode. If the device properties are constant, the software node can also be constant. Signed-off-by: Heikki Krogerus Acked-by: Robert Jarzmik Signed-off-by: Wolfram Sang --- arch/arm/mach-pxa/stargate2.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-pxa/stargate2.c b/arch/arm/mach-pxa/stargate2.c index e2353f7dcf01..7ad627465768 100644 --- a/arch/arm/mach-pxa/stargate2.c +++ b/arch/arm/mach-pxa/stargate2.c @@ -794,6 +794,10 @@ static const struct property_entry pca9500_eeprom_properties[] = { { } }; +static const struct software_node pca9500_eeprom_node = { + .properties = pca9500_eeprom_properties, +}; + /** * stargate2_reset_bluetooth() reset the bluecore to ensure consistent state **/ @@ -929,7 +933,7 @@ static struct i2c_board_info __initdata stargate2_i2c_board_info[] = { }, { .type = "24c02", .addr = 0x57, - .properties = pca9500_eeprom_properties, + .swnode = &pca9500_eeprom_node, }, { .type = "max1238", .addr = 0x35, -- cgit v1.2.3 From dc317fb81b57ba36cd7e15c8bd98c3d6be013f5f Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 29 Mar 2021 13:50:40 +0300 Subject: ARM: s3c: mini2440: Constify the software node Additional device properties are always just a part of a software fwnode. If the device properties are constant, the software node can also be constant. Signed-off-by: Heikki Krogerus Acked-by: Krzysztof Kozlowski Signed-off-by: Wolfram Sang --- arch/arm/mach-s3c/mach-mini2440.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-s3c/mach-mini2440.c b/arch/arm/mach-s3c/mach-mini2440.c index 4100905dfbd0..551ec660ab59 100644 --- a/arch/arm/mach-s3c/mach-mini2440.c +++ b/arch/arm/mach-s3c/mach-mini2440.c @@ -542,10 +542,14 @@ static const struct property_entry mini2440_at24_properties[] = { { } }; +static const struct software_node mini2440_at24_node = { + .properties = mini2440_at24_properties, +}; + static struct i2c_board_info mini2440_i2c_devs[] __initdata = { { I2C_BOARD_INFO("24c08", 0x50), - .properties = mini2440_at24_properties, + .swnode = &mini2440_at24_node, }, }; -- cgit v1.2.3 From 3a3438e594bf8b2d8fcfd11f3aa2a93e215b92b7 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 29 Mar 2021 13:50:41 +0300 Subject: platform/x86: intel_cht_int33fe_microb: Constify the software node Additional device properties are always just a part of a software fwnode. If the device properties are constant, the software node can also be constant. Signed-off-by: Heikki Krogerus Reviewed-by: Hans de Goede Signed-off-by: Wolfram Sang --- drivers/platform/x86/intel_cht_int33fe_microb.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel_cht_int33fe_microb.c b/drivers/platform/x86/intel_cht_int33fe_microb.c index 20b11e0d9a75..673f41cd14b5 100644 --- a/drivers/platform/x86/intel_cht_int33fe_microb.c +++ b/drivers/platform/x86/intel_cht_int33fe_microb.c @@ -35,6 +35,10 @@ static const struct property_entry bq27xxx_props[] = { { } }; +static const struct software_node bq27xxx_node = { + .properties = bq27xxx_props, +}; + int cht_int33fe_microb_probe(struct cht_int33fe_data *data) { struct device *dev = data->dev; @@ -43,7 +47,7 @@ int cht_int33fe_microb_probe(struct cht_int33fe_data *data) memset(&board_info, 0, sizeof(board_info)); strscpy(board_info.type, "bq27542", ARRAY_SIZE(board_info.type)); board_info.dev_name = "bq27542"; - board_info.properties = bq27xxx_props; + board_info.swnode = &bq27xxx_node; data->battery_fg = i2c_acpi_new_device(dev, 1, &board_info); return PTR_ERR_OR_ZERO(data->battery_fg); -- cgit v1.2.3 From f9c3d2734343c934f2e7270c0d47a4c88e10b847 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 29 Mar 2021 13:50:42 +0300 Subject: i2c: cht-wc: Constify the software node Additional device properties are always just a part of a software fwnode. If the device properties are constant, the software node can also be constant. Signed-off-by: Heikki Krogerus Reviewed-by: Hans de Goede Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-cht-wc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-cht-wc.c b/drivers/i2c/busses/i2c-cht-wc.c index f80d79e973cd..08f491ea21ac 100644 --- a/drivers/i2c/busses/i2c-cht-wc.c +++ b/drivers/i2c/busses/i2c-cht-wc.c @@ -280,6 +280,10 @@ static const struct property_entry bq24190_props[] = { { } }; +static const struct software_node bq24190_node = { + .properties = bq24190_props, +}; + static struct regulator_consumer_supply fusb302_consumer = { .supply = "vbus", /* Must match fusb302 dev_name in intel_cht_int33fe.c */ @@ -308,7 +312,7 @@ static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev) .type = "bq24190", .addr = 0x6b, .dev_name = "bq24190", - .properties = bq24190_props, + .swnode = &bq24190_node, .platform_data = &bq24190_pdata, }; int ret, reg, irq; -- cgit v1.2.3 From 239798f5fb5e87b7ed253df5c5de893aad57fc6b Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 29 Mar 2021 13:50:43 +0300 Subject: i2c: nvidia-gpu: Constify the software node Additional device properties are always just a part of a software fwnode. If the device properties are constant, the software node can also be constant. Signed-off-by: Heikki Krogerus Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-nvidia-gpu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 6b20601ffb13..b5055a3cbd93 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -262,6 +262,10 @@ static const struct property_entry ccgx_props[] = { { } }; +static const struct software_node ccgx_node = { + .properties = ccgx_props, +}; + static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) { i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev, @@ -274,7 +278,7 @@ static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) sizeof(i2cd->gpu_ccgx_ucsi->type)); i2cd->gpu_ccgx_ucsi->addr = 0x8; i2cd->gpu_ccgx_ucsi->irq = irq; - i2cd->gpu_ccgx_ucsi->properties = ccgx_props; + i2cd->gpu_ccgx_ucsi->swnode = &ccgx_node; i2cd->ccgx_client = i2c_new_client_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); return PTR_ERR_OR_ZERO(i2cd->ccgx_client); } -- cgit v1.2.3 From dd7a37102b79ae55184d8dea641774254cf8b1ac Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 29 Mar 2021 13:50:44 +0300 Subject: i2c: icy: Constify the software node Complete software node can now be supplied to the device with struct i2c_board_info. Signed-off-by: Heikki Krogerus Reviewed-by: Max Staudt Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-icy.c | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/drivers/i2c/busses/i2c-icy.c b/drivers/i2c/busses/i2c-icy.c index 66c9923fc766..c8c422e9dda4 100644 --- a/drivers/i2c/busses/i2c-icy.c +++ b/drivers/i2c/busses/i2c-icy.c @@ -54,7 +54,6 @@ struct icy_i2c { void __iomem *reg_s0; void __iomem *reg_s1; - struct fwnode_handle *ltc2990_fwnode; struct i2c_client *ltc2990_client; }; @@ -115,6 +114,10 @@ static const struct property_entry icy_ltc2990_props[] = { { } }; +static const struct software_node icy_ltc2990_node = { + .properties = icy_ltc2990_props, +}; + static int icy_probe(struct zorro_dev *z, const struct zorro_device_id *ent) { @@ -123,6 +126,7 @@ static int icy_probe(struct zorro_dev *z, struct fwnode_handle *new_fwnode; struct i2c_board_info ltc2990_info = { .type = "ltc2990", + .swnode = &icy_ltc2990_node, }; i2c = devm_kzalloc(&z->dev, sizeof(*i2c), GFP_KERNEL); @@ -174,26 +178,10 @@ static int icy_probe(struct zorro_dev *z, * * See property_entry above for in1, in2, temp3. */ - new_fwnode = fwnode_create_software_node(icy_ltc2990_props, NULL); - if (IS_ERR(new_fwnode)) { - dev_info(&z->dev, "Failed to create fwnode for LTC2990, error: %ld\n", - PTR_ERR(new_fwnode)); - } else { - /* - * Store the fwnode so we can destroy it on .remove(). - * Only store it on success, as fwnode_remove_software_node() - * is NULL safe, but not PTR_ERR safe. - */ - i2c->ltc2990_fwnode = new_fwnode; - ltc2990_info.fwnode = new_fwnode; - - i2c->ltc2990_client = - i2c_new_scanned_device(&i2c->adapter, - <c2990_info, - icy_ltc2990_addresses, - NULL); - } - + i2c->ltc2990_client = i2c_new_scanned_device(&i2c->adapter, + <c2990_info, + icy_ltc2990_addresses, + NULL); return 0; } @@ -202,8 +190,6 @@ static void icy_remove(struct zorro_dev *z) struct icy_i2c *i2c = dev_get_drvdata(&z->dev); i2c_unregister_device(i2c->ltc2990_client); - fwnode_remove_software_node(i2c->ltc2990_fwnode); - i2c_del_adapter(&i2c->adapter); } -- cgit v1.2.3 From 2c02f659851a962a57fc663e9aa33fe57606086a Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 29 Mar 2021 13:50:45 +0300 Subject: platform/chrome: chromeos_laptop - Prepare complete software nodes The older device property API is going to be removed soon and that will affect also I2C subystem. Supplying complete software nodes instead of only the properties in them for the I2C devices. Signed-off-by: Heikki Krogerus Acked-by: Enric Balletbo i Serra Signed-off-by: Wolfram Sang --- drivers/platform/chrome/chromeos_laptop.c | 100 ++++++++++++++++++------------ 1 file changed, 60 insertions(+), 40 deletions(-) diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 472a03daa869..4e14b4d6635d 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -52,12 +52,15 @@ struct i2c_peripheral { enum i2c_adapter_type type; u32 pci_devid; + const struct property_entry *properties; + struct i2c_client *client; }; struct acpi_peripheral { char hid[ACPI_ID_LEN]; - const struct property_entry *properties; + struct software_node swnode; + struct i2c_client *client; }; struct chromeos_laptop { @@ -68,7 +71,7 @@ struct chromeos_laptop { struct i2c_peripheral *i2c_peripherals; unsigned int num_i2c_peripherals; - const struct acpi_peripheral *acpi_peripherals; + struct acpi_peripheral *acpi_peripherals; unsigned int num_acpi_peripherals; }; @@ -161,7 +164,7 @@ static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter) static bool chromeos_laptop_adjust_client(struct i2c_client *client) { - const struct acpi_peripheral *acpi_dev; + struct acpi_peripheral *acpi_dev; struct acpi_device_id acpi_ids[2] = { }; int i; int error; @@ -175,8 +178,7 @@ static bool chromeos_laptop_adjust_client(struct i2c_client *client) memcpy(acpi_ids[0].id, acpi_dev->hid, ACPI_ID_LEN); if (acpi_match_device(acpi_ids, &client->dev)) { - error = device_add_properties(&client->dev, - acpi_dev->properties); + error = device_add_software_node(&client->dev, &acpi_dev->swnode); if (error) { dev_err(&client->dev, "failed to add properties: %d\n", @@ -184,6 +186,8 @@ static bool chromeos_laptop_adjust_client(struct i2c_client *client) break; } + acpi_dev->client = client; + return true; } } @@ -193,15 +197,28 @@ static bool chromeos_laptop_adjust_client(struct i2c_client *client) static void chromeos_laptop_detach_i2c_client(struct i2c_client *client) { + struct acpi_peripheral *acpi_dev; struct i2c_peripheral *i2c_dev; int i; - for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { - i2c_dev = &cros_laptop->i2c_peripherals[i]; + if (has_acpi_companion(&client->dev)) + for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) { + acpi_dev = &cros_laptop->acpi_peripherals[i]; - if (i2c_dev->client == client) - i2c_dev->client = NULL; - } + if (acpi_dev->client == client) { + acpi_dev->client = NULL; + return; + } + } + else + for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { + i2c_dev = &cros_laptop->i2c_peripherals[i]; + + if (i2c_dev->client == client) { + i2c_dev->client = NULL; + return; + } + } } static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb, @@ -302,28 +319,26 @@ static struct i2c_peripheral chromebook_pixel_peripherals[] __initdata = { .board_info = { I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), - .properties = - chromebook_atmel_touchscreen_props, .flags = I2C_CLIENT_WAKE, }, .dmi_name = "touchscreen", .irqflags = IRQF_TRIGGER_FALLING, .type = I2C_ADAPTER_PANEL, .alt_addr = ATMEL_TS_I2C_BL_ADDR, + .properties = chromebook_atmel_touchscreen_props, }, /* Touchpad. */ { .board_info = { I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR), - .properties = - chromebook_pixel_trackpad_props, .flags = I2C_CLIENT_WAKE, }, .dmi_name = "trackpad", .irqflags = IRQF_TRIGGER_FALLING, .type = I2C_ADAPTER_VGADDC, .alt_addr = ATMEL_TP_I2C_BL_ADDR, + .properties = chromebook_pixel_trackpad_props, }, /* Light Sensor. */ { @@ -414,8 +429,6 @@ static struct i2c_peripheral acer_c720_peripherals[] __initdata = { .board_info = { I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), - .properties = - chromebook_atmel_touchscreen_props, .flags = I2C_CLIENT_WAKE, }, .dmi_name = "touchscreen", @@ -423,6 +436,7 @@ static struct i2c_peripheral acer_c720_peripherals[] __initdata = { .type = I2C_ADAPTER_DESIGNWARE, .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)), .alt_addr = ATMEL_TS_I2C_BL_ADDR, + .properties = chromebook_atmel_touchscreen_props, }, /* Touchpad. */ { @@ -498,12 +512,16 @@ static struct acpi_peripheral samus_peripherals[] __initdata = { /* Touchpad */ { .hid = "ATML0000", - .properties = samus_trackpad_props, + .swnode = { + .properties = samus_trackpad_props, + }, }, /* Touchsceen */ { .hid = "ATML0001", - .properties = chromebook_atmel_touchscreen_props, + .swnode = { + .properties = chromebook_atmel_touchscreen_props, + }, }, }; DECLARE_ACPI_CROS_LAPTOP(samus); @@ -512,12 +530,16 @@ static struct acpi_peripheral generic_atmel_peripherals[] __initdata = { /* Touchpad */ { .hid = "ATML0000", - .properties = chromebook_pixel_trackpad_props, + .swnode = { + .properties = chromebook_pixel_trackpad_props, + }, }, /* Touchsceen */ { .hid = "ATML0001", - .properties = chromebook_atmel_touchscreen_props, + .swnode = { + .properties = chromebook_atmel_touchscreen_props, + }, }, }; DECLARE_ACPI_CROS_LAPTOP(generic_atmel); @@ -743,12 +765,11 @@ chromeos_laptop_prepare_i2c_peripherals(struct chromeos_laptop *cros_laptop, if (error) goto err_out; - /* We need to deep-copy properties */ - if (info->properties) { - info->properties = - property_entries_dup(info->properties); - if (IS_ERR(info->properties)) { - error = PTR_ERR(info->properties); + /* Create primary fwnode for the device - copies everything */ + if (i2c_dev->properties) { + info->fwnode = fwnode_create_software_node(i2c_dev->properties, NULL); + if (IS_ERR(info->fwnode)) { + error = PTR_ERR(info->fwnode); goto err_out; } } @@ -760,8 +781,8 @@ err_out: while (--i >= 0) { i2c_dev = &cros_laptop->i2c_peripherals[i]; info = &i2c_dev->board_info; - if (info->properties) - property_entries_free(info->properties); + if (!IS_ERR_OR_NULL(info->fwnode)) + fwnode_remove_software_node(info->fwnode); } kfree(cros_laptop->i2c_peripherals); return error; @@ -801,11 +822,11 @@ chromeos_laptop_prepare_acpi_peripherals(struct chromeos_laptop *cros_laptop, *acpi_dev = *src_dev; /* We need to deep-copy properties */ - if (src_dev->properties) { - acpi_dev->properties = - property_entries_dup(src_dev->properties); - if (IS_ERR(acpi_dev->properties)) { - error = PTR_ERR(acpi_dev->properties); + if (src_dev->swnode.properties) { + acpi_dev->swnode.properties = + property_entries_dup(src_dev->swnode.properties); + if (IS_ERR(acpi_dev->swnode.properties)) { + error = PTR_ERR(acpi_dev->swnode.properties); goto err_out; } } @@ -821,8 +842,8 @@ chromeos_laptop_prepare_acpi_peripherals(struct chromeos_laptop *cros_laptop, err_out: while (--i >= 0) { acpi_dev = &acpi_peripherals[i]; - if (acpi_dev->properties) - property_entries_free(acpi_dev->properties); + if (!IS_ERR_OR_NULL(acpi_dev->swnode.properties)) + property_entries_free(acpi_dev->swnode.properties); } kfree(acpi_peripherals); @@ -833,21 +854,20 @@ static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop) { const struct acpi_peripheral *acpi_dev; struct i2c_peripheral *i2c_dev; - struct i2c_board_info *info; int i; for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { i2c_dev = &cros_laptop->i2c_peripherals[i]; - info = &i2c_dev->board_info; - i2c_unregister_device(i2c_dev->client); - property_entries_free(info->properties); } for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) { acpi_dev = &cros_laptop->acpi_peripherals[i]; - property_entries_free(acpi_dev->properties); + if (acpi_dev->client) + device_remove_software_node(&acpi_dev->client->dev); + + property_entries_free(acpi_dev->swnode.properties); } kfree(cros_laptop->i2c_peripherals); -- cgit v1.2.3 From 9d383e96448dbfdd97a37e618f6af5a17a60ce0d Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 29 Mar 2021 13:50:46 +0300 Subject: Input: elantech - Prepare a complete software node for the device Creating a software node and supplying that for the device instead of only the device properties in it. A software node was always created in any case to hold the additional device properties, so this change does not have any real effect. This change makes it possible to remove support for the problematic "dangling" device properties from i2c subsystem, i.e. the "properties" member from struct i2c_board_info. The problems caused by them are not related to this driver. Signed-off-by: Heikki Krogerus Acked-by: Dmitry Torokhov Signed-off-by: Wolfram Sang --- drivers/input/mouse/elantech.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 97381e2e03ba..2d0bc029619f 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1885,8 +1885,6 @@ static int elantech_create_smbus(struct psmouse *psmouse, }; unsigned int idx = 0; - smbus_board.properties = i2c_props; - i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-x", info->x_max + 1); i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-y", @@ -1918,6 +1916,10 @@ static int elantech_create_smbus(struct psmouse *psmouse, if (elantech_is_buttonpad(info)) i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,clickpad"); + smbus_board.fwnode = fwnode_create_software_node(i2c_props, NULL); + if (IS_ERR(smbus_board.fwnode)) + return PTR_ERR(smbus_board.fwnode); + return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false, leave_breadcrumbs); } -- cgit v1.2.3 From 4b2b4cc50ba6d607d1611ea6b2046a58d16e45eb Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 29 Mar 2021 13:50:47 +0300 Subject: i2c: Remove support for dangling device properties From now on only accepting complete software nodes. Signed-off-by: Heikki Krogerus Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-boardinfo.c | 11 ----------- drivers/i2c/i2c-core-base.c | 15 +-------------- include/linux/i2c.h | 2 -- 3 files changed, 1 insertion(+), 27 deletions(-) diff --git a/drivers/i2c/i2c-boardinfo.c b/drivers/i2c/i2c-boardinfo.c index 8bc51d4e69df..4df8ad092df3 100644 --- a/drivers/i2c/i2c-boardinfo.c +++ b/drivers/i2c/i2c-boardinfo.c @@ -47,7 +47,6 @@ EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num); * * The board info passed can safely be __initdata, but be careful of embedded * pointers (for platform_data, functions, etc) since that won't be copied. - * Device properties are deep-copied though. */ int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len) { @@ -72,16 +71,6 @@ int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsig devinfo->busnum = busnum; devinfo->board_info = *info; - if (info->properties) { - devinfo->board_info.properties = - property_entries_dup(info->properties); - if (IS_ERR(devinfo->board_info.properties)) { - status = PTR_ERR(devinfo->board_info.properties); - kfree(devinfo); - break; - } - } - if (info->resources) { devinfo->board_info.resources = kmemdup(info->resources, diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 266b2013b1f1..d296b5000a1b 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -909,23 +909,13 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf i2c_dev_set_name(adap, client, info); - if (info->properties) { - status = device_add_properties(&client->dev, info->properties); - if (status) { - dev_err(&adap->dev, - "Failed to add properties to client %s: %d\n", - client->name, status); - goto out_err_put_of_node; - } - } - if (info->swnode) { status = device_add_software_node(&client->dev, info->swnode); if (status) { dev_err(&adap->dev, "Failed to add software node to client %s: %d\n", client->name, status); - goto out_free_props; + goto out_err_put_of_node; } } @@ -940,9 +930,6 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf out_remove_swnode: device_remove_software_node(&client->dev); -out_free_props: - if (info->properties) - device_remove_properties(&client->dev); out_err_put_of_node: of_node_put(info->of_node); out_err: diff --git a/include/linux/i2c.h b/include/linux/i2c.h index cb1f882a3e88..54b3ccc71e37 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -391,7 +391,6 @@ static inline bool i2c_detect_slave_mode(struct device *dev) { return false; } * @platform_data: stored in i2c_client.dev.platform_data * @of_node: pointer to OpenFirmware device node * @fwnode: device node supplied by the platform firmware - * @properties: Deprecated - use swnode instead * @swnode: software node for the device * @resources: resources associated with the device * @num_resources: number of resources in the @resources array @@ -416,7 +415,6 @@ struct i2c_board_info { void *platform_data; struct device_node *of_node; struct fwnode_handle *fwnode; - const struct property_entry *properties; const struct software_node *swnode; const struct resource *resources; unsigned int num_resources; -- cgit v1.2.3 From 07740c92ae57ca21204f1e0c6f59272cdf3190cc Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 8 Apr 2021 19:17:17 +0800 Subject: i2c: core: add managed function for adding i2c adapters Some I2C controller drivers will only unregister the I2C adapter in their .remove() callback, which can be done by simply using a managed variant to add the I2C adapter. So add the managed functions for adding the I2C adapter. Reviewed-by: Andy Shevchenko Reviewed-by: Dmitry Osipenko Signed-off-by: Yicong Yang Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 26 ++++++++++++++++++++++++++ include/linux/i2c.h | 1 + 2 files changed, 27 insertions(+) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index c81cc9a09877..c4c28b585b50 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1693,6 +1693,32 @@ void i2c_del_adapter(struct i2c_adapter *adap) } EXPORT_SYMBOL(i2c_del_adapter); +static void devm_i2c_del_adapter(void *adapter) +{ + i2c_del_adapter(adapter); +} + +/** + * devm_i2c_add_adapter - device-managed variant of i2c_add_adapter() + * @dev: managing device for adding this I2C adapter + * @adapter: the adapter to add + * Context: can sleep + * + * Add adapter with dynamic bus number, same with i2c_add_adapter() + * but the adapter will be auto deleted on driver detach. + */ +int devm_i2c_add_adapter(struct device *dev, struct i2c_adapter *adapter) +{ + int ret; + + ret = i2c_add_adapter(adapter); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, devm_i2c_del_adapter, adapter); +} +EXPORT_SYMBOL_GPL(devm_i2c_add_adapter); + static void i2c_parse_timing(struct device *dev, char *prop_name, u32 *cur_val_p, u32 def_val, bool use_def) { diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 59b892c5bb05..ce473ddc0378 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -846,6 +846,7 @@ static inline void i2c_mark_adapter_resumed(struct i2c_adapter *adap) */ #if IS_ENABLED(CONFIG_I2C) int i2c_add_adapter(struct i2c_adapter *adap); +int devm_i2c_add_adapter(struct device *dev, struct i2c_adapter *adapter); void i2c_del_adapter(struct i2c_adapter *adap); int i2c_add_numbered_adapter(struct i2c_adapter *adap); -- cgit v1.2.3 From 3b4c747cd32078172dd238929e38a43cfed83580 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 8 Apr 2021 19:17:18 +0800 Subject: i2c: core: add api to provide frequency mode strings Some I2C drivers like Designware and HiSilicon will print the bus frequency mode information, so add a public one that everyone can make use of. Tested-by: Jarkko Nikula Reviewed-by: Jarkko Nikula Reviewed-by: Andy Shevchenko Signed-off-by: Yicong Yang Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 21 +++++++++++++++++++++ include/linux/i2c.h | 3 +++ 2 files changed, 24 insertions(+) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index c4c28b585b50..5c16083694bf 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -76,6 +76,27 @@ void i2c_transfer_trace_unreg(void) static_branch_dec(&i2c_trace_msg_key); } +const char *i2c_freq_mode_string(u32 bus_freq_hz) +{ + switch (bus_freq_hz) { + case I2C_MAX_STANDARD_MODE_FREQ: + return "Standard Mode (100 kHz)"; + case I2C_MAX_FAST_MODE_FREQ: + return "Fast Mode (400 kHz)"; + case I2C_MAX_FAST_MODE_PLUS_FREQ: + return "Fast Mode Plus (1.0 MHz)"; + case I2C_MAX_TURBO_MODE_FREQ: + return "Turbo Mode (1.4 MHz)"; + case I2C_MAX_HIGH_SPEED_MODE_FREQ: + return "High Speed Mode (3.4 MHz)"; + case I2C_MAX_ULTRA_FAST_MODE_FREQ: + return "Ultra Fast Mode (5.0 MHz)"; + default: + return "Unknown Mode"; + } +} +EXPORT_SYMBOL_GPL(i2c_freq_mode_string); + const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, const struct i2c_client *client) { diff --git a/include/linux/i2c.h b/include/linux/i2c.h index ce473ddc0378..e5d8b9dad6bf 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -51,6 +51,9 @@ struct module; struct property_entry; #if IS_ENABLED(CONFIG_I2C) +/* Return the Frequency mode string based on the bus frequency */ +const char *i2c_freq_mode_string(u32 bus_freq_hz); + /* * The master routines are the ones normally used to transmit data to devices * on a bus (or read from them). Apart from two basic transfer functions to -- cgit v1.2.3 From d62fbdb99a85730af408399bfae9fa2aa708c6f1 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 8 Apr 2021 19:17:19 +0800 Subject: i2c: add support for HiSilicon I2C controller Add HiSilicon I2C controller driver for the Kunpeng SoC. It provides the access to the i2c busses, which connects to the eeprom, rtc, etc. The driver works with IRQ mode, and supports basic I2C features and 10bit address. The DMA is not supported. Reviewed-by: Andy Shevchenko Reviewed-by: Dmitry Osipenko Signed-off-by: Yicong Yang Signed-off-by: Wolfram Sang --- MAINTAINERS | 7 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-hisi.c | 504 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 522 insertions(+) create mode 100644 drivers/i2c/busses/i2c-hisi.c diff --git a/MAINTAINERS b/MAINTAINERS index 2e7607a1f929..56e9e4d777d8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8044,6 +8044,13 @@ F: drivers/crypto/hisilicon/hpre/hpre.h F: drivers/crypto/hisilicon/hpre/hpre_crypto.c F: drivers/crypto/hisilicon/hpre/hpre_main.c +HISILICON I2C CONTROLLER DRIVER +M: Yicong Yang +L: linux-i2c@vger.kernel.org +S: Maintained +W: https://www.hisilicon.com +F: drivers/i2c/busses/i2c-hisi.c + HISILICON LPC BUS DRIVER M: john.garry@huawei.com S: Maintained diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 338a306ec39e..0196d19955b7 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -645,6 +645,16 @@ config I2C_HIGHLANDER This driver can also be built as a module. If so, the module will be called i2c-highlander. +config I2C_HISI + tristate "HiSilicon I2C controller" + depends on ARM64 || COMPILE_TEST + help + Say Y here if you want to have Hisilicon I2C controller support + available on the Kunpeng Server. + + This driver can also be built as a module. If so, the module + will be called i2c-hisi. + config I2C_IBM_IIC tristate "IBM PPC 4xx on-chip I2C interface" depends on 4xx diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 45cd53515c77..69e9963615f6 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_I2C_EMEV2) += i2c-emev2.o obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o +obj-$(CONFIG_I2C_HISI) += i2c-hisi.o obj-$(CONFIG_I2C_HIX5HD2) += i2c-hix5hd2.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o obj-$(CONFIG_I2C_IMG) += i2c-img-scb.o diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c new file mode 100644 index 000000000000..acf394812061 --- /dev/null +++ b/drivers/i2c/busses/i2c-hisi.c @@ -0,0 +1,504 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * HiSilicon I2C Controller Driver for Kunpeng SoC + * + * Copyright (c) 2021 HiSilicon Technologies Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HISI_I2C_FRAME_CTRL 0x0000 +#define HISI_I2C_FRAME_CTRL_SPEED_MODE GENMASK(1, 0) +#define HISI_I2C_FRAME_CTRL_ADDR_TEN BIT(2) +#define HISI_I2C_SLV_ADDR 0x0004 +#define HISI_I2C_SLV_ADDR_VAL GENMASK(9, 0) +#define HISI_I2C_SLV_ADDR_GC_S_MODE BIT(10) +#define HISI_I2C_SLV_ADDR_GC_S_EN BIT(11) +#define HISI_I2C_CMD_TXDATA 0x0008 +#define HISI_I2C_CMD_TXDATA_DATA GENMASK(7, 0) +#define HISI_I2C_CMD_TXDATA_RW BIT(8) +#define HISI_I2C_CMD_TXDATA_P_EN BIT(9) +#define HISI_I2C_CMD_TXDATA_SR_EN BIT(10) +#define HISI_I2C_RXDATA 0x000c +#define HISI_I2C_RXDATA_DATA GENMASK(7, 0) +#define HISI_I2C_SS_SCL_HCNT 0x0010 +#define HISI_I2C_SS_SCL_LCNT 0x0014 +#define HISI_I2C_FS_SCL_HCNT 0x0018 +#define HISI_I2C_FS_SCL_LCNT 0x001c +#define HISI_I2C_HS_SCL_HCNT 0x0020 +#define HISI_I2C_HS_SCL_LCNT 0x0024 +#define HISI_I2C_FIFO_CTRL 0x0028 +#define HISI_I2C_FIFO_RX_CLR BIT(0) +#define HISI_I2C_FIFO_TX_CLR BIT(1) +#define HISI_I2C_FIFO_RX_AF_THRESH GENMASK(7, 2) +#define HISI_I2C_FIFO_TX_AE_THRESH GENMASK(13, 8) +#define HISI_I2C_FIFO_STATE 0x002c +#define HISI_I2C_FIFO_STATE_RX_RERR BIT(0) +#define HISI_I2C_FIFO_STATE_RX_WERR BIT(1) +#define HISI_I2C_FIFO_STATE_RX_EMPTY BIT(3) +#define HISI_I2C_FIFO_STATE_TX_RERR BIT(6) +#define HISI_I2C_FIFO_STATE_TX_WERR BIT(7) +#define HISI_I2C_FIFO_STATE_TX_FULL BIT(11) +#define HISI_I2C_SDA_HOLD 0x0030 +#define HISI_I2C_SDA_HOLD_TX GENMASK(15, 0) +#define HISI_I2C_SDA_HOLD_RX GENMASK(23, 16) +#define HISI_I2C_FS_SPK_LEN 0x0038 +#define HISI_I2C_FS_SPK_LEN_CNT GENMASK(7, 0) +#define HISI_I2C_HS_SPK_LEN 0x003c +#define HISI_I2C_HS_SPK_LEN_CNT GENMASK(7, 0) +#define HISI_I2C_INT_MSTAT 0x0044 +#define HISI_I2C_INT_CLR 0x0048 +#define HISI_I2C_INT_MASK 0x004C +#define HISI_I2C_TRANS_STATE 0x0050 +#define HISI_I2C_TRANS_ERR 0x0054 +#define HISI_I2C_VERSION 0x0058 + +#define HISI_I2C_INT_ALL GENMASK(4, 0) +#define HISI_I2C_INT_TRANS_CPLT BIT(0) +#define HISI_I2C_INT_TRANS_ERR BIT(1) +#define HISI_I2C_INT_FIFO_ERR BIT(2) +#define HISI_I2C_INT_RX_FULL BIT(3) +#define HISI_I2C_INT_TX_EMPTY BIT(4) +#define HISI_I2C_INT_ERR \ + (HISI_I2C_INT_TRANS_ERR | HISI_I2C_INT_FIFO_ERR) + +#define HISI_I2C_STD_SPEED_MODE 0 +#define HISI_I2C_FAST_SPEED_MODE 1 +#define HISI_I2C_HIGH_SPEED_MODE 2 + +#define HISI_I2C_TX_FIFO_DEPTH 64 +#define HISI_I2C_RX_FIFO_DEPTH 64 +#define HISI_I2C_TX_F_AE_THRESH 1 +#define HISI_I2C_RX_F_AF_THRESH 60 + +#define HZ_PER_KHZ 1000 + +#define NSEC_TO_CYCLES(ns, clk_rate_khz) \ + DIV_ROUND_UP_ULL((clk_rate_khz) * (ns), NSEC_PER_MSEC) + +struct hisi_i2c_controller { + struct i2c_adapter adapter; + void __iomem *iobase; + struct device *dev; + int irq; + + /* Intermediates for recording the transfer process */ + struct completion *completion; + struct i2c_msg *msgs; + int msg_num; + int msg_tx_idx; + int buf_tx_idx; + int msg_rx_idx; + int buf_rx_idx; + u16 tar_addr; + u32 xfer_err; + + /* I2C bus configuration */ + struct i2c_timings t; + u32 clk_rate_khz; + u32 spk_len; +}; + +static void hisi_i2c_enable_int(struct hisi_i2c_controller *ctlr, u32 mask) +{ + writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_MASK); +} + +static void hisi_i2c_disable_int(struct hisi_i2c_controller *ctlr, u32 mask) +{ + writel_relaxed((~mask) & HISI_I2C_INT_ALL, ctlr->iobase + HISI_I2C_INT_MASK); +} + +static void hisi_i2c_clear_int(struct hisi_i2c_controller *ctlr, u32 mask) +{ + writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_CLR); +} + +static void hisi_i2c_handle_errors(struct hisi_i2c_controller *ctlr) +{ + u32 int_err = ctlr->xfer_err, reg; + + if (int_err & HISI_I2C_INT_FIFO_ERR) { + reg = readl(ctlr->iobase + HISI_I2C_FIFO_STATE); + + if (reg & HISI_I2C_FIFO_STATE_RX_RERR) + dev_err(ctlr->dev, "rx fifo error read\n"); + + if (reg & HISI_I2C_FIFO_STATE_RX_WERR) + dev_err(ctlr->dev, "rx fifo error write\n"); + + if (reg & HISI_I2C_FIFO_STATE_TX_RERR) + dev_err(ctlr->dev, "tx fifo error read\n"); + + if (reg & HISI_I2C_FIFO_STATE_TX_WERR) + dev_err(ctlr->dev, "tx fifo error write\n"); + } +} + +static int hisi_i2c_start_xfer(struct hisi_i2c_controller *ctlr) +{ + struct i2c_msg *msg = ctlr->msgs; + u32 reg; + + reg = readl(ctlr->iobase + HISI_I2C_FRAME_CTRL); + reg &= ~HISI_I2C_FRAME_CTRL_ADDR_TEN; + if (msg->flags & I2C_M_TEN) + reg |= HISI_I2C_FRAME_CTRL_ADDR_TEN; + writel(reg, ctlr->iobase + HISI_I2C_FRAME_CTRL); + + reg = readl(ctlr->iobase + HISI_I2C_SLV_ADDR); + reg &= ~HISI_I2C_SLV_ADDR_VAL; + reg |= FIELD_PREP(HISI_I2C_SLV_ADDR_VAL, msg->addr); + writel(reg, ctlr->iobase + HISI_I2C_SLV_ADDR); + + reg = readl(ctlr->iobase + HISI_I2C_FIFO_CTRL); + reg |= HISI_I2C_FIFO_RX_CLR | HISI_I2C_FIFO_TX_CLR; + writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL); + reg &= ~(HISI_I2C_FIFO_RX_CLR | HISI_I2C_FIFO_TX_CLR); + writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL); + + hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL); + hisi_i2c_enable_int(ctlr, HISI_I2C_INT_ALL); + + return 0; +} + +static void hisi_i2c_reset_xfer(struct hisi_i2c_controller *ctlr) +{ + ctlr->msg_num = 0; + ctlr->xfer_err = 0; + ctlr->msg_tx_idx = 0; + ctlr->msg_rx_idx = 0; + ctlr->buf_tx_idx = 0; + ctlr->buf_rx_idx = 0; +} + +/* + * Initialize the transfer information and start the I2C bus transfer. + * We only configure the transfer and do some pre/post works here, and + * wait for the transfer done. The major transfer process is performed + * in the IRQ handler. + */ +static int hisi_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct hisi_i2c_controller *ctlr = i2c_get_adapdata(adap); + DECLARE_COMPLETION_ONSTACK(done); + int ret = num; + + hisi_i2c_reset_xfer(ctlr); + ctlr->completion = &done; + ctlr->msg_num = num; + ctlr->msgs = msgs; + + hisi_i2c_start_xfer(ctlr); + + if (!wait_for_completion_timeout(ctlr->completion, adap->timeout)) { + hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL); + synchronize_irq(ctlr->irq); + i2c_recover_bus(&ctlr->adapter); + dev_err(ctlr->dev, "bus transfer timeout\n"); + ret = -EIO; + } + + if (ctlr->xfer_err) { + hisi_i2c_handle_errors(ctlr); + ret = -EIO; + } + + hisi_i2c_reset_xfer(ctlr); + ctlr->completion = NULL; + + return ret; +} + +static u32 hisi_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm hisi_i2c_algo = { + .master_xfer = hisi_i2c_master_xfer, + .functionality = hisi_i2c_functionality, +}; + +static int hisi_i2c_read_rx_fifo(struct hisi_i2c_controller *ctlr) +{ + struct i2c_msg *cur_msg; + u32 fifo_state; + + while (ctlr->msg_rx_idx < ctlr->msg_num) { + cur_msg = ctlr->msgs + ctlr->msg_rx_idx; + + if (!(cur_msg->flags & I2C_M_RD)) { + ctlr->msg_rx_idx++; + continue; + } + + fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE); + while (!(fifo_state & HISI_I2C_FIFO_STATE_RX_EMPTY) && + ctlr->buf_rx_idx < cur_msg->len) { + cur_msg->buf[ctlr->buf_rx_idx++] = readl(ctlr->iobase + HISI_I2C_RXDATA); + fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE); + } + + if (ctlr->buf_rx_idx == cur_msg->len) { + ctlr->buf_rx_idx = 0; + ctlr->msg_rx_idx++; + } + + if (fifo_state & HISI_I2C_FIFO_STATE_RX_EMPTY) + break; + } + + return 0; +} + +static void hisi_i2c_xfer_msg(struct hisi_i2c_controller *ctlr) +{ + int max_write = HISI_I2C_TX_FIFO_DEPTH; + bool need_restart = false, last_msg; + struct i2c_msg *cur_msg; + u32 cmd, fifo_state; + + while (ctlr->msg_tx_idx < ctlr->msg_num) { + cur_msg = ctlr->msgs + ctlr->msg_tx_idx; + last_msg = (ctlr->msg_tx_idx == ctlr->msg_num - 1); + + /* Signal the SR bit when we start transferring a new message */ + if (ctlr->msg_tx_idx && !ctlr->buf_tx_idx) + need_restart = true; + + fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE); + while (!(fifo_state & HISI_I2C_FIFO_STATE_TX_FULL) && + ctlr->buf_tx_idx < cur_msg->len && max_write) { + cmd = 0; + + if (need_restart) { + cmd |= HISI_I2C_CMD_TXDATA_SR_EN; + need_restart = false; + } + + /* Signal the STOP bit at the last frame of the last message */ + if (ctlr->buf_tx_idx == cur_msg->len - 1 && last_msg) + cmd |= HISI_I2C_CMD_TXDATA_P_EN; + + if (cur_msg->flags & I2C_M_RD) + cmd |= HISI_I2C_CMD_TXDATA_RW; + else + cmd |= FIELD_PREP(HISI_I2C_CMD_TXDATA_DATA, + cur_msg->buf[ctlr->buf_tx_idx]); + + writel(cmd, ctlr->iobase + HISI_I2C_CMD_TXDATA); + ctlr->buf_tx_idx++; + max_write--; + + fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE); + } + + /* Update the transfer index after per message transfer is done. */ + if (ctlr->buf_tx_idx == cur_msg->len) { + ctlr->buf_tx_idx = 0; + ctlr->msg_tx_idx++; + } + + if ((fifo_state & HISI_I2C_FIFO_STATE_TX_FULL) || + max_write == 0) + break; + } +} + +static irqreturn_t hisi_i2c_irq(int irq, void *context) +{ + struct hisi_i2c_controller *ctlr = context; + u32 int_stat; + + int_stat = readl(ctlr->iobase + HISI_I2C_INT_MSTAT); + hisi_i2c_clear_int(ctlr, int_stat); + if (!(int_stat & HISI_I2C_INT_ALL)) + return IRQ_NONE; + + if (int_stat & HISI_I2C_INT_TX_EMPTY) + hisi_i2c_xfer_msg(ctlr); + + if (int_stat & HISI_I2C_INT_ERR) { + ctlr->xfer_err = int_stat; + goto out; + } + + /* Drain the rx fifo before finish the transfer */ + if (int_stat & (HISI_I2C_INT_TRANS_CPLT | HISI_I2C_INT_RX_FULL)) + hisi_i2c_read_rx_fifo(ctlr); + +out: + if (int_stat & HISI_I2C_INT_TRANS_CPLT || ctlr->xfer_err) { + hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL); + hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL); + complete(ctlr->completion); + } + + return IRQ_HANDLED; +} + +/* + * Helper function for calculating and configuring the HIGH and LOW + * periods of SCL clock. The caller will pass the ratio of the + * counts (divide / divisor) according to the target speed mode, + * and the target registers. + */ +static void hisi_i2c_set_scl(struct hisi_i2c_controller *ctlr, + u32 divide, u32 divisor, + u32 reg_hcnt, u32 reg_lcnt) +{ + u32 total_cnt, t_scl_hcnt, t_scl_lcnt, scl_fall_cnt, scl_rise_cnt; + u32 scl_hcnt, scl_lcnt; + + /* Total SCL clock cycles per speed period */ + total_cnt = DIV_ROUND_UP_ULL(ctlr->clk_rate_khz * HZ_PER_KHZ, ctlr->t.bus_freq_hz); + /* Total HIGH level SCL clock cycles including edges */ + t_scl_hcnt = DIV_ROUND_UP_ULL(total_cnt * divide, divisor); + /* Total LOW level SCL clock cycles including edges */ + t_scl_lcnt = total_cnt - t_scl_hcnt; + /* Fall edge SCL clock cycles */ + scl_fall_cnt = NSEC_TO_CYCLES(ctlr->t.scl_fall_ns, ctlr->clk_rate_khz); + /* Rise edge SCL clock cycles */ + scl_rise_cnt = NSEC_TO_CYCLES(ctlr->t.scl_rise_ns, ctlr->clk_rate_khz); + + /* Calculated HIGH and LOW periods of SCL clock */ + scl_hcnt = t_scl_hcnt - ctlr->spk_len - 7 - scl_fall_cnt; + scl_lcnt = t_scl_lcnt - 1 - scl_rise_cnt; + + writel(scl_hcnt, ctlr->iobase + reg_hcnt); + writel(scl_lcnt, ctlr->iobase + reg_lcnt); +} + +static void hisi_i2c_configure_bus(struct hisi_i2c_controller *ctlr) +{ + u32 reg, sda_hold_cnt, speed_mode; + + i2c_parse_fw_timings(ctlr->dev, &ctlr->t, true); + ctlr->spk_len = NSEC_TO_CYCLES(ctlr->t.digital_filter_width_ns, ctlr->clk_rate_khz); + + switch (ctlr->t.bus_freq_hz) { + case I2C_MAX_FAST_MODE_FREQ: + speed_mode = HISI_I2C_FAST_SPEED_MODE; + hisi_i2c_set_scl(ctlr, 26, 76, HISI_I2C_FS_SCL_HCNT, HISI_I2C_FS_SCL_LCNT); + break; + case I2C_MAX_HIGH_SPEED_MODE_FREQ: + speed_mode = HISI_I2C_HIGH_SPEED_MODE; + hisi_i2c_set_scl(ctlr, 6, 22, HISI_I2C_HS_SCL_HCNT, HISI_I2C_HS_SCL_LCNT); + break; + case I2C_MAX_STANDARD_MODE_FREQ: + default: + speed_mode = HISI_I2C_STD_SPEED_MODE; + + /* For default condition force the bus speed to standard mode. */ + ctlr->t.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ; + hisi_i2c_set_scl(ctlr, 40, 87, HISI_I2C_SS_SCL_HCNT, HISI_I2C_SS_SCL_LCNT); + break; + } + + reg = readl(ctlr->iobase + HISI_I2C_FRAME_CTRL); + reg &= ~HISI_I2C_FRAME_CTRL_SPEED_MODE; + reg |= FIELD_PREP(HISI_I2C_FRAME_CTRL_SPEED_MODE, speed_mode); + writel(reg, ctlr->iobase + HISI_I2C_FRAME_CTRL); + + sda_hold_cnt = NSEC_TO_CYCLES(ctlr->t.sda_hold_ns, ctlr->clk_rate_khz); + + reg = FIELD_PREP(HISI_I2C_SDA_HOLD_TX, sda_hold_cnt); + writel(reg, ctlr->iobase + HISI_I2C_SDA_HOLD); + + writel(ctlr->spk_len, ctlr->iobase + HISI_I2C_FS_SPK_LEN); + + reg = FIELD_PREP(HISI_I2C_FIFO_RX_AF_THRESH, HISI_I2C_RX_F_AF_THRESH); + reg |= FIELD_PREP(HISI_I2C_FIFO_TX_AE_THRESH, HISI_I2C_TX_F_AE_THRESH); + writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL); +} + +static int hisi_i2c_probe(struct platform_device *pdev) +{ + struct hisi_i2c_controller *ctlr; + struct device *dev = &pdev->dev; + struct i2c_adapter *adapter; + u64 clk_rate_hz; + u32 hw_version; + int ret; + + ctlr = devm_kzalloc(dev, sizeof(*ctlr), GFP_KERNEL); + if (!ctlr) + return -ENOMEM; + + ctlr->iobase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ctlr->iobase)) + return PTR_ERR(ctlr->iobase); + + ctlr->irq = platform_get_irq(pdev, 0); + if (ctlr->irq < 0) + return ctlr->irq; + + ctlr->dev = dev; + + hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL); + + ret = devm_request_irq(dev, ctlr->irq, hisi_i2c_irq, 0, "hisi-i2c", ctlr); + if (ret) { + dev_err(dev, "failed to request irq handler, ret = %d\n", ret); + return ret; + } + + ret = device_property_read_u64(dev, "clk_rate", &clk_rate_hz); + if (ret) { + dev_err(dev, "failed to get clock frequency, ret = %d\n", ret); + return ret; + } + + ctlr->clk_rate_khz = DIV_ROUND_UP_ULL(clk_rate_hz, HZ_PER_KHZ); + + hisi_i2c_configure_bus(ctlr); + + adapter = &ctlr->adapter; + snprintf(adapter->name, sizeof(adapter->name), + "HiSilicon I2C Controller %s", dev_name(dev)); + adapter->owner = THIS_MODULE; + adapter->algo = &hisi_i2c_algo; + adapter->dev.parent = dev; + i2c_set_adapdata(adapter, ctlr); + + ret = devm_i2c_add_adapter(dev, adapter); + if (ret) + return ret; + + hw_version = readl(ctlr->iobase + HISI_I2C_VERSION); + dev_info(ctlr->dev, "speed mode is %s. hw version 0x%x\n", + i2c_freq_mode_string(ctlr->t.bus_freq_hz), hw_version); + + return 0; +} + +static const struct acpi_device_id hisi_i2c_acpi_ids[] = { + { "HISI03D1", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hisi_i2c_acpi_ids); + +static struct platform_driver hisi_i2c_driver = { + .probe = hisi_i2c_probe, + .driver = { + .name = "hisi-i2c", + .acpi_match_table = hisi_i2c_acpi_ids, + }, +}; +module_platform_driver(hisi_i2c_driver); + +MODULE_AUTHOR("Yicong Yang "); +MODULE_DESCRIPTION("HiSilicon I2C Controller Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 71aee62783e9dc16472acf7657ce16318613ad2f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 8 Apr 2021 19:17:21 +0800 Subject: i2c: designware: Switch over to i2c_freq_mode_string() Use generic i2c_freq_mode_string() helper to print chosen bus speed. Acked-by: Jarkko Nikula Signed-off-by: Andy Shevchenko Signed-off-by: Yicong Yang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-master.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 8a59470a82ad..ceae6b2fed83 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -39,10 +39,10 @@ static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev) static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) { - const char *mode_str, *fp_str = ""; u32 comp_param1; u32 sda_falling_time, scl_falling_time; struct i2c_timings *t = &dev->timings; + const char *fp_str = ""; u32 ic_clk; int ret; @@ -157,22 +157,10 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) ret = i2c_dw_set_sda_hold(dev); if (ret) - goto out; - - switch (dev->master_cfg & DW_IC_CON_SPEED_MASK) { - case DW_IC_CON_SPEED_STD: - mode_str = "Standard Mode"; - break; - case DW_IC_CON_SPEED_HIGH: - mode_str = "High Speed Mode"; - break; - default: - mode_str = "Fast Mode"; - } - dev_dbg(dev->dev, "Bus speed: %s%s\n", mode_str, fp_str); + return ret; -out: - return ret; + dev_dbg(dev->dev, "Bus speed: %s\n", i2c_freq_mode_string(t->bus_freq_hz)); + return 0; } /** -- cgit v1.2.3 From 5b5475826c5265cead7ce4ca6d34ec0c566c70aa Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 21 Mar 2021 18:38:32 -0700 Subject: i2c: ensure timely release of driver-allocated resources More and more drivers rely on devres to manage their resources, however if bus' probe() and release() methods are not trivial and control some of resources as well (for example enable or disable clocks, or attach device to a power domain), we need to make sure that driver-allocated resources are released immediately after driver's remove() method returns, and not postponed until driver core gets around to releasing resources. To fix that we open a new devres group before calling driver's probe() and explicitly release it when we return from driver's remove(). Tested-by: Jeff LaBundy Signed-off-by: Dmitry Torokhov Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 21 ++++++++++++++++++++- include/linux/i2c.h | 3 +++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 5c16083694bf..4351bf529d0f 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -539,6 +539,13 @@ static int i2c_device_probe(struct device *dev) if (status) goto err_clear_wakeup_irq; + client->devres_group_id = devres_open_group(&client->dev, NULL, + GFP_KERNEL); + if (!client->devres_group_id) { + status = -ENOMEM; + goto err_detach_pm_domain; + } + /* * When there are no more users of probe(), * rename probe_new to probe. @@ -551,11 +558,21 @@ static int i2c_device_probe(struct device *dev) else status = -EINVAL; + /* + * Note that we are not closing the devres group opened above so + * even resources that were attached to the device after probe is + * run are released when i2c_device_remove() is executed. This is + * needed as some drivers would allocate additional resources, + * for example when updating firmware. + */ + if (status) - goto err_detach_pm_domain; + goto err_release_driver_resources; return 0; +err_release_driver_resources: + devres_release_group(&client->dev, client->devres_group_id); err_detach_pm_domain: dev_pm_domain_detach(&client->dev, true); err_clear_wakeup_irq: @@ -584,6 +601,8 @@ static int i2c_device_remove(struct device *dev) dev_warn(dev, "remove failed (%pe), will be ignored\n", ERR_PTR(status)); } + devres_release_group(&client->dev, client->devres_group_id); + dev_pm_domain_detach(&client->dev, true); dev_pm_clear_wake_irq(&client->dev); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index e5d8b9dad6bf..e8f2ac8c9c3d 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -309,6 +309,8 @@ struct i2c_driver { * userspace_devices list * @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter * calls it to pass on slave events to the slave driver. + * @devres_group_id: id of the devres group that will be created for resources + * acquired when probing this device. * * An i2c_client identifies a single device (i.e. chip) connected to an * i2c bus. The behaviour exposed to Linux is defined by the driver @@ -337,6 +339,7 @@ struct i2c_client { #if IS_ENABLED(CONFIG_I2C_SLAVE) i2c_slave_cb_t slave_cb; /* callback for slave mode */ #endif + void *devres_group_id; /* ID of probe devres group */ }; #define to_i2c_client(d) container_of(d, struct i2c_client, dev) -- cgit v1.2.3 From a888f9b95a448017d9c6b1dc58e6dc57f02505de Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Mon, 29 Mar 2021 14:52:01 +1300 Subject: dt-bindings: i2c-mpc: Document interrupt property as required All of the in-tree device-trees that use the one of the compatible strings from i2c-mpc.c supply an interrupts property. Make this property mandatory to aid refactoring the driver. Signed-off-by: Chris Packham Reviewed-by: Rob Herring Signed-off-by: Wolfram Sang --- Documentation/devicetree/bindings/i2c/i2c-mpc.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/i2c/i2c-mpc.txt b/Documentation/devicetree/bindings/i2c/i2c-mpc.txt index 42a390526957..b15acb43d84d 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-mpc.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-mpc.txt @@ -7,14 +7,14 @@ Required properties : compatible processor, e.g. mpc8313, mpc8543, mpc8544, mpc5121, mpc5200 or mpc5200b. For the mpc5121, an additional node "fsl,mpc5121-i2c-ctrl" is required as shown in the example below. - -Recommended properties : - - interrupts : where a is the interrupt number and b is a field that represents an encoding of the sense and level information for the interrupt. This should be encoded based on the information in section 2) depending on the type of interrupt controller you have. + +Recommended properties : + - fsl,preserve-clocking : boolean; if defined, the clock settings from the bootloader are preserved (not touched). - clock-frequency : desired I2C bus clock frequency in Hz. -- cgit v1.2.3 From 81acb4015a96cb9646765e8a022681934817fa7a Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Mon, 29 Mar 2021 14:52:02 +1300 Subject: dt-bindings: i2c: convert i2c-mpc to json-schema Convert i2c-mpc to YAML. Signed-off-by: Chris Packham Reviewed-by: Rob Herring Signed-off-by: Wolfram Sang --- Documentation/devicetree/bindings/i2c/i2c-mpc.txt | 62 --------------- Documentation/devicetree/bindings/i2c/i2c-mpc.yaml | 91 ++++++++++++++++++++++ 2 files changed, 91 insertions(+), 62 deletions(-) delete mode 100644 Documentation/devicetree/bindings/i2c/i2c-mpc.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mpc.yaml diff --git a/Documentation/devicetree/bindings/i2c/i2c-mpc.txt b/Documentation/devicetree/bindings/i2c/i2c-mpc.txt deleted file mode 100644 index b15acb43d84d..000000000000 --- a/Documentation/devicetree/bindings/i2c/i2c-mpc.txt +++ /dev/null @@ -1,62 +0,0 @@ -* I2C - -Required properties : - - - reg : Offset and length of the register set for the device - - compatible : should be "fsl,CHIP-i2c" where CHIP is the name of a - compatible processor, e.g. mpc8313, mpc8543, mpc8544, mpc5121, - mpc5200 or mpc5200b. For the mpc5121, an additional node - "fsl,mpc5121-i2c-ctrl" is required as shown in the example below. - - interrupts : where a is the interrupt number and b is a - field that represents an encoding of the sense and level - information for the interrupt. This should be encoded based on - the information in section 2) depending on the type of interrupt - controller you have. - -Recommended properties : - - - fsl,preserve-clocking : boolean; if defined, the clock settings - from the bootloader are preserved (not touched). - - clock-frequency : desired I2C bus clock frequency in Hz. - - fsl,timeout : I2C bus timeout in microseconds. - -Examples : - - /* MPC5121 based board */ - i2c@1740 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,mpc5121-i2c", "fsl-i2c"; - reg = <0x1740 0x20>; - interrupts = <11 0x8>; - interrupt-parent = <&ipic>; - clock-frequency = <100000>; - }; - - i2ccontrol@1760 { - compatible = "fsl,mpc5121-i2c-ctrl"; - reg = <0x1760 0x8>; - }; - - /* MPC5200B based board */ - i2c@3d00 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,mpc5200b-i2c","fsl,mpc5200-i2c","fsl-i2c"; - reg = <0x3d00 0x40>; - interrupts = <2 15 0>; - interrupt-parent = <&mpc5200_pic>; - fsl,preserve-clocking; - }; - - /* MPC8544 base board */ - i2c@3100 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,mpc8544-i2c", "fsl-i2c"; - reg = <0x3100 0x100>; - interrupts = <43 2>; - interrupt-parent = <&mpic>; - clock-frequency = <400000>; - fsl,timeout = <10000>; - }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-mpc.yaml b/Documentation/devicetree/bindings/i2c/i2c-mpc.yaml new file mode 100644 index 000000000000..7b553d559c83 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-mpc.yaml @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/i2c-mpc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: I2C-Bus adapter for MPC824x/83xx/85xx/86xx/512x/52xx SoCs + +maintainers: + - Chris Packham + +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - mpc5200-i2c + - fsl,mpc5200-i2c + - fsl,mpc5121-i2c + - fsl,mpc8313-i2c + - fsl,mpc8543-i2c + - fsl,mpc8544-i2c + - const: fsl-i2c + - items: + - const: fsl,mpc5200b-i2c + - const: fsl,mpc5200-i2c + - const: fsl-i2c + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + fsl,preserve-clocking: + $ref: /schemas/types.yaml#/definitions/flag + description: | + if defined, the clock settings from the bootloader are + preserved (not touched) + + fsl,timeout: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + I2C bus timeout in microseconds + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + /* MPC5121 based board */ + i2c@1740 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,mpc5121-i2c", "fsl-i2c"; + reg = <0x1740 0x20>; + interrupts = <11 0x8>; + interrupt-parent = <&ipic>; + clock-frequency = <100000>; + }; + + /* MPC5200B based board */ + i2c@3d00 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,mpc5200b-i2c", "fsl,mpc5200-i2c", "fsl-i2c"; + reg = <0x3d00 0x40>; + interrupts = <2 15 0>; + interrupt-parent = <&mpc5200_pic>; + fsl,preserve-clocking; + }; + + /* MPC8544 base board */ + i2c@3100 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,mpc8544-i2c", "fsl-i2c"; + reg = <0x3100 0x100>; + interrupts = <43 2>; + interrupt-parent = <&mpic>; + clock-frequency = <400000>; + fsl,timeout = <10000>; + }; +... -- cgit v1.2.3 From 65171b2df15eb7545431d75c2729b5062da89b43 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Mon, 29 Mar 2021 14:52:03 +1300 Subject: i2c: mpc: Make use of i2c_recover_bus() Move the existing calls of mpc_i2c_fixup() to a recovery function registered via bus_recovery_info. This makes it more obvious that recovery is supported and allows for a future where recovery is triggered by the i2c core. Signed-off-by: Chris Packham Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mpc.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index d94f05c8b8b7..6a0d55e9e8e3 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -586,7 +586,7 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) { writeb(status & ~CSR_MAL, i2c->base + MPC_I2C_SR); - mpc_i2c_fixup(i2c); + i2c_recover_bus(&i2c->adap); } return -EIO; } @@ -622,7 +622,7 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) { writeb(status & ~CSR_MAL, i2c->base + MPC_I2C_SR); - mpc_i2c_fixup(i2c); + i2c_recover_bus(&i2c->adap); } return -EIO; } @@ -637,6 +637,15 @@ static u32 mpc_functionality(struct i2c_adapter *adap) | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL; } +static int fsl_i2c_bus_recovery(struct i2c_adapter *adap) +{ + struct mpc_i2c *i2c = i2c_get_adapdata(adap); + + mpc_i2c_fixup(i2c); + + return 0; +} + static const struct i2c_algorithm mpc_algo = { .master_xfer = mpc_xfer, .functionality = mpc_functionality, @@ -648,6 +657,10 @@ static struct i2c_adapter mpc_ops = { .timeout = HZ, }; +static struct i2c_bus_recovery_info fsl_i2c_recovery_info = { + .recover_bus = fsl_i2c_bus_recovery, +}; + static const struct of_device_id mpc_i2c_of_match[]; static int fsl_i2c_probe(struct platform_device *op) { @@ -740,6 +753,7 @@ static int fsl_i2c_probe(struct platform_device *op) i2c_set_adapdata(&i2c->adap, i2c); i2c->adap.dev.parent = &op->dev; i2c->adap.dev.of_node = of_node_get(op->dev.of_node); + i2c->adap.bus_recovery_info = &fsl_i2c_recovery_info; result = i2c_add_adapter(&i2c->adap); if (result < 0) -- cgit v1.2.3 From bc72675228c781996d7f62849e84dd23145479d5 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Mon, 29 Mar 2021 14:52:04 +1300 Subject: i2c: mpc: make interrupt mandatory and remove polling code All the in-tree dts files that use one of the compatible strings from i2c-mpc.c provide an interrupt property. By making this mandatory we can simplify the code. Signed-off-by: Chris Packham Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mpc.c | 51 +++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 6a0d55e9e8e3..5b746a898e8e 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -123,37 +123,21 @@ static void mpc_i2c_fixup(struct mpc_i2c *i2c) static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing) { - unsigned long orig_jiffies = jiffies; u32 cmd_err; - int result = 0; + int result; - if (!i2c->irq) { - while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) { - schedule(); - if (time_after(jiffies, orig_jiffies + timeout)) { - dev_dbg(i2c->dev, "timeout\n"); - writeccr(i2c, 0); - result = -ETIMEDOUT; - break; - } - } - cmd_err = readb(i2c->base + MPC_I2C_SR); - writeb(0, i2c->base + MPC_I2C_SR); - } else { - /* Interrupt mode */ - result = wait_event_timeout(i2c->queue, + result = wait_event_timeout(i2c->queue, (i2c->interrupt & CSR_MIF), timeout); - if (unlikely(!(i2c->interrupt & CSR_MIF))) { - dev_dbg(i2c->dev, "wait timeout\n"); - writeccr(i2c, 0); - result = -ETIMEDOUT; - } - - cmd_err = i2c->interrupt; - i2c->interrupt = 0; + if (unlikely(!(i2c->interrupt & CSR_MIF))) { + dev_dbg(i2c->dev, "wait timeout\n"); + writeccr(i2c, 0); + result = -ETIMEDOUT; } + cmd_err = i2c->interrupt; + i2c->interrupt = 0; + if (result < 0) return result; @@ -694,13 +678,16 @@ static int fsl_i2c_probe(struct platform_device *op) } i2c->irq = irq_of_parse_and_map(op->dev.of_node, 0); - if (i2c->irq) { /* no i2c->irq implies polling */ - result = request_irq(i2c->irq, mpc_i2c_isr, - IRQF_SHARED, "i2c-mpc", i2c); - if (result < 0) { - dev_err(i2c->dev, "failed to attach interrupt\n"); - goto fail_request; - } + if (i2c->irq < 0) { + result = i2c->irq; + goto fail_map; + } + + result = request_irq(i2c->irq, mpc_i2c_isr, + IRQF_SHARED, "i2c-mpc", i2c); + if (result < 0) { + dev_err(i2c->dev, "failed to attach interrupt\n"); + goto fail_request; } /* -- cgit v1.2.3 From 09aab7add7bf9a7368da94fd09529847255f5fd9 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Mon, 29 Mar 2021 14:52:05 +1300 Subject: i2c: mpc: use device managed APIs Use device managed functions an clean up error handling. Signed-off-by: Chris Packham Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mpc.c | 46 +++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 5b746a898e8e..46cdb36e2f9b 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -654,7 +654,6 @@ static int fsl_i2c_probe(struct platform_device *op) u32 clock = MPC_I2C_CLOCK_LEGACY; int result = 0; int plen; - struct resource res; struct clk *clk; int err; @@ -662,7 +661,7 @@ static int fsl_i2c_probe(struct platform_device *op) if (!match) return -EINVAL; - i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); + i2c = devm_kzalloc(&op->dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) return -ENOMEM; @@ -670,24 +669,21 @@ static int fsl_i2c_probe(struct platform_device *op) init_waitqueue_head(&i2c->queue); - i2c->base = of_iomap(op->dev.of_node, 0); - if (!i2c->base) { + i2c->base = devm_platform_ioremap_resource(op, 0); + if (IS_ERR(i2c->base)) { dev_err(i2c->dev, "failed to map controller\n"); - result = -ENOMEM; - goto fail_map; + return PTR_ERR(i2c->base); } - i2c->irq = irq_of_parse_and_map(op->dev.of_node, 0); - if (i2c->irq < 0) { - result = i2c->irq; - goto fail_map; - } + i2c->irq = platform_get_irq(op, 0); + if (i2c->irq < 0) + return i2c->irq; - result = request_irq(i2c->irq, mpc_i2c_isr, + result = devm_request_irq(&op->dev, i2c->irq, mpc_i2c_isr, IRQF_SHARED, "i2c-mpc", i2c); if (result < 0) { dev_err(i2c->dev, "failed to attach interrupt\n"); - goto fail_request; + return result; } /* @@ -699,7 +695,7 @@ static int fsl_i2c_probe(struct platform_device *op) err = clk_prepare_enable(clk); if (err) { dev_err(&op->dev, "failed to enable clock\n"); - goto fail_request; + return err; } else { i2c->clk_per = clk; } @@ -731,32 +727,26 @@ static int fsl_i2c_probe(struct platform_device *op) } dev_info(i2c->dev, "timeout %u us\n", mpc_ops.timeout * 1000000 / HZ); - platform_set_drvdata(op, i2c); - i2c->adap = mpc_ops; - of_address_to_resource(op->dev.of_node, 0, &res); scnprintf(i2c->adap.name, sizeof(i2c->adap.name), - "MPC adapter at 0x%llx", (unsigned long long)res.start); - i2c_set_adapdata(&i2c->adap, i2c); + "MPC adapter (%s)", of_node_full_name(op->dev.of_node)); i2c->adap.dev.parent = &op->dev; + i2c->adap.nr = op->id; i2c->adap.dev.of_node = of_node_get(op->dev.of_node); i2c->adap.bus_recovery_info = &fsl_i2c_recovery_info; + platform_set_drvdata(op, i2c); + i2c_set_adapdata(&i2c->adap, i2c); - result = i2c_add_adapter(&i2c->adap); - if (result < 0) + result = i2c_add_numbered_adapter(&i2c->adap); + if (result) goto fail_add; - return result; + return 0; fail_add: if (i2c->clk_per) clk_disable_unprepare(i2c->clk_per); - free_irq(i2c->irq, i2c); - fail_request: - irq_dispose_mapping(i2c->irq); - iounmap(i2c->base); - fail_map: - kfree(i2c); + return result; }; -- cgit v1.2.3 From 4aa3e48d2e09424449b2e0f2d5581388ba9e261b Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 12 Apr 2021 16:00:26 +0000 Subject: i2c: mpc: drop release for resource allocated with devm_* It's not necessary to release resource which allocated with devm_* and those release may leads to a double free. And also remove useless irq_dispose_mapping() call since mapping not created. Fixes: 09aab7add7bf ("i2c: mpc: use device managed APIs") Reported-by: Hulk Robot Signed-off-by: Wei Yongjun Reviewed-by: Chris Packham Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mpc.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 46cdb36e2f9b..6e5614acebac 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -759,12 +759,6 @@ static int fsl_i2c_remove(struct platform_device *op) if (i2c->clk_per) clk_disable_unprepare(i2c->clk_per); - if (i2c->irq) - free_irq(i2c->irq, i2c); - - irq_dispose_mapping(i2c->irq); - iounmap(i2c->base); - kfree(i2c); return 0; }; -- cgit v1.2.3 From 270282bdf4e5a2484e1244db67e3743cefca6d8f Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Tue, 13 Apr 2021 17:09:54 +1200 Subject: i2c: mpc: Remove redundant NULL check In mpc_i2c_get_fdr_8xxx div is assigned as we iterate through the mpc_i2c_dividers_8xxx array. By the time we exit the loop div will either have the value that matches the requested speed or be pointing at the last entry in mpc_i2c_dividers_8xxx. Checking for div being NULL after the loop is redundant so remove the check. Reported-by: Wolfram Sang Signed-off-by: Chris Packham Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 6e5614acebac..3c8bcdfff7e7 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -399,7 +399,7 @@ static int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, } *real_clk = fsl_get_sys_freq() / prescaler / div->divider; - return div ? (int)div->fdr : -EINVAL; + return (int)div->fdr; } static void mpc_i2c_setup_8xxx(struct device_node *node, -- cgit v1.2.3 From e8bbc3497e63a887231f88b58e7aeb56906dc1c4 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Tue, 13 Apr 2021 17:09:56 +1200 Subject: MAINTAINERS: Add Chris Packham as FREESCALE MPC I2C maintainer Add Chris Packham as FREESCALE MPC I2C maintainer. Signed-off-by: Chris Packham Signed-off-by: Wolfram Sang --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 56e9e4d777d8..3bc77ba8cd05 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7135,6 +7135,13 @@ S: Maintained F: Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.yaml F: drivers/i2c/busses/i2c-imx-lpi2c.c +FREESCALE MPC I2C DRIVER +M: Chris Packham +L: linux-i2c@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/i2c/i2c-mpc.yaml +F: drivers/i2c/busses/i2c-mpc.c + FREESCALE QORIQ DPAA ETHERNET DRIVER M: Madalin Bucur L: netdev@vger.kernel.org -- cgit v1.2.3 From d5c1d60697a5f591bf717fd84d077585f0bff778 Mon Sep 17 00:00:00 2001 From: Bixuan Cui Date: Sat, 10 Apr 2021 11:50:44 +0800 Subject: i2c: sprd: Add missing MODULE_DEVICE_TABLE This patch adds missing MODULE_DEVICE_TABLE definition which generates correct modalias for automatic loading of this driver when it is built as an external module. Reported-by: Hulk Robot Signed-off-by: Bixuan Cui Reviewed-by: Baolin Wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sprd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c index 2917fecf6c80..9f77d1ddbaf8 100644 --- a/drivers/i2c/busses/i2c-sprd.c +++ b/drivers/i2c/busses/i2c-sprd.c @@ -640,6 +640,7 @@ static const struct of_device_id sprd_i2c_of_match[] = { { .compatible = "sprd,sc9860-i2c", }, {}, }; +MODULE_DEVICE_TABLE(of, sprd_i2c_of_match); static struct platform_driver sprd_i2c_driver = { .probe = sprd_i2c_probe, -- cgit v1.2.3 From 23ceb8462dc6f4b4decdb5536a7e5fc477cdf0b6 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 1 Dec 2020 17:31:17 +0800 Subject: i2c: cadence: fix reference leak when pm_runtime_get_sync fails The PM reference count is not expected to be incremented on return in functions cdns_i2c_master_xfer and cdns_reg_slave. However, pm_runtime_get_sync will increment pm usage counter even failed. Forgetting to putting operation will result in a reference leak here. Replace it with pm_runtime_resume_and_get to keep usage counter balanced. Fixes: 7fa32329ca03 ("i2c: cadence: Move to sensible power management") Reported-by: Hulk Robot Signed-off-by: Qinglang Miao Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-cadence.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index e4b7f2a951ad..e8eae8725900 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -789,7 +789,7 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, bool change_role = false; #endif - ret = pm_runtime_get_sync(id->dev); + ret = pm_runtime_resume_and_get(id->dev); if (ret < 0) return ret; @@ -911,7 +911,7 @@ static int cdns_reg_slave(struct i2c_client *slave) if (slave->flags & I2C_CLIENT_TEN) return -EAFNOSUPPORT; - ret = pm_runtime_get_sync(id->dev); + ret = pm_runtime_resume_and_get(id->dev); if (ret < 0) return ret; -- cgit v1.2.3 From 223125e37af8a641ea4a09747a6a52172fc4b903 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 1 Dec 2020 17:31:39 +0800 Subject: i2c: img-scb: fix reference leak when pm_runtime_get_sync fails The PM reference count is not expected to be incremented on return in functions img_i2c_xfer and img_i2c_init. However, pm_runtime_get_sync will increment the PM reference count even failed. Forgetting to putting operation will result in a reference leak here. Replace it with pm_runtime_resume_and_get to keep usage counter balanced. Fixes: 93222bd9b966 ("i2c: img-scb: Add runtime PM") Reported-by: Hulk Robot Signed-off-by: Qinglang Miao Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-img-scb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c index 98a89301ed2a..8e987945ed45 100644 --- a/drivers/i2c/busses/i2c-img-scb.c +++ b/drivers/i2c/busses/i2c-img-scb.c @@ -1057,7 +1057,7 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, atomic = true; } - ret = pm_runtime_get_sync(adap->dev.parent); + ret = pm_runtime_resume_and_get(adap->dev.parent); if (ret < 0) return ret; @@ -1158,7 +1158,7 @@ static int img_i2c_init(struct img_i2c *i2c) u32 rev; int ret; - ret = pm_runtime_get_sync(i2c->adap.dev.parent); + ret = pm_runtime_resume_and_get(i2c->adap.dev.parent); if (ret < 0) return ret; -- cgit v1.2.3 From 278e5bbdb9a94fa063c0f9bcde2479d0b8042462 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 1 Dec 2020 17:31:40 +0800 Subject: i2c: imx-lpi2c: fix reference leak when pm_runtime_get_sync fails The PM reference count is not expected to be incremented on return in lpi2c_imx_master_enable. However, pm_runtime_get_sync will increment the PM reference count even failed. Forgetting to putting operation will result in a reference leak here. Replace it with pm_runtime_resume_and_get to keep usage counter balanced. Fixes: 13d6eb20fc79 ("i2c: imx-lpi2c: add runtime pm support") Reported-by: Hulk Robot Signed-off-by: Qinglang Miao Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-imx-lpi2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index 9db6ccded5e9..8b9ba055c418 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -259,7 +259,7 @@ static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx) unsigned int temp; int ret; - ret = pm_runtime_get_sync(lpi2c_imx->adapter.dev.parent); + ret = pm_runtime_resume_and_get(lpi2c_imx->adapter.dev.parent); if (ret < 0) return ret; -- cgit v1.2.3 From 47ff617217ca6a13194fcb35c6c3a0c57c080693 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 1 Dec 2020 17:31:41 +0800 Subject: i2c: imx: fix reference leak when pm_runtime_get_sync fails In i2c_imx_xfer() and i2c_imx_remove(), the pm reference count is not expected to be incremented on return. However, pm_runtime_get_sync will increment pm reference count even failed. Forgetting to putting operation will result in a reference leak here. Replace it with pm_runtime_resume_and_get to keep usage counter balanced. Fixes: 3a5ee18d2a32 ("i2c: imx: implement master_xfer_atomic callback") Reported-by: Hulk Robot Signed-off-by: Qinglang Miao Reviewed-by: Oleksij Rempel Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-imx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index b80fdc1f0092..dc9c4b4cc25a 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1253,7 +1253,7 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter); int result; - result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent); + result = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent); if (result < 0) return result; @@ -1496,7 +1496,7 @@ static int i2c_imx_remove(struct platform_device *pdev) struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev); int irq, ret; - ret = pm_runtime_get_sync(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) return ret; -- cgit v1.2.3 From 780f629741257ed6c54bd3eb53b57f648eabf200 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 1 Dec 2020 17:31:43 +0800 Subject: i2c: omap: fix reference leak when pm_runtime_get_sync fails The PM reference count is not expected to be incremented on return in omap_i2c_probe() and omap_i2c_remove(). However, pm_runtime_get_sync will increment the PM reference count even failed. Forgetting to putting operation will result in a reference leak here. I Replace it with pm_runtime_resume_and_get to keep usage counter balanced. What's more, error path 'err_free_mem' seems not like a proper name any more. So I change the name to err_disable_pm and move pm_runtime_disable below, for pm_runtime of 'pdev->dev' should be disabled when pm_runtime_resume_and_get fails. Fixes: 3b0fb97c8dc4 ("I2C: OMAP: Handle error check for pm runtime") Reported-by: Hulk Robot Signed-off-by: Qinglang Miao Reviewed-by: Grygorii Strashko Reviewed-by: Vignesh Raghavendra Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-omap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 12ac4212aded..d4f6c6d60683 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -1404,9 +1404,9 @@ omap_i2c_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(omap->dev, OMAP_I2C_PM_TIMEOUT); pm_runtime_use_autosuspend(omap->dev); - r = pm_runtime_get_sync(omap->dev); + r = pm_runtime_resume_and_get(omap->dev); if (r < 0) - goto err_free_mem; + goto err_disable_pm; /* * Read the Rev hi bit-[15:14] ie scheme this is 1 indicates ver2. @@ -1513,8 +1513,8 @@ err_unuse_clocks: omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0); pm_runtime_dont_use_autosuspend(omap->dev); pm_runtime_put_sync(omap->dev); +err_disable_pm: pm_runtime_disable(&pdev->dev); -err_free_mem: return r; } @@ -1525,7 +1525,7 @@ static int omap_i2c_remove(struct platform_device *pdev) int ret; i2c_del_adapter(&omap->adapter); - ret = pm_runtime_get_sync(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) return ret; -- cgit v1.2.3 From 3a4f326463117cee3adcb72999ca34a9aaafda93 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 1 Dec 2020 17:31:44 +0800 Subject: i2c: sprd: fix reference leak when pm_runtime_get_sync fails The PM reference count is not expected to be incremented on return in sprd_i2c_master_xfer() and sprd_i2c_remove(). However, pm_runtime_get_sync will increment the PM reference count even failed. Forgetting to putting operation will result in a reference leak here. Replace it with pm_runtime_resume_and_get to keep usage counter balanced. Fixes: 8b9ec0719834 ("i2c: Add Spreadtrum I2C controller driver") Reported-by: Hulk Robot Signed-off-by: Qinglang Miao Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sprd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c index 9f77d1ddbaf8..4fe15cd78907 100644 --- a/drivers/i2c/busses/i2c-sprd.c +++ b/drivers/i2c/busses/i2c-sprd.c @@ -290,7 +290,7 @@ static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct sprd_i2c *i2c_dev = i2c_adap->algo_data; int im, ret; - ret = pm_runtime_get_sync(i2c_dev->dev); + ret = pm_runtime_resume_and_get(i2c_dev->dev); if (ret < 0) return ret; @@ -576,7 +576,7 @@ static int sprd_i2c_remove(struct platform_device *pdev) struct sprd_i2c *i2c_dev = platform_get_drvdata(pdev); int ret; - ret = pm_runtime_get_sync(i2c_dev->dev); + ret = pm_runtime_resume_and_get(i2c_dev->dev); if (ret < 0) return ret; -- cgit v1.2.3 From 2c662660ce2bd3b09dae21a9a9ac9395e1e6c00b Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 1 Dec 2020 17:31:45 +0800 Subject: i2c: stm32f7: fix reference leak when pm_runtime_get_sync fails The PM reference count is not expected to be incremented on return in these stm32f7_i2c_xx serious functions. However, pm_runtime_get_sync will increment the PM reference count even failed. Forgetting to putting operation will result in a reference leak here. Replace it with pm_runtime_resume_and_get to keep usage counter balanced. Fixes: ea6dd25deeb5 ("i2c: stm32f7: add PM_SLEEP suspend/resume support") Reported-by: Hulk Robot Signed-off-by: Qinglang Miao Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-stm32f7.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 7e06e6b79b21..0138317ea600 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -1660,7 +1660,7 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap, i2c_dev->msg_id = 0; f7_msg->smbus = false; - ret = pm_runtime_get_sync(i2c_dev->dev); + ret = pm_runtime_resume_and_get(i2c_dev->dev); if (ret < 0) return ret; @@ -1706,7 +1706,7 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, f7_msg->read_write = read_write; f7_msg->smbus = true; - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) return ret; @@ -1807,7 +1807,7 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) if (ret) return ret; - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) return ret; @@ -1888,7 +1888,7 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave) WARN_ON(!i2c_dev->slave[id]); - ret = pm_runtime_get_sync(i2c_dev->dev); + ret = pm_runtime_resume_and_get(i2c_dev->dev); if (ret < 0) return ret; @@ -2276,7 +2276,7 @@ static int __maybe_unused stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_de int ret; struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs; - ret = pm_runtime_get_sync(i2c_dev->dev); + ret = pm_runtime_resume_and_get(i2c_dev->dev); if (ret < 0) return ret; @@ -2298,7 +2298,7 @@ static int __maybe_unused stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_d int ret; struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs; - ret = pm_runtime_get_sync(i2c_dev->dev); + ret = pm_runtime_resume_and_get(i2c_dev->dev); if (ret < 0) return ret; -- cgit v1.2.3 From a85c5c7a3aa8041777ff691400b4046e56149fd3 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 1 Dec 2020 17:31:46 +0800 Subject: i2c: xiic: fix reference leak when pm_runtime_get_sync fails The PM reference count is not expected to be incremented on return in xiic_xfer and xiic_i2c_remove. However, pm_runtime_get_sync will increment the PM reference count even failed. Forgetting to putting operation will result in a reference leak here. Replace it with pm_runtime_resume_and_get to keep usage counter balanced. Fixes: 10b17004a74c ("i2c: xiic: Fix the clocking across bind unbind") Reported-by: Hulk Robot Signed-off-by: Qinglang Miao Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-xiic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index 087b2951942e..2a8568b97c14 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -706,7 +706,7 @@ static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) dev_dbg(adap->dev.parent, "%s entry SR: 0x%x\n", __func__, xiic_getreg8(i2c, XIIC_SR_REG_OFFSET)); - err = pm_runtime_get_sync(i2c->dev); + err = pm_runtime_resume_and_get(i2c->dev); if (err < 0) return err; @@ -873,7 +873,7 @@ static int xiic_i2c_remove(struct platform_device *pdev) /* remove adapter & data */ i2c_del_adapter(&i2c->adap); - ret = pm_runtime_get_sync(i2c->dev); + ret = pm_runtime_resume_and_get(i2c->dev); if (ret < 0) return ret; -- cgit v1.2.3 From c4b1fcc310e655fa8414696c38a84d36c00684c8 Mon Sep 17 00:00:00 2001 From: Ye Weihua Date: Thu, 8 Apr 2021 19:06:38 +0800 Subject: i2c: imx: Fix PM reference leak in i2c_imx_reg_slave() pm_runtime_get_sync() will increment the PM reference count even on failure. Forgetting to put the reference again will result in a leak. Replace it with pm_runtime_resume_and_get() to keep the usage counter balanced. Reported-by: Hulk Robot Signed-off-by: Ye Weihua Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index dc9c4b4cc25a..dc5ca71906db 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -801,7 +801,7 @@ static int i2c_imx_reg_slave(struct i2c_client *client) i2c_imx->last_slave_event = I2C_SLAVE_STOP; /* Resume */ - ret = pm_runtime_get_sync(i2c_imx->adapter.dev.parent); + ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent); if (ret < 0) { dev_err(&i2c_imx->adapter.dev, "failed to resume i2c controller"); return ret; -- cgit v1.2.3 From 5581c2c5d02bc63a0edb53e061c8e97cd490646e Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 10 Apr 2021 23:14:35 +0300 Subject: i2c: cadence: add IRQ check The driver neglects to check the result of platform_get_irq()'s call and blithely passes the negative error codes to devm_request_irq() (which takes *unsigned* IRQ #), causing it to fail with -EINVAL, overriding an original error code. Stop calling devm_request_irq() with invalid IRQ #s. Fixes: df8eb5691c48 ("i2c: Add driver for Cadence I2C controller") Signed-off-by: Sergey Shtylyov Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-cadence.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index e8eae8725900..c1bbc4caeb5c 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -1200,7 +1200,10 @@ static int cdns_i2c_probe(struct platform_device *pdev) if (IS_ERR(id->membase)) return PTR_ERR(id->membase); - id->irq = platform_get_irq(pdev, 0); + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + id->irq = ret; id->adap.owner = THIS_MODULE; id->adap.dev.of_node = pdev->dev.of_node; -- cgit v1.2.3 From bb6129c32867baa7988f7fd2066cf18ed662d240 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 10 Apr 2021 23:16:41 +0300 Subject: i2c: emev2: add IRQ check The driver neglects to check the result of platform_get_irq()'s call and blithely passes the negative error codes to devm_request_irq() (which takes *unsigned* IRQ #), causing it to fail with -EINVAL, overriding an original error code. Stop calling devm_request_irq() with invalid IRQ #s. Fixes: 5faf6e1f58b4 ("i2c: emev2: add driver") Signed-off-by: Sergey Shtylyov Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-emev2.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-emev2.c b/drivers/i2c/busses/i2c-emev2.c index a08554c1a570..bdff0e6345d9 100644 --- a/drivers/i2c/busses/i2c-emev2.c +++ b/drivers/i2c/busses/i2c-emev2.c @@ -395,7 +395,10 @@ static int em_i2c_probe(struct platform_device *pdev) em_i2c_reset(&priv->adap); - priv->irq = platform_get_irq(pdev, 0); + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto err_clk; + priv->irq = ret; ret = devm_request_irq(&pdev->dev, priv->irq, em_i2c_irq_handler, 0, "em_i2c", priv); if (ret) -- cgit v1.2.3 From c5e5f7a8d931fb4beba245bdbc94734175fda9de Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 10 Apr 2021 23:18:31 +0300 Subject: i2c: jz4780: add IRQ check The driver neglects to check the result of platform_get_irq()'s call and blithely passes the negative error codes to devm_request_irq() (which takes *unsigned* IRQ #), causing it to fail with -EINVAL, overriding an original error code. Stop calling devm_request_irq() with invalid IRQ #s. Fixes: ba92222ed63a ("i2c: jz4780: Add i2c bus controller driver for Ingenic JZ4780") Signed-off-by: Sergey Shtylyov Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-jz4780.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c index 8509c5f11356..331a4b567849 100644 --- a/drivers/i2c/busses/i2c-jz4780.c +++ b/drivers/i2c/busses/i2c-jz4780.c @@ -825,7 +825,10 @@ static int jz4780_i2c_probe(struct platform_device *pdev) jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, 0x0); - i2c->irq = platform_get_irq(pdev, 0); + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto err; + i2c->irq = ret; ret = devm_request_irq(&pdev->dev, i2c->irq, jz4780_i2c_irq, 0, dev_name(&pdev->dev), i2c); if (ret) -- cgit v1.2.3 From 0d3bf53e897dce943b98d975bbde77156af6cd81 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 10 Apr 2021 23:20:49 +0300 Subject: i2c: mlxbf: add IRQ check The driver neglects to check the result of platform_get_irq()'s call and blithely passes the negative error codes to devm_request_irq() (which takes *unsigned* IRQ #), causing it to fail with -EINVAL, overriding an original error code. Stop calling devm_request_irq() with invalid IRQ #s. Fixes: b5b5b32081cd ("i2c: mlxbf: I2C SMBus driver for Mellanox BlueField SoC") Signed-off-by: Sergey Shtylyov Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mlxbf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c index 80ab831df349..8716032f030a 100644 --- a/drivers/i2c/busses/i2c-mlxbf.c +++ b/drivers/i2c/busses/i2c-mlxbf.c @@ -2370,6 +2370,8 @@ static int mlxbf_i2c_probe(struct platform_device *pdev) mlxbf_i2c_init_slave(pdev, priv); irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; ret = devm_request_irq(dev, irq, mlxbf_smbus_irq, IRQF_ONESHOT | IRQF_SHARED | IRQF_PROBE_SHARED, dev_name(dev), priv); -- cgit v1.2.3 From 147178cf03a6dcb337e703d4dacd008683022a58 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 10 Apr 2021 23:23:33 +0300 Subject: i2c: rcar: add IRQ check The driver neglects to check the result of platform_get_irq()'s call and blithely passes the negative error codes to devm_request_irq() (which takes *unsigned* IRQ #), causing it to fail with -EINVAL, overriding an original error code. Stop calling devm_request_irq() with the invalid IRQ #s. Fixes: 6ccbe607132b ("i2c: add Renesas R-Car I2C driver") Signed-off-by: Sergey Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 1f0c164f671a..327c092a4130 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -1099,7 +1099,10 @@ static int rcar_i2c_probe(struct platform_device *pdev) if (of_property_read_bool(dev->of_node, "smbus")) priv->flags |= ID_P_HOST_NOTIFY; - priv->irq = platform_get_irq(pdev, 0); + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto out_pm_disable; + priv->irq = ret; ret = devm_request_irq(dev, priv->irq, irqhandler, irqflags, dev_name(dev), priv); if (ret < 0) { dev_err(dev, "cannot get irq %d\n", priv->irq); -- cgit v1.2.3 From e5b2e3e742015dd2aa6bc7bcef2cb59b2de1221c Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 10 Apr 2021 23:25:10 +0300 Subject: i2c: sh7760: add IRQ check The driver neglects to check the result of platform_get_irq()'s call and blithely passes the negative error codes to devm_request_irq() (which takes *unsigned* IRQ #), causing it to fail with -EINVAL, overriding an original error code. Stop calling devm_request_irq() with invalid IRQ #s. Fixes: a26c20b1fa6d ("i2c: Renesas SH7760 I2C master driver") Signed-off-by: Sergey Shtylyov Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh7760.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c index c2005c789d2b..c79c9f542c5a 100644 --- a/drivers/i2c/busses/i2c-sh7760.c +++ b/drivers/i2c/busses/i2c-sh7760.c @@ -471,7 +471,10 @@ static int sh7760_i2c_probe(struct platform_device *pdev) goto out2; } - id->irq = platform_get_irq(pdev, 0); + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + id->irq = ret; id->adap.nr = pdev->id; id->adap.algo = &sh7760_i2c_algo; -- cgit v1.2.3 From 1538d82f4647c686f09888b36604dca2fc2bdaf6 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Thu, 15 Apr 2021 10:33:20 +1200 Subject: i2c: mpc: Interrupt driven transfer The fsl-i2c controller will generate an interrupt after every byte transferred. Make use of this interrupt to drive a state machine which allows the next part of a transfer to happen as soon as the interrupt is received. This is particularly helpful with SMBUS devices like the LM81 which will timeout if we take too long between bytes in a transfer. Signed-off-by: Chris Packham Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mpc.c | 426 ++++++++++++++++++++++++------------------- 1 file changed, 239 insertions(+), 187 deletions(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 3c8bcdfff7e7..0a80fafbe6c6 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -58,11 +58,35 @@ #define CSR_MIF 0x02 #define CSR_RXAK 0x01 +enum mpc_i2c_action { + MPC_I2C_ACTION_START = 1, + MPC_I2C_ACTION_RESTART, + MPC_I2C_ACTION_READ_BEGIN, + MPC_I2C_ACTION_READ_BYTE, + MPC_I2C_ACTION_WRITE, + MPC_I2C_ACTION_STOP, + + __MPC_I2C_ACTION_CNT +}; + +static const char * const action_str[] = { + "invalid", + "start", + "restart", + "read begin", + "read", + "write", + "stop", +}; + +static_assert(ARRAY_SIZE(action_str) == __MPC_I2C_ACTION_CNT); + struct mpc_i2c { struct device *dev; void __iomem *base; u32 interrupt; - wait_queue_head_t queue; + wait_queue_head_t waitq; + spinlock_t lock; struct i2c_adapter adap; int irq; u32 real_clk; @@ -70,6 +94,16 @@ struct mpc_i2c { u8 fdr, dfsrr; #endif struct clk *clk_per; + u32 cntl_bits; + enum mpc_i2c_action action; + struct i2c_msg *msgs; + int num_msgs; + int curr_msg; + u32 byte_posn; + u32 block; + int rc; + int expect_rxack; + }; struct mpc_i2c_divider { @@ -86,19 +120,6 @@ static inline void writeccr(struct mpc_i2c *i2c, u32 x) writeb(x, i2c->base + MPC_I2C_CR); } -static irqreturn_t mpc_i2c_isr(int irq, void *dev_id) -{ - struct mpc_i2c *i2c = dev_id; - if (readb(i2c->base + MPC_I2C_SR) & CSR_MIF) { - /* Read again to allow register to stabilise */ - i2c->interrupt = readb(i2c->base + MPC_I2C_SR); - writeb(0, i2c->base + MPC_I2C_SR); - wake_up(&i2c->queue); - return IRQ_HANDLED; - } - return IRQ_NONE; -} - /* Sometimes 9th clock pulse isn't generated, and slave doesn't release * the bus, because it wants to send ACK. * Following sequence of enabling/disabling and sending start/stop generates @@ -121,45 +142,6 @@ static void mpc_i2c_fixup(struct mpc_i2c *i2c) } } -static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing) -{ - u32 cmd_err; - int result; - - result = wait_event_timeout(i2c->queue, - (i2c->interrupt & CSR_MIF), timeout); - - if (unlikely(!(i2c->interrupt & CSR_MIF))) { - dev_dbg(i2c->dev, "wait timeout\n"); - writeccr(i2c, 0); - result = -ETIMEDOUT; - } - - cmd_err = i2c->interrupt; - i2c->interrupt = 0; - - if (result < 0) - return result; - - if (!(cmd_err & CSR_MCF)) { - dev_dbg(i2c->dev, "unfinished\n"); - return -EIO; - } - - if (cmd_err & CSR_MAL) { - dev_dbg(i2c->dev, "MAL\n"); - return -EAGAIN; - } - - if (writing && (cmd_err & CSR_RXAK)) { - dev_dbg(i2c->dev, "No RXAK\n"); - /* generate stop */ - writeccr(i2c, CCR_MEN); - return -ENXIO; - } - return 0; -} - #if defined(CONFIG_PPC_MPC52xx) || defined(CONFIG_PPC_MPC512x) static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = { {20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23}, @@ -434,168 +416,209 @@ static void mpc_i2c_setup_8xxx(struct device_node *node, } #endif /* CONFIG_FSL_SOC */ -static void mpc_i2c_start(struct mpc_i2c *i2c) +static void mpc_i2c_finish(struct mpc_i2c *i2c, int rc) { - /* Clear arbitration */ - writeb(0, i2c->base + MPC_I2C_SR); - /* Start with MEN */ - writeccr(i2c, CCR_MEN); + i2c->rc = rc; + i2c->block = 0; + i2c->cntl_bits = CCR_MEN; + writeccr(i2c, i2c->cntl_bits); + wake_up(&i2c->waitq); } -static void mpc_i2c_stop(struct mpc_i2c *i2c) +static void mpc_i2c_do_action(struct mpc_i2c *i2c) { - writeccr(i2c, CCR_MEN); -} + struct i2c_msg *msg = &i2c->msgs[i2c->curr_msg]; + int dir = 0; + int recv_len = 0; + u8 byte; + + dev_dbg(i2c->dev, "action = %s\n", action_str[i2c->action]); + + i2c->cntl_bits &= ~(CCR_RSTA | CCR_MTX | CCR_TXAK); + + if (msg->flags & I2C_M_RD) + dir = 1; + if (msg->flags & I2C_M_RECV_LEN) + recv_len = 1; + + switch (i2c->action) { + case MPC_I2C_ACTION_RESTART: + i2c->cntl_bits |= CCR_RSTA; + fallthrough; + + case MPC_I2C_ACTION_START: + i2c->cntl_bits |= CCR_MSTA | CCR_MTX; + writeccr(i2c, i2c->cntl_bits); + writeb((msg->addr << 1) | dir, i2c->base + MPC_I2C_DR); + i2c->expect_rxack = 1; + i2c->action = dir ? MPC_I2C_ACTION_READ_BEGIN : MPC_I2C_ACTION_WRITE; + break; + + case MPC_I2C_ACTION_READ_BEGIN: + if (msg->len) { + if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN)) + i2c->cntl_bits |= CCR_TXAK; + + writeccr(i2c, i2c->cntl_bits); + /* Dummy read */ + readb(i2c->base + MPC_I2C_DR); + } + i2c->action = MPC_I2C_ACTION_READ_BYTE; + break; + + case MPC_I2C_ACTION_READ_BYTE: + if (i2c->byte_posn || !recv_len) { + /* Generate Tx ACK on next to last byte */ + if (i2c->byte_posn == msg->len - 2) + i2c->cntl_bits |= CCR_TXAK; + /* Do not generate stop on last byte */ + if (i2c->byte_posn == msg->len - 1) + i2c->cntl_bits |= CCR_MTX; -static int mpc_write(struct mpc_i2c *i2c, int target, - const u8 *data, int length, int restart) -{ - int i, result; - unsigned timeout = i2c->adap.timeout; - u32 flags = restart ? CCR_RSTA : 0; + writeccr(i2c, i2c->cntl_bits); + } - /* Start as master */ - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags); - /* Write target byte */ - writeb((target << 1), i2c->base + MPC_I2C_DR); + byte = readb(i2c->base + MPC_I2C_DR); - result = i2c_wait(i2c, timeout, 1); - if (result < 0) - return result; + if (i2c->byte_posn == 0 && recv_len) { + if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX) { + mpc_i2c_finish(i2c, -EPROTO); + return; + } + msg->len += byte; + /* + * For block reads, generate Tx ACK here if data length + * is 1 byte (total length is 2 bytes). + */ + if (msg->len == 2) { + i2c->cntl_bits |= CCR_TXAK; + writeccr(i2c, i2c->cntl_bits); + } + } + + dev_dbg(i2c->dev, "%s %02x\n", action_str[i2c->action], byte); + msg->buf[i2c->byte_posn++] = byte; + break; - for (i = 0; i < length; i++) { - /* Write data byte */ - writeb(data[i], i2c->base + MPC_I2C_DR); + case MPC_I2C_ACTION_WRITE: + dev_dbg(i2c->dev, "%s %02x\n", action_str[i2c->action], + msg->buf[i2c->byte_posn]); + writeb(msg->buf[i2c->byte_posn++], i2c->base + MPC_I2C_DR); + i2c->expect_rxack = 1; + break; - result = i2c_wait(i2c, timeout, 1); - if (result < 0) - return result; + case MPC_I2C_ACTION_STOP: + mpc_i2c_finish(i2c, 0); + break; + + default: + WARN(1, "Unexpected action %d\n", i2c->action); + break; } - return 0; + if (msg->len == i2c->byte_posn) { + i2c->curr_msg++; + i2c->byte_posn = 0; + + if (i2c->curr_msg == i2c->num_msgs) { + i2c->action = MPC_I2C_ACTION_STOP; + /* + * We don't get another interrupt on read so + * finish the transfer now + */ + if (dir) + mpc_i2c_finish(i2c, 0); + } else { + i2c->action = MPC_I2C_ACTION_RESTART; + } + } } -static int mpc_read(struct mpc_i2c *i2c, int target, - u8 *data, int length, int restart, bool recv_len) +static void mpc_i2c_do_intr(struct mpc_i2c *i2c, u8 status) { - unsigned timeout = i2c->adap.timeout; - int i, result; - u32 flags = restart ? CCR_RSTA : 0; + spin_lock(&i2c->lock); - /* Switch to read - restart */ - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags); - /* Write target address byte - this time with the read flag set */ - writeb((target << 1) | 1, i2c->base + MPC_I2C_DR); + if (!(status & CSR_MCF)) { + dev_dbg(i2c->dev, "unfinished\n"); + mpc_i2c_finish(i2c, -EIO); + goto out; + } - result = i2c_wait(i2c, timeout, 1); - if (result < 0) - return result; + if (status & CSR_MAL) { + dev_dbg(i2c->dev, "arbitration lost\n"); + mpc_i2c_finish(i2c, -EAGAIN); + goto out; + } - if (length) { - if (length == 1 && !recv_len) - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); - else - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA); - /* Dummy read */ - readb(i2c->base + MPC_I2C_DR); + if (i2c->expect_rxack && (status & CSR_RXAK)) { + dev_dbg(i2c->dev, "no Rx ACK\n"); + mpc_i2c_finish(i2c, -ENXIO); + goto out; } + i2c->expect_rxack = 0; - for (i = 0; i < length; i++) { - u8 byte; - - result = i2c_wait(i2c, timeout, 0); - if (result < 0) - return result; - - /* - * For block reads, we have to know the total length (1st byte) - * before we can determine if we are done. - */ - if (i || !recv_len) { - /* Generate txack on next to last byte */ - if (i == length - 2) - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA - | CCR_TXAK); - /* Do not generate stop on last byte */ - if (i == length - 1) - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA - | CCR_MTX); - } + mpc_i2c_do_action(i2c); - byte = readb(i2c->base + MPC_I2C_DR); +out: + spin_unlock(&i2c->lock); +} - /* - * Adjust length if first received byte is length. - * The length is 1 length byte plus actually data length - */ - if (i == 0 && recv_len) { - if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX) - return -EPROTO; - length += byte; - /* - * For block reads, generate txack here if data length - * is 1 byte (total length is 2 bytes). - */ - if (length == 2) - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA - | CCR_TXAK); - } - data[i] = byte; +static irqreturn_t mpc_i2c_isr(int irq, void *dev_id) +{ + struct mpc_i2c *i2c = dev_id; + u8 status; + + status = readb(i2c->base + MPC_I2C_SR); + if (status & CSR_MIF) { + writeb(0, i2c->base + MPC_I2C_SR); + mpc_i2c_do_intr(i2c, status); + return IRQ_HANDLED; } + return IRQ_NONE; +} - return length; +static int mpc_i2c_wait_for_completion(struct mpc_i2c *i2c) +{ + long time_left; + + time_left = wait_event_timeout(i2c->waitq, !i2c->block, i2c->adap.timeout); + if (!time_left) + return -ETIMEDOUT; + if (time_left < 0) + return time_left; + + return 0; } -static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +static int mpc_i2c_execute_msg(struct mpc_i2c *i2c) { - struct i2c_msg *pmsg; - int i; - int ret = 0; - unsigned long orig_jiffies = jiffies; - struct mpc_i2c *i2c = i2c_get_adapdata(adap); + unsigned long orig_jiffies; + unsigned long flags; + int ret; - mpc_i2c_start(i2c); + spin_lock_irqsave(&i2c->lock, flags); - /* Allow bus up to 1s to become not busy */ - while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) { - if (signal_pending(current)) { - dev_dbg(i2c->dev, "Interrupted\n"); - writeccr(i2c, 0); - return -EINTR; - } - if (time_after(jiffies, orig_jiffies + HZ)) { - u8 status = readb(i2c->base + MPC_I2C_SR); + i2c->curr_msg = 0; + i2c->rc = 0; + i2c->byte_posn = 0; + i2c->block = 1; + i2c->action = MPC_I2C_ACTION_START; - dev_dbg(i2c->dev, "timeout\n"); - if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) { - writeb(status & ~CSR_MAL, - i2c->base + MPC_I2C_SR); - i2c_recover_bus(&i2c->adap); - } - return -EIO; - } - schedule(); - } + i2c->cntl_bits = CCR_MEN | CCR_MIEN; + writeb(0, i2c->base + MPC_I2C_SR); + writeccr(i2c, i2c->cntl_bits); + + mpc_i2c_do_action(i2c); + + spin_unlock_irqrestore(&i2c->lock, flags); + + ret = mpc_i2c_wait_for_completion(i2c); + if (ret) + i2c->rc = ret; + + if (i2c->rc == -EIO || i2c->rc == -EAGAIN || i2c->rc == -ETIMEDOUT) + i2c_recover_bus(&i2c->adap); - for (i = 0; ret >= 0 && i < num; i++) { - pmsg = &msgs[i]; - dev_dbg(i2c->dev, - "Doing %s %d bytes to 0x%02x - %d of %d messages\n", - pmsg->flags & I2C_M_RD ? "read" : "write", - pmsg->len, pmsg->addr, i + 1, num); - if (pmsg->flags & I2C_M_RD) { - bool recv_len = pmsg->flags & I2C_M_RECV_LEN; - - ret = mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i, - recv_len); - if (recv_len && ret > 0) - pmsg->len = ret; - } else { - ret = - mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i); - } - } - mpc_i2c_stop(i2c); /* Initiate STOP */ orig_jiffies = jiffies; /* Wait until STOP is seen, allow up to 1 s */ while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) { @@ -612,7 +635,35 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) } cond_resched(); } - return (ret < 0) ? ret : num; + + return i2c->rc; +} + +static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + int rc, ret = num; + struct mpc_i2c *i2c = i2c_get_adapdata(adap); + int i; + + dev_dbg(i2c->dev, "num = %d\n", num); + for (i = 0; i < num; i++) + dev_dbg(i2c->dev, " addr = %02x, flags = %02x, len = %d, %*ph\n", + msgs[i].addr, msgs[i].flags, msgs[i].len, + msgs[i].flags & I2C_M_RD ? 0 : msgs[i].len, + msgs[i].buf); + + WARN_ON(i2c->msgs != NULL); + i2c->msgs = msgs; + i2c->num_msgs = num; + + rc = mpc_i2c_execute_msg(i2c); + if (rc < 0) + ret = rc; + + i2c->num_msgs = 0; + i2c->msgs = NULL; + + return ret; } static u32 mpc_functionality(struct i2c_adapter *adap) @@ -667,7 +718,8 @@ static int fsl_i2c_probe(struct platform_device *op) i2c->dev = &op->dev; /* for debug and error output */ - init_waitqueue_head(&i2c->queue); + init_waitqueue_head(&i2c->waitq); + spin_lock_init(&i2c->lock); i2c->base = devm_platform_ioremap_resource(op, 0); if (IS_ERR(i2c->base)) { -- cgit v1.2.3 From 97b4dff130f5451bcfb7e677f16366c205b51d0f Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Thu, 15 Apr 2021 10:33:21 +1200 Subject: i2c: mpc: Update license and copyright Use SPDX-License-Identifier and add copyright for Allied Telesis because of the reasonably large rewrite in the preceding patch. Signed-off-by: Chris Packham Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mpc.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 0a80fafbe6c6..37244138875a 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -1,16 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * (C) Copyright 2003-2004 - * Humboldt Solutions Ltd, adrian@humboldt.co.uk. - * This is a combined i2c adapter and algorithm driver for the * MPC107/Tsi107 PowerPC northbridge and processors that include * the same I2C unit (8240, 8245, 85xx). * - * Release 0.8 - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. + * Copyright (C) 2003-2004 Humboldt Solutions Ltd, adrian@humboldt.co.uk + * Copyright (C) 2021 Allied Telesis Labs */ #include -- cgit v1.2.3 From c9598d04e738e289a2d95721b2f8c63ba0d977d2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 15 Apr 2021 10:33:22 +1200 Subject: i2c: mpc: Use devm_clk_get_optional() The peripheral clock is optional and we may get an -EPROBE_DEFER error code which would not be propagated correctly, fix this by using devm_clk_get_optional(). Signed-off-by: Andy Shevchenko Reviewed-by: Chris Packham Signed-off-by: Chris Packham Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mpc.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 37244138875a..d2209c04f67a 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -737,17 +737,18 @@ static int fsl_i2c_probe(struct platform_device *op) * enable clock for the I2C peripheral (non fatal), * keep a reference upon successful allocation */ - clk = devm_clk_get(&op->dev, NULL); - if (!IS_ERR(clk)) { - err = clk_prepare_enable(clk); - if (err) { - dev_err(&op->dev, "failed to enable clock\n"); - return err; - } else { - i2c->clk_per = clk; - } + clk = devm_clk_get_optional(&op->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + err = clk_prepare_enable(clk); + if (err) { + dev_err(&op->dev, "failed to enable clock\n"); + return err; } + i2c->clk_per = clk; + if (of_property_read_bool(op->dev.of_node, "fsl,preserve-clocking")) { clock = MPC_I2C_CLOCK_PRESERVE; } else { @@ -791,8 +792,7 @@ static int fsl_i2c_probe(struct platform_device *op) return 0; fail_add: - if (i2c->clk_per) - clk_disable_unprepare(i2c->clk_per); + clk_disable_unprepare(i2c->clk_per); return result; }; @@ -803,8 +803,7 @@ static int fsl_i2c_remove(struct platform_device *op) i2c_del_adapter(&i2c->adap); - if (i2c->clk_per) - clk_disable_unprepare(i2c->clk_per); + clk_disable_unprepare(i2c->clk_per); return 0; }; -- cgit v1.2.3 From 66679e9b8362163a05348e73d8ddae6da7a572c5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 15 Apr 2021 10:33:23 +1200 Subject: i2c: mpc: Remove CONFIG_PM_SLEEP ifdeffery Use __maybe_unused for the suspend()/resume() hooks and get rid of the CONFIG_PM_SLEEP ifdeffery to improve the code. Signed-off-by: Andy Shevchenko Reviewed-by: Chris Packham Signed-off-by: Chris Packham Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mpc.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index d2209c04f67a..0865f7ac80bd 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -85,9 +85,7 @@ struct mpc_i2c { struct i2c_adapter adap; int irq; u32 real_clk; -#ifdef CONFIG_PM_SLEEP u8 fdr, dfsrr; -#endif struct clk *clk_per; u32 cntl_bits; enum mpc_i2c_action action; @@ -808,8 +806,7 @@ static int fsl_i2c_remove(struct platform_device *op) return 0; }; -#ifdef CONFIG_PM_SLEEP -static int mpc_i2c_suspend(struct device *dev) +static int __maybe_unused mpc_i2c_suspend(struct device *dev) { struct mpc_i2c *i2c = dev_get_drvdata(dev); @@ -819,7 +816,7 @@ static int mpc_i2c_suspend(struct device *dev) return 0; } -static int mpc_i2c_resume(struct device *dev) +static int __maybe_unused mpc_i2c_resume(struct device *dev) { struct mpc_i2c *i2c = dev_get_drvdata(dev); @@ -828,12 +825,7 @@ static int mpc_i2c_resume(struct device *dev) return 0; } - static SIMPLE_DEV_PM_OPS(mpc_i2c_pm_ops, mpc_i2c_suspend, mpc_i2c_resume); -#define MPC_I2C_PM_OPS (&mpc_i2c_pm_ops) -#else -#define MPC_I2C_PM_OPS NULL -#endif static const struct mpc_i2c_data mpc_i2c_data_512x = { .setup = mpc_i2c_setup_512x, @@ -876,7 +868,7 @@ static struct platform_driver mpc_i2c_driver = { .driver = { .name = DRV_NAME, .of_match_table = mpc_i2c_of_match, - .pm = MPC_I2C_PM_OPS, + .pm = &mpc_i2c_pm_ops, }, }; -- cgit v1.2.3 From 30a153815c690e7a7d98834fb17ff25166cd9b39 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 15 Apr 2021 10:33:24 +1200 Subject: i2c: mpc: Use device_get_match_data() helper Use the device_get_match_data() helper instead of open coding. Signed-off-by: Andy Shevchenko Reviewed-by: Chris Packham Signed-off-by: Chris Packham Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mpc.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 0865f7ac80bd..7a9abeeb6da0 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -689,10 +690,9 @@ static struct i2c_bus_recovery_info fsl_i2c_recovery_info = { .recover_bus = fsl_i2c_bus_recovery, }; -static const struct of_device_id mpc_i2c_of_match[]; static int fsl_i2c_probe(struct platform_device *op) { - const struct of_device_id *match; + const struct mpc_i2c_data *data; struct mpc_i2c *i2c; const u32 *prop; u32 clock = MPC_I2C_CLOCK_LEGACY; @@ -701,10 +701,6 @@ static int fsl_i2c_probe(struct platform_device *op) struct clk *clk; int err; - match = of_match_device(mpc_i2c_of_match, &op->dev); - if (!match) - return -EINVAL; - i2c = devm_kzalloc(&op->dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) return -ENOMEM; @@ -756,8 +752,8 @@ static int fsl_i2c_probe(struct platform_device *op) clock = *prop; } - if (match->data) { - const struct mpc_i2c_data *data = match->data; + data = device_get_match_data(&op->dev); + if (data) { data->setup(op->dev.of_node, i2c, clock); } else { /* Backwards compatibility */ -- cgit v1.2.3 From 30b9cb274da2d2c6d9dffe4372d7311a8cb80a64 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 15 Apr 2021 10:33:25 +1200 Subject: i2c: mpc: Drop duplicate message from devm_platform_ioremap_resource() devm_platform_ioremap_resource() prints a message in case of error. Drop custom one. Signed-off-by: Andy Shevchenko Reviewed-by: Chris Packham Signed-off-by: Chris Packham Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mpc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 7a9abeeb6da0..30d9e89a3db2 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -711,10 +711,8 @@ static int fsl_i2c_probe(struct platform_device *op) spin_lock_init(&i2c->lock); i2c->base = devm_platform_ioremap_resource(op, 0); - if (IS_ERR(i2c->base)) { - dev_err(i2c->dev, "failed to map controller\n"); + if (IS_ERR(i2c->base)) return PTR_ERR(i2c->base); - } i2c->irq = platform_get_irq(op, 0); if (i2c->irq < 0) -- cgit v1.2.3 From 5e77a61f506b1ac8d86355edd52ebeb038179339 Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Mon, 5 Apr 2021 17:03:48 +0800 Subject: i2c: amd8111: Fix coding style issues Fix the following checkpatch errors: ERROR: "foo * bar" should be "foo *bar" #189: FILE: drivers/i2c/busses/i2c-amd8111.c:189: ERROR: "foo * bar" should be "foo *bar" #191: FILE: drivers/i2c/busses/i2c-amd8111.c:191: ERROR: switch and case should be at the same indent #201: FILE: drivers/i2c/busses/i2c-amd8111.c:201: ERROR: switch and case should be at the same indent #359: FILE: drivers/i2c/busses/i2c-amd8111.c:359: No functional changes. Signed-off-by: Tian Tao Signed-off-by: Zihao Tang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-amd8111.c | 268 +++++++++++++++++++-------------------- 1 file changed, 134 insertions(+), 134 deletions(-) diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index 34862ad3423e..1ed7e945bb6d 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -186,9 +186,9 @@ static int amd_ec_write(struct amd_smbus *smbus, unsigned char address, #define AMD_SMB_PRTCL_PEC 0x80 -static s32 amd8111_access(struct i2c_adapter * adap, u16 addr, +static s32 amd8111_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, - union i2c_smbus_data * data) + union i2c_smbus_data *data) { struct amd_smbus *smbus = adap->algo_data; unsigned char protocol, len, pec, temp[2]; @@ -199,130 +199,130 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr, pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0; switch (size) { - case I2C_SMBUS_QUICK: - protocol |= AMD_SMB_PRTCL_QUICK; - read_write = I2C_SMBUS_WRITE; - break; - - case I2C_SMBUS_BYTE: - if (read_write == I2C_SMBUS_WRITE) { - status = amd_ec_write(smbus, AMD_SMB_CMD, - command); - if (status) - return status; - } - protocol |= AMD_SMB_PRTCL_BYTE; - break; - - case I2C_SMBUS_BYTE_DATA: - status = amd_ec_write(smbus, AMD_SMB_CMD, command); + case I2C_SMBUS_QUICK: + protocol |= AMD_SMB_PRTCL_QUICK; + read_write = I2C_SMBUS_WRITE; + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) { + status = amd_ec_write(smbus, AMD_SMB_CMD, + command); if (status) return status; - if (read_write == I2C_SMBUS_WRITE) { - status = amd_ec_write(smbus, AMD_SMB_DATA, - data->byte); - if (status) - return status; - } - protocol |= AMD_SMB_PRTCL_BYTE_DATA; - break; + } + protocol |= AMD_SMB_PRTCL_BYTE; + break; - case I2C_SMBUS_WORD_DATA: - status = amd_ec_write(smbus, AMD_SMB_CMD, command); + case I2C_SMBUS_BYTE_DATA: + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + if (read_write == I2C_SMBUS_WRITE) { + status = amd_ec_write(smbus, AMD_SMB_DATA, + data->byte); if (status) return status; - if (read_write == I2C_SMBUS_WRITE) { - status = amd_ec_write(smbus, AMD_SMB_DATA, - data->word & 0xff); - if (status) - return status; - status = amd_ec_write(smbus, AMD_SMB_DATA + 1, - data->word >> 8); - if (status) - return status; - } - protocol |= AMD_SMB_PRTCL_WORD_DATA | pec; - break; + } + protocol |= AMD_SMB_PRTCL_BYTE_DATA; + break; - case I2C_SMBUS_BLOCK_DATA: - status = amd_ec_write(smbus, AMD_SMB_CMD, command); - if (status) - return status; - if (read_write == I2C_SMBUS_WRITE) { - len = min_t(u8, data->block[0], - I2C_SMBUS_BLOCK_MAX); - status = amd_ec_write(smbus, AMD_SMB_BCNT, len); - if (status) - return status; - for (i = 0; i < len; i++) { - status = - amd_ec_write(smbus, AMD_SMB_DATA + i, - data->block[i + 1]); - if (status) - return status; - } - } - protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec; - break; - - case I2C_SMBUS_I2C_BLOCK_DATA: - len = min_t(u8, data->block[0], - I2C_SMBUS_BLOCK_MAX); - status = amd_ec_write(smbus, AMD_SMB_CMD, command); - if (status) - return status; - status = amd_ec_write(smbus, AMD_SMB_BCNT, len); - if (status) - return status; - if (read_write == I2C_SMBUS_WRITE) - for (i = 0; i < len; i++) { - status = - amd_ec_write(smbus, AMD_SMB_DATA + i, - data->block[i + 1]); - if (status) - return status; - } - protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA; - break; - - case I2C_SMBUS_PROC_CALL: - status = amd_ec_write(smbus, AMD_SMB_CMD, command); - if (status) - return status; + case I2C_SMBUS_WORD_DATA: + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + if (read_write == I2C_SMBUS_WRITE) { status = amd_ec_write(smbus, AMD_SMB_DATA, - data->word & 0xff); + data->word & 0xff); if (status) return status; status = amd_ec_write(smbus, AMD_SMB_DATA + 1, - data->word >> 8); + data->word >> 8); if (status) return status; - protocol = AMD_SMB_PRTCL_PROC_CALL | pec; - read_write = I2C_SMBUS_READ; - break; + } + protocol |= AMD_SMB_PRTCL_WORD_DATA | pec; + break; - case I2C_SMBUS_BLOCK_PROC_CALL: + case I2C_SMBUS_BLOCK_DATA: + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + if (read_write == I2C_SMBUS_WRITE) { len = min_t(u8, data->block[0], - I2C_SMBUS_BLOCK_MAX - 1); - status = amd_ec_write(smbus, AMD_SMB_CMD, command); - if (status) - return status; + I2C_SMBUS_BLOCK_MAX); status = amd_ec_write(smbus, AMD_SMB_BCNT, len); if (status) return status; for (i = 0; i < len; i++) { - status = amd_ec_write(smbus, AMD_SMB_DATA + i, - data->block[i + 1]); + status = + amd_ec_write(smbus, AMD_SMB_DATA + i, + data->block[i + 1]); if (status) return status; } - protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec; - read_write = I2C_SMBUS_READ; - break; + } + protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + len = min_t(u8, data->block[0], + I2C_SMBUS_BLOCK_MAX); + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_BCNT, len); + if (status) + return status; + if (read_write == I2C_SMBUS_WRITE) + for (i = 0; i < len; i++) { + status = + amd_ec_write(smbus, AMD_SMB_DATA + i, + data->block[i + 1]); + if (status) + return status; + } + protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA; + break; - default: - dev_warn(&adap->dev, "Unsupported transaction %d\n", size); - return -EOPNOTSUPP; + case I2C_SMBUS_PROC_CALL: + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_DATA, + data->word & 0xff); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_DATA + 1, + data->word >> 8); + if (status) + return status; + protocol = AMD_SMB_PRTCL_PROC_CALL | pec; + read_write = I2C_SMBUS_READ; + break; + + case I2C_SMBUS_BLOCK_PROC_CALL: + len = min_t(u8, data->block[0], + I2C_SMBUS_BLOCK_MAX - 1); + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_BCNT, len); + if (status) + return status; + for (i = 0; i < len; i++) { + status = amd_ec_write(smbus, AMD_SMB_DATA + i, + data->block[i + 1]); + if (status) + return status; + } + protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec; + read_write = I2C_SMBUS_READ; + break; + + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; } status = amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1); @@ -357,40 +357,40 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr, return 0; switch (size) { - case I2C_SMBUS_BYTE: - case I2C_SMBUS_BYTE_DATA: - status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte); - if (status) - return status; - break; + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte); + if (status) + return status; + break; - case I2C_SMBUS_WORD_DATA: - case I2C_SMBUS_PROC_CALL: - status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0); - if (status) - return status; - status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1); - if (status) - return status; - data->word = (temp[1] << 8) | temp[0]; - break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0); + if (status) + return status; + status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1); + if (status) + return status; + data->word = (temp[1] << 8) | temp[0]; + break; - case I2C_SMBUS_BLOCK_DATA: - case I2C_SMBUS_BLOCK_PROC_CALL: - status = amd_ec_read(smbus, AMD_SMB_BCNT, &len); + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + status = amd_ec_read(smbus, AMD_SMB_BCNT, &len); + if (status) + return status; + len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX); + fallthrough; + case I2C_SMBUS_I2C_BLOCK_DATA: + for (i = 0; i < len; i++) { + status = amd_ec_read(smbus, AMD_SMB_DATA + i, + data->block + i + 1); if (status) return status; - len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX); - fallthrough; - case I2C_SMBUS_I2C_BLOCK_DATA: - for (i = 0; i < len; i++) { - status = amd_ec_read(smbus, AMD_SMB_DATA + i, - data->block + i + 1); - if (status) - return status; - } - data->block[0] = len; - break; + } + data->block[0] = len; + break; } return 0; -- cgit v1.2.3 From 87c2de5fa6f1cf59dac90f53a5a482a281c16da0 Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Mon, 5 Apr 2021 17:03:49 +0800 Subject: i2c: iop3xx: Fix coding style issues Fix 20 checkpatch errors. Among these errors, 18 of them are incorrect space usage, such as: ERROR: space prohibited after that open parenthesis '(' #128: FILE: drivers/i2c/busses/i2c-iop3xx.c:128: + if ( !rc ) rc = -I2C_ERR_BERR The remaining two errors are trailing statements should be on next line, such as: ERROR: trailing statements should be on next line #128: FILE: drivers/i2c/busses/i2c-iop3xx.c:128: + if ( !rc ) rc = -I2C_ERR_BERR; No functional changes. Signed-off-by: Tian Tao Signed-off-by: Zihao Tang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-iop3xx.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index 2f8b8050a223..cfecaf18ccbb 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -125,10 +125,12 @@ iop3xx_i2c_error(u32 sr) int rc = 0; if ((sr & IOP3XX_ISR_BERRD)) { - if ( !rc ) rc = -I2C_ERR_BERR; + if (!rc) + rc = -I2C_ERR_BERR; } if ((sr & IOP3XX_ISR_ALD)) { - if ( !rc ) rc = -I2C_ERR_ALD; + if (!rc) + rc = -I2C_ERR_ALD; } return rc; } @@ -151,12 +153,12 @@ iop3xx_i2c_get_srstat(struct i2c_algo_iop3xx_data *iop3xx_adap) * sleep until interrupted, then recover and analyse the SR * saved by handler */ -typedef int (* compare_func)(unsigned test, unsigned mask); +typedef int (*compare_func)(unsigned test, unsigned mask); /* returns 1 on correct comparison */ static int iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap, - unsigned flags, unsigned* status, + unsigned flags, unsigned *status, compare_func compare) { unsigned sr = 0; @@ -167,7 +169,7 @@ iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap, do { interrupted = wait_event_interruptible_timeout ( iop3xx_adap->waitq, - (done = compare( sr = iop3xx_i2c_get_srstat(iop3xx_adap) ,flags )), + (done = compare(sr = iop3xx_i2c_get_srstat(iop3xx_adap), flags)), 1 * HZ ); if ((rc = iop3xx_i2c_error(sr)) < 0) { @@ -177,7 +179,7 @@ iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap, *status = sr; return -ETIMEDOUT; } - } while(!done); + } while (!done); *status = sr; @@ -204,7 +206,7 @@ iop3xx_i2c_wait_tx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) { return iop3xx_i2c_wait_event( iop3xx_adap, - IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD, + IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD, status, any_bits_set); } @@ -226,7 +228,7 @@ iop3xx_i2c_wait_idle(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) static int iop3xx_i2c_send_target_addr(struct i2c_algo_iop3xx_data *iop3xx_adap, - struct i2c_msg* msg) + struct i2c_msg *msg) { unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); int status; @@ -273,7 +275,7 @@ iop3xx_i2c_write_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char byte, } static int -iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char* byte, +iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char *byte, int stop) { unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); @@ -305,7 +307,7 @@ iop3xx_i2c_writebytes(struct i2c_adapter *i2c_adap, const char *buf, int count) int rc = 0; for (ii = 0; rc == 0 && ii != count; ++ii) - rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii==count-1); + rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii == count-1); return rc; } @@ -317,7 +319,7 @@ iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count) int rc = 0; for (ii = 0; rc == 0 && ii != count; ++ii) - rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii==count-1); + rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii == count-1); return rc; } @@ -330,7 +332,7 @@ iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count) * condition. */ static int -iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg* pmsg) +iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg *pmsg) { struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; int rc; @@ -369,7 +371,7 @@ iop3xx_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, iop3xx_i2c_transaction_cleanup(iop3xx_adap); - if(ret) + if (ret) return ret; return im; -- cgit v1.2.3 From 28fb89ff97b06cdf4229ce5705f3ead3bb010ba0 Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Mon, 5 Apr 2021 17:03:50 +0800 Subject: i2c: nomadik: Fix space errors Fix the following checkpatch errors: ERROR: space prohibited before that ',' (ctx:WxW) #280: FILE: drivers/i2c/busses/i2c-nomadik.c:280: + i2c_clr_bit(dev->virtbase + I2C_CR , I2C_CR_PE); ^ ERROR: space prohibited before that ',' (ctx:WxW) #528: FILE: drivers/i2c/busses/i2c-nomadik.c:528: + i2c_set_bit(dev->virtbase + I2C_CR , I2C_CR_PE); ^ No functional changes. Signed-off-by: Tian Tao Signed-off-by: Zihao Tang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-nomadik.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index a3363b20f168..dc77e1c4e80f 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -277,7 +277,7 @@ static int init_hw(struct nmk_i2c_dev *dev) goto exit; /* disable the controller */ - i2c_clr_bit(dev->virtbase + I2C_CR , I2C_CR_PE); + i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE); disable_all_interrupts(dev); @@ -525,7 +525,7 @@ static int write_i2c(struct nmk_i2c_dev *dev, u16 flags) dev->virtbase + I2C_CR); /* enable the controller */ - i2c_set_bit(dev->virtbase + I2C_CR , I2C_CR_PE); + i2c_set_bit(dev->virtbase + I2C_CR, I2C_CR_PE); init_completion(&dev->xfer_complete); -- cgit v1.2.3 From af92cca1710f63741925d97fe6ec0bd5eecec627 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 15 Apr 2021 11:38:02 +0200 Subject: i2c: s3c2410: simplify getting of_device_id match data Use of_device_get_match_data() to make the code slightly smaller. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Sylwester Nawrocki Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-s3c2410.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 62a903fbe912..ab928613afba 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -156,12 +157,8 @@ MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); */ static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *pdev) { - if (pdev->dev.of_node) { - const struct of_device_id *match; - - match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node); - return (kernel_ulong_t)match->data; - } + if (pdev->dev.of_node) + return (kernel_ulong_t)of_device_get_match_data(&pdev->dev); return platform_get_device_id(pdev)->driver_data; } -- cgit v1.2.3 From 068ff57d78011e6ed3561455a999f4a0272ea2c7 Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Mon, 5 Apr 2021 16:18:42 +0800 Subject: i2c: core: Fix spacing error by checkpatch Fix the following checkpatch error: #614: FILE: drivers/i2c/i2c-core-base.c:614: + len = acpi_device_modalias(dev, buf, PAGE_SIZE -1); ^ No functional changes. Signed-off-by: Tian Tao Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 4351bf529d0f..3478d208d174 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -651,7 +651,7 @@ modalias_show(struct device *dev, struct device_attribute *attr, char *buf) if (len != -ENODEV) return len; - len = acpi_device_modalias(dev, buf, PAGE_SIZE -1); + len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); if (len != -ENODEV) return len; -- cgit v1.2.3 From 8f51c1763ae98bb63fc04627ceae383aa0e8ff7b Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 12 Apr 2021 16:41:12 +0300 Subject: i2c: i801: Add support for Intel Alder Lake PCH-M Add PCI ID of SMBus controller on Intel Alder Lake PCH-M. Signed-off-by: Jarkko Nikula Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-i801.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 4acee6f9e5a3..99d446763530 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -73,6 +73,7 @@ * Comet Lake-V (PCH) 0xa3a3 32 hard yes yes yes * Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes * Alder Lake-P (PCH) 0x51a3 32 hard yes yes yes + * Alder Lake-M (PCH) 0x54a3 32 hard yes yes yes * * Features supported by this driver: * Software PEC no @@ -230,6 +231,7 @@ #define PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS 0x4b23 #define PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS 0x4da3 #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS 0x51a3 +#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS 0x54a3 #define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4 #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS 0x7aa3 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 @@ -1087,6 +1089,7 @@ static const struct pci_device_id i801_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS) }, { 0, } }; @@ -1771,6 +1774,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) case PCI_DEVICE_ID_INTEL_EBG_SMBUS: case PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS: case PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS: + case PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS: priv->features |= FEATURE_BLOCK_PROC; priv->features |= FEATURE_I2C_BLOCK_READ; priv->features |= FEATURE_IRQ; -- cgit v1.2.3 From 92dfb27240fea2776f61c5422472cb6defca7767 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Sat, 17 Apr 2021 22:05:05 +0300 Subject: i2c: sh7760: fix IRQ error path While adding the invalid IRQ check after calling platform_get_irq(), I managed to overlook that the driver has a complex error path in its probe() method, thus a simple *return* couldn't be used. Use a proper *goto* instead! Fixes: e5b2e3e74201 ("i2c: sh7760: add IRQ check") Signed-off-by: Sergey Shtylyov Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh7760.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c index c79c9f542c5a..319d1fa617c8 100644 --- a/drivers/i2c/busses/i2c-sh7760.c +++ b/drivers/i2c/busses/i2c-sh7760.c @@ -473,7 +473,7 @@ static int sh7760_i2c_probe(struct platform_device *pdev) ret = platform_get_irq(pdev, 0); if (ret < 0) - return ret; + goto out3; id->irq = ret; id->adap.nr = pdev->id; -- cgit v1.2.3 From 63ce8e3df8f6deca2da52eaf064751ad4018b46e Mon Sep 17 00:00:00 2001 From: Qii Wang Date: Sat, 17 Apr 2021 14:46:50 +0800 Subject: i2c: mediatek: Fix send master code at more than 1MHz There are some omissions in the previous patch about replacing I2C_MAX_FAST_MODE__FREQ with I2C_MAX_FAST_MODE_PLUS_FREQ and need to fix it. Fixes: b44658e755b5("i2c: mediatek: Send i2c master code at more than 1MHz") Signed-off-by: Qii Wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 2ffd2f354d0a..3e3426155450 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -564,7 +564,7 @@ static const struct i2c_spec_values *mtk_i2c_get_spec(unsigned int speed) static int mtk_i2c_max_step_cnt(unsigned int target_speed) { - if (target_speed > I2C_MAX_FAST_MODE_FREQ) + if (target_speed > I2C_MAX_FAST_MODE_PLUS_FREQ) return MAX_HS_STEP_CNT_DIV; else return MAX_STEP_CNT_DIV; @@ -635,7 +635,7 @@ static int mtk_i2c_check_ac_timing(struct mtk_i2c *i2c, if (sda_min > sda_max) return -3; - if (check_speed > I2C_MAX_FAST_MODE_FREQ) { + if (check_speed > I2C_MAX_FAST_MODE_PLUS_FREQ) { if (i2c->dev_comp->ltiming_adjust) { i2c->ac_timing.hs = I2C_TIME_DEFAULT_VALUE | (sample_cnt << 12) | (high_cnt << 8); @@ -850,7 +850,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, control_reg = mtk_i2c_readw(i2c, OFFSET_CONTROL) & ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS); - if ((i2c->speed_hz > I2C_MAX_FAST_MODE_FREQ) || (left_num >= 1)) + if ((i2c->speed_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) || (left_num >= 1)) control_reg |= I2C_CONTROL_RS; if (i2c->op == I2C_MASTER_WRRD) @@ -1067,7 +1067,8 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, } } - if (i2c->auto_restart && num >= 2 && i2c->speed_hz > I2C_MAX_FAST_MODE_FREQ) + if (i2c->auto_restart && num >= 2 && + i2c->speed_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) /* ignore the first restart irq after the master code, * otherwise the first transfer will be discarded. */ -- cgit v1.2.3 From 3186b880447ad3cc9b6487fa626a71d64b831524 Mon Sep 17 00:00:00 2001 From: Qii Wang Date: Sat, 17 Apr 2021 14:46:51 +0800 Subject: i2c: mediatek: Fix wrong dma sync flag The right flag is apdma_sync when apdma remove hand-shake signel. Fixes: 05f6f7271a38 ("i2c: mediatek: Fix apdma and i2c hand-shake timeout") Signed-off-by: Qii Wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 3e3426155450..bf25acba2ed5 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -479,7 +479,7 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c) { u16 control_reg; - if (i2c->dev_comp->dma_sync) { + if (i2c->dev_comp->apdma_sync) { writel(I2C_DMA_WARM_RST, i2c->pdmabase + OFFSET_RST); udelay(10); writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST); -- cgit v1.2.3 From a80f24945fcfdff31bdf04837145e56570741a67 Mon Sep 17 00:00:00 2001 From: Qii Wang Date: Sat, 17 Apr 2021 14:46:52 +0800 Subject: i2c: mediatek: Use scl_int_delay_ns to compensate clock-stretching The parameters of tSU,STA/tHD,STA/tSU,STOP maybe out of spec due to device clock-stretch or circuit loss, we could get a suitable scl_int_delay_ns from i2c_timings to compensate these parameters to meet the spec via EXT_CONF register. Signed-off-by: Qii Wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index bf25acba2ed5..5ddfa4e56ee2 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -231,6 +231,7 @@ struct mtk_i2c { struct i2c_adapter adap; /* i2c host adapter */ struct device *dev; struct completion msg_complete; + struct i2c_timings timing_info; /* set in i2c probe */ void __iomem *base; /* i2c base addr */ @@ -607,7 +608,8 @@ static int mtk_i2c_check_ac_timing(struct mtk_i2c *i2c, else clk_ns = sample_ns / 2; - su_sta_cnt = DIV_ROUND_UP(spec->min_su_sta_ns, clk_ns); + su_sta_cnt = DIV_ROUND_UP(spec->min_su_sta_ns + + i2c->timing_info.scl_int_delay_ns, clk_ns); if (su_sta_cnt > max_sta_cnt) return -1; @@ -1176,6 +1178,8 @@ static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c) i2c->use_push_pull = of_property_read_bool(np, "mediatek,use-push-pull"); + i2c_parse_fw_timings(i2c->dev, &i2c->timing_info, true); + return 0; } -- cgit v1.2.3