1/*	$NetBSD: pcf8584.c,v 1.8 2010/03/31 21:01:03 macallan Exp $	*/
2/*	$OpenBSD: pcf8584.c,v 1.9 2007/10/20 18:46:21 kettenis Exp $ */
3
4/*
5 * Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/param.h>
21#include <sys/systm.h>
22#include <sys/device.h>
23#include <sys/malloc.h>
24#include <sys/kernel.h>
25#include <sys/rwlock.h>
26#include <sys/proc.h>
27#include <sys/bus.h>
28
29#include <dev/i2c/i2cvar.h>
30
31#include <dev/ic/pcf8584var.h>
32
33#define PCF_S0			0x00
34#define PCF_S1			0x01
35#define PCF_S2			0x02
36#define PCF_S3			0x03
37
38#define PCF_CTRL_ACK		(1<<0)
39#define PCF_CTRL_STO		(1<<1)
40#define PCF_CTRL_STA		(1<<2)
41#define PCF_CTRL_ENI		(1<<3)
42#define PCF_CTRL_ES2		(1<<4)
43#define PCF_CTRL_ES1		(1<<5)
44#define PCF_CTRL_ESO		(1<<6)
45#define PCF_CTRL_PIN		(1<<7)
46
47#define PCF_CTRL_START		(PCF_CTRL_PIN | PCF_CTRL_ESO | \
48    PCF_CTRL_STA | PCF_CTRL_ACK)
49#define PCF_CTRL_STOP		(PCF_CTRL_PIN | PCF_CTRL_ESO | \
50    PCF_CTRL_STO | PCF_CTRL_ACK)
51#define PCF_CTRL_REPSTART	(PCF_CTRL_ESO | PCF_CTRL_STA | PCF_CTRL_ACK)
52#define PCF_CTRL_IDLE		(PCF_CTRL_PIN | PCF_CTRL_ESO | PCF_CTRL_ACK)
53
54#define PCF_STAT_nBB		(1<<0)
55#define PCF_STAT_LAB		(1<<1)
56#define PCF_STAT_AAS		(1<<2)
57#define PCF_STAT_AD0		(1<<3)
58#define PCF_STAT_LRB		(1<<3)
59#define PCF_STAT_BER		(1<<4)
60#define PCF_STAT_STS		(1<<5)
61#define PCF_STAT_PIN		(1<<7)
62
63void		pcfiic_init(struct pcfiic_softc *);
64int		pcfiic_i2c_acquire_bus(void *, int);
65void		pcfiic_i2c_release_bus(void *, int);
66int		pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
67		    size_t, void *, size_t, int);
68
69int		pcfiic_xmit(struct pcfiic_softc *, u_int8_t, const u_int8_t *,
70		    size_t);
71int		pcfiic_recv(struct pcfiic_softc *, u_int8_t, u_int8_t *,
72		    size_t);
73
74u_int8_t	pcfiic_read(struct pcfiic_softc *, bus_size_t);
75void		pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t);
76void		pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t);
77int		pcfiic_wait_nBB(struct pcfiic_softc *);
78int		pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *);
79
80void
81pcfiic_init(struct pcfiic_softc *sc)
82{
83	/* init S1 */
84	pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN);
85	/* own address */
86	pcfiic_write(sc, PCF_S0, sc->sc_addr);
87
88	/* select clock reg */
89	pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN|PCF_CTRL_ES1);
90	pcfiic_write(sc, PCF_S0, sc->sc_clock);
91
92	pcfiic_write(sc, PCF_S1, PCF_CTRL_IDLE);
93
94	delay(200000);	/* Multi-Master mode, wait for longest i2c message */
95}
96
97void
98pcfiic_attach(struct pcfiic_softc *sc, i2c_addr_t addr, u_int8_t clock,
99    int swapregs)
100{
101	struct i2cbus_attach_args		iba;
102
103	if (swapregs) {
104		sc->sc_regmap[PCF_S1] = PCF_S0;
105		sc->sc_regmap[PCF_S0] = PCF_S1;
106	} else {
107		sc->sc_regmap[PCF_S0] = PCF_S0;
108		sc->sc_regmap[PCF_S1] = PCF_S1;
109	}
110	sc->sc_clock = clock;
111	sc->sc_addr = addr;
112
113	pcfiic_init(sc);
114
115	printf("\n");
116
117	if (sc->sc_master)
118		pcfiic_choose_bus(sc, 0);
119
120	rw_init(&sc->sc_lock);
121	sc->sc_i2c.ic_cookie = sc;
122	sc->sc_i2c.ic_acquire_bus = pcfiic_i2c_acquire_bus;
123	sc->sc_i2c.ic_release_bus = pcfiic_i2c_release_bus;
124	sc->sc_i2c.ic_exec = pcfiic_i2c_exec;
125
126	bzero(&iba, sizeof(iba));
127	iba.iba_tag = &sc->sc_i2c;
128	config_found(sc->sc_dev, &iba, iicbus_print);
129}
130
131int
132pcfiic_intr(void *arg)
133{
134	return (0);
135}
136
137int
138pcfiic_i2c_acquire_bus(void *arg, int flags)
139{
140	struct pcfiic_softc	*sc = arg;
141
142	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
143		return (0);
144
145	rw_enter(&sc->sc_lock, RW_WRITER);
146	return 0;
147}
148
149void
150pcfiic_i2c_release_bus(void *arg, int flags)
151{
152	struct pcfiic_softc	*sc = arg;
153
154	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
155		return;
156
157	rw_exit(&sc->sc_lock);
158}
159
160int
161pcfiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
162    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
163{
164	struct pcfiic_softc	*sc = arg;
165	int			ret = 0;
166
167#if 0
168        printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n",
169            device_xname(sc->sc_dev), op, addr, (int)cmdlen, (int)len, flags);
170#endif
171
172	if (cold || sc->sc_poll)
173		flags |= I2C_F_POLL;
174
175	if (sc->sc_master)
176		pcfiic_choose_bus(sc, addr >> 7);
177
178	if (cmdlen > 0)
179		if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen) != 0)
180			return (1);
181
182	if (len > 0) {
183		if (I2C_OP_WRITE_P(op))
184			ret = pcfiic_xmit(sc, addr & 0x7f, buf, len);
185		else
186			ret = pcfiic_recv(sc, addr & 0x7f, buf, len);
187	}
188	return (ret);
189}
190
191int
192pcfiic_xmit(struct pcfiic_softc *sc, u_int8_t addr, const u_int8_t *buf,
193    size_t len)
194{
195	int			i, err = 0;
196	volatile u_int8_t	r;
197
198	if (pcfiic_wait_nBB(sc) != 0)
199		return (1);
200
201	pcfiic_write(sc, PCF_S0, addr << 1);
202	pcfiic_write(sc, PCF_S1, PCF_CTRL_START);
203
204	for (i = 0; i <= len; i++) {
205		if (pcfiic_wait_pin(sc, &r) != 0) {
206			pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
207			return (1);
208		}
209
210		if (r & PCF_STAT_LRB) {
211			err = 1;
212			break;
213		}
214
215		if (i < len)
216			pcfiic_write(sc, PCF_S0, buf[i]);
217	}
218	pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
219	return (err);
220}
221
222int
223pcfiic_recv(struct pcfiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len)
224{
225	int			i = 0, err = 0;
226	volatile u_int8_t	r;
227
228	if (pcfiic_wait_nBB(sc) != 0)
229		return (1);
230
231	pcfiic_write(sc, PCF_S0, (addr << 1) | 0x01);
232	pcfiic_write(sc, PCF_S1, PCF_CTRL_START);
233
234	for (i = 0; i <= len; i++) {
235		if (pcfiic_wait_pin(sc, &r) != 0) {
236			pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
237			return (1);
238		}
239
240		if ((i != len) && (r & PCF_STAT_LRB)) {
241			pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
242			return (1);
243		}
244
245		if (i == len - 1) {
246			pcfiic_write(sc, PCF_S1, PCF_CTRL_ESO);
247		} else if (i == len) {
248			pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
249		}
250
251		r = pcfiic_read(sc, PCF_S0);
252		if (i > 0)
253			buf[i - 1] = r;
254	}
255	return (err);
256}
257
258u_int8_t
259pcfiic_read(struct pcfiic_softc *sc, bus_size_t r)
260{
261	bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1,
262	    BUS_SPACE_BARRIER_READ);
263	return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r]));
264}
265
266void
267pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v)
268{
269	volatile uint8_t junk;
270	bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v);
271	junk = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCF_S1);
272}
273
274void
275pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus)
276{
277	bus_space_write_1(sc->sc_iot, sc->sc_ioh2, 0, bus);
278	bus_space_barrier(sc->sc_iot, sc->sc_ioh2, 0, 1,
279	    BUS_SPACE_BARRIER_WRITE);
280}
281
282int
283pcfiic_wait_nBB(struct pcfiic_softc *sc)
284{
285	int		i;
286
287	for (i = 0; i < 1000; i++) {
288		if (pcfiic_read(sc, PCF_S1) & PCF_STAT_nBB)
289			return (0);
290		delay(1000);
291	}
292	return (1);
293}
294
295int
296pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r)
297{
298	int		i;
299
300	for (i = 0; i < 1000; i++) {
301		*r = pcfiic_read(sc, PCF_S1);
302		if ((*r & PCF_STAT_PIN) == 0)
303			return (0);
304		delay(1000);
305	}
306	return (1);
307}
308