cdnsiic.c revision 1.1 1 1.1 jmcneill /* $NetBSD: cdnsiic.c,v 1.1 2022/11/05 17:31:37 jmcneill Exp $ */
2 1.1 jmcneill
3 1.1 jmcneill /*-
4 1.1 jmcneill * Copyright (c) 2022 Jared McNeill <jmcneill (at) invisible.ca>
5 1.1 jmcneill * All rights reserved.
6 1.1 jmcneill *
7 1.1 jmcneill * Redistribution and use in source and binary forms, with or without
8 1.1 jmcneill * modification, are permitted provided that the following conditions
9 1.1 jmcneill * are met:
10 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright
11 1.1 jmcneill * notice, this list of conditions and the following disclaimer.
12 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the
14 1.1 jmcneill * documentation and/or other materials provided with the distribution.
15 1.1 jmcneill *
16 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 jmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 jmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 jmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 jmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 jmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 jmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 jmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 jmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 jmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 jmcneill * POSSIBILITY OF SUCH DAMAGE.
27 1.1 jmcneill */
28 1.1 jmcneill
29 1.1 jmcneill /*
30 1.1 jmcneill * Cadence I2C controller
31 1.1 jmcneill */
32 1.1 jmcneill
33 1.1 jmcneill #include <sys/cdefs.h>
34 1.1 jmcneill
35 1.1 jmcneill __KERNEL_RCSID(0, "$NetBSD: cdnsiic.c,v 1.1 2022/11/05 17:31:37 jmcneill Exp $");
36 1.1 jmcneill
37 1.1 jmcneill #include <sys/param.h>
38 1.1 jmcneill #include <sys/bus.h>
39 1.1 jmcneill #include <sys/device.h>
40 1.1 jmcneill #include <sys/intr.h>
41 1.1 jmcneill #include <sys/systm.h>
42 1.1 jmcneill #include <sys/time.h>
43 1.1 jmcneill #include <sys/kmem.h>
44 1.1 jmcneill
45 1.1 jmcneill #include <dev/clk/clk.h>
46 1.1 jmcneill #include <dev/i2c/i2cvar.h>
47 1.1 jmcneill
48 1.1 jmcneill #include <dev/ic/cdnsiicvar.h>
49 1.1 jmcneill
50 1.1 jmcneill /* From Zynq-7000 SoC Technical Reference Manual, "Supports 16-byte FIFO" */
51 1.1 jmcneill #define FIFO_DEPTH 16
52 1.1 jmcneill
53 1.1 jmcneill /* Poll timeout, in microseconds. */
54 1.1 jmcneill #define POLL_TIMEOUT 10000
55 1.1 jmcneill
56 1.1 jmcneill #define CR_REG 0x00
57 1.1 jmcneill #define CR_DIV_A __BITS(15,14)
58 1.1 jmcneill #define CR_DIV_B __BITS(13,8)
59 1.1 jmcneill #define CR_CLR_FIFO __BIT(6)
60 1.1 jmcneill #define CR_HOLD __BIT(4)
61 1.1 jmcneill #define CR_ACKEN __BIT(3)
62 1.1 jmcneill #define CR_NEA __BIT(2)
63 1.1 jmcneill #define CR_MS __BIT(1)
64 1.1 jmcneill #define CR_RD_WR __BIT(0)
65 1.1 jmcneill #define SR_REG 0x04
66 1.1 jmcneill #define SR_TXDV __BIT(6)
67 1.1 jmcneill #define SR_RXDV __BIT(5)
68 1.1 jmcneill #define ADDR_REG 0x08
69 1.1 jmcneill #define DATA_REG 0x0c
70 1.1 jmcneill #define ISR_REG 0x10
71 1.1 jmcneill #define ISR_ARB_LOST __BIT(9)
72 1.1 jmcneill #define ISR_RX_UNF __BIT(7)
73 1.1 jmcneill #define ISR_TX_OVR __BIT(6)
74 1.1 jmcneill #define ISR_RX_OVR __BIT(5)
75 1.1 jmcneill #define ISR_SLV_RDY __BIT(4)
76 1.1 jmcneill #define ISR_TO __BIT(3)
77 1.1 jmcneill #define ISR_NACK __BIT(2)
78 1.1 jmcneill #define ISR_DATA __BIT(1)
79 1.1 jmcneill #define ISR_COMP __BIT(0)
80 1.1 jmcneill #define ISR_ERROR_MASK (ISR_ARB_LOST | ISR_TX_OVR | ISR_RX_OVR | ISR_NACK)
81 1.1 jmcneill #define TRANS_SIZE_REG 0x14
82 1.1 jmcneill #define SLV_PAUSE_REG 0x18
83 1.1 jmcneill #define TIME_OUT_REG 0x1c
84 1.1 jmcneill #define IMR_REG 0x20
85 1.1 jmcneill #define IER_REG 0x24
86 1.1 jmcneill #define IDR_REG 0x28
87 1.1 jmcneill
88 1.1 jmcneill #define RD4(sc, reg) \
89 1.1 jmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
90 1.1 jmcneill #define WR4(sc, reg, val) \
91 1.1 jmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
92 1.1 jmcneill
93 1.1 jmcneill static int
94 1.1 jmcneill cdnsiic_init(struct cdnsiic_softc *sc)
95 1.1 jmcneill {
96 1.1 jmcneill int diva, divb;
97 1.1 jmcneill int diff, calc_bus_freq;
98 1.1 jmcneill int best_diva, best_divb, best_diff;
99 1.1 jmcneill u_int pclk;
100 1.1 jmcneill
101 1.1 jmcneill /*
102 1.1 jmcneill * SCL frequency is calculated by the following formula:
103 1.1 jmcneill *
104 1.1 jmcneill * SCL Divisor = 22 * (divisor_a + 1) * (divisor_b + 1)
105 1.1 jmcneill * SCL = PCLK / SCLK Divisor
106 1.1 jmcneill */
107 1.1 jmcneill
108 1.1 jmcneill pclk = clk_get_rate(sc->sc_pclk);
109 1.1 jmcneill best_diff = sc->sc_bus_freq;
110 1.1 jmcneill best_diva = best_divb = 0;
111 1.1 jmcneill
112 1.1 jmcneill for (diva = 0; diva <= 0x3; diva++) {
113 1.1 jmcneill divb = howmany(pclk, 22 * sc->sc_bus_freq * (diva + 1)) - 1;
114 1.1 jmcneill if (divb < 0 || divb > 0x3f) {
115 1.1 jmcneill continue;
116 1.1 jmcneill }
117 1.1 jmcneill calc_bus_freq = pclk / (22 * (diva + 1) * (divb + 1));
118 1.1 jmcneill diff = sc->sc_bus_freq - calc_bus_freq;
119 1.1 jmcneill if (diff < best_diff) {
120 1.1 jmcneill best_diff = diff;
121 1.1 jmcneill best_diva = diva;
122 1.1 jmcneill best_divb = divb;
123 1.1 jmcneill }
124 1.1 jmcneill }
125 1.1 jmcneill if (best_diff == sc->sc_bus_freq) {
126 1.1 jmcneill return ENXIO;
127 1.1 jmcneill }
128 1.1 jmcneill
129 1.1 jmcneill WR4(sc, CR_REG,
130 1.1 jmcneill __SHIFTIN(best_diva, CR_DIV_A) |
131 1.1 jmcneill __SHIFTIN(best_divb, CR_DIV_B) |
132 1.1 jmcneill CR_CLR_FIFO |
133 1.1 jmcneill CR_ACKEN |
134 1.1 jmcneill CR_NEA |
135 1.1 jmcneill CR_MS);
136 1.1 jmcneill WR4(sc, TIME_OUT_REG, 0xff);
137 1.1 jmcneill
138 1.1 jmcneill return 0;
139 1.1 jmcneill }
140 1.1 jmcneill
141 1.1 jmcneill static int
142 1.1 jmcneill cdnsiic_poll_fifo(struct cdnsiic_softc *sc, uint32_t sr_mask, uint32_t sr_maskval)
143 1.1 jmcneill {
144 1.1 jmcneill uint32_t sr_val, isr_val;
145 1.1 jmcneill int retry = POLL_TIMEOUT;
146 1.1 jmcneill
147 1.1 jmcneill while (--retry > 0) {
148 1.1 jmcneill sr_val = RD4(sc, SR_REG);
149 1.1 jmcneill isr_val = RD4(sc, ISR_REG);
150 1.1 jmcneill if ((isr_val & ISR_ERROR_MASK) != 0) {
151 1.1 jmcneill return EIO;
152 1.1 jmcneill }
153 1.1 jmcneill if ((sr_val & sr_mask) == sr_maskval) {
154 1.1 jmcneill return 0;
155 1.1 jmcneill }
156 1.1 jmcneill delay(1);
157 1.1 jmcneill }
158 1.1 jmcneill
159 1.1 jmcneill return ETIMEDOUT;
160 1.1 jmcneill }
161 1.1 jmcneill
162 1.1 jmcneill static int
163 1.1 jmcneill cdnsiic_poll_transfer_complete(struct cdnsiic_softc *sc)
164 1.1 jmcneill {
165 1.1 jmcneill uint32_t val;
166 1.1 jmcneill int retry = POLL_TIMEOUT;
167 1.1 jmcneill
168 1.1 jmcneill while (--retry > 0) {
169 1.1 jmcneill val = RD4(sc, ISR_REG);
170 1.1 jmcneill if ((val & ISR_COMP) != 0) {
171 1.1 jmcneill return 0;
172 1.1 jmcneill }
173 1.1 jmcneill delay(1);
174 1.1 jmcneill }
175 1.1 jmcneill
176 1.1 jmcneill return ETIMEDOUT;
177 1.1 jmcneill }
178 1.1 jmcneill
179 1.1 jmcneill static int
180 1.1 jmcneill cdnsiic_write(struct cdnsiic_softc *sc, i2c_addr_t addr,
181 1.1 jmcneill const uint8_t *data, size_t datalen, bool send_stop)
182 1.1 jmcneill {
183 1.1 jmcneill uint32_t val;
184 1.1 jmcneill u_int xferlen, fifo_space, n;
185 1.1 jmcneill bool write_addr = true;
186 1.1 jmcneill int error;
187 1.1 jmcneill
188 1.1 jmcneill if (datalen == 0 || datalen > 256) {
189 1.1 jmcneill return EINVAL;
190 1.1 jmcneill }
191 1.1 jmcneill
192 1.1 jmcneill val = RD4(sc, CR_REG);
193 1.1 jmcneill val |= CR_CLR_FIFO;
194 1.1 jmcneill val &= ~CR_RD_WR;
195 1.1 jmcneill WR4(sc, CR_REG, val);
196 1.1 jmcneill WR4(sc, ISR_REG, RD4(sc, ISR_REG));
197 1.1 jmcneill
198 1.1 jmcneill while (datalen > 0) {
199 1.1 jmcneill fifo_space = FIFO_DEPTH - RD4(sc, TRANS_SIZE_REG);
200 1.1 jmcneill xferlen = uimin(datalen, fifo_space);
201 1.1 jmcneill for (n = 0; n < xferlen; n++, data++) {
202 1.1 jmcneill WR4(sc, DATA_REG, *data);
203 1.1 jmcneill }
204 1.1 jmcneill if (write_addr) {
205 1.1 jmcneill WR4(sc, ADDR_REG, addr);
206 1.1 jmcneill write_addr = false;
207 1.1 jmcneill }
208 1.1 jmcneill datalen -= xferlen;
209 1.1 jmcneill error = cdnsiic_poll_fifo(sc, SR_TXDV, 0);
210 1.1 jmcneill if (error != 0) {
211 1.1 jmcneill return error;
212 1.1 jmcneill }
213 1.1 jmcneill }
214 1.1 jmcneill
215 1.1 jmcneill return cdnsiic_poll_transfer_complete(sc);
216 1.1 jmcneill }
217 1.1 jmcneill
218 1.1 jmcneill static int
219 1.1 jmcneill cdnsiic_read(struct cdnsiic_softc *sc, i2c_addr_t addr,
220 1.1 jmcneill uint8_t *data, size_t datalen)
221 1.1 jmcneill {
222 1.1 jmcneill uint32_t val;
223 1.1 jmcneill int error;
224 1.1 jmcneill
225 1.1 jmcneill if (datalen == 0 || datalen > 255) {
226 1.1 jmcneill return EINVAL;
227 1.1 jmcneill }
228 1.1 jmcneill
229 1.1 jmcneill val = RD4(sc, CR_REG);
230 1.1 jmcneill val |= CR_CLR_FIFO | CR_RD_WR;
231 1.1 jmcneill WR4(sc, CR_REG, val);
232 1.1 jmcneill WR4(sc, ISR_REG, RD4(sc, ISR_REG));
233 1.1 jmcneill WR4(sc, TRANS_SIZE_REG, datalen);
234 1.1 jmcneill WR4(sc, ADDR_REG, addr);
235 1.1 jmcneill
236 1.1 jmcneill while (datalen > 0) {
237 1.1 jmcneill error = cdnsiic_poll_fifo(sc, SR_RXDV, SR_RXDV);
238 1.1 jmcneill if (error != 0) {
239 1.1 jmcneill return error;
240 1.1 jmcneill }
241 1.1 jmcneill *data = RD4(sc, DATA_REG) & 0xff;
242 1.1 jmcneill data++;
243 1.1 jmcneill datalen--;
244 1.1 jmcneill }
245 1.1 jmcneill
246 1.1 jmcneill return cdnsiic_poll_transfer_complete(sc);
247 1.1 jmcneill }
248 1.1 jmcneill
249 1.1 jmcneill static int
250 1.1 jmcneill cdnsiic_exec(void *priv, i2c_op_t op, i2c_addr_t addr,
251 1.1 jmcneill const void *cmdbuf, size_t cmdlen, void *buf, size_t buflen, int flags)
252 1.1 jmcneill {
253 1.1 jmcneill struct cdnsiic_softc * const sc = priv;
254 1.1 jmcneill uint32_t val;
255 1.1 jmcneill int error;
256 1.1 jmcneill
257 1.1 jmcneill val = RD4(sc, CR_REG);
258 1.1 jmcneill WR4(sc, CR_REG, val | CR_HOLD);
259 1.1 jmcneill
260 1.1 jmcneill if (cmdlen > 0) {
261 1.1 jmcneill error = cdnsiic_write(sc, addr, cmdbuf, cmdlen, false);
262 1.1 jmcneill if (error != 0) {
263 1.1 jmcneill goto done;
264 1.1 jmcneill }
265 1.1 jmcneill }
266 1.1 jmcneill if (I2C_OP_READ_P(op)) {
267 1.1 jmcneill error = cdnsiic_read(sc, addr, buf, buflen);
268 1.1 jmcneill } else {
269 1.1 jmcneill error = cdnsiic_write(sc, addr, buf, buflen, true);
270 1.1 jmcneill }
271 1.1 jmcneill
272 1.1 jmcneill done:
273 1.1 jmcneill val = RD4(sc, CR_REG);
274 1.1 jmcneill WR4(sc, CR_REG, val & ~CR_HOLD);
275 1.1 jmcneill
276 1.1 jmcneill return error;
277 1.1 jmcneill }
278 1.1 jmcneill
279 1.1 jmcneill int
280 1.1 jmcneill cdnsiic_attach(struct cdnsiic_softc *sc)
281 1.1 jmcneill {
282 1.1 jmcneill int error;
283 1.1 jmcneill
284 1.1 jmcneill aprint_naive("\n");
285 1.1 jmcneill aprint_normal(": Cadence I2C (%u Hz)\n", sc->sc_bus_freq);
286 1.1 jmcneill
287 1.1 jmcneill error = cdnsiic_init(sc);
288 1.1 jmcneill if (error != 0) {
289 1.1 jmcneill return error;
290 1.1 jmcneill }
291 1.1 jmcneill
292 1.1 jmcneill iic_tag_init(&sc->sc_ic);
293 1.1 jmcneill sc->sc_ic.ic_cookie = sc;
294 1.1 jmcneill sc->sc_ic.ic_exec = cdnsiic_exec;
295 1.1 jmcneill
296 1.1 jmcneill return 0;
297 1.1 jmcneill }
298