Commit 0ae982df authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull i2c updates from Wolfram Sang:
 "I2C Core:
   - prevent double-free of an fwnode if it is a software node
   - use recent helpers instead of custom ACPI or outdated OF ones
   - add a more elaborate description of a message flag

  Cleanups and refactorings:
   - lpi2c, riic, st, stm32f7: general improvements
   - riic: support more flexible IRQ configurations
   - tegra: fix documentation

  Improvements:
   - lpi2c: improve register polling and add atomic transfer
   - imx: use guarded spinlocks

  New hardware support:
   - Samsung Exynos 2200
   - Renesas RZ/T2H (R9A09G077), RZ/N2H (R9A09G087)

  DT binding:
   - rk3x: enable power domains
   - nxp: support clock property"

* tag 'i2c-for-6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux:
  i2c: core: Fix double-free of fwnode in i2c_unregister_device()
  i2c: lpi2c: implement xfer_atomic callback
  i2c: lpi2c: use readl_poll_timeout() for register polling
  dt-bindings: i2c: i2c-rk3x: Allow use of a power-domain
  dt-bindings: i2c: exynos5: add samsung,exynos2200-hsi2c compatible
  i2c: lpi2c: convert to use secs_to_jiffies()
  i2c: st: Use min() to improve code
  i2c: imx: use guard to take spinlock
  i2c: stm32f7: Use str_on_off() helper
  dt-bindings: i2c: nxp,pnx-i2c: allow clocks property
  i2c: riic: Add support for RZ/T2H SoC
  i2c: riic: Move generic compatible string to end of array
  i2c: riic: Pass IRQ desc array as part of OF data
  dt-bindings: i2c: renesas,riic: Document RZ/T2H and RZ/N2H support
  dt-bindings: i2c: renesas,riic: Move ref for i2c-controller.yaml to the end
  i2c: tegra: Add missing kernel-doc for dma_dev member
  i2c: Clarify behavior of I2C_M_RD flag
  i2c: mux: pca954x: Use dev_fwnode()
  i2c: acpi: Replace custom code with device_match_acpi_handle()
parents 0919a5b3 1c24e5fc
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ properties:
      - items:
          - enum:
              - google,gs101-hsi2c
              - samsung,exynos2200-hsi2c
              - samsung,exynos850-hsi2c
          - const: samsung,exynosautov9-hsi2c
      - const: samsung,exynos5-hsi2c    # Exynos5250 and Exynos5420
+3 −0
Original line number Diff line number Diff line
@@ -105,6 +105,9 @@ properties:
      (t(f) in the I2C specification). If not specified we will use the SCL
      value since they are the same in nearly all cases.

  power-domains:
    maxItems: 1

required:
  - compatible
  - reg
+3 −0
Original line number Diff line number Diff line
@@ -22,6 +22,9 @@ properties:
  interrupts:
    maxItems: 1

  clocks:
    maxItems: 1

  clock-frequency:
    default: 100000

+71 −34
Original line number Diff line number Diff line
@@ -10,9 +10,6 @@ maintainers:
  - Chris Brandt <chris.brandt@renesas.com>
  - Wolfram Sang <wsa+renesas@sang-engineering.com>

allOf:
  - $ref: /schemas/i2c/i2c-controller.yaml#

properties:
  compatible:
    oneOf:
@@ -32,13 +29,20 @@ properties:
              - renesas,riic-r9a09g056   # RZ/V2N
          - const: renesas,riic-r9a09g057   # RZ/V2H(P)

      - const: renesas,riic-r9a09g057   # RZ/V2H(P)
      - enum:
          - renesas,riic-r9a09g057   # RZ/V2H(P)
          - renesas,riic-r9a09g077   # RZ/T2H

      - items:
          - const: renesas,riic-r9a09g087  # RZ/N2H
          - const: renesas,riic-r9a09g077  # RZ/T2H

  reg:
    maxItems: 1

  interrupts:
    items:
    oneOf:
      - items:
          - description: Transmit End Interrupt
          - description: Receive Data Full Interrupt
          - description: Transmit Data Empty Interrupt
@@ -47,9 +51,15 @@ properties:
          - description: NACK Reception Interrupt
          - description: Arbitration-Lost Interrupt
          - description: Timeout Interrupt
      - items:
          - description: Transfer Error Or Event Generation
          - description: Receive Data Full Interrupt
          - description: Transmit Data Empty Interrupt
          - description: Transmit End Interrupt

  interrupt-names:
    items:
    oneOf:
      - items:
          - const: tei
          - const: ri
          - const: ti
@@ -58,6 +68,11 @@ properties:
          - const: naki
          - const: ali
          - const: tmoi
      - items:
          - const: eei
          - const: rxi
          - const: txi
          - const: tei

  clock-frequency:
    description:
@@ -84,7 +99,29 @@ required:
  - '#address-cells'
  - '#size-cells'

if:
allOf:
  - $ref: /schemas/i2c/i2c-controller.yaml#

  - if:
      properties:
        compatible:
          contains:
            const: renesas,riic-r9a09g077
    then:
      properties:
        interrupts:
          maxItems: 4
        interrupt-names:
          maxItems: 4
        resets: false
    else:
      properties:
        interrupts:
          minItems: 8
        interrupt-names:
          minItems: 8

  - if:
      properties:
        compatible:
          contains:
+173 −83
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -187,19 +188,25 @@ struct lpi2c_imx_struct {
	struct i2c_client	*target;
};

#define lpi2c_imx_read_msr_poll_timeout(atomic, val, cond)                    \
	(atomic ? readl_poll_timeout_atomic(lpi2c_imx->base + LPI2C_MSR, val, \
					    cond, 0, 500000) :                \
		  readl_poll_timeout(lpi2c_imx->base + LPI2C_MSR, val, cond,  \
				     0, 500000))

static void lpi2c_imx_intctrl(struct lpi2c_imx_struct *lpi2c_imx,
			      unsigned int enable)
{
	writel(enable, lpi2c_imx->base + LPI2C_MIER);
}

static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx)
static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx, bool atomic)
{
	unsigned long orig_jiffies = jiffies;
	unsigned int temp;
	int err;

	while (1) {
		temp = readl(lpi2c_imx->base + LPI2C_MSR);
	err = lpi2c_imx_read_msr_poll_timeout(atomic, temp,
					      temp & (MSR_ALF | MSR_BBF | MSR_MBF));

	/* check for arbitration lost, clear if set */
	if (temp & MSR_ALF) {
@@ -207,21 +214,22 @@ static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx)
		return -EAGAIN;
	}

		if (temp & (MSR_BBF | MSR_MBF))
			break;

		if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
	/* check for bus not busy */
	if (err) {
		dev_dbg(&lpi2c_imx->adapter.dev, "bus not work\n");
		if (lpi2c_imx->adapter.bus_recovery_info)
			i2c_recover_bus(&lpi2c_imx->adapter);
		return -ETIMEDOUT;
	}
		schedule();
	}

	return 0;
}

static u32 lpi2c_imx_txfifo_cnt(struct lpi2c_imx_struct *lpi2c_imx)
{
	return readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
}

static void lpi2c_imx_set_mode(struct lpi2c_imx_struct *lpi2c_imx)
{
	unsigned int bitrate = lpi2c_imx->bitrate;
@@ -242,7 +250,7 @@ static void lpi2c_imx_set_mode(struct lpi2c_imx_struct *lpi2c_imx)
}

static int lpi2c_imx_start(struct lpi2c_imx_struct *lpi2c_imx,
			   struct i2c_msg *msgs)
			   struct i2c_msg *msgs, bool atomic)
{
	unsigned int temp;

@@ -254,30 +262,23 @@ static int lpi2c_imx_start(struct lpi2c_imx_struct *lpi2c_imx,
	temp = i2c_8bit_addr_from_msg(msgs) | (GEN_START << 8);
	writel(temp, lpi2c_imx->base + LPI2C_MTDR);

	return lpi2c_imx_bus_busy(lpi2c_imx);
	return lpi2c_imx_bus_busy(lpi2c_imx, atomic);
}

static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx)
static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx, bool atomic)
{
	unsigned long orig_jiffies = jiffies;
	unsigned int temp;
	int err;

	writel(GEN_STOP << 8, lpi2c_imx->base + LPI2C_MTDR);

	do {
		temp = readl(lpi2c_imx->base + LPI2C_MSR);
		if (temp & MSR_SDF)
			break;
	err = lpi2c_imx_read_msr_poll_timeout(atomic, temp, temp & MSR_SDF);

		if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
	if (err) {
		dev_dbg(&lpi2c_imx->adapter.dev, "stop timeout\n");
		if (lpi2c_imx->adapter.bus_recovery_info)
			i2c_recover_bus(&lpi2c_imx->adapter);
			break;
	}
		schedule();

	} while (1);
}

/* CLKLO = I2C_CLK_RATIO * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI/2 */
@@ -391,28 +392,25 @@ static int lpi2c_imx_pio_msg_complete(struct lpi2c_imx_struct *lpi2c_imx)
	return time_left ? 0 : -ETIMEDOUT;
}

static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx)
static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx, bool atomic)
{
	unsigned long orig_jiffies = jiffies;
	u32 txcnt;
	unsigned int temp;
	int err;

	do {
		txcnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
	err = lpi2c_imx_read_msr_poll_timeout(atomic, temp,
					      (temp & MSR_NDF) || !lpi2c_imx_txfifo_cnt(lpi2c_imx));

		if (readl(lpi2c_imx->base + LPI2C_MSR) & MSR_NDF) {
	if (temp & MSR_NDF) {
		dev_dbg(&lpi2c_imx->adapter.dev, "NDF detected\n");
		return -EIO;
	}

		if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
	if (err) {
		dev_dbg(&lpi2c_imx->adapter.dev, "txfifo empty timeout\n");
		if (lpi2c_imx->adapter.bus_recovery_info)
			i2c_recover_bus(&lpi2c_imx->adapter);
		return -ETIMEDOUT;
	}
		schedule();

	} while (txcnt);

	return 0;
}
@@ -436,7 +434,7 @@ static void lpi2c_imx_set_rx_watermark(struct lpi2c_imx_struct *lpi2c_imx)
	writel(temp << 16, lpi2c_imx->base + LPI2C_MFCR);
}

static void lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx)
static bool lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx, bool atomic)
{
	unsigned int data, txcnt;

@@ -451,13 +449,19 @@ static void lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx)
		txcnt++;
	}

	if (lpi2c_imx->delivered < lpi2c_imx->msglen)
	if (lpi2c_imx->delivered < lpi2c_imx->msglen) {
		if (!atomic)
			lpi2c_imx_intctrl(lpi2c_imx, MIER_TDIE | MIER_NDIE);
	else
		return false;
	}

	if (!atomic)
		complete(&lpi2c_imx->complete);

	return true;
}

static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx)
static bool lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx, bool atomic)
{
	unsigned int blocklen, remaining;
	unsigned int temp, data;
@@ -482,8 +486,9 @@ static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx)
	remaining = lpi2c_imx->msglen - lpi2c_imx->delivered;

	if (!remaining) {
		if (!atomic)
			complete(&lpi2c_imx->complete);
		return;
		return true;
	}

	/* not finished, still waiting for rx data */
@@ -501,7 +506,10 @@ static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx)
		writel(temp, lpi2c_imx->base + LPI2C_MTDR);
	}

	if (!atomic)
		lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE);

	return false;
}

static void lpi2c_imx_write(struct lpi2c_imx_struct *lpi2c_imx,
@@ -509,10 +517,28 @@ static void lpi2c_imx_write(struct lpi2c_imx_struct *lpi2c_imx,
{
	lpi2c_imx->tx_buf = msgs->buf;
	lpi2c_imx_set_tx_watermark(lpi2c_imx);
	lpi2c_imx_write_txfifo(lpi2c_imx);
	lpi2c_imx_write_txfifo(lpi2c_imx, false);
}

static int lpi2c_imx_write_atomic(struct lpi2c_imx_struct *lpi2c_imx,
				  struct i2c_msg *msgs)
{
	u32 temp;
	int err;

	lpi2c_imx->tx_buf = msgs->buf;

	err = lpi2c_imx_read_msr_poll_timeout(true, temp,
					      (temp & MSR_NDF) ||
					      lpi2c_imx_write_txfifo(lpi2c_imx, true));

	if (temp & MSR_NDF)
		return -EIO;

	return err;
}

static void lpi2c_imx_read(struct lpi2c_imx_struct *lpi2c_imx,
static void lpi2c_imx_read_init(struct lpi2c_imx_struct *lpi2c_imx,
				struct i2c_msg *msgs)
{
	unsigned int temp;
@@ -524,8 +550,43 @@ static void lpi2c_imx_read(struct lpi2c_imx_struct *lpi2c_imx,
	temp = msgs->len > CHUNK_DATA ? CHUNK_DATA - 1 : msgs->len - 1;
	temp |= (RECV_DATA << 8);
	writel(temp, lpi2c_imx->base + LPI2C_MTDR);
}

	lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE);
static bool lpi2c_imx_read_chunk_atomic(struct lpi2c_imx_struct *lpi2c_imx)
{
	u32 rxcnt;

	rxcnt = (readl(lpi2c_imx->base + LPI2C_MFSR) >> 16) & 0xFF;
	if (!rxcnt)
		return false;

	if (!lpi2c_imx_read_rxfifo(lpi2c_imx, true))
		return false;

	return true;
}

static int lpi2c_imx_read_atomic(struct lpi2c_imx_struct *lpi2c_imx,
				 struct i2c_msg *msgs)
{
	u32 temp;
	int tmo_us;

	tmo_us = 1000000;
	do {
		if (lpi2c_imx_read_chunk_atomic(lpi2c_imx))
			return 0;

		temp = readl(lpi2c_imx->base + LPI2C_MSR);

		if (temp & MSR_NDF)
			return -EIO;

		udelay(100);
		tmo_us -= 100;
	} while (tmo_us > 0);

	return -ETIMEDOUT;
}

static bool is_use_dma(struct lpi2c_imx_struct *lpi2c_imx, struct i2c_msg *msg)
@@ -545,14 +606,27 @@ static int lpi2c_imx_pio_xfer(struct lpi2c_imx_struct *lpi2c_imx,
{
	reinit_completion(&lpi2c_imx->complete);

	if (msg->flags & I2C_M_RD)
		lpi2c_imx_read(lpi2c_imx, msg);
	else
	if (msg->flags & I2C_M_RD) {
		lpi2c_imx_read_init(lpi2c_imx, msg);
		lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE);
	} else {
		lpi2c_imx_write(lpi2c_imx, msg);
	}

	return lpi2c_imx_pio_msg_complete(lpi2c_imx);
}

static int lpi2c_imx_pio_xfer_atomic(struct lpi2c_imx_struct *lpi2c_imx,
				     struct i2c_msg *msg)
{
	if (msg->flags & I2C_M_RD) {
		lpi2c_imx_read_init(lpi2c_imx, msg);
		return lpi2c_imx_read_atomic(lpi2c_imx, msg);
	}

	return lpi2c_imx_write_atomic(lpi2c_imx, msg);
}

static int lpi2c_imx_dma_timeout_calculate(struct lpi2c_imx_struct *lpi2c_imx)
{
	unsigned long time = 0;
@@ -563,7 +637,7 @@ static int lpi2c_imx_dma_timeout_calculate(struct lpi2c_imx_struct *lpi2c_imx)
	time += 1;

	/* Double calculated time */
	return msecs_to_jiffies(time * MSEC_PER_SEC);
	return secs_to_jiffies(time);
}

static int lpi2c_imx_alloc_rx_cmd_buf(struct lpi2c_imx_struct *lpi2c_imx)
@@ -947,8 +1021,8 @@ static int lpi2c_imx_dma_xfer(struct lpi2c_imx_struct *lpi2c_imx,
	return ret;
}

static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
			  struct i2c_msg *msgs, int num)
static int lpi2c_imx_xfer_common(struct i2c_adapter *adapter,
				 struct i2c_msg *msgs, int num, bool atomic)
{
	struct lpi2c_imx_struct *lpi2c_imx = i2c_get_adapdata(adapter);
	unsigned int temp;
@@ -959,7 +1033,7 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
		return result;

	for (i = 0; i < num; i++) {
		result = lpi2c_imx_start(lpi2c_imx, &msgs[i]);
		result = lpi2c_imx_start(lpi2c_imx, &msgs[i], atomic);
		if (result)
			goto disable;

@@ -971,6 +1045,10 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
		lpi2c_imx->tx_buf = NULL;
		lpi2c_imx->delivered = 0;
		lpi2c_imx->msglen = msgs[i].len;

		if (atomic) {
			result = lpi2c_imx_pio_xfer_atomic(lpi2c_imx, &msgs[i]);
		} else {
			init_completion(&lpi2c_imx->complete);

			if (is_use_dma(lpi2c_imx, &msgs[i])) {
@@ -980,19 +1058,20 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
			} else {
				result = lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]);
			}
		}

		if (result)
			goto stop;

		if (!(msgs[i].flags & I2C_M_RD)) {
			result = lpi2c_imx_txfifo_empty(lpi2c_imx);
			result = lpi2c_imx_txfifo_empty(lpi2c_imx, atomic);
			if (result)
				goto stop;
		}
	}

stop:
	lpi2c_imx_stop(lpi2c_imx);
	lpi2c_imx_stop(lpi2c_imx, atomic);

	temp = readl(lpi2c_imx->base + LPI2C_MSR);
	if ((temp & MSR_NDF) && !result)
@@ -1008,6 +1087,16 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
	return (result < 0) ? result : num;
}

static int lpi2c_imx_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
{
	return lpi2c_imx_xfer_common(adapter, msgs, num, false);
}

static int lpi2c_imx_xfer_atomic(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
{
	return lpi2c_imx_xfer_common(adapter, msgs, num, true);
}

static irqreturn_t lpi2c_imx_target_isr(struct lpi2c_imx_struct *lpi2c_imx,
					u32 ssr, u32 sier_filter)
{
@@ -1070,9 +1159,9 @@ static irqreturn_t lpi2c_imx_master_isr(struct lpi2c_imx_struct *lpi2c_imx)
	if (temp & MSR_NDF)
		complete(&lpi2c_imx->complete);
	else if (temp & MSR_RDF)
		lpi2c_imx_read_rxfifo(lpi2c_imx);
		lpi2c_imx_read_rxfifo(lpi2c_imx, false);
	else if (temp & MSR_TDF)
		lpi2c_imx_write_txfifo(lpi2c_imx);
		lpi2c_imx_write_txfifo(lpi2c_imx, false);

	return IRQ_HANDLED;
}
@@ -1269,6 +1358,7 @@ static u32 lpi2c_imx_func(struct i2c_adapter *adapter)

static const struct i2c_algorithm lpi2c_imx_algo = {
	.xfer = lpi2c_imx_xfer,
	.xfer_atomic = lpi2c_imx_xfer_atomic,
	.functionality = lpi2c_imx_func,
	.reg_target = lpi2c_imx_register_target,
	.unreg_target = lpi2c_imx_unregister_target,
Loading