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/dev/powermac_nvram/powermac_nvram.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2006 Maxim Sobolev <sobomax@FreeBSD.org>
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
   20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
   24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   25  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   26  * POSSIBILITY OF SUCH DAMAGE.
   27  *
   28  * $FreeBSD$
   29  */
   30 
   31 #include <sys/param.h>
   32 #include <sys/systm.h>
   33 #include <sys/module.h>
   34 #include <sys/bus.h>
   35 #include <sys/conf.h>
   36 #include <sys/kernel.h>
   37 #include <sys/lock.h>
   38 #include <sys/sx.h>
   39 #include <sys/uio.h>
   40 
   41 #include <dev/ofw/openfirm.h>
   42 #include <dev/ofw/ofw_bus.h>
   43 #include <dev/ofw/ofw_bus_subr.h>
   44 
   45 #include <machine/bus.h>
   46 #include <machine/md_var.h>
   47 #include <machine/pio.h>
   48 #include <machine/resource.h>
   49 
   50 #include <sys/rman.h>
   51 
   52 #include <dev/powermac_nvram/powermac_nvramvar.h>
   53 
   54 #include <vm/vm.h>
   55 #include <vm/pmap.h>
   56 
   57 /*
   58  * Device interface.
   59  */
   60 static int              powermac_nvram_probe(device_t);
   61 static int              powermac_nvram_attach(device_t);
   62 static int              powermac_nvram_detach(device_t);
   63 
   64 /* Helper functions */
   65 static int              powermac_nvram_check(void *data);
   66 static int              chrp_checksum(int sum, uint8_t *, uint8_t *);
   67 static uint32_t         adler_checksum(uint8_t *, int);
   68 static int              erase_bank(device_t, uint8_t *);
   69 static int              write_bank(device_t, uint8_t *, uint8_t *);
   70 
   71 /*
   72  * Driver methods.
   73  */
   74 static device_method_t  powermac_nvram_methods[] = {
   75         /* Device interface */
   76         DEVMETHOD(device_probe,         powermac_nvram_probe),
   77         DEVMETHOD(device_attach,        powermac_nvram_attach),
   78         DEVMETHOD(device_detach,        powermac_nvram_detach),
   79         { 0, 0 }
   80 };
   81 
   82 static driver_t powermac_nvram_driver = {
   83         "powermac_nvram",
   84         powermac_nvram_methods,
   85         sizeof(struct powermac_nvram_softc)
   86 };
   87 
   88 DRIVER_MODULE(powermac_nvram, ofwbus, powermac_nvram_driver, 0, 0);
   89 
   90 /*
   91  * Cdev methods.
   92  */
   93 
   94 static  d_open_t        powermac_nvram_open;
   95 static  d_close_t       powermac_nvram_close;
   96 static  d_read_t        powermac_nvram_read;
   97 static  d_write_t       powermac_nvram_write;
   98 
   99 static struct cdevsw powermac_nvram_cdevsw = {
  100         .d_version =    D_VERSION,
  101         .d_open =       powermac_nvram_open,
  102         .d_close =      powermac_nvram_close,
  103         .d_read =       powermac_nvram_read,
  104         .d_write =      powermac_nvram_write,
  105         .d_name =       "powermac_nvram",
  106 };
  107 
  108 static int
  109 powermac_nvram_probe(device_t dev)
  110 {
  111         const char      *type, *compatible;
  112 
  113         type = ofw_bus_get_type(dev);
  114         compatible = ofw_bus_get_compat(dev);
  115 
  116         if (type == NULL || compatible == NULL)
  117                 return ENXIO;
  118 
  119         if (strcmp(type, "nvram") != 0)
  120                 return ENXIO;
  121         if (strcmp(compatible, "amd-0137") != 0 &&
  122             !ofw_bus_is_compatible(dev, "nvram,flash"))
  123                 return ENXIO;
  124 
  125         device_set_desc(dev, "Apple NVRAM");
  126         return 0;
  127 }
  128 
  129 static int
  130 powermac_nvram_attach(device_t dev)
  131 {
  132         struct powermac_nvram_softc *sc;
  133         const char      *compatible;
  134         phandle_t node;
  135         u_int32_t reg[3];
  136         int gen0, gen1, i;
  137 
  138         node = ofw_bus_get_node(dev);
  139         sc = device_get_softc(dev);
  140 
  141         if ((i = OF_getprop(node, "reg", reg, sizeof(reg))) < 8)
  142                 return ENXIO;
  143 
  144         sc->sc_dev = dev;
  145         sc->sc_node = node;
  146 
  147         compatible = ofw_bus_get_compat(dev);
  148         if (strcmp(compatible, "amd-0137") == 0)
  149                 sc->sc_type = FLASH_TYPE_AMD;
  150         else
  151                 sc->sc_type = FLASH_TYPE_SM;
  152 
  153         /*
  154          * Find which byte of reg corresponds to the 32-bit physical address.
  155          * We should probably read #address-cells from /chosen instead.
  156          */
  157         i = (i/4) - 2;
  158 
  159         sc->sc_bank0 = pmap_mapdev(reg[i], NVRAM_SIZE * 2);
  160         sc->sc_bank1 = (char *)sc->sc_bank0 + NVRAM_SIZE;
  161 
  162         gen0 = powermac_nvram_check(sc->sc_bank0);
  163         gen1 = powermac_nvram_check(sc->sc_bank1);
  164 
  165         if (gen0 == -1 && gen1 == -1) {
  166                 if (sc->sc_bank0 != NULL)
  167                         pmap_unmapdev(sc->sc_bank0, NVRAM_SIZE * 2);
  168                 device_printf(dev, "both banks appear to be corrupt\n");
  169                 return ENXIO;
  170         }
  171         device_printf(dev, "bank0 generation %d, bank1 generation %d\n",
  172             gen0, gen1);
  173 
  174         sc->sc_bank = (gen0 > gen1) ? sc->sc_bank0 : sc->sc_bank1;
  175         bcopy(sc->sc_bank, sc->sc_data, NVRAM_SIZE);
  176 
  177         sc->sc_cdev = make_dev(&powermac_nvram_cdevsw, 0, 0, 0, 0600,
  178             "powermac_nvram");
  179         sc->sc_cdev->si_drv1 = sc;
  180 
  181         sx_init(&sc->sc_lock, "powermac_nvram");
  182 
  183         return 0;
  184 }
  185 
  186 static int
  187 powermac_nvram_detach(device_t dev)
  188 {
  189         struct powermac_nvram_softc *sc;
  190 
  191         sc = device_get_softc(dev);
  192 
  193         if (sc->sc_bank0 != NULL)
  194                 pmap_unmapdev(sc->sc_bank0, NVRAM_SIZE * 2);
  195 
  196         if (sc->sc_cdev != NULL)
  197                 destroy_dev(sc->sc_cdev);
  198 
  199         sx_destroy(&sc->sc_lock);
  200 
  201         return 0;
  202 }
  203 
  204 static int
  205 powermac_nvram_open(struct cdev *dev, int flags, int fmt, struct thread *td)
  206 {
  207         struct powermac_nvram_softc *sc = dev->si_drv1;
  208         int err;
  209 
  210         err = 0;
  211         sx_xlock(&sc->sc_lock);
  212         if (sc->sc_isopen)
  213                 err = EBUSY;
  214         else
  215                 sc->sc_isopen = 1;
  216         sc->sc_rpos = sc->sc_wpos = 0;
  217         sx_xunlock(&sc->sc_lock);
  218 
  219         return (err);
  220 }
  221 
  222 static int
  223 powermac_nvram_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
  224 {
  225         struct powermac_nvram_softc *sc = dev->si_drv1;
  226         struct core99_header *header;
  227         void *bank;
  228 
  229         sx_xlock(&sc->sc_lock);
  230         if (sc->sc_wpos != sizeof(sc->sc_data)) {
  231                 /* Short write, restore in-memory copy */
  232                 bcopy(sc->sc_bank, sc->sc_data, NVRAM_SIZE);
  233                 sc->sc_isopen = 0;
  234                 sx_xunlock(&sc->sc_lock);
  235                 return 0;
  236         }
  237 
  238         header = (struct core99_header *)sc->sc_data;
  239 
  240         header->generation = ((struct core99_header *)sc->sc_bank)->generation;
  241         header->generation++;
  242         header->chrp_header.signature = CORE99_SIGNATURE;
  243 
  244         header->adler_checksum =
  245             adler_checksum((uint8_t *)&(header->generation),
  246             NVRAM_SIZE - offsetof(struct core99_header, generation));
  247         header->chrp_header.chrp_checksum = chrp_checksum(header->chrp_header.signature,
  248             (uint8_t *)&(header->chrp_header.length),
  249             (uint8_t *)&(header->adler_checksum));
  250 
  251         bank = (sc->sc_bank == sc->sc_bank0) ? sc->sc_bank1 : sc->sc_bank0;
  252         if (erase_bank(sc->sc_dev, bank) != 0 ||
  253             write_bank(sc->sc_dev, bank, sc->sc_data) != 0) {
  254                 sc->sc_isopen = 0;
  255                 sx_xunlock(&sc->sc_lock);
  256                 return ENOSPC;
  257         }
  258         sc->sc_bank = bank;
  259         sc->sc_isopen = 0;
  260         sx_xunlock(&sc->sc_lock);
  261         return 0;
  262 }
  263 
  264 static int
  265 powermac_nvram_read(struct cdev *dev, struct uio *uio, int ioflag)
  266 {
  267         int rv, amnt, data_available;
  268         struct powermac_nvram_softc *sc = dev->si_drv1;
  269 
  270         rv = 0;
  271 
  272         sx_xlock(&sc->sc_lock);
  273         while (uio->uio_resid > 0) {
  274                 data_available = sizeof(sc->sc_data) - sc->sc_rpos;
  275                 if (data_available > 0) {
  276                         amnt = MIN(uio->uio_resid, data_available);
  277                         rv = uiomove((void *)(sc->sc_data + sc->sc_rpos),
  278                             amnt, uio);
  279                         if (rv != 0)
  280                                 break;
  281                         sc->sc_rpos += amnt;
  282                 } else {
  283                         break;
  284                 }
  285         }
  286         sx_xunlock(&sc->sc_lock);
  287 
  288         return rv;
  289 }
  290 
  291 static int
  292 powermac_nvram_write(struct cdev *dev, struct uio *uio, int ioflag)
  293 {
  294         int rv, amnt, data_available;
  295         struct powermac_nvram_softc *sc = dev->si_drv1;
  296 
  297         if (sc->sc_wpos >= sizeof(sc->sc_data))
  298                 return EINVAL;
  299 
  300         rv = 0;
  301 
  302         sx_xlock(&sc->sc_lock);
  303         while (uio->uio_resid > 0) {
  304                 data_available = sizeof(sc->sc_data) - sc->sc_wpos;
  305                 if (data_available > 0) {
  306                         amnt = MIN(uio->uio_resid, data_available);
  307                         rv = uiomove((void *)(sc->sc_data + sc->sc_wpos),
  308                             amnt, uio);
  309                         if (rv != 0)
  310                                 break;
  311                         sc->sc_wpos += amnt;
  312                 } else {
  313                         break;
  314                 }
  315         }
  316         sx_xunlock(&sc->sc_lock);
  317 
  318         return rv;
  319 }
  320 
  321 static int
  322 powermac_nvram_check(void *data)
  323 {
  324         struct core99_header *header;
  325 
  326         header = (struct core99_header *)data;
  327 
  328         if (header->chrp_header.signature != CORE99_SIGNATURE)
  329                 return -1;
  330         if (header->chrp_header.chrp_checksum !=
  331             chrp_checksum(header->chrp_header.signature,
  332             (uint8_t *)&(header->chrp_header.length),
  333             (uint8_t *)&(header->adler_checksum)))
  334                 return -1;
  335         if (header->adler_checksum !=
  336             adler_checksum((uint8_t *)&(header->generation),
  337             NVRAM_SIZE - offsetof(struct core99_header, generation)))
  338                 return -1;
  339         return header->generation;
  340 }
  341 
  342 static int
  343 chrp_checksum(int sum, uint8_t *data, uint8_t *end)
  344 {
  345 
  346         for (; data < end; data++)
  347                 sum += data[0];
  348         while (sum > 0xff)
  349                 sum = (sum & 0xff) + (sum >> 8);
  350         return sum;
  351 }
  352 
  353 static uint32_t
  354 adler_checksum(uint8_t *data, int len)
  355 {
  356         uint32_t low, high;
  357         int i;
  358 
  359         low = 1;
  360         high = 0;
  361         for (i = 0; i < len; i++) {
  362                 if ((i % 5000) == 0) {
  363                         low %= 65521UL;
  364                         high %= 65521UL;
  365                 }
  366                 low += data[i];
  367                 high += low;
  368         }
  369         low %= 65521UL;
  370         high %= 65521UL;
  371 
  372         return (high << 16) | low;
  373 }
  374 
  375 #define OUTB_DELAY(a, v)        outb(a, v); DELAY(1);
  376 
  377 static int
  378 wait_operation_complete_amd(uint8_t *bank)
  379 {
  380         int i;
  381 
  382         for (i = 1000000; i != 0; i--)
  383                 if ((inb(bank) ^ inb(bank)) == 0)
  384                         return 0;
  385         return -1;
  386 }
  387 
  388 static int
  389 erase_bank_amd(device_t dev, uint8_t *bank)
  390 {
  391         unsigned int i;
  392 
  393         /* Unlock 1 */
  394         OUTB_DELAY(bank + 0x555, 0xaa);
  395         /* Unlock 2 */
  396         OUTB_DELAY(bank + 0x2aa, 0x55);
  397 
  398         /* Sector-Erase */
  399         OUTB_DELAY(bank + 0x555, 0x80);
  400         OUTB_DELAY(bank + 0x555, 0xaa);
  401         OUTB_DELAY(bank + 0x2aa, 0x55);
  402         OUTB_DELAY(bank, 0x30);
  403 
  404         if (wait_operation_complete_amd(bank) != 0) {
  405                 device_printf(dev, "flash erase timeout\n");
  406                 return -1;
  407         }
  408 
  409         /* Reset */
  410         OUTB_DELAY(bank, 0xf0);
  411 
  412         for (i = 0; i < NVRAM_SIZE; i++) {
  413                 if (bank[i] != 0xff) {
  414                         device_printf(dev, "flash erase has failed\n");
  415                         return -1;
  416                 }
  417         }
  418         return 0;
  419 }
  420 
  421 static int
  422 write_bank_amd(device_t dev, uint8_t *bank, uint8_t *data)
  423 {
  424         unsigned int i;
  425 
  426         for (i = 0; i < NVRAM_SIZE; i++) {
  427                 /* Unlock 1 */
  428                 OUTB_DELAY(bank + 0x555, 0xaa);
  429                 /* Unlock 2 */
  430                 OUTB_DELAY(bank + 0x2aa, 0x55);
  431 
  432                 /* Write single word */
  433                 OUTB_DELAY(bank + 0x555, 0xa0);
  434                 OUTB_DELAY(bank + i, data[i]);
  435                 if (wait_operation_complete_amd(bank) != 0) {
  436                         device_printf(dev, "flash write timeout\n");
  437                         return -1;
  438                 }
  439         }
  440 
  441         /* Reset */
  442         OUTB_DELAY(bank, 0xf0);
  443 
  444         for (i = 0; i < NVRAM_SIZE; i++) {
  445                 if (bank[i] != data[i]) {
  446                         device_printf(dev, "flash write has failed\n");
  447                         return -1;
  448                 }
  449         }
  450         return 0;
  451 }
  452 
  453 static int
  454 wait_operation_complete_sm(uint8_t *bank)
  455 {
  456         int i;
  457 
  458         for (i = 1000000; i != 0; i--) {
  459                 outb(bank, SM_FLASH_CMD_READ_STATUS);
  460                 if (inb(bank) & SM_FLASH_STATUS_DONE)
  461                         return (0);
  462         }
  463         return (-1);
  464 }
  465 
  466 static int
  467 erase_bank_sm(device_t dev, uint8_t *bank)
  468 {
  469         unsigned int i;
  470 
  471         outb(bank, SM_FLASH_CMD_ERASE_SETUP);
  472         outb(bank, SM_FLASH_CMD_ERASE_CONFIRM);
  473 
  474         if (wait_operation_complete_sm(bank) != 0) {
  475                 device_printf(dev, "flash erase timeout\n");
  476                 return (-1);
  477         }
  478 
  479         outb(bank, SM_FLASH_CMD_CLEAR_STATUS);
  480         outb(bank, SM_FLASH_CMD_RESET);
  481 
  482         for (i = 0; i < NVRAM_SIZE; i++) {
  483                 if (bank[i] != 0xff) {
  484                         device_printf(dev, "flash write has failed\n");
  485                         return (-1);
  486                 }
  487         }
  488         return (0);
  489 }
  490 
  491 static int
  492 write_bank_sm(device_t dev, uint8_t *bank, uint8_t *data)
  493 {
  494         unsigned int i;
  495 
  496         for (i = 0; i < NVRAM_SIZE; i++) {
  497                 OUTB_DELAY(bank + i, SM_FLASH_CMD_WRITE_SETUP);
  498                 outb(bank + i, data[i]);
  499                 if (wait_operation_complete_sm(bank) != 0) {
  500                         device_printf(dev, "flash write error/timeout\n");
  501                         break;
  502                 }
  503         }
  504 
  505         outb(bank, SM_FLASH_CMD_CLEAR_STATUS);
  506         outb(bank, SM_FLASH_CMD_RESET);
  507 
  508         for (i = 0; i < NVRAM_SIZE; i++) {
  509                 if (bank[i] != data[i]) {
  510                         device_printf(dev, "flash write has failed\n");
  511                         return (-1);
  512                 }
  513         }
  514         return (0);
  515 }
  516 
  517 static int
  518 erase_bank(device_t dev, uint8_t *bank)
  519 {
  520         struct powermac_nvram_softc *sc;
  521 
  522         sc = device_get_softc(dev);
  523 
  524         sx_assert(&sc->sc_lock, SA_XLOCKED);
  525         if (sc->sc_type == FLASH_TYPE_AMD)
  526                 return (erase_bank_amd(dev, bank));
  527         else
  528                 return (erase_bank_sm(dev, bank));
  529 }
  530 
  531 static int
  532 write_bank(device_t dev, uint8_t *bank, uint8_t *data)
  533 {
  534         struct powermac_nvram_softc *sc;
  535 
  536         sc = device_get_softc(dev);
  537 
  538         sx_assert(&sc->sc_lock, SA_XLOCKED);
  539         if (sc->sc_type == FLASH_TYPE_AMD)
  540                 return (write_bank_amd(dev, bank, data));
  541         else
  542                 return (write_bank_sm(dev, bank, data));
  543 }

Cache object: 6dfc97a99abb1ef170847b4b819a01a4


[ 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.