1238046Smarcel/*- 2238046Smarcel * Copyright (C) 2012 Juniper Networks, Inc. 3238046Smarcel * Copyright (C) 2009-2012 Semihalf 4238046Smarcel * All rights reserved. 5238046Smarcel * 6238046Smarcel * Redistribution and use in source and binary forms, with or without 7238046Smarcel * modification, are permitted provided that the following conditions 8238046Smarcel * are met: 9238046Smarcel * 1. Redistributions of source code must retain the above copyright 10238046Smarcel * notice, this list of conditions and the following disclaimer. 11238046Smarcel * 2. Redistributions in binary form must reproduce the above copyright 12238046Smarcel * notice, this list of conditions and the following disclaimer in the 13238046Smarcel * documentation and/or other materials provided with the distribution. 14238046Smarcel * 15238046Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16238046Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17238046Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18238046Smarcel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19238046Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20238046Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21238046Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22238046Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23238046Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24238046Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25238046Smarcel * SUCH DAMAGE. 26238046Smarcel */ 27238046Smarcel/* 28238046Smarcel * TODO : 29238046Smarcel * 30238046Smarcel * -- test support for small pages 31238046Smarcel * -- support for reading ONFI parameters 32238046Smarcel * -- support for cached and interleaving commands 33238046Smarcel * -- proper setting of AL bits in FMR 34238046Smarcel */ 35238046Smarcel 36238046Smarcel#include <sys/cdefs.h> 37238046Smarcel__FBSDID("$FreeBSD$"); 38238046Smarcel 39238046Smarcel#include <sys/param.h> 40238046Smarcel#include <sys/systm.h> 41238046Smarcel#include <sys/proc.h> 42238046Smarcel#include <sys/bus.h> 43238046Smarcel#include <sys/conf.h> 44238046Smarcel#include <sys/kernel.h> 45238046Smarcel#include <sys/module.h> 46238046Smarcel#include <sys/malloc.h> 47238046Smarcel#include <sys/rman.h> 48238046Smarcel#include <sys/sysctl.h> 49238046Smarcel#include <sys/time.h> 50238046Smarcel#include <sys/kdb.h> 51238046Smarcel 52238046Smarcel#include <machine/bus.h> 53238046Smarcel#include <machine/fdt.h> 54238046Smarcel 55238046Smarcel#include <dev/ofw/ofw_bus.h> 56238046Smarcel#include <dev/ofw/ofw_bus_subr.h> 57238046Smarcel 58238046Smarcel#include <powerpc/mpc85xx/lbc.h> 59238046Smarcel 60238046Smarcel#include <dev/nand/nand.h> 61238046Smarcel#include <dev/nand/nandbus.h> 62238046Smarcel 63238046Smarcel#include "nfc_fsl.h" 64238046Smarcel 65238046Smarcel#include "nfc_if.h" 66238046Smarcel 67238046Smarcel#define LBC_READ(regname) lbc_read_reg(dev, (LBC85XX_ ## regname)) 68238046Smarcel#define LBC_WRITE(regname, val) lbc_write_reg(dev, (LBC85XX_ ## regname), val) 69238046Smarcel 70238046Smarcelenum addr_type { 71238046Smarcel ADDR_NONE, 72238046Smarcel ADDR_ID, 73238046Smarcel ADDR_ROW, 74238046Smarcel ADDR_ROWCOL 75238046Smarcel}; 76238046Smarcel 77238046Smarcelstruct fsl_nfc_fcm { 78238046Smarcel /* Read-only after initialization */ 79238046Smarcel uint32_t reg_fmr; 80238046Smarcel 81238046Smarcel /* To be preserved across "start_command" */ 82238046Smarcel u_int buf_ofs; 83238046Smarcel u_int read_ptr; 84238046Smarcel u_int status:1; 85238046Smarcel 86238046Smarcel /* Command state -- cleared by "start_command" */ 87238046Smarcel uint32_t fcm_startzero; 88238046Smarcel uint32_t reg_fcr; 89238046Smarcel uint32_t reg_fir; 90238046Smarcel uint32_t reg_mdr; 91238046Smarcel uint32_t reg_fbcr; 92238046Smarcel uint32_t reg_fbar; 93238046Smarcel uint32_t reg_fpar; 94238046Smarcel u_int cmdnr; 95238046Smarcel u_int opnr; 96238046Smarcel u_int pg_ofs; 97238046Smarcel enum addr_type addr_type; 98238046Smarcel u_int addr_bytes; 99238046Smarcel u_int row_addr; 100238046Smarcel u_int column_addr; 101238046Smarcel u_int data_fir:8; 102238046Smarcel uint32_t fcm_endzero; 103238046Smarcel}; 104238046Smarcel 105238046Smarcelstruct fsl_nand_softc { 106238046Smarcel struct nand_softc nand_dev; 107238046Smarcel device_t dev; 108238046Smarcel struct resource *res; 109238046Smarcel int rid; /* Resourceid */ 110238046Smarcel struct lbc_devinfo *dinfo; 111238046Smarcel struct fsl_nfc_fcm fcm; 112238046Smarcel uint8_t col_cycles; 113238046Smarcel uint8_t row_cycles; 114238046Smarcel uint16_t pgsz; /* Page size */ 115238046Smarcel}; 116238046Smarcel 117238046Smarcelstatic int fsl_nand_attach(device_t dev); 118238046Smarcelstatic int fsl_nand_probe(device_t dev); 119238046Smarcelstatic int fsl_nand_detach(device_t dev); 120238046Smarcel 121238046Smarcelstatic int fsl_nfc_select_cs(device_t dev, uint8_t cs); 122238046Smarcelstatic int fsl_nfc_read_rnb(device_t dev); 123238046Smarcelstatic int fsl_nfc_send_command(device_t dev, uint8_t command); 124238046Smarcelstatic int fsl_nfc_send_address(device_t dev, uint8_t address); 125238046Smarcelstatic uint8_t fsl_nfc_read_byte(device_t dev); 126238046Smarcelstatic int fsl_nfc_start_command(device_t dev); 127238046Smarcelstatic void fsl_nfc_read_buf(device_t dev, void *buf, uint32_t len); 128238046Smarcelstatic void fsl_nfc_write_buf(device_t dev, void *buf, uint32_t len); 129238046Smarcel 130238046Smarcelstatic device_method_t fsl_nand_methods[] = { 131238046Smarcel DEVMETHOD(device_probe, fsl_nand_probe), 132238046Smarcel DEVMETHOD(device_attach, fsl_nand_attach), 133238046Smarcel DEVMETHOD(device_detach, fsl_nand_detach), 134238046Smarcel 135238046Smarcel DEVMETHOD(nfc_select_cs, fsl_nfc_select_cs), 136238046Smarcel DEVMETHOD(nfc_read_rnb, fsl_nfc_read_rnb), 137238046Smarcel DEVMETHOD(nfc_start_command, fsl_nfc_start_command), 138238046Smarcel DEVMETHOD(nfc_send_command, fsl_nfc_send_command), 139238046Smarcel DEVMETHOD(nfc_send_address, fsl_nfc_send_address), 140238046Smarcel DEVMETHOD(nfc_read_byte, fsl_nfc_read_byte), 141238046Smarcel DEVMETHOD(nfc_read_buf, fsl_nfc_read_buf), 142238046Smarcel DEVMETHOD(nfc_write_buf, fsl_nfc_write_buf), 143238046Smarcel { 0, 0 }, 144238046Smarcel}; 145238046Smarcel 146238046Smarcelstatic driver_t fsl_nand_driver = { 147238046Smarcel "nand", 148238046Smarcel fsl_nand_methods, 149238046Smarcel sizeof(struct fsl_nand_softc), 150238046Smarcel}; 151238046Smarcel 152238046Smarcelstatic devclass_t fsl_nand_devclass; 153238046Smarcel 154238046SmarcelDRIVER_MODULE(fsl_nand, lbc, fsl_nand_driver, fsl_nand_devclass, 155238046Smarcel 0, 0); 156238046Smarcel 157238046Smarcelstatic int fsl_nand_build_address(device_t dev, uint32_t page, uint32_t column); 158238046Smarcelstatic int fsl_nand_chip_preprobe(device_t dev, struct nand_id *id); 159238046Smarcel 160238046Smarcel#ifdef NAND_DEBUG_TIMING 161238046Smarcelstatic device_t fcm_devs[8]; 162238046Smarcel#endif 163238046Smarcel 164238046Smarcel#define CMD_SHIFT(cmd_num) (24 - ((cmd_num) * 8)) 165238046Smarcel#define OP_SHIFT(op_num) (28 - ((op_num) * 4)) 166238046Smarcel 167238046Smarcel#define FSL_LARGE_PAGE_SIZE (2112) 168238046Smarcel#define FSL_SMALL_PAGE_SIZE (528) 169238046Smarcel 170238046Smarcelstatic void 171238046Smarcelfsl_nand_init_regs(struct fsl_nand_softc *sc) 172238046Smarcel{ 173238046Smarcel uint32_t or_v, br_v; 174238046Smarcel device_t dev; 175238046Smarcel 176238046Smarcel dev = sc->dev; 177238046Smarcel 178238046Smarcel sc->fcm.reg_fmr = (15 << FMR_CWTO_SHIFT); 179238046Smarcel 180238046Smarcel /* 181238046Smarcel * Setup 4 row cycles and hope that chip ignores superfluous address 182238046Smarcel * bytes. 183238046Smarcel */ 184238046Smarcel sc->fcm.reg_fmr |= (2 << FMR_AL_SHIFT); 185238046Smarcel 186238046Smarcel /* Reprogram BR(x) */ 187238046Smarcel br_v = lbc_read_reg(dev, LBC85XX_BR(sc->dinfo->di_bank)); 188238046Smarcel br_v &= 0xffff8000; 189238046Smarcel br_v |= 1 << 11; /* 8-bit port size */ 190238046Smarcel br_v |= 0 << 9; /* No ECC checking and generation */ 191238046Smarcel br_v |= 1 << 5; /* FCM machine */ 192238046Smarcel br_v |= 1; /* Valid */ 193238046Smarcel lbc_write_reg(dev, LBC85XX_BR(sc->dinfo->di_bank), br_v); 194238046Smarcel 195238046Smarcel /* Reprogram OR(x) */ 196238046Smarcel or_v = lbc_read_reg(dev, LBC85XX_OR(sc->dinfo->di_bank)); 197238046Smarcel or_v &= 0xfffffc00; 198238046Smarcel or_v |= 0x03AE; /* Default POR timing */ 199238046Smarcel lbc_write_reg(dev, LBC85XX_OR(sc->dinfo->di_bank), or_v); 200238046Smarcel 201238046Smarcel if (or_v & OR_FCM_PAGESIZE) { 202238046Smarcel sc->pgsz = FSL_LARGE_PAGE_SIZE; 203238046Smarcel sc->col_cycles = 2; 204238046Smarcel nand_debug(NDBG_DRV, "%s: large page NAND device at #%d", 205238046Smarcel device_get_nameunit(dev), sc->dinfo->di_bank); 206238046Smarcel } else { 207238046Smarcel sc->pgsz = FSL_SMALL_PAGE_SIZE; 208238046Smarcel sc->col_cycles = 1; 209238046Smarcel nand_debug(NDBG_DRV, "%s: small page NAND device at #%d", 210238046Smarcel device_get_nameunit(dev), sc->dinfo->di_bank); 211238046Smarcel } 212238046Smarcel} 213238046Smarcel 214238046Smarcelstatic int 215238046Smarcelfsl_nand_probe(device_t dev) 216238046Smarcel{ 217238046Smarcel 218238046Smarcel if (!ofw_bus_is_compatible(dev, "fsl,elbc-fcm-nand")) 219238046Smarcel return (ENXIO); 220238046Smarcel 221238046Smarcel device_set_desc(dev, "Freescale localbus FCM Controller"); 222238046Smarcel return (BUS_PROBE_DEFAULT); 223238046Smarcel} 224238046Smarcel 225238046Smarcelstatic int 226238046Smarcelfsl_nand_attach(device_t dev) 227238046Smarcel{ 228238046Smarcel struct fsl_nand_softc *sc; 229238046Smarcel struct nand_id id; 230238046Smarcel struct nand_params *param; 231238046Smarcel uint32_t num_pages; 232238046Smarcel 233238046Smarcel sc = device_get_softc(dev); 234238046Smarcel sc->dev = dev; 235238046Smarcel sc->dinfo = device_get_ivars(dev); 236238046Smarcel 237238046Smarcel sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, 238238046Smarcel RF_ACTIVE); 239238046Smarcel if (sc->res == NULL) { 240238046Smarcel device_printf(dev, "could not allocate resources!\n"); 241238046Smarcel return (ENXIO); 242238046Smarcel } 243238046Smarcel 244238046Smarcel bzero(&sc->fcm, sizeof(sc->fcm)); 245238046Smarcel 246238046Smarcel /* Init register and check if HW ECC turned on */ 247238046Smarcel fsl_nand_init_regs(sc); 248238046Smarcel 249238046Smarcel /* Chip is probed, so determine number of row address cycles */ 250238046Smarcel fsl_nand_chip_preprobe(dev, &id); 251238046Smarcel param = nand_get_params(&id); 252238046Smarcel if (param != NULL) { 253238046Smarcel num_pages = (param->chip_size << 20) / param->page_size; 254238046Smarcel while(num_pages) { 255238046Smarcel sc->row_cycles++; 256238046Smarcel num_pages >>= 8; 257238046Smarcel } 258238046Smarcel 259238046Smarcel sc->fcm.reg_fmr &= ~(FMR_AL); 260238046Smarcel sc->fcm.reg_fmr |= (sc->row_cycles - 2) << FMR_AL_SHIFT; 261238046Smarcel } 262238046Smarcel 263238046Smarcel nand_init(&sc->nand_dev, dev, NAND_ECC_SOFT, 0, 0, NULL, NULL); 264238046Smarcel 265238046Smarcel#ifdef NAND_DEBUG_TIMING 266238046Smarcel fcm_devs[sc->dinfo->di_bank] = dev; 267238046Smarcel#endif 268238046Smarcel 269238046Smarcel return (nandbus_create(dev)); 270238046Smarcel} 271238046Smarcel 272238046Smarcelstatic int 273238046Smarcelfsl_nand_detach(device_t dev) 274238046Smarcel{ 275238046Smarcel struct fsl_nand_softc *sc; 276238046Smarcel 277238046Smarcel sc = device_get_softc(dev); 278238046Smarcel 279238046Smarcel if (sc->res != NULL) 280238046Smarcel bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); 281238046Smarcel 282238046Smarcel return (0); 283238046Smarcel} 284238046Smarcel 285238046Smarcelstatic int 286238046Smarcelfsl_nfc_select_cs(device_t dev, uint8_t cs) 287238046Smarcel{ 288238046Smarcel 289238046Smarcel // device_printf(dev, "%s(cs=%u)\n", __func__, cs); 290238046Smarcel return ((cs > 0) ? EINVAL : 0); 291238046Smarcel} 292238046Smarcel 293238046Smarcelstatic int 294238046Smarcelfsl_nfc_read_rnb(device_t dev) 295238046Smarcel{ 296238046Smarcel 297238046Smarcel // device_printf(dev, "%s()\n", __func__); 298238046Smarcel return (0); 299238046Smarcel} 300238046Smarcel 301238046Smarcelstatic int 302238046Smarcelfsl_nfc_send_command(device_t dev, uint8_t command) 303238046Smarcel{ 304238046Smarcel struct fsl_nand_softc *sc; 305238046Smarcel struct fsl_nfc_fcm *fcm; 306238046Smarcel uint8_t fir_op; 307238046Smarcel 308238046Smarcel // device_printf(dev, "%s(command=%u)\n", __func__, command); 309238046Smarcel 310238046Smarcel sc = device_get_softc(dev); 311238046Smarcel fcm = &sc->fcm; 312238046Smarcel 313238046Smarcel if (command == NAND_CMD_PROG_END) { 314238046Smarcel fcm->reg_fir |= (FIR_OP_WB << OP_SHIFT(fcm->opnr)); 315238046Smarcel fcm->opnr++; 316238046Smarcel } 317238046Smarcel fcm->reg_fcr |= command << CMD_SHIFT(fcm->cmdnr); 318238046Smarcel fir_op = (fcm->cmdnr == 0) ? FIR_OP_CW0 : FIR_OP_CM(fcm->cmdnr); 319238046Smarcel fcm->cmdnr++; 320238046Smarcel 321238046Smarcel fcm->reg_fir |= (fir_op << OP_SHIFT(fcm->opnr)); 322238046Smarcel fcm->opnr++; 323238046Smarcel 324238046Smarcel switch (command) { 325238046Smarcel case NAND_CMD_READ_ID: 326238046Smarcel fcm->data_fir = FIR_OP_RBW; 327238046Smarcel fcm->addr_type = ADDR_ID; 328238046Smarcel break; 329238046Smarcel case NAND_CMD_SMALLOOB: 330238046Smarcel fcm->pg_ofs += 256; 331238046Smarcel /*FALLTHROUGH*/ 332238046Smarcel case NAND_CMD_SMALLB: 333238046Smarcel fcm->pg_ofs += 256; 334238046Smarcel /*FALLTHROUGH*/ 335238046Smarcel case NAND_CMD_READ: /* NAND_CMD_SMALLA */ 336238046Smarcel fcm->data_fir = FIR_OP_RBW; 337238046Smarcel fcm->addr_type = ADDR_ROWCOL; 338238046Smarcel break; 339238046Smarcel case NAND_CMD_STATUS: 340238046Smarcel fcm->data_fir = FIR_OP_RS; 341238046Smarcel fcm->status = 1; 342238046Smarcel break; 343238046Smarcel case NAND_CMD_ERASE: 344238046Smarcel fcm->addr_type = ADDR_ROW; 345238046Smarcel break; 346238046Smarcel case NAND_CMD_PROG: 347238046Smarcel fcm->addr_type = ADDR_ROWCOL; 348238046Smarcel break; 349238046Smarcel } 350238046Smarcel return (0); 351238046Smarcel} 352238046Smarcel 353238046Smarcelstatic int 354238046Smarcelfsl_nfc_send_address(device_t dev, uint8_t addr) 355238046Smarcel{ 356238046Smarcel struct fsl_nand_softc *sc; 357238046Smarcel struct fsl_nfc_fcm *fcm; 358238046Smarcel uint32_t addr_bits; 359238046Smarcel 360238046Smarcel // device_printf(dev, "%s(address=%u)\n", __func__, addr); 361238046Smarcel 362238046Smarcel sc = device_get_softc(dev); 363238046Smarcel fcm = &sc->fcm; 364238046Smarcel 365238046Smarcel KASSERT(fcm->addr_type != ADDR_NONE, 366238046Smarcel ("controller doesn't expect address cycle")); 367238046Smarcel 368238046Smarcel addr_bits = addr; 369238046Smarcel 370238046Smarcel if (fcm->addr_type == ADDR_ID) { 371238046Smarcel fcm->reg_fir |= (FIR_OP_UA << OP_SHIFT(fcm->opnr)); 372238046Smarcel fcm->opnr++; 373238046Smarcel 374238046Smarcel fcm->reg_fbcr = 5; 375238046Smarcel fcm->reg_fbar = 0; 376238046Smarcel fcm->reg_fpar = 0; 377238046Smarcel fcm->reg_mdr = addr_bits; 378238046Smarcel fcm->buf_ofs = 0; 379238046Smarcel fcm->read_ptr = 0; 380238046Smarcel return (0); 381238046Smarcel } 382238046Smarcel 383238046Smarcel if (fcm->addr_type == ADDR_ROW) { 384238046Smarcel addr_bits <<= fcm->addr_bytes * 8; 385238046Smarcel fcm->row_addr |= addr_bits; 386238046Smarcel fcm->addr_bytes++; 387238046Smarcel if (fcm->addr_bytes < sc->row_cycles) 388238046Smarcel return (0); 389238046Smarcel } else { 390238046Smarcel if (fcm->addr_bytes < sc->col_cycles) { 391238046Smarcel addr_bits <<= fcm->addr_bytes * 8; 392238046Smarcel fcm->column_addr |= addr_bits; 393238046Smarcel } else { 394238046Smarcel addr_bits <<= (fcm->addr_bytes - sc->col_cycles) * 8; 395238046Smarcel fcm->row_addr |= addr_bits; 396238046Smarcel } 397238046Smarcel fcm->addr_bytes++; 398238046Smarcel if (fcm->addr_bytes < (sc->row_cycles + sc->col_cycles)) 399238046Smarcel return (0); 400238046Smarcel } 401238046Smarcel 402238046Smarcel return (fsl_nand_build_address(dev, fcm->row_addr, fcm->column_addr)); 403238046Smarcel} 404238046Smarcel 405238046Smarcelstatic int 406238046Smarcelfsl_nand_build_address(device_t dev, uint32_t row, uint32_t column) 407238046Smarcel{ 408238046Smarcel struct fsl_nand_softc *sc; 409238046Smarcel struct fsl_nfc_fcm *fcm; 410238046Smarcel uint32_t byte_count = 0; 411238046Smarcel uint32_t block_address = 0; 412238046Smarcel uint32_t page_address = 0; 413238046Smarcel 414238046Smarcel sc = device_get_softc(dev); 415238046Smarcel fcm = &sc->fcm; 416238046Smarcel 417238046Smarcel fcm->read_ptr = 0; 418238046Smarcel fcm->buf_ofs = 0; 419238046Smarcel 420238046Smarcel if (fcm->addr_type == ADDR_ROWCOL) { 421238046Smarcel fcm->reg_fir |= (FIR_OP_CA << OP_SHIFT(fcm->opnr)); 422238046Smarcel fcm->opnr++; 423238046Smarcel 424238046Smarcel column += fcm->pg_ofs; 425238046Smarcel fcm->pg_ofs = 0; 426238046Smarcel 427238046Smarcel page_address |= column; 428238046Smarcel 429238046Smarcel if (column != 0) { 430238046Smarcel byte_count = sc->pgsz - column; 431238046Smarcel fcm->read_ptr = column; 432238046Smarcel } 433238046Smarcel } 434238046Smarcel 435238046Smarcel fcm->reg_fir |= (FIR_OP_PA << OP_SHIFT(fcm->opnr)); 436238046Smarcel fcm->opnr++; 437238046Smarcel 438238046Smarcel if (sc->pgsz == FSL_LARGE_PAGE_SIZE) { 439238046Smarcel block_address = row >> 6; 440238046Smarcel page_address |= ((row << FPAR_LP_PI_SHIFT) & FPAR_LP_PI); 441238046Smarcel fcm->buf_ofs = (row & 1) * 4096; 442238046Smarcel } else { 443238046Smarcel block_address = row >> 5; 444238046Smarcel page_address |= ((row << FPAR_SP_PI_SHIFT) & FPAR_SP_PI); 445238046Smarcel fcm->buf_ofs = (row & 7) * 1024; 446238046Smarcel } 447238046Smarcel 448238046Smarcel fcm->reg_fbcr = byte_count; 449238046Smarcel fcm->reg_fbar = block_address; 450238046Smarcel fcm->reg_fpar = page_address; 451238046Smarcel return (0); 452238046Smarcel} 453238046Smarcel 454238046Smarcelstatic int 455238046Smarcelfsl_nfc_start_command(device_t dev) 456238046Smarcel{ 457238046Smarcel struct fsl_nand_softc *sc; 458238046Smarcel struct fsl_nfc_fcm *fcm; 459238046Smarcel uint32_t fmr, ltesr_v; 460238046Smarcel int error, timeout; 461238046Smarcel 462238046Smarcel // device_printf(dev, "%s()\n", __func__); 463238046Smarcel 464238046Smarcel sc = device_get_softc(dev); 465238046Smarcel fcm = &sc->fcm; 466238046Smarcel 467238046Smarcel fmr = fcm->reg_fmr | FMR_OP; 468238046Smarcel 469238046Smarcel if (fcm->data_fir) 470238046Smarcel fcm->reg_fir |= (fcm->data_fir << OP_SHIFT(fcm->opnr)); 471238046Smarcel 472238046Smarcel LBC_WRITE(FIR, fcm->reg_fir); 473238046Smarcel LBC_WRITE(FCR, fcm->reg_fcr); 474238046Smarcel 475238046Smarcel LBC_WRITE(FMR, fmr); 476238046Smarcel 477238046Smarcel LBC_WRITE(FBCR, fcm->reg_fbcr); 478238046Smarcel LBC_WRITE(FBAR, fcm->reg_fbar); 479238046Smarcel LBC_WRITE(FPAR, fcm->reg_fpar); 480238046Smarcel 481238046Smarcel if (fcm->addr_type == ADDR_ID) 482238046Smarcel LBC_WRITE(MDR, fcm->reg_mdr); 483238046Smarcel 484238046Smarcel nand_debug(NDBG_DRV, "BEFORE:\nFMR=%#x, FIR=%#x, FCR=%#x", fmr, 485238046Smarcel fcm->reg_fir, fcm->reg_fcr); 486238046Smarcel nand_debug(NDBG_DRV, "MDR=%#x, FBAR=%#x, FPAR=%#x, FBCR=%#x", 487238046Smarcel LBC_READ(MDR), fcm->reg_fbar, fcm->reg_fpar, fcm->reg_fbcr); 488238046Smarcel 489238046Smarcel LBC_WRITE(LSOR, sc->dinfo->di_bank); 490238046Smarcel 491238046Smarcel timeout = (cold) ? FSL_FCM_WAIT_TIMEOUT : ~0; 492238046Smarcel error = 0; 493238046Smarcel ltesr_v = LBC_READ(LTESR); 494238046Smarcel while (!error && (ltesr_v & LTESR_CC) == 0) { 495238046Smarcel if (cold) { 496238046Smarcel DELAY(1000); 497238046Smarcel timeout--; 498238046Smarcel if (timeout < 0) 499238046Smarcel error = EWOULDBLOCK; 500238046Smarcel } else 501238046Smarcel error = tsleep(device_get_parent(sc->dev), PRIBIO, 502238046Smarcel "nfcfsl", hz); 503238046Smarcel ltesr_v = LBC_READ(LTESR); 504238046Smarcel } 505238046Smarcel if (error) 506238046Smarcel nand_debug(NDBG_DRV, "Command complete wait timeout\n"); 507238046Smarcel 508238046Smarcel nand_debug(NDBG_DRV, "AFTER:\nLTESR=%#x, LTEDR=%#x, LTEIR=%#x," 509238046Smarcel " LTEATR=%#x, LTEAR=%#x, LTECCR=%#x", ltesr_v, 510238046Smarcel LBC_READ(LTEDR), LBC_READ(LTEIR), LBC_READ(LTEATR), 511238046Smarcel LBC_READ(LTEAR), LBC_READ(LTECCR)); 512238046Smarcel 513238046Smarcel bzero(&fcm->fcm_startzero, 514238046Smarcel __rangeof(struct fsl_nfc_fcm, fcm_startzero, fcm_endzero)); 515238046Smarcel 516238046Smarcel if (fcm->status) 517238046Smarcel sc->fcm.reg_mdr = LBC_READ(MDR); 518238046Smarcel 519238046Smarcel /* Even if timeout occured, we should perform steps below */ 520238046Smarcel LBC_WRITE(LTESR, ltesr_v); 521238046Smarcel LBC_WRITE(LTEATR, 0); 522238046Smarcel 523238046Smarcel return (error); 524238046Smarcel} 525238046Smarcel 526238046Smarcelstatic uint8_t 527238046Smarcelfsl_nfc_read_byte(device_t dev) 528238046Smarcel{ 529238046Smarcel struct fsl_nand_softc *sc = device_get_softc(dev); 530238046Smarcel uint32_t offset; 531238046Smarcel 532238046Smarcel // device_printf(dev, "%s()\n", __func__); 533238046Smarcel 534238046Smarcel /* 535238046Smarcel * LBC controller allows us to read status into a MDR instead of FCM 536238046Smarcel * buffer. If last operation requested before read_byte() was STATUS, 537238046Smarcel * then return MDR instead of reading a single byte from a buffer. 538238046Smarcel */ 539238046Smarcel if (sc->fcm.status) { 540238046Smarcel sc->fcm.status = 0; 541238046Smarcel return (sc->fcm.reg_mdr); 542238046Smarcel } 543238046Smarcel 544238046Smarcel KASSERT(sc->fcm.read_ptr < sc->pgsz, 545238046Smarcel ("Attempt to read beyond buffer %x %x", sc->fcm.read_ptr, 546238046Smarcel sc->pgsz)); 547238046Smarcel 548238046Smarcel offset = sc->fcm.buf_ofs + sc->fcm.read_ptr; 549238046Smarcel sc->fcm.read_ptr++; 550238046Smarcel return (bus_read_1(sc->res, offset)); 551238046Smarcel} 552238046Smarcel 553238046Smarcelstatic void 554238046Smarcelfsl_nfc_read_buf(device_t dev, void *buf, uint32_t len) 555238046Smarcel{ 556238046Smarcel struct fsl_nand_softc *sc = device_get_softc(dev); 557238046Smarcel uint32_t offset; 558238046Smarcel int bytesleft = 0; 559238046Smarcel 560238046Smarcel // device_printf(dev, "%s(buf=%p, len=%u)\n", __func__, buf, len); 561238046Smarcel 562238046Smarcel nand_debug(NDBG_DRV, "REQUEST OF 0x%0x B (BIB=0x%0x, NTR=0x%0x)", 563238046Smarcel len, sc->pgsz, sc->fcm.read_ptr); 564238046Smarcel 565238046Smarcel bytesleft = MIN((unsigned int)len, sc->pgsz - sc->fcm.read_ptr); 566238046Smarcel 567238046Smarcel offset = sc->fcm.buf_ofs + sc->fcm.read_ptr; 568238046Smarcel bus_read_region_1(sc->res, offset, buf, bytesleft); 569238046Smarcel sc->fcm.read_ptr += bytesleft; 570238046Smarcel} 571238046Smarcel 572238046Smarcelstatic void 573238046Smarcelfsl_nfc_write_buf(device_t dev, void *buf, uint32_t len) 574238046Smarcel{ 575238046Smarcel struct fsl_nand_softc *sc = device_get_softc(dev); 576238046Smarcel uint32_t offset; 577238046Smarcel int bytesleft = 0; 578238046Smarcel 579238046Smarcel // device_printf(dev, "%s(buf=%p, len=%u)\n", __func__, buf, len); 580238046Smarcel 581238046Smarcel KASSERT(len <= sc->pgsz - sc->fcm.read_ptr, 582238046Smarcel ("Attempt to write beyond buffer")); 583238046Smarcel 584238046Smarcel bytesleft = MIN((unsigned int)len, sc->pgsz - sc->fcm.read_ptr); 585238046Smarcel 586238046Smarcel nand_debug(NDBG_DRV, "REQUEST TO WRITE 0x%0x (BIB=0x%0x, NTR=0x%0x)", 587238046Smarcel bytesleft, sc->pgsz, sc->fcm.read_ptr); 588238046Smarcel 589238046Smarcel offset = sc->fcm.buf_ofs + sc->fcm.read_ptr; 590238046Smarcel bus_write_region_1(sc->res, offset, buf, bytesleft); 591238046Smarcel sc->fcm.read_ptr += bytesleft; 592238046Smarcel} 593238046Smarcel 594238046Smarcelstatic int 595238046Smarcelfsl_nand_chip_preprobe(device_t dev, struct nand_id *id) 596238046Smarcel{ 597238046Smarcel 598238046Smarcel if (fsl_nfc_send_command(dev, NAND_CMD_RESET) != 0) 599238046Smarcel return (ENXIO); 600238046Smarcel 601238046Smarcel if (fsl_nfc_start_command(dev) != 0) 602238046Smarcel return (ENXIO); 603238046Smarcel 604238046Smarcel DELAY(1000); 605238046Smarcel 606238046Smarcel if (fsl_nfc_send_command(dev, NAND_CMD_READ_ID)) 607238046Smarcel return (ENXIO); 608238046Smarcel 609238046Smarcel if (fsl_nfc_send_address(dev, 0)) 610238046Smarcel return (ENXIO); 611238046Smarcel 612238046Smarcel if (fsl_nfc_start_command(dev) != 0) 613238046Smarcel return (ENXIO); 614238046Smarcel 615238046Smarcel DELAY(25); 616238046Smarcel 617238046Smarcel id->man_id = fsl_nfc_read_byte(dev); 618238046Smarcel id->dev_id = fsl_nfc_read_byte(dev); 619238046Smarcel 620238046Smarcel nand_debug(NDBG_DRV, "manufacturer id: %x chip id: %x", 621238046Smarcel id->man_id, id->dev_id); 622238046Smarcel 623238046Smarcel return (0); 624238046Smarcel} 625238046Smarcel 626238046Smarcel#ifdef NAND_DEBUG_TIMING 627238046Smarcel 628238046Smarcelstatic SYSCTL_NODE(_debug, OID_AUTO, fcm, CTLFLAG_RD, 0, "FCM timing"); 629238046Smarcel 630238046Smarcelstatic u_int csct = 1; /* 22: Chip select to command time (trlx). */ 631238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, csct, CTLFLAG_RW, &csct, 1, 632238046Smarcel "Chip select to command time: determines how far in advance -LCSn is " 633238046Smarcel "asserted prior to any bus activity during a NAND Flash access handled " 634238046Smarcel "by the FCM. This helps meet chip-select setup times for slow memories."); 635238046Smarcel 636238046Smarcelstatic u_int cst = 1; /* 23: Command setup time (trlx). */ 637238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, cst, CTLFLAG_RW, &cst, 1, 638238046Smarcel "Command setup time: determines the delay of -LFWE assertion relative to " 639238046Smarcel "the command, address, or data change when the external memory access " 640238046Smarcel "is handled by the FCM."); 641238046Smarcel 642238046Smarcelstatic u_int cht = 1; /* 24: Command hold time (trlx). */ 643238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, cht, CTLFLAG_RW, &cht, 1, 644238046Smarcel "Command hold time: determines the -LFWE negation prior to the command, " 645238046Smarcel "address, or data change when the external memory access is handled by " 646238046Smarcel "the FCM."); 647238046Smarcel 648238046Smarcelstatic u_int scy = 2; /* 25-27: Cycle length in bus clocks */ 649238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, scy, CTLFLAG_RW, &scy, 2, 650238046Smarcel "Cycle length in bus clocks: see RM"); 651238046Smarcel 652238046Smarcelstatic u_int rst = 1; /* 28: Read setup time (trlx). */ 653238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, rst, CTLFLAG_RW, &rst, 1, 654238046Smarcel "Read setup time: determines the delay of -LFRE assertion relative to " 655238046Smarcel "sampling of read data when the external memory access is handled by " 656238046Smarcel "the FCM."); 657238046Smarcel 658238046Smarcelstatic u_int trlx = 1; /* 29: Timing relaxed. */ 659238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, trlx, CTLFLAG_RW, &trlx, 1, 660238046Smarcel "Timing relaxed: modifies the settings of timing parameters for slow " 661238046Smarcel "memories. See RM"); 662238046Smarcel 663238046Smarcelstatic u_int ehtr = 1; /* 30: Extended hold time on read accesses. */ 664238046SmarcelSYSCTL_UINT(_debug_fcm, OID_AUTO, ehtr, CTLFLAG_RW, &ehtr, 1, 665238046Smarcel "Extended hold time on read accesses: indicates with TRLX how many " 666238046Smarcel "cycles are inserted between a read access from the current bank and " 667238046Smarcel "the next access."); 668238046Smarcel 669238046Smarcelstatic u_int 670238046Smarcelfsl_nand_get_timing(void) 671238046Smarcel{ 672238046Smarcel u_int timing; 673238046Smarcel 674238046Smarcel timing = ((csct & 1) << 9) | ((cst & 1) << 8) | ((cht & 1) << 7) | 675238046Smarcel ((scy & 7) << 4) | ((rst & 1) << 3) | ((trlx & 1) << 2) | 676238046Smarcel ((ehtr & 1) << 1); 677238046Smarcel 678238046Smarcel printf("nfc_fsl: timing = %u\n", timing); 679238046Smarcel return (timing); 680238046Smarcel} 681238046Smarcel 682238046Smarcelstatic int 683238046Smarcelfsl_sysctl_program(SYSCTL_HANDLER_ARGS) 684238046Smarcel{ 685238046Smarcel struct fsl_nand_softc *sc; 686238046Smarcel int error, i; 687238046Smarcel device_t dev; 688238046Smarcel uint32_t or_v; 689238046Smarcel 690238046Smarcel error = sysctl_wire_old_buffer(req, sizeof(int)); 691238046Smarcel if (error == 0) { 692238046Smarcel i = 0; 693238046Smarcel error = sysctl_handle_int(oidp, &i, 0, req); 694238046Smarcel } 695238046Smarcel if (error != 0 || req->newptr == NULL) 696238046Smarcel return (error); 697238046Smarcel 698238046Smarcel for (i = 0; i < 8; i++) { 699238046Smarcel dev = fcm_devs[i]; 700238046Smarcel if (dev == NULL) 701238046Smarcel continue; 702238046Smarcel sc = device_get_softc(dev); 703238046Smarcel 704238046Smarcel /* Reprogram OR(x) */ 705238046Smarcel or_v = lbc_read_reg(dev, LBC85XX_OR(sc->dinfo->di_bank)); 706238046Smarcel or_v &= 0xfffffc00; 707238046Smarcel or_v |= fsl_nand_get_timing(); 708238046Smarcel lbc_write_reg(dev, LBC85XX_OR(sc->dinfo->di_bank), or_v); 709238046Smarcel } 710238046Smarcel return (0); 711238046Smarcel} 712238046Smarcel 713238046SmarcelSYSCTL_PROC(_debug_fcm, OID_AUTO, program, CTLTYPE_INT | CTLFLAG_RW, NULL, 0, 714238046Smarcel fsl_sysctl_program, "I", "write to program FCM with current values"); 715238046Smarcel 716238046Smarcel#endif /* NAND_DEBUG_TIMING */ 717