1*36dba039Sjsg /* $OpenBSD: imxiic.c,v 1.2 2024/04/14 03:26:25 jsg Exp $ */
2a8794a2cSpatrick /*
3a8794a2cSpatrick * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
4a8794a2cSpatrick *
5a8794a2cSpatrick * Permission to use, copy, modify, and distribute this software for any
6a8794a2cSpatrick * purpose with or without fee is hereby granted, provided that the above
7a8794a2cSpatrick * copyright notice and this permission notice appear in all copies.
8a8794a2cSpatrick *
9a8794a2cSpatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10a8794a2cSpatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11a8794a2cSpatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12a8794a2cSpatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13a8794a2cSpatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14a8794a2cSpatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15a8794a2cSpatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16a8794a2cSpatrick */
17a8794a2cSpatrick
18a8794a2cSpatrick #include <sys/param.h>
19a8794a2cSpatrick #include <sys/device.h>
20a8794a2cSpatrick #include <sys/kernel.h>
21a8794a2cSpatrick #include <sys/systm.h>
22a8794a2cSpatrick
23a8794a2cSpatrick #include <machine/bus.h>
24a8794a2cSpatrick
25a8794a2cSpatrick #include <dev/ic/imxiicvar.h>
26a8794a2cSpatrick
27a8794a2cSpatrick /* registers */
28a8794a2cSpatrick #define I2C_IADR 0x00
29a8794a2cSpatrick #define I2C_IFDR 0x01
30a8794a2cSpatrick #define I2C_I2CR 0x02
31a8794a2cSpatrick #define I2C_I2SR 0x03
32a8794a2cSpatrick #define I2C_I2DR 0x04
33a8794a2cSpatrick
34a8794a2cSpatrick #define I2C_I2CR_RSTA (1 << 2)
35a8794a2cSpatrick #define I2C_I2CR_TXAK (1 << 3)
36a8794a2cSpatrick #define I2C_I2CR_MTX (1 << 4)
37a8794a2cSpatrick #define I2C_I2CR_MSTA (1 << 5)
38a8794a2cSpatrick #define I2C_I2CR_IIEN (1 << 6)
39a8794a2cSpatrick #define I2C_I2CR_IEN (1 << 7)
40a8794a2cSpatrick #define I2C_I2SR_RXAK (1 << 0)
41a8794a2cSpatrick #define I2C_I2SR_IIF (1 << 1)
42a8794a2cSpatrick #define I2C_I2SR_IAL (1 << 4)
43a8794a2cSpatrick #define I2C_I2SR_IBB (1 << 5)
44a8794a2cSpatrick
45a8794a2cSpatrick void imxiic_enable(struct imxiic_softc *, int);
46a8794a2cSpatrick void imxiic_clear_iodone(struct imxiic_softc *);
47a8794a2cSpatrick void imxiic_setspeed(struct imxiic_softc *, u_int);
48a8794a2cSpatrick int imxiic_wait_state(struct imxiic_softc *, uint32_t, uint32_t);
49a8794a2cSpatrick int imxiic_read(struct imxiic_softc *, int, const void *, int,
50a8794a2cSpatrick void *, int);
51a8794a2cSpatrick int imxiic_write(struct imxiic_softc *, int, const void *, int,
52a8794a2cSpatrick const void *, int);
53a8794a2cSpatrick
54a8794a2cSpatrick int imxiic_i2c_acquire_bus(void *, int);
55a8794a2cSpatrick void imxiic_i2c_release_bus(void *, int);
56a8794a2cSpatrick int imxiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
57a8794a2cSpatrick void *, size_t, int);
58a8794a2cSpatrick
59a8794a2cSpatrick uint8_t imxiic_read_1(struct imxiic_softc *, int);
60a8794a2cSpatrick void imxiic_write_1(struct imxiic_softc *, int, uint8_t);
61a8794a2cSpatrick
62a8794a2cSpatrick #define HREAD1(sc, reg) \
63a8794a2cSpatrick imxiic_read_1((sc), (reg))
64a8794a2cSpatrick #define HWRITE1(sc, reg, val) \
65a8794a2cSpatrick imxiic_write_1((sc), (reg), (val))
66a8794a2cSpatrick #define HSET1(sc, reg, bits) \
67a8794a2cSpatrick HWRITE1((sc), (reg), HREAD1((sc), (reg)) | (bits))
68a8794a2cSpatrick #define HCLR1(sc, reg, bits) \
69a8794a2cSpatrick HWRITE1((sc), (reg), HREAD1((sc), (reg)) & ~(bits))
70a8794a2cSpatrick
71a8794a2cSpatrick struct cfdriver imxiic_cd = {
72a8794a2cSpatrick NULL, "imxiic", DV_DULL
73a8794a2cSpatrick };
74a8794a2cSpatrick
75a8794a2cSpatrick void
imxiic_enable(struct imxiic_softc * sc,int on)76a8794a2cSpatrick imxiic_enable(struct imxiic_softc *sc, int on)
77a8794a2cSpatrick {
78a8794a2cSpatrick /*
79a8794a2cSpatrick * VF610: write 1 to clear bits
80a8794a2cSpatrick * iMX21: write 0 to clear bits
81a8794a2cSpatrick */
82a8794a2cSpatrick if (sc->sc_type == I2C_TYPE_VF610)
83a8794a2cSpatrick HWRITE1(sc, I2C_I2SR, I2C_I2SR_IAL | I2C_I2SR_IIF);
84a8794a2cSpatrick else
85a8794a2cSpatrick HWRITE1(sc, I2C_I2SR, 0);
86a8794a2cSpatrick
87a8794a2cSpatrick /* VF610 inverts enable bit meaning */
88a8794a2cSpatrick if (sc->sc_type == I2C_TYPE_VF610)
89a8794a2cSpatrick on = !on;
90a8794a2cSpatrick if (on)
91a8794a2cSpatrick HWRITE1(sc, I2C_I2CR, I2C_I2CR_IEN);
92a8794a2cSpatrick else
93a8794a2cSpatrick HWRITE1(sc, I2C_I2CR, 0);
94a8794a2cSpatrick }
95a8794a2cSpatrick
96a8794a2cSpatrick void
imxiic_clear_iodone(struct imxiic_softc * sc)97a8794a2cSpatrick imxiic_clear_iodone(struct imxiic_softc *sc)
98a8794a2cSpatrick {
99a8794a2cSpatrick /*
100a8794a2cSpatrick * VF610: write bit to clear bit
101a8794a2cSpatrick * iMX21: clear bit, keep rest
102a8794a2cSpatrick */
103a8794a2cSpatrick if (sc->sc_type == I2C_TYPE_VF610)
104a8794a2cSpatrick HWRITE1(sc, I2C_I2SR, I2C_I2SR_IIF);
105a8794a2cSpatrick else
106a8794a2cSpatrick HCLR1(sc, I2C_I2SR, I2C_I2SR_IIF);
107a8794a2cSpatrick }
108a8794a2cSpatrick
109a8794a2cSpatrick void
imxiic_setspeed(struct imxiic_softc * sc,u_int speed)110a8794a2cSpatrick imxiic_setspeed(struct imxiic_softc *sc, u_int speed)
111a8794a2cSpatrick {
112a8794a2cSpatrick if (!sc->frequency) {
113a8794a2cSpatrick uint32_t div;
114a8794a2cSpatrick int i;
115a8794a2cSpatrick
116a8794a2cSpatrick div = (sc->sc_clkrate + speed - 1) / speed;
117a8794a2cSpatrick if (div < sc->sc_clk_div[0].div)
118a8794a2cSpatrick i = 0;
119a8794a2cSpatrick else if (div > sc->sc_clk_div[sc->sc_clk_ndiv - 1].div)
120a8794a2cSpatrick i = sc->sc_clk_ndiv - 1;
121a8794a2cSpatrick else
122*36dba039Sjsg for (i = 0; sc->sc_clk_div[i].div < div; i++)
123*36dba039Sjsg ;
124a8794a2cSpatrick
125a8794a2cSpatrick sc->frequency = sc->sc_clk_div[i].val;
126a8794a2cSpatrick }
127a8794a2cSpatrick
128a8794a2cSpatrick HWRITE1(sc, I2C_IFDR, sc->frequency);
129a8794a2cSpatrick }
130a8794a2cSpatrick
131a8794a2cSpatrick int
imxiic_wait_state(struct imxiic_softc * sc,uint32_t mask,uint32_t value)132a8794a2cSpatrick imxiic_wait_state(struct imxiic_softc *sc, uint32_t mask, uint32_t value)
133a8794a2cSpatrick {
134a8794a2cSpatrick uint32_t state;
135a8794a2cSpatrick int timeout;
136a8794a2cSpatrick for (timeout = 1000; timeout > 0; timeout--) {
137a8794a2cSpatrick if (((state = HREAD1(sc, I2C_I2SR)) & mask) == value)
138a8794a2cSpatrick return 0;
139a8794a2cSpatrick delay(10);
140a8794a2cSpatrick }
141a8794a2cSpatrick return ETIMEDOUT;
142a8794a2cSpatrick }
143a8794a2cSpatrick
144a8794a2cSpatrick int
imxiic_read(struct imxiic_softc * sc,int addr,const void * cmd,int cmdlen,void * data,int len)145a8794a2cSpatrick imxiic_read(struct imxiic_softc *sc, int addr, const void *cmd, int cmdlen,
146a8794a2cSpatrick void *data, int len)
147a8794a2cSpatrick {
148a8794a2cSpatrick int i;
149a8794a2cSpatrick
150a8794a2cSpatrick if (cmdlen > 0) {
151a8794a2cSpatrick if (imxiic_write(sc, addr, cmd, cmdlen, NULL, 0))
152a8794a2cSpatrick return (EIO);
153a8794a2cSpatrick
154a8794a2cSpatrick HSET1(sc, I2C_I2CR, I2C_I2CR_RSTA);
155a8794a2cSpatrick delay(1);
156a8794a2cSpatrick if (imxiic_wait_state(sc, I2C_I2SR_IBB, I2C_I2SR_IBB))
157a8794a2cSpatrick return (EIO);
158a8794a2cSpatrick }
159a8794a2cSpatrick
160a8794a2cSpatrick imxiic_clear_iodone(sc);
161a8794a2cSpatrick HWRITE1(sc, I2C_I2DR, (addr << 1) | 1);
162a8794a2cSpatrick
163a8794a2cSpatrick if (imxiic_wait_state(sc, I2C_I2SR_IIF, I2C_I2SR_IIF))
164a8794a2cSpatrick return (EIO);
165a8794a2cSpatrick imxiic_clear_iodone(sc);
166a8794a2cSpatrick if (HREAD1(sc, I2C_I2SR) & I2C_I2SR_RXAK)
167a8794a2cSpatrick return (EIO);
168a8794a2cSpatrick
169a8794a2cSpatrick HCLR1(sc, I2C_I2CR, I2C_I2CR_MTX);
170a8794a2cSpatrick if (len - 1)
171a8794a2cSpatrick HCLR1(sc, I2C_I2CR, I2C_I2CR_TXAK);
172a8794a2cSpatrick
173a8794a2cSpatrick /* dummy read */
174a8794a2cSpatrick HREAD1(sc, I2C_I2DR);
175a8794a2cSpatrick
176a8794a2cSpatrick for (i = 0; i < len; i++) {
177a8794a2cSpatrick if (imxiic_wait_state(sc, I2C_I2SR_IIF, I2C_I2SR_IIF))
178a8794a2cSpatrick return (EIO);
179a8794a2cSpatrick imxiic_clear_iodone(sc);
180a8794a2cSpatrick
181a8794a2cSpatrick if (i == (len - 1)) {
182a8794a2cSpatrick HCLR1(sc, I2C_I2CR, I2C_I2CR_MSTA | I2C_I2CR_MTX);
183a8794a2cSpatrick imxiic_wait_state(sc, I2C_I2SR_IBB, 0);
184a8794a2cSpatrick sc->stopped = 1;
185a8794a2cSpatrick } else if (i == (len - 2)) {
186a8794a2cSpatrick HSET1(sc, I2C_I2CR, I2C_I2CR_TXAK);
187a8794a2cSpatrick }
188a8794a2cSpatrick ((uint8_t*)data)[i] = HREAD1(sc, I2C_I2DR);
189a8794a2cSpatrick }
190a8794a2cSpatrick
191a8794a2cSpatrick return 0;
192a8794a2cSpatrick }
193a8794a2cSpatrick
194a8794a2cSpatrick int
imxiic_write(struct imxiic_softc * sc,int addr,const void * cmd,int cmdlen,const void * data,int len)195a8794a2cSpatrick imxiic_write(struct imxiic_softc *sc, int addr, const void *cmd, int cmdlen,
196a8794a2cSpatrick const void *data, int len)
197a8794a2cSpatrick {
198a8794a2cSpatrick int i;
199a8794a2cSpatrick
200a8794a2cSpatrick imxiic_clear_iodone(sc);
201a8794a2cSpatrick HWRITE1(sc, I2C_I2DR, addr << 1);
202a8794a2cSpatrick
203a8794a2cSpatrick if (imxiic_wait_state(sc, I2C_I2SR_IIF, I2C_I2SR_IIF))
204a8794a2cSpatrick return (EIO);
205a8794a2cSpatrick imxiic_clear_iodone(sc);
206a8794a2cSpatrick if (HREAD1(sc, I2C_I2SR) & I2C_I2SR_RXAK)
207a8794a2cSpatrick return (EIO);
208a8794a2cSpatrick
209a8794a2cSpatrick for (i = 0; i < cmdlen; i++) {
210a8794a2cSpatrick HWRITE1(sc, I2C_I2DR, ((uint8_t*)cmd)[i]);
211a8794a2cSpatrick if (imxiic_wait_state(sc, I2C_I2SR_IIF, I2C_I2SR_IIF))
212a8794a2cSpatrick return (EIO);
213a8794a2cSpatrick imxiic_clear_iodone(sc);
214a8794a2cSpatrick if (HREAD1(sc, I2C_I2SR) & I2C_I2SR_RXAK)
215a8794a2cSpatrick return (EIO);
216a8794a2cSpatrick }
217a8794a2cSpatrick
218a8794a2cSpatrick for (i = 0; i < len; i++) {
219a8794a2cSpatrick HWRITE1(sc, I2C_I2DR, ((uint8_t*)data)[i]);
220a8794a2cSpatrick if (imxiic_wait_state(sc, I2C_I2SR_IIF, I2C_I2SR_IIF))
221a8794a2cSpatrick return (EIO);
222a8794a2cSpatrick imxiic_clear_iodone(sc);
223a8794a2cSpatrick if (HREAD1(sc, I2C_I2SR) & I2C_I2SR_RXAK)
224a8794a2cSpatrick return (EIO);
225a8794a2cSpatrick }
226a8794a2cSpatrick return 0;
227a8794a2cSpatrick }
228a8794a2cSpatrick
229a8794a2cSpatrick int
imxiic_i2c_acquire_bus(void * cookie,int flags)230a8794a2cSpatrick imxiic_i2c_acquire_bus(void *cookie, int flags)
231a8794a2cSpatrick {
232a8794a2cSpatrick struct imxiic_softc *sc = cookie;
233a8794a2cSpatrick
234a8794a2cSpatrick rw_enter(&sc->sc_buslock, RW_WRITE);
235a8794a2cSpatrick
236a8794a2cSpatrick /* set speed */
237a8794a2cSpatrick imxiic_setspeed(sc, sc->sc_bitrate);
238a8794a2cSpatrick
239a8794a2cSpatrick /* enable the controller */
240a8794a2cSpatrick imxiic_enable(sc, 1);
241a8794a2cSpatrick
242a8794a2cSpatrick /* wait for it to be stable */
243a8794a2cSpatrick delay(50);
244a8794a2cSpatrick
245a8794a2cSpatrick return 0;
246a8794a2cSpatrick }
247a8794a2cSpatrick
248a8794a2cSpatrick void
imxiic_i2c_release_bus(void * cookie,int flags)249a8794a2cSpatrick imxiic_i2c_release_bus(void *cookie, int flags)
250a8794a2cSpatrick {
251a8794a2cSpatrick struct imxiic_softc *sc = cookie;
252a8794a2cSpatrick
253a8794a2cSpatrick imxiic_enable(sc, 0);
254a8794a2cSpatrick
255a8794a2cSpatrick rw_exit(&sc->sc_buslock);
256a8794a2cSpatrick }
257a8794a2cSpatrick
258a8794a2cSpatrick int
imxiic_i2c_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * cmdbuf,size_t cmdlen,void * buf,size_t len,int flags)259a8794a2cSpatrick imxiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
260a8794a2cSpatrick const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
261a8794a2cSpatrick {
262a8794a2cSpatrick struct imxiic_softc *sc = cookie;
263a8794a2cSpatrick int ret = 0;
264a8794a2cSpatrick
265a8794a2cSpatrick if (!I2C_OP_STOP_P(op))
266a8794a2cSpatrick return EINVAL;
267a8794a2cSpatrick
268a8794a2cSpatrick /* start transaction */
269a8794a2cSpatrick HSET1(sc, I2C_I2CR, I2C_I2CR_MSTA);
270a8794a2cSpatrick
271a8794a2cSpatrick if (imxiic_wait_state(sc, I2C_I2SR_IBB, I2C_I2SR_IBB)) {
272a8794a2cSpatrick ret = EIO;
273a8794a2cSpatrick goto fail;
274a8794a2cSpatrick }
275a8794a2cSpatrick
276a8794a2cSpatrick sc->stopped = 0;
277a8794a2cSpatrick
278a8794a2cSpatrick HSET1(sc, I2C_I2CR, I2C_I2CR_IIEN | I2C_I2CR_MTX | I2C_I2CR_TXAK);
279a8794a2cSpatrick
280a8794a2cSpatrick if (I2C_OP_READ_P(op)) {
281a8794a2cSpatrick ret = imxiic_read(sc, addr, cmdbuf, cmdlen, buf, len);
282a8794a2cSpatrick } else {
283a8794a2cSpatrick ret = imxiic_write(sc, addr, cmdbuf, cmdlen, buf, len);
284a8794a2cSpatrick }
285a8794a2cSpatrick
286a8794a2cSpatrick fail:
287a8794a2cSpatrick if (!sc->stopped) {
288a8794a2cSpatrick HCLR1(sc, I2C_I2CR, I2C_I2CR_MSTA | I2C_I2CR_MTX);
289a8794a2cSpatrick imxiic_wait_state(sc, I2C_I2SR_IBB, 0);
290a8794a2cSpatrick sc->stopped = 1;
291a8794a2cSpatrick }
292a8794a2cSpatrick
293a8794a2cSpatrick return ret;
294a8794a2cSpatrick }
295a8794a2cSpatrick
296a8794a2cSpatrick uint8_t
imxiic_read_1(struct imxiic_softc * sc,int reg)297a8794a2cSpatrick imxiic_read_1(struct imxiic_softc *sc, int reg)
298a8794a2cSpatrick {
299a8794a2cSpatrick reg <<= sc->sc_reg_shift;
300a8794a2cSpatrick
301a8794a2cSpatrick return bus_space_read_1(sc->sc_iot, sc->sc_ioh, reg);
302a8794a2cSpatrick }
303a8794a2cSpatrick
304a8794a2cSpatrick void
imxiic_write_1(struct imxiic_softc * sc,int reg,uint8_t val)305a8794a2cSpatrick imxiic_write_1(struct imxiic_softc *sc, int reg, uint8_t val)
306a8794a2cSpatrick {
307a8794a2cSpatrick reg <<= sc->sc_reg_shift;
308a8794a2cSpatrick
309a8794a2cSpatrick bus_space_write_1(sc->sc_iot, sc->sc_ioh, reg, val);
310a8794a2cSpatrick }
311