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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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 4a7695429eade517b07ea72f9ec366130e81a076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bence=20Cs=C3=B3k=C3=A1s?= 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 --- drivers/i2c/busses/Kconfig | 10 ++ drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-cp2615.c | 330 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 341 insertions(+) create mode 100644 drivers/i2c/busses/i2c-cp2615.c (limited to 'drivers/i2c') 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(+) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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(-) (limited to 'drivers/i2c') 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 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/i2c') 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); -- 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(-) (limited to 'drivers/i2c') 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 i