1 /*- 2 * Copyright (c) 2020 Justin Hibbits 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #include <sys/cdefs.h> 26 __FBSDID("$FreeBSD$"); 27 28 #include <sys/param.h> 29 #include <sys/systm.h> 30 #include <sys/conf.h> 31 #include <sys/kernel.h> 32 #include <sys/bus.h> 33 #include <sys/limits.h> 34 #include <sys/module.h> 35 #include <sys/malloc.h> 36 #include <sys/mutex.h> 37 #include <sys/rman.h> 38 #include <sys/sysctl.h> 39 40 #include <machine/bus.h> 41 42 #include <dev/ofw/ofw_bus.h> 43 #include <dev/ofw/ofw_bus_subr.h> 44 45 #include "cpld.h" 46 47 /* 48 * A driver for the AmigaOne X5000 "Cyrus+" CPLD. 49 * 50 * This is the interface between the CPU and the "Xena" (XMOS) chip. Since the 51 * XMOS is programmable via a SPI-attached flash memory, there's no direct 52 * driver written for the Xena attachment. Instead, a userspace process would 53 * communicate with the Xena by issuing ioctl()s to this CPLD. 54 */ 55 56 /* Resource access addresses. */ 57 #define CPLD_MEM_ADDR 0x0000 58 #define CPLD_MEM_DATA 0x8000 59 60 #define CPLD_MAX_DRAM_WORDS 0x800 61 62 /* CPLD Registers. */ 63 #define CPLD_REG_SIG1 0x00 64 #define CPLD_REG_SIG2 0x01 65 #define CPLD_REG_HWREV 0x02 66 #define CPLD_REG_MBC2X 0x05 67 #define CPLD_REG_MBX2C 0x06 68 #define CPLD_REG_XDEBUG 0x0c 69 #define CPLD_REG_XJTAG 0x0d 70 #define CPLD_REG_FAN_TACHO 0x10 71 #define CPLD_REG_DATE_LW 0x21 72 #define CPLD_REG_DATE_UW 0x22 73 #define CPLD_REG_TIME_LW 0x23 74 #define CPLD_REG_TIME_UW 0x24 75 #define CPLD_REG_SCR1 0x30 76 #define CPLD_REG_SCR2 0x31 77 #define CPLD_REG_RAM 0x8000 78 79 struct cpld_softc { 80 device_t sc_dev; 81 struct resource *sc_mem; 82 struct cdev *sc_cdev; 83 struct mtx sc_mutex; 84 bool sc_isopen; 85 }; 86 87 static d_open_t cpld_open; 88 static d_close_t cpld_close; 89 static d_ioctl_t cpld_ioctl; 90 91 static struct cdevsw cpld_cdevsw = { 92 .d_version = D_VERSION, 93 .d_open = cpld_open, 94 .d_close = cpld_close, 95 .d_ioctl = cpld_ioctl, 96 .d_name = "nvram", 97 }; 98 99 static device_probe_t cpld_probe; 100 static device_attach_t cpld_attach; 101 static int cpld_fan_sysctl(SYSCTL_HANDLER_ARGS); 102 103 static device_method_t cpld_methods[] = { 104 DEVMETHOD(device_probe, cpld_probe), 105 DEVMETHOD(device_attach, cpld_attach), 106 107 DEVMETHOD_END 108 }; 109 110 static driver_t cpld_driver = { 111 "cpld", 112 cpld_methods, 113 sizeof(struct cpld_softc) 114 }; 115 116 static devclass_t cpld_devclass; 117 DRIVER_MODULE(cpld, lbc, cpld_driver, cpld_devclass, 0, 0); 118 119 static void 120 cpld_write(struct cpld_softc *sc, int addr, int data) 121 { 122 bus_write_2(sc->sc_mem, CPLD_MEM_ADDR, addr); 123 bus_write_2(sc->sc_mem, CPLD_MEM_DATA, data); 124 } 125 126 static int 127 cpld_read(struct cpld_softc *sc, int addr) 128 { 129 bus_write_2(sc->sc_mem, CPLD_MEM_ADDR, addr); 130 131 return (bus_read_2(sc->sc_mem, CPLD_MEM_DATA)); 132 } 133 134 static int 135 cpld_probe(device_t dev) 136 { 137 if (!ofw_bus_is_compatible(dev, "aeon,cyrus-cpld")) 138 return (ENXIO); 139 140 device_set_desc(dev, "AmigaOne Cyrus CPLD"); 141 142 return (BUS_PROBE_GENERIC); 143 } 144 145 static int 146 cpld_attach(device_t dev) 147 { 148 struct make_dev_args mda; 149 struct cpld_softc *sc; 150 int rid; 151 int date, time, tmp; 152 int err; 153 struct sysctl_ctx_list *ctx; 154 struct sysctl_oid *tree; 155 156 sc = device_get_softc(dev); 157 sc->sc_dev = dev; 158 159 rid = 0; 160 sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 161 RF_ACTIVE|RF_SHAREABLE); 162 if (sc->sc_mem == NULL) { 163 device_printf(dev, "Unable to allocate memory resource.\n"); 164 return (ENXIO); 165 } 166 mtx_init(&sc->sc_mutex, "cpld", NULL, MTX_DEF); 167 if (bootverbose) { 168 date = (cpld_read(sc, CPLD_REG_DATE_UW) << 16) | 169 cpld_read(sc, CPLD_REG_DATE_LW); 170 time = (cpld_read(sc, CPLD_REG_TIME_UW) << 16) | 171 cpld_read(sc, CPLD_REG_TIME_LW); 172 173 device_printf(dev, "Build date: %04x-%02x-%02x\n", 174 (date >> 16) & 0xffff, (date >> 8) & 0xff, date & 0xff); 175 device_printf(dev, "Build time: %02x:%02x:%02x\n", 176 (time >> 16) & 0xff, (time >> 8) & 0xff, time & 0xff); 177 } 178 179 tmp = cpld_read(sc, CPLD_REG_HWREV); 180 device_printf(dev, "Hardware revision: %d\n", tmp); 181 182 ctx = device_get_sysctl_ctx(dev); 183 tree = device_get_sysctl_tree(dev); 184 185 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 186 "cpu_fan", CTLTYPE_INT | CTLFLAG_RD, sc, 0, 187 cpld_fan_sysctl, "I", "CPU Fan speed in RPM"); 188 189 make_dev_args_init(&mda); 190 mda.mda_flags = MAKEDEV_CHECKNAME; 191 mda.mda_devsw = &cpld_cdevsw; 192 mda.mda_uid = UID_ROOT; 193 mda.mda_gid = GID_WHEEL; 194 mda.mda_mode = 0660; 195 mda.mda_si_drv1 = sc; 196 err = make_dev_s(&mda, &sc->sc_cdev, "cpld"); 197 if (err != 0) { 198 device_printf(dev, "Error creating character device: %d\n", err); 199 device_printf(dev, "Only sysctl interfaces will be available.\n"); 200 } 201 202 return (0); 203 } 204 205 static int 206 cpld_fan_sysctl(SYSCTL_HANDLER_ARGS) 207 { 208 struct cpld_softc *sc; 209 int error, old, rpm; 210 211 sc = arg1; 212 mtx_lock(&sc->sc_mutex); 213 /* Read until we get some level of read stability. */ 214 rpm = cpld_read(sc, CPLD_REG_FAN_TACHO); 215 do { 216 old = rpm; 217 rpm = cpld_read(sc, CPLD_REG_FAN_TACHO); 218 } while (abs(rpm - old) > 10); 219 mtx_unlock(&sc->sc_mutex); 220 221 /* Convert RPS->RPM. */ 222 rpm *= 60; 223 error = sysctl_handle_int(oidp, &rpm, 0, req); 224 225 return (error); 226 } 227 228 static int 229 cpld_open(struct cdev *dev, int flags, int fmt, struct thread *td) 230 { 231 struct cpld_softc *sc = dev->si_drv1; 232 233 if (sc->sc_isopen) 234 return (EBUSY); 235 sc->sc_isopen = 1; 236 return (0); 237 } 238 239 static int 240 cpld_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 241 { 242 struct cpld_softc *sc = dev->si_drv1; 243 244 sc->sc_isopen = 0; 245 return (0); 246 } 247 248 static int 249 cpld_send(device_t dev, struct cpld_cmd_data *d) 250 { 251 struct cpld_softc *sc; 252 uint16_t *word; 253 int i; 254 255 if (d->cmd > USHRT_MAX) 256 return (EINVAL); 257 258 sc = device_get_softc(dev); 259 260 mtx_lock(&sc->sc_mutex); 261 for (i = 0, word = d->words; i < d->len; i++, word++) { 262 if (i == 0) 263 cpld_write(sc, CPLD_REG_RAM, *word); 264 else 265 bus_write_4(sc->sc_mem, CPLD_MEM_DATA, *word); 266 } 267 268 cpld_write(sc, CPLD_REG_MBC2X, d->cmd); 269 mtx_unlock(&sc->sc_mutex); 270 271 return (0); 272 } 273 274 static int 275 cpld_recv(device_t dev, struct cpld_cmd_data *d) 276 { 277 struct cpld_softc *sc; 278 uint16_t *word; 279 int i; 280 281 sc = device_get_softc(dev); 282 283 mtx_lock(&sc->sc_mutex); 284 d->cmd = cpld_read(sc, CPLD_REG_MBX2C); 285 286 for (i = 0, word = d->words; i < d->len; i++, word++) { 287 if (i == 0) 288 *word = cpld_read(sc, CPLD_REG_RAM); 289 else 290 *word = bus_read_4(sc->sc_mem, CPLD_MEM_DATA); 291 } 292 mtx_unlock(&sc->sc_mutex); 293 294 return (0); 295 } 296 297 static int 298 cpld_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 299 { 300 struct cpld_softc *sc; 301 struct cpld_cmd_data *d; 302 void *xfer_data, *tmp; 303 int err; 304 305 sc = dev->si_drv1; 306 307 err = 0; 308 d = (struct cpld_cmd_data *)data; 309 if (d->len + d->offset > CPLD_MAX_DRAM_WORDS) { 310 return (EINVAL); 311 } 312 xfer_data = malloc(d->len * sizeof(uint16_t), M_TEMP, M_WAITOK); 313 314 switch (cmd) { 315 case IOCCPLDSEND: 316 err = copyin(d->words, xfer_data, d->len * sizeof(uint16_t)); 317 d->words = xfer_data; 318 if (err == 0) 319 err = cpld_send(sc->sc_dev, d); 320 break; 321 case IOCCPLDRECV: 322 tmp = d->words; 323 d->words = xfer_data; 324 err = cpld_recv(sc->sc_dev, d); 325 d->words = tmp; 326 if (err == 0) 327 err = copyout(xfer_data, d->words, 328 d->len * sizeof(uint16_t)); 329 break; 330 default: 331 err = ENOTTY; 332 break; 333 } 334 free(xfer_data, M_TEMP); 335 336 return (err); 337 } 338