The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/powerpc/amigaone/cpld_a1222.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   24  */
   25 
   26 #include <sys/cdefs.h>
   27 __FBSDID("$FreeBSD$");
   28 
   29 #include <sys/param.h>
   30 #include <sys/systm.h>
   31 #include <sys/conf.h>
   32 #include <sys/kernel.h>
   33 #include <sys/bus.h>
   34 #include <sys/limits.h>
   35 #include <sys/module.h>
   36 #include <sys/malloc.h>
   37 #include <sys/mutex.h>
   38 #include <sys/rman.h>
   39 #include <sys/sysctl.h>
   40 
   41 #include <machine/bus.h>
   42 
   43 #include <dev/ofw/ofw_bus.h>
   44 #include <dev/ofw/ofw_bus_subr.h>
   45 
   46 #include <sys/kdb.h>
   47 
   48 #include "cpld.h"
   49 
   50 /*
   51  * A driver for the AmigaOne A1222 "Tabor" Main CPLD.
   52  *
   53  * The main CPLD is the interface between the CPU and the GPIO CPLD.
   54  * Communication with the GPIO CPLD is over the main CPLD's mailbox interface,
   55  * along with the dual-port RAM on the CPLD.
   56  *
   57  * Only one process can open the CPLD character device at a time.  The driver
   58  * enforces this to simplify the communication protocol.
   59  */
   60 
   61 /* Resource access addresses. */
   62 #define CPLD_MEM_ADDR_H         0x00
   63 #define CPLD_MEM_ADDR_L         0x01
   64 #define CPLD_MEM_DATA           0x80
   65 
   66 #define CPLD_MAX_DRAM_WORDS     0x800
   67 
   68 /* CPLD Registers. */
   69 #define CPLD_REG_SIG1           0x00
   70 #define CPLD_REG_SIG2           0x01
   71 #define CPLD_REG_HWREV          0x02
   72 #define CPLD_REG_CPLDREV        0x03
   73 #define CPLD_REG_MBC2X          0x04
   74 #define CPLD_REG_MBX2C          0x05
   75 #define CPLD_REG_FAN1_TACHO_U   0x10
   76 #define CPLD_REG_FAN1_TACHO_L   0x11
   77 #define CPLD_REG_FAN2_TACHO_U   0x12
   78 #define CPLD_REG_FAN2_TACHO_L   0x13
   79 #define CPLD_REG_FAN3_TACHO_U   0x14
   80 #define CPLD_REG_FAN3_TACHO_L   0x15
   81 #define CPLD_REG_DATE_UU        0x20
   82 #define CPLD_REG_DATE_UL        0x21
   83 #define CPLD_REG_DATE_LU        0x22
   84 #define CPLD_REG_DATE_LL        0x23
   85 #define CPLD_REG_TIME_UU        0x24
   86 #define CPLD_REG_TIME_UL        0x25
   87 #define CPLD_REG_TIME_LU        0x26
   88 #define CPLD_REG_TIME_LL        0x27
   89 #define CPLD_REG_SCR1           0x5c
   90 #define CPLD_REG_SCR2           0x6a
   91 #define CPLD_REG_RAM            0x80
   92 
   93 struct cpld_softc {
   94         device_t         sc_dev;
   95         struct resource *sc_mem;
   96         struct cdev     *sc_cdev;
   97         struct mtx       sc_mutex;
   98         bool             sc_isopen;
   99 };
  100 
  101 static d_open_t         cpld_open;
  102 static d_close_t        cpld_close;
  103 static d_ioctl_t        cpld_ioctl;
  104 
  105 static struct cdevsw cpld_cdevsw = {
  106         .d_version =    D_VERSION,
  107         .d_open =       cpld_open,
  108         .d_close =      cpld_close,
  109         .d_ioctl =      cpld_ioctl,
  110         .d_name =       "nvram",
  111 };
  112 
  113 static device_probe_t   cpld_probe;
  114 static device_attach_t  cpld_attach;
  115 static int              cpld_fan_sysctl(SYSCTL_HANDLER_ARGS);
  116 
  117 static device_method_t cpld_methods[] = {
  118         DEVMETHOD(device_probe,         cpld_probe),
  119         DEVMETHOD(device_attach,        cpld_attach),
  120 
  121         DEVMETHOD_END
  122 };
  123 
  124 static driver_t cpld_driver = {
  125         "cpld",
  126         cpld_methods,
  127         sizeof(struct cpld_softc)
  128 };
  129 
  130 DRIVER_MODULE(cpld, lbc, cpld_driver, 0, 0);
  131 
  132 static void
  133 cpld_write(struct cpld_softc *sc, int addr, int data)
  134 {
  135         if (addr >= CPLD_REG_RAM) {
  136                 bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_H, addr);
  137                 bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_L, addr);
  138         } else
  139                 bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_H, addr);
  140         bus_write_1(sc->sc_mem, CPLD_MEM_DATA, data);
  141 }
  142 
  143 static int
  144 cpld_read(struct cpld_softc *sc, int addr)
  145 {
  146         if (addr >= CPLD_REG_RAM) {
  147                 bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_H, addr);
  148                 bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_L, addr);
  149         } else
  150                 bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_H, addr);
  151 
  152         return (bus_read_1(sc->sc_mem, CPLD_MEM_DATA));
  153 }
  154 
  155 /*
  156  * This is only to read a register that's split into two 8-bit registers.
  157  * Dual-port RAM is not accepted for this purpose.
  158  */
  159 static int
  160 cpld_read_pair(struct cpld_softc *sc, int addr)
  161 {
  162         int tmp;
  163 
  164         KASSERT(addr <= 0xff, ("Invalid register-pair base address %x.", addr));
  165         bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_H, addr);
  166         tmp = bus_read_1(sc->sc_mem, CPLD_MEM_DATA) << 8;
  167 
  168         bus_write_1(sc->sc_mem, CPLD_MEM_ADDR_H, addr + 1);
  169         tmp |= bus_read_1(sc->sc_mem, CPLD_MEM_DATA);
  170 
  171         return (tmp);
  172 }
  173 
  174 static int
  175 cpld_probe(device_t dev)
  176 {
  177         if (!ofw_bus_is_compatible(dev, "aeon,tabor-cpld"))
  178                 return (ENXIO);
  179 
  180         device_set_desc(dev, "AmigaOne Tabor CPLD");
  181 
  182         return (BUS_PROBE_GENERIC);
  183 }
  184 
  185 static int
  186 cpld_attach(device_t dev)
  187 {
  188         struct make_dev_args mda;
  189         struct cpld_softc *sc;
  190         int rid;
  191         int date, time, tmp;
  192         int err;
  193         struct sysctl_ctx_list *ctx;
  194         struct sysctl_oid *tree;
  195 
  196         sc = device_get_softc(dev);
  197         sc->sc_dev = dev;
  198 
  199         rid = 0;
  200         sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
  201             RF_ACTIVE|RF_SHAREABLE);
  202         if (sc->sc_mem == NULL) {
  203                 device_printf(dev, "Unable to allocate memory resource.\n");
  204                 return (ENXIO);
  205         }
  206         mtx_init(&sc->sc_mutex, "cpld", NULL, MTX_DEF);
  207 
  208         if (bootverbose) {
  209                 date = (cpld_read_pair(sc, CPLD_REG_DATE_UU) << 16) |
  210                     cpld_read_pair(sc, CPLD_REG_DATE_LU);
  211                 time = (cpld_read_pair(sc, CPLD_REG_TIME_UU) << 16) |
  212                     cpld_read_pair(sc, CPLD_REG_TIME_LU);
  213 
  214                 device_printf(dev, "Build date: %04x-%02x-%02x\n",
  215                     (date >> 16) & 0xffff, (date >> 8) & 0xff, date & 0xff);
  216 #if 0
  217                 /* Build time is nonsense on tested system. */
  218                 device_printf(dev, "Build time: %02x:%02x:%02x\n",
  219                     (time >> 16) & 0xff, (time >> 8) & 0xff, time & 0xff);
  220 #endif
  221         }
  222 
  223         tmp = cpld_read(sc, CPLD_REG_HWREV);
  224         device_printf(dev, "Hardware revision: %d\n", tmp);
  225 
  226         ctx = device_get_sysctl_ctx(dev);
  227         tree = device_get_sysctl_tree(dev);
  228 
  229         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
  230             "cpu_fan", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
  231             CPLD_REG_FAN1_TACHO_U, cpld_fan_sysctl, "I",
  232             "CPU Fan speed in RPM");
  233 
  234         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
  235             "case_1_fan", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
  236             CPLD_REG_FAN2_TACHO_U, cpld_fan_sysctl, "I",
  237             "Case fan 1 speed in RPM");
  238 
  239         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
  240             "case_2_fan", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,
  241             CPLD_REG_FAN3_TACHO_U, cpld_fan_sysctl, "I",
  242             "Case fan 2 speed in RPM");
  243 
  244         make_dev_args_init(&mda);
  245         mda.mda_flags =  MAKEDEV_CHECKNAME;
  246         mda.mda_devsw = &cpld_cdevsw;
  247         mda.mda_uid = UID_ROOT;
  248         mda.mda_gid = GID_WHEEL;
  249         mda.mda_mode = 0660;
  250         err = make_dev_s(&mda, &sc->sc_cdev, "cpld");
  251         if (err != 0) {
  252                 device_printf(dev, "Error creating character device: %d\n", err);
  253                 device_printf(dev, "Only sysctl interfaces will be available.\n");
  254         }
  255 
  256         return (0);
  257 }
  258 
  259 static int
  260 cpld_fan_sysctl(SYSCTL_HANDLER_ARGS)
  261 {
  262         struct cpld_softc *sc;
  263         int error, old, rpm;
  264         int fan_reg;
  265 
  266         sc = arg1;
  267         fan_reg = arg2;
  268         mtx_lock(&sc->sc_mutex);
  269         /* Read until we get some level of read stability. */
  270         rpm = cpld_read(sc, fan_reg);
  271         do {
  272                 old = rpm;
  273                 rpm = cpld_read_pair(sc, fan_reg);
  274         } while (abs(rpm - old) > 10);
  275         mtx_unlock(&sc->sc_mutex);
  276 
  277         /* Convert RPS->RPM. */
  278         rpm *= 60;
  279         error = sysctl_handle_int(oidp, &rpm, 0, req);
  280 
  281         return (error);
  282 }
  283 
  284 static int
  285 cpld_open(struct cdev *dev, int flags, int fmt, struct thread *td)
  286 {
  287         struct cpld_softc *sc = dev->si_drv1;
  288 
  289         if (sc->sc_isopen)
  290                 return (EBUSY);
  291         sc->sc_isopen = 1;
  292         return (0);
  293 }
  294 
  295 static int
  296 cpld_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
  297 {
  298         struct cpld_softc *sc = dev->si_drv1;
  299 
  300         sc->sc_isopen = 0;
  301         return (0);
  302 }
  303 
  304 /*
  305  * Send a command over the CPLD to the other side.
  306  *
  307  * This will first copy the data into the dual-port RAM, then signal the other
  308  * side by writing to the mailbox.
  309  */
  310 static int
  311 cpld_send(device_t dev, struct cpld_cmd_data *d)
  312 {
  313         struct cpld_softc *sc;
  314         uint16_t *word;
  315         int i;
  316 
  317         if (d->cmd > USHRT_MAX)
  318                 return (EINVAL);
  319 
  320         sc = device_get_softc(dev);
  321 
  322         mtx_lock(&sc->sc_mutex);
  323         for (i = 0, word = d->words; i < d->len; i++, word++) {
  324                 if (i == 0)
  325                         cpld_write(sc, CPLD_REG_RAM + d->offset, *word);
  326                 else
  327                         bus_write_4(sc->sc_mem, CPLD_MEM_DATA, *word);
  328         }
  329 
  330         cpld_write(sc, CPLD_REG_MBC2X, d->cmd);
  331         mtx_unlock(&sc->sc_mutex);
  332 
  333         return (0);
  334 }
  335 
  336 static int
  337 cpld_recv(device_t dev, struct cpld_cmd_data *d)
  338 {
  339         struct cpld_softc *sc;
  340         uint16_t *word;
  341         int i;
  342 
  343         sc = device_get_softc(dev);
  344 
  345         mtx_lock(&sc->sc_mutex);
  346         d->cmd = cpld_read(sc, CPLD_REG_MBX2C);
  347 
  348         for (i = 0, word = d->words; i < d->len; i++, word++) {
  349                 if (i == 0)
  350                         *word = cpld_read(sc, CPLD_REG_RAM + d->offset);
  351                 else
  352                         *word = bus_read_4(sc->sc_mem, CPLD_MEM_DATA);
  353         }
  354         mtx_unlock(&sc->sc_mutex);
  355 
  356         return (0);
  357 }
  358 
  359 static int
  360 cpld_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
  361     struct thread *td)
  362 {
  363         struct cpld_softc *sc;
  364         struct cpld_cmd_data *d;
  365         void *xfer_data, *tmp;
  366         int err;
  367 
  368         sc = dev->si_drv1;
  369 
  370         err = 0;
  371         d = (struct cpld_cmd_data *)data;
  372         if (d->len + d->offset > CPLD_MAX_DRAM_WORDS) {
  373                 return (EINVAL);
  374         }
  375         xfer_data = malloc(d->len * sizeof(uint16_t), M_TEMP, M_WAITOK);
  376 
  377         switch (cmd) {
  378         case IOCCPLDSEND:
  379                 err = copyin(d->words, xfer_data, d->len * sizeof(uint16_t));
  380                 d->words = xfer_data;
  381                 if (err == 0)
  382                         err = cpld_send(sc->sc_dev, d);
  383                 break;
  384         case IOCCPLDRECV:
  385                 tmp = d->words;
  386                 d->words = xfer_data;
  387                 err = cpld_recv(sc->sc_dev, d);
  388                 d->words = tmp;
  389                 if (err == 0)
  390                         err = copyout(xfer_data, d->words,
  391                             d->len * sizeof(uint16_t));
  392                 break;
  393         default:
  394                 err = ENOTTY;
  395                 break;
  396         }
  397         free(xfer_data, M_TEMP);
  398 
  399         return (err);
  400 }

Cache object: bde069cc62435c43b7dac5d6820a594b


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.