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