18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2011 NXP Semiconductors
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Code portions referenced from the i2x-pxa and i2c-pnx drivers
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Make SMBus byte and word transactions work on LPC178x/7x
88c2ecf20Sopenharmony_ci * Copyright (c) 2012
98c2ecf20Sopenharmony_ci * Alexander Potashev, Emcraft Systems, aspotashev@emcraft.com
108c2ecf20Sopenharmony_ci * Anton Protopopov, Emcraft Systems, antonp@emcraft.com
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/clk.h>
168c2ecf20Sopenharmony_ci#include <linux/errno.h>
178c2ecf20Sopenharmony_ci#include <linux/i2c.h>
188c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <linux/module.h>
228c2ecf20Sopenharmony_ci#include <linux/of.h>
238c2ecf20Sopenharmony_ci#include <linux/of_device.h>
248c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
258c2ecf20Sopenharmony_ci#include <linux/sched.h>
268c2ecf20Sopenharmony_ci#include <linux/time.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* LPC24xx register offsets and bits */
298c2ecf20Sopenharmony_ci#define LPC24XX_I2CONSET	0x00
308c2ecf20Sopenharmony_ci#define LPC24XX_I2STAT		0x04
318c2ecf20Sopenharmony_ci#define LPC24XX_I2DAT		0x08
328c2ecf20Sopenharmony_ci#define LPC24XX_I2ADDR		0x0c
338c2ecf20Sopenharmony_ci#define LPC24XX_I2SCLH		0x10
348c2ecf20Sopenharmony_ci#define LPC24XX_I2SCLL		0x14
358c2ecf20Sopenharmony_ci#define LPC24XX_I2CONCLR	0x18
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define LPC24XX_AA		BIT(2)
388c2ecf20Sopenharmony_ci#define LPC24XX_SI		BIT(3)
398c2ecf20Sopenharmony_ci#define LPC24XX_STO		BIT(4)
408c2ecf20Sopenharmony_ci#define LPC24XX_STA		BIT(5)
418c2ecf20Sopenharmony_ci#define LPC24XX_I2EN		BIT(6)
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define LPC24XX_STO_AA		(LPC24XX_STO | LPC24XX_AA)
448c2ecf20Sopenharmony_ci#define LPC24XX_CLEAR_ALL	(LPC24XX_AA | LPC24XX_SI | LPC24XX_STO | \
458c2ecf20Sopenharmony_ci				 LPC24XX_STA | LPC24XX_I2EN)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* I2C SCL clock has different duty cycle depending on mode */
488c2ecf20Sopenharmony_ci#define I2C_STD_MODE_DUTY		46
498c2ecf20Sopenharmony_ci#define I2C_FAST_MODE_DUTY		36
508c2ecf20Sopenharmony_ci#define I2C_FAST_MODE_PLUS_DUTY		38
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/*
538c2ecf20Sopenharmony_ci * 26 possible I2C status codes, but codes applicable only
548c2ecf20Sopenharmony_ci * to master are listed here and used in this driver
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_cienum {
578c2ecf20Sopenharmony_ci	M_BUS_ERROR		= 0x00,
588c2ecf20Sopenharmony_ci	M_START			= 0x08,
598c2ecf20Sopenharmony_ci	M_REPSTART		= 0x10,
608c2ecf20Sopenharmony_ci	MX_ADDR_W_ACK		= 0x18,
618c2ecf20Sopenharmony_ci	MX_ADDR_W_NACK		= 0x20,
628c2ecf20Sopenharmony_ci	MX_DATA_W_ACK		= 0x28,
638c2ecf20Sopenharmony_ci	MX_DATA_W_NACK		= 0x30,
648c2ecf20Sopenharmony_ci	M_DATA_ARB_LOST		= 0x38,
658c2ecf20Sopenharmony_ci	MR_ADDR_R_ACK		= 0x40,
668c2ecf20Sopenharmony_ci	MR_ADDR_R_NACK		= 0x48,
678c2ecf20Sopenharmony_ci	MR_DATA_R_ACK		= 0x50,
688c2ecf20Sopenharmony_ci	MR_DATA_R_NACK		= 0x58,
698c2ecf20Sopenharmony_ci	M_I2C_IDLE		= 0xf8,
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct lpc2k_i2c {
738c2ecf20Sopenharmony_ci	void __iomem		*base;
748c2ecf20Sopenharmony_ci	struct clk		*clk;
758c2ecf20Sopenharmony_ci	int			irq;
768c2ecf20Sopenharmony_ci	wait_queue_head_t	wait;
778c2ecf20Sopenharmony_ci	struct i2c_adapter	adap;
788c2ecf20Sopenharmony_ci	struct i2c_msg		*msg;
798c2ecf20Sopenharmony_ci	int			msg_idx;
808c2ecf20Sopenharmony_ci	int			msg_status;
818c2ecf20Sopenharmony_ci	int			is_last;
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic void i2c_lpc2k_reset(struct lpc2k_i2c *i2c)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	/* Will force clear all statuses */
878c2ecf20Sopenharmony_ci	writel(LPC24XX_CLEAR_ALL, i2c->base + LPC24XX_I2CONCLR);
888c2ecf20Sopenharmony_ci	writel(0, i2c->base + LPC24XX_I2ADDR);
898c2ecf20Sopenharmony_ci	writel(LPC24XX_I2EN, i2c->base + LPC24XX_I2CONSET);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int i2c_lpc2k_clear_arb(struct lpc2k_i2c *i2c)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/*
978c2ecf20Sopenharmony_ci	 * If the transfer needs to abort for some reason, we'll try to
988c2ecf20Sopenharmony_ci	 * force a stop condition to clear any pending bus conditions
998c2ecf20Sopenharmony_ci	 */
1008c2ecf20Sopenharmony_ci	writel(LPC24XX_STO, i2c->base + LPC24XX_I2CONSET);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* Wait for status change */
1038c2ecf20Sopenharmony_ci	while (readl(i2c->base + LPC24XX_I2STAT) != M_I2C_IDLE) {
1048c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout)) {
1058c2ecf20Sopenharmony_ci			/* Bus was not idle, try to reset adapter */
1068c2ecf20Sopenharmony_ci			i2c_lpc2k_reset(i2c);
1078c2ecf20Sopenharmony_ci			return -EBUSY;
1088c2ecf20Sopenharmony_ci		}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		cpu_relax();
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return 0;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic void i2c_lpc2k_pump_msg(struct lpc2k_i2c *i2c)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	unsigned char data;
1198c2ecf20Sopenharmony_ci	u32 status;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/*
1228c2ecf20Sopenharmony_ci	 * I2C in the LPC2xxx series is basically a state machine.
1238c2ecf20Sopenharmony_ci	 * Just run through the steps based on the current status.
1248c2ecf20Sopenharmony_ci	 */
1258c2ecf20Sopenharmony_ci	status = readl(i2c->base + LPC24XX_I2STAT);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	switch (status) {
1288c2ecf20Sopenharmony_ci	case M_START:
1298c2ecf20Sopenharmony_ci	case M_REPSTART:
1308c2ecf20Sopenharmony_ci		/* Start bit was just sent out, send out addr and dir */
1318c2ecf20Sopenharmony_ci		data = i2c_8bit_addr_from_msg(i2c->msg);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		writel(data, i2c->base + LPC24XX_I2DAT);
1348c2ecf20Sopenharmony_ci		writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONCLR);
1358c2ecf20Sopenharmony_ci		break;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	case MX_ADDR_W_ACK:
1388c2ecf20Sopenharmony_ci	case MX_DATA_W_ACK:
1398c2ecf20Sopenharmony_ci		/*
1408c2ecf20Sopenharmony_ci		 * Address or data was sent out with an ACK. If there is more
1418c2ecf20Sopenharmony_ci		 * data to send, send it now
1428c2ecf20Sopenharmony_ci		 */
1438c2ecf20Sopenharmony_ci		if (i2c->msg_idx < i2c->msg->len) {
1448c2ecf20Sopenharmony_ci			writel(i2c->msg->buf[i2c->msg_idx],
1458c2ecf20Sopenharmony_ci			       i2c->base + LPC24XX_I2DAT);
1468c2ecf20Sopenharmony_ci		} else if (i2c->is_last) {
1478c2ecf20Sopenharmony_ci			/* Last message, send stop */
1488c2ecf20Sopenharmony_ci			writel(LPC24XX_STO_AA, i2c->base + LPC24XX_I2CONSET);
1498c2ecf20Sopenharmony_ci			writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR);
1508c2ecf20Sopenharmony_ci			i2c->msg_status = 0;
1518c2ecf20Sopenharmony_ci			disable_irq_nosync(i2c->irq);
1528c2ecf20Sopenharmony_ci		} else {
1538c2ecf20Sopenharmony_ci			i2c->msg_status = 0;
1548c2ecf20Sopenharmony_ci			disable_irq_nosync(i2c->irq);
1558c2ecf20Sopenharmony_ci		}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		i2c->msg_idx++;
1588c2ecf20Sopenharmony_ci		break;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	case MR_ADDR_R_ACK:
1618c2ecf20Sopenharmony_ci		/* Receive first byte from slave */
1628c2ecf20Sopenharmony_ci		if (i2c->msg->len == 1) {
1638c2ecf20Sopenharmony_ci			/* Last byte, return NACK */
1648c2ecf20Sopenharmony_ci			writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONCLR);
1658c2ecf20Sopenharmony_ci		} else {
1668c2ecf20Sopenharmony_ci			/* Not last byte, return ACK */
1678c2ecf20Sopenharmony_ci			writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONSET);
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONCLR);
1718c2ecf20Sopenharmony_ci		break;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	case MR_DATA_R_NACK:
1748c2ecf20Sopenharmony_ci		/*
1758c2ecf20Sopenharmony_ci		 * The I2C shows NACK status on reads, so we need to accept
1768c2ecf20Sopenharmony_ci		 * the NACK as an ACK here. This should be ok, as the real
1778c2ecf20Sopenharmony_ci		 * BACK would of been caught on the address write.
1788c2ecf20Sopenharmony_ci		 */
1798c2ecf20Sopenharmony_ci	case MR_DATA_R_ACK:
1808c2ecf20Sopenharmony_ci		/* Data was received */
1818c2ecf20Sopenharmony_ci		if (i2c->msg_idx < i2c->msg->len) {
1828c2ecf20Sopenharmony_ci			i2c->msg->buf[i2c->msg_idx] =
1838c2ecf20Sopenharmony_ci					readl(i2c->base + LPC24XX_I2DAT);
1848c2ecf20Sopenharmony_ci		}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		/* If transfer is done, send STOP */
1878c2ecf20Sopenharmony_ci		if (i2c->msg_idx >= i2c->msg->len - 1 && i2c->is_last) {
1888c2ecf20Sopenharmony_ci			writel(LPC24XX_STO_AA, i2c->base + LPC24XX_I2CONSET);
1898c2ecf20Sopenharmony_ci			writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR);
1908c2ecf20Sopenharmony_ci			i2c->msg_status = 0;
1918c2ecf20Sopenharmony_ci		}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		/* Message is done */
1948c2ecf20Sopenharmony_ci		if (i2c->msg_idx >= i2c->msg->len - 1) {
1958c2ecf20Sopenharmony_ci			i2c->msg_status = 0;
1968c2ecf20Sopenharmony_ci			disable_irq_nosync(i2c->irq);
1978c2ecf20Sopenharmony_ci		}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci		/*
2008c2ecf20Sopenharmony_ci		 * One pre-last data input, send NACK to tell the slave that
2018c2ecf20Sopenharmony_ci		 * this is going to be the last data byte to be transferred.
2028c2ecf20Sopenharmony_ci		 */
2038c2ecf20Sopenharmony_ci		if (i2c->msg_idx >= i2c->msg->len - 2) {
2048c2ecf20Sopenharmony_ci			/* One byte left to receive - NACK */
2058c2ecf20Sopenharmony_ci			writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONCLR);
2068c2ecf20Sopenharmony_ci		} else {
2078c2ecf20Sopenharmony_ci			/* More than one byte left to receive - ACK */
2088c2ecf20Sopenharmony_ci			writel(LPC24XX_AA, i2c->base + LPC24XX_I2CONSET);
2098c2ecf20Sopenharmony_ci		}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONCLR);
2128c2ecf20Sopenharmony_ci		i2c->msg_idx++;
2138c2ecf20Sopenharmony_ci		break;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	case MX_ADDR_W_NACK:
2168c2ecf20Sopenharmony_ci	case MX_DATA_W_NACK:
2178c2ecf20Sopenharmony_ci	case MR_ADDR_R_NACK:
2188c2ecf20Sopenharmony_ci		/* NACK processing is done */
2198c2ecf20Sopenharmony_ci		writel(LPC24XX_STO_AA, i2c->base + LPC24XX_I2CONSET);
2208c2ecf20Sopenharmony_ci		i2c->msg_status = -ENXIO;
2218c2ecf20Sopenharmony_ci		disable_irq_nosync(i2c->irq);
2228c2ecf20Sopenharmony_ci		break;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	case M_DATA_ARB_LOST:
2258c2ecf20Sopenharmony_ci		/* Arbitration lost */
2268c2ecf20Sopenharmony_ci		i2c->msg_status = -EAGAIN;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		/* Release the I2C bus */
2298c2ecf20Sopenharmony_ci		writel(LPC24XX_STA | LPC24XX_STO, i2c->base + LPC24XX_I2CONCLR);
2308c2ecf20Sopenharmony_ci		disable_irq_nosync(i2c->irq);
2318c2ecf20Sopenharmony_ci		break;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	default:
2348c2ecf20Sopenharmony_ci		/* Unexpected statuses */
2358c2ecf20Sopenharmony_ci		i2c->msg_status = -EIO;
2368c2ecf20Sopenharmony_ci		disable_irq_nosync(i2c->irq);
2378c2ecf20Sopenharmony_ci		break;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* Exit on failure or all bytes transferred */
2418c2ecf20Sopenharmony_ci	if (i2c->msg_status != -EBUSY)
2428c2ecf20Sopenharmony_ci		wake_up(&i2c->wait);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/*
2458c2ecf20Sopenharmony_ci	 * If `msg_status` is zero, then `lpc2k_process_msg()`
2468c2ecf20Sopenharmony_ci	 * is responsible for clearing the SI flag.
2478c2ecf20Sopenharmony_ci	 */
2488c2ecf20Sopenharmony_ci	if (i2c->msg_status != 0)
2498c2ecf20Sopenharmony_ci		writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR);
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic int lpc2k_process_msg(struct lpc2k_i2c *i2c, int msgidx)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	/* A new transfer is kicked off by initiating a start condition */
2558c2ecf20Sopenharmony_ci	if (!msgidx) {
2568c2ecf20Sopenharmony_ci		writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONSET);
2578c2ecf20Sopenharmony_ci	} else {
2588c2ecf20Sopenharmony_ci		/*
2598c2ecf20Sopenharmony_ci		 * A multi-message I2C transfer continues where the
2608c2ecf20Sopenharmony_ci		 * previous I2C transfer left off and uses the
2618c2ecf20Sopenharmony_ci		 * current condition of the I2C adapter.
2628c2ecf20Sopenharmony_ci		 */
2638c2ecf20Sopenharmony_ci		if (unlikely(i2c->msg->flags & I2C_M_NOSTART)) {
2648c2ecf20Sopenharmony_ci			WARN_ON(i2c->msg->len == 0);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci			if (!(i2c->msg->flags & I2C_M_RD)) {
2678c2ecf20Sopenharmony_ci				/* Start transmit of data */
2688c2ecf20Sopenharmony_ci				writel(i2c->msg->buf[0],
2698c2ecf20Sopenharmony_ci				       i2c->base + LPC24XX_I2DAT);
2708c2ecf20Sopenharmony_ci				i2c->msg_idx++;
2718c2ecf20Sopenharmony_ci			}
2728c2ecf20Sopenharmony_ci		} else {
2738c2ecf20Sopenharmony_ci			/* Start or repeated start */
2748c2ecf20Sopenharmony_ci			writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONSET);
2758c2ecf20Sopenharmony_ci		}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		writel(LPC24XX_SI, i2c->base + LPC24XX_I2CONCLR);
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	enable_irq(i2c->irq);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	/* Wait for transfer completion */
2838c2ecf20Sopenharmony_ci	if (wait_event_timeout(i2c->wait, i2c->msg_status != -EBUSY,
2848c2ecf20Sopenharmony_ci			       msecs_to_jiffies(1000)) == 0) {
2858c2ecf20Sopenharmony_ci		disable_irq_nosync(i2c->irq);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	return i2c->msg_status;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic int i2c_lpc2k_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
2948c2ecf20Sopenharmony_ci			  int msg_num)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct lpc2k_i2c *i2c = i2c_get_adapdata(adap);
2978c2ecf20Sopenharmony_ci	int ret, i;
2988c2ecf20Sopenharmony_ci	u32 stat;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* Check for bus idle condition */
3018c2ecf20Sopenharmony_ci	stat = readl(i2c->base + LPC24XX_I2STAT);
3028c2ecf20Sopenharmony_ci	if (stat != M_I2C_IDLE) {
3038c2ecf20Sopenharmony_ci		/* Something is holding the bus, try to clear it */
3048c2ecf20Sopenharmony_ci		return i2c_lpc2k_clear_arb(i2c);
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/* Process a single message at a time */
3088c2ecf20Sopenharmony_ci	for (i = 0; i < msg_num; i++) {
3098c2ecf20Sopenharmony_ci		/* Save message pointer and current message data index */
3108c2ecf20Sopenharmony_ci		i2c->msg = &msgs[i];
3118c2ecf20Sopenharmony_ci		i2c->msg_idx = 0;
3128c2ecf20Sopenharmony_ci		i2c->msg_status = -EBUSY;
3138c2ecf20Sopenharmony_ci		i2c->is_last = (i == (msg_num - 1));
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		ret = lpc2k_process_msg(i2c, i);
3168c2ecf20Sopenharmony_ci		if (ret)
3178c2ecf20Sopenharmony_ci			return ret;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	return msg_num;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic irqreturn_t i2c_lpc2k_handler(int irq, void *dev_id)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct lpc2k_i2c *i2c = dev_id;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (readl(i2c->base + LPC24XX_I2CONSET) & LPC24XX_SI) {
3288c2ecf20Sopenharmony_ci		i2c_lpc2k_pump_msg(i2c);
3298c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return IRQ_NONE;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic u32 i2c_lpc2k_functionality(struct i2c_adapter *adap)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	/* Only emulated SMBus for now */
3388c2ecf20Sopenharmony_ci	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic const struct i2c_algorithm i2c_lpc2k_algorithm = {
3428c2ecf20Sopenharmony_ci	.master_xfer	= i2c_lpc2k_xfer,
3438c2ecf20Sopenharmony_ci	.functionality	= i2c_lpc2k_functionality,
3448c2ecf20Sopenharmony_ci};
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic int i2c_lpc2k_probe(struct platform_device *pdev)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct lpc2k_i2c *i2c;
3498c2ecf20Sopenharmony_ci	u32 bus_clk_rate;
3508c2ecf20Sopenharmony_ci	u32 scl_high;
3518c2ecf20Sopenharmony_ci	u32 clkrate;
3528c2ecf20Sopenharmony_ci	int ret;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
3558c2ecf20Sopenharmony_ci	if (!i2c)
3568c2ecf20Sopenharmony_ci		return -ENOMEM;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	i2c->base = devm_platform_ioremap_resource(pdev, 0);
3598c2ecf20Sopenharmony_ci	if (IS_ERR(i2c->base))
3608c2ecf20Sopenharmony_ci		return PTR_ERR(i2c->base);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	i2c->irq = platform_get_irq(pdev, 0);
3638c2ecf20Sopenharmony_ci	if (i2c->irq < 0)
3648c2ecf20Sopenharmony_ci		return i2c->irq;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	init_waitqueue_head(&i2c->wait);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	i2c->clk = devm_clk_get(&pdev->dev, NULL);
3698c2ecf20Sopenharmony_ci	if (IS_ERR(i2c->clk)) {
3708c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "error getting clock\n");
3718c2ecf20Sopenharmony_ci		return PTR_ERR(i2c->clk);
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(i2c->clk);
3758c2ecf20Sopenharmony_ci	if (ret) {
3768c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "unable to enable clock.\n");
3778c2ecf20Sopenharmony_ci		return ret;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, i2c->irq, i2c_lpc2k_handler, 0,
3818c2ecf20Sopenharmony_ci			       dev_name(&pdev->dev), i2c);
3828c2ecf20Sopenharmony_ci	if (ret < 0) {
3838c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "can't request interrupt.\n");
3848c2ecf20Sopenharmony_ci		goto fail_clk;
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	disable_irq_nosync(i2c->irq);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* Place controller is a known state */
3908c2ecf20Sopenharmony_ci	i2c_lpc2k_reset(i2c);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
3938c2ecf20Sopenharmony_ci				   &bus_clk_rate);
3948c2ecf20Sopenharmony_ci	if (ret)
3958c2ecf20Sopenharmony_ci		bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	clkrate = clk_get_rate(i2c->clk);
3988c2ecf20Sopenharmony_ci	if (clkrate == 0) {
3998c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "can't get I2C base clock\n");
4008c2ecf20Sopenharmony_ci		ret = -EINVAL;
4018c2ecf20Sopenharmony_ci		goto fail_clk;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* Setup I2C dividers to generate clock with proper duty cycle */
4058c2ecf20Sopenharmony_ci	clkrate = clkrate / bus_clk_rate;
4068c2ecf20Sopenharmony_ci	if (bus_clk_rate <= I2C_MAX_STANDARD_MODE_FREQ)
4078c2ecf20Sopenharmony_ci		scl_high = (clkrate * I2C_STD_MODE_DUTY) / 100;
4088c2ecf20Sopenharmony_ci	else if (bus_clk_rate <= I2C_MAX_FAST_MODE_FREQ)
4098c2ecf20Sopenharmony_ci		scl_high = (clkrate * I2C_FAST_MODE_DUTY) / 100;
4108c2ecf20Sopenharmony_ci	else
4118c2ecf20Sopenharmony_ci		scl_high = (clkrate * I2C_FAST_MODE_PLUS_DUTY) / 100;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	writel(scl_high, i2c->base + LPC24XX_I2SCLH);
4148c2ecf20Sopenharmony_ci	writel(clkrate - scl_high, i2c->base + LPC24XX_I2SCLL);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, i2c);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	i2c_set_adapdata(&i2c->adap, i2c);
4198c2ecf20Sopenharmony_ci	i2c->adap.owner = THIS_MODULE;
4208c2ecf20Sopenharmony_ci	strlcpy(i2c->adap.name, "LPC2K I2C adapter", sizeof(i2c->adap.name));
4218c2ecf20Sopenharmony_ci	i2c->adap.algo = &i2c_lpc2k_algorithm;
4228c2ecf20Sopenharmony_ci	i2c->adap.dev.parent = &pdev->dev;
4238c2ecf20Sopenharmony_ci	i2c->adap.dev.of_node = pdev->dev.of_node;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	ret = i2c_add_adapter(&i2c->adap);
4268c2ecf20Sopenharmony_ci	if (ret < 0)
4278c2ecf20Sopenharmony_ci		goto fail_clk;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "LPC2K I2C adapter\n");
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return 0;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cifail_clk:
4348c2ecf20Sopenharmony_ci	clk_disable_unprepare(i2c->clk);
4358c2ecf20Sopenharmony_ci	return ret;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic int i2c_lpc2k_remove(struct platform_device *dev)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct lpc2k_i2c *i2c = platform_get_drvdata(dev);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	i2c_del_adapter(&i2c->adap);
4438c2ecf20Sopenharmony_ci	clk_disable_unprepare(i2c->clk);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	return 0;
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4498c2ecf20Sopenharmony_cistatic int i2c_lpc2k_suspend(struct device *dev)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	struct lpc2k_i2c *i2c = dev_get_drvdata(dev);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	clk_disable(i2c->clk);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	return 0;
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic int i2c_lpc2k_resume(struct device *dev)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct lpc2k_i2c *i2c = dev_get_drvdata(dev);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	clk_enable(i2c->clk);
4638c2ecf20Sopenharmony_ci	i2c_lpc2k_reset(i2c);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	return 0;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic const struct dev_pm_ops i2c_lpc2k_dev_pm_ops = {
4698c2ecf20Sopenharmony_ci	.suspend_noirq = i2c_lpc2k_suspend,
4708c2ecf20Sopenharmony_ci	.resume_noirq = i2c_lpc2k_resume,
4718c2ecf20Sopenharmony_ci};
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci#define I2C_LPC2K_DEV_PM_OPS (&i2c_lpc2k_dev_pm_ops)
4748c2ecf20Sopenharmony_ci#else
4758c2ecf20Sopenharmony_ci#define I2C_LPC2K_DEV_PM_OPS NULL
4768c2ecf20Sopenharmony_ci#endif
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic const struct of_device_id lpc2k_i2c_match[] = {
4798c2ecf20Sopenharmony_ci	{ .compatible = "nxp,lpc1788-i2c" },
4808c2ecf20Sopenharmony_ci	{},
4818c2ecf20Sopenharmony_ci};
4828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, lpc2k_i2c_match);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic struct platform_driver i2c_lpc2k_driver = {
4858c2ecf20Sopenharmony_ci	.probe	= i2c_lpc2k_probe,
4868c2ecf20Sopenharmony_ci	.remove	= i2c_lpc2k_remove,
4878c2ecf20Sopenharmony_ci	.driver	= {
4888c2ecf20Sopenharmony_ci		.name		= "lpc2k-i2c",
4898c2ecf20Sopenharmony_ci		.pm		= I2C_LPC2K_DEV_PM_OPS,
4908c2ecf20Sopenharmony_ci		.of_match_table	= lpc2k_i2c_match,
4918c2ecf20Sopenharmony_ci	},
4928c2ecf20Sopenharmony_ci};
4938c2ecf20Sopenharmony_cimodule_platform_driver(i2c_lpc2k_driver);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
4968c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("I2C driver for LPC2xxx devices");
4978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4988c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:lpc2k-i2c");
499