18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright Intel Corporation (C) 2017.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Based on the i2c-axxia.c driver.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/clk.h>
88c2ecf20Sopenharmony_ci#include <linux/clkdev.h>
98c2ecf20Sopenharmony_ci#include <linux/err.h>
108c2ecf20Sopenharmony_ci#include <linux/i2c.h>
118c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define ALTR_I2C_TFR_CMD	0x00	/* Transfer Command register */
198c2ecf20Sopenharmony_ci#define     ALTR_I2C_TFR_CMD_STA	BIT(9)	/* send START before byte */
208c2ecf20Sopenharmony_ci#define     ALTR_I2C_TFR_CMD_STO	BIT(8)	/* send STOP after byte */
218c2ecf20Sopenharmony_ci#define     ALTR_I2C_TFR_CMD_RW_D	BIT(0)	/* Direction of transfer */
228c2ecf20Sopenharmony_ci#define ALTR_I2C_RX_DATA	0x04	/* RX data FIFO register */
238c2ecf20Sopenharmony_ci#define ALTR_I2C_CTRL		0x08	/* Control register */
248c2ecf20Sopenharmony_ci#define     ALTR_I2C_CTRL_RXT_SHFT	4	/* RX FIFO Threshold */
258c2ecf20Sopenharmony_ci#define     ALTR_I2C_CTRL_TCT_SHFT	2	/* TFER CMD FIFO Threshold */
268c2ecf20Sopenharmony_ci#define     ALTR_I2C_CTRL_BSPEED	BIT(1)	/* Bus Speed (1=Fast) */
278c2ecf20Sopenharmony_ci#define     ALTR_I2C_CTRL_EN	BIT(0)	/* Enable Core (1=Enable) */
288c2ecf20Sopenharmony_ci#define ALTR_I2C_ISER		0x0C	/* Interrupt Status Enable register */
298c2ecf20Sopenharmony_ci#define     ALTR_I2C_ISER_RXOF_EN	BIT(4)	/* Enable RX OVERFLOW IRQ */
308c2ecf20Sopenharmony_ci#define     ALTR_I2C_ISER_ARB_EN	BIT(3)	/* Enable ARB LOST IRQ */
318c2ecf20Sopenharmony_ci#define     ALTR_I2C_ISER_NACK_EN	BIT(2)	/* Enable NACK DET IRQ */
328c2ecf20Sopenharmony_ci#define     ALTR_I2C_ISER_RXRDY_EN	BIT(1)	/* Enable RX Ready IRQ */
338c2ecf20Sopenharmony_ci#define     ALTR_I2C_ISER_TXRDY_EN	BIT(0)	/* Enable TX Ready IRQ */
348c2ecf20Sopenharmony_ci#define ALTR_I2C_ISR		0x10	/* Interrupt Status register */
358c2ecf20Sopenharmony_ci#define     ALTR_I2C_ISR_RXOF		BIT(4)	/* RX OVERFLOW IRQ */
368c2ecf20Sopenharmony_ci#define     ALTR_I2C_ISR_ARB		BIT(3)	/* ARB LOST IRQ */
378c2ecf20Sopenharmony_ci#define     ALTR_I2C_ISR_NACK		BIT(2)	/* NACK DET IRQ */
388c2ecf20Sopenharmony_ci#define     ALTR_I2C_ISR_RXRDY		BIT(1)	/* RX Ready IRQ */
398c2ecf20Sopenharmony_ci#define     ALTR_I2C_ISR_TXRDY		BIT(0)	/* TX Ready IRQ */
408c2ecf20Sopenharmony_ci#define ALTR_I2C_STATUS		0x14	/* Status register */
418c2ecf20Sopenharmony_ci#define     ALTR_I2C_STAT_CORE		BIT(0)	/* Core Status (0=idle) */
428c2ecf20Sopenharmony_ci#define ALTR_I2C_TC_FIFO_LVL	0x18	/* Transfer FIFO LVL register */
438c2ecf20Sopenharmony_ci#define ALTR_I2C_RX_FIFO_LVL	0x1C	/* Receive FIFO LVL register */
448c2ecf20Sopenharmony_ci#define ALTR_I2C_SCL_LOW	0x20	/* SCL low count register */
458c2ecf20Sopenharmony_ci#define ALTR_I2C_SCL_HIGH	0x24	/* SCL high count register */
468c2ecf20Sopenharmony_ci#define ALTR_I2C_SDA_HOLD	0x28	/* SDA hold count register */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define ALTR_I2C_ALL_IRQ	(ALTR_I2C_ISR_RXOF | ALTR_I2C_ISR_ARB | \
498c2ecf20Sopenharmony_ci				 ALTR_I2C_ISR_NACK | ALTR_I2C_ISR_RXRDY | \
508c2ecf20Sopenharmony_ci				 ALTR_I2C_ISR_TXRDY)
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define ALTR_I2C_THRESHOLD	0	/* IRQ Threshold at 1 element */
538c2ecf20Sopenharmony_ci#define ALTR_I2C_DFLT_FIFO_SZ	4
548c2ecf20Sopenharmony_ci#define ALTR_I2C_TIMEOUT	100000	/* 100ms */
558c2ecf20Sopenharmony_ci#define ALTR_I2C_XFER_TIMEOUT	(msecs_to_jiffies(250))
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/**
588c2ecf20Sopenharmony_ci * altr_i2c_dev - I2C device context
598c2ecf20Sopenharmony_ci * @base: pointer to register struct
608c2ecf20Sopenharmony_ci * @msg: pointer to current message
618c2ecf20Sopenharmony_ci * @msg_len: number of bytes transferred in msg
628c2ecf20Sopenharmony_ci * @msg_err: error code for completed message
638c2ecf20Sopenharmony_ci * @msg_complete: xfer completion object
648c2ecf20Sopenharmony_ci * @dev: device reference
658c2ecf20Sopenharmony_ci * @adapter: core i2c abstraction
668c2ecf20Sopenharmony_ci * @i2c_clk: clock reference for i2c input clock
678c2ecf20Sopenharmony_ci * @bus_clk_rate: current i2c bus clock rate
688c2ecf20Sopenharmony_ci * @buf: ptr to msg buffer for easier use.
698c2ecf20Sopenharmony_ci * @fifo_size: size of the FIFO passed in.
708c2ecf20Sopenharmony_ci * @isr_mask: cached copy of local ISR enables.
718c2ecf20Sopenharmony_ci * @isr_status: cached copy of local ISR status.
728c2ecf20Sopenharmony_ci * @isr_mutex: mutex for IRQ thread.
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_cistruct altr_i2c_dev {
758c2ecf20Sopenharmony_ci	void __iomem *base;
768c2ecf20Sopenharmony_ci	struct i2c_msg *msg;
778c2ecf20Sopenharmony_ci	size_t msg_len;
788c2ecf20Sopenharmony_ci	int msg_err;
798c2ecf20Sopenharmony_ci	struct completion msg_complete;
808c2ecf20Sopenharmony_ci	struct device *dev;
818c2ecf20Sopenharmony_ci	struct i2c_adapter adapter;
828c2ecf20Sopenharmony_ci	struct clk *i2c_clk;
838c2ecf20Sopenharmony_ci	u32 bus_clk_rate;
848c2ecf20Sopenharmony_ci	u8 *buf;
858c2ecf20Sopenharmony_ci	u32 fifo_size;
868c2ecf20Sopenharmony_ci	u32 isr_mask;
878c2ecf20Sopenharmony_ci	u32 isr_status;
888c2ecf20Sopenharmony_ci	struct mutex isr_mutex;
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void
928c2ecf20Sopenharmony_cialtr_i2c_int_enable(struct altr_i2c_dev *idev, u32 mask, bool enable)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	u32 int_en;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	int_en = readl(idev->base + ALTR_I2C_ISER);
978c2ecf20Sopenharmony_ci	if (enable)
988c2ecf20Sopenharmony_ci		idev->isr_mask = int_en | mask;
998c2ecf20Sopenharmony_ci	else
1008c2ecf20Sopenharmony_ci		idev->isr_mask = int_en & ~mask;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	writel(idev->isr_mask, idev->base + ALTR_I2C_ISER);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic void altr_i2c_int_clear(struct altr_i2c_dev *idev, u32 mask)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	u32 int_en = readl(idev->base + ALTR_I2C_ISR);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	writel(int_en | mask, idev->base + ALTR_I2C_ISR);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void altr_i2c_core_disable(struct altr_i2c_dev *idev)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	u32 tmp = readl(idev->base + ALTR_I2C_CTRL);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	writel(tmp & ~ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic void altr_i2c_core_enable(struct altr_i2c_dev *idev)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	u32 tmp = readl(idev->base + ALTR_I2C_CTRL);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	writel(tmp | ALTR_I2C_CTRL_EN, idev->base + ALTR_I2C_CTRL);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic void altr_i2c_reset(struct altr_i2c_dev *idev)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	altr_i2c_core_disable(idev);
1298c2ecf20Sopenharmony_ci	altr_i2c_core_enable(idev);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic inline void altr_i2c_stop(struct altr_i2c_dev *idev)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	writel(ALTR_I2C_TFR_CMD_STO, idev->base + ALTR_I2C_TFR_CMD);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic void altr_i2c_init(struct altr_i2c_dev *idev)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	u32 divisor = clk_get_rate(idev->i2c_clk) / idev->bus_clk_rate;
1408c2ecf20Sopenharmony_ci	u32 clk_mhz = clk_get_rate(idev->i2c_clk) / 1000000;
1418c2ecf20Sopenharmony_ci	u32 tmp = (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_RXT_SHFT) |
1428c2ecf20Sopenharmony_ci		  (ALTR_I2C_THRESHOLD << ALTR_I2C_CTRL_TCT_SHFT);
1438c2ecf20Sopenharmony_ci	u32 t_high, t_low;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (idev->bus_clk_rate <= I2C_MAX_STANDARD_MODE_FREQ) {
1468c2ecf20Sopenharmony_ci		tmp &= ~ALTR_I2C_CTRL_BSPEED;
1478c2ecf20Sopenharmony_ci		/* Standard mode SCL 50/50 */
1488c2ecf20Sopenharmony_ci		t_high = divisor * 1 / 2;
1498c2ecf20Sopenharmony_ci		t_low = divisor * 1 / 2;
1508c2ecf20Sopenharmony_ci	} else {
1518c2ecf20Sopenharmony_ci		tmp |= ALTR_I2C_CTRL_BSPEED;
1528c2ecf20Sopenharmony_ci		/* Fast mode SCL 33/66 */
1538c2ecf20Sopenharmony_ci		t_high = divisor * 1 / 3;
1548c2ecf20Sopenharmony_ci		t_low = divisor * 2 / 3;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci	writel(tmp, idev->base + ALTR_I2C_CTRL);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	dev_dbg(idev->dev, "rate=%uHz per_clk=%uMHz -> ratio=1:%u\n",
1598c2ecf20Sopenharmony_ci		idev->bus_clk_rate, clk_mhz, divisor);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* Reset controller */
1628c2ecf20Sopenharmony_ci	altr_i2c_reset(idev);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/* SCL High Time */
1658c2ecf20Sopenharmony_ci	writel(t_high, idev->base + ALTR_I2C_SCL_HIGH);
1668c2ecf20Sopenharmony_ci	/* SCL Low Time */
1678c2ecf20Sopenharmony_ci	writel(t_low, idev->base + ALTR_I2C_SCL_LOW);
1688c2ecf20Sopenharmony_ci	/* SDA Hold Time, 300ns */
1698c2ecf20Sopenharmony_ci	writel(3 * clk_mhz / 10, idev->base + ALTR_I2C_SDA_HOLD);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* Mask all master interrupt bits */
1728c2ecf20Sopenharmony_ci	altr_i2c_int_enable(idev, ALTR_I2C_ALL_IRQ, false);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/**
1768c2ecf20Sopenharmony_ci * altr_i2c_transfer - On the last byte to be transmitted, send
1778c2ecf20Sopenharmony_ci * a Stop bit on the last byte.
1788c2ecf20Sopenharmony_ci */
1798c2ecf20Sopenharmony_cistatic void altr_i2c_transfer(struct altr_i2c_dev *idev, u32 data)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	/* On the last byte to be transmitted, send STOP */
1828c2ecf20Sopenharmony_ci	if (idev->msg_len == 1)
1838c2ecf20Sopenharmony_ci		data |= ALTR_I2C_TFR_CMD_STO;
1848c2ecf20Sopenharmony_ci	if (idev->msg_len > 0)
1858c2ecf20Sopenharmony_ci		writel(data, idev->base + ALTR_I2C_TFR_CMD);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/**
1898c2ecf20Sopenharmony_ci * altr_i2c_empty_rx_fifo - Fetch data from RX FIFO until end of
1908c2ecf20Sopenharmony_ci * transfer. Send a Stop bit on the last byte.
1918c2ecf20Sopenharmony_ci */
1928c2ecf20Sopenharmony_cistatic void altr_i2c_empty_rx_fifo(struct altr_i2c_dev *idev)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	size_t rx_fifo_avail = readl(idev->base + ALTR_I2C_RX_FIFO_LVL);
1958c2ecf20Sopenharmony_ci	int bytes_to_transfer = min(rx_fifo_avail, idev->msg_len);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	while (bytes_to_transfer-- > 0) {
1988c2ecf20Sopenharmony_ci		*idev->buf++ = readl(idev->base + ALTR_I2C_RX_DATA);
1998c2ecf20Sopenharmony_ci		idev->msg_len--;
2008c2ecf20Sopenharmony_ci		altr_i2c_transfer(idev, 0);
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/**
2058c2ecf20Sopenharmony_ci * altr_i2c_fill_tx_fifo - Fill TX FIFO from current message buffer.
2068c2ecf20Sopenharmony_ci * @return: Number of bytes left to transfer.
2078c2ecf20Sopenharmony_ci */
2088c2ecf20Sopenharmony_cistatic int altr_i2c_fill_tx_fifo(struct altr_i2c_dev *idev)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	size_t tx_fifo_avail = idev->fifo_size - readl(idev->base +
2118c2ecf20Sopenharmony_ci						       ALTR_I2C_TC_FIFO_LVL);
2128c2ecf20Sopenharmony_ci	int bytes_to_transfer = min(tx_fifo_avail, idev->msg_len);
2138c2ecf20Sopenharmony_ci	int ret = idev->msg_len - bytes_to_transfer;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	while (bytes_to_transfer-- > 0) {
2168c2ecf20Sopenharmony_ci		altr_i2c_transfer(idev, *idev->buf++);
2178c2ecf20Sopenharmony_ci		idev->msg_len--;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return ret;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic irqreturn_t altr_i2c_isr_quick(int irq, void *_dev)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct altr_i2c_dev *idev = _dev;
2268c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_HANDLED;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* Read IRQ status but only interested in Enabled IRQs. */
2298c2ecf20Sopenharmony_ci	idev->isr_status = readl(idev->base + ALTR_I2C_ISR) & idev->isr_mask;
2308c2ecf20Sopenharmony_ci	if (idev->isr_status)
2318c2ecf20Sopenharmony_ci		ret = IRQ_WAKE_THREAD;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return ret;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic irqreturn_t altr_i2c_isr(int irq, void *_dev)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	int ret;
2398c2ecf20Sopenharmony_ci	bool read, finish = false;
2408c2ecf20Sopenharmony_ci	struct altr_i2c_dev *idev = _dev;
2418c2ecf20Sopenharmony_ci	u32 status = idev->isr_status;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	mutex_lock(&idev->isr_mutex);
2448c2ecf20Sopenharmony_ci	if (!idev->msg) {
2458c2ecf20Sopenharmony_ci		dev_warn(idev->dev, "unexpected interrupt\n");
2468c2ecf20Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ);
2478c2ecf20Sopenharmony_ci		goto out;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	read = (idev->msg->flags & I2C_M_RD) != 0;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/* handle Lost Arbitration */
2528c2ecf20Sopenharmony_ci	if (unlikely(status & ALTR_I2C_ISR_ARB)) {
2538c2ecf20Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ISR_ARB);
2548c2ecf20Sopenharmony_ci		idev->msg_err = -EAGAIN;
2558c2ecf20Sopenharmony_ci		finish = true;
2568c2ecf20Sopenharmony_ci	} else if (unlikely(status & ALTR_I2C_ISR_NACK)) {
2578c2ecf20Sopenharmony_ci		dev_dbg(idev->dev, "Could not get ACK\n");
2588c2ecf20Sopenharmony_ci		idev->msg_err = -ENXIO;
2598c2ecf20Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ISR_NACK);
2608c2ecf20Sopenharmony_ci		altr_i2c_stop(idev);
2618c2ecf20Sopenharmony_ci		finish = true;
2628c2ecf20Sopenharmony_ci	} else if (read && unlikely(status & ALTR_I2C_ISR_RXOF)) {
2638c2ecf20Sopenharmony_ci		/* handle RX FIFO Overflow */
2648c2ecf20Sopenharmony_ci		altr_i2c_empty_rx_fifo(idev);
2658c2ecf20Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ISR_RXRDY);
2668c2ecf20Sopenharmony_ci		altr_i2c_stop(idev);
2678c2ecf20Sopenharmony_ci		dev_err(idev->dev, "RX FIFO Overflow\n");
2688c2ecf20Sopenharmony_ci		finish = true;
2698c2ecf20Sopenharmony_ci	} else if (read && (status & ALTR_I2C_ISR_RXRDY)) {
2708c2ecf20Sopenharmony_ci		/* RX FIFO needs service? */
2718c2ecf20Sopenharmony_ci		altr_i2c_empty_rx_fifo(idev);
2728c2ecf20Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ISR_RXRDY);
2738c2ecf20Sopenharmony_ci		if (!idev->msg_len)
2748c2ecf20Sopenharmony_ci			finish = true;
2758c2ecf20Sopenharmony_ci	} else if (!read && (status & ALTR_I2C_ISR_TXRDY)) {
2768c2ecf20Sopenharmony_ci		/* TX FIFO needs service? */
2778c2ecf20Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ISR_TXRDY);
2788c2ecf20Sopenharmony_ci		if (idev->msg_len > 0)
2798c2ecf20Sopenharmony_ci			altr_i2c_fill_tx_fifo(idev);
2808c2ecf20Sopenharmony_ci		else
2818c2ecf20Sopenharmony_ci			finish = true;
2828c2ecf20Sopenharmony_ci	} else {
2838c2ecf20Sopenharmony_ci		dev_warn(idev->dev, "Unexpected interrupt: 0x%x\n", status);
2848c2ecf20Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ);
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (finish) {
2888c2ecf20Sopenharmony_ci		/* Wait for the Core to finish */
2898c2ecf20Sopenharmony_ci		ret = readl_poll_timeout_atomic(idev->base + ALTR_I2C_STATUS,
2908c2ecf20Sopenharmony_ci						status,
2918c2ecf20Sopenharmony_ci						!(status & ALTR_I2C_STAT_CORE),
2928c2ecf20Sopenharmony_ci						1, ALTR_I2C_TIMEOUT);
2938c2ecf20Sopenharmony_ci		if (ret)
2948c2ecf20Sopenharmony_ci			dev_err(idev->dev, "message timeout\n");
2958c2ecf20Sopenharmony_ci		altr_i2c_int_enable(idev, ALTR_I2C_ALL_IRQ, false);
2968c2ecf20Sopenharmony_ci		altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ);
2978c2ecf20Sopenharmony_ci		complete(&idev->msg_complete);
2988c2ecf20Sopenharmony_ci		dev_dbg(idev->dev, "Message Complete\n");
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ciout:
3018c2ecf20Sopenharmony_ci	mutex_unlock(&idev->isr_mutex);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic int altr_i2c_xfer_msg(struct altr_i2c_dev *idev, struct i2c_msg *msg)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	u32 imask = ALTR_I2C_ISR_RXOF | ALTR_I2C_ISR_ARB | ALTR_I2C_ISR_NACK;
3098c2ecf20Sopenharmony_ci	unsigned long time_left;
3108c2ecf20Sopenharmony_ci	u32 value;
3118c2ecf20Sopenharmony_ci	u8 addr = i2c_8bit_addr_from_msg(msg);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	mutex_lock(&idev->isr_mutex);
3148c2ecf20Sopenharmony_ci	idev->msg = msg;
3158c2ecf20Sopenharmony_ci	idev->msg_len = msg->len;
3168c2ecf20Sopenharmony_ci	idev->buf = msg->buf;
3178c2ecf20Sopenharmony_ci	idev->msg_err = 0;
3188c2ecf20Sopenharmony_ci	reinit_completion(&idev->msg_complete);
3198c2ecf20Sopenharmony_ci	altr_i2c_core_enable(idev);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/* Make sure RX FIFO is empty */
3228c2ecf20Sopenharmony_ci	do {
3238c2ecf20Sopenharmony_ci		readl(idev->base + ALTR_I2C_RX_DATA);
3248c2ecf20Sopenharmony_ci	} while (readl(idev->base + ALTR_I2C_RX_FIFO_LVL));
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	writel(ALTR_I2C_TFR_CMD_STA | addr, idev->base + ALTR_I2C_TFR_CMD);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if ((msg->flags & I2C_M_RD) != 0) {
3298c2ecf20Sopenharmony_ci		imask |= ALTR_I2C_ISER_RXOF_EN | ALTR_I2C_ISER_RXRDY_EN;
3308c2ecf20Sopenharmony_ci		altr_i2c_int_enable(idev, imask, true);
3318c2ecf20Sopenharmony_ci		/* write the first byte to start the RX */
3328c2ecf20Sopenharmony_ci		altr_i2c_transfer(idev, 0);
3338c2ecf20Sopenharmony_ci	} else {
3348c2ecf20Sopenharmony_ci		imask |= ALTR_I2C_ISR_TXRDY;
3358c2ecf20Sopenharmony_ci		altr_i2c_int_enable(idev, imask, true);
3368c2ecf20Sopenharmony_ci		altr_i2c_fill_tx_fifo(idev);
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci	mutex_unlock(&idev->isr_mutex);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	time_left = wait_for_completion_timeout(&idev->msg_complete,
3418c2ecf20Sopenharmony_ci						ALTR_I2C_XFER_TIMEOUT);
3428c2ecf20Sopenharmony_ci	mutex_lock(&idev->isr_mutex);
3438c2ecf20Sopenharmony_ci	altr_i2c_int_enable(idev, imask, false);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	value = readl(idev->base + ALTR_I2C_STATUS) & ALTR_I2C_STAT_CORE;
3468c2ecf20Sopenharmony_ci	if (value)
3478c2ecf20Sopenharmony_ci		dev_err(idev->dev, "Core Status not IDLE...\n");
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	if (time_left == 0) {
3508c2ecf20Sopenharmony_ci		idev->msg_err = -ETIMEDOUT;
3518c2ecf20Sopenharmony_ci		dev_dbg(idev->dev, "Transaction timed out.\n");
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	altr_i2c_core_disable(idev);
3558c2ecf20Sopenharmony_ci	mutex_unlock(&idev->isr_mutex);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return idev->msg_err;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int
3618c2ecf20Sopenharmony_cialtr_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct altr_i2c_dev *idev = i2c_get_adapdata(adap);
3648c2ecf20Sopenharmony_ci	int i, ret;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
3678c2ecf20Sopenharmony_ci		ret = altr_i2c_xfer_msg(idev, msgs++);
3688c2ecf20Sopenharmony_ci		if (ret)
3698c2ecf20Sopenharmony_ci			return ret;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci	return num;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic u32 altr_i2c_func(struct i2c_adapter *adap)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic const struct i2c_algorithm altr_i2c_algo = {
3808c2ecf20Sopenharmony_ci	.master_xfer = altr_i2c_xfer,
3818c2ecf20Sopenharmony_ci	.functionality = altr_i2c_func,
3828c2ecf20Sopenharmony_ci};
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic int altr_i2c_probe(struct platform_device *pdev)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	struct altr_i2c_dev *idev = NULL;
3878c2ecf20Sopenharmony_ci	int irq, ret;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL);
3908c2ecf20Sopenharmony_ci	if (!idev)
3918c2ecf20Sopenharmony_ci		return -ENOMEM;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	idev->base = devm_platform_ioremap_resource(pdev, 0);
3948c2ecf20Sopenharmony_ci	if (IS_ERR(idev->base))
3958c2ecf20Sopenharmony_ci		return PTR_ERR(idev->base);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
3988c2ecf20Sopenharmony_ci	if (irq < 0)
3998c2ecf20Sopenharmony_ci		return irq;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	idev->i2c_clk = devm_clk_get(&pdev->dev, NULL);
4028c2ecf20Sopenharmony_ci	if (IS_ERR(idev->i2c_clk)) {
4038c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "missing clock\n");
4048c2ecf20Sopenharmony_ci		return PTR_ERR(idev->i2c_clk);
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	idev->dev = &pdev->dev;
4088c2ecf20Sopenharmony_ci	init_completion(&idev->msg_complete);
4098c2ecf20Sopenharmony_ci	mutex_init(&idev->isr_mutex);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	ret = device_property_read_u32(idev->dev, "fifo-size",
4128c2ecf20Sopenharmony_ci				       &idev->fifo_size);
4138c2ecf20Sopenharmony_ci	if (ret) {
4148c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "FIFO size set to default of %d\n",
4158c2ecf20Sopenharmony_ci			ALTR_I2C_DFLT_FIFO_SZ);
4168c2ecf20Sopenharmony_ci		idev->fifo_size = ALTR_I2C_DFLT_FIFO_SZ;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	ret = device_property_read_u32(idev->dev, "clock-frequency",
4208c2ecf20Sopenharmony_ci				       &idev->bus_clk_rate);
4218c2ecf20Sopenharmony_ci	if (ret) {
4228c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Default to 100kHz\n");
4238c2ecf20Sopenharmony_ci		idev->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ;	/* default clock rate */
4248c2ecf20Sopenharmony_ci	}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (idev->bus_clk_rate > I2C_MAX_FAST_MODE_FREQ) {
4278c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "invalid clock-frequency %d\n",
4288c2ecf20Sopenharmony_ci			idev->bus_clk_rate);
4298c2ecf20Sopenharmony_ci		return -EINVAL;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(&pdev->dev, irq, altr_i2c_isr_quick,
4338c2ecf20Sopenharmony_ci					altr_i2c_isr, IRQF_ONESHOT,
4348c2ecf20Sopenharmony_ci					pdev->name, idev);
4358c2ecf20Sopenharmony_ci	if (ret) {
4368c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to claim IRQ %d\n", irq);
4378c2ecf20Sopenharmony_ci		return ret;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(idev->i2c_clk);
4418c2ecf20Sopenharmony_ci	if (ret) {
4428c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable clock\n");
4438c2ecf20Sopenharmony_ci		return ret;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	mutex_lock(&idev->isr_mutex);
4478c2ecf20Sopenharmony_ci	altr_i2c_init(idev);
4488c2ecf20Sopenharmony_ci	mutex_unlock(&idev->isr_mutex);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	i2c_set_adapdata(&idev->adapter, idev);
4518c2ecf20Sopenharmony_ci	strlcpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name));
4528c2ecf20Sopenharmony_ci	idev->adapter.owner = THIS_MODULE;
4538c2ecf20Sopenharmony_ci	idev->adapter.algo = &altr_i2c_algo;
4548c2ecf20Sopenharmony_ci	idev->adapter.dev.parent = &pdev->dev;
4558c2ecf20Sopenharmony_ci	idev->adapter.dev.of_node = pdev->dev.of_node;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, idev);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	ret = i2c_add_adapter(&idev->adapter);
4608c2ecf20Sopenharmony_ci	if (ret) {
4618c2ecf20Sopenharmony_ci		clk_disable_unprepare(idev->i2c_clk);
4628c2ecf20Sopenharmony_ci		return ret;
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "Altera SoftIP I2C Probe Complete\n");
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	return 0;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic int altr_i2c_remove(struct platform_device *pdev)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	struct altr_i2c_dev *idev = platform_get_drvdata(pdev);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	clk_disable_unprepare(idev->i2c_clk);
4748c2ecf20Sopenharmony_ci	i2c_del_adapter(&idev->adapter);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	return 0;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci/* Match table for of_platform binding */
4808c2ecf20Sopenharmony_cistatic const struct of_device_id altr_i2c_of_match[] = {
4818c2ecf20Sopenharmony_ci	{ .compatible = "altr,softip-i2c-v1.0" },
4828c2ecf20Sopenharmony_ci	{},
4838c2ecf20Sopenharmony_ci};
4848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, altr_i2c_of_match);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic struct platform_driver altr_i2c_driver = {
4878c2ecf20Sopenharmony_ci	.probe = altr_i2c_probe,
4888c2ecf20Sopenharmony_ci	.remove = altr_i2c_remove,
4898c2ecf20Sopenharmony_ci	.driver = {
4908c2ecf20Sopenharmony_ci		.name = "altera-i2c",
4918c2ecf20Sopenharmony_ci		.of_match_table = altr_i2c_of_match,
4928c2ecf20Sopenharmony_ci	},
4938c2ecf20Sopenharmony_ci};
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cimodule_platform_driver(altr_i2c_driver);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Altera Soft IP I2C bus driver");
4988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thor Thayer <thor.thayer@linux.intel.com>");
4998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
500