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 static devclass_t powermac_nvram_devclass; 89 90 DRIVER_MODULE(powermac_nvram, ofwbus, powermac_nvram_driver, powermac_nvram_devclass, 0, 0); 91 92 /* 93 * Cdev methods. 94 */ 95 96 static d_open_t powermac_nvram_open; 97 static d_close_t powermac_nvram_close; 98 static d_read_t powermac_nvram_read; 99 static d_write_t powermac_nvram_write; 100 101 static struct cdevsw powermac_nvram_cdevsw = { 102 .d_version = D_VERSION, 103 .d_open = powermac_nvram_open, 104 .d_close = powermac_nvram_close, 105 .d_read = powermac_nvram_read, 106 .d_write = powermac_nvram_write, 107 .d_name = "powermac_nvram", 108 }; 109 110 static int 111 powermac_nvram_probe(device_t dev) 112 { 113 const char *type, *compatible; 114 115 type = ofw_bus_get_type(dev); 116 compatible = ofw_bus_get_compat(dev); 117 118 if (type == NULL || compatible == NULL) 119 return ENXIO; 120 121 if (strcmp(type, "nvram") != 0) 122 return ENXIO; 123 if (strcmp(compatible, "amd-0137") != 0 && 124 !ofw_bus_is_compatible(dev, "nvram,flash")) 125 return ENXIO; 126 127 device_set_desc(dev, "Apple NVRAM"); 128 return 0; 129 } 130 131 static int 132 powermac_nvram_attach(device_t dev) 133 { 134 struct powermac_nvram_softc *sc; 135 const char *compatible; 136 phandle_t node; 137 u_int32_t reg[3]; 138 int gen0, gen1, i; 139 140 node = ofw_bus_get_node(dev); 141 sc = device_get_softc(dev); 142 143 if ((i = OF_getprop(node, "reg", reg, sizeof(reg))) < 8) 144 return ENXIO; 145 146 sc->sc_dev = dev; 147 sc->sc_node = node; 148 149 compatible = ofw_bus_get_compat(dev); 150 if (strcmp(compatible, "amd-0137") == 0) 151 sc->sc_type = FLASH_TYPE_AMD; 152 else 153 sc->sc_type = FLASH_TYPE_SM; 154 155 /* 156 * Find which byte of reg corresponds to the 32-bit physical address. 157 * We should probably read #address-cells from /chosen instead. 158 */ 159 i = (i/4) - 2; 160 161 sc->sc_bank0 = (vm_offset_t)pmap_mapdev(reg[i], NVRAM_SIZE * 2); 162 sc->sc_bank1 = sc->sc_bank0 + NVRAM_SIZE; 163 164 gen0 = powermac_nvram_check((void *)sc->sc_bank0); 165 gen1 = powermac_nvram_check((void *)sc->sc_bank1); 166 167 if (gen0 == -1 && gen1 == -1) { 168 if ((void *)sc->sc_bank0 != NULL) 169 pmap_unmapdev(sc->sc_bank0, NVRAM_SIZE * 2); 170 device_printf(dev, "both banks appear to be corrupt\n"); 171 return ENXIO; 172 } 173 device_printf(dev, "bank0 generation %d, bank1 generation %d\n", 174 gen0, gen1); 175 176 sc->sc_bank = (gen0 > gen1) ? sc->sc_bank0 : sc->sc_bank1; 177 bcopy((void *)sc->sc_bank, (void *)sc->sc_data, NVRAM_SIZE); 178 179 sc->sc_cdev = make_dev(&powermac_nvram_cdevsw, 0, 0, 0, 0600, 180 "powermac_nvram"); 181 sc->sc_cdev->si_drv1 = sc; 182 183 sx_init(&sc->sc_lock, "powermac_nvram"); 184 185 return 0; 186 } 187 188 static int 189 powermac_nvram_detach(device_t dev) 190 { 191 struct powermac_nvram_softc *sc; 192 193 sc = device_get_softc(dev); 194 195 if ((void *)sc->sc_bank0 != NULL) 196 pmap_unmapdev(sc->sc_bank0, NVRAM_SIZE * 2); 197 198 if (sc->sc_cdev != NULL) 199 destroy_dev(sc->sc_cdev); 200 201 sx_destroy(&sc->sc_lock); 202 203 return 0; 204 } 205 206 static int 207 powermac_nvram_open(struct cdev *dev, int flags, int fmt, struct thread *td) 208 { 209 struct powermac_nvram_softc *sc = dev->si_drv1; 210 int err; 211 212 err = 0; 213 sx_xlock(&sc->sc_lock); 214 if (sc->sc_isopen) 215 err = EBUSY; 216 else 217 sc->sc_isopen = 1; 218 sc->sc_rpos = sc->sc_wpos = 0; 219 sx_xunlock(&sc->sc_lock); 220 221 return 0; 222 } 223 224 static int 225 powermac_nvram_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 226 { 227 struct powermac_nvram_softc *sc = dev->si_drv1; 228 struct core99_header *header; 229 vm_offset_t bank; 230 231 sx_xlock(&sc->sc_lock); 232 if (sc->sc_wpos != sizeof(sc->sc_data)) { 233 /* Short write, restore in-memory copy */ 234 bcopy((void *)sc->sc_bank, (void *)sc->sc_data, NVRAM_SIZE); 235 sc->sc_isopen = 0; 236 sx_xunlock(&sc->sc_lock); 237 return 0; 238 } 239 240 header = (struct core99_header *)sc->sc_data; 241 242 header->generation = ((struct core99_header *)sc->sc_bank)->generation; 243 header->generation++; 244 header->chrp_header.signature = CORE99_SIGNATURE; 245 246 header->adler_checksum = 247 adler_checksum((uint8_t *)&(header->generation), 248 NVRAM_SIZE - offsetof(struct core99_header, generation)); 249 header->chrp_header.chrp_checksum = chrp_checksum(header->chrp_header.signature, 250 (uint8_t *)&(header->chrp_header.length), 251 (uint8_t *)&(header->adler_checksum)); 252 253 bank = (sc->sc_bank == sc->sc_bank0) ? sc->sc_bank1 : sc->sc_bank0; 254 if (erase_bank(sc->sc_dev, (uint8_t *)bank) != 0 || 255 write_bank(sc->sc_dev, (uint8_t *)bank, sc->sc_data) != 0) { 256 sc->sc_isopen = 0; 257 sx_xunlock(&sc->sc_lock); 258 return ENOSPC; 259 } 260 sc->sc_bank = bank; 261 sc->sc_isopen = 0; 262 sx_xunlock(&sc->sc_lock); 263 return 0; 264 } 265 266 static int 267 powermac_nvram_read(struct cdev *dev, struct uio *uio, int ioflag) 268 { 269 int rv, amnt, data_available; 270 struct powermac_nvram_softc *sc = dev->si_drv1; 271 272 rv = 0; 273 274 sx_xlock(&sc->sc_lock); 275 while (uio->uio_resid > 0) { 276 data_available = sizeof(sc->sc_data) - sc->sc_rpos; 277 if (data_available > 0) { 278 amnt = MIN(uio->uio_resid, data_available); 279 rv = uiomove((void *)(sc->sc_data + sc->sc_rpos), 280 amnt, uio); 281 if (rv != 0) 282 break; 283 sc->sc_rpos += amnt; 284 } else { 285 break; 286 } 287 } 288 sx_xunlock(&sc->sc_lock); 289 290 return rv; 291 } 292 293 static int 294 powermac_nvram_write(struct cdev *dev, struct uio *uio, int ioflag) 295 { 296 int rv, amnt, data_available; 297 struct powermac_nvram_softc *sc = dev->si_drv1; 298 299 if (sc->sc_wpos >= sizeof(sc->sc_data)) 300 return EINVAL; 301 302 rv = 0; 303 304 sx_xlock(&sc->sc_lock); 305 while (uio->uio_resid > 0) { 306 data_available = sizeof(sc->sc_data) - sc->sc_wpos; 307 if (data_available > 0) { 308 amnt = MIN(uio->uio_resid, data_available); 309 rv = uiomove((void *)(sc->sc_data + sc->sc_wpos), 310 amnt, uio); 311 if (rv != 0) 312 break; 313 sc->sc_wpos += amnt; 314 } else { 315 break; 316 } 317 } 318 sx_xunlock(&sc->sc_lock); 319 320 return rv; 321 } 322 323 static int 324 powermac_nvram_check(void *data) 325 { 326 struct core99_header *header; 327 328 header = (struct core99_header *)data; 329 330 if (header->chrp_header.signature != CORE99_SIGNATURE) 331 return -1; 332 if (header->chrp_header.chrp_checksum != 333 chrp_checksum(header->chrp_header.signature, 334 (uint8_t *)&(header->chrp_header.length), 335 (uint8_t *)&(header->adler_checksum))) 336 return -1; 337 if (header->adler_checksum != 338 adler_checksum((uint8_t *)&(header->generation), 339 NVRAM_SIZE - offsetof(struct core99_header, generation))) 340 return -1; 341 return header->generation; 342 } 343 344 static int 345 chrp_checksum(int sum, uint8_t *data, uint8_t *end) 346 { 347 348 for (; data < end; data++) 349 sum += data[0]; 350 while (sum > 0xff) 351 sum = (sum & 0xff) + (sum >> 8); 352 return sum; 353 } 354 355 static uint32_t 356 adler_checksum(uint8_t *data, int len) 357 { 358 uint32_t low, high; 359 int i; 360 361 low = 1; 362 high = 0; 363 for (i = 0; i < len; i++) { 364 if ((i % 5000) == 0) { 365 high %= 65521UL; 366 high %= 65521UL; 367 } 368 low += data[i]; 369 high += low; 370 } 371 low %= 65521UL; 372 high %= 65521UL; 373 374 return (high << 16) | low; 375 } 376 377 #define OUTB_DELAY(a, v) outb(a, v); DELAY(1); 378 379 static int 380 wait_operation_complete_amd(uint8_t *bank) 381 { 382 int i; 383 384 for (i = 1000000; i != 0; i--) 385 if ((inb(bank) ^ inb(bank)) == 0) 386 return 0; 387 return -1; 388 } 389 390 static int 391 erase_bank_amd(device_t dev, uint8_t *bank) 392 { 393 unsigned int i; 394 395 /* Unlock 1 */ 396 OUTB_DELAY(bank + 0x555, 0xaa); 397 /* Unlock 2 */ 398 OUTB_DELAY(bank + 0x2aa, 0x55); 399 400 /* Sector-Erase */ 401 OUTB_DELAY(bank + 0x555, 0x80); 402 OUTB_DELAY(bank + 0x555, 0xaa); 403 OUTB_DELAY(bank + 0x2aa, 0x55); 404 OUTB_DELAY(bank, 0x30); 405 406 if (wait_operation_complete_amd(bank) != 0) { 407 device_printf(dev, "flash erase timeout\n"); 408 return -1; 409 } 410 411 /* Reset */ 412 OUTB_DELAY(bank, 0xf0); 413 414 for (i = 0; i < NVRAM_SIZE; i++) { 415 if (bank[i] != 0xff) { 416 device_printf(dev, "flash erase has failed\n"); 417 return -1; 418 } 419 } 420 return 0; 421 } 422 423 static int 424 write_bank_amd(device_t dev, uint8_t *bank, uint8_t *data) 425 { 426 unsigned int i; 427 428 for (i = 0; i < NVRAM_SIZE; i++) { 429 /* Unlock 1 */ 430 OUTB_DELAY(bank + 0x555, 0xaa); 431 /* Unlock 2 */ 432 OUTB_DELAY(bank + 0x2aa, 0x55); 433 434 /* Write single word */ 435 OUTB_DELAY(bank + 0x555, 0xa0); 436 OUTB_DELAY(bank + i, data[i]); 437 if (wait_operation_complete_amd(bank) != 0) { 438 device_printf(dev, "flash write timeout\n"); 439 return -1; 440 } 441 } 442 443 /* Reset */ 444 OUTB_DELAY(bank, 0xf0); 445 446 for (i = 0; i < NVRAM_SIZE; i++) { 447 if (bank[i] != data[i]) { 448 device_printf(dev, "flash write has failed\n"); 449 return -1; 450 } 451 } 452 return 0; 453 } 454 455 static int 456 wait_operation_complete_sm(uint8_t *bank) 457 { 458 int i; 459 460 for (i = 1000000; i != 0; i--) { 461 outb(bank, SM_FLASH_CMD_READ_STATUS); 462 if (inb(bank) & SM_FLASH_STATUS_DONE) 463 return (0); 464 } 465 return (-1); 466 } 467 468 static int 469 erase_bank_sm(device_t dev, uint8_t *bank) 470 { 471 unsigned int i; 472 473 outb(bank, SM_FLASH_CMD_ERASE_SETUP); 474 outb(bank, SM_FLASH_CMD_ERASE_CONFIRM); 475 476 if (wait_operation_complete_sm(bank) != 0) { 477 device_printf(dev, "flash erase timeout\n"); 478 return (-1); 479 } 480 481 outb(bank, SM_FLASH_CMD_CLEAR_STATUS); 482 outb(bank, SM_FLASH_CMD_RESET); 483 484 for (i = 0; i < NVRAM_SIZE; i++) { 485 if (bank[i] != 0xff) { 486 device_printf(dev, "flash write has failed\n"); 487 return (-1); 488 } 489 } 490 return (0); 491 } 492 493 static int 494 write_bank_sm(device_t dev, uint8_t *bank, uint8_t *data) 495 { 496 unsigned int i; 497 498 for (i = 0; i < NVRAM_SIZE; i++) { 499 OUTB_DELAY(bank + i, SM_FLASH_CMD_WRITE_SETUP); 500 outb(bank + i, data[i]); 501 if (wait_operation_complete_sm(bank) != 0) { 502 device_printf(dev, "flash write error/timeout\n"); 503 break; 504 } 505 } 506 507 outb(bank, SM_FLASH_CMD_CLEAR_STATUS); 508 outb(bank, SM_FLASH_CMD_RESET); 509 510 for (i = 0; i < NVRAM_SIZE; i++) { 511 if (bank[i] != data[i]) { 512 device_printf(dev, "flash write has failed\n"); 513 return (-1); 514 } 515 } 516 return (0); 517 } 518 519 static int 520 erase_bank(device_t dev, uint8_t *bank) 521 { 522 struct powermac_nvram_softc *sc; 523 524 sc = device_get_softc(dev); 525 526 sx_assert(&sc->sc_lock, SA_XLOCKED); 527 if (sc->sc_type == FLASH_TYPE_AMD) 528 return (erase_bank_amd(dev, bank)); 529 else 530 return (erase_bank_sm(dev, bank)); 531 } 532 533 static int 534 write_bank(device_t dev, uint8_t *bank, uint8_t *data) 535 { 536 struct powermac_nvram_softc *sc; 537 538 sc = device_get_softc(dev); 539 540 sx_assert(&sc->sc_lock, SA_XLOCKED); 541 if (sc->sc_type == FLASH_TYPE_AMD) 542 return (write_bank_amd(dev, bank, data)); 543 else 544 return (write_bank_sm(dev, bank, data)); 545 } 546