Path: blob/main/sys/powerpc/amigaone/cpld_x5000.c
108036 views
/*-1* Copyright (c) 2020 Justin Hibbits2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6*7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR14* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES15* 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, BUT18* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,19* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY20* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT21* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF22* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.23*/2425#include <sys/param.h>26#include <sys/systm.h>27#include <sys/conf.h>28#include <sys/kernel.h>29#include <sys/bus.h>30#include <sys/limits.h>31#include <sys/module.h>32#include <sys/malloc.h>33#include <sys/mutex.h>34#include <sys/rman.h>35#include <sys/sysctl.h>3637#include <machine/bus.h>3839#include <dev/ofw/ofw_bus.h>40#include <dev/ofw/ofw_bus_subr.h>4142#include "cpld.h"4344/*45* A driver for the AmigaOne X5000 "Cyrus+" CPLD.46*47* This is the interface between the CPU and the "Xena" (XMOS) chip. Since the48* XMOS is programmable via a SPI-attached flash memory, there's no direct49* driver written for the Xena attachment. Instead, a userspace process would50* communicate with the Xena by issuing ioctl()s to this CPLD.51*/5253/* Resource access addresses. */54#define CPLD_MEM_ADDR 0x000055#define CPLD_MEM_DATA 0x80005657#define CPLD_MAX_DRAM_WORDS 0x8005859/* CPLD Registers. */60#define CPLD_REG_SIG1 0x0061#define CPLD_REG_SIG2 0x0162#define CPLD_REG_HWREV 0x0263#define CPLD_REG_MBC2X 0x0564#define CPLD_REG_MBX2C 0x0665#define CPLD_REG_XDEBUG 0x0c66#define CPLD_REG_XJTAG 0x0d67#define CPLD_REG_FAN_TACHO 0x1068#define CPLD_REG_DATE_LW 0x2169#define CPLD_REG_DATE_UW 0x2270#define CPLD_REG_TIME_LW 0x2371#define CPLD_REG_TIME_UW 0x2472#define CPLD_REG_SCR1 0x3073#define CPLD_REG_SCR2 0x3174#define CPLD_REG_RAM 0x80007576struct cpld_softc {77device_t sc_dev;78struct resource *sc_mem;79struct cdev *sc_cdev;80struct mtx sc_mutex;81bool sc_isopen;82};8384static d_open_t cpld_open;85static d_close_t cpld_close;86static d_ioctl_t cpld_ioctl;8788static struct cdevsw cpld_cdevsw = {89.d_version = D_VERSION,90.d_open = cpld_open,91.d_close = cpld_close,92.d_ioctl = cpld_ioctl,93.d_name = "nvram",94};9596static device_probe_t cpld_probe;97static device_attach_t cpld_attach;98static int cpld_fan_sysctl(SYSCTL_HANDLER_ARGS);99100static device_method_t cpld_methods[] = {101DEVMETHOD(device_probe, cpld_probe),102DEVMETHOD(device_attach, cpld_attach),103104DEVMETHOD_END105};106107static driver_t cpld_driver = {108"cpld",109cpld_methods,110sizeof(struct cpld_softc)111};112113DRIVER_MODULE(cpld, lbc, cpld_driver, 0, 0);114115static void116cpld_write(struct cpld_softc *sc, int addr, int data)117{118bus_write_2(sc->sc_mem, CPLD_MEM_ADDR, addr);119bus_write_2(sc->sc_mem, CPLD_MEM_DATA, data);120}121122static int123cpld_read(struct cpld_softc *sc, int addr)124{125bus_write_2(sc->sc_mem, CPLD_MEM_ADDR, addr);126127return (bus_read_2(sc->sc_mem, CPLD_MEM_DATA));128}129130static int131cpld_probe(device_t dev)132{133if (!ofw_bus_is_compatible(dev, "aeon,cyrus-cpld"))134return (ENXIO);135136device_set_desc(dev, "AmigaOne Cyrus CPLD");137138return (BUS_PROBE_GENERIC);139}140141static int142cpld_attach(device_t dev)143{144struct make_dev_args mda;145struct cpld_softc *sc;146int rid;147int date, time, tmp;148int err;149struct sysctl_ctx_list *ctx;150struct sysctl_oid *tree;151152sc = device_get_softc(dev);153sc->sc_dev = dev;154155rid = 0;156sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,157RF_ACTIVE|RF_SHAREABLE);158if (sc->sc_mem == NULL) {159device_printf(dev, "Unable to allocate memory resource.\n");160return (ENXIO);161}162mtx_init(&sc->sc_mutex, "cpld", NULL, MTX_DEF);163if (bootverbose) {164date = (cpld_read(sc, CPLD_REG_DATE_UW) << 16) |165cpld_read(sc, CPLD_REG_DATE_LW);166time = (cpld_read(sc, CPLD_REG_TIME_UW) << 16) |167cpld_read(sc, CPLD_REG_TIME_LW);168169device_printf(dev, "Build date: %04x-%02x-%02x\n",170(date >> 16) & 0xffff, (date >> 8) & 0xff, date & 0xff);171device_printf(dev, "Build time: %02x:%02x:%02x\n",172(time >> 16) & 0xff, (time >> 8) & 0xff, time & 0xff);173}174175tmp = cpld_read(sc, CPLD_REG_HWREV);176device_printf(dev, "Hardware revision: %d\n", tmp);177178ctx = device_get_sysctl_ctx(dev);179tree = device_get_sysctl_tree(dev);180181SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,182"cpu_fan", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,183cpld_fan_sysctl, "I", "CPU Fan speed in RPM");184185make_dev_args_init(&mda);186mda.mda_flags = MAKEDEV_CHECKNAME;187mda.mda_devsw = &cpld_cdevsw;188mda.mda_uid = UID_ROOT;189mda.mda_gid = GID_WHEEL;190mda.mda_mode = 0660;191mda.mda_si_drv1 = sc;192err = make_dev_s(&mda, &sc->sc_cdev, "cpld");193if (err != 0) {194device_printf(dev, "Error creating character device: %d\n", err);195device_printf(dev, "Only sysctl interfaces will be available.\n");196}197198return (0);199}200201static int202cpld_fan_sysctl(SYSCTL_HANDLER_ARGS)203{204struct cpld_softc *sc;205int error, old, rpm;206207sc = arg1;208mtx_lock(&sc->sc_mutex);209/* Read until we get some level of read stability. */210rpm = cpld_read(sc, CPLD_REG_FAN_TACHO);211do {212old = rpm;213rpm = cpld_read(sc, CPLD_REG_FAN_TACHO);214} while (abs(rpm - old) > 10);215mtx_unlock(&sc->sc_mutex);216217/* Convert RPS->RPM. */218rpm *= 60;219error = sysctl_handle_int(oidp, &rpm, 0, req);220221return (error);222}223224static int225cpld_open(struct cdev *dev, int flags, int fmt, struct thread *td)226{227struct cpld_softc *sc = dev->si_drv1;228229if (sc->sc_isopen)230return (EBUSY);231sc->sc_isopen = 1;232return (0);233}234235static int236cpld_close(struct cdev *dev, int fflag, int devtype, struct thread *td)237{238struct cpld_softc *sc = dev->si_drv1;239240sc->sc_isopen = 0;241return (0);242}243244static int245cpld_send(device_t dev, struct cpld_cmd_data *d)246{247struct cpld_softc *sc;248uint16_t *word;249int i;250251if (d->cmd > USHRT_MAX)252return (EINVAL);253254sc = device_get_softc(dev);255256mtx_lock(&sc->sc_mutex);257for (i = 0, word = d->words; i < d->len; i++, word++) {258if (i == 0)259cpld_write(sc, CPLD_REG_RAM, *word);260else261bus_write_4(sc->sc_mem, CPLD_MEM_DATA, *word);262}263264cpld_write(sc, CPLD_REG_MBC2X, d->cmd);265mtx_unlock(&sc->sc_mutex);266267return (0);268}269270static int271cpld_recv(device_t dev, struct cpld_cmd_data *d)272{273struct cpld_softc *sc;274uint16_t *word;275int i;276277sc = device_get_softc(dev);278279mtx_lock(&sc->sc_mutex);280d->cmd = cpld_read(sc, CPLD_REG_MBX2C);281282for (i = 0, word = d->words; i < d->len; i++, word++) {283if (i == 0)284*word = cpld_read(sc, CPLD_REG_RAM);285else286*word = bus_read_4(sc->sc_mem, CPLD_MEM_DATA);287}288mtx_unlock(&sc->sc_mutex);289290return (0);291}292293static int294cpld_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)295{296struct cpld_softc *sc;297struct cpld_cmd_data *d;298void *xfer_data, *tmp;299int err;300301sc = dev->si_drv1;302303err = 0;304d = (struct cpld_cmd_data *)data;305if (d->len + d->offset > CPLD_MAX_DRAM_WORDS) {306return (EINVAL);307}308xfer_data = malloc(d->len * sizeof(uint16_t), M_TEMP, M_WAITOK);309310switch (cmd) {311case IOCCPLDSEND:312err = copyin(d->words, xfer_data, d->len * sizeof(uint16_t));313d->words = xfer_data;314if (err == 0)315err = cpld_send(sc->sc_dev, d);316break;317case IOCCPLDRECV:318tmp = d->words;319d->words = xfer_data;320err = cpld_recv(sc->sc_dev, d);321d->words = tmp;322if (err == 0)323err = copyout(xfer_data, d->words,324d->len * sizeof(uint16_t));325break;326default:327err = ENOTTY;328break;329}330free(xfer_data, M_TEMP);331332return (err);333}334335336