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