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