Commit ebe70b3a authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull i2c fixes from Wolfram Sang:
 "Two fixes for the testunit and and a fixup for the code reorganization
  of the previous wmt-driver"

* tag 'i2c-for-6.10-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux:
  i2c: testunit: discard write requests while old command is running
  i2c: testunit: don't erase registers after STOP
  i2c: viai2c: turn common code into a proper module
parents 921863fd c116deaf
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -29,8 +29,7 @@ obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o
obj-$(CONFIG_I2C_SIS96X)	+= i2c-sis96x.o
obj-$(CONFIG_I2C_VIA)		+= i2c-via.o
obj-$(CONFIG_I2C_VIAPRO)	+= i2c-viapro.o
i2c-zhaoxin-objs := i2c-viai2c-zhaoxin.o i2c-viai2c-common.o
obj-$(CONFIG_I2C_ZHAOXIN)	+= i2c-zhaoxin.o
obj-$(CONFIG_I2C_ZHAOXIN)	+= i2c-viai2c-zhaoxin.o i2c-viai2c-common.o

# Mac SMBus host controller drivers
obj-$(CONFIG_I2C_HYDRA)		+= i2c-hydra.o
@@ -120,8 +119,7 @@ obj-$(CONFIG_I2C_TEGRA_BPMP) += i2c-tegra-bpmp.o
obj-$(CONFIG_I2C_UNIPHIER)	+= i2c-uniphier.o
obj-$(CONFIG_I2C_UNIPHIER_F)	+= i2c-uniphier-f.o
obj-$(CONFIG_I2C_VERSATILE)	+= i2c-versatile.o
i2c-wmt-objs := i2c-viai2c-wmt.o i2c-viai2c-common.o
obj-$(CONFIG_I2C_WMT)		+= i2c-wmt.o
obj-$(CONFIG_I2C_WMT)		+= i2c-viai2c-wmt.o i2c-viai2c-common.o
i2c-octeon-objs := i2c-octeon-core.o i2c-octeon-platdrv.o
obj-$(CONFIG_I2C_OCTEON)	+= i2c-octeon.o
i2c-thunderx-objs := i2c-octeon-core.o i2c-thunderx-pcidrv.o
+9 −62
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ int viai2c_wait_bus_not_busy(struct viai2c *i2c)

	return 0;
}
EXPORT_SYMBOL_GPL(viai2c_wait_bus_not_busy);

static int viai2c_write(struct viai2c *i2c, struct i2c_msg *pmsg, int last)
{
@@ -121,6 +122,7 @@ int viai2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)

	return (ret < 0) ? ret : i;
}
EXPORT_SYMBOL_GPL(viai2c_xfer);

/*
 * Main process of the byte mode xfer
@@ -130,7 +132,7 @@ int viai2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 *  0: there is still data that needs to be transferred
 *  -EIO: error occurred
 */
static int viai2c_irq_xfer(struct viai2c *i2c)
int viai2c_irq_xfer(struct viai2c *i2c)
{
	u16 val;
	struct i2c_msg *msg = i2c->msg;
@@ -171,51 +173,11 @@ static int viai2c_irq_xfer(struct viai2c *i2c)

	return i2c->xfered_len == msg->len;
}

int __weak viai2c_fifo_irq_xfer(struct viai2c *i2c, bool irq)
{
	return 0;
}

static irqreturn_t viai2c_isr(int irq, void *data)
{
	struct viai2c *i2c = data;
	u8 status;

	/* save the status and write-clear it */
	status = readw(i2c->base + VIAI2C_REG_ISR);
	if (!status && i2c->platform == VIAI2C_PLAT_ZHAOXIN)
		return IRQ_NONE;

	writew(status, i2c->base + VIAI2C_REG_ISR);

	i2c->ret = 0;
	if (status & VIAI2C_ISR_NACK_ADDR)
		i2c->ret = -EIO;

	if (i2c->platform == VIAI2C_PLAT_WMT && (status & VIAI2C_ISR_SCL_TIMEOUT))
		i2c->ret = -ETIMEDOUT;

	if (!i2c->ret) {
		if (i2c->mode == VIAI2C_BYTE_MODE)
			i2c->ret = viai2c_irq_xfer(i2c);
		else
			i2c->ret = viai2c_fifo_irq_xfer(i2c, true);
	}

	/* All the data has been successfully transferred or error occurred */
	if (i2c->ret)
		complete(&i2c->complete);

	return IRQ_HANDLED;
}
EXPORT_SYMBOL_GPL(viai2c_irq_xfer);

int viai2c_init(struct platform_device *pdev, struct viai2c **pi2c, int plat)
{
	int err;
	int irq_flags;
	struct viai2c *i2c;
	struct device_node *np = pdev->dev.of_node;

	i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
	if (!i2c)
@@ -225,28 +187,8 @@ int viai2c_init(struct platform_device *pdev, struct viai2c **pi2c, int plat)
	if (IS_ERR(i2c->base))
		return PTR_ERR(i2c->base);

	if (plat == VIAI2C_PLAT_WMT) {
		irq_flags = 0;
		i2c->irq = irq_of_parse_and_map(np, 0);
		if (!i2c->irq)
			return -EINVAL;
	} else if (plat == VIAI2C_PLAT_ZHAOXIN) {
		irq_flags = IRQF_SHARED;
		i2c->irq = platform_get_irq(pdev, 0);
		if (i2c->irq < 0)
			return i2c->irq;
	} else {
		return dev_err_probe(&pdev->dev, -EINVAL, "wrong platform type\n");
	}

	i2c->platform = plat;

	err = devm_request_irq(&pdev->dev, i2c->irq, viai2c_isr,
			       irq_flags, pdev->name, i2c);
	if (err)
		return dev_err_probe(&pdev->dev, err,
				"failed to request irq %i\n", i2c->irq);

	i2c->dev = &pdev->dev;
	init_completion(&i2c->complete);
	platform_set_drvdata(pdev, i2c);
@@ -254,3 +196,8 @@ int viai2c_init(struct platform_device *pdev, struct viai2c **pi2c, int plat)
	*pi2c = i2c;
	return 0;
}
EXPORT_SYMBOL_GPL(viai2c_init);

MODULE_DESCRIPTION("Via/Wondermedia/Zhaoxin I2C master-mode bus adapter");
MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
MODULE_LICENSE("GPL");
+1 −1
Original line number Diff line number Diff line
@@ -80,6 +80,6 @@ struct viai2c {
int viai2c_wait_bus_not_busy(struct viai2c *i2c);
int viai2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num);
int viai2c_init(struct platform_device *pdev, struct viai2c **pi2c, int plat);
int viai2c_fifo_irq_xfer(struct viai2c *i2c, bool irq);
int viai2c_irq_xfer(struct viai2c *i2c);

#endif
+36 −0
Original line number Diff line number Diff line
@@ -72,6 +72,32 @@ static int wmt_i2c_reset_hardware(struct viai2c *i2c)
	return 0;
}

static irqreturn_t wmt_i2c_isr(int irq, void *data)
{
	struct viai2c *i2c = data;
	u8 status;

	/* save the status and write-clear it */
	status = readw(i2c->base + VIAI2C_REG_ISR);
	writew(status, i2c->base + VIAI2C_REG_ISR);

	i2c->ret = 0;
	if (status & VIAI2C_ISR_NACK_ADDR)
		i2c->ret = -EIO;

	if (status & VIAI2C_ISR_SCL_TIMEOUT)
		i2c->ret = -ETIMEDOUT;

	if (!i2c->ret)
		i2c->ret = viai2c_irq_xfer(i2c);

	/* All the data has been successfully transferred or error occurred */
	if (i2c->ret)
		complete(&i2c->complete);

	return IRQ_HANDLED;
}

static int wmt_i2c_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
@@ -84,6 +110,16 @@ static int wmt_i2c_probe(struct platform_device *pdev)
	if (err)
		return err;

	i2c->irq = platform_get_irq(pdev, 0);
	if (i2c->irq < 0)
		return i2c->irq;

	err = devm_request_irq(&pdev->dev, i2c->irq, wmt_i2c_isr,
			       0, pdev->name, i2c);
	if (err)
		return dev_err_probe(&pdev->dev, err,
				"failed to request irq %i\n", i2c->irq);

	i2c->clk = of_clk_get(np, 0);
	if (IS_ERR(i2c->clk)) {
		dev_err(&pdev->dev, "unable to request clock\n");
+91 −22
Original line number Diff line number Diff line
@@ -49,8 +49,47 @@ struct viai2c_zhaoxin {
	u16			xfer_len;
};

/* 'irq == true' means in interrupt context */
int viai2c_fifo_irq_xfer(struct viai2c *i2c, bool irq)
static int viai2c_fifo_xfer(struct viai2c *i2c)
{
	u16 i;
	u8 tmp;
	struct i2c_msg *msg = i2c->msg;
	void __iomem *base = i2c->base;
	bool read = !!(msg->flags & I2C_M_RD);
	struct viai2c_zhaoxin *priv = i2c->pltfm_priv;

	/* reset fifo buffer */
	tmp = ioread8(base + ZXI2C_REG_HCR);
	iowrite8(tmp | ZXI2C_HCR_RST_FIFO, base + ZXI2C_REG_HCR);

	/* set xfer len */
	priv->xfer_len = min_t(u16, msg->len - i2c->xfered_len, ZXI2C_FIFO_SIZE);
	if (read) {
		iowrite8(priv->xfer_len - 1, base + ZXI2C_REG_HRLR);
	} else {
		iowrite8(priv->xfer_len - 1, base + ZXI2C_REG_HTLR);
		/* set write data */
		for (i = 0; i < priv->xfer_len; i++)
			iowrite8(msg->buf[i2c->xfered_len + i], base + ZXI2C_REG_HTDR);
	}

	/* prepare to stop transmission */
	if (priv->hrv && msg->len == (i2c->xfered_len + priv->xfer_len)) {
		tmp = ioread8(base + VIAI2C_REG_CR);
		tmp |= read ? VIAI2C_CR_RX_END : VIAI2C_CR_TX_END;
		iowrite8(tmp, base + VIAI2C_REG_CR);
	}

	u16 tcr_val = i2c->tcr;

	/* start transmission */
	tcr_val |= read ? VIAI2C_TCR_READ : 0;
	writew(tcr_val | msg->addr, base + VIAI2C_REG_TCR);

	return 0;
}

static int viai2c_fifo_irq_xfer(struct viai2c *i2c)
{
	u16 i;
	u8 tmp;
@@ -59,7 +98,6 @@ int viai2c_fifo_irq_xfer(struct viai2c *i2c, bool irq)
	bool read = !!(msg->flags & I2C_M_RD);
	struct viai2c_zhaoxin *priv = i2c->pltfm_priv;

	if (irq) {
	/* get the received data */
	if (read)
		for (i = 0; i < priv->xfer_len; i++)
@@ -68,7 +106,6 @@ int viai2c_fifo_irq_xfer(struct viai2c *i2c, bool irq)
	i2c->xfered_len += priv->xfer_len;
	if (i2c->xfered_len == msg->len)
		return 1;
	}

	/* reset fifo buffer */
	tmp = ioread8(base + ZXI2C_REG_HCR);
@@ -92,17 +129,9 @@ int viai2c_fifo_irq_xfer(struct viai2c *i2c, bool irq)
		iowrite8(tmp, base + VIAI2C_REG_CR);
	}

	if (irq) {
	/* continue transmission */
	tmp = ioread8(base + VIAI2C_REG_CR);
	iowrite8(tmp |= VIAI2C_CR_CPU_RDY, base + VIAI2C_REG_CR);
	} else {
		u16 tcr_val = i2c->tcr;

		/* start transmission */
		tcr_val |= read ? VIAI2C_TCR_READ : 0;
		writew(tcr_val | msg->addr, base + VIAI2C_REG_TCR);
	}

	return 0;
}
@@ -135,7 +164,7 @@ static int zxi2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int
		priv->xfer_len = 0;
		i2c->xfered_len = 0;

		viai2c_fifo_irq_xfer(i2c, 0);
		viai2c_fifo_xfer(i2c);

		if (!wait_for_completion_timeout(&i2c->complete, VIAI2C_TIMEOUT))
			return -ETIMEDOUT;
@@ -228,6 +257,36 @@ static void zxi2c_get_bus_speed(struct viai2c *i2c)
	dev_info(i2c->dev, "speed mode is %s\n", i2c_freq_mode_string(params[0]));
}

static irqreturn_t zxi2c_isr(int irq, void *data)
{
	struct viai2c *i2c = data;
	u8 status;

	/* save the status and write-clear it */
	status = readw(i2c->base + VIAI2C_REG_ISR);
	if (!status)
		return IRQ_NONE;

	writew(status, i2c->base + VIAI2C_REG_ISR);

	i2c->ret = 0;
	if (status & VIAI2C_ISR_NACK_ADDR)
		i2c->ret = -EIO;

	if (!i2c->ret) {
		if (i2c->mode == VIAI2C_BYTE_MODE)
			i2c->ret = viai2c_irq_xfer(i2c);
		else
			i2c->ret = viai2c_fifo_irq_xfer(i2c);
	}

	/* All the data has been successfully transferred or error occurred */
	if (i2c->ret)
		complete(&i2c->complete);

	return IRQ_HANDLED;
}

static int zxi2c_probe(struct platform_device *pdev)
{
	int error;
@@ -239,6 +298,16 @@ static int zxi2c_probe(struct platform_device *pdev)
	if (error)
		return error;

	i2c->irq = platform_get_irq(pdev, 0);
	if (i2c->irq < 0)
		return i2c->irq;

	error = devm_request_irq(&pdev->dev, i2c->irq, zxi2c_isr,
				 IRQF_SHARED, pdev->name, i2c);
	if (error)
		return dev_err_probe(&pdev->dev, error,
				"failed to request irq %i\n", i2c->irq);

	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;
Loading