1235537Sgber/*- 2235537Sgber * Copyright (C) 2009-2012 Semihalf 3235537Sgber * All rights reserved. 4235537Sgber * 5235537Sgber * Redistribution and use in source and binary forms, with or without 6235537Sgber * modification, are permitted provided that the following conditions 7235537Sgber * are met: 8235537Sgber * 1. Redistributions of source code must retain the above copyright 9235537Sgber * notice, this list of conditions and the following disclaimer. 10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright 11235537Sgber * notice, this list of conditions and the following disclaimer in the 12235537Sgber * documentation and/or other materials provided with the distribution. 13235537Sgber * 14235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17235537Sgber * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24235537Sgber * SUCH DAMAGE. 25235537Sgber */ 26235537Sgber 27235537Sgber#include <sys/cdefs.h> 28235537Sgber__FBSDID("$FreeBSD$"); 29235537Sgber 30235537Sgber#include <sys/param.h> 31235537Sgber#include <sys/systm.h> 32235537Sgber#include <sys/kernel.h> 33235537Sgber#include <sys/socket.h> 34235537Sgber#include <sys/malloc.h> 35235537Sgber#include <sys/module.h> 36235537Sgber#include <sys/bus.h> 37235537Sgber#include <sys/proc.h> 38235537Sgber#include <sys/lock.h> 39235537Sgber#include <sys/mutex.h> 40235537Sgber#include <sys/condvar.h> 41235537Sgber 42235537Sgber#include <dev/nand/nand.h> 43235537Sgber#include <dev/nand/nandbus.h> 44235537Sgber#include "nand_if.h" 45235537Sgber#include "nandbus_if.h" 46235537Sgber#include "nfc_if.h" 47235537Sgber 48235537Sgber#define NAND_NCS 4 49235537Sgber 50235537Sgberstatic int nandbus_probe(device_t dev); 51235537Sgberstatic int nandbus_attach(device_t dev); 52235537Sgberstatic int nandbus_detach(device_t dev); 53235537Sgber 54235537Sgberstatic int nandbus_child_location_str(device_t, device_t, char *, size_t); 55235537Sgberstatic int nandbus_child_pnpinfo_str(device_t, device_t, char *, size_t); 56235537Sgber 57235537Sgberstatic int nandbus_get_status(device_t, uint8_t *); 58235537Sgberstatic void nandbus_read_buffer(device_t, void *, uint32_t); 59235537Sgberstatic int nandbus_select_cs(device_t, uint8_t); 60235537Sgberstatic int nandbus_send_command(device_t, uint8_t); 61235537Sgberstatic int nandbus_send_address(device_t, uint8_t); 62235537Sgberstatic int nandbus_start_command(device_t); 63235537Sgberstatic int nandbus_wait_ready(device_t, uint8_t *); 64235537Sgberstatic void nandbus_write_buffer(device_t, void *, uint32_t); 65235537Sgberstatic int nandbus_get_ecc(device_t, void *, uint32_t, void *, int *); 66235537Sgberstatic int nandbus_correct_ecc(device_t, void *, int, void *, void *); 67235537Sgberstatic void nandbus_lock(device_t); 68235537Sgberstatic void nandbus_unlock(device_t); 69235537Sgber 70235537Sgberstatic int nand_readid(device_t, uint8_t *, uint8_t *); 71235537Sgberstatic int nand_probe_onfi(device_t, uint8_t *); 72235537Sgberstatic int nand_reset(device_t); 73235537Sgber 74235537Sgberstruct nandbus_softc { 75235537Sgber device_t dev; 76235537Sgber struct cv nandbus_cv; 77235537Sgber struct mtx nandbus_mtx; 78235537Sgber uint8_t busy; 79235537Sgber}; 80235537Sgber 81235537Sgberstatic device_method_t nandbus_methods[] = { 82235537Sgber /* device interface */ 83235537Sgber DEVMETHOD(device_probe, nandbus_probe), 84235537Sgber DEVMETHOD(device_attach, nandbus_attach), 85235537Sgber DEVMETHOD(device_detach, nandbus_detach), 86235537Sgber DEVMETHOD(device_shutdown, bus_generic_shutdown), 87235537Sgber 88235537Sgber /* bus interface */ 89235537Sgber DEVMETHOD(bus_print_child, bus_generic_print_child), 90235537Sgber DEVMETHOD(bus_driver_added, bus_generic_driver_added), 91235537Sgber DEVMETHOD(bus_child_pnpinfo_str, nandbus_child_pnpinfo_str), 92235537Sgber DEVMETHOD(bus_child_location_str, nandbus_child_location_str), 93235537Sgber 94235537Sgber /* nandbus interface */ 95235537Sgber DEVMETHOD(nandbus_get_status, nandbus_get_status), 96235537Sgber DEVMETHOD(nandbus_read_buffer, nandbus_read_buffer), 97235537Sgber DEVMETHOD(nandbus_select_cs, nandbus_select_cs), 98235537Sgber DEVMETHOD(nandbus_send_command, nandbus_send_command), 99235537Sgber DEVMETHOD(nandbus_send_address, nandbus_send_address), 100235537Sgber DEVMETHOD(nandbus_start_command,nandbus_start_command), 101235537Sgber DEVMETHOD(nandbus_wait_ready, nandbus_wait_ready), 102235537Sgber DEVMETHOD(nandbus_write_buffer, nandbus_write_buffer), 103235537Sgber DEVMETHOD(nandbus_get_ecc, nandbus_get_ecc), 104235537Sgber DEVMETHOD(nandbus_correct_ecc, nandbus_correct_ecc), 105235537Sgber DEVMETHOD(nandbus_lock, nandbus_lock), 106235537Sgber DEVMETHOD(nandbus_unlock, nandbus_unlock), 107235537Sgber { 0, 0 } 108235537Sgber}; 109235537Sgber 110235537Sgberdevclass_t nandbus_devclass; 111235537Sgber 112235537Sgberdriver_t nandbus_driver = { 113235537Sgber "nandbus", 114235537Sgber nandbus_methods, 115235537Sgber sizeof(struct nandbus_softc) 116235537Sgber}; 117235537Sgber 118235537SgberDRIVER_MODULE(nandbus, nand, nandbus_driver, nandbus_devclass, 0, 0); 119235537Sgber 120235537Sgberint 121235537Sgbernandbus_create(device_t nfc) 122235537Sgber{ 123235537Sgber device_t child; 124235537Sgber 125235537Sgber child = device_add_child(nfc, "nandbus", -1); 126235537Sgber if (!child) 127235537Sgber return (ENODEV); 128235537Sgber 129235537Sgber bus_generic_attach(nfc); 130235537Sgber 131235537Sgber return(0); 132235537Sgber} 133235537Sgber 134235537Sgbervoid 135235537Sgbernandbus_destroy(device_t nfc) 136235537Sgber{ 137235537Sgber device_t *children; 138235537Sgber int nchildren, i; 139235537Sgber 140235537Sgber mtx_lock(&Giant); 141235537Sgber /* Detach & delete all children */ 142235537Sgber if (!device_get_children(nfc, &children, &nchildren)) { 143235537Sgber for (i = 0; i < nchildren; i++) 144235537Sgber device_delete_child(nfc, children[i]); 145235537Sgber 146235537Sgber free(children, M_TEMP); 147235537Sgber } 148235537Sgber mtx_unlock(&Giant); 149235537Sgber} 150235537Sgber 151235537Sgberstatic int 152235537Sgbernandbus_probe(device_t dev) 153235537Sgber{ 154235537Sgber 155235537Sgber device_set_desc(dev, "NAND bus"); 156235537Sgber 157235537Sgber return (0); 158235537Sgber} 159235537Sgber 160235537Sgberstatic int 161235537Sgbernandbus_attach(device_t dev) 162235537Sgber{ 163235537Sgber device_t child, nfc; 164235537Sgber struct nand_id chip_id; 165235537Sgber struct nandbus_softc *sc; 166235537Sgber struct nandbus_ivar *ivar; 167235537Sgber struct nand_softc *nfc_sc; 168235537Sgber struct nand_params *chip_params; 169235537Sgber uint8_t cs, onfi; 170235537Sgber 171235537Sgber sc = device_get_softc(dev); 172235537Sgber sc->dev = dev; 173235537Sgber 174235537Sgber nfc = device_get_parent(dev); 175235537Sgber nfc_sc = device_get_softc(nfc); 176235537Sgber 177235537Sgber mtx_init(&sc->nandbus_mtx, "nandbus lock", MTX_DEF, 0); 178235537Sgber cv_init(&sc->nandbus_cv, "nandbus cv"); 179235537Sgber 180235537Sgber /* Check each possible CS for existing nand devices */ 181235537Sgber for (cs = 0; cs < NAND_NCS; cs++) { 182235537Sgber nand_debug(NDBG_BUS,"probe chip select %x", cs); 183235537Sgber 184235537Sgber /* Select & reset chip */ 185235537Sgber if (nandbus_select_cs(dev, cs)) 186235537Sgber break; 187235537Sgber 188235537Sgber if (nand_reset(dev)) 189235537Sgber continue; 190235537Sgber 191235537Sgber /* Read manufacturer and device id */ 192235537Sgber if (nand_readid(dev, &chip_id.man_id, &chip_id.dev_id)) 193235537Sgber continue; 194235537Sgber 195235537Sgber if (chip_id.man_id == 0xff) 196235537Sgber continue; 197235537Sgber 198235537Sgber /* Check if chip is ONFI compliant */ 199235537Sgber if (nand_probe_onfi(dev, &onfi) != 0) { 200235537Sgber continue; 201235537Sgber } 202235537Sgber 203235537Sgber ivar = malloc(sizeof(struct nandbus_ivar), 204235537Sgber M_NAND, M_WAITOK); 205235537Sgber 206235537Sgber if (onfi == 1) { 207235537Sgber ivar->cs = cs; 208235537Sgber ivar->cols = 0; 209235537Sgber ivar->rows = 0; 210235537Sgber ivar->params = NULL; 211235537Sgber ivar->man_id = chip_id.man_id; 212235537Sgber ivar->dev_id = chip_id.dev_id; 213235537Sgber ivar->is_onfi = onfi; 214235537Sgber ivar->chip_cdev_name = nfc_sc->chip_cdev_name; 215235537Sgber 216235537Sgber child = device_add_child(dev, NULL, -1); 217235537Sgber device_set_ivars(child, ivar); 218235537Sgber continue; 219235537Sgber } 220235537Sgber 221235537Sgber chip_params = nand_get_params(&chip_id); 222235537Sgber if (chip_params == NULL) { 223235537Sgber nand_debug(NDBG_BUS,"Chip description not found! " 224235537Sgber "(manuf: 0x%0x, chipid: 0x%0x)\n", 225235537Sgber chip_id.man_id, chip_id.dev_id); 226235537Sgber free(ivar, M_NAND); 227235537Sgber continue; 228235537Sgber } 229235537Sgber 230235537Sgber ivar->cs = cs; 231235537Sgber ivar->cols = 1; 232235537Sgber ivar->rows = 2; 233235537Sgber ivar->params = chip_params; 234235537Sgber ivar->man_id = chip_id.man_id; 235235537Sgber ivar->dev_id = chip_id.dev_id; 236235537Sgber ivar->is_onfi = onfi; 237235537Sgber ivar->chip_cdev_name = nfc_sc->chip_cdev_name; 238235537Sgber 239235537Sgber /* 240235537Sgber * Check what type of device we have. 241235537Sgber * devices bigger than 32MiB have on more row (3) 242235537Sgber */ 243235537Sgber if (chip_params->chip_size > 32) 244235537Sgber ivar->rows++; 245235537Sgber /* Large page devices have one more col (2) */ 246235537Sgber if (chip_params->chip_size >= 128 && 247235537Sgber chip_params->page_size > 512) 248235537Sgber ivar->cols++; 249235537Sgber 250235537Sgber child = device_add_child(dev, NULL, -1); 251235537Sgber device_set_ivars(child, ivar); 252235537Sgber } 253235537Sgber 254235537Sgber bus_generic_attach(dev); 255235537Sgber return (0); 256235537Sgber} 257235537Sgber 258235537Sgberstatic int 259235537Sgbernandbus_detach(device_t dev) 260235537Sgber{ 261235537Sgber struct nandbus_softc *sc; 262235537Sgber 263235537Sgber sc = device_get_softc(dev); 264235537Sgber 265235537Sgber bus_generic_detach(dev); 266235537Sgber 267235537Sgber mtx_destroy(&sc->nandbus_mtx); 268235537Sgber cv_destroy(&sc->nandbus_cv); 269235537Sgber 270235537Sgber return (0); 271235537Sgber} 272235537Sgber 273235537Sgberstatic int 274235537Sgbernandbus_child_location_str(device_t bus, device_t child, char *buf, 275235537Sgber size_t buflen) 276235537Sgber{ 277235537Sgber struct nandbus_ivar *ivar = device_get_ivars(child); 278235537Sgber 279235537Sgber snprintf(buf, buflen, "at cs#%d", ivar->cs); 280235537Sgber return (0); 281235537Sgber} 282235537Sgber 283235537Sgberstatic int 284235537Sgbernandbus_child_pnpinfo_str(device_t bus, device_t child, char *buf, 285235537Sgber size_t buflen) 286235537Sgber{ 287235537Sgber // XXX man id, model id ???? 288235537Sgber *buf = '\0'; 289235537Sgber return (0); 290235537Sgber} 291235537Sgber 292235537Sgberstatic int 293235537Sgbernand_readid(device_t bus, uint8_t *man_id, uint8_t *dev_id) 294235537Sgber{ 295235537Sgber device_t nfc; 296235537Sgber 297235537Sgber if (!bus || !man_id || !dev_id) 298235537Sgber return (EINVAL); 299235537Sgber 300235537Sgber nand_debug(NDBG_BUS,"read id"); 301235537Sgber 302235537Sgber nfc = device_get_parent(bus); 303235537Sgber 304235537Sgber if (NFC_SEND_COMMAND(nfc, NAND_CMD_READ_ID)) { 305235537Sgber nand_debug(NDBG_BUS,"Error : could not send READ ID command"); 306235537Sgber return (ENXIO); 307235537Sgber } 308235537Sgber 309235537Sgber if (NFC_SEND_ADDRESS(nfc, 0)) { 310235537Sgber nand_debug(NDBG_BUS,"Error : could not sent address to chip"); 311235537Sgber return (ENXIO); 312235537Sgber } 313235537Sgber 314235537Sgber if (NFC_START_COMMAND(nfc) != 0) { 315235537Sgber nand_debug(NDBG_BUS,"Error : could not start command"); 316235537Sgber return (ENXIO); 317235537Sgber } 318235537Sgber 319235537Sgber DELAY(25); 320235537Sgber 321235537Sgber *man_id = NFC_READ_BYTE(nfc); 322235537Sgber *dev_id = NFC_READ_BYTE(nfc); 323235537Sgber 324235537Sgber nand_debug(NDBG_BUS,"manufacturer id: %x chip id: %x", *man_id, 325235537Sgber *dev_id); 326235537Sgber 327235537Sgber return (0); 328235537Sgber} 329235537Sgber 330235537Sgberstatic int 331235537Sgbernand_probe_onfi(device_t bus, uint8_t *onfi_compliant) 332235537Sgber{ 333235537Sgber device_t nfc; 334251022Sgber char onfi_id[] = {'O', 'N', 'F', 'I', '\0'}; 335235537Sgber int i; 336235537Sgber 337235537Sgber nand_debug(NDBG_BUS,"probing ONFI"); 338235537Sgber 339235537Sgber nfc = device_get_parent(bus); 340235537Sgber 341235537Sgber if (NFC_SEND_COMMAND(nfc, NAND_CMD_READ_ID)) { 342235537Sgber nand_debug(NDBG_BUS,"Error : could not sent READ ID command"); 343235537Sgber return (ENXIO); 344235537Sgber } 345235537Sgber 346235537Sgber if (NFC_SEND_ADDRESS(nfc, ONFI_SIG_ADDR)) { 347235537Sgber nand_debug(NDBG_BUS,"Error : could not sent address to chip"); 348235537Sgber return (ENXIO); 349235537Sgber } 350235537Sgber 351235537Sgber if (NFC_START_COMMAND(nfc) != 0) { 352235537Sgber nand_debug(NDBG_BUS,"Error : could not start command"); 353235537Sgber return (ENXIO); 354235537Sgber } 355235537Sgber for (i = 0; onfi_id[i] != '\0'; i++) 356235537Sgber if (NFC_READ_BYTE(nfc) != onfi_id[i]) { 357235537Sgber nand_debug(NDBG_BUS,"ONFI non-compliant"); 358235537Sgber *onfi_compliant = 0; 359235537Sgber return (0); 360235537Sgber } 361235537Sgber 362235537Sgber nand_debug(NDBG_BUS,"ONFI compliant"); 363235537Sgber *onfi_compliant = 1; 364235537Sgber 365235537Sgber return (0); 366235537Sgber} 367235537Sgber 368235537Sgberstatic int 369235537Sgbernand_reset(device_t bus) 370235537Sgber{ 371235537Sgber device_t nfc; 372235537Sgber nand_debug(NDBG_BUS,"resetting..."); 373235537Sgber 374235537Sgber nfc = device_get_parent(bus); 375235537Sgber 376235537Sgber if (NFC_SEND_COMMAND(nfc, NAND_CMD_RESET) != 0) { 377235537Sgber nand_debug(NDBG_BUS,"Error : could not sent RESET command"); 378235537Sgber return (ENXIO); 379235537Sgber } 380235537Sgber 381235537Sgber if (NFC_START_COMMAND(nfc) != 0) { 382235537Sgber nand_debug(NDBG_BUS,"Error : could not start RESET command"); 383235537Sgber return (ENXIO); 384235537Sgber } 385235537Sgber 386235537Sgber DELAY(1000); 387235537Sgber 388235537Sgber return (0); 389235537Sgber} 390235537Sgber 391235537Sgbervoid 392235537Sgbernandbus_lock(device_t dev) 393235537Sgber{ 394235537Sgber struct nandbus_softc *sc; 395235537Sgber 396235537Sgber sc = device_get_softc(dev); 397235537Sgber 398235537Sgber mtx_lock(&sc->nandbus_mtx); 399235537Sgber if (sc->busy) 400235537Sgber cv_wait(&sc->nandbus_cv, &sc->nandbus_mtx); 401235537Sgber sc->busy = 1; 402235537Sgber mtx_unlock(&sc->nandbus_mtx); 403235537Sgber} 404235537Sgber 405235537Sgbervoid 406235537Sgbernandbus_unlock(device_t dev) 407235537Sgber{ 408235537Sgber struct nandbus_softc *sc; 409235537Sgber 410235537Sgber sc = device_get_softc(dev); 411235537Sgber 412235537Sgber mtx_lock(&sc->nandbus_mtx); 413235537Sgber sc->busy = 0; 414235537Sgber cv_signal(&sc->nandbus_cv); 415235537Sgber mtx_unlock(&sc->nandbus_mtx); 416235537Sgber} 417235537Sgber 418235537Sgberint 419235537Sgbernandbus_select_cs(device_t dev, uint8_t cs) 420235537Sgber{ 421235537Sgber 422235537Sgber return (NFC_SELECT_CS(device_get_parent(dev), cs)); 423235537Sgber} 424235537Sgber 425235537Sgberint 426235537Sgbernandbus_send_command(device_t dev, uint8_t command) 427235537Sgber{ 428235537Sgber int err; 429235537Sgber 430235537Sgber if ((err = NFC_SEND_COMMAND(device_get_parent(dev), command))) 431235537Sgber nand_debug(NDBG_BUS,"Err: Could not send command %x, err %x", 432235537Sgber command, err); 433235537Sgber 434235537Sgber return (err); 435235537Sgber} 436235537Sgber 437235537Sgberint 438235537Sgbernandbus_send_address(device_t dev, uint8_t address) 439235537Sgber{ 440235537Sgber int err; 441235537Sgber 442235537Sgber if ((err = NFC_SEND_ADDRESS(device_get_parent(dev), address))) 443235537Sgber nand_debug(NDBG_BUS,"Err: Could not send address %x, err %x", 444235537Sgber address, err); 445235537Sgber 446235537Sgber return (err); 447235537Sgber} 448235537Sgber 449235537Sgberint 450235537Sgbernandbus_start_command(device_t dev) 451235537Sgber{ 452235537Sgber int err; 453235537Sgber 454235537Sgber if ((err = NFC_START_COMMAND(device_get_parent(dev)))) 455235537Sgber nand_debug(NDBG_BUS,"Err: Could not start command, err %x", 456235537Sgber err); 457235537Sgber 458235537Sgber return (err); 459235537Sgber} 460235537Sgber 461235537Sgbervoid 462235537Sgbernandbus_read_buffer(device_t dev, void *buf, uint32_t len) 463235537Sgber{ 464235537Sgber 465235537Sgber NFC_READ_BUF(device_get_parent(dev), buf, len); 466235537Sgber} 467235537Sgber 468235537Sgbervoid 469235537Sgbernandbus_write_buffer(device_t dev, void *buf, uint32_t len) 470235537Sgber{ 471235537Sgber 472235537Sgber NFC_WRITE_BUF(device_get_parent(dev), buf, len); 473235537Sgber} 474235537Sgber 475235537Sgberint 476235537Sgbernandbus_get_status(device_t dev, uint8_t *status) 477235537Sgber{ 478235537Sgber int err; 479235537Sgber 480235537Sgber if ((err = NANDBUS_SEND_COMMAND(dev, NAND_CMD_STATUS))) 481235537Sgber return (err); 482235537Sgber if ((err = NANDBUS_START_COMMAND(dev))) 483235537Sgber return (err); 484235537Sgber 485235537Sgber *status = NFC_READ_BYTE(device_get_parent(dev)); 486235537Sgber 487235537Sgber return (0); 488235537Sgber} 489235537Sgber 490235537Sgberint 491235537Sgbernandbus_wait_ready(device_t dev, uint8_t *status) 492235537Sgber{ 493235537Sgber struct timeval tv, tv2; 494235537Sgber 495235537Sgber tv2.tv_sec = 0; 496235537Sgber tv2.tv_usec = 50 * 5000; /* 10ms */ 497235537Sgber 498235537Sgber getmicrotime(&tv); 499235537Sgber timevaladd(&tv, &tv2); 500235537Sgber 501235537Sgber do { 502235537Sgber if (NANDBUS_GET_STATUS(dev, status)) 503235537Sgber return (ENXIO); 504235537Sgber 505235537Sgber if (*status & NAND_STATUS_RDY) 506235537Sgber return (0); 507235537Sgber 508235537Sgber getmicrotime(&tv2); 509235537Sgber } while (timevalcmp(&tv2, &tv, <=)); 510235537Sgber 511235537Sgber return (EBUSY); 512235537Sgber} 513235537Sgber 514235537Sgberint 515235537Sgbernandbus_get_ecc(device_t dev, void *buf, uint32_t pagesize, void *ecc, 516235537Sgber int *needwrite) 517235537Sgber{ 518235537Sgber 519235537Sgber return (NFC_GET_ECC(device_get_parent(dev), buf, pagesize, ecc, needwrite)); 520235537Sgber} 521235537Sgber 522235537Sgberint 523235537Sgbernandbus_correct_ecc(device_t dev, void *buf, int pagesize, void *readecc, 524235537Sgber void *calcecc) 525235537Sgber{ 526235537Sgber 527235537Sgber return (NFC_CORRECT_ECC(device_get_parent(dev), buf, pagesize, 528235537Sgber readecc, calcecc)); 529235537Sgber} 530235537Sgber 531