Path: blob/main/sys/powerpc/amigaone/cpld_a1222.c
108009 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 <sys/kdb.h>4344#include "cpld.h"4546/*47* A driver for the AmigaOne A1222 "Tabor" Main CPLD.48*49* The main CPLD is the interface between the CPU and the GPIO CPLD.50* Communication with the GPIO CPLD is over the main CPLD's mailbox interface,51* along with the dual-port RAM on the CPLD.52*53* Only one process can open the CPLD character device at a time. The driver54* enforces this to simplify the communication protocol.55*/5657/* Resource access addresses. */58#define CPLD_MEM_ADDR_H 0x0059#define CPLD_MEM_ADDR_L 0x0160#define CPLD_MEM_DATA 0x806162#define CPLD_MAX_DRAM_WORDS 0x8006364/* CPLD Registers. */65#define CPLD_REG_SIG1 0x0066#define CPLD_REG_SIG2 0x0167#define CPLD_REG_HWREV 0x0268#define CPLD_REG_CPLDREV 0x0369#define CPLD_REG_MBC2X 0x0470#define CPLD_REG_MBX2C 0x0571#define CPLD_REG_FAN1_TACHO_U 0x1072#define CPLD_REG_FAN1_TACHO_L 0x1173#define CPLD_REG_FAN2_TACHO_U 0x1274#define CPLD_REG_FAN2_TACHO_L 0x1375#define CPLD_REG_FAN3_TACHO_U 0x1476#define CPLD_REG_FAN3_TACHO_L 0x1577#define CPLD_REG_DATE_UU 0x2078#define CPLD_REG_DATE_UL 0x2179#define CPLD_REG_DATE_LU 0x2280#define CPLD_REG_DATE_LL 0x2381#define CPLD_REG_TIME_UU 0x2482#define CPLD_REG_TIME_UL 0x2583#define CPLD_REG_TIME_LU 0x2684#define CPLD_REG_TIME_LL 0x2785#define CPLD_REG_SCR1 0x5c86#define CPLD_REG_SCR2 0x6a87#define CPLD_REG_RAM 0x808889struct cpld_softc {90device_t sc_dev;91struct resource *sc_mem;92struct cdev *sc_cdev;93struct mtx sc_mutex;94bool sc_isopen;95};9697static d_open_t cpld_open;98static d_close_t cpld_close;99static d_ioctl_t cpld_ioctl;100101static struct cdevsw cpld_cdevsw = {102.d_version = D_VERSION,103.d_open = cpld_open,104.d_close = cpld_close,105.d_ioctl = cpld_ioctl,106.d_name = "nvram",107};108109static device_probe_t cpld_probe;110static device_attach_t cpld_attach;111static int cpld_fan_sysctl(SYSCTL_HANDLER_ARGS);112113static device_method_t cpld_methods[] = {114DEVMETHOD(device_probe, cpld_probe),115DEVMETHOD(device_attach, cpld_attach),116117DEVMETHOD_END118};119120static driver_t cpld_driver = {121"cpld",122cpld_methods,123sizeof(struct cpld_softc)124};125126DRIVER_MODULE(cpld, lbc, cpld_driver, 0, 0);127128static void129cpld_write(struct cpld_softc *sc, int addr, int data)130{131if (addr >= CPLD_REG_RAM) {132bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_H, addr);133bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_L, addr);134} else135bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_H, addr);136bus_write_1(sc->sc_mem, CPLD_MEM_DATA, data);137}138139static int140cpld_read(struct cpld_softc *sc, int addr)141{142if (addr >= CPLD_REG_RAM) {143bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_H, addr);144bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_L, addr);145} else146bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_H, addr);147148return (bus_read_1(sc->sc_mem, CPLD_MEM_DATA));149}150151/*152* This is only to read a register that's split into two 8-bit registers.153* Dual-port RAM is not accepted for this purpose.154*/155static int156cpld_read_pair(struct cpld_softc *sc, int addr)157{158int tmp;159160KASSERT(addr <= 0xff, ("Invalid register-pair base address %x.", addr));161bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_H, addr);162tmp = bus_read_1(sc->sc_mem, CPLD_MEM_DATA) << 8;163164bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_H, addr + 1);165tmp |= bus_read_1(sc->sc_mem, CPLD_MEM_DATA);166167return (tmp);168}169170static int171cpld_probe(device_t dev)172{173if (!ofw_bus_is_compatible(dev, "aeon,tabor-cpld"))174return (ENXIO);175176device_set_desc(dev, "AmigaOne Tabor CPLD");177178return (BUS_PROBE_GENERIC);179}180181static int182cpld_attach(device_t dev)183{184struct make_dev_args mda;185struct cpld_softc *sc;186int rid;187int date, time, tmp;188int err;189struct sysctl_ctx_list *ctx;190struct sysctl_oid *tree;191192sc = device_get_softc(dev);193sc->sc_dev = dev;194195rid = 0;196sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,197RF_ACTIVE|RF_SHAREABLE);198if (sc->sc_mem == NULL) {199device_printf(dev, "Unable to allocate memory resource.\n");200return (ENXIO);201}202mtx_init(&sc->sc_mutex, "cpld", NULL, MTX_DEF);203204if (bootverbose) {205date = (cpld_read_pair(sc, CPLD_REG_DATE_UU) << 16) |206cpld_read_pair(sc, CPLD_REG_DATE_LU);207time = (cpld_read_pair(sc, CPLD_REG_TIME_UU) << 16) |208cpld_read_pair(sc, CPLD_REG_TIME_LU);209210device_printf(dev, "Build date: %04x-%02x-%02x\n",211(date >> 16) & 0xffff, (date >> 8) & 0xff, date & 0xff);212#if 0213/* Build time is nonsense on tested system. */214device_printf(dev, "Build time: %02x:%02x:%02x\n",215(time >> 16) & 0xff, (time >> 8) & 0xff, time & 0xff);216#endif217}218219tmp = cpld_read(sc, CPLD_REG_HWREV);220device_printf(dev, "Hardware revision: %d\n", tmp);221222ctx = device_get_sysctl_ctx(dev);223tree = device_get_sysctl_tree(dev);224225SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,226"cpu_fan", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,227CPLD_REG_FAN1_TACHO_U, cpld_fan_sysctl, "I",228"CPU Fan speed in RPM");229230SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,231"case_1_fan", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,232CPLD_REG_FAN2_TACHO_U, cpld_fan_sysctl, "I",233"Case fan 1 speed in RPM");234235SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,236"case_2_fan", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,237CPLD_REG_FAN3_TACHO_U, cpld_fan_sysctl, "I",238"Case fan 2 speed in RPM");239240make_dev_args_init(&mda);241mda.mda_flags = MAKEDEV_CHECKNAME;242mda.mda_devsw = &cpld_cdevsw;243mda.mda_uid = UID_ROOT;244mda.mda_gid = GID_WHEEL;245mda.mda_mode = 0660;246err = make_dev_s(&mda, &sc->sc_cdev, "cpld");247if (err != 0) {248device_printf(dev, "Error creating character device: %d\n", err);249device_printf(dev, "Only sysctl interfaces will be available.\n");250}251252return (0);253}254255static int256cpld_fan_sysctl(SYSCTL_HANDLER_ARGS)257{258struct cpld_softc *sc;259int error, old, rpm;260int fan_reg;261262sc = arg1;263fan_reg = arg2;264mtx_lock(&sc->sc_mutex);265/* Read until we get some level of read stability. */266rpm = cpld_read(sc, fan_reg);267do {268old = rpm;269rpm = cpld_read_pair(sc, fan_reg);270} while (abs(rpm - old) > 10);271mtx_unlock(&sc->sc_mutex);272273/* Convert RPS->RPM. */274rpm *= 60;275error = sysctl_handle_int(oidp, &rpm, 0, req);276277return (error);278}279280static int281cpld_open(struct cdev *dev, int flags, int fmt, struct thread *td)282{283struct cpld_softc *sc = dev->si_drv1;284285if (sc->sc_isopen)286return (EBUSY);287sc->sc_isopen = 1;288return (0);289}290291static int292cpld_close(struct cdev *dev, int fflag, int devtype, struct thread *td)293{294struct cpld_softc *sc = dev->si_drv1;295296sc->sc_isopen = 0;297return (0);298}299300/*301* Send a command over the CPLD to the other side.302*303* This will first copy the data into the dual-port RAM, then signal the other304* side by writing to the mailbox.305*/306static int307cpld_send(device_t dev, struct cpld_cmd_data *d)308{309struct cpld_softc *sc;310uint16_t *word;311int i;312313if (d->cmd > USHRT_MAX)314return (EINVAL);315316sc = device_get_softc(dev);317318mtx_lock(&sc->sc_mutex);319for (i = 0, word = d->words; i < d->len; i++, word++) {320if (i == 0)321cpld_write(sc, CPLD_REG_RAM + d->offset, *word);322else323bus_write_4(sc->sc_mem, CPLD_MEM_DATA, *word);324}325326cpld_write(sc, CPLD_REG_MBC2X, d->cmd);327mtx_unlock(&sc->sc_mutex);328329return (0);330}331332static int333cpld_recv(device_t dev, struct cpld_cmd_data *d)334{335struct cpld_softc *sc;336uint16_t *word;337int i;338339sc = device_get_softc(dev);340341mtx_lock(&sc->sc_mutex);342d->cmd = cpld_read(sc, CPLD_REG_MBX2C);343344for (i = 0, word = d->words; i < d->len; i++, word++) {345if (i == 0)346*word = cpld_read(sc, CPLD_REG_RAM + d->offset);347else348*word = bus_read_4(sc->sc_mem, CPLD_MEM_DATA);349}350mtx_unlock(&sc->sc_mutex);351352return (0);353}354355static int356cpld_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,357struct thread *td)358{359struct cpld_softc *sc;360struct cpld_cmd_data *d;361void *xfer_data, *tmp;362int err;363364sc = dev->si_drv1;365366err = 0;367d = (struct cpld_cmd_data *)data;368if (d->len + d->offset > CPLD_MAX_DRAM_WORDS) {369return (EINVAL);370}371xfer_data = malloc(d->len * sizeof(uint16_t), M_TEMP, M_WAITOK);372373switch (cmd) {374case IOCCPLDSEND:375err = copyin(d->words, xfer_data, d->len * sizeof(uint16_t));376d->words = xfer_data;377if (err == 0)378err = cpld_send(sc->sc_dev, d);379break;380case IOCCPLDRECV:381tmp = d->words;382d->words = xfer_data;383err = cpld_recv(sc->sc_dev, d);384d->words = tmp;385if (err == 0)386err = copyout(xfer_data, d->words,387d->len * sizeof(uint16_t));388break;389default:390err = ENOTTY;391break;392}393free(xfer_data, M_TEMP);394395return (err);396}397398399