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