162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright Intel Corporation (C) 2017.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Based on the i2c-axxia.c driver.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/clk.h>
862306a36Sopenharmony_ci#include <linux/clkdev.h>
962306a36Sopenharmony_ci#include <linux/err.h>
1062306a36Sopenharmony_ci#include <linux/i2c.h>
1162306a36Sopenharmony_ci#include <linux/iopoll.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define ALTR_I2C_TFR_CMD	0x00	/* Transfer Command register */
1962306a36Sopenharmony_ci#define     ALTR_I2C_TFR_CMD_STA	BIT(9)	/* send START before byte */
2062306a36Sopenharmony_ci#define     ALTR_I2C_TFR_CMD_STO	BIT(8)	/* send STOP after byte */
2162306a36Sopenharmony_ci#define     ALTR_I2C_TFR_CMD_RW_D	BIT(0)	/* Direction of transfer */
2262306a36Sopenharmony_ci#define ALTR_I2C_RX_DATA	0x04	/* RX data FIFO register */
2362306a36Sopenharmony_ci#define ALTR_I2C_CTRL		0x08	/* Control register */
2462306a36Sopenharmony_ci#define     ALTR_I2C_CTRL_RXT_SHFT	4	/* RX FIFO Threshold */
2562306a36Sopenharmony_ci#define     ALTR_I2C_CTRL_TCT_SHFT	2	/* TFER CMD FIFO Threshold */
2662306a36Sopenharmony_ci#define     ALTR_I2C_CTRL_BSPEED	BIT(1)	/* Bus Speed (1=Fast) */
2762306a36Sopenharmony_ci#define     ALTR_I2C_CTRL_EN	BIT(0)	/* Enable Core (1=Enable) */
2862306a36Sopenharmony_ci#define ALTR_I2C_ISER		0x0C	/* Interrupt Status Enable register */
2962306a36Sopenharmony_ci#define     ALTR_I2C_ISER_RXOF_EN	BIT(4)	/* Enable RX OVERFLOW IRQ */
3062306a36Sopenharmony_ci#define     ALTR_I2C_ISER_ARB_EN	BIT(3)	/* Enable ARB LOST IRQ */
3162306a36Sopenharmony_ci#define     ALTR_I2C_ISER_NACK_EN	BIT(2)	/* Enable NACK DET IRQ */
3262306a36Sopenharmony_ci#define     ALTR_I2C_ISER_RXRDY_EN	BIT(1)	/* Enable RX Ready IRQ */
3362306a36Sopenharmony_ci#define     ALTR_I2C_ISER_TXRDY_EN	BIT(0)	/* Enable TX Ready IRQ */
3462306a36Sopenharmony_ci#define ALTR_I2C_ISR		0x10	/* Interrupt Status register */
3562306a36Sopenharmony_ci#define     ALTR_I2C_ISR_RXOF		BIT(4)	/* RX OVERFLOW IRQ */
3662306a36Sopenharmony_ci#define     ALTR_I2C_ISR_ARB		BIT(3)	/* ARB LOST IRQ */
3762306a36Sopenharmony_ci#define     ALTR_I2C_ISR_NACK		BIT(2)	/* NACK DET IRQ */
3862306a36Sopenharmony_ci#define     ALTR_I2C_ISR_RXRDY		BIT(1)	/* RX Ready IRQ */
3962306a36Sopenharmony_ci#define     ALTR_I2C_ISR_TXRDY		BIT(0)	/* TX Ready IRQ */
4062306a36Sopenharmony_ci#define ALTR_I2C_STATUS		0x14	/* Status register */
4162306a36Sopenharmony_ci#define     ALTR_I2C_STAT_CORE		BIT(0)	/* Core Status (0=idle) */
4262306a36Sopenharmony_ci#define ALTR_I2C_TC_FIFO_LVL	0x18	/* Transfer FIFO LVL register */
4362306a36Sopenharmony_ci#define ALTR_I2C_RX_FIFO_LVL	0x1C	/* Receive FIFO LVL register */
4462306a36Sopenharmony_ci#define ALTR_I2C_SCL_LOW	0x20	/* SCL low count register */
4562306a36Sopenharmony_ci#define ALTR_I2C_SCL_HIGH	0x24	/* SCL high count register */
4662306a36Sopenharmony_ci#define ALTR_I2C_SDA_HOLD	0x28	/* SDA hold count register */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define ALTR_I2C_ALL_IRQ	(ALTR_I2C_ISR_RXOF | ALTR_I2C_ISR_ARB | \
4962306a36Sopenharmony_ci				 ALTR_I2C_ISR_NACK | ALTR_I2C_ISR_RXRDY | \
5062306a36Sopenharmony_ci				 ALTR_I2C_ISR_TXRDY)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define ALTR_I2C_THRESHOLD	0	/* IRQ Threshold at 1 element */
5362306a36Sopenharmony_ci#define ALTR_I2C_DFLT_FIFO_SZ	4
5462306a36Sopenharmony_ci#define ALTR_I2C_TIMEOUT	100000	/* 100ms */
5562306a36Sopenharmony_ci#define ALTR_I2C_XFER_TIMEOUT	(msecs_to_jiffies(250))
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/**
5862306a36Sopenharmony_ci * struct altr_i2c_dev - I2C device context
5962306a36Sopenharmony_ci * @base: pointer to register struct
6062306a36Sopenharmony_ci * @msg: pointer to current message
6162306a36Sopenharmony_ci * @msg_len: number of bytes transferred in msg
6262306a36Sopenharmony_ci * @msg_err: error code for completed message
6362306a36Sopenharmony_ci * @msg_complete: xfer completion object
6462306a36Sopenharmony_ci * @dev: device reference
6562306a36Sopenharmony_ci * @adapter: core i2c abstraction
6662306a36Sopenharmony_ci * @i2c_clk: clock reference for i2c input clock
6762306a36Sopenharmony_ci * @bus_clk_rate: current i2c bus clock rate
6862306a36Sopenharmony_ci * @buf: ptr to msg buffer for easier use.
6962306a36Sopenharmony_ci * @fifo_size: size of the FIFO passed in.
7062306a36Sopenharmony_ci * @isr_mask: cached copy of local ISR enables.
7162306a36Sopenharmony_ci * @isr_status: cached copy of local ISR status.
7262306a36Sopenharmony_ci * @isr_mutex: mutex for IRQ thread.
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_cistruct altr_i2c_dev {
7562306a36Sopenharmony_ci	void __iomem *base;
7662306a36Sopenharmony_ci	struct i2c_msg *msg;
7762306a36Sopenharmony_ci	size_t msg_len;
7862306a36Sopenharmony_ci	int msg_err;
7962306a36Sopenharmony_ci	struct completion msg_complete;
8062306a36Sopenharmony_ci	struct device *dev;
8162306a36Sopenharmony_ci	struct i2c_adapter adapter;
8262306a36Sopenharmony_ci	struct clk *i2c_clk;
8362306a36Sopenharmony_ci	u32 bus_clk_rate;
8462306a36Sopenharmony_ci	u8 *buf;
8562306a36Sopenharmony_ci	u32 fifo_size;
8662306a36Sopenharmony_ci	u32 isr_mask;
8762306a36Sopenharmony_ci	u32 isr_status;
8862306a36Sopenharmony_ci	struct mutex isr_mutex;
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic void
9262306a36Sopenharmony_cialtr_i2c_int_enable(struct altr_i2c_dev *idev, u32 mask, bool enable)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	u32 int_en;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	int_en = readl(idev->base + ALTR_I2C_ISER);
9762306a36Sopenharmony_ci	if (enable)
9862306a36Sopenharmony_ci		idev->isr_mask = int_en | mask;
9962306a36Sopenharmony_ci	else
10062306a36Sopenharmony_ci		idev->isr_mask = int_en & ~mask;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	writel(idev->isr_mask, idev->base + ALTR_I2C_ISER);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic void altr_i2c_int_clear(struct altr_i2c_dev *idev, u32 mask)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	u32 int_en = readl(idev->base + ALTR_I2C_ISR);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	writel(int_en | mask, idev->base + ALTR_I2C_ISR);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic void altr_i2c_core_disable(struct altr_i2c_dev *idev)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	u32 tmp = readl(idev->base + ALTR_I2C_CTRL);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	writel(tmp & ~ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL);
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic void altr_i2c_core_enable(struct altr_i2c_dev *idev)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	u32 tmp = readl(idev->base + ALTR_I2C_CTRL);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	writel(tmp | ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic void altr_i2c_reset(struct altr_i2c_dev *idev)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	altr_i2c_core_disable(idev);
12962306a36Sopenharmony_ci	altr_i2c_core_enable(idev);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic inline void altr_i2c_stop(struct altr_i2c_dev *idev)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	writel(ALTR_I2C_TFR_CMD_STO, idev->base + ALTR_I2C_TFR_CMD);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic void altr_i2c_init(struct altr_i2c_dev *idev)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	u32 divisor = clk_get_rate(idev->i2c_clk) / idev->bus_clk_rate;
14062306a36Sopenharmony_ci	u32 clk_mhz = clk_get_rate(idev->i2c_clk) / 1000000;
14162306a36Sopenharmony_ci	u32 tmp = (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_RXT_SHFT) |
14262306a36Sopenharmony_ci		  (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_TCT_SHFT);
14362306a36Sopenharmony_ci	u32 t_high, t_low;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (idev->bus_clk_rate <= I2C_MAX_STANDARD_MODE_FREQ) {
14662306a36Sopenharmony_ci		tmp &= ~ALTR_I2C_CTRL_BSPEED;
14762306a36Sopenharmony_ci		/* Standard mode SCL 50/50 */
14862306a36Sopenharmony_ci		t_high = divisor * 1 / 2;
14962306a36Sopenharmony_ci		t_low = divisor * 1 / 2;
15062306a36Sopenharmony_ci	} else {
15162306a36Sopenharmony_ci		tmp |= ALTR_I2C_CTRL_BSPEED;
15262306a36Sopenharmony_ci		/* Fast mode SCL 33/66 */
15362306a36Sopenharmony_ci		t_high = divisor * 1 / 3;
15462306a36Sopenharmony_ci		t_low = divisor * 2 / 3;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci	writel(tmp, idev->base + ALTR_I2C_CTRL);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	dev_dbg(idev->dev, "rate=%uHz per_clk=%uMHz -> ratio=1:%u\n",
15962306a36Sopenharmony_ci		idev->bus_clk_rate, clk_mhz, divisor);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* Reset controller */
16262306a36Sopenharmony_ci	altr_i2c_reset(idev);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* SCL High Time */
16562306a36Sopenharmony_ci	writel(t_high, idev->base + ALTR_I2C_SCL_HIGH);
16662306a36Sopenharmony_ci	/* SCL Low Time */
16762306a36Sopenharmony_ci	writel(t_low, idev->base + ALTR_I2C_SCL_LOW);
16862306a36Sopenharmony_ci	/* SDA Hold Time, 300ns */
16962306a36Sopenharmony_ci	writel(3 * clk_mhz / 10, idev->base + ALTR_I2C_SDA_HOLD);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* Mask all master interrupt bits */
17262306a36Sopenharmony_ci	altr_i2c_int_enable(idev, ALTR_I2C_ALL_IRQ, false);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/*
17662306a36Sopenharmony_ci * altr_i2c_transfer - On the last byte to be transmitted, send
17762306a36Sopenharmony_ci * a Stop bit on the last byte.
17862306a36Sopenharmony_ci */
17962306a36Sopenharmony_cistatic void altr_i2c_transfer(struct altr_i2c_dev *idev, u32 data)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	/* On the last byte to be transmitted, send STOP */
18262306a36Sopenharmony_ci	if (idev->msg_len == 1)
18362306a36Sopenharmony_ci		data |= ALTR_I2C_TFR_CMD_STO;
18462306a36Sopenharmony_ci	if (idev->msg_len > 0)
18562306a36Sopenharmony_ci		writel(data, idev->base + ALTR_I2C_TFR_CMD);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/*
18962306a36Sopenharmony_ci * altr_i2c_empty_rx_fifo - Fetch data from RX FIFO until end of
19062306a36Sopenharmony_ci * transfer. Send a Stop bit on the last byte.
19162306a36Sopenharmony_ci */
19262306a36Sopenharmony_cistatic void altr_i2c_empty_rx_fifo(struct altr_i2c_dev *idev)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	size_t rx_fifo_avail = readl(idev->base + ALTR_I2C_RX_FIFO_LVL);
19562306a36Sopenharmony_ci	int bytes_to_transfer = min(rx_fifo_avail, idev->msg_len);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	while (bytes_to_transfer-- > 0) {
19862306a36Sopenharmony_ci		*idev->buf++ = readl(idev->base + ALTR_I2C_RX_DATA);
19962306a36Sopenharmony_ci		idev->msg_len--;
20062306a36Sopenharmony_ci		altr_i2c_transfer(idev, 0);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/*
20562306a36Sopenharmony_ci * altr_i2c_fill_tx_fifo - Fill TX FIFO from current message buffer.
20662306a36Sopenharmony_ci */
20762306a36Sopenharmony_cistatic int altr_i2c_fill_tx_fifo(struct altr_i2c_dev *idev)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	size_t tx_fifo_avail = idev->fifo_size - readl(idev->base +
21062306a36Sopenharmony_ci						       ALTR_I2C_TC_FIFO_LVL);
21162306a36Sopenharmony_ci	int bytes_to_transfer = min(tx_fifo_avail, idev->msg_len);
21262306a36Sopenharmony_ci	int ret = idev->msg_len - bytes_to_transfer;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	while (bytes_to_transfer-- > 0) {
21562306a36Sopenharmony_ci		altr_i2c_transfer(idev, *idev->buf++);
21662306a36Sopenharmony_ci		idev->msg_len--;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return ret;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic irqreturn_t altr_i2c_isr_quick(int irq, void *_dev)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct altr_i2c_dev *idev = _dev;
22562306a36Sopenharmony_ci	irqreturn_t ret = IRQ_HANDLED;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* Read IRQ status but only interested in Enabled IRQs. */
22862306a36Sopenharmony_ci	idev->isr_status = readl(idev->base + ALTR_I2C_ISR) & idev->isr_mask;
22962306a36Sopenharmony_ci	if (idev->isr_status)
23062306a36Sopenharmony_ci		ret = IRQ_WAKE_THREAD;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return ret;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic irqreturn_t altr_i2c_isr(int irq, void *_dev)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	int ret;
23862306a36Sopenharmony_ci	bool read, finish = false;
23962306a36Sopenharmony_ci	struct altr_i2c_dev *idev = _dev;
24062306a36Sopenharmony_ci	u32 status = idev->isr_status;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	mutex_lock(&idev->isr_mutex);
24362306a36Sopenharmony_ci	if (!idev->msg) {
24462306a36Sopenharmony_ci		dev_warn(idev->dev, "unexpected interrupt\n");
24562306a36Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ);
24662306a36Sopenharmony_ci		goto out;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci	read = (idev->msg->flags & I2C_M_RD) != 0;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* handle Lost Arbitration */
25162306a36Sopenharmony_ci	if (unlikely(status & ALTR_I2C_ISR_ARB)) {
25262306a36Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ISR_ARB);
25362306a36Sopenharmony_ci		idev->msg_err = -EAGAIN;
25462306a36Sopenharmony_ci		finish = true;
25562306a36Sopenharmony_ci	} else if (unlikely(status & ALTR_I2C_ISR_NACK)) {
25662306a36Sopenharmony_ci		dev_dbg(idev->dev, "Could not get ACK\n");
25762306a36Sopenharmony_ci		idev->msg_err = -ENXIO;
25862306a36Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ISR_NACK);
25962306a36Sopenharmony_ci		altr_i2c_stop(idev);
26062306a36Sopenharmony_ci		finish = true;
26162306a36Sopenharmony_ci	} else if (read && unlikely(status & ALTR_I2C_ISR_RXOF)) {
26262306a36Sopenharmony_ci		/* handle RX FIFO Overflow */
26362306a36Sopenharmony_ci		altr_i2c_empty_rx_fifo(idev);
26462306a36Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ISR_RXRDY);
26562306a36Sopenharmony_ci		altr_i2c_stop(idev);
26662306a36Sopenharmony_ci		dev_err(idev->dev, "RX FIFO Overflow\n");
26762306a36Sopenharmony_ci		finish = true;
26862306a36Sopenharmony_ci	} else if (read && (status & ALTR_I2C_ISR_RXRDY)) {
26962306a36Sopenharmony_ci		/* RX FIFO needs service? */
27062306a36Sopenharmony_ci		altr_i2c_empty_rx_fifo(idev);
27162306a36Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ISR_RXRDY);
27262306a36Sopenharmony_ci		if (!idev->msg_len)
27362306a36Sopenharmony_ci			finish = true;
27462306a36Sopenharmony_ci	} else if (!read && (status & ALTR_I2C_ISR_TXRDY)) {
27562306a36Sopenharmony_ci		/* TX FIFO needs service? */
27662306a36Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ISR_TXRDY);
27762306a36Sopenharmony_ci		if (idev->msg_len > 0)
27862306a36Sopenharmony_ci			altr_i2c_fill_tx_fifo(idev);
27962306a36Sopenharmony_ci		else
28062306a36Sopenharmony_ci			finish = true;
28162306a36Sopenharmony_ci	} else {
28262306a36Sopenharmony_ci		dev_warn(idev->dev, "Unexpected interrupt: 0x%x\n", status);
28362306a36Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ);
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (finish) {
28762306a36Sopenharmony_ci		/* Wait for the Core to finish */
28862306a36Sopenharmony_ci		ret = readl_poll_timeout_atomic(idev->base + ALTR_I2C_STATUS,
28962306a36Sopenharmony_ci						status,
29062306a36Sopenharmony_ci						!(status & ALTR_I2C_STAT_CORE),
29162306a36Sopenharmony_ci						1, ALTR_I2C_TIMEOUT);
29262306a36Sopenharmony_ci		if (ret)
29362306a36Sopenharmony_ci			dev_err(idev->dev, "message timeout\n");
29462306a36Sopenharmony_ci		altr_i2c_int_enable(idev, ALTR_I2C_ALL_IRQ, false);
29562306a36Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ);
29662306a36Sopenharmony_ci		complete(&idev->msg_complete);
29762306a36Sopenharmony_ci		dev_dbg(idev->dev, "Message Complete\n");
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ciout:
30062306a36Sopenharmony_ci	mutex_unlock(&idev->isr_mutex);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return IRQ_HANDLED;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic int altr_i2c_xfer_msg(struct altr_i2c_dev *idev, struct i2c_msg *msg)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	u32 imask = ALTR_I2C_ISR_RXOF | ALTR_I2C_ISR_ARB | ALTR_I2C_ISR_NACK;
30862306a36Sopenharmony_ci	unsigned long time_left;
30962306a36Sopenharmony_ci	u32 value;
31062306a36Sopenharmony_ci	u8 addr = i2c_8bit_addr_from_msg(msg);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	mutex_lock(&idev->isr_mutex);
31362306a36Sopenharmony_ci	idev->msg = msg;
31462306a36Sopenharmony_ci	idev->msg_len = msg->len;
31562306a36Sopenharmony_ci	idev->buf = msg->buf;
31662306a36Sopenharmony_ci	idev->msg_err = 0;
31762306a36Sopenharmony_ci	reinit_completion(&idev->msg_complete);
31862306a36Sopenharmony_ci	altr_i2c_core_enable(idev);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* Make sure RX FIFO is empty */
32162306a36Sopenharmony_ci	do {
32262306a36Sopenharmony_ci		readl(idev->base + ALTR_I2C_RX_DATA);
32362306a36Sopenharmony_ci	} while (readl(idev->base + ALTR_I2C_RX_FIFO_LVL));
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	writel(ALTR_I2C_TFR_CMD_STA | addr, idev->base + ALTR_I2C_TFR_CMD);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if ((msg->flags & I2C_M_RD) != 0) {
32862306a36Sopenharmony_ci		imask |= ALTR_I2C_ISER_RXOF_EN | ALTR_I2C_ISER_RXRDY_EN;
32962306a36Sopenharmony_ci		altr_i2c_int_enable(idev, imask, true);
33062306a36Sopenharmony_ci		/* write the first byte to start the RX */
33162306a36Sopenharmony_ci		altr_i2c_transfer(idev, 0);
33262306a36Sopenharmony_ci	} else {
33362306a36Sopenharmony_ci		imask |= ALTR_I2C_ISR_TXRDY;
33462306a36Sopenharmony_ci		altr_i2c_int_enable(idev, imask, true);
33562306a36Sopenharmony_ci		altr_i2c_fill_tx_fifo(idev);
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci	mutex_unlock(&idev->isr_mutex);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	time_left = wait_for_completion_timeout(&idev->msg_complete,
34062306a36Sopenharmony_ci						ALTR_I2C_XFER_TIMEOUT);
34162306a36Sopenharmony_ci	mutex_lock(&idev->isr_mutex);
34262306a36Sopenharmony_ci	altr_i2c_int_enable(idev, imask, false);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	value = readl(idev->base + ALTR_I2C_STATUS) & ALTR_I2C_STAT_CORE;
34562306a36Sopenharmony_ci	if (value)
34662306a36Sopenharmony_ci		dev_err(idev->dev, "Core Status not IDLE...\n");
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (time_left == 0) {
34962306a36Sopenharmony_ci		idev->msg_err = -ETIMEDOUT;
35062306a36Sopenharmony_ci		dev_dbg(idev->dev, "Transaction timed out.\n");
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	altr_i2c_core_disable(idev);
35462306a36Sopenharmony_ci	mutex_unlock(&idev->isr_mutex);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	return idev->msg_err;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic int
36062306a36Sopenharmony_cialtr_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct altr_i2c_dev *idev = i2c_get_adapdata(adap);
36362306a36Sopenharmony_ci	int i, ret;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	for (i = 0; i < num; i++) {
36662306a36Sopenharmony_ci		ret = altr_i2c_xfer_msg(idev, msgs++);
36762306a36Sopenharmony_ci		if (ret)
36862306a36Sopenharmony_ci			return ret;
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci	return num;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic u32 altr_i2c_func(struct i2c_adapter *adap)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic const struct i2c_algorithm altr_i2c_algo = {
37962306a36Sopenharmony_ci	.master_xfer = altr_i2c_xfer,
38062306a36Sopenharmony_ci	.functionality = altr_i2c_func,
38162306a36Sopenharmony_ci};
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic int altr_i2c_probe(struct platform_device *pdev)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct altr_i2c_dev *idev = NULL;
38662306a36Sopenharmony_ci	int irq, ret;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL);
38962306a36Sopenharmony_ci	if (!idev)
39062306a36Sopenharmony_ci		return -ENOMEM;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	idev->base = devm_platform_ioremap_resource(pdev, 0);
39362306a36Sopenharmony_ci	if (IS_ERR(idev->base))
39462306a36Sopenharmony_ci		return PTR_ERR(idev->base);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
39762306a36Sopenharmony_ci	if (irq < 0)
39862306a36Sopenharmony_ci		return irq;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	idev->i2c_clk = devm_clk_get(&pdev->dev, NULL);
40162306a36Sopenharmony_ci	if (IS_ERR(idev->i2c_clk)) {
40262306a36Sopenharmony_ci		dev_err(&pdev->dev, "missing clock\n");
40362306a36Sopenharmony_ci		return PTR_ERR(idev->i2c_clk);
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	idev->dev = &pdev->dev;
40762306a36Sopenharmony_ci	init_completion(&idev->msg_complete);
40862306a36Sopenharmony_ci	mutex_init(&idev->isr_mutex);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	ret = device_property_read_u32(idev->dev, "fifo-size",
41162306a36Sopenharmony_ci				       &idev->fifo_size);
41262306a36Sopenharmony_ci	if (ret) {
41362306a36Sopenharmony_ci		dev_err(&pdev->dev, "FIFO size set to default of %d\n",
41462306a36Sopenharmony_ci			ALTR_I2C_DFLT_FIFO_SZ);
41562306a36Sopenharmony_ci		idev->fifo_size = ALTR_I2C_DFLT_FIFO_SZ;
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	ret = device_property_read_u32(idev->dev, "clock-frequency",
41962306a36Sopenharmony_ci				       &idev->bus_clk_rate);
42062306a36Sopenharmony_ci	if (ret) {
42162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Default to 100kHz\n");
42262306a36Sopenharmony_ci		idev->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ;	/* default clock rate */
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (idev->bus_clk_rate > I2C_MAX_FAST_MODE_FREQ) {
42662306a36Sopenharmony_ci		dev_err(&pdev->dev, "invalid clock-frequency %d\n",
42762306a36Sopenharmony_ci			idev->bus_clk_rate);
42862306a36Sopenharmony_ci		return -EINVAL;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	ret = devm_request_threaded_irq(&pdev->dev, irq, altr_i2c_isr_quick,
43262306a36Sopenharmony_ci					altr_i2c_isr, IRQF_ONESHOT,
43362306a36Sopenharmony_ci					pdev->name, idev);
43462306a36Sopenharmony_ci	if (ret) {
43562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to claim IRQ %d\n", irq);
43662306a36Sopenharmony_ci		return ret;
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	ret = clk_prepare_enable(idev->i2c_clk);
44062306a36Sopenharmony_ci	if (ret) {
44162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable clock\n");
44262306a36Sopenharmony_ci		return ret;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	mutex_lock(&idev->isr_mutex);
44662306a36Sopenharmony_ci	altr_i2c_init(idev);
44762306a36Sopenharmony_ci	mutex_unlock(&idev->isr_mutex);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	i2c_set_adapdata(&idev->adapter, idev);
45062306a36Sopenharmony_ci	strscpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name));
45162306a36Sopenharmony_ci	idev->adapter.owner = THIS_MODULE;
45262306a36Sopenharmony_ci	idev->adapter.algo = &altr_i2c_algo;
45362306a36Sopenharmony_ci	idev->adapter.dev.parent = &pdev->dev;
45462306a36Sopenharmony_ci	idev->adapter.dev.of_node = pdev->dev.of_node;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	platform_set_drvdata(pdev, idev);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	ret = i2c_add_adapter(&idev->adapter);
45962306a36Sopenharmony_ci	if (ret) {
46062306a36Sopenharmony_ci		clk_disable_unprepare(idev->i2c_clk);
46162306a36Sopenharmony_ci		return ret;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci	dev_info(&pdev->dev, "Altera SoftIP I2C Probe Complete\n");
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	return 0;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic void altr_i2c_remove(struct platform_device *pdev)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	struct altr_i2c_dev *idev = platform_get_drvdata(pdev);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	clk_disable_unprepare(idev->i2c_clk);
47362306a36Sopenharmony_ci	i2c_del_adapter(&idev->adapter);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci/* Match table for of_platform binding */
47762306a36Sopenharmony_cistatic const struct of_device_id altr_i2c_of_match[] = {
47862306a36Sopenharmony_ci	{ .compatible = "altr,softip-i2c-v1.0" },
47962306a36Sopenharmony_ci	{},
48062306a36Sopenharmony_ci};
48162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, altr_i2c_of_match);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic struct platform_driver altr_i2c_driver = {
48462306a36Sopenharmony_ci	.probe = altr_i2c_probe,
48562306a36Sopenharmony_ci	.remove_new = altr_i2c_remove,
48662306a36Sopenharmony_ci	.driver = {
48762306a36Sopenharmony_ci		.name = "altera-i2c",
48862306a36Sopenharmony_ci		.of_match_table = altr_i2c_of_match,
48962306a36Sopenharmony_ci	},
49062306a36Sopenharmony_ci};
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cimodule_platform_driver(altr_i2c_driver);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ciMODULE_DESCRIPTION("Altera Soft IP I2C bus driver");
49562306a36Sopenharmony_ciMODULE_AUTHOR("Thor Thayer <thor.thayer@linux.intel.com>");
49662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
497