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