18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
38c2ecf20Sopenharmony_ci/* i2c-iop3xx.c i2c driver algorithms for Intel XScale IOP3xx & IXP46x       */
48c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
58c2ecf20Sopenharmony_ci/* Copyright (C) 2003 Peter Milne, D-TACQ Solutions Ltd
68c2ecf20Sopenharmony_ci *                    <Peter dot Milne at D hyphen TACQ dot com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * With acknowledgements to i2c-algo-ibm_ocp.c by
98c2ecf20Sopenharmony_ci * Ian DaSilva, MontaVista Software, Inc. idasilva@mvista.com
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * And i2c-algo-pcf.c, which was created by Simon G. Vogl and Hans Berglund:
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Copyright (C) 1995-1997 Simon G. Vogl, 1998-2000 Hans Berglund
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * And which acknowledged Kyösti Mälkki <kmalkki@cc.hut.fi>,
168c2ecf20Sopenharmony_ci * Frodo Looijaard <frodol@dds.nl>, Martin Bailey<mbailey@littlefeet-inc.com>
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * Major cleanup by Deepak Saxena <dsaxena@plexity.net>, 01/2005:
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * - Use driver model to pass per-chip info instead of hardcoding and #ifdefs
218c2ecf20Sopenharmony_ci * - Use ioremap/__raw_readl/__raw_writel instead of direct dereference
228c2ecf20Sopenharmony_ci * - Make it work with IXP46x chips
238c2ecf20Sopenharmony_ci * - Cleanup function names, coding style, etc
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * - writing to slave address causes latchup on iop331.
268c2ecf20Sopenharmony_ci *	fix: driver refuses to address self.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
308c2ecf20Sopenharmony_ci#include <linux/kernel.h>
318c2ecf20Sopenharmony_ci#include <linux/module.h>
328c2ecf20Sopenharmony_ci#include <linux/delay.h>
338c2ecf20Sopenharmony_ci#include <linux/slab.h>
348c2ecf20Sopenharmony_ci#include <linux/errno.h>
358c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
368c2ecf20Sopenharmony_ci#include <linux/i2c.h>
378c2ecf20Sopenharmony_ci#include <linux/io.h>
388c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include "i2c-iop3xx.h"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* global unit counter */
438c2ecf20Sopenharmony_cistatic int i2c_id;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic inline unsigned char
468c2ecf20Sopenharmony_ciiic_cook_addr(struct i2c_msg *msg)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	unsigned char addr;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	addr = i2c_8bit_addr_from_msg(msg);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return addr;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void
568c2ecf20Sopenharmony_ciiop3xx_i2c_reset(struct i2c_algo_iop3xx_data *iop3xx_adap)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	/* Follows devman 9.3 */
598c2ecf20Sopenharmony_ci	__raw_writel(IOP3XX_ICR_UNIT_RESET, iop3xx_adap->ioaddr + CR_OFFSET);
608c2ecf20Sopenharmony_ci	__raw_writel(IOP3XX_ISR_CLEARBITS, iop3xx_adap->ioaddr + SR_OFFSET);
618c2ecf20Sopenharmony_ci	__raw_writel(0, iop3xx_adap->ioaddr + CR_OFFSET);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic void
658c2ecf20Sopenharmony_ciiop3xx_i2c_enable(struct i2c_algo_iop3xx_data *iop3xx_adap)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	u32 cr = IOP3XX_ICR_GCD | IOP3XX_ICR_SCLEN | IOP3XX_ICR_UE;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/*
708c2ecf20Sopenharmony_ci	 * Every time unit enable is asserted, GPOD needs to be cleared
718c2ecf20Sopenharmony_ci	 * on IOP3XX to avoid data corruption on the bus. We use the
728c2ecf20Sopenharmony_ci	 * gpiod_set_raw_value() to make sure the 0 hits the hardware
738c2ecf20Sopenharmony_ci	 * GPOD register. These descriptors are only passed along to
748c2ecf20Sopenharmony_ci	 * the device if this is necessary.
758c2ecf20Sopenharmony_ci	 */
768c2ecf20Sopenharmony_ci	if (iop3xx_adap->gpio_scl)
778c2ecf20Sopenharmony_ci		gpiod_set_raw_value(iop3xx_adap->gpio_scl, 0);
788c2ecf20Sopenharmony_ci	if (iop3xx_adap->gpio_sda)
798c2ecf20Sopenharmony_ci		gpiod_set_raw_value(iop3xx_adap->gpio_sda, 0);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	/* NB SR bits not same position as CR IE bits :-( */
828c2ecf20Sopenharmony_ci	iop3xx_adap->SR_enabled =
838c2ecf20Sopenharmony_ci		IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD |
848c2ecf20Sopenharmony_ci		IOP3XX_ISR_RXFULL | IOP3XX_ISR_TXEMPTY;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	cr |= IOP3XX_ICR_ALD_IE | IOP3XX_ICR_BERR_IE |
878c2ecf20Sopenharmony_ci		IOP3XX_ICR_RXFULL_IE | IOP3XX_ICR_TXEMPTY_IE;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void
938c2ecf20Sopenharmony_ciiop3xx_i2c_transaction_cleanup(struct i2c_algo_iop3xx_data *iop3xx_adap)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	cr &= ~(IOP3XX_ICR_MSTART | IOP3XX_ICR_TBYTE |
988c2ecf20Sopenharmony_ci		IOP3XX_ICR_MSTOP | IOP3XX_ICR_SCLEN);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/*
1048c2ecf20Sopenharmony_ci * NB: the handler has to clear the source of the interrupt!
1058c2ecf20Sopenharmony_ci * Then it passes the SR flags of interest to BH via adap data
1068c2ecf20Sopenharmony_ci */
1078c2ecf20Sopenharmony_cistatic irqreturn_t
1088c2ecf20Sopenharmony_ciiop3xx_i2c_irq_handler(int this_irq, void *dev_id)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct i2c_algo_iop3xx_data *iop3xx_adap = dev_id;
1118c2ecf20Sopenharmony_ci	u32 sr = __raw_readl(iop3xx_adap->ioaddr + SR_OFFSET);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if ((sr &= iop3xx_adap->SR_enabled)) {
1148c2ecf20Sopenharmony_ci		__raw_writel(sr, iop3xx_adap->ioaddr + SR_OFFSET);
1158c2ecf20Sopenharmony_ci		iop3xx_adap->SR_received |= sr;
1168c2ecf20Sopenharmony_ci		wake_up_interruptible(&iop3xx_adap->waitq);
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/* check all error conditions, clear them , report most important */
1228c2ecf20Sopenharmony_cistatic int
1238c2ecf20Sopenharmony_ciiop3xx_i2c_error(u32 sr)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	int rc = 0;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if ((sr & IOP3XX_ISR_BERRD)) {
1288c2ecf20Sopenharmony_ci		if ( !rc ) rc = -I2C_ERR_BERR;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci	if ((sr & IOP3XX_ISR_ALD)) {
1318c2ecf20Sopenharmony_ci		if ( !rc ) rc = -I2C_ERR_ALD;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci	return rc;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic inline u32
1378c2ecf20Sopenharmony_ciiop3xx_i2c_get_srstat(struct i2c_algo_iop3xx_data *iop3xx_adap)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	unsigned long flags;
1408c2ecf20Sopenharmony_ci	u32 sr;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	spin_lock_irqsave(&iop3xx_adap->lock, flags);
1438c2ecf20Sopenharmony_ci	sr = iop3xx_adap->SR_received;
1448c2ecf20Sopenharmony_ci	iop3xx_adap->SR_received = 0;
1458c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&iop3xx_adap->lock, flags);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return sr;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/*
1518c2ecf20Sopenharmony_ci * sleep until interrupted, then recover and analyse the SR
1528c2ecf20Sopenharmony_ci * saved by handler
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_citypedef int (* compare_func)(unsigned test, unsigned mask);
1558c2ecf20Sopenharmony_ci/* returns 1 on correct comparison */
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic int
1588c2ecf20Sopenharmony_ciiop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap,
1598c2ecf20Sopenharmony_ci			  unsigned flags, unsigned* status,
1608c2ecf20Sopenharmony_ci			  compare_func compare)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	unsigned sr = 0;
1638c2ecf20Sopenharmony_ci	int interrupted;
1648c2ecf20Sopenharmony_ci	int done;
1658c2ecf20Sopenharmony_ci	int rc = 0;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	do {
1688c2ecf20Sopenharmony_ci		interrupted = wait_event_interruptible_timeout (
1698c2ecf20Sopenharmony_ci			iop3xx_adap->waitq,
1708c2ecf20Sopenharmony_ci			(done = compare( sr = iop3xx_i2c_get_srstat(iop3xx_adap) ,flags )),
1718c2ecf20Sopenharmony_ci			1 * HZ
1728c2ecf20Sopenharmony_ci			);
1738c2ecf20Sopenharmony_ci		if ((rc = iop3xx_i2c_error(sr)) < 0) {
1748c2ecf20Sopenharmony_ci			*status = sr;
1758c2ecf20Sopenharmony_ci			return rc;
1768c2ecf20Sopenharmony_ci		} else if (!interrupted) {
1778c2ecf20Sopenharmony_ci			*status = sr;
1788c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
1798c2ecf20Sopenharmony_ci		}
1808c2ecf20Sopenharmony_ci	} while(!done);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	*status = sr;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return 0;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/*
1888c2ecf20Sopenharmony_ci * Concrete compare_funcs
1898c2ecf20Sopenharmony_ci */
1908c2ecf20Sopenharmony_cistatic int
1918c2ecf20Sopenharmony_ciall_bits_clear(unsigned test, unsigned mask)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	return (test & mask) == 0;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic int
1978c2ecf20Sopenharmony_ciany_bits_set(unsigned test, unsigned mask)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	return (test & mask) != 0;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic int
2038c2ecf20Sopenharmony_ciiop3xx_i2c_wait_tx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	return iop3xx_i2c_wait_event(
2068c2ecf20Sopenharmony_ci		iop3xx_adap,
2078c2ecf20Sopenharmony_ci	        IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD,
2088c2ecf20Sopenharmony_ci		status, any_bits_set);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic int
2128c2ecf20Sopenharmony_ciiop3xx_i2c_wait_rx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	return iop3xx_i2c_wait_event(
2158c2ecf20Sopenharmony_ci		iop3xx_adap,
2168c2ecf20Sopenharmony_ci		IOP3XX_ISR_RXFULL | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD,
2178c2ecf20Sopenharmony_ci		status,	any_bits_set);
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic int
2218c2ecf20Sopenharmony_ciiop3xx_i2c_wait_idle(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	return iop3xx_i2c_wait_event(
2248c2ecf20Sopenharmony_ci		iop3xx_adap, IOP3XX_ISR_UNITBUSY, status, all_bits_clear);
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic int
2288c2ecf20Sopenharmony_ciiop3xx_i2c_send_target_addr(struct i2c_algo_iop3xx_data *iop3xx_adap,
2298c2ecf20Sopenharmony_ci				struct i2c_msg* msg)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
2328c2ecf20Sopenharmony_ci	int status;
2338c2ecf20Sopenharmony_ci	int rc;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* avoid writing to my slave address (hangs on 80331),
2368c2ecf20Sopenharmony_ci	 * forbidden in Intel developer manual
2378c2ecf20Sopenharmony_ci	 */
2388c2ecf20Sopenharmony_ci	if (msg->addr == MYSAR) {
2398c2ecf20Sopenharmony_ci		return -EBUSY;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	__raw_writel(iic_cook_addr(msg), iop3xx_adap->ioaddr + DBR_OFFSET);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	cr &= ~(IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK);
2458c2ecf20Sopenharmony_ci	cr |= IOP3XX_ICR_MSTART | IOP3XX_ICR_TBYTE;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
2488c2ecf20Sopenharmony_ci	rc = iop3xx_i2c_wait_tx_done(iop3xx_adap, &status);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return rc;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic int
2548c2ecf20Sopenharmony_ciiop3xx_i2c_write_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char byte,
2558c2ecf20Sopenharmony_ci				int stop)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
2588c2ecf20Sopenharmony_ci	int status;
2598c2ecf20Sopenharmony_ci	int rc = 0;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	__raw_writel(byte, iop3xx_adap->ioaddr + DBR_OFFSET);
2628c2ecf20Sopenharmony_ci	cr &= ~IOP3XX_ICR_MSTART;
2638c2ecf20Sopenharmony_ci	if (stop) {
2648c2ecf20Sopenharmony_ci		cr |= IOP3XX_ICR_MSTOP;
2658c2ecf20Sopenharmony_ci	} else {
2668c2ecf20Sopenharmony_ci		cr &= ~IOP3XX_ICR_MSTOP;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci	cr |= IOP3XX_ICR_TBYTE;
2698c2ecf20Sopenharmony_ci	__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
2708c2ecf20Sopenharmony_ci	rc = iop3xx_i2c_wait_tx_done(iop3xx_adap, &status);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return rc;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic int
2768c2ecf20Sopenharmony_ciiop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char* byte,
2778c2ecf20Sopenharmony_ci				int stop)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
2808c2ecf20Sopenharmony_ci	int status;
2818c2ecf20Sopenharmony_ci	int rc = 0;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	cr &= ~IOP3XX_ICR_MSTART;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	if (stop) {
2868c2ecf20Sopenharmony_ci		cr |= IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK;
2878c2ecf20Sopenharmony_ci	} else {
2888c2ecf20Sopenharmony_ci		cr &= ~(IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK);
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	cr |= IOP3XX_ICR_TBYTE;
2918c2ecf20Sopenharmony_ci	__raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	rc = iop3xx_i2c_wait_rx_done(iop3xx_adap, &status);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	*byte = __raw_readl(iop3xx_adap->ioaddr + DBR_OFFSET);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return rc;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int
3018c2ecf20Sopenharmony_ciiop3xx_i2c_writebytes(struct i2c_adapter *i2c_adap, const char *buf, int count)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
3048c2ecf20Sopenharmony_ci	int ii;
3058c2ecf20Sopenharmony_ci	int rc = 0;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	for (ii = 0; rc == 0 && ii != count; ++ii)
3088c2ecf20Sopenharmony_ci		rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii==count-1);
3098c2ecf20Sopenharmony_ci	return rc;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic int
3138c2ecf20Sopenharmony_ciiop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
3168c2ecf20Sopenharmony_ci	int ii;
3178c2ecf20Sopenharmony_ci	int rc = 0;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	for (ii = 0; rc == 0 && ii != count; ++ii)
3208c2ecf20Sopenharmony_ci		rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii==count-1);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return rc;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci/*
3268c2ecf20Sopenharmony_ci * Description:  This function implements combined transactions.  Combined
3278c2ecf20Sopenharmony_ci * transactions consist of combinations of reading and writing blocks of data.
3288c2ecf20Sopenharmony_ci * FROM THE SAME ADDRESS
3298c2ecf20Sopenharmony_ci * Each transfer (i.e. a read or a write) is separated by a repeated start
3308c2ecf20Sopenharmony_ci * condition.
3318c2ecf20Sopenharmony_ci */
3328c2ecf20Sopenharmony_cistatic int
3338c2ecf20Sopenharmony_ciiop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg* pmsg)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
3368c2ecf20Sopenharmony_ci	int rc;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	rc = iop3xx_i2c_send_target_addr(iop3xx_adap, pmsg);
3398c2ecf20Sopenharmony_ci	if (rc < 0) {
3408c2ecf20Sopenharmony_ci		return rc;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if ((pmsg->flags&I2C_M_RD)) {
3448c2ecf20Sopenharmony_ci		return iop3xx_i2c_readbytes(i2c_adap, pmsg->buf, pmsg->len);
3458c2ecf20Sopenharmony_ci	} else {
3468c2ecf20Sopenharmony_ci		return iop3xx_i2c_writebytes(i2c_adap, pmsg->buf, pmsg->len);
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci/*
3518c2ecf20Sopenharmony_ci * master_xfer() - main read/write entry
3528c2ecf20Sopenharmony_ci */
3538c2ecf20Sopenharmony_cistatic int
3548c2ecf20Sopenharmony_ciiop3xx_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
3558c2ecf20Sopenharmony_ci				int num)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
3588c2ecf20Sopenharmony_ci	int im = 0;
3598c2ecf20Sopenharmony_ci	int ret = 0;
3608c2ecf20Sopenharmony_ci	int status;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	iop3xx_i2c_wait_idle(iop3xx_adap, &status);
3638c2ecf20Sopenharmony_ci	iop3xx_i2c_reset(iop3xx_adap);
3648c2ecf20Sopenharmony_ci	iop3xx_i2c_enable(iop3xx_adap);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	for (im = 0; ret == 0 && im != num; im++) {
3678c2ecf20Sopenharmony_ci		ret = iop3xx_i2c_handle_msg(i2c_adap, &msgs[im]);
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	iop3xx_i2c_transaction_cleanup(iop3xx_adap);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if(ret)
3738c2ecf20Sopenharmony_ci		return ret;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	return im;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic u32
3798c2ecf20Sopenharmony_ciiop3xx_i2c_func(struct i2c_adapter *adap)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic const struct i2c_algorithm iop3xx_i2c_algo = {
3858c2ecf20Sopenharmony_ci	.master_xfer	= iop3xx_i2c_master_xfer,
3868c2ecf20Sopenharmony_ci	.functionality	= iop3xx_i2c_func,
3878c2ecf20Sopenharmony_ci};
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int
3908c2ecf20Sopenharmony_ciiop3xx_i2c_remove(struct platform_device *pdev)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	struct i2c_adapter *padapter = platform_get_drvdata(pdev);
3938c2ecf20Sopenharmony_ci	struct i2c_algo_iop3xx_data *adapter_data =
3948c2ecf20Sopenharmony_ci		(struct i2c_algo_iop3xx_data *)padapter->algo_data;
3958c2ecf20Sopenharmony_ci	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3968c2ecf20Sopenharmony_ci	unsigned long cr = __raw_readl(adapter_data->ioaddr + CR_OFFSET);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/*
3998c2ecf20Sopenharmony_ci	 * Disable the actual HW unit
4008c2ecf20Sopenharmony_ci	 */
4018c2ecf20Sopenharmony_ci	cr &= ~(IOP3XX_ICR_ALD_IE | IOP3XX_ICR_BERR_IE |
4028c2ecf20Sopenharmony_ci		IOP3XX_ICR_RXFULL_IE | IOP3XX_ICR_TXEMPTY_IE);
4038c2ecf20Sopenharmony_ci	__raw_writel(cr, adapter_data->ioaddr + CR_OFFSET);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	iounmap(adapter_data->ioaddr);
4068c2ecf20Sopenharmony_ci	release_mem_region(res->start, IOP3XX_I2C_IO_SIZE);
4078c2ecf20Sopenharmony_ci	kfree(adapter_data);
4088c2ecf20Sopenharmony_ci	kfree(padapter);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic int
4148c2ecf20Sopenharmony_ciiop3xx_i2c_probe(struct platform_device *pdev)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	struct resource *res;
4178c2ecf20Sopenharmony_ci	int ret, irq;
4188c2ecf20Sopenharmony_ci	struct i2c_adapter *new_adapter;
4198c2ecf20Sopenharmony_ci	struct i2c_algo_iop3xx_data *adapter_data;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	new_adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
4228c2ecf20Sopenharmony_ci	if (!new_adapter) {
4238c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4248c2ecf20Sopenharmony_ci		goto out;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	adapter_data = kzalloc(sizeof(struct i2c_algo_iop3xx_data), GFP_KERNEL);
4288c2ecf20Sopenharmony_ci	if (!adapter_data) {
4298c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4308c2ecf20Sopenharmony_ci		goto free_adapter;
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	adapter_data->gpio_scl = devm_gpiod_get_optional(&pdev->dev,
4348c2ecf20Sopenharmony_ci							 "scl",
4358c2ecf20Sopenharmony_ci							 GPIOD_ASIS);
4368c2ecf20Sopenharmony_ci	if (IS_ERR(adapter_data->gpio_scl)) {
4378c2ecf20Sopenharmony_ci		ret = PTR_ERR(adapter_data->gpio_scl);
4388c2ecf20Sopenharmony_ci		goto free_both;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci	adapter_data->gpio_sda = devm_gpiod_get_optional(&pdev->dev,
4418c2ecf20Sopenharmony_ci							 "sda",
4428c2ecf20Sopenharmony_ci							 GPIOD_ASIS);
4438c2ecf20Sopenharmony_ci	if (IS_ERR(adapter_data->gpio_sda)) {
4448c2ecf20Sopenharmony_ci		ret = PTR_ERR(adapter_data->gpio_sda);
4458c2ecf20Sopenharmony_ci		goto free_both;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
4498c2ecf20Sopenharmony_ci	if (!res) {
4508c2ecf20Sopenharmony_ci		ret = -ENODEV;
4518c2ecf20Sopenharmony_ci		goto free_both;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if (!request_mem_region(res->start, IOP3XX_I2C_IO_SIZE, pdev->name)) {
4558c2ecf20Sopenharmony_ci		ret = -EBUSY;
4568c2ecf20Sopenharmony_ci		goto free_both;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	/* set the adapter enumeration # */
4608c2ecf20Sopenharmony_ci	adapter_data->id = i2c_id++;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	adapter_data->ioaddr = ioremap(res->start, IOP3XX_I2C_IO_SIZE);
4638c2ecf20Sopenharmony_ci	if (!adapter_data->ioaddr) {
4648c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4658c2ecf20Sopenharmony_ci		goto release_region;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
4698c2ecf20Sopenharmony_ci	if (irq < 0) {
4708c2ecf20Sopenharmony_ci		ret = irq;
4718c2ecf20Sopenharmony_ci		goto unmap;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci	ret = request_irq(irq, iop3xx_i2c_irq_handler, 0,
4748c2ecf20Sopenharmony_ci				pdev->name, adapter_data);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	if (ret)
4778c2ecf20Sopenharmony_ci		goto unmap;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	memcpy(new_adapter->name, pdev->name, strlen(pdev->name));
4808c2ecf20Sopenharmony_ci	new_adapter->owner = THIS_MODULE;
4818c2ecf20Sopenharmony_ci	new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
4828c2ecf20Sopenharmony_ci	new_adapter->dev.parent = &pdev->dev;
4838c2ecf20Sopenharmony_ci	new_adapter->dev.of_node = pdev->dev.of_node;
4848c2ecf20Sopenharmony_ci	new_adapter->nr = pdev->id;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/*
4878c2ecf20Sopenharmony_ci	 * Default values...should these come in from board code?
4888c2ecf20Sopenharmony_ci	 */
4898c2ecf20Sopenharmony_ci	new_adapter->timeout = HZ;
4908c2ecf20Sopenharmony_ci	new_adapter->algo = &iop3xx_i2c_algo;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	init_waitqueue_head(&adapter_data->waitq);
4938c2ecf20Sopenharmony_ci	spin_lock_init(&adapter_data->lock);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	iop3xx_i2c_reset(adapter_data);
4968c2ecf20Sopenharmony_ci	iop3xx_i2c_enable(adapter_data);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, new_adapter);
4998c2ecf20Sopenharmony_ci	new_adapter->algo_data = adapter_data;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	i2c_add_numbered_adapter(new_adapter);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	return 0;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ciunmap:
5068c2ecf20Sopenharmony_ci	iounmap(adapter_data->ioaddr);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cirelease_region:
5098c2ecf20Sopenharmony_ci	release_mem_region(res->start, IOP3XX_I2C_IO_SIZE);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cifree_both:
5128c2ecf20Sopenharmony_ci	kfree(adapter_data);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cifree_adapter:
5158c2ecf20Sopenharmony_ci	kfree(new_adapter);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ciout:
5188c2ecf20Sopenharmony_ci	return ret;
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic const struct of_device_id i2c_iop3xx_match[] = {
5228c2ecf20Sopenharmony_ci	{ .compatible = "intel,iop3xx-i2c", },
5238c2ecf20Sopenharmony_ci	{ .compatible = "intel,ixp4xx-i2c", },
5248c2ecf20Sopenharmony_ci	{},
5258c2ecf20Sopenharmony_ci};
5268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, i2c_iop3xx_match);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cistatic struct platform_driver iop3xx_i2c_driver = {
5298c2ecf20Sopenharmony_ci	.probe		= iop3xx_i2c_probe,
5308c2ecf20Sopenharmony_ci	.remove		= iop3xx_i2c_remove,
5318c2ecf20Sopenharmony_ci	.driver		= {
5328c2ecf20Sopenharmony_ci		.name	= "IOP3xx-I2C",
5338c2ecf20Sopenharmony_ci		.of_match_table = i2c_iop3xx_match,
5348c2ecf20Sopenharmony_ci	},
5358c2ecf20Sopenharmony_ci};
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cimodule_platform_driver(iop3xx_i2c_driver);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ciMODULE_AUTHOR("D-TACQ Solutions Ltd <www.d-tacq.com>");
5408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IOP3xx iic algorithm and driver");
5418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5428c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:IOP3xx-I2C");
543