pcf8584.c revision 1.6 1 /* $NetBSD: pcf8584.c,v 1.6 2010/02/28 11:47: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
28 #include <machine/bus.h>
29
30 #include <dev/i2c/i2cvar.h>
31
32 #include <dev/ic/pcf8584var.h>
33
34 #define PCF_S0 0x00
35 #define PCF_S1 0x01
36 #define PCF_S2 0x02
37 #define PCF_S3 0x03
38
39 #define PCF_CTRL_ACK (1<<0)
40 #define PCF_CTRL_STO (1<<1)
41 #define PCF_CTRL_STA (1<<2)
42 #define PCF_CTRL_ENI (1<<3)
43 #define PCF_CTRL_ES2 (1<<4)
44 #define PCF_CTRL_ES1 (1<<5)
45 #define PCF_CTRL_ESO (1<<6)
46 #define PCF_CTRL_PIN (1<<7)
47
48 #define PCF_CTRL_START (PCF_CTRL_PIN | PCF_CTRL_ESO | \
49 PCF_CTRL_STA | PCF_CTRL_ACK)
50 #define PCF_CTRL_STOP (PCF_CTRL_PIN | PCF_CTRL_ESO | \
51 PCF_CTRL_STO | PCF_CTRL_ACK)
52 #define PCF_CTRL_REPSTART (PCF_CTRL_ESO | PCF_CTRL_STA | PCF_CTRL_ACK)
53 #define PCF_CTRL_IDLE (PCF_CTRL_PIN | PCF_CTRL_ESO | PCF_CTRL_ACK)
54
55 #define PCF_STAT_nBB (1<<0)
56 #define PCF_STAT_LAB (1<<1)
57 #define PCF_STAT_AAS (1<<2)
58 #define PCF_STAT_AD0 (1<<3)
59 #define PCF_STAT_LRB (1<<3)
60 #define PCF_STAT_BER (1<<4)
61 #define PCF_STAT_STS (1<<5)
62 #define PCF_STAT_PIN (1<<7)
63
64 void pcfiic_init(struct pcfiic_softc *);
65 int pcfiic_i2c_acquire_bus(void *, int);
66 void pcfiic_i2c_release_bus(void *, int);
67 int pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
68 size_t, void *, size_t, int);
69
70 int pcfiic_xmit(struct pcfiic_softc *, u_int8_t, const u_int8_t *,
71 size_t);
72 int pcfiic_recv(struct pcfiic_softc *, u_int8_t, u_int8_t *,
73 size_t);
74
75 u_int8_t pcfiic_read(struct pcfiic_softc *, bus_size_t);
76 void pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t);
77 void pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t);
78 int pcfiic_wait_nBB(struct pcfiic_softc *);
79 int pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *);
80
81 void
82 pcfiic_init(struct pcfiic_softc *sc)
83 {
84 /* init S1 */
85 pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN);
86 /* own address */
87 pcfiic_write(sc, PCF_S0, sc->sc_addr);
88
89 /* select clock reg */
90 pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN|PCF_CTRL_ES1);
91 pcfiic_write(sc, PCF_S0, sc->sc_clock);
92
93 pcfiic_write(sc, PCF_S1, PCF_CTRL_IDLE);
94
95 delay(200000); /* Multi-Master mode, wait for longest i2c message */
96 }
97
98 void
99 pcfiic_attach(struct pcfiic_softc *sc, i2c_addr_t addr, u_int8_t clock,
100 int swapregs)
101 {
102 struct i2cbus_attach_args iba;
103
104 if (swapregs) {
105 sc->sc_regmap[PCF_S1] = PCF_S0;
106 sc->sc_regmap[PCF_S0] = PCF_S1;
107 } else {
108 sc->sc_regmap[PCF_S0] = PCF_S0;
109 sc->sc_regmap[PCF_S1] = PCF_S1;
110 }
111 sc->sc_clock = clock;
112 sc->sc_addr = addr;
113
114 pcfiic_init(sc);
115
116 printf("\n");
117
118 if (sc->sc_master)
119 pcfiic_choose_bus(sc, 0);
120
121 rw_init(&sc->sc_lock);
122 sc->sc_i2c.ic_cookie = sc;
123 sc->sc_i2c.ic_acquire_bus = pcfiic_i2c_acquire_bus;
124 sc->sc_i2c.ic_release_bus = pcfiic_i2c_release_bus;
125 sc->sc_i2c.ic_exec = pcfiic_i2c_exec;
126
127 bzero(&iba, sizeof(iba));
128 iba.iba_tag = &sc->sc_i2c;
129 config_found(sc->sc_dev, &iba, iicbus_print);
130 }
131
132 int
133 pcfiic_intr(void *arg)
134 {
135 return (0);
136 }
137
138 int
139 pcfiic_i2c_acquire_bus(void *arg, int flags)
140 {
141 struct pcfiic_softc *sc = arg;
142
143 if (cold || sc->sc_poll || (flags & I2C_F_POLL))
144 return (0);
145
146 rw_enter(&sc->sc_lock, RW_WRITER);
147 return 0;
148 }
149
150 void
151 pcfiic_i2c_release_bus(void *arg, int flags)
152 {
153 struct pcfiic_softc *sc = arg;
154
155 if (cold || sc->sc_poll || (flags & I2C_F_POLL))
156 return;
157
158 rw_exit(&sc->sc_lock);
159 }
160
161 int
162 pcfiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
163 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
164 {
165 struct pcfiic_softc *sc = arg;
166 int ret = 0;
167
168 #if 0
169 printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n",
170 sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags);
171 #endif
172
173 if (cold || sc->sc_poll)
174 flags |= I2C_F_POLL;
175
176 if (sc->sc_master)
177 pcfiic_choose_bus(sc, addr >> 7);
178
179 if (cmdlen > 0)
180 if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen) != 0)
181 return (1);
182
183 if (len > 0) {
184 if (I2C_OP_WRITE_P(op))
185 ret = pcfiic_xmit(sc, addr & 0x7f, buf, len);
186 else
187 ret = pcfiic_recv(sc, addr & 0x7f, buf, len);
188 }
189 return (ret);
190 }
191
192 int
193 pcfiic_xmit(struct pcfiic_softc *sc, u_int8_t addr, const u_int8_t *buf,
194 size_t len)
195 {
196 int i, err = 0;
197 volatile u_int8_t r;
198
199 if (pcfiic_wait_nBB(sc) != 0)
200 return (1);
201
202 pcfiic_write(sc, PCF_S0, addr << 1);
203 pcfiic_write(sc, PCF_S1, PCF_CTRL_START);
204
205 for (i = 0; i <= len; i++) {
206 if (pcfiic_wait_pin(sc, &r) != 0) {
207 pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
208 return (1);
209 }
210
211 if (r & PCF_STAT_LRB) {
212 err = 1;
213 break;
214 }
215
216 if (i < len)
217 pcfiic_write(sc, PCF_S0, buf[i]);
218 }
219 pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
220 return (err);
221 }
222
223 int
224 pcfiic_recv(struct pcfiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len)
225 {
226 int i = 0, err = 0;
227 volatile u_int8_t r;
228
229 if (pcfiic_wait_nBB(sc) != 0)
230 return (1);
231
232 pcfiic_write(sc, PCF_S0, (addr << 1) | 0x01);
233 pcfiic_write(sc, PCF_S1, PCF_CTRL_START);
234
235 for (i = 0; i <= len; i++) {
236 if (pcfiic_wait_pin(sc, &r) != 0) {
237 pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
238 return (1);
239 }
240
241 if ((i != len) && (r & PCF_STAT_LRB)) {
242 pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
243 return (1);
244 }
245
246 if (i == len - 1) {
247 pcfiic_write(sc, PCF_S1, PCF_CTRL_ESO);
248 } else if (i == len) {
249 pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP);
250 }
251
252 r = pcfiic_read(sc, PCF_S0);
253 if (i > 0)
254 buf[i - 1] = r;
255 }
256 return (err);
257 }
258
259 u_int8_t
260 pcfiic_read(struct pcfiic_softc *sc, bus_size_t r)
261 {
262 bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1,
263 BUS_SPACE_BARRIER_READ);
264 return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r]));
265 }
266
267 void
268 pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v)
269 {
270 bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v);
271 bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1,
272 BUS_SPACE_BARRIER_WRITE);
273 }
274
275 void
276 pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus)
277 {
278 bus_space_write_1(sc->sc_iot, sc->sc_ioh2, 0, bus);
279 bus_space_barrier(sc->sc_iot, sc->sc_ioh2, 0, 1,
280 BUS_SPACE_BARRIER_WRITE);
281 }
282
283 int
284 pcfiic_wait_nBB(struct pcfiic_softc *sc)
285 {
286 int i;
287
288 for (i = 0; i < 1000; i++) {
289 if (pcfiic_read(sc, PCF_S1) & PCF_STAT_nBB)
290 return (0);
291 delay(1000);
292 }
293 return (1);
294 }
295
296 int
297 pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r)
298 {
299 int i;
300
301 for (i = 0; i < 1000; i++) {
302 *r = pcfiic_read(sc, PCF_S1);
303 if ((*r & PCF_STAT_PIN) == 0)
304 return (0);
305 delay(1000);
306 }
307 return (1);
308 }
309