pcf8584.c revision 1.10 1 /* $NetBSD: pcf8584.c,v 1.10 2013/09/15 09:17:28 martin 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 (at) 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
63 void pcfiic_init(struct pcfiic_softc *);
64 int pcfiic_i2c_acquire_bus(void *, int);
65 void pcfiic_i2c_release_bus(void *, int);
66 int pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
67 size_t, void *, size_t, int);
68
69 int pcfiic_xmit(struct pcfiic_softc *, u_int8_t, const u_int8_t *,
70 size_t);
71 int pcfiic_recv(struct pcfiic_softc *, u_int8_t, u_int8_t *,
72 size_t);
73
74 u_int8_t pcfiic_read(struct pcfiic_softc *, bus_size_t);
75 void pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t);
76 void pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t);
77 int pcfiic_wait_nBB(struct pcfiic_softc *);
78 int pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *);
79
80 void
81 pcfiic_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
97 void
98 pcfiic_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
131 int
132 pcfiic_intr(void *arg)
133 {
134 return (0);
135 }
136
137 int
138 pcfiic_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
149 void
150 pcfiic_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
160 int
161 pcfiic_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
191 int
192 pcfiic_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
222 int
223 pcfiic_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
258 u_int8_t
259 pcfiic_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
266 void
267 pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v)
268 {
269 bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v);
270 (void)bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCF_S1);
271 }
272
273 void
274 pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus)
275 {
276 bus_space_write_1(sc->sc_iot, sc->sc_ioh2, 0, bus);
277 bus_space_barrier(sc->sc_iot, sc->sc_ioh2, 0, 1,
278 BUS_SPACE_BARRIER_WRITE);
279 }
280
281 int
282 pcfiic_wait_nBB(struct pcfiic_softc *sc)
283 {
284 int i;
285
286 for (i = 0; i < 1000; i++) {
287 if (pcfiic_read(sc, PCF_S1) & PCF_STAT_nBB)
288 return (0);
289 delay(1000);
290 }
291 return (1);
292 }
293
294 int
295 pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r)
296 {
297 int i;
298
299 for (i = 0; i < 1000; i++) {
300 *r = pcfiic_read(sc, PCF_S1);
301 if ((*r & PCF_STAT_PIN) == 0)
302 return (0);
303 delay(1000);
304 }
305 return (1);
306 }
307