pcf8584.c revision 1.22 1 /* $NetBSD: pcf8584.c,v 1.22 2025/09/15 13:23:03 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 (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/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 if (swapregs) {
77 sc->sc_regmap[PCF8584_S1] = PCF8584_S0;
78 sc->sc_regmap[PCF8584_S0] = PCF8584_S1;
79 } else {
80 sc->sc_regmap[PCF8584_S0] = PCF8584_S0;
81 sc->sc_regmap[PCF8584_S1] = PCF8584_S1;
82 }
83 sc->sc_clock = clock;
84 sc->sc_addr = addr;
85
86 pcfiic_init(sc);
87
88 if (sc->sc_master)
89 pcfiic_choose_bus(sc, 0);
90
91 iic_tag_init(&sc->sc_i2c);
92 sc->sc_i2c.ic_cookie = sc;
93 sc->sc_i2c.ic_exec = pcfiic_i2c_exec;
94
95 iicbus_attach(sc->sc_dev, &sc->sc_i2c);
96 }
97
98 int
99 pcfiic_intr(void *arg)
100 {
101 return (0);
102 }
103
104 int
105 pcfiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
106 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
107 {
108 struct pcfiic_softc *sc = arg;
109 int ret = 0;
110
111 #if 0
112 printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n",
113 device_xname(sc->sc_dev), op, addr, (int)cmdlen, (int)len, flags);
114 #endif
115
116 if (sc->sc_poll)
117 flags |= I2C_F_POLL;
118
119 if (sc->sc_master)
120 pcfiic_choose_bus(sc, addr >> 7);
121
122 /*
123 * If we are writing, write address, cmdbuf, buf.
124 * If we are reading, write address, cmdbuf, then read address, buf.
125 */
126 if (I2C_OP_WRITE_P(op)) {
127 ret = pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen, buf, len);
128 } else {
129 if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen, NULL, 0) != 0)
130 return (1);
131 ret = pcfiic_recv(sc, addr & 0x7f, buf, len);
132 }
133 return (ret);
134 }
135
136 int
137 pcfiic_xmit(struct pcfiic_softc *sc, u_int8_t addr, const u_int8_t *cmdbuf,
138 size_t cmdlen, const u_int8_t *buf, size_t len)
139 {
140 int i, err = 0;
141 volatile u_int8_t r;
142
143 if (pcfiic_wait_BBN(sc) != 0)
144 return (1);
145
146 pcfiic_write(sc, PCF8584_S0, addr << 1);
147 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_START);
148
149 for (i = 0; i <= cmdlen + len; i++) {
150 if (pcfiic_wait_pin(sc, &r) != 0) {
151 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
152 return (1);
153 }
154
155 if (r & PCF8584_STATUS_LRB) {
156 err = 1;
157 break;
158 }
159
160 if (i < cmdlen)
161 pcfiic_write(sc, PCF8584_S0, cmdbuf[i]);
162 else if (i < cmdlen + len)
163 pcfiic_write(sc, PCF8584_S0, buf[i - cmdlen]);
164 }
165 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
166 return (err);
167 }
168
169 int
170 pcfiic_recv(struct pcfiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len)
171 {
172 int i = 0, err = 0;
173 volatile u_int8_t r;
174
175 if (pcfiic_wait_BBN(sc) != 0)
176 return (1);
177
178 pcfiic_write(sc, PCF8584_S0, (addr << 1) | 0x01);
179 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_START);
180
181 for (i = 0; i <= len; i++) {
182 if (pcfiic_wait_pin(sc, &r) != 0) {
183 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
184 return (1);
185 }
186
187 if ((i != len) && (r & PCF8584_STATUS_LRB)) {
188 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
189 return (1);
190 }
191
192 if (i == len - 1) {
193 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_NAK);
194 } else if (i == len) {
195 pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP);
196 }
197
198 r = pcfiic_read(sc, PCF8584_S0);
199 if (i > 0)
200 buf[i - 1] = r;
201 }
202 return (err);
203 }
204
205 u_int8_t
206 pcfiic_read(struct pcfiic_softc *sc, bus_size_t r)
207 {
208 bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1,
209 BUS_SPACE_BARRIER_READ);
210 return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r]));
211 }
212
213 void
214 pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v)
215 {
216 bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v);
217 (void)bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCF8584_S1);
218 }
219
220 void
221 pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus)
222 {
223 bus_space_write_1(sc->sc_iot, sc->sc_ioh2, 0, bus);
224 bus_space_barrier(sc->sc_iot, sc->sc_ioh2, 0, 1,
225 BUS_SPACE_BARRIER_WRITE);
226 }
227
228 int
229 pcfiic_wait_BBN(struct pcfiic_softc *sc)
230 {
231 int i;
232
233 for (i = 0; i < 1000; i++) {
234 if (pcfiic_read(sc, PCF8584_S1) & PCF8584_STATUS_BBN)
235 return (0);
236 delay(1000);
237 }
238 return (1);
239 }
240
241 int
242 pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r)
243 {
244 int i;
245
246 for (i = 0; i < 1000; i++) {
247 *r = pcfiic_read(sc, PCF8584_S1);
248 if ((*r & PCF8584_STATUS_PIN) == 0)
249 return (0);
250 delay(1000);
251 }
252 return (1);
253 }
254