From 2a05eb9f3c4b57e038a222f7eb6532454b946a97 Mon Sep 17 00:00:00 2001 From: Justin Hibbits Date: Sun, 19 Jan 2020 21:43:15 +0000 Subject: PowerPC: Add CPLD driver for AmigaOne X5000 Summary: The CPLD is the communications medium between the CPU and the XMOS "Xena" event coprocessor. It provides a mailbox communication feature, along with dual-port RAM to be used between the CPU and XMOS. Also, it provides basic board stats as well, such as PCIe presence, JTAG signals, and CPU fan speed reporting (in revolutions per second). Only fan speed reading is handled, as a sysctl. Reviewed by: bdragon Differential Revision: https://reviews.freebsd.org/D23136 --- sys/conf/files.powerpc | 1 + sys/powerpc/amigaone/cpld.h | 45 +++++ sys/powerpc/amigaone/cpld_x5000.c | 335 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 381 insertions(+) create mode 100644 sys/powerpc/amigaone/cpld.h create mode 100644 sys/powerpc/amigaone/cpld_x5000.c diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc index 62fb3b0c15d2..05d38d58be7a 100644 --- a/sys/conf/files.powerpc +++ b/sys/conf/files.powerpc @@ -107,6 +107,7 @@ powerpc/aim/moea64_native.c optional aim powerpc/aim/mp_cpudep.c optional aim powerpc/aim/slb.c optional aim powerpc64 powerpc/amigaone/platform_amigaone.c optional amigaone +powerpc/amigaone/cpld_x5000.c optional powerpc amigaone | powerpc64 amigaone powerpc/booke/locore.S optional booke no-obj powerpc/booke/booke_machdep.c optional booke powerpc/booke/machdep_e500.c optional booke_e500 diff --git a/sys/powerpc/amigaone/cpld.h b/sys/powerpc/amigaone/cpld.h new file mode 100644 index 000000000000..bdab9fd9c857 --- /dev/null +++ b/sys/powerpc/amigaone/cpld.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2020 Justin Hibbits + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef AMIGAONE_CPLD_H +#define AMIGAONE_CPLD_H + +#include + +/* + * Write 'words' to 'offset' offset in dual-port RAM, then write cmd to mailbox. + */ +struct cpld_cmd_data { + unsigned int cmd; + unsigned int len; + unsigned int offset; + void *words; +}; + +#define IOCCPLDSEND _IOW('c', 2, struct cpld_cmd_data) +#define IOCCPLDRECV _IOW('c', 3, struct cpld_cmd_data) + +#endif /* AMIGAONE_CPLD_H */ diff --git a/sys/powerpc/amigaone/cpld_x5000.c b/sys/powerpc/amigaone/cpld_x5000.c new file mode 100644 index 000000000000..901b014cf469 --- /dev/null +++ b/sys/powerpc/amigaone/cpld_x5000.c @@ -0,0 +1,335 @@ +/*- + * Copyright (c) 2020 Justin Hibbits + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "cpld.h" + +/* + * A driver for the AmigaOne X5000 "Cyrus+" CPLD. + * + * This is the interface between the CPU and the "Xena" (XMOS) chip. Since the + * XMOS is programmable via a SPI-attached flash memory, there's no direct + * driver written for the Xena attachment. Instead, a userspace process would + * communicate with the Xena by issuing ioctl()s to this CPLD. + */ + +/* Resource access addresses. */ +#define CPLD_MEM_ADDR 0x0000 +#define CPLD_MEM_DATA 0x8000 + +#define CPLD_MAX_DRAM_WORDS 0x800 + +/* CPLD Registers. */ +#define CPLD_REG_SIG1 0x00 +#define CPLD_REG_SIG2 0x01 +#define CPLD_REG_HWREV 0x02 +#define CPLD_REG_MBC2X 0x05 +#define CPLD_REG_MBX2C 0x06 +#define CPLD_REG_XDEBUG 0x0c +#define CPLD_REG_XJTAG 0x0d +#define CPLD_REG_FAN_TACHO 0x10 +#define CPLD_REG_DATE_LW 0x21 +#define CPLD_REG_DATE_UW 0x22 +#define CPLD_REG_TIME_LW 0x23 +#define CPLD_REG_TIME_UW 0x24 +#define CPLD_REG_SCR1 0x30 +#define CPLD_REG_SCR2 0x31 +#define CPLD_REG_RAM 0x8000 + +struct cpld_softc { + device_t sc_dev; + struct resource *sc_mem; + struct cdev *sc_cdev; + struct mtx sc_mutex; + bool sc_isopen; +}; + +static d_open_t cpld_open; +static d_close_t cpld_close; +static d_ioctl_t cpld_ioctl; + +static struct cdevsw cpld_cdevsw = { + .d_version = D_VERSION, + .d_open = cpld_open, + .d_close = cpld_close, + .d_ioctl = cpld_ioctl, + .d_name = "nvram", +}; + +static device_probe_t cpld_probe; +static device_attach_t cpld_attach; +static int cpld_fan_sysctl(SYSCTL_HANDLER_ARGS); + +static device_method_t cpld_methods[] = { + DEVMETHOD(device_probe, cpld_probe), + DEVMETHOD(device_attach, cpld_attach), + + DEVMETHOD_END +}; + +static driver_t cpld_driver = { + "cpld", + cpld_methods, + sizeof(struct cpld_softc) +}; + +static devclass_t cpld_devclass; +DRIVER_MODULE(cpld, lbc, cpld_driver, cpld_devclass, 0, 0); + +static void +cpld_write(struct cpld_softc *sc, int addr, int data) +{ + bus_write_2(sc->sc_mem, CPLD_MEM_ADDR, addr); + bus_write_2(sc->sc_mem, CPLD_MEM_DATA, data); +} + +static int +cpld_read(struct cpld_softc *sc, int addr) +{ + bus_write_2(sc->sc_mem, CPLD_MEM_ADDR, addr); + + return (bus_read_2(sc->sc_mem, CPLD_MEM_DATA)); +} + +static int +cpld_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "aeon,cyrus-cpld")) + return (ENXIO); + + device_set_desc(dev, "AmigaOne Cyrus CPLD"); + + return (BUS_PROBE_GENERIC); +} + +static int +cpld_attach(device_t dev) +{ + struct make_dev_args mda; + struct cpld_softc *sc; + int rid; + int date, time, tmp; + int err; + struct sysctl_ctx_list *ctx; + struct sysctl_oid *tree; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + rid = 0; + sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE|RF_SHAREABLE); + if (sc->sc_mem == NULL) { + device_printf(dev, "Unable to allocate memory resource.\n"); + return (ENXIO); + } + mtx_init(&sc->sc_mutex, "cpld", NULL, MTX_DEF); + date = (cpld_read(sc, CPLD_REG_DATE_UW) << 16) | + cpld_read(sc, CPLD_REG_DATE_LW); + time = (cpld_read(sc, CPLD_REG_TIME_UW) << 16) | + cpld_read(sc, CPLD_REG_TIME_LW); + + device_printf(dev, "Build date: %04x-%02x-%02x\n", (date >> 16) & 0xffff, + (date >> 8) & 0xff, date & 0xff); + device_printf(dev, "Build time: %02x:%02x:%02x\n", (time >> 16) & 0xff, + (time >> 8) & 0xff, time & 0xff); + + tmp = cpld_read(sc, CPLD_REG_HWREV); + device_printf(dev, "Hardware revision: %d\n", tmp); + + ctx = device_get_sysctl_ctx(dev); + tree = device_get_sysctl_tree(dev); + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "cpu_fan", CTLTYPE_INT | CTLFLAG_RD, sc, 0, + cpld_fan_sysctl, "I", "CPU Fan speed in RPM"); + + make_dev_args_init(&mda); + mda.mda_flags = MAKEDEV_CHECKNAME; + mda.mda_devsw = &cpld_cdevsw; + mda.mda_uid = UID_ROOT; + mda.mda_gid = GID_WHEEL; + mda.mda_mode = 0660; + mda.mda_si_drv1 = sc; + err = make_dev_s(&mda, &sc->sc_cdev, "cpld"); + if (err != 0) { + device_printf(dev, "Error creating character device: %d\n", err); + device_printf(dev, "Only sysctl interfaces will be available.\n"); + } + + return (0); +} + +static int +cpld_fan_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct cpld_softc *sc; + int error, old, rpm; + + sc = arg1; + mtx_lock(&sc->sc_mutex); + /* Read until we get some level of read stability. */ + rpm = cpld_read(sc, CPLD_REG_FAN_TACHO); + do { + old = rpm; + rpm = cpld_read(sc, CPLD_REG_FAN_TACHO); + } while (abs(rpm - old) > 10); + mtx_unlock(&sc->sc_mutex); + + /* Convert RPS->RPM. */ + rpm *= 60; + error = sysctl_handle_int(oidp, &rpm, 0, req); + + return (error); +} + +static int +cpld_open(struct cdev *dev, int flags, int fmt, struct thread *td) +{ + struct cpld_softc *sc = dev->si_drv1; + + if (sc->sc_isopen) + return (EBUSY); + sc->sc_isopen = 1; + return (0); +} + +static int +cpld_close(struct cdev *dev, int fflag, int devtype, struct thread *td) +{ + struct cpld_softc *sc = dev->si_drv1; + + sc->sc_isopen = 0; + return (0); +} + +static int +cpld_send(device_t dev, struct cpld_cmd_data *d) +{ + struct cpld_softc *sc; + uint16_t *word; + int i; + + if (d->cmd > USHRT_MAX) + return (EINVAL); + + sc = device_get_softc(dev); + + mtx_lock(&sc->sc_mutex); + for (i = 0, word = d->words; i < d->len; i++, word++) { + if (i == 0) + cpld_write(sc, CPLD_REG_RAM, *word); + else + bus_write_4(sc->sc_mem, CPLD_MEM_DATA, *word); + } + + cpld_write(sc, CPLD_REG_MBC2X, d->cmd); + mtx_unlock(&sc->sc_mutex); + + return (0); +} + +static int +cpld_recv(device_t dev, struct cpld_cmd_data *d) +{ + struct cpld_softc *sc; + uint16_t *word; + int i; + + sc = device_get_softc(dev); + + mtx_lock(&sc->sc_mutex); + d->cmd = cpld_read(sc, CPLD_REG_MBX2C); + + for (i = 0, word = d->words; i < d->len; i++, word++) { + if (i == 0) + *word = cpld_read(sc, CPLD_REG_RAM); + else + *word = bus_read_4(sc->sc_mem, CPLD_MEM_DATA); + } + mtx_unlock(&sc->sc_mutex); + + return (0); +} + +static int +cpld_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) +{ + struct cpld_softc *sc; + struct cpld_cmd_data *d; + void *xfer_data, *tmp; + int err; + + sc = dev->si_drv1; + + err = 0; + d = (struct cpld_cmd_data *)data; + if (d->len + d->offset > CPLD_MAX_DRAM_WORDS) { + return (EINVAL); + } + xfer_data = malloc(d->len * sizeof(uint16_t), M_TEMP, M_WAITOK); + + switch (cmd) { + case IOCCPLDSEND: + err = copyin(d->words, xfer_data, d->len * sizeof(uint16_t)); + d->words = xfer_data; + if (err == 0) + err = cpld_send(sc->sc_dev, d); + break; + case IOCCPLDRECV: + tmp = d->words; + d->words = xfer_data; + err = cpld_recv(sc->sc_dev, d); + d->words = tmp; + if (err == 0) + err = copyout(xfer_data, d->words, + d->len * sizeof(uint16_t)); + break; + default: + err = ENOTTY; + break; + } + free(xfer_data, M_TEMP); + + return (err); +} -- cgit v1.2.3