18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Provides I2C support for Philips PNX010x/PNX4008 boards.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Authors: Dennis Kovalev <dkovalev@ru.mvista.com>
58c2ecf20Sopenharmony_ci *	    Vitaly Wool <vwool@ru.mvista.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * 2004-2006 (c) MontaVista Software, Inc. This file is licensed under
88c2ecf20Sopenharmony_ci * the terms of the GNU General Public License version 2. This program
98c2ecf20Sopenharmony_ci * is licensed "as is" without any warranty of any kind, whether express
108c2ecf20Sopenharmony_ci * or implied.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/ioport.h>
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci#include <linux/i2c.h>
188c2ecf20Sopenharmony_ci#include <linux/timer.h>
198c2ecf20Sopenharmony_ci#include <linux/completion.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/io.h>
228c2ecf20Sopenharmony_ci#include <linux/err.h>
238c2ecf20Sopenharmony_ci#include <linux/clk.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <linux/of.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define I2C_PNX_TIMEOUT_DEFAULT		10 /* msec */
288c2ecf20Sopenharmony_ci#define I2C_PNX_SPEED_KHZ_DEFAULT	100
298c2ecf20Sopenharmony_ci#define I2C_PNX_REGION_SIZE		0x100
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct i2c_pnx_mif {
328c2ecf20Sopenharmony_ci	int			ret;		/* Return value */
338c2ecf20Sopenharmony_ci	int			mode;		/* Interface mode */
348c2ecf20Sopenharmony_ci	struct completion	complete;	/* I/O completion */
358c2ecf20Sopenharmony_ci	struct timer_list	timer;		/* Timeout */
368c2ecf20Sopenharmony_ci	u8 *			buf;		/* Data buffer */
378c2ecf20Sopenharmony_ci	int			len;		/* Length of data buffer */
388c2ecf20Sopenharmony_ci	int			order;		/* RX Bytes to order via TX */
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistruct i2c_pnx_algo_data {
428c2ecf20Sopenharmony_ci	void __iomem		*ioaddr;
438c2ecf20Sopenharmony_ci	struct i2c_pnx_mif	mif;
448c2ecf20Sopenharmony_ci	int			last;
458c2ecf20Sopenharmony_ci	struct clk		*clk;
468c2ecf20Sopenharmony_ci	struct i2c_adapter	adapter;
478c2ecf20Sopenharmony_ci	int			irq;
488c2ecf20Sopenharmony_ci	u32			timeout;
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cienum {
528c2ecf20Sopenharmony_ci	mstatus_tdi = 0x00000001,
538c2ecf20Sopenharmony_ci	mstatus_afi = 0x00000002,
548c2ecf20Sopenharmony_ci	mstatus_nai = 0x00000004,
558c2ecf20Sopenharmony_ci	mstatus_drmi = 0x00000008,
568c2ecf20Sopenharmony_ci	mstatus_active = 0x00000020,
578c2ecf20Sopenharmony_ci	mstatus_scl = 0x00000040,
588c2ecf20Sopenharmony_ci	mstatus_sda = 0x00000080,
598c2ecf20Sopenharmony_ci	mstatus_rff = 0x00000100,
608c2ecf20Sopenharmony_ci	mstatus_rfe = 0x00000200,
618c2ecf20Sopenharmony_ci	mstatus_tff = 0x00000400,
628c2ecf20Sopenharmony_ci	mstatus_tfe = 0x00000800,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cienum {
668c2ecf20Sopenharmony_ci	mcntrl_tdie = 0x00000001,
678c2ecf20Sopenharmony_ci	mcntrl_afie = 0x00000002,
688c2ecf20Sopenharmony_ci	mcntrl_naie = 0x00000004,
698c2ecf20Sopenharmony_ci	mcntrl_drmie = 0x00000008,
708c2ecf20Sopenharmony_ci	mcntrl_drsie = 0x00000010,
718c2ecf20Sopenharmony_ci	mcntrl_rffie = 0x00000020,
728c2ecf20Sopenharmony_ci	mcntrl_daie = 0x00000040,
738c2ecf20Sopenharmony_ci	mcntrl_tffie = 0x00000080,
748c2ecf20Sopenharmony_ci	mcntrl_reset = 0x00000100,
758c2ecf20Sopenharmony_ci	mcntrl_cdbmode = 0x00000400,
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cienum {
798c2ecf20Sopenharmony_ci	rw_bit = 1 << 0,
808c2ecf20Sopenharmony_ci	start_bit = 1 << 8,
818c2ecf20Sopenharmony_ci	stop_bit = 1 << 9,
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#define I2C_REG_RX(a)	((a)->ioaddr)		/* Rx FIFO reg (RO) */
858c2ecf20Sopenharmony_ci#define I2C_REG_TX(a)	((a)->ioaddr)		/* Tx FIFO reg (WO) */
868c2ecf20Sopenharmony_ci#define I2C_REG_STS(a)	((a)->ioaddr + 0x04)	/* Status reg (RO) */
878c2ecf20Sopenharmony_ci#define I2C_REG_CTL(a)	((a)->ioaddr + 0x08)	/* Ctl reg */
888c2ecf20Sopenharmony_ci#define I2C_REG_CKL(a)	((a)->ioaddr + 0x0c)	/* Clock divider low */
898c2ecf20Sopenharmony_ci#define I2C_REG_CKH(a)	((a)->ioaddr + 0x10)	/* Clock divider high */
908c2ecf20Sopenharmony_ci#define I2C_REG_ADR(a)	((a)->ioaddr + 0x14)	/* I2C address */
918c2ecf20Sopenharmony_ci#define I2C_REG_RFL(a)	((a)->ioaddr + 0x18)	/* Rx FIFO level (RO) */
928c2ecf20Sopenharmony_ci#define I2C_REG_TFL(a)	((a)->ioaddr + 0x1c)	/* Tx FIFO level (RO) */
938c2ecf20Sopenharmony_ci#define I2C_REG_RXB(a)	((a)->ioaddr + 0x20)	/* Num of bytes Rx-ed (RO) */
948c2ecf20Sopenharmony_ci#define I2C_REG_TXB(a)	((a)->ioaddr + 0x24)	/* Num of bytes Tx-ed (RO) */
958c2ecf20Sopenharmony_ci#define I2C_REG_TXS(a)	((a)->ioaddr + 0x28)	/* Tx slave FIFO (RO) */
968c2ecf20Sopenharmony_ci#define I2C_REG_STFL(a)	((a)->ioaddr + 0x2c)	/* Tx slave FIFO level (RO) */
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic inline int wait_timeout(struct i2c_pnx_algo_data *data)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	long timeout = data->timeout;
1018c2ecf20Sopenharmony_ci	while (timeout > 0 &&
1028c2ecf20Sopenharmony_ci			(ioread32(I2C_REG_STS(data)) & mstatus_active)) {
1038c2ecf20Sopenharmony_ci		mdelay(1);
1048c2ecf20Sopenharmony_ci		timeout--;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci	return (timeout <= 0);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic inline int wait_reset(struct i2c_pnx_algo_data *data)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	long timeout = data->timeout;
1128c2ecf20Sopenharmony_ci	while (timeout > 0 &&
1138c2ecf20Sopenharmony_ci			(ioread32(I2C_REG_CTL(data)) & mcntrl_reset)) {
1148c2ecf20Sopenharmony_ci		mdelay(1);
1158c2ecf20Sopenharmony_ci		timeout--;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci	return (timeout <= 0);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct timer_list *timer = &alg_data->mif.timer;
1238c2ecf20Sopenharmony_ci	unsigned long expires = msecs_to_jiffies(alg_data->timeout);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (expires <= 1)
1268c2ecf20Sopenharmony_ci		expires = 2;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	del_timer_sync(timer);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev, "Timer armed at %lu plus %lu jiffies.\n",
1318c2ecf20Sopenharmony_ci		jiffies, expires);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	timer->expires = jiffies + expires;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	add_timer(timer);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/**
1398c2ecf20Sopenharmony_ci * i2c_pnx_start - start a device
1408c2ecf20Sopenharmony_ci * @slave_addr:		slave address
1418c2ecf20Sopenharmony_ci * @adap:		pointer to adapter structure
1428c2ecf20Sopenharmony_ci *
1438c2ecf20Sopenharmony_ci * Generate a START signal in the desired mode.
1448c2ecf20Sopenharmony_ci */
1458c2ecf20Sopenharmony_cistatic int i2c_pnx_start(unsigned char slave_addr,
1468c2ecf20Sopenharmony_ci	struct i2c_pnx_algo_data *alg_data)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev, "%s(): addr 0x%x mode %d\n", __func__,
1498c2ecf20Sopenharmony_ci		slave_addr, alg_data->mif.mode);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* Check for 7 bit slave addresses only */
1528c2ecf20Sopenharmony_ci	if (slave_addr & ~0x7f) {
1538c2ecf20Sopenharmony_ci		dev_err(&alg_data->adapter.dev,
1548c2ecf20Sopenharmony_ci			"%s: Invalid slave address %x. Only 7-bit addresses are supported\n",
1558c2ecf20Sopenharmony_ci			alg_data->adapter.name, slave_addr);
1568c2ecf20Sopenharmony_ci		return -EINVAL;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* First, make sure bus is idle */
1608c2ecf20Sopenharmony_ci	if (wait_timeout(alg_data)) {
1618c2ecf20Sopenharmony_ci		/* Somebody else is monopolizing the bus */
1628c2ecf20Sopenharmony_ci		dev_err(&alg_data->adapter.dev,
1638c2ecf20Sopenharmony_ci			"%s: Bus busy. Slave addr = %02x, cntrl = %x, stat = %x\n",
1648c2ecf20Sopenharmony_ci			alg_data->adapter.name, slave_addr,
1658c2ecf20Sopenharmony_ci			ioread32(I2C_REG_CTL(alg_data)),
1668c2ecf20Sopenharmony_ci			ioread32(I2C_REG_STS(alg_data)));
1678c2ecf20Sopenharmony_ci		return -EBUSY;
1688c2ecf20Sopenharmony_ci	} else if (ioread32(I2C_REG_STS(alg_data)) & mstatus_afi) {
1698c2ecf20Sopenharmony_ci		/* Sorry, we lost the bus */
1708c2ecf20Sopenharmony_ci		dev_err(&alg_data->adapter.dev,
1718c2ecf20Sopenharmony_ci		        "%s: Arbitration failure. Slave addr = %02x\n",
1728c2ecf20Sopenharmony_ci			alg_data->adapter.name, slave_addr);
1738c2ecf20Sopenharmony_ci		return -EIO;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/*
1778c2ecf20Sopenharmony_ci	 * OK, I2C is enabled and we have the bus.
1788c2ecf20Sopenharmony_ci	 * Clear the current TDI and AFI status flags.
1798c2ecf20Sopenharmony_ci	 */
1808c2ecf20Sopenharmony_ci	iowrite32(ioread32(I2C_REG_STS(alg_data)) | mstatus_tdi | mstatus_afi,
1818c2ecf20Sopenharmony_ci		  I2C_REG_STS(alg_data));
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev, "%s(): sending %#x\n", __func__,
1848c2ecf20Sopenharmony_ci		(slave_addr << 1) | start_bit | alg_data->mif.mode);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* Write the slave address, START bit and R/W bit */
1878c2ecf20Sopenharmony_ci	iowrite32((slave_addr << 1) | start_bit | alg_data->mif.mode,
1888c2ecf20Sopenharmony_ci		  I2C_REG_TX(alg_data));
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev, "%s(): exit\n", __func__);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/**
1968c2ecf20Sopenharmony_ci * i2c_pnx_stop - stop a device
1978c2ecf20Sopenharmony_ci * @adap:		pointer to I2C adapter structure
1988c2ecf20Sopenharmony_ci *
1998c2ecf20Sopenharmony_ci * Generate a STOP signal to terminate the master transaction.
2008c2ecf20Sopenharmony_ci */
2018c2ecf20Sopenharmony_cistatic void i2c_pnx_stop(struct i2c_pnx_algo_data *alg_data)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	/* Only 1 msec max timeout due to interrupt context */
2048c2ecf20Sopenharmony_ci	long timeout = 1000;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n",
2078c2ecf20Sopenharmony_ci		__func__, ioread32(I2C_REG_STS(alg_data)));
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* Write a STOP bit to TX FIFO */
2108c2ecf20Sopenharmony_ci	iowrite32(0xff | stop_bit, I2C_REG_TX(alg_data));
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* Wait until the STOP is seen. */
2138c2ecf20Sopenharmony_ci	while (timeout > 0 &&
2148c2ecf20Sopenharmony_ci	       (ioread32(I2C_REG_STS(alg_data)) & mstatus_active)) {
2158c2ecf20Sopenharmony_ci		/* may be called from interrupt context */
2168c2ecf20Sopenharmony_ci		udelay(1);
2178c2ecf20Sopenharmony_ci		timeout--;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n",
2218c2ecf20Sopenharmony_ci		__func__, ioread32(I2C_REG_STS(alg_data)));
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/**
2258c2ecf20Sopenharmony_ci * i2c_pnx_master_xmit - transmit data to slave
2268c2ecf20Sopenharmony_ci * @adap:		pointer to I2C adapter structure
2278c2ecf20Sopenharmony_ci *
2288c2ecf20Sopenharmony_ci * Sends one byte of data to the slave
2298c2ecf20Sopenharmony_ci */
2308c2ecf20Sopenharmony_cistatic int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	u32 val;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n",
2358c2ecf20Sopenharmony_ci		__func__, ioread32(I2C_REG_STS(alg_data)));
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (alg_data->mif.len > 0) {
2388c2ecf20Sopenharmony_ci		/* We still have something to talk about... */
2398c2ecf20Sopenharmony_ci		val = *alg_data->mif.buf++;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci		if (alg_data->mif.len == 1)
2428c2ecf20Sopenharmony_ci			val |= stop_bit;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci		alg_data->mif.len--;
2458c2ecf20Sopenharmony_ci		iowrite32(val, I2C_REG_TX(alg_data));
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		dev_dbg(&alg_data->adapter.dev, "%s(): xmit %#x [%d]\n",
2488c2ecf20Sopenharmony_ci			__func__, val, alg_data->mif.len + 1);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci		if (alg_data->mif.len == 0) {
2518c2ecf20Sopenharmony_ci			if (alg_data->last) {
2528c2ecf20Sopenharmony_ci				/* Wait until the STOP is seen. */
2538c2ecf20Sopenharmony_ci				if (wait_timeout(alg_data))
2548c2ecf20Sopenharmony_ci					dev_err(&alg_data->adapter.dev,
2558c2ecf20Sopenharmony_ci						"The bus is still active after timeout\n");
2568c2ecf20Sopenharmony_ci			}
2578c2ecf20Sopenharmony_ci			/* Disable master interrupts */
2588c2ecf20Sopenharmony_ci			iowrite32(ioread32(I2C_REG_CTL(alg_data)) &
2598c2ecf20Sopenharmony_ci				~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),
2608c2ecf20Sopenharmony_ci				  I2C_REG_CTL(alg_data));
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci			del_timer_sync(&alg_data->mif.timer);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci			dev_dbg(&alg_data->adapter.dev,
2658c2ecf20Sopenharmony_ci				"%s(): Waking up xfer routine.\n",
2668c2ecf20Sopenharmony_ci				__func__);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci			complete(&alg_data->mif.complete);
2698c2ecf20Sopenharmony_ci		}
2708c2ecf20Sopenharmony_ci	} else if (alg_data->mif.len == 0) {
2718c2ecf20Sopenharmony_ci		/* zero-sized transfer */
2728c2ecf20Sopenharmony_ci		i2c_pnx_stop(alg_data);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		/* Disable master interrupts. */
2758c2ecf20Sopenharmony_ci		iowrite32(ioread32(I2C_REG_CTL(alg_data)) &
2768c2ecf20Sopenharmony_ci			~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),
2778c2ecf20Sopenharmony_ci			  I2C_REG_CTL(alg_data));
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		/* Stop timer. */
2808c2ecf20Sopenharmony_ci		del_timer_sync(&alg_data->mif.timer);
2818c2ecf20Sopenharmony_ci		dev_dbg(&alg_data->adapter.dev,
2828c2ecf20Sopenharmony_ci			"%s(): Waking up xfer routine after zero-xfer.\n",
2838c2ecf20Sopenharmony_ci			__func__);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		complete(&alg_data->mif.complete);
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n",
2898c2ecf20Sopenharmony_ci		__func__, ioread32(I2C_REG_STS(alg_data)));
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	return 0;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci/**
2958c2ecf20Sopenharmony_ci * i2c_pnx_master_rcv - receive data from slave
2968c2ecf20Sopenharmony_ci * @adap:		pointer to I2C adapter structure
2978c2ecf20Sopenharmony_ci *
2988c2ecf20Sopenharmony_ci * Reads one byte data from the slave
2998c2ecf20Sopenharmony_ci */
3008c2ecf20Sopenharmony_cistatic int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	unsigned int val = 0;
3038c2ecf20Sopenharmony_ci	u32 ctl = 0;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev, "%s(): entering: stat = %04x.\n",
3068c2ecf20Sopenharmony_ci		__func__, ioread32(I2C_REG_STS(alg_data)));
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	/* Check, whether there is already data,
3098c2ecf20Sopenharmony_ci	 * or we didn't 'ask' for it yet.
3108c2ecf20Sopenharmony_ci	 */
3118c2ecf20Sopenharmony_ci	if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) {
3128c2ecf20Sopenharmony_ci		/* 'Asking' is done asynchronously, e.g. dummy TX of several
3138c2ecf20Sopenharmony_ci		 * bytes is done before the first actual RX arrives in FIFO.
3148c2ecf20Sopenharmony_ci		 * Therefore, ordered bytes (via TX) are counted separately.
3158c2ecf20Sopenharmony_ci		 */
3168c2ecf20Sopenharmony_ci		if (alg_data->mif.order) {
3178c2ecf20Sopenharmony_ci			dev_dbg(&alg_data->adapter.dev,
3188c2ecf20Sopenharmony_ci				"%s(): Write dummy data to fill Rx-fifo...\n",
3198c2ecf20Sopenharmony_ci				__func__);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci			if (alg_data->mif.order == 1) {
3228c2ecf20Sopenharmony_ci				/* Last byte, do not acknowledge next rcv. */
3238c2ecf20Sopenharmony_ci				val |= stop_bit;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci				/*
3268c2ecf20Sopenharmony_ci				 * Enable interrupt RFDAIE (data in Rx fifo),
3278c2ecf20Sopenharmony_ci				 * and disable DRMIE (need data for Tx)
3288c2ecf20Sopenharmony_ci				 */
3298c2ecf20Sopenharmony_ci				ctl = ioread32(I2C_REG_CTL(alg_data));
3308c2ecf20Sopenharmony_ci				ctl |= mcntrl_rffie | mcntrl_daie;
3318c2ecf20Sopenharmony_ci				ctl &= ~mcntrl_drmie;
3328c2ecf20Sopenharmony_ci				iowrite32(ctl, I2C_REG_CTL(alg_data));
3338c2ecf20Sopenharmony_ci			}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci			/*
3368c2ecf20Sopenharmony_ci			 * Now we'll 'ask' for data:
3378c2ecf20Sopenharmony_ci			 * For each byte we want to receive, we must
3388c2ecf20Sopenharmony_ci			 * write a (dummy) byte to the Tx-FIFO.
3398c2ecf20Sopenharmony_ci			 */
3408c2ecf20Sopenharmony_ci			iowrite32(val, I2C_REG_TX(alg_data));
3418c2ecf20Sopenharmony_ci			alg_data->mif.order--;
3428c2ecf20Sopenharmony_ci		}
3438c2ecf20Sopenharmony_ci		return 0;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/* Handle data. */
3478c2ecf20Sopenharmony_ci	if (alg_data->mif.len > 0) {
3488c2ecf20Sopenharmony_ci		val = ioread32(I2C_REG_RX(alg_data));
3498c2ecf20Sopenharmony_ci		*alg_data->mif.buf++ = (u8) (val & 0xff);
3508c2ecf20Sopenharmony_ci		dev_dbg(&alg_data->adapter.dev, "%s(): rcv 0x%x [%d]\n",
3518c2ecf20Sopenharmony_ci			__func__, val, alg_data->mif.len);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		alg_data->mif.len--;
3548c2ecf20Sopenharmony_ci		if (alg_data->mif.len == 0) {
3558c2ecf20Sopenharmony_ci			if (alg_data->last)
3568c2ecf20Sopenharmony_ci				/* Wait until the STOP is seen. */
3578c2ecf20Sopenharmony_ci				if (wait_timeout(alg_data))
3588c2ecf20Sopenharmony_ci					dev_err(&alg_data->adapter.dev,
3598c2ecf20Sopenharmony_ci						"The bus is still active after timeout\n");
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci			/* Disable master interrupts */
3628c2ecf20Sopenharmony_ci			ctl = ioread32(I2C_REG_CTL(alg_data));
3638c2ecf20Sopenharmony_ci			ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie |
3648c2ecf20Sopenharmony_ci				 mcntrl_drmie | mcntrl_daie);
3658c2ecf20Sopenharmony_ci			iowrite32(ctl, I2C_REG_CTL(alg_data));
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci			/* Kill timer. */
3688c2ecf20Sopenharmony_ci			del_timer_sync(&alg_data->mif.timer);
3698c2ecf20Sopenharmony_ci			complete(&alg_data->mif.complete);
3708c2ecf20Sopenharmony_ci		}
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev, "%s(): exiting: stat = %04x.\n",
3748c2ecf20Sopenharmony_ci		__func__, ioread32(I2C_REG_STS(alg_data)));
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	return 0;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct i2c_pnx_algo_data *alg_data = dev_id;
3828c2ecf20Sopenharmony_ci	u32 stat, ctl;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev,
3858c2ecf20Sopenharmony_ci		"%s(): mstat = %x mctrl = %x, mode = %d\n",
3868c2ecf20Sopenharmony_ci		__func__,
3878c2ecf20Sopenharmony_ci		ioread32(I2C_REG_STS(alg_data)),
3888c2ecf20Sopenharmony_ci		ioread32(I2C_REG_CTL(alg_data)),
3898c2ecf20Sopenharmony_ci		alg_data->mif.mode);
3908c2ecf20Sopenharmony_ci	stat = ioread32(I2C_REG_STS(alg_data));
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/* let's see what kind of event this is */
3938c2ecf20Sopenharmony_ci	if (stat & mstatus_afi) {
3948c2ecf20Sopenharmony_ci		/* We lost arbitration in the midst of a transfer */
3958c2ecf20Sopenharmony_ci		alg_data->mif.ret = -EIO;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci		/* Disable master interrupts. */
3988c2ecf20Sopenharmony_ci		ctl = ioread32(I2C_REG_CTL(alg_data));
3998c2ecf20Sopenharmony_ci		ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie |
4008c2ecf20Sopenharmony_ci			 mcntrl_drmie);
4018c2ecf20Sopenharmony_ci		iowrite32(ctl, I2C_REG_CTL(alg_data));
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		/* Stop timer, to prevent timeout. */
4048c2ecf20Sopenharmony_ci		del_timer_sync(&alg_data->mif.timer);
4058c2ecf20Sopenharmony_ci		complete(&alg_data->mif.complete);
4068c2ecf20Sopenharmony_ci	} else if (stat & mstatus_nai) {
4078c2ecf20Sopenharmony_ci		/* Slave did not acknowledge, generate a STOP */
4088c2ecf20Sopenharmony_ci		dev_dbg(&alg_data->adapter.dev,
4098c2ecf20Sopenharmony_ci			"%s(): Slave did not acknowledge, generating a STOP.\n",
4108c2ecf20Sopenharmony_ci			__func__);
4118c2ecf20Sopenharmony_ci		i2c_pnx_stop(alg_data);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci		/* Disable master interrupts. */
4148c2ecf20Sopenharmony_ci		ctl = ioread32(I2C_REG_CTL(alg_data));
4158c2ecf20Sopenharmony_ci		ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie |
4168c2ecf20Sopenharmony_ci			 mcntrl_drmie);
4178c2ecf20Sopenharmony_ci		iowrite32(ctl, I2C_REG_CTL(alg_data));
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci		/* Our return value. */
4208c2ecf20Sopenharmony_ci		alg_data->mif.ret = -EIO;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		/* Stop timer, to prevent timeout. */
4238c2ecf20Sopenharmony_ci		del_timer_sync(&alg_data->mif.timer);
4248c2ecf20Sopenharmony_ci		complete(&alg_data->mif.complete);
4258c2ecf20Sopenharmony_ci	} else {
4268c2ecf20Sopenharmony_ci		/*
4278c2ecf20Sopenharmony_ci		 * Two options:
4288c2ecf20Sopenharmony_ci		 * - Master Tx needs data.
4298c2ecf20Sopenharmony_ci		 * - There is data in the Rx-fifo
4308c2ecf20Sopenharmony_ci		 * The latter is only the case if we have requested for data,
4318c2ecf20Sopenharmony_ci		 * via a dummy write. (See 'i2c_pnx_master_rcv'.)
4328c2ecf20Sopenharmony_ci		 * We therefore check, as a sanity check, whether that interrupt
4338c2ecf20Sopenharmony_ci		 * has been enabled.
4348c2ecf20Sopenharmony_ci		 */
4358c2ecf20Sopenharmony_ci		if ((stat & mstatus_drmi) || !(stat & mstatus_rfe)) {
4368c2ecf20Sopenharmony_ci			if (alg_data->mif.mode == I2C_SMBUS_WRITE) {
4378c2ecf20Sopenharmony_ci				i2c_pnx_master_xmit(alg_data);
4388c2ecf20Sopenharmony_ci			} else if (alg_data->mif.mode == I2C_SMBUS_READ) {
4398c2ecf20Sopenharmony_ci				i2c_pnx_master_rcv(alg_data);
4408c2ecf20Sopenharmony_ci			}
4418c2ecf20Sopenharmony_ci		}
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	/* Clear TDI and AFI bits */
4458c2ecf20Sopenharmony_ci	stat = ioread32(I2C_REG_STS(alg_data));
4468c2ecf20Sopenharmony_ci	iowrite32(stat | mstatus_tdi | mstatus_afi, I2C_REG_STS(alg_data));
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev,
4498c2ecf20Sopenharmony_ci		"%s(): exiting, stat = %x ctrl = %x.\n",
4508c2ecf20Sopenharmony_ci		 __func__, ioread32(I2C_REG_STS(alg_data)),
4518c2ecf20Sopenharmony_ci		 ioread32(I2C_REG_CTL(alg_data)));
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic void i2c_pnx_timeout(struct timer_list *t)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	struct i2c_pnx_algo_data *alg_data = from_timer(alg_data, t, mif.timer);
4598c2ecf20Sopenharmony_ci	u32 ctl;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	dev_err(&alg_data->adapter.dev,
4628c2ecf20Sopenharmony_ci		"Master timed out. stat = %04x, cntrl = %04x. Resetting master...\n",
4638c2ecf20Sopenharmony_ci		ioread32(I2C_REG_STS(alg_data)),
4648c2ecf20Sopenharmony_ci		ioread32(I2C_REG_CTL(alg_data)));
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	/* Reset master and disable interrupts */
4678c2ecf20Sopenharmony_ci	ctl = ioread32(I2C_REG_CTL(alg_data));
4688c2ecf20Sopenharmony_ci	ctl &= ~(mcntrl_afie | mcntrl_naie | mcntrl_rffie | mcntrl_drmie);
4698c2ecf20Sopenharmony_ci	iowrite32(ctl, I2C_REG_CTL(alg_data));
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	ctl |= mcntrl_reset;
4728c2ecf20Sopenharmony_ci	iowrite32(ctl, I2C_REG_CTL(alg_data));
4738c2ecf20Sopenharmony_ci	wait_reset(alg_data);
4748c2ecf20Sopenharmony_ci	alg_data->mif.ret = -EIO;
4758c2ecf20Sopenharmony_ci	complete(&alg_data->mif.complete);
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic inline void bus_reset_if_active(struct i2c_pnx_algo_data *alg_data)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	u32 stat;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_active) {
4838c2ecf20Sopenharmony_ci		dev_err(&alg_data->adapter.dev,
4848c2ecf20Sopenharmony_ci			"%s: Bus is still active after xfer. Reset it...\n",
4858c2ecf20Sopenharmony_ci			alg_data->adapter.name);
4868c2ecf20Sopenharmony_ci		iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,
4878c2ecf20Sopenharmony_ci			  I2C_REG_CTL(alg_data));
4888c2ecf20Sopenharmony_ci		wait_reset(alg_data);
4898c2ecf20Sopenharmony_ci	} else if (!(stat & mstatus_rfe) || !(stat & mstatus_tfe)) {
4908c2ecf20Sopenharmony_ci		/* If there is data in the fifo's after transfer,
4918c2ecf20Sopenharmony_ci		 * flush fifo's by reset.
4928c2ecf20Sopenharmony_ci		 */
4938c2ecf20Sopenharmony_ci		iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,
4948c2ecf20Sopenharmony_ci			  I2C_REG_CTL(alg_data));
4958c2ecf20Sopenharmony_ci		wait_reset(alg_data);
4968c2ecf20Sopenharmony_ci	} else if (stat & mstatus_nai) {
4978c2ecf20Sopenharmony_ci		iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,
4988c2ecf20Sopenharmony_ci			  I2C_REG_CTL(alg_data));
4998c2ecf20Sopenharmony_ci		wait_reset(alg_data);
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci/**
5048c2ecf20Sopenharmony_ci * i2c_pnx_xfer - generic transfer entry point
5058c2ecf20Sopenharmony_ci * @adap:		pointer to I2C adapter structure
5068c2ecf20Sopenharmony_ci * @msgs:		array of messages
5078c2ecf20Sopenharmony_ci * @num:		number of messages
5088c2ecf20Sopenharmony_ci *
5098c2ecf20Sopenharmony_ci * Initiates the transfer
5108c2ecf20Sopenharmony_ci */
5118c2ecf20Sopenharmony_cistatic int
5128c2ecf20Sopenharmony_cii2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	struct i2c_msg *pmsg;
5158c2ecf20Sopenharmony_ci	int rc = 0, completed = 0, i;
5168c2ecf20Sopenharmony_ci	struct i2c_pnx_algo_data *alg_data = adap->algo_data;
5178c2ecf20Sopenharmony_ci	u32 stat;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev,
5208c2ecf20Sopenharmony_ci		"%s(): entering: %d messages, stat = %04x.\n",
5218c2ecf20Sopenharmony_ci		__func__, num, ioread32(I2C_REG_STS(alg_data)));
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	bus_reset_if_active(alg_data);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	/* Process transactions in a loop. */
5268c2ecf20Sopenharmony_ci	for (i = 0; rc >= 0 && i < num; i++) {
5278c2ecf20Sopenharmony_ci		u8 addr;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci		pmsg = &msgs[i];
5308c2ecf20Sopenharmony_ci		addr = pmsg->addr;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		if (pmsg->flags & I2C_M_TEN) {
5338c2ecf20Sopenharmony_ci			dev_err(&alg_data->adapter.dev,
5348c2ecf20Sopenharmony_ci				"%s: 10 bits addr not supported!\n",
5358c2ecf20Sopenharmony_ci				alg_data->adapter.name);
5368c2ecf20Sopenharmony_ci			rc = -EINVAL;
5378c2ecf20Sopenharmony_ci			break;
5388c2ecf20Sopenharmony_ci		}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci		alg_data->mif.buf = pmsg->buf;
5418c2ecf20Sopenharmony_ci		alg_data->mif.len = pmsg->len;
5428c2ecf20Sopenharmony_ci		alg_data->mif.order = pmsg->len;
5438c2ecf20Sopenharmony_ci		alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ?
5448c2ecf20Sopenharmony_ci			I2C_SMBUS_READ : I2C_SMBUS_WRITE;
5458c2ecf20Sopenharmony_ci		alg_data->mif.ret = 0;
5468c2ecf20Sopenharmony_ci		alg_data->last = (i == num - 1);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci		dev_dbg(&alg_data->adapter.dev, "%s(): mode %d, %d bytes\n",
5498c2ecf20Sopenharmony_ci			__func__, alg_data->mif.mode, alg_data->mif.len);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci		i2c_pnx_arm_timer(alg_data);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci		/* initialize the completion var */
5548c2ecf20Sopenharmony_ci		init_completion(&alg_data->mif.complete);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci		/* Enable master interrupt */
5578c2ecf20Sopenharmony_ci		iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_afie |
5588c2ecf20Sopenharmony_ci				mcntrl_naie | mcntrl_drmie,
5598c2ecf20Sopenharmony_ci			  I2C_REG_CTL(alg_data));
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci		/* Put start-code and slave-address on the bus. */
5628c2ecf20Sopenharmony_ci		rc = i2c_pnx_start(addr, alg_data);
5638c2ecf20Sopenharmony_ci		if (rc < 0)
5648c2ecf20Sopenharmony_ci			break;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci		/* Wait for completion */
5678c2ecf20Sopenharmony_ci		wait_for_completion(&alg_data->mif.complete);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci		if (!(rc = alg_data->mif.ret))
5708c2ecf20Sopenharmony_ci			completed++;
5718c2ecf20Sopenharmony_ci		dev_dbg(&alg_data->adapter.dev,
5728c2ecf20Sopenharmony_ci			"%s(): Complete, return code = %d.\n",
5738c2ecf20Sopenharmony_ci			__func__, rc);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci		/* Clear TDI and AFI bits in case they are set. */
5768c2ecf20Sopenharmony_ci		if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_tdi) {
5778c2ecf20Sopenharmony_ci			dev_dbg(&alg_data->adapter.dev,
5788c2ecf20Sopenharmony_ci				"%s: TDI still set... clearing now.\n",
5798c2ecf20Sopenharmony_ci				alg_data->adapter.name);
5808c2ecf20Sopenharmony_ci			iowrite32(stat, I2C_REG_STS(alg_data));
5818c2ecf20Sopenharmony_ci		}
5828c2ecf20Sopenharmony_ci		if ((stat = ioread32(I2C_REG_STS(alg_data))) & mstatus_afi) {
5838c2ecf20Sopenharmony_ci			dev_dbg(&alg_data->adapter.dev,
5848c2ecf20Sopenharmony_ci				"%s: AFI still set... clearing now.\n",
5858c2ecf20Sopenharmony_ci				alg_data->adapter.name);
5868c2ecf20Sopenharmony_ci			iowrite32(stat, I2C_REG_STS(alg_data));
5878c2ecf20Sopenharmony_ci		}
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	bus_reset_if_active(alg_data);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	/* Cleanup to be sure... */
5938c2ecf20Sopenharmony_ci	alg_data->mif.buf = NULL;
5948c2ecf20Sopenharmony_ci	alg_data->mif.len = 0;
5958c2ecf20Sopenharmony_ci	alg_data->mif.order = 0;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	dev_dbg(&alg_data->adapter.dev, "%s(): exiting, stat = %x\n",
5988c2ecf20Sopenharmony_ci		__func__, ioread32(I2C_REG_STS(alg_data)));
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	if (completed != num)
6018c2ecf20Sopenharmony_ci		return ((rc < 0) ? rc : -EREMOTEIO);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	return num;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic u32 i2c_pnx_func(struct i2c_adapter *adapter)
6078c2ecf20Sopenharmony_ci{
6088c2ecf20Sopenharmony_ci	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_cistatic const struct i2c_algorithm pnx_algorithm = {
6128c2ecf20Sopenharmony_ci	.master_xfer = i2c_pnx_xfer,
6138c2ecf20Sopenharmony_ci	.functionality = i2c_pnx_func,
6148c2ecf20Sopenharmony_ci};
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
6178c2ecf20Sopenharmony_cistatic int i2c_pnx_controller_suspend(struct device *dev)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	clk_disable_unprepare(alg_data->clk);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	return 0;
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic int i2c_pnx_controller_resume(struct device *dev)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	return clk_prepare_enable(alg_data->clk);
6318c2ecf20Sopenharmony_ci}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(i2c_pnx_pm,
6348c2ecf20Sopenharmony_ci			 i2c_pnx_controller_suspend, i2c_pnx_controller_resume);
6358c2ecf20Sopenharmony_ci#define PNX_I2C_PM	(&i2c_pnx_pm)
6368c2ecf20Sopenharmony_ci#else
6378c2ecf20Sopenharmony_ci#define PNX_I2C_PM	NULL
6388c2ecf20Sopenharmony_ci#endif
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_cistatic int i2c_pnx_probe(struct platform_device *pdev)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci	unsigned long tmp;
6438c2ecf20Sopenharmony_ci	int ret = 0;
6448c2ecf20Sopenharmony_ci	struct i2c_pnx_algo_data *alg_data;
6458c2ecf20Sopenharmony_ci	unsigned long freq;
6468c2ecf20Sopenharmony_ci	struct resource *res;
6478c2ecf20Sopenharmony_ci	u32 speed = I2C_PNX_SPEED_KHZ_DEFAULT * 1000;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	alg_data = devm_kzalloc(&pdev->dev, sizeof(*alg_data), GFP_KERNEL);
6508c2ecf20Sopenharmony_ci	if (!alg_data)
6518c2ecf20Sopenharmony_ci		return -ENOMEM;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, alg_data);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	alg_data->adapter.dev.parent = &pdev->dev;
6568c2ecf20Sopenharmony_ci	alg_data->adapter.algo = &pnx_algorithm;
6578c2ecf20Sopenharmony_ci	alg_data->adapter.algo_data = alg_data;
6588c2ecf20Sopenharmony_ci	alg_data->adapter.nr = pdev->id;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT;
6618c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
6628c2ecf20Sopenharmony_ci	alg_data->adapter.dev.of_node = of_node_get(pdev->dev.of_node);
6638c2ecf20Sopenharmony_ci	if (pdev->dev.of_node) {
6648c2ecf20Sopenharmony_ci		of_property_read_u32(pdev->dev.of_node, "clock-frequency",
6658c2ecf20Sopenharmony_ci				     &speed);
6668c2ecf20Sopenharmony_ci		/*
6678c2ecf20Sopenharmony_ci		 * At this point, it is planned to add an OF timeout property.
6688c2ecf20Sopenharmony_ci		 * As soon as there is a consensus about how to call and handle
6698c2ecf20Sopenharmony_ci		 * this, sth. like the following can be put here:
6708c2ecf20Sopenharmony_ci		 *
6718c2ecf20Sopenharmony_ci		 * of_property_read_u32(pdev->dev.of_node, "timeout",
6728c2ecf20Sopenharmony_ci		 *                      &alg_data->timeout);
6738c2ecf20Sopenharmony_ci		 */
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci#endif
6768c2ecf20Sopenharmony_ci	alg_data->clk = devm_clk_get(&pdev->dev, NULL);
6778c2ecf20Sopenharmony_ci	if (IS_ERR(alg_data->clk))
6788c2ecf20Sopenharmony_ci		return PTR_ERR(alg_data->clk);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	timer_setup(&alg_data->mif.timer, i2c_pnx_timeout, 0);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name),
6838c2ecf20Sopenharmony_ci		 "%s", pdev->name);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	/* Register I/O resource */
6868c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
6878c2ecf20Sopenharmony_ci	alg_data->ioaddr = devm_ioremap_resource(&pdev->dev, res);
6888c2ecf20Sopenharmony_ci	if (IS_ERR(alg_data->ioaddr))
6898c2ecf20Sopenharmony_ci		return PTR_ERR(alg_data->ioaddr);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(alg_data->clk);
6928c2ecf20Sopenharmony_ci	if (ret)
6938c2ecf20Sopenharmony_ci		return ret;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	freq = clk_get_rate(alg_data->clk);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/*
6988c2ecf20Sopenharmony_ci	 * Clock Divisor High This value is the number of system clocks
6998c2ecf20Sopenharmony_ci	 * the serial clock (SCL) will be high.
7008c2ecf20Sopenharmony_ci	 * For example, if the system clock period is 50 ns and the maximum
7018c2ecf20Sopenharmony_ci	 * desired serial period is 10000 ns (100 kHz), then CLKHI would be
7028c2ecf20Sopenharmony_ci	 * set to 0.5*(f_sys/f_i2c)-2=0.5*(20e6/100e3)-2=98. The actual value
7038c2ecf20Sopenharmony_ci	 * programmed into CLKHI will vary from this slightly due to
7048c2ecf20Sopenharmony_ci	 * variations in the output pad's rise and fall times as well as
7058c2ecf20Sopenharmony_ci	 * the deglitching filter length.
7068c2ecf20Sopenharmony_ci	 */
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	tmp = (freq / speed) / 2 - 2;
7098c2ecf20Sopenharmony_ci	if (tmp > 0x3FF)
7108c2ecf20Sopenharmony_ci		tmp = 0x3FF;
7118c2ecf20Sopenharmony_ci	iowrite32(tmp, I2C_REG_CKH(alg_data));
7128c2ecf20Sopenharmony_ci	iowrite32(tmp, I2C_REG_CKL(alg_data));
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	iowrite32(mcntrl_reset, I2C_REG_CTL(alg_data));
7158c2ecf20Sopenharmony_ci	if (wait_reset(alg_data)) {
7168c2ecf20Sopenharmony_ci		ret = -ENODEV;
7178c2ecf20Sopenharmony_ci		goto out_clock;
7188c2ecf20Sopenharmony_ci	}
7198c2ecf20Sopenharmony_ci	init_completion(&alg_data->mif.complete);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	alg_data->irq = platform_get_irq(pdev, 0);
7228c2ecf20Sopenharmony_ci	if (alg_data->irq < 0) {
7238c2ecf20Sopenharmony_ci		ret = alg_data->irq;
7248c2ecf20Sopenharmony_ci		goto out_clock;
7258c2ecf20Sopenharmony_ci	}
7268c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, alg_data->irq, i2c_pnx_interrupt,
7278c2ecf20Sopenharmony_ci			       0, pdev->name, alg_data);
7288c2ecf20Sopenharmony_ci	if (ret)
7298c2ecf20Sopenharmony_ci		goto out_clock;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	/* Register this adapter with the I2C subsystem */
7328c2ecf20Sopenharmony_ci	ret = i2c_add_numbered_adapter(&alg_data->adapter);
7338c2ecf20Sopenharmony_ci	if (ret < 0)
7348c2ecf20Sopenharmony_ci		goto out_clock;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "%s: Master at %pap, irq %d.\n",
7378c2ecf20Sopenharmony_ci		alg_data->adapter.name, &res->start, alg_data->irq);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	return 0;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ciout_clock:
7428c2ecf20Sopenharmony_ci	clk_disable_unprepare(alg_data->clk);
7438c2ecf20Sopenharmony_ci	return ret;
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_cistatic int i2c_pnx_remove(struct platform_device *pdev)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	i2c_del_adapter(&alg_data->adapter);
7518c2ecf20Sopenharmony_ci	clk_disable_unprepare(alg_data->clk);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	return 0;
7548c2ecf20Sopenharmony_ci}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
7578c2ecf20Sopenharmony_cistatic const struct of_device_id i2c_pnx_of_match[] = {
7588c2ecf20Sopenharmony_ci	{ .compatible = "nxp,pnx-i2c" },
7598c2ecf20Sopenharmony_ci	{ },
7608c2ecf20Sopenharmony_ci};
7618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, i2c_pnx_of_match);
7628c2ecf20Sopenharmony_ci#endif
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_cistatic struct platform_driver i2c_pnx_driver = {
7658c2ecf20Sopenharmony_ci	.driver = {
7668c2ecf20Sopenharmony_ci		.name = "pnx-i2c",
7678c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(i2c_pnx_of_match),
7688c2ecf20Sopenharmony_ci		.pm = PNX_I2C_PM,
7698c2ecf20Sopenharmony_ci	},
7708c2ecf20Sopenharmony_ci	.probe = i2c_pnx_probe,
7718c2ecf20Sopenharmony_ci	.remove = i2c_pnx_remove,
7728c2ecf20Sopenharmony_ci};
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cistatic int __init i2c_adap_pnx_init(void)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	return platform_driver_register(&i2c_pnx_driver);
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistatic void __exit i2c_adap_pnx_exit(void)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	platform_driver_unregister(&i2c_pnx_driver);
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vitaly Wool");
7858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dennis Kovalev <source@mvista.com>");
7868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("I2C driver for Philips IP3204-based I2C busses");
7878c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
7888c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:pnx-i2c");
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci/* We need to make sure I2C is initialized before USB */
7918c2ecf20Sopenharmony_cisubsys_initcall(i2c_adap_pnx_init);
7928c2ecf20Sopenharmony_cimodule_exit(i2c_adap_pnx_exit);
793