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