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

Cache object: 6a294a4a980395178dfcc10bb9584838


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