Module Name: src Committed By: matt Date: Tue Nov 5 18:44:02 UTC 2013
Modified Files: src/sys/arch/mips/rmi [matt-nb5-mips64]: rmixl_nand_pci.c Log Message: Start of a nand for xlp3xx/xlp2xx. To generate a diff of this commit: cvs rdiff -u -r1.1.2.2 -r1.1.2.3 src/sys/arch/mips/rmi/rmixl_nand_pci.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/mips/rmi/rmixl_nand_pci.c diff -u src/sys/arch/mips/rmi/rmixl_nand_pci.c:1.1.2.2 src/sys/arch/mips/rmi/rmixl_nand_pci.c:1.1.2.3 --- src/sys/arch/mips/rmi/rmixl_nand_pci.c:1.1.2.2 Fri Dec 30 06:48:56 2011 +++ src/sys/arch/mips/rmi/rmixl_nand_pci.c Tue Nov 5 18:44:02 2013 @@ -27,16 +27,18 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include "locators.h" + #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: rmixl_nand_pci.c,v 1.1.2.2 2011/12/30 06:48:56 matt Exp $"); +__KERNEL_RCSID(1, "$NetBSD: rmixl_nand_pci.c,v 1.1.2.3 2013/11/05 18:44:02 matt Exp $"); #include <sys/param.h> +#include <sys/condvar.h> #include <sys/device.h> +#include <sys/mutex.h> #include <sys/bus.h> -#include "locators.h" - #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> #include <dev/pci/pcidevs.h> @@ -51,27 +53,118 @@ __KERNEL_RCSID(1, "$NetBSD: rmixl_nand_p static int xlnand_pci_match(device_t, cfdata_t, void *); static void xlnand_pci_attach(device_t, device_t, void *); +static int xlnand_pci_detach(device_t, int); + +static int xlnand_read_page(device_t, size_t, uint8_t *); +static int xlnand_program_page(device_t, size_t, const uint8_t *); + +static void xlnand_select(device_t, bool); +static void xlnand_command(device_t, uint8_t); +static void xlnand_address(device_t, uint8_t); +static void xlnand_busy(device_t); +static void xlnand_read_buf(device_t, void *, size_t); +static void xlnand_write_buf(device_t, const void *, size_t); + +static void xlnand_read_1(device_t, uint8_t *); +static void xlnand_read_2(device_t, uint16_t *); +static void xlnand_write_1(device_t, uint8_t); +static void xlnand_write_2(device_t, uint16_t); + +static int xlnand_intr(void *); + +struct xlnand_chip { + device_t xlch_nanddev; + kcondvar_t xlch_cv_ready; + + uint32_t xlch_cmds; + uint64_t xlch_addrs; + uint32_t xlch_data; + uint32_t xlch_int_mask; + uint8_t xlch_cmdshift; + uint8_t xlch_addrshift; + uint8_t xlch_num; + uint8_t xlch_datalen; + uint8_t xlch_chipnum; + + struct nand_interface xlch_nand_if; + char xlch_wmesg[16]; +}; struct xlnand_softc { - device_t sc_dev; - bus_space_tag_t sc_bst; - bus_space_handle_t sc_bsh; + device_t xlsc_dev; + bus_dma_tag_t xlsc_dmat; + bus_space_tag_t xlsc_bst; + bus_space_handle_t xlsc_bsh; + void *xlsc_ih; + uint8_t xlsc_buswidth; + + kcondvar_t xlsc_cv_available; + struct xlnand_chip *xlsc_active_chip; + + bus_dma_segment_t xlsc_xferseg; + bus_dmamap_t xlsc_xfermap; + void *xlsc_xferbuf; + + struct xlnand_chip xlsc_chips[8]; + + kmutex_t xlsc_intr_lock __aligned(64); + kmutex_t xlsc_wait_lock __aligned(64); +}; + +#define CMDSEQ1(a,b) (((ONFI_ ## b) << 8) | RMIXLP_NAND_CMD_ ## a) +#define CMDSEQ2(a,b,c) (((ONFI_ ## c) << 16) | CMDSEQ1(a,b)) +#define CMDSEQ3(a,b,c,d) (((ONFI_ ## d) << 14) | CMDSEQ2(a,b,c)) + +static const uint32_t xlnand_cmdseqs[] = { + CMDSEQ1(SEQ_0, RESET), + //CMDSEQ1(SEQ_0, SYNCRONOUS_RESET), + CMDSEQ1(SEQ_1, READ_ID), + CMDSEQ1(SEQ_2, READ_UNIQUE_ID), + CMDSEQ1(SEQ_2, READ_PARAMETER_PAGE), + CMDSEQ1(SEQ_2, GET_FEATURES), + CMDSEQ1(SEQ_3, SET_FEATURES), + //CMDSEQ1(SEQ_17, SET_FEATURES2), + CMDSEQ1(SEQ_4, READ_STATUS), + //CMDSEQ1(SEQ_5, SELECT_LUN_WITH_STATUS), + CMDSEQ2(SEQ_6, CHANGE_READ_COLUMN, CHANGE_READ_COLUMN_START), + //CMDSEQ2(SEQ_7, SELECT_CACHE_REGISTER, CHANGE_READ_COLUMN_START), + //CMDSEQ1(SEQ_8, CHANGE_WRITE_COLUMN), + //CMDSEQ1(SEQ_12, CHANGE_ROW_ADDRESS), + CMDSEQ2(SEQ_10, READ, READ_START), + CMDSEQ2(SEQ_10, READ, READ_CACHE_RANDOM), + CMDSEQ1(SEQ_11, READ_CACHE_SEQUENTIAL), + CMDSEQ1(SEQ_11, READ_CACHE_END), + CMDSEQ2(SEQ_12, READ, READ_INTERLEAVED), + //CMDSEQ2(SEQ_15, READ, READ, READ_START), + CMDSEQ2(SEQ_12, PAGE_PROGRAM, PAGE_PROGRAM_START), + CMDSEQ1(SEQ_13, PAGE_PROGRAM), + CMDSEQ2(SEQ_12, PAGE_PROGRAM, PAGE_CACHE_PROGRAM), + CMDSEQ2(SEQ_12, PAGE_PROGRAM, PAGE_PROGRAM_INTERLEAVED), + //CMDSEQ1(SEQ_0, WRITE_PAGE), + //CMDSEQ1(SEQ_0, WRITE_PAGE_CACHE), + //CMDSEQ1(SEQ_0, WRITE_PAGE_INTERLEAVED), + CMDSEQ2(SEQ_10, READ, READ_COPYBACK), + CMDSEQ2(SEQ_9, COPYBACK_PROGRAM, COPYBACK_PROGRAM_START), + CMDSEQ2(SEQ_12, COPYBACK_PROGRAM, COPYBACK_PROGRAM_INTERLEAVED), + CMDSEQ1(SEQ_13, COPYBACK_PROGRAM), + CMDSEQ2(SEQ_14, BLOCK_ERASE, BLOCK_ERASE_START), + CMDSEQ2(SEQ_14, BLOCK_ERASE, BLOCK_ERASE_INTERLEAVED), }; static inline uint32_t -xlnand_read_4(struct xlnand_softc *sc, bus_size_t off) +xlnand_read_4(struct xlnand_softc *xlsc, bus_size_t off) { - return bus_space_read_4(sc->sc_bst, sc->sc_bsh, off); + return bus_space_read_4(xlsc->xlsc_bst, xlsc->xlsc_bsh, off); } static inline void -xlnand_write_4(struct xlnand_softc *sc, bus_size_t off, uint32_t v) +xlnand_write_4(struct xlnand_softc *xlsc, bus_size_t off, uint32_t v) { - bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, v); + bus_space_write_4(xlsc->xlsc_bst, xlsc->xlsc_bsh, off, v); } CFATTACH_DECL_NEW(xlnand_pci, sizeof(struct xlnand_softc), - xlnand_pci_match, xlnand_pci_attach, NULL, NULL); + xlnand_pci_match, xlnand_pci_attach, xlnand_pci_detach, NULL); static int xlnand_pci_match(device_t parent, cfdata_t cf, void *aux) @@ -89,17 +182,24 @@ xlnand_pci_attach(device_t parent, devic { struct rmixl_config * const rcp = &rmixl_configuration; struct pci_attach_args * const pa = aux; - struct xlnand_softc * const sc = device_private(self); - // struct norbus_attach_args iba; - - sc->sc_dev = self; - sc->sc_bst = &rcp->rc_pci_ecfg_eb_memt; + struct xlnand_softc * const xlsc = device_private(self); + cfdata_t cf = device_cfdata(self); + const char * const xname = device_xname(self); + uint8_t csmask = (cf->cf_flags & 0xff ? cf->cf_flags & 0xff : 1); + uint32_t v; + + xlsc->xlsc_dev = self; + xlsc->xlsc_bst = &rcp->rc_pci_ecfg_eb_memt; + + mutex_init(&xlsc->xlsc_intr_lock, MUTEX_DEFAULT, IPL_BIO); + mutex_init(&xlsc->xlsc_wait_lock, MUTEX_DEFAULT, IPL_SOFTBIO); + cv_init(&xlsc->xlsc_cv_available, xname); /* * Why isn't this accessible via a BAR? */ - if (bus_space_subregion(sc->sc_bst, rcp->rc_pci_ecfg_eb_memh, - pa->pa_tag, 0, &sc->sc_bsh)) { + if (bus_space_subregion(xlsc->xlsc_bst, rcp->rc_pci_ecfg_eb_memh, + pa->pa_tag, 0, &xlsc->xlsc_bsh)) { aprint_error(": can't map registers\n"); return; } @@ -110,11 +210,435 @@ xlnand_pci_attach(device_t parent, devic * If a NAND is using non-0 RDY/BSY signals, we need to take control * of those from GPIO. */ - uint32_t r = xlnand_read_4(sc, RMIXLP_NAND_RDYBSY_SEL); + uint32_t r = xlnand_read_4(xlsc, RMIXLP_NAND_RDYBSY_SEL); for (r >>= 3; r != 0; r >>= 3) { u_int rdybsy = r & 7; if (rdybsy != 0) { rcp->rc_gpio_available &= ~__BIT(33 + rdybsy); } } + + /* + * determine buswidth + */ + v = bus_space_read_4(xlsc->xlsc_bst, xlsc->xlsc_bsh, RMIXLP_NAND_CTRL); + if (v & RMIXLP_NAND_CTRL_IOW) { + xlsc->xlsc_buswidth = 2; /* 16 bit */ + } else { + xlsc->xlsc_buswidth = 1; /* 8 bit */ + } + aprint_debug_dev(self, "bus width %d bits\n", 8 * xlsc->xlsc_buswidth); + + pci_intr_handle_t pcih; + + pci_intr_map(pa, &pcih); + + if (pci_intr_establish(pa->pa_pc, pcih, IPL_BIO, xlnand_intr, xlsc) == NULL) { + aprint_error_dev(self, "failed to establish interrupt\n"); + } else { + const char * const intrstr = pci_intr_string(pa->pa_pc, pcih); + aprint_normal_dev(self, "interrupting at %s\n", intrstr); + } + + for (size_t i = 0; (1 << i) <= csmask; i++) { + struct xlnand_chip * const xlch = &xlsc->xlsc_chips[i]; + if ((csmask & (1 << i)) == 0) + continue; + xlch->xlch_chipnum = i; + nand_init_interface(&xlch->xlch_nand_if); + xlch->xlch_nand_if.select = xlnand_select; + xlch->xlch_nand_if.command = xlnand_command; + xlch->xlch_nand_if.address = xlnand_address; + xlch->xlch_nand_if.read_buf_1 = xlnand_read_buf; + xlch->xlch_nand_if.read_buf_2 = xlnand_read_buf; + xlch->xlch_nand_if.read_1 = xlnand_read_1; + xlch->xlch_nand_if.read_2 = xlnand_read_2; + xlch->xlch_nand_if.write_buf_1 = xlnand_write_buf; + xlch->xlch_nand_if.write_buf_2 = xlnand_write_buf; + xlch->xlch_nand_if.write_1 = xlnand_write_1; + xlch->xlch_nand_if.write_2 = xlnand_write_2; + xlch->xlch_nand_if.read_page = xlnand_read_page; + xlch->xlch_nand_if.busy = xlnand_busy; + xlch->xlch_nand_if.program_page = xlnand_program_page; + + xlch->xlch_nand_if.ecc.necc_code_size = 3; + xlch->xlch_nand_if.ecc.necc_block_size = 256; + + snprintf(xlch->xlch_wmesg, sizeof(xlch->xlch_wmesg), + "%s#%zu", xname, i); + + cv_init(&xlch->xlch_cv_ready, xlch->xlch_wmesg); + + /* + * reset to get NAND into known state + */ + KASSERT(xlsc->xlsc_active_chip == NULL); + xlnand_select(self, true); + KASSERT(xlsc->xlsc_active_chip != NULL); + xlnand_command(self, ONFI_RESET); + xlnand_busy(self); + xlnand_select(self, false); + KASSERT(xlsc->xlsc_active_chip == NULL); + } + + if (! pmf_device_register1(self, NULL, NULL, NULL)) + aprint_error_dev(self, "couldn't establish power handler\n"); + + for (size_t i = 0; (1 << i) <= csmask; i++) { + struct xlnand_chip * const xlch = &xlsc->xlsc_chips[i]; + if ((csmask & (1 << i)) == 0 || 1) + continue; + xlch->xlch_nanddev = nand_attach_mi(&xlch->xlch_nand_if, self); + } +} + +static int +xlnand_pci_detach(device_t self, int flags) +{ + struct xlnand_softc * const xlsc = device_private(self); + int rv = 0; + + pmf_device_deregister(self); + + for (size_t i = 0; i < __arraycount(xlsc->xlsc_chips); i++) { + struct xlnand_chip * const xlch = &xlsc->xlsc_chips[i]; + if (xlch->xlch_nanddev != NULL) { + rv = config_detach(xlch->xlch_nanddev, flags); + } + } + + return rv; +} +/* + * Given the advanced controller, we need to collect the commands and + * addresses until our busy is called and we construct the combined + * command to send to the device. We can do up to 3 command bytes and 5 + * address bytes. + */ +static void +xlnand_command(device_t self, uint8_t command) +{ + struct xlnand_softc * const xlsc = device_private(self); + struct xlnand_chip * const xlch = xlsc->xlsc_active_chip; + + KASSERT(xlch != NULL); + + xlch->xlch_cmds |= command << xlch->xlch_cmdshift; + xlch->xlch_cmdshift += 8; +} + +static void +xlnand_address(device_t self, uint8_t address) +{ + struct xlnand_softc * const xlsc = device_private(self); + struct xlnand_chip * const xlch = xlsc->xlsc_active_chip; + + xlch->xlch_addrs |= address << xlch->xlch_addrshift; + xlch->xlch_addrshift += 8; +} + +static void +xlnand_busy(device_t self) +{ + struct xlnand_softc * const xlsc = device_private(self); + struct xlnand_chip * const xlch = xlsc->xlsc_active_chip; + uint32_t last_cmd = xlch->xlch_cmds; + size_t i; + + xlnand_write_4(xlsc, RMIXLP_NAND_FIFO_INIT, 1); + xlnand_write_4(xlsc, RMIXLP_NAND_ADDR0_H, xlch->xlch_addrs >> 32); + xlnand_write_4(xlsc, RMIXLP_NAND_ADDR0_L, xlch->xlch_addrs); + for (i = 0; i < __arraycount(xlnand_cmdseqs); i++) { + if (xlch->xlch_cmds == (xlnand_cmdseqs[i] >> 8)) { + xlnand_write_4(xlsc, RMIXLP_NAND_CMD, + xlnand_cmdseqs[i]); + break; + } + } + KASSERTMSG(i < __arraycount(xlnand_cmdseqs), ("%#x", xlch->xlch_cmds)); + xlch->xlch_addrs = 0; + xlch->xlch_cmds = 0; + xlch->xlch_datalen = 0; + xlch->xlch_cmdshift = 0; + xlch->xlch_addrshift = 0; + + for(u_int count=100000; count--;) { + uint32_t istatus = xlnand_read_4(xlsc, RMIXLP_NAND_STATUS); + if ((istatus & RMIXLP_NAND_STATUS_BSY) == 0) { + if (last_cmd == ONFI_READ_STATUS) { + xlch->xlch_datalen = 4; + xlch->xlch_data = xlnand_read_4(xlsc, + RMIXLP_NAND_READ_STATUS); + } + return; + } + DELAY(1); + } +} + +static int +xlnand_intr(void *arg) +{ + struct xlnand_softc * const xlsc = arg; + int rv = 0; + + mutex_enter(&xlsc->xlsc_intr_lock); + + uint32_t mask = xlnand_read_4(xlsc, RMIXLP_NAND_INT_MASK); + + uint32_t v = xlnand_read_4(xlsc, RMIXLP_NAND_INT_STATUS); + xlnand_write_4(xlsc, RMIXLP_NAND_INT_MASK, mask & ~v); + + u_int mrdy = __SHIFTOUT(mask & v, RMIXLP_NAND_INT_MRDY); + + struct xlnand_chip *xlch = xlsc->xlsc_chips; + for (u_int n = ffs(mrdy); n != 0; xlch++, n = ffs(mrdy)) { + mrdy >>= n; + xlch += n - 1; + cv_signal(&xlch->xlch_cv_ready); + rv = 1; + } + + mutex_exit(&xlsc->xlsc_intr_lock); + return rv; +} + +static void +xlnand_intr_wait(struct xlnand_softc *xlsc, struct xlnand_chip *xlch) +{ + mutex_enter(&xlsc->xlsc_intr_lock); + while ((xlnand_read_4(xlsc, RMIXLP_NAND_INT_STATUS) & xlch->xlch_int_mask) == 0) { + cv_wait(&xlch->xlch_cv_ready, &xlsc->xlsc_intr_lock); + } + mutex_exit(&xlsc->xlsc_intr_lock); +} + +static void +xlnand_select(device_t self, bool enable) +{ + struct xlnand_softc * const xlsc = device_private(self); + struct xlnand_chip * const xlch = &xlsc->xlsc_chips[0]; + + mutex_enter(&xlsc->xlsc_wait_lock); + if (enable) { + if (xlsc->xlsc_active_chip != xlch) { + while (xlsc->xlsc_active_chip != NULL) { + cv_wait(&xlsc->xlsc_cv_available, + &xlsc->xlsc_wait_lock); + } + xlsc->xlsc_active_chip = xlch; + } + } else if (xlsc->xlsc_active_chip != NULL) { + KASSERT(xlsc->xlsc_active_chip == xlch); + xlsc->xlsc_active_chip = NULL; + cv_signal(&xlsc->xlsc_cv_available); + } + mutex_exit(&xlsc->xlsc_wait_lock); +} + +static void +xlnand_read_1(device_t self, uint8_t *p) +{ + struct xlnand_softc * const xlsc = device_private(self); + struct xlnand_chip * const xlch = xlsc->xlsc_active_chip; + KASSERT(xlch != NULL); + if (xlch->xlch_datalen == 0) { + xlch->xlch_data = xlnand_read_4(xlsc, RMIXLP_NAND_FIFO_DATA); + xlch->xlch_data = be32toh(xlch->xlch_data); + xlch->xlch_datalen = 4; + } + *p = xlch->xlch_data & 0xff; + xlch->xlch_data >>= 8; + xlch->xlch_datalen--; +} + +static void +xlnand_read_2(device_t self, uint16_t *p) +{ + struct xlnand_softc * const xlsc = device_private(self); + struct xlnand_chip * const xlch = xlsc->xlsc_active_chip; + KASSERT(xlch != NULL); + if (xlch->xlch_datalen == 0) { + xlch->xlch_data = xlnand_read_4(xlsc, RMIXLP_NAND_FIFO_DATA); + xlch->xlch_data = be32toh(xlch->xlch_data); + xlch->xlch_datalen = 4; + } + *p = xlch->xlch_data & 0xffff; + xlch->xlch_data >>= 16; + xlch->xlch_datalen -= 2; +} + +static void +xlnand_read_buf(device_t self, void *buf, size_t len) +{ + struct xlnand_softc * const xlsc = device_private(self); + uint32_t *dp32 = (uint32_t *)buf; + + /* + * We ignore alignment knowning that XLP will not fault on unaligned + * accesses. + */ + const size_t len32 = len / 4; +#ifdef __MIPSEB__ + bus_space_read_multi_4(xlsc->xlsc_bst, xlsc->xlsc_bsh, + RMIXLP_NAND_FIFO_DATA, dp32, len32); + dp32 += len32; +#else + for (size_t i = 0; i < len32; i++) { + uint32_t v = xlnand_read_4(xlsc, RMIXLP_NAND_FIFO_DATA); + *dp32++ = be32toh(v); + } +#endif + if ((len &= 3) != 0) { + uint32_t v = xlnand_read_4(xlsc, RMIXLP_NAND_FIFO_DATA); + v = be32toh(v); + memcpy(dp32, &v, len); + } +} + +static void +xlnand_write_1(device_t self, uint8_t v) +{ + struct xlnand_softc * const xlsc = device_private(self); + + xlnand_write_4(xlsc, RMIXLP_NAND_FIFO_DATA, htobe32(v)); +} + +static void +xlnand_write_2(device_t self, uint16_t v) +{ + struct xlnand_softc * const xlsc = device_private(self); + + xlnand_write_4(xlsc, RMIXLP_NAND_FIFO_DATA, htobe32(v)); +} + +static void +xlnand_write_buf(device_t self, const void *buf, size_t len) +{ + struct xlnand_softc * const xlsc = device_private(self); + const uint32_t *dp32 = (const uint32_t *)buf; + + const size_t len32 = len / 4; +#ifdef __MIPSEB__ + bus_space_write_multi_4(xlsc->xlsc_bst, xlsc->xlsc_bsh, + RMIXLP_NAND_FIFO_DATA, dp32, len32); + dp32 += len32; +#else + for (size_t i = 0; i < len32; i++) { + const uint32_t v = *dp32++; + xlnand_write_4(xlsc, RMIXLP_NAND_FIFO_DATA, htobe32(v)); + } +#endif + + if ((len &= 3) != 0) { + uint32_t v = 0; + memcpy(&v, dp32, len); + xlnand_write_4(xlsc, RMIXLP_NAND_FIFO_DATA, htobe32(v)); + } +} + +static void +xlnand_do_dma(struct xlnand_softc *xlsc, struct xlnand_chip *xlch, + size_t len, uint64_t address, bool read_p) +{ + bus_dmamap_t map = xlsc->xlsc_xfermap; + bus_dma_segment_t * const ds = map->dm_segs; + + /* + * Step 1 + */ +#if 0 + v = xlnand_read_4(xlsc, RMIXLP_NAND_CTRL); + v |= RMIXLP_NAND_CTRL_IE; + xlnand_write_4(xlsc, RMIXLP_NAND_CTRL, v); +#else + KASSERT(xlnand_read_4(xlsc, RMIXLP_NAND_CTRL) & RMIXLP_NAND_CTRL_IE); +#endif + /* + * Step 2 + */ + mutex_enter(&xlsc->xlsc_intr_lock); + xlnand_write_4(xlsc, RMIXLP_NAND_INT_MASK, xlch->xlch_int_mask); + xlnand_write_4(xlsc, RMIXLP_NAND_INT_STATUS, 0); + mutex_exit(&xlsc->xlsc_intr_lock); + + /* + * Step 3 + */ + xlnand_write_4(xlsc, RMIXLP_NAND_DMA_CTRL, + RMIXLP_NAND_DMA_CTRL_START + | (read_p ? RMIXLP_NAND_DMA_CTRL_READ : RMIXLP_NAND_DMA_CTRL_READ)); + xlnand_write_4(xlsc, RMIXLP_NAND_DMA_BAR_H, ds->ds_addr >> 32); + xlnand_write_4(xlsc, RMIXLP_NAND_DMA_BAR_L, ds->ds_addr); + xlnand_write_4(xlsc, RMIXLP_NAND_DMA_CNT, ds->ds_len); + + bus_dmamap_sync(xlsc->xlsc_dmat, map, 0, len, + (read_p ? BUS_DMASYNC_PREWRITE : BUS_DMASYNC_PREREAD)); + + /* + * Step 4. + */ + xlnand_write_4(xlsc, RMIXLP_NAND_ADDR0_L, address); + xlnand_write_4(xlsc, RMIXLP_NAND_ADDR0_H, address >> 32); + + /* + * Step 5. + */ + xlnand_write_4(xlsc, RMIXLP_NAND_MEMCTRL, xlch->xlch_chipnum); + + /* + * Step 6. + */ + uint32_t cmd = RMIXLP_NAND_CMD_ISEL | (read_p + ? CMDSEQ2(SEQ_10, READ, READ_START) + : CMDSEQ2(SEQ_12, PAGE_PROGRAM, PAGE_PROGRAM_START)); + xlnand_write_4(xlsc, RMIXLP_NAND_CMD, cmd); + + xlnand_intr_wait(xlsc, xlch); + + xlnand_write_4(xlsc, RMIXLP_NAND_INT_MASK, RMIXLP_NAND_INT_MRDYn(0)); + xlnand_write_4(xlsc, RMIXLP_NAND_INT_STATUS, 0); + + + bus_dmamap_sync(xlsc->xlsc_dmat, map, 0, len, + (read_p ? BUS_DMASYNC_POSTWRITE : BUS_DMASYNC_POSTREAD)); +} + +int +xlnand_read_page(device_t self, size_t offset, uint8_t *data) +{ + struct nand_softc * const sc = device_private(self); + struct nand_chip * const chip = &sc->sc_chip; + struct xlnand_softc * const xlsc = device_private(sc->controller_dev); + + KASSERT((offset & (chip->nc_page_size - 1)) == 0); + uint64_t address = (uint64_t) offset << (8 * chip->nc_addr_cycles_column - chip->nc_page_shift); + + xlnand_select(sc->controller_dev, true); + + xlnand_do_dma(xlsc, xlsc->xlsc_active_chip, chip->nc_page_size, + address, true); + + memcpy(data, (void *)xlsc->xlsc_xferbuf, chip->nc_page_size); + return 0; +} + +int +xlnand_program_page(device_t self, size_t offset, const uint8_t *data) +{ + struct nand_softc * const sc = device_private(self); + struct nand_chip * const chip = &sc->sc_chip; + struct xlnand_softc * const xlsc = device_private(sc->controller_dev); + + KASSERT((offset & (chip->nc_page_size - 1)) == 0); + uint64_t address = (uint64_t) offset << (8 * chip->nc_addr_cycles_column - chip->nc_page_shift); + + xlnand_select(sc->controller_dev, true); + + memcpy((void *)xlsc->xlsc_xferbuf, data, chip->nc_page_size); + + xlnand_do_dma(xlsc, xlsc->xlsc_active_chip, chip->nc_page_size, + address, false); + + return 0; }