18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci    Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci    National Semiconductor SCx200 ACCESS.bus support
68c2ecf20Sopenharmony_ci    Also supports the AMD CS5535 and AMD CS5536
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci    Based on i2c-keywest.c which is:
98c2ecf20Sopenharmony_ci        Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org>
108c2ecf20Sopenharmony_ci        Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci*/
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/errno.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/init.h>
208c2ecf20Sopenharmony_ci#include <linux/i2c.h>
218c2ecf20Sopenharmony_ci#include <linux/pci.h>
228c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
238c2ecf20Sopenharmony_ci#include <linux/delay.h>
248c2ecf20Sopenharmony_ci#include <linux/mutex.h>
258c2ecf20Sopenharmony_ci#include <linux/slab.h>
268c2ecf20Sopenharmony_ci#include <linux/io.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <linux/scx200.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NatSemi SCx200 ACCESS.bus Driver");
328c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:cs5535-smb");
338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define MAX_DEVICES 4
368c2ecf20Sopenharmony_cistatic int base[MAX_DEVICES] = { 0x820, 0x840 };
378c2ecf20Sopenharmony_cimodule_param_hw_array(base, int, ioport, NULL, 0);
388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(base, "Base addresses for the ACCESS.bus controllers");
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define POLL_TIMEOUT	(HZ/5)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cienum scx200_acb_state {
438c2ecf20Sopenharmony_ci	state_idle,
448c2ecf20Sopenharmony_ci	state_address,
458c2ecf20Sopenharmony_ci	state_command,
468c2ecf20Sopenharmony_ci	state_repeat_start,
478c2ecf20Sopenharmony_ci	state_quick,
488c2ecf20Sopenharmony_ci	state_read,
498c2ecf20Sopenharmony_ci	state_write,
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic const char *scx200_acb_state_name[] = {
538c2ecf20Sopenharmony_ci	"idle",
548c2ecf20Sopenharmony_ci	"address",
558c2ecf20Sopenharmony_ci	"command",
568c2ecf20Sopenharmony_ci	"repeat_start",
578c2ecf20Sopenharmony_ci	"quick",
588c2ecf20Sopenharmony_ci	"read",
598c2ecf20Sopenharmony_ci	"write",
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* Physical interface */
638c2ecf20Sopenharmony_cistruct scx200_acb_iface {
648c2ecf20Sopenharmony_ci	struct scx200_acb_iface *next;
658c2ecf20Sopenharmony_ci	struct i2c_adapter adapter;
668c2ecf20Sopenharmony_ci	unsigned base;
678c2ecf20Sopenharmony_ci	struct mutex mutex;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* State machine data */
708c2ecf20Sopenharmony_ci	enum scx200_acb_state state;
718c2ecf20Sopenharmony_ci	int result;
728c2ecf20Sopenharmony_ci	u8 address_byte;
738c2ecf20Sopenharmony_ci	u8 command;
748c2ecf20Sopenharmony_ci	u8 *ptr;
758c2ecf20Sopenharmony_ci	char needs_reset;
768c2ecf20Sopenharmony_ci	unsigned len;
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/* Register Definitions */
808c2ecf20Sopenharmony_ci#define ACBSDA		(iface->base + 0)
818c2ecf20Sopenharmony_ci#define ACBST		(iface->base + 1)
828c2ecf20Sopenharmony_ci#define    ACBST_SDAST		0x40 /* SDA Status */
838c2ecf20Sopenharmony_ci#define    ACBST_BER		0x20
848c2ecf20Sopenharmony_ci#define    ACBST_NEGACK		0x10 /* Negative Acknowledge */
858c2ecf20Sopenharmony_ci#define    ACBST_STASTR		0x08 /* Stall After Start */
868c2ecf20Sopenharmony_ci#define    ACBST_MASTER		0x02
878c2ecf20Sopenharmony_ci#define ACBCST		(iface->base + 2)
888c2ecf20Sopenharmony_ci#define    ACBCST_BB		0x02
898c2ecf20Sopenharmony_ci#define ACBCTL1		(iface->base + 3)
908c2ecf20Sopenharmony_ci#define    ACBCTL1_STASTRE	0x80
918c2ecf20Sopenharmony_ci#define    ACBCTL1_NMINTE	0x40
928c2ecf20Sopenharmony_ci#define    ACBCTL1_ACK		0x10
938c2ecf20Sopenharmony_ci#define    ACBCTL1_STOP		0x02
948c2ecf20Sopenharmony_ci#define    ACBCTL1_START	0x01
958c2ecf20Sopenharmony_ci#define ACBADDR		(iface->base + 4)
968c2ecf20Sopenharmony_ci#define ACBCTL2		(iface->base + 5)
978c2ecf20Sopenharmony_ci#define    ACBCTL2_ENABLE	0x01
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/************************************************************************/
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	const char *errmsg;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	dev_dbg(&iface->adapter.dev, "state %s, status = 0x%02x\n",
1068c2ecf20Sopenharmony_ci		scx200_acb_state_name[iface->state], status);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (status & ACBST_BER) {
1098c2ecf20Sopenharmony_ci		errmsg = "bus error";
1108c2ecf20Sopenharmony_ci		goto error;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci	if (!(status & ACBST_MASTER)) {
1138c2ecf20Sopenharmony_ci		errmsg = "not master";
1148c2ecf20Sopenharmony_ci		goto error;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci	if (status & ACBST_NEGACK) {
1178c2ecf20Sopenharmony_ci		dev_dbg(&iface->adapter.dev, "negative ack in state %s\n",
1188c2ecf20Sopenharmony_ci			scx200_acb_state_name[iface->state]);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci		iface->state = state_idle;
1218c2ecf20Sopenharmony_ci		iface->result = -ENXIO;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
1248c2ecf20Sopenharmony_ci		outb(ACBST_STASTR | ACBST_NEGACK, ACBST);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		/* Reset the status register */
1278c2ecf20Sopenharmony_ci		outb(0, ACBST);
1288c2ecf20Sopenharmony_ci		return;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	switch (iface->state) {
1328c2ecf20Sopenharmony_ci	case state_idle:
1338c2ecf20Sopenharmony_ci		dev_warn(&iface->adapter.dev, "interrupt in idle state\n");
1348c2ecf20Sopenharmony_ci		break;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	case state_address:
1378c2ecf20Sopenharmony_ci		/* Do a pointer write first */
1388c2ecf20Sopenharmony_ci		outb(iface->address_byte & ~1, ACBSDA);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci		iface->state = state_command;
1418c2ecf20Sopenharmony_ci		break;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	case state_command:
1448c2ecf20Sopenharmony_ci		outb(iface->command, ACBSDA);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		if (iface->address_byte & 1)
1478c2ecf20Sopenharmony_ci			iface->state = state_repeat_start;
1488c2ecf20Sopenharmony_ci		else
1498c2ecf20Sopenharmony_ci			iface->state = state_write;
1508c2ecf20Sopenharmony_ci		break;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	case state_repeat_start:
1538c2ecf20Sopenharmony_ci		outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1);
1548c2ecf20Sopenharmony_ci		fallthrough;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	case state_quick:
1578c2ecf20Sopenharmony_ci		if (iface->address_byte & 1) {
1588c2ecf20Sopenharmony_ci			if (iface->len == 1)
1598c2ecf20Sopenharmony_ci				outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1);
1608c2ecf20Sopenharmony_ci			else
1618c2ecf20Sopenharmony_ci				outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1);
1628c2ecf20Sopenharmony_ci			outb(iface->address_byte, ACBSDA);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci			iface->state = state_read;
1658c2ecf20Sopenharmony_ci		} else {
1668c2ecf20Sopenharmony_ci			outb(iface->address_byte, ACBSDA);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci			iface->state = state_write;
1698c2ecf20Sopenharmony_ci		}
1708c2ecf20Sopenharmony_ci		break;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	case state_read:
1738c2ecf20Sopenharmony_ci		/* Set ACK if _next_ byte will be the last one */
1748c2ecf20Sopenharmony_ci		if (iface->len == 2)
1758c2ecf20Sopenharmony_ci			outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1);
1768c2ecf20Sopenharmony_ci		else
1778c2ecf20Sopenharmony_ci			outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci		if (iface->len == 1) {
1808c2ecf20Sopenharmony_ci			iface->result = 0;
1818c2ecf20Sopenharmony_ci			iface->state = state_idle;
1828c2ecf20Sopenharmony_ci			outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		*iface->ptr++ = inb(ACBSDA);
1868c2ecf20Sopenharmony_ci		--iface->len;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci		break;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	case state_write:
1918c2ecf20Sopenharmony_ci		if (iface->len == 0) {
1928c2ecf20Sopenharmony_ci			iface->result = 0;
1938c2ecf20Sopenharmony_ci			iface->state = state_idle;
1948c2ecf20Sopenharmony_ci			outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
1958c2ecf20Sopenharmony_ci			break;
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		outb(*iface->ptr++, ACBSDA);
1998c2ecf20Sopenharmony_ci		--iface->len;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		break;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci error:
2078c2ecf20Sopenharmony_ci	dev_err(&iface->adapter.dev,
2088c2ecf20Sopenharmony_ci		"%s in state %s (addr=0x%02x, len=%d, status=0x%02x)\n", errmsg,
2098c2ecf20Sopenharmony_ci		scx200_acb_state_name[iface->state], iface->address_byte,
2108c2ecf20Sopenharmony_ci		iface->len, status);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	iface->state = state_idle;
2138c2ecf20Sopenharmony_ci	iface->result = -EIO;
2148c2ecf20Sopenharmony_ci	iface->needs_reset = 1;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic void scx200_acb_poll(struct scx200_acb_iface *iface)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	u8 status;
2208c2ecf20Sopenharmony_ci	unsigned long timeout;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	timeout = jiffies + POLL_TIMEOUT;
2238c2ecf20Sopenharmony_ci	while (1) {
2248c2ecf20Sopenharmony_ci		status = inb(ACBST);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		/* Reset the status register to avoid the hang */
2278c2ecf20Sopenharmony_ci		outb(0, ACBST);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		if ((status & (ACBST_SDAST|ACBST_BER|ACBST_NEGACK)) != 0) {
2308c2ecf20Sopenharmony_ci			scx200_acb_machine(iface, status);
2318c2ecf20Sopenharmony_ci			return;
2328c2ecf20Sopenharmony_ci		}
2338c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout))
2348c2ecf20Sopenharmony_ci			break;
2358c2ecf20Sopenharmony_ci		cpu_relax();
2368c2ecf20Sopenharmony_ci		cond_resched();
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	dev_err(&iface->adapter.dev, "timeout in state %s\n",
2408c2ecf20Sopenharmony_ci		scx200_acb_state_name[iface->state]);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	iface->state = state_idle;
2438c2ecf20Sopenharmony_ci	iface->result = -EIO;
2448c2ecf20Sopenharmony_ci	iface->needs_reset = 1;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic void scx200_acb_reset(struct scx200_acb_iface *iface)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	/* Disable the ACCESS.bus device and Configure the SCL
2508c2ecf20Sopenharmony_ci	   frequency: 16 clock cycles */
2518c2ecf20Sopenharmony_ci	outb(0x70, ACBCTL2);
2528c2ecf20Sopenharmony_ci	/* Polling mode */
2538c2ecf20Sopenharmony_ci	outb(0, ACBCTL1);
2548c2ecf20Sopenharmony_ci	/* Disable slave address */
2558c2ecf20Sopenharmony_ci	outb(0, ACBADDR);
2568c2ecf20Sopenharmony_ci	/* Enable the ACCESS.bus device */
2578c2ecf20Sopenharmony_ci	outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2);
2588c2ecf20Sopenharmony_ci	/* Free STALL after START */
2598c2ecf20Sopenharmony_ci	outb(inb(ACBCTL1) & ~(ACBCTL1_STASTRE | ACBCTL1_NMINTE), ACBCTL1);
2608c2ecf20Sopenharmony_ci	/* Send a STOP */
2618c2ecf20Sopenharmony_ci	outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
2628c2ecf20Sopenharmony_ci	/* Clear BER, NEGACK and STASTR bits */
2638c2ecf20Sopenharmony_ci	outb(ACBST_BER | ACBST_NEGACK | ACBST_STASTR, ACBST);
2648c2ecf20Sopenharmony_ci	/* Clear BB bit */
2658c2ecf20Sopenharmony_ci	outb(inb(ACBCST) | ACBCST_BB, ACBCST);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic s32 scx200_acb_smbus_xfer(struct i2c_adapter *adapter,
2698c2ecf20Sopenharmony_ci				 u16 address, unsigned short flags,
2708c2ecf20Sopenharmony_ci				 char rw, u8 command, int size,
2718c2ecf20Sopenharmony_ci				 union i2c_smbus_data *data)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct scx200_acb_iface *iface = i2c_get_adapdata(adapter);
2748c2ecf20Sopenharmony_ci	int len;
2758c2ecf20Sopenharmony_ci	u8 *buffer;
2768c2ecf20Sopenharmony_ci	u16 cur_word;
2778c2ecf20Sopenharmony_ci	int rc;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	switch (size) {
2808c2ecf20Sopenharmony_ci	case I2C_SMBUS_QUICK:
2818c2ecf20Sopenharmony_ci		len = 0;
2828c2ecf20Sopenharmony_ci		buffer = NULL;
2838c2ecf20Sopenharmony_ci		break;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	case I2C_SMBUS_BYTE:
2868c2ecf20Sopenharmony_ci		len = 1;
2878c2ecf20Sopenharmony_ci		buffer = rw ? &data->byte : &command;
2888c2ecf20Sopenharmony_ci		break;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	case I2C_SMBUS_BYTE_DATA:
2918c2ecf20Sopenharmony_ci		len = 1;
2928c2ecf20Sopenharmony_ci		buffer = &data->byte;
2938c2ecf20Sopenharmony_ci		break;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	case I2C_SMBUS_WORD_DATA:
2968c2ecf20Sopenharmony_ci		len = 2;
2978c2ecf20Sopenharmony_ci		cur_word = cpu_to_le16(data->word);
2988c2ecf20Sopenharmony_ci		buffer = (u8 *)&cur_word;
2998c2ecf20Sopenharmony_ci		break;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	case I2C_SMBUS_I2C_BLOCK_DATA:
3028c2ecf20Sopenharmony_ci		len = data->block[0];
3038c2ecf20Sopenharmony_ci		if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
3048c2ecf20Sopenharmony_ci			return -EINVAL;
3058c2ecf20Sopenharmony_ci		buffer = &data->block[1];
3068c2ecf20Sopenharmony_ci		break;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	default:
3098c2ecf20Sopenharmony_ci		return -EINVAL;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	dev_dbg(&adapter->dev,
3138c2ecf20Sopenharmony_ci		"size=%d, address=0x%x, command=0x%x, len=%d, read=%d\n",
3148c2ecf20Sopenharmony_ci		size, address, command, len, rw);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (!len && rw == I2C_SMBUS_READ) {
3178c2ecf20Sopenharmony_ci		dev_dbg(&adapter->dev, "zero length read\n");
3188c2ecf20Sopenharmony_ci		return -EINVAL;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	mutex_lock(&iface->mutex);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	iface->address_byte = (address << 1) | rw;
3248c2ecf20Sopenharmony_ci	iface->command = command;
3258c2ecf20Sopenharmony_ci	iface->ptr = buffer;
3268c2ecf20Sopenharmony_ci	iface->len = len;
3278c2ecf20Sopenharmony_ci	iface->result = -EINVAL;
3288c2ecf20Sopenharmony_ci	iface->needs_reset = 0;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE)
3338c2ecf20Sopenharmony_ci		iface->state = state_quick;
3348c2ecf20Sopenharmony_ci	else
3358c2ecf20Sopenharmony_ci		iface->state = state_address;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	while (iface->state != state_idle)
3388c2ecf20Sopenharmony_ci		scx200_acb_poll(iface);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (iface->needs_reset)
3418c2ecf20Sopenharmony_ci		scx200_acb_reset(iface);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	rc = iface->result;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	mutex_unlock(&iface->mutex);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (rc == 0 && size == I2C_SMBUS_WORD_DATA && rw == I2C_SMBUS_READ)
3488c2ecf20Sopenharmony_ci		data->word = le16_to_cpu(cur_word);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci#ifdef DEBUG
3518c2ecf20Sopenharmony_ci	dev_dbg(&adapter->dev, "transfer done, result: %d", rc);
3528c2ecf20Sopenharmony_ci	if (buffer) {
3538c2ecf20Sopenharmony_ci		int i;
3548c2ecf20Sopenharmony_ci		printk(" data:");
3558c2ecf20Sopenharmony_ci		for (i = 0; i < len; ++i)
3568c2ecf20Sopenharmony_ci			printk(" %02x", buffer[i]);
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci	printk("\n");
3598c2ecf20Sopenharmony_ci#endif
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	return rc;
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic u32 scx200_acb_func(struct i2c_adapter *adapter)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
3678c2ecf20Sopenharmony_ci	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
3688c2ecf20Sopenharmony_ci	       I2C_FUNC_SMBUS_I2C_BLOCK;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci/* For now, we only handle combined mode (smbus) */
3728c2ecf20Sopenharmony_cistatic const struct i2c_algorithm scx200_acb_algorithm = {
3738c2ecf20Sopenharmony_ci	.smbus_xfer	= scx200_acb_smbus_xfer,
3748c2ecf20Sopenharmony_ci	.functionality	= scx200_acb_func,
3758c2ecf20Sopenharmony_ci};
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic struct scx200_acb_iface *scx200_acb_list;
3788c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(scx200_acb_list_mutex);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int scx200_acb_probe(struct scx200_acb_iface *iface)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	u8 val;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* Disable the ACCESS.bus device and Configure the SCL
3858c2ecf20Sopenharmony_ci	   frequency: 16 clock cycles */
3868c2ecf20Sopenharmony_ci	outb(0x70, ACBCTL2);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (inb(ACBCTL2) != 0x70) {
3898c2ecf20Sopenharmony_ci		pr_debug("ACBCTL2 readback failed\n");
3908c2ecf20Sopenharmony_ci		return -ENXIO;
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	val = inb(ACBCTL1);
3968c2ecf20Sopenharmony_ci	if (val) {
3978c2ecf20Sopenharmony_ci		pr_debug("disabled, but ACBCTL1=0x%02x\n", val);
3988c2ecf20Sopenharmony_ci		return -ENXIO;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	val = inb(ACBCTL1);
4068c2ecf20Sopenharmony_ci	if ((val & ACBCTL1_NMINTE) != ACBCTL1_NMINTE) {
4078c2ecf20Sopenharmony_ci		pr_debug("enabled, but NMINTE won't be set, ACBCTL1=0x%02x\n",
4088c2ecf20Sopenharmony_ci			 val);
4098c2ecf20Sopenharmony_ci		return -ENXIO;
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	return 0;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic struct scx200_acb_iface *scx200_create_iface(const char *text,
4168c2ecf20Sopenharmony_ci		struct device *dev, int index)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	struct scx200_acb_iface *iface;
4198c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	iface = kzalloc(sizeof(*iface), GFP_KERNEL);
4228c2ecf20Sopenharmony_ci	if (!iface)
4238c2ecf20Sopenharmony_ci		return NULL;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	adapter = &iface->adapter;
4268c2ecf20Sopenharmony_ci	i2c_set_adapdata(adapter, iface);
4278c2ecf20Sopenharmony_ci	snprintf(adapter->name, sizeof(adapter->name), "%s ACB%d", text, index);
4288c2ecf20Sopenharmony_ci	adapter->owner = THIS_MODULE;
4298c2ecf20Sopenharmony_ci	adapter->algo = &scx200_acb_algorithm;
4308c2ecf20Sopenharmony_ci	adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
4318c2ecf20Sopenharmony_ci	adapter->dev.parent = dev;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	mutex_init(&iface->mutex);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	return iface;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic int scx200_acb_create(struct scx200_acb_iface *iface)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter;
4418c2ecf20Sopenharmony_ci	int rc;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	adapter = &iface->adapter;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	rc = scx200_acb_probe(iface);
4468c2ecf20Sopenharmony_ci	if (rc) {
4478c2ecf20Sopenharmony_ci		pr_warn("probe failed\n");
4488c2ecf20Sopenharmony_ci		return rc;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	scx200_acb_reset(iface);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (i2c_add_adapter(adapter) < 0) {
4548c2ecf20Sopenharmony_ci		pr_err("failed to register\n");
4558c2ecf20Sopenharmony_ci		return -ENODEV;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (!adapter->dev.parent) {
4598c2ecf20Sopenharmony_ci		/* If there's no dev, we're tracking (ISA) ifaces manually */
4608c2ecf20Sopenharmony_ci		mutex_lock(&scx200_acb_list_mutex);
4618c2ecf20Sopenharmony_ci		iface->next = scx200_acb_list;
4628c2ecf20Sopenharmony_ci		scx200_acb_list = iface;
4638c2ecf20Sopenharmony_ci		mutex_unlock(&scx200_acb_list_mutex);
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	return 0;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic struct scx200_acb_iface *scx200_create_dev(const char *text,
4708c2ecf20Sopenharmony_ci		unsigned long base, int index, struct device *dev)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	struct scx200_acb_iface *iface;
4738c2ecf20Sopenharmony_ci	int rc;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	iface = scx200_create_iface(text, dev, index);
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	if (iface == NULL)
4788c2ecf20Sopenharmony_ci		return NULL;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (!request_region(base, 8, iface->adapter.name)) {
4818c2ecf20Sopenharmony_ci		pr_err("can't allocate io 0x%lx-0x%lx\n", base, base + 8 - 1);
4828c2ecf20Sopenharmony_ci		goto errout_free;
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	iface->base = base;
4868c2ecf20Sopenharmony_ci	rc = scx200_acb_create(iface);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (rc == 0)
4898c2ecf20Sopenharmony_ci		return iface;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	release_region(base, 8);
4928c2ecf20Sopenharmony_ci errout_free:
4938c2ecf20Sopenharmony_ci	kfree(iface);
4948c2ecf20Sopenharmony_ci	return NULL;
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic int scx200_probe(struct platform_device *pdev)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	struct scx200_acb_iface *iface;
5008c2ecf20Sopenharmony_ci	struct resource *res;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
5038c2ecf20Sopenharmony_ci	if (!res) {
5048c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "can't fetch device resource info\n");
5058c2ecf20Sopenharmony_ci		return -ENODEV;
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	iface = scx200_create_dev("CS5535", res->start, 0, &pdev->dev);
5098c2ecf20Sopenharmony_ci	if (!iface)
5108c2ecf20Sopenharmony_ci		return -EIO;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "SCx200 device '%s' registered\n",
5138c2ecf20Sopenharmony_ci			iface->adapter.name);
5148c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, iface);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	return 0;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic void scx200_cleanup_iface(struct scx200_acb_iface *iface)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	i2c_del_adapter(&iface->adapter);
5228c2ecf20Sopenharmony_ci	release_region(iface->base, 8);
5238c2ecf20Sopenharmony_ci	kfree(iface);
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic int scx200_remove(struct platform_device *pdev)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	struct scx200_acb_iface *iface;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	iface = platform_get_drvdata(pdev);
5318c2ecf20Sopenharmony_ci	scx200_cleanup_iface(iface);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	return 0;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic struct platform_driver scx200_pci_driver = {
5378c2ecf20Sopenharmony_ci	.driver = {
5388c2ecf20Sopenharmony_ci		.name = "cs5535-smb",
5398c2ecf20Sopenharmony_ci	},
5408c2ecf20Sopenharmony_ci	.probe = scx200_probe,
5418c2ecf20Sopenharmony_ci	.remove = scx200_remove,
5428c2ecf20Sopenharmony_ci};
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistatic const struct pci_device_id scx200_isa[] = {
5458c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) },
5468c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) },
5478c2ecf20Sopenharmony_ci	{ 0, }
5488c2ecf20Sopenharmony_ci};
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic __init void scx200_scan_isa(void)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	int i;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (!pci_dev_present(scx200_isa))
5558c2ecf20Sopenharmony_ci		return;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_DEVICES; ++i) {
5588c2ecf20Sopenharmony_ci		if (base[i] == 0)
5598c2ecf20Sopenharmony_ci			continue;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci		/* XXX: should we care about failures? */
5628c2ecf20Sopenharmony_ci		scx200_create_dev("SCx200", base[i], i, NULL);
5638c2ecf20Sopenharmony_ci	}
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic int __init scx200_acb_init(void)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	pr_debug("NatSemi SCx200 ACCESS.bus Driver\n");
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	/* First scan for ISA-based devices */
5718c2ecf20Sopenharmony_ci	scx200_scan_isa();	/* XXX: should we care about errors? */
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	/* If at least one bus was created, init must succeed */
5748c2ecf20Sopenharmony_ci	if (scx200_acb_list)
5758c2ecf20Sopenharmony_ci		return 0;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	/* No ISA devices; register the platform driver for PCI-based devices */
5788c2ecf20Sopenharmony_ci	return platform_driver_register(&scx200_pci_driver);
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cistatic void __exit scx200_acb_cleanup(void)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	struct scx200_acb_iface *iface;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	platform_driver_unregister(&scx200_pci_driver);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	mutex_lock(&scx200_acb_list_mutex);
5888c2ecf20Sopenharmony_ci	while ((iface = scx200_acb_list) != NULL) {
5898c2ecf20Sopenharmony_ci		scx200_acb_list = iface->next;
5908c2ecf20Sopenharmony_ci		mutex_unlock(&scx200_acb_list_mutex);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci		scx200_cleanup_iface(iface);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci		mutex_lock(&scx200_acb_list_mutex);
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci	mutex_unlock(&scx200_acb_list_mutex);
5978c2ecf20Sopenharmony_ci}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cimodule_init(scx200_acb_init);
6008c2ecf20Sopenharmony_cimodule_exit(scx200_acb_cleanup);
601