1e21e245bcd9d5244735799387d14421789b20557David S. Miller/* bbc_i2c.c: I2C low-level driver for BBC device on UltraSPARC-III
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *            platforms.
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
4e21e245bcd9d5244735799387d14421789b20557David S. Miller * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net)
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sched.h>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/wait.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
16e21e245bcd9d5244735799387d14421789b20557David S. Miller#include <linux/of.h>
17e21e245bcd9d5244735799387d14421789b20557David S. Miller#include <linux/of_device.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/bbc.h>
194b5dff76a70cb1d8b935b8b93fe0df0bbe66640dDavid S. Miller#include <asm/io.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "bbc_i2c.h"
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Convert this driver to use i2c bus layer someday... */
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_PIN	0x80
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_ESO	0x40
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_ES1	0x20
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_ES2	0x10
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_ENI	0x08
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_STA	0x04
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_STO	0x02
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_ACK	0x01
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_START    (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ENI | I2C_PCF_STA | I2C_PCF_ACK)
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_STOP     (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK)
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_REPSTART (              I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK)
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_IDLE     (I2C_PCF_PIN | I2C_PCF_ESO               | I2C_PCF_ACK)
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_INI 0x40   /* 1 if not initialized */
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_STS 0x20
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_BER 0x10
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_AD0 0x08
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_LRB 0x08
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_AAS 0x04
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_LAB 0x02
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define I2C_PCF_BB  0x01
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The BBC devices have two I2C controllers.  The first I2C controller
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * connects mainly to configuration proms (NVRAM, cpu configuration,
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * dimm types, etc.).  Whereas the second I2C controller connects to
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * environmental control devices such as fans and temperature sensors.
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The second controller also connects to the smartcard reader, if present.
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
542dc11581376829303b98eadb2de253bee065a56aGrant Likelystatic void set_device_claimage(struct bbc_i2c_bus *bp, struct platform_device *op, int val)
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < NUM_CHILDREN; i++) {
59e21e245bcd9d5244735799387d14421789b20557David S. Miller		if (bp->devs[i].device == op) {
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			bp->devs[i].client_claimed = val;
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return;
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define claim_device(BP,ECHILD)		set_device_claimage(BP,ECHILD,1)
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define release_device(BP,ECHILD)	set_device_claimage(BP,ECHILD,0)
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
692dc11581376829303b98eadb2de253bee065a56aGrant Likelystruct platform_device *bbc_i2c_getdev(struct bbc_i2c_bus *bp, int index)
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
712dc11581376829303b98eadb2de253bee065a56aGrant Likely	struct platform_device *op = NULL;
72e21e245bcd9d5244735799387d14421789b20557David S. Miller	int curidx = 0, i;
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
74e21e245bcd9d5244735799387d14421789b20557David S. Miller	for (i = 0; i < NUM_CHILDREN; i++) {
75e21e245bcd9d5244735799387d14421789b20557David S. Miller		if (!(op = bp->devs[i].device))
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
77e21e245bcd9d5244735799387d14421789b20557David S. Miller		if (curidx == index)
78e21e245bcd9d5244735799387d14421789b20557David S. Miller			goto out;
79e21e245bcd9d5244735799387d14421789b20557David S. Miller		op = NULL;
80e21e245bcd9d5244735799387d14421789b20557David S. Miller		curidx++;
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (curidx == index)
85e21e245bcd9d5244735799387d14421789b20557David S. Miller		return op;
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NULL;
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
892dc11581376829303b98eadb2de253bee065a56aGrant Likelystruct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct platform_device *op)
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct bbc_i2c_client *client;
92e21e245bcd9d5244735799387d14421789b20557David S. Miller	const u32 *reg;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
94dd00cc486ab1c17049a535413d1751ef3482141cYoann Padioleau	client = kzalloc(sizeof(*client), GFP_KERNEL);
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!client)
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	client->bp = bp;
98e21e245bcd9d5244735799387d14421789b20557David S. Miller	client->op = op;
99e21e245bcd9d5244735799387d14421789b20557David S. Miller
10061c7a080a5a061c976988fd4b844dfb468dda255Grant Likely	reg = of_get_property(op->dev.of_node, "reg", NULL);
101e21e245bcd9d5244735799387d14421789b20557David S. Miller	if (!reg) {
102e21e245bcd9d5244735799387d14421789b20557David S. Miller		kfree(client);
103e21e245bcd9d5244735799387d14421789b20557David S. Miller		return NULL;
104e21e245bcd9d5244735799387d14421789b20557David S. Miller	}
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
106e21e245bcd9d5244735799387d14421789b20557David S. Miller	client->bus = reg[0];
107e21e245bcd9d5244735799387d14421789b20557David S. Miller	client->address = reg[1];
108e21e245bcd9d5244735799387d14421789b20557David S. Miller
109e21e245bcd9d5244735799387d14421789b20557David S. Miller	claim_device(bp, op);
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return client;
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid bbc_i2c_detach(struct bbc_i2c_client *client)
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct bbc_i2c_bus *bp = client->bp;
1172dc11581376829303b98eadb2de253bee065a56aGrant Likely	struct platform_device *op = client->op;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
119e21e245bcd9d5244735799387d14421789b20557David S. Miller	release_device(bp, op);
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(client);
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wait_for_pin(struct bbc_i2c_bus *bp, u8 *status)
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	DECLARE_WAITQUEUE(wait, current);
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int limit = 32;
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret = 1;
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	bp->waiting = 1;
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	add_wait_queue(&bp->wq, &wait);
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (limit-- > 0) {
132f4c13638185c91a269c63fcfb980d89180cf43a1Roel Kluin		long val;
1333b36fb8471f8639d565b69c9a456a3ef9413df59David S. Miller
1343b36fb8471f8639d565b69c9a456a3ef9413df59David S. Miller		val = wait_event_interruptible_timeout(
1353b36fb8471f8639d565b69c9a456a3ef9413df59David S. Miller				bp->wq,
1363b36fb8471f8639d565b69c9a456a3ef9413df59David S. Miller				(((*status = readb(bp->i2c_control_regs + 0))
1373b36fb8471f8639d565b69c9a456a3ef9413df59David S. Miller				  & I2C_PCF_PIN) == 0),
1383b36fb8471f8639d565b69c9a456a3ef9413df59David S. Miller				msecs_to_jiffies(250));
1393b36fb8471f8639d565b69c9a456a3ef9413df59David S. Miller		if (val > 0) {
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ret = 0;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	remove_wait_queue(&bp->wq, &wait);
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	bp->waiting = 0;
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint bbc_i2c_writeb(struct bbc_i2c_client *client, unsigned char val, int off)
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct bbc_i2c_bus *bp = client->bp;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int address = client->address;
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 status;
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret = -1;
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bp->i2c_bussel_reg != NULL)
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		writeb(client->bus, bp->i2c_bussel_reg);
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(address, bp->i2c_control_regs + 0x1);
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0);
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wait_for_pin(bp, &status))
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(off, bp->i2c_control_regs + 0x1);
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wait_for_pin(bp, &status) ||
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    (status & I2C_PCF_LRB) != 0)
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(val, bp->i2c_control_regs + 0x1);
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wait_for_pin(bp, &status))
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = 0;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0);
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint bbc_i2c_readb(struct bbc_i2c_client *client, unsigned char *byte, int off)
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct bbc_i2c_bus *bp = client->bp;
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char address = client->address, status;
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret = -1;
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bp->i2c_bussel_reg != NULL)
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		writeb(client->bus, bp->i2c_bussel_reg);
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(address, bp->i2c_control_regs + 0x1);
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0);
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wait_for_pin(bp, &status))
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(off, bp->i2c_control_regs + 0x1);
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wait_for_pin(bp, &status) ||
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    (status & I2C_PCF_LRB) != 0)
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0);
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	address |= 0x1; /* READ */
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(address, bp->i2c_control_regs + 0x1);
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(I2C_PCF_START, bp->i2c_control_regs + 0x0);
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wait_for_pin(bp, &status))
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Set PIN back to one so the device sends the first
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * byte.
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	(void) readb(bp->i2c_control_regs + 0x1);
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wait_for_pin(bp, &status))
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(I2C_PCF_ESO | I2C_PCF_ENI, bp->i2c_control_regs + 0x0);
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*byte = readb(bp->i2c_control_regs + 0x1);
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wait_for_pin(bp, &status))
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = 0;
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(I2C_PCF_STOP, bp->i2c_control_regs + 0x0);
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	(void) readb(bp->i2c_control_regs + 0x1);
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint bbc_i2c_write_buf(struct bbc_i2c_client *client,
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      char *buf, int len, int off)
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret = 0;
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (len > 0) {
236e410471029ba99e85af5e2a1e7e747c7b4de2bc3Axel Lin		ret = bbc_i2c_writeb(client, *buf, off);
237e410471029ba99e85af5e2a1e7e747c7b4de2bc3Axel Lin		if (ret < 0)
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len--;
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf++;
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		off++;
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint bbc_i2c_read_buf(struct bbc_i2c_client *client,
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		     char *buf, int len, int off)
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret = 0;
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (len > 0) {
252e410471029ba99e85af5e2a1e7e747c7b4de2bc3Axel Lin		ret = bbc_i2c_readb(client, buf, off);
253e410471029ba99e85af5e2a1e7e747c7b4de2bc3Axel Lin		if (ret < 0)
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len--;
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf++;
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		off++;
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(bbc_i2c_getdev);
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(bbc_i2c_attach);
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(bbc_i2c_detach);
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(bbc_i2c_writeb);
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(bbc_i2c_readb);
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(bbc_i2c_write_buf);
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(bbc_i2c_read_buf);
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2717d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t bbc_i2c_interrupt(int irq, void *dev_id)
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct bbc_i2c_bus *bp = dev_id;
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* PIN going from set to clear is the only event which
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * makes the i2c assert an interrupt.
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bp->waiting &&
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    !(readb(bp->i2c_control_regs + 0x0) & I2C_PCF_PIN))
2803b36fb8471f8639d565b69c9a456a3ef9413df59David S. Miller		wake_up_interruptible(&bp->wq);
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IRQ_HANDLED;
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __init reset_one_i2c(struct bbc_i2c_bus *bp)
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0);
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(bp->own, bp->i2c_control_regs + 0x1);
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0);
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(bp->clock, bp->i2c_control_regs + 0x1);
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0);
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2942dc11581376829303b98eadb2de253bee065a56aGrant Likelystatic struct bbc_i2c_bus * __init attach_one_i2c(struct platform_device *op, int index)
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
29650aa485e1abb7566ce68418c7bbc6a6b454f9039Mariusz Kozlowski	struct bbc_i2c_bus *bp;
297e21e245bcd9d5244735799387d14421789b20557David S. Miller	struct device_node *dp;
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int entry;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
30050aa485e1abb7566ce68418c7bbc6a6b454f9039Mariusz Kozlowski	bp = kzalloc(sizeof(*bp), GFP_KERNEL);
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!bp)
302e21e245bcd9d5244735799387d14421789b20557David S. Miller		return NULL;
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
304e21e245bcd9d5244735799387d14421789b20557David S. Miller	bp->i2c_control_regs = of_ioremap(&op->resource[0], 0, 0x2, "bbc_i2c_regs");
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!bp->i2c_control_regs)
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto fail;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
308e21e245bcd9d5244735799387d14421789b20557David S. Miller	bp->i2c_bussel_reg = of_ioremap(&op->resource[1], 0, 0x1, "bbc_i2c_bussel");
309e21e245bcd9d5244735799387d14421789b20557David S. Miller	if (!bp->i2c_bussel_reg)
310e21e245bcd9d5244735799387d14421789b20557David S. Miller		goto fail;
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	bp->waiting = 0;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	init_waitqueue_head(&bp->wq);
3141636f8ac2b08410df4766449f7c86b912443cd99Grant Likely	if (request_irq(op->archdata.irqs[0], bbc_i2c_interrupt,
315dace145374b8e39aeb920304c358ab5e220341abThomas Gleixner			IRQF_SHARED, "bbc_i2c", bp))
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto fail;
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	bp->index = index;
319e21e245bcd9d5244735799387d14421789b20557David S. Miller	bp->op = op;
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&bp->lock);
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	entry = 0;
32461c7a080a5a061c976988fd4b844dfb468dda255Grant Likely	for (dp = op->dev.of_node->child;
325e21e245bcd9d5244735799387d14421789b20557David S. Miller	     dp && entry < 8;
326e21e245bcd9d5244735799387d14421789b20557David S. Miller	     dp = dp->sibling, entry++) {
3272dc11581376829303b98eadb2de253bee065a56aGrant Likely		struct platform_device *child_op;
328e21e245bcd9d5244735799387d14421789b20557David S. Miller
329e21e245bcd9d5244735799387d14421789b20557David S. Miller		child_op = of_find_device_by_node(dp);
330e21e245bcd9d5244735799387d14421789b20557David S. Miller		bp->devs[entry].device = child_op;
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		bp->devs[entry].client_claimed = 0;
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(I2C_PCF_PIN, bp->i2c_control_regs + 0x0);
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	bp->own = readb(bp->i2c_control_regs + 0x01);
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(I2C_PCF_PIN | I2C_PCF_ES1, bp->i2c_control_regs + 0x0);
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	bp->clock = readb(bp->i2c_control_regs + 0x01);
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO "i2c-%d: Regs at %p, %d devices, own %02x, clock %02x.\n",
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       bp->index, bp->i2c_control_regs, entry, bp->own, bp->clock);
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	reset_one_i2c(bp);
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
344e21e245bcd9d5244735799387d14421789b20557David S. Miller	return bp;
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfail:
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bp->i2c_bussel_reg)
348e21e245bcd9d5244735799387d14421789b20557David S. Miller		of_iounmap(&op->resource[1], bp->i2c_bussel_reg, 1);
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bp->i2c_control_regs)
350e21e245bcd9d5244735799387d14421789b20557David S. Miller		of_iounmap(&op->resource[0], bp->i2c_control_regs, 2);
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(bp);
352e21e245bcd9d5244735799387d14421789b20557David S. Miller	return NULL;
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
355e21e245bcd9d5244735799387d14421789b20557David S. Millerextern int bbc_envctrl_init(struct bbc_i2c_bus *bp);
356e21e245bcd9d5244735799387d14421789b20557David S. Millerextern void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp);
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3584ebb24f707187196937607c60810d42f7112d7aaGrant Likelystatic int __devinit bbc_i2c_probe(struct platform_device *op)
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
360e21e245bcd9d5244735799387d14421789b20557David S. Miller	struct bbc_i2c_bus *bp;
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err, index = 0;
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
363e21e245bcd9d5244735799387d14421789b20557David S. Miller	bp = attach_one_i2c(op, index);
364e21e245bcd9d5244735799387d14421789b20557David S. Miller	if (!bp)
365e21e245bcd9d5244735799387d14421789b20557David S. Miller		return -EINVAL;
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
367e21e245bcd9d5244735799387d14421789b20557David S. Miller	err = bbc_envctrl_init(bp);
368e21e245bcd9d5244735799387d14421789b20557David S. Miller	if (err) {
3691636f8ac2b08410df4766449f7c86b912443cd99Grant Likely		free_irq(op->archdata.irqs[0], bp);
370e21e245bcd9d5244735799387d14421789b20557David S. Miller		if (bp->i2c_bussel_reg)
371e21e245bcd9d5244735799387d14421789b20557David S. Miller			of_iounmap(&op->resource[0], bp->i2c_bussel_reg, 1);
372e21e245bcd9d5244735799387d14421789b20557David S. Miller		if (bp->i2c_control_regs)
373e21e245bcd9d5244735799387d14421789b20557David S. Miller			of_iounmap(&op->resource[1], bp->i2c_control_regs, 2);
374e21e245bcd9d5244735799387d14421789b20557David S. Miller		kfree(bp);
375e21e245bcd9d5244735799387d14421789b20557David S. Miller	} else {
376e21e245bcd9d5244735799387d14421789b20557David S. Miller		dev_set_drvdata(&op->dev, bp);
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3822dc11581376829303b98eadb2de253bee065a56aGrant Likelystatic int __devexit bbc_i2c_remove(struct platform_device *op)
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
384e21e245bcd9d5244735799387d14421789b20557David S. Miller	struct bbc_i2c_bus *bp = dev_get_drvdata(&op->dev);
385e21e245bcd9d5244735799387d14421789b20557David S. Miller
386e21e245bcd9d5244735799387d14421789b20557David S. Miller	bbc_envctrl_cleanup(bp);
387e21e245bcd9d5244735799387d14421789b20557David S. Miller
3881636f8ac2b08410df4766449f7c86b912443cd99Grant Likely	free_irq(op->archdata.irqs[0], bp);
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
390e21e245bcd9d5244735799387d14421789b20557David S. Miller	if (bp->i2c_bussel_reg)
391e21e245bcd9d5244735799387d14421789b20557David S. Miller		of_iounmap(&op->resource[0], bp->i2c_bussel_reg, 1);
392e21e245bcd9d5244735799387d14421789b20557David S. Miller	if (bp->i2c_control_regs)
393e21e245bcd9d5244735799387d14421789b20557David S. Miller		of_iounmap(&op->resource[1], bp->i2c_control_regs, 2);
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
395e21e245bcd9d5244735799387d14421789b20557David S. Miller	kfree(bp);
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
397e21e245bcd9d5244735799387d14421789b20557David S. Miller	return 0;
398e21e245bcd9d5244735799387d14421789b20557David S. Miller}
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
400fd098316ef533e8441576f020ead4beab93154ceDavid S. Millerstatic const struct of_device_id bbc_i2c_match[] = {
401e21e245bcd9d5244735799387d14421789b20557David S. Miller	{
402e21e245bcd9d5244735799387d14421789b20557David S. Miller		.name = "i2c",
403e21e245bcd9d5244735799387d14421789b20557David S. Miller		.compatible = "SUNW,bbc-i2c",
404e21e245bcd9d5244735799387d14421789b20557David S. Miller	},
405e21e245bcd9d5244735799387d14421789b20557David S. Miller	{},
406e21e245bcd9d5244735799387d14421789b20557David S. Miller};
407e21e245bcd9d5244735799387d14421789b20557David S. MillerMODULE_DEVICE_TABLE(of, bbc_i2c_match);
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4094ebb24f707187196937607c60810d42f7112d7aaGrant Likelystatic struct platform_driver bbc_i2c_driver = {
4104018294b53d1dae026880e45f174c1cc63b5d435Grant Likely	.driver = {
4114018294b53d1dae026880e45f174c1cc63b5d435Grant Likely		.name = "bbc_i2c",
4124018294b53d1dae026880e45f174c1cc63b5d435Grant Likely		.owner = THIS_MODULE,
4134018294b53d1dae026880e45f174c1cc63b5d435Grant Likely		.of_match_table = bbc_i2c_match,
4144018294b53d1dae026880e45f174c1cc63b5d435Grant Likely	},
415e21e245bcd9d5244735799387d14421789b20557David S. Miller	.probe		= bbc_i2c_probe,
41633b07db9f38fe73b3895f8d4db8fdee03e3afec3Linus Torvalds	.remove		= __devexit_p(bbc_i2c_remove),
417e21e245bcd9d5244735799387d14421789b20557David S. Miller};
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
419dbf2b92d54e73e4a2524b90d29bd498ecc4aa593Axel Linmodule_platform_driver(bbc_i2c_driver);
420e21e245bcd9d5244735799387d14421789b20557David S. Miller
421b5e7ae5dd034c2c0ed75c31fca04a805097817bcDavid S. MillerMODULE_LICENSE("GPL");
422