1*a6cacc53Skettenis /* $OpenBSD: pcf8584.c,v 1.11 2010/08/01 18:48:41 kettenis Exp $ */
2c74283e0Sdlg
3c74283e0Sdlg /*
4c74283e0Sdlg * Copyright (c) 2006 David Gwynne <dlg@openbsd.org>
5c74283e0Sdlg *
6c74283e0Sdlg * Permission to use, copy, modify, and distribute this software for any
7c74283e0Sdlg * purpose with or without fee is hereby granted, provided that the above
8c74283e0Sdlg * copyright notice and this permission notice appear in all copies.
9c74283e0Sdlg *
10c74283e0Sdlg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11c74283e0Sdlg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c74283e0Sdlg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c74283e0Sdlg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c74283e0Sdlg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15c74283e0Sdlg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16c74283e0Sdlg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c74283e0Sdlg */
18c74283e0Sdlg
19c74283e0Sdlg #include <sys/param.h>
20c74283e0Sdlg #include <sys/systm.h>
21c74283e0Sdlg #include <sys/device.h>
22c74283e0Sdlg #include <sys/malloc.h>
23c74283e0Sdlg #include <sys/kernel.h>
24ee945c99Sjsg #include <sys/rwlock.h>
25c74283e0Sdlg #include <sys/proc.h>
26c74283e0Sdlg
27c74283e0Sdlg #include <machine/bus.h>
28c74283e0Sdlg
29c74283e0Sdlg #include <dev/i2c/i2cvar.h>
30c74283e0Sdlg
31c74283e0Sdlg #include <dev/ic/pcf8584var.h>
32c74283e0Sdlg
33c74283e0Sdlg #define PCF_S0 0x00
34c74283e0Sdlg #define PCF_S1 0x01
35c74283e0Sdlg #define PCF_S2 0x02
36c74283e0Sdlg #define PCF_S3 0x03
37c74283e0Sdlg
38c74283e0Sdlg #define PCF_CTRL_ACK (1<<0)
39c74283e0Sdlg #define PCF_CTRL_STO (1<<1)
40c74283e0Sdlg #define PCF_CTRL_STA (1<<2)
41c74283e0Sdlg #define PCF_CTRL_ENI (1<<3)
42c74283e0Sdlg #define PCF_CTRL_ES2 (1<<4)
43c74283e0Sdlg #define PCF_CTRL_ES1 (1<<5)
44c74283e0Sdlg #define PCF_CTRL_ESO (1<<6)
45c74283e0Sdlg #define PCF_CTRL_PIN (1<<7)
46c74283e0Sdlg
47c74283e0Sdlg #define PCF_CTRL_START (PCF_CTRL_PIN | PCF_CTRL_ESO | \
48c74283e0Sdlg PCF_CTRL_STA | PCF_CTRL_ACK)
49c74283e0Sdlg #define PCF_CTRL_STOP (PCF_CTRL_PIN | PCF_CTRL_ESO | \
50c74283e0Sdlg PCF_CTRL_STO | PCF_CTRL_ACK)
51c74283e0Sdlg #define PCF_CTRL_REPSTART (PCF_CTRL_ESO | PCF_CTRL_STA | PCF_CTRL_ACK)
52c74283e0Sdlg #define PCF_CTRL_IDLE (PCF_CTRL_PIN | PCF_CTRL_ESO | PCF_CTRL_ACK)
53c74283e0Sdlg
54c74283e0Sdlg #define PCF_STAT_nBB (1<<0)
55c74283e0Sdlg #define PCF_STAT_LAB (1<<1)
56c74283e0Sdlg #define PCF_STAT_AAS (1<<2)
57c74283e0Sdlg #define PCF_STAT_AD0 (1<<3)
58c74283e0Sdlg #define PCF_STAT_LRB (1<<3)
59c74283e0Sdlg #define PCF_STAT_BER (1<<4)
60c74283e0Sdlg #define PCF_STAT_STS (1<<5)
61c74283e0Sdlg #define PCF_STAT_PIN (1<<7)
62c74283e0Sdlg
63c74283e0Sdlg struct cfdriver pcfiic_cd = {
64c74283e0Sdlg NULL, "pcfiic", DV_DULL
65c74283e0Sdlg };
66c74283e0Sdlg
673e5c8021Sderaadt void pcfiic_init(struct pcfiic_softc *);
68c74283e0Sdlg int pcfiic_i2c_acquire_bus(void *, int);
69c74283e0Sdlg void pcfiic_i2c_release_bus(void *, int);
70c74283e0Sdlg int pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
71c74283e0Sdlg size_t, void *, size_t, int);
72c74283e0Sdlg
73c74283e0Sdlg int pcfiic_xmit(struct pcfiic_softc *, u_int8_t, const u_int8_t *,
74c74283e0Sdlg size_t);
75c74283e0Sdlg int pcfiic_recv(struct pcfiic_softc *, u_int8_t, u_int8_t *,
76c74283e0Sdlg size_t);
77c74283e0Sdlg
78b3b2c8a0Skettenis u_int8_t pcfiic_read(struct pcfiic_softc *, bus_size_t);
79b3b2c8a0Skettenis void pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t);
809c6a5656Sderaadt void pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t);
81c74283e0Sdlg int pcfiic_wait_nBB(struct pcfiic_softc *);
82c74283e0Sdlg int pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *);
83c74283e0Sdlg
84c74283e0Sdlg void
pcfiic_init(struct pcfiic_softc * sc)853e5c8021Sderaadt pcfiic_init(struct pcfiic_softc *sc)
863e5c8021Sderaadt {
873e5c8021Sderaadt /* init S1 */
883e5c8021Sderaadt pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN);
893e5c8021Sderaadt /* own address */
903e5c8021Sderaadt pcfiic_write(sc, PCF_S0, sc->sc_addr);
913e5c8021Sderaadt
923e5c8021Sderaadt /* select clock reg */
933e5c8021Sderaadt pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN|PCF_CTRL_ES1);
943e5c8021Sderaadt pcfiic_write(sc, PCF_S0, sc->sc_clock);
953e5c8021Sderaadt
963e5c8021Sderaadt pcfiic_write(sc, PCF_S1, PCF_CTRL_IDLE);
973e5c8021Sderaadt
983e5c8021Sderaadt delay(200000); /* Multi-Master mode, wait for longest i2c message */
993e5c8021Sderaadt }
1003e5c8021Sderaadt
1013e5c8021Sderaadt void
pcfiic_attach(struct pcfiic_softc * sc,i2c_addr_t addr,u_int8_t clock,int swapregs,void (* scan_func)(struct device *,struct i2cbus_attach_args *,void *),void * scan_arg)1023e5c8021Sderaadt pcfiic_attach(struct pcfiic_softc *sc, i2c_addr_t addr, u_int8_t clock,
1033e5c8021Sderaadt int swapregs,
104c74283e0Sdlg void (*scan_func)(struct device *, struct i2cbus_attach_args *, void *),
105c74283e0Sdlg void *scan_arg)
106c74283e0Sdlg {
107c74283e0Sdlg struct i2cbus_attach_args iba;
108c74283e0Sdlg
1093e5c8021Sderaadt if (swapregs) {
1103e5c8021Sderaadt sc->sc_regmap[PCF_S1] = PCF_S0;
1113e5c8021Sderaadt sc->sc_regmap[PCF_S0] = PCF_S1;
1123e5c8021Sderaadt } else {
1133e5c8021Sderaadt sc->sc_regmap[PCF_S0] = PCF_S0;
1143e5c8021Sderaadt sc->sc_regmap[PCF_S1] = PCF_S1;
1153e5c8021Sderaadt }
1163e5c8021Sderaadt sc->sc_clock = clock;
1173e5c8021Sderaadt sc->sc_addr = addr;
118c74283e0Sdlg
1193e5c8021Sderaadt pcfiic_init(sc);
120c74283e0Sdlg
121e18683d3Sdlg printf("\n");
122c74283e0Sdlg
1239c6a5656Sderaadt if (sc->sc_master)
1249c6a5656Sderaadt pcfiic_choose_bus(sc, 0);
1259c6a5656Sderaadt
126ee945c99Sjsg rw_init(&sc->sc_lock, "iiclk");
127c74283e0Sdlg sc->sc_i2c.ic_cookie = sc;
128c74283e0Sdlg sc->sc_i2c.ic_acquire_bus = pcfiic_i2c_acquire_bus;
129c74283e0Sdlg sc->sc_i2c.ic_release_bus = pcfiic_i2c_release_bus;
130c74283e0Sdlg sc->sc_i2c.ic_exec = pcfiic_i2c_exec;
131c74283e0Sdlg
132c74283e0Sdlg bzero(&iba, sizeof(iba));
133c74283e0Sdlg iba.iba_name = "iic";
134c74283e0Sdlg iba.iba_tag = &sc->sc_i2c;
135c74283e0Sdlg iba.iba_bus_scan = scan_func;
136c74283e0Sdlg iba.iba_bus_scan_arg = scan_arg;
137c74283e0Sdlg config_found(&sc->sc_dev, &iba, iicbus_print);
138c74283e0Sdlg }
139c74283e0Sdlg
140c74283e0Sdlg int
pcfiic_intr(void * arg)141c74283e0Sdlg pcfiic_intr(void *arg)
142c74283e0Sdlg {
143c74283e0Sdlg return (0);
144c74283e0Sdlg }
145c74283e0Sdlg
146c74283e0Sdlg int
pcfiic_i2c_acquire_bus(void * arg,int flags)147c74283e0Sdlg pcfiic_i2c_acquire_bus(void *arg, int flags)
148c74283e0Sdlg {
149c74283e0Sdlg struct pcfiic_softc *sc = arg;
150c74283e0Sdlg
151c74283e0Sdlg if (cold || sc->sc_poll || (flags & I2C_F_POLL))
152c74283e0Sdlg return (0);
153c74283e0Sdlg
154ee945c99Sjsg return (rw_enter(&sc->sc_lock, RW_WRITE | RW_INTR));
155c74283e0Sdlg }
156c74283e0Sdlg
157c74283e0Sdlg void
pcfiic_i2c_release_bus(void * arg,int flags)158c74283e0Sdlg pcfiic_i2c_release_bus(void *arg, int flags)
159c74283e0Sdlg {
160c74283e0Sdlg struct pcfiic_softc *sc = arg;
161c74283e0Sdlg
162c74283e0Sdlg if (cold || sc->sc_poll || (flags & I2C_F_POLL))
163c74283e0Sdlg return;
164c74283e0Sdlg
165ee945c99Sjsg rw_exit(&sc->sc_lock);
166c74283e0Sdlg }
167c74283e0Sdlg
168c74283e0Sdlg int
pcfiic_i2c_exec(void * arg,i2c_op_t op,i2c_addr_t addr,const void * cmdbuf,size_t cmdlen,void * buf,size_t len,int flags)169c74283e0Sdlg pcfiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
170c74283e0Sdlg const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
171c74283e0Sdlg {
172c74283e0Sdlg struct pcfiic_softc *sc = arg;
173c74283e0Sdlg int ret = 0;
174c74283e0Sdlg
175c74283e0Sdlg #if 0
176c74283e0Sdlg printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n",
177c74283e0Sdlg sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags);
178c74283e0Sdlg #endif
179c74283e0Sdlg
180c74283e0Sdlg if (cold || sc->sc_poll)
181c74283e0Sdlg flags |= I2C_F_POLL;
182c74283e0Sdlg
1839c6a5656Sderaadt if (sc->sc_master)
1849c6a5656Sderaadt pcfiic_choose_bus(sc, addr >> 7);
1859c6a5656Sderaadt
186c265439bSderaadt if (cmdlen > 0)
1879c6a5656Sderaadt if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen) != 0)
188c74283e0Sdlg return (1);
189c74283e0Sdlg
190c74283e0Sdlg if (len > 0) {
191c74283e0Sdlg if (I2C_OP_WRITE_P(op))
1929c6a5656Sderaadt ret = pcfiic_xmit(sc, addr & 0x7f, buf, len);
193c74283e0Sdlg else
1949c6a5656Sderaadt ret = pcfiic_recv(sc, addr & 0x7f, buf, len);
195c74283e0Sdlg }
196c74283e0Sdlg return (ret);
197c74283e0Sdlg }
198c74283e0Sdlg
199c74283e0Sdlg int
pcfiic_xmit(struct pcfiic_softc * sc,u_int8_t addr,const u_int8_t * buf,size_t len)200c74283e0Sdlg pcfiic_xmit(struct pcfiic_softc *sc, u_int8_t addr, const u_int8_t *buf,
201c74283e0Sdlg size_t len)
202c74283e0Sdlg {
2033e5c8021Sderaadt int i, err = 0;
204c74283e0Sdlg volatile u_int8_t r;
205c74283e0Sdlg
2063e5c8021Sderaadt if (pcfiic_wait_nBB(sc) != 0)
207e5eda077Sderaadt return (1);
208c74283e0Sdlg
209c74283e0Sdlg pcfiic_write(sc, PCF_S0, addr << 1);
2109c6a5656Sderaadt pcfiic_write(sc, PCF_S1, PCF_CTRL_START);
211c74283e0Sdlg
212c74283e0Sdlg for (i = 0; i <= len; i++) {
2133e5c8021Sderaadt if (pcfiic_wait_pin(sc, &r) != 0) {
2143e5c8021Sderaadt pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
2153e5c8021Sderaadt return (1);
216e5eda077Sderaadt }
217c74283e0Sdlg
218c74283e0Sdlg if (r & PCF_STAT_LRB) {
219c74283e0Sdlg err = 1;
220c74283e0Sdlg break;
221c74283e0Sdlg }
222c74283e0Sdlg
223c74283e0Sdlg if (i < len)
224c74283e0Sdlg pcfiic_write(sc, PCF_S0, buf[i]);
225c74283e0Sdlg }
2269c6a5656Sderaadt pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
227c74283e0Sdlg return (err);
228c74283e0Sdlg }
229c74283e0Sdlg
230c74283e0Sdlg int
pcfiic_recv(struct pcfiic_softc * sc,u_int8_t addr,u_int8_t * buf,size_t len)231c74283e0Sdlg pcfiic_recv(struct pcfiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len)
232c74283e0Sdlg {
233c265439bSderaadt int i = 0, err = 0;
234c74283e0Sdlg volatile u_int8_t r;
235c74283e0Sdlg
236c74283e0Sdlg if (pcfiic_wait_nBB(sc) != 0)
237c74283e0Sdlg return (1);
238c74283e0Sdlg
2393e5c8021Sderaadt pcfiic_write(sc, PCF_S0, (addr << 1) | 0x01);
2409c6a5656Sderaadt pcfiic_write(sc, PCF_S1, PCF_CTRL_START);
241c74283e0Sdlg
242c74283e0Sdlg for (i = 0; i <= len; i++) {
243c74283e0Sdlg if (pcfiic_wait_pin(sc, &r) != 0) {
2449c6a5656Sderaadt pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
245c74283e0Sdlg return (1);
246c74283e0Sdlg }
247c74283e0Sdlg
248c74283e0Sdlg if ((i != len) && (r & PCF_STAT_LRB)) {
2499c6a5656Sderaadt pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
250c74283e0Sdlg return (1);
251c74283e0Sdlg }
252c74283e0Sdlg
253c74283e0Sdlg if (i == len - 1) {
254c74283e0Sdlg pcfiic_write(sc, PCF_S1, PCF_CTRL_ESO);
255c74283e0Sdlg } else if (i == len) {
2569c6a5656Sderaadt pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
257c74283e0Sdlg }
258c74283e0Sdlg
259c74283e0Sdlg r = pcfiic_read(sc, PCF_S0);
260c74283e0Sdlg if (i > 0)
261c74283e0Sdlg buf[i - 1] = r;
262c74283e0Sdlg }
263c74283e0Sdlg return (err);
264c74283e0Sdlg }
265c74283e0Sdlg
266b3b2c8a0Skettenis u_int8_t
pcfiic_read(struct pcfiic_softc * sc,bus_size_t r)267c74283e0Sdlg pcfiic_read(struct pcfiic_softc *sc, bus_size_t r)
268c74283e0Sdlg {
2693e5c8021Sderaadt bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1,
270c74283e0Sdlg BUS_SPACE_BARRIER_READ);
2713e5c8021Sderaadt return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r]));
272c74283e0Sdlg }
273c74283e0Sdlg
274b3b2c8a0Skettenis void
pcfiic_write(struct pcfiic_softc * sc,bus_size_t r,u_int8_t v)275c74283e0Sdlg pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v)
276c74283e0Sdlg {
2773e5c8021Sderaadt bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v);
278*a6cacc53Skettenis bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, 4,
279*a6cacc53Skettenis BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ);
280*a6cacc53Skettenis bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[PCF_S1]);
281c74283e0Sdlg }
282c74283e0Sdlg
2839c6a5656Sderaadt void
pcfiic_choose_bus(struct pcfiic_softc * sc,u_int8_t bus)2849c6a5656Sderaadt pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus)
2859c6a5656Sderaadt {
2869c6a5656Sderaadt bus_space_write_1(sc->sc_iot, sc->sc_ioh2, 0, bus);
2879c6a5656Sderaadt bus_space_barrier(sc->sc_iot, sc->sc_ioh2, 0, 1,
2889c6a5656Sderaadt BUS_SPACE_BARRIER_WRITE);
2899c6a5656Sderaadt }
2909c6a5656Sderaadt
291c74283e0Sdlg int
pcfiic_wait_nBB(struct pcfiic_softc * sc)292c74283e0Sdlg pcfiic_wait_nBB(struct pcfiic_softc *sc)
293c74283e0Sdlg {
294c74283e0Sdlg int i;
295c74283e0Sdlg
296c74283e0Sdlg for (i = 0; i < 1000; i++) {
297c74283e0Sdlg if (pcfiic_read(sc, PCF_S1) & PCF_STAT_nBB)
298c74283e0Sdlg return (0);
299c74283e0Sdlg delay(1000);
300c74283e0Sdlg }
301c74283e0Sdlg return (1);
302c74283e0Sdlg }
303c74283e0Sdlg
304c74283e0Sdlg int
pcfiic_wait_pin(struct pcfiic_softc * sc,volatile u_int8_t * r)305c74283e0Sdlg pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r)
306c74283e0Sdlg {
307c74283e0Sdlg int i;
308c74283e0Sdlg
309c74283e0Sdlg for (i = 0; i < 1000; i++) {
310c74283e0Sdlg *r = pcfiic_read(sc, PCF_S1);
311c74283e0Sdlg if ((*r & PCF_STAT_PIN) == 0)
312c74283e0Sdlg return (0);
313c74283e0Sdlg delay(1000);
314c74283e0Sdlg }
315c74283e0Sdlg return (1);
316c74283e0Sdlg }
317