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