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