Index: ar934x_nfc.c =================================================================== --- ar934x_nfc.c (revision 0) +++ ar934x_nfc.c (working copy) @@ -0,0 +1,958 @@ +/*- + * Copyright (c) 2014 Adrian Chadd . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +#include "nfc_if.h" + +#define NAND_DEBUG +/* #undef NAND_DEBUG */ +#ifdef NAND_DEBUG +#define debug(fmt, args...) do { \ + printf("NAND:" fmt "\n", ##args); } while (0) +#else +#define debug(fmt, args...) +#endif + +/* + * Configuration: whether to use IRQ for completions or to just + * busy-wait. + */ +#define AR934X_NFC_USE_IRQ false +//#define AR934X_NFC_USE_IRQ true + + +/* + * register space access macros + */ +#define NFC_BARRIER_READ(sc) bus_barrier(sc->sc_mem_res, 0, 0, \ + BUS_SPACE_BARRIER_READ) +#define NFC_BARRIER_WRITE(sc) bus_barrier(sc->sc_mem_res, 0, 0, \ + BUS_SPACE_BARRIER_WRITE) +#define NFC_BARRIER_RW(sc) bus_barrier(sc->sc_mem_res, 0, 0, \ + BUS_SPACE_BARRIER_READ | \ + BUS_SPACE_BARRIER_WRITE) + +#define NFC_WRITE(sc, reg, val) do { \ + bus_write_4(sc->sc_mem_res, (reg), (val)); \ + NFC_BARRIER_WRITE((sc)); \ + } while (0) +#define NFC_READ(sc, reg) bus_read_4(sc->sc_mem_res, (reg)) + +#define NFC_SET_BITS(sc, reg, bits) \ + NFC_WRITE(sc, reg, NFC_READ(sc, (reg)) | (bits)) + +#define NFC_CLEAR_BITS(sc, reg, bits) \ + NFC_WRITE(sc, reg, NFC_READ(sc, (reg)) & ~(bits)) + +#define NFC_LOCK_INIT(sc) \ + do { \ + mtx_init(&sc->sc_mtx, \ + device_get_nameunit(dev), \ + "ar934x_nfc lock", \ + MTX_DEF); \ + } while (0) +#define NFC_LOCK_DESTROY(sc) \ + do { \ + mtx_destroy(&sc->sc_mtx); \ + } while (0) + +#define NFC_LOCK_ASSERT(sc) mtx_assert(&sc->sc_mtx, MA_OWNED) +#define NFC_UNLOCK_ASSERT(sc) mtx_assert(&sc->sc_mtx, MA_NOTOWNED) +#define NFC_LOCK(sc) mtx_lock(&sc->sc_mtx) +#define NFC_UNLOCK(sc) mtx_unlock(&sc->sc_mtx) + +struct ar934x_nfc_buf { + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + bus_addr_t paddr; + char * buf; + int buf_len; +}; + +struct ar934x_nfc_softc { + struct nand_softc sc_nand_dev; + device_t sc_busdev; + device_t sc_dev; + + int sc_mem_rid; + struct resource *sc_mem_res; + + struct resource *sc_irq_res; + struct mtx sc_mtx; + + /* Local buffer for DMA work */ + struct ar934x_nfc_buf buf; + /* Local buffer read/write byte offset */ + uint32_t buf_index; + + /* XXX protected by lock/wakeup */ + uint32_t irq_status; + + /* Driver configuration */ + uint32_t ctrl_reg; + uint32_t small_page; + uint32_t read_id; + uint32_t addr_count0; + uint32_t ecc_ctrl_reg; + uint32_t ecc_offset_reg; + uint32_t swap_dma; +}; + +static int ar934x_nfc_attach(device_t dev); +static int ar934x_nfc_probe(device_t dev); +static int ar934x_nfc_send_command(device_t dev, uint8_t command); +static int ar934x_nfc_send_address(device_t dev, uint8_t addr); +static uint8_t ar934x_nfc_read_byte(device_t dev); +static void ar934x_nfc_read_buf(device_t dev, void* buf, uint32_t len); +static void ar934x_nfc_write_buf(device_t dev, void* buf, uint32_t len); +static int ar934x_nfc_select_cs(device_t dev, uint8_t cs); +//static void ar934x_nfc_set_flags(device_t dev, uint8_t flags); + +static device_method_t ar934x_nfc_methods[] = { + DEVMETHOD(device_probe, ar934x_nfc_probe), + DEVMETHOD(device_attach, ar934x_nfc_attach), + /* XXX detach? */ + + DEVMETHOD(nfc_send_command, ar934x_nfc_send_command), + DEVMETHOD(nfc_send_address, ar934x_nfc_send_address), + DEVMETHOD(nfc_read_byte, ar934x_nfc_read_byte), + DEVMETHOD(nfc_read_buf, ar934x_nfc_read_buf), + DEVMETHOD(nfc_write_buf, ar934x_nfc_write_buf), + DEVMETHOD(nfc_select_cs, ar934x_nfc_select_cs), + /* XXX rnb? */ + + DEVMETHOD_END +}; + +static driver_t ar934x_nfc_driver = { + "nand", + ar934x_nfc_methods, + sizeof(struct ar934x_nfc_softc), +}; + +static devclass_t ar934x_nfc_devclass; +DRIVER_MODULE(ar934x_nfc, apb, ar934x_nfc_driver, ar934x_nfc_devclass, 0, 0); + +/* + * DMA buffer interface. + */ + +static void +ar934x_dma_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + bus_addr_t *paddr = (bus_addr_t*) arg; + + KASSERT(error == 0, ("error %u on bus_dma callback", error)); + *paddr = segs->ds_addr; +} + +static void +ar934x_dma_buf_clear(struct ar934x_nfc_softc *sc) +{ + + NFC_LOCK_ASSERT(sc); + + if (sc->buf.buf == NULL) { + return; + } + + memset(sc->buf.buf, '\0', sc->buf.buf_len); +} + +static int +ar934x_dma_buf_alloc(struct ar934x_nfc_softc *sc) +{ + int error; + + NFC_LOCK_ASSERT(sc); + + /* + * Setup DMA buffer area. + * + * BUS_DMA_ALLOCNOW is not used; we never use bounce + * buffers for the descriptors themselves. + */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), /* parent */ + PAGE_SIZE, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + sc->buf.buf_len, /* maxsize */ + 1, /* nsegments */ + sc->buf.buf_len, /* maxsegsize */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &sc->buf.dma_tag); + + if (error != 0) { + device_printf(sc->sc_dev, + "%s: cannot allocate buffer DMA tag: error=%d\n", + __func__, + error); + return (error); + } + + /* Allocate buffer for DMA. */ + error = bus_dmamem_alloc(sc->buf.dma_tag, + (void *) &sc->buf.buf, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT, + &sc->buf.dma_map); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: unable to alloc memory for DMA buffer: error=%d\n", + __func__, + error); + goto fail1; + } + + /* Load DMA map for buffer. */ + error = bus_dmamap_load(sc->buf.dma_tag, + sc->buf.dma_map, + sc->buf.buf, + sc->buf.buf_len, + ar934x_dma_load_cb, + &sc->buf.paddr, + BUS_DMA_NOWAIT); + + if (error != 0) { + device_printf(sc->sc_dev, + "%s: unable to map DMA buffer, error %u\n", + __func__, + error); + goto fail2; + } + + /* All done! */ + + return (0); + +fail2: + bus_dmamem_free(sc->buf.dma_tag, sc->buf.buf, sc->buf.dma_map); +fail1: + bus_dma_tag_destroy(sc->buf.dma_tag); + memset(&sc->buf, 0, sizeof(sc->buf)); + return (error); +} + +/* XXX until this is used */ +void ar934x_dma_buf_free(struct ar934x_nfc_softc *sc); + +/* static */ void +ar934x_dma_buf_free(struct ar934x_nfc_softc *sc) +{ + + NFC_LOCK_ASSERT(sc); + + if (sc->buf.buf == NULL) + return; + + bus_dmamem_free(sc->buf.dma_tag, sc->buf.buf, sc->buf.dma_map); + bus_dma_tag_destroy(sc->buf.dma_tag); + memset(&sc->buf, 0, sizeof(sc->buf)); +} + +/* + * Hardware interface. + */ + +static inline bool +ar934x_hal_use_irq(struct ar934x_nfc_softc *sc) +{ + + return (AR934X_NFC_USE_IRQ); +} + +static inline void +ar934x_hal_write_cmd_reg(struct ar934x_nfc_softc *sc, uint32_t cmd_reg) +{ + /* write barrier? */ + + NFC_WRITE(sc, AR934X_NFC_REG_CMD, cmd_reg); + /* XXX do we need this with this API? */ + /* flush write */ + NFC_READ(sc, AR934X_NFC_REG_CMD); +} + + +static bool +__ar934x_hal_dev_ready(struct ar934x_nfc_softc *sc) +{ + uint32_t status; + + status = NFC_READ(sc, AR934X_NFC_REG_STATUS); + return (status & 0xff) == 0xff; +} + +static int +ar934x_hal_wait_dev_ready(struct ar934x_nfc_softc *sc) +{ + unsigned long timeout; + + NFC_LOCK_ASSERT(sc); + +#if 0 + timeout = jiffies + msecs_to_jiffies(AR934X_NFC_DEV_READY_TIMEOUT); + do { + if (__ar934x_nfc_dev_ready(nfc)) + return 0; + /* XXX delay? */ + } while time_before(jiffies, timeout); +#endif + for (timeout = 0 ; timeout < AR934X_NFC_DEV_READY_TIMEOUT; timeout++) { + if (__ar934x_hal_dev_ready(sc)) + return (0); + DELAY(1 * 1000); + } + + device_printf(sc->sc_dev, + "timeout waiting for device ready, status:%08x int:%08x\n", + NFC_READ(sc, AR934X_NFC_REG_STATUS), + NFC_READ(sc, AR934X_NFC_REG_INT_STATUS)); + return (ETIMEDOUT); +} + +static inline bool +__ar934x_hal_is_dma_ready(struct ar934x_nfc_softc *sc) +{ + uint32_t status; + + status = NFC_READ(sc, AR934X_NFC_REG_DMA_CTRL); + return (status & AR934X_NFC_DMA_CTRL_DMA_READY) != 0; +} + + +static int +ar934x_hal_wait_dma_ready(struct ar934x_nfc_softc *sc) +{ + int i; + +#if 0 + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(AR934X_NFC_DMA_READY_TIMEOUT); + do { + if (__ar934x_nfc_is_dma_ready(nfc)) + return 0; + } while time_before(jiffies, timeout); +#endif + for (i = 0; i < AR934X_NFC_DMA_READY_TIMEOUT; i++) { + if (__ar934x_hal_is_dma_ready(sc)) + return 0; + DELAY(1 * 1000); + } + + device_printf(sc->sc_dev, + "timeout waiting for DMA ready, dma_ctrl:%08x\n", + NFC_READ(sc, AR934X_NFC_REG_DMA_CTRL)); + return (ETIMEDOUT); +} + + +static int +ar934x_hal_wait_irq(struct ar934x_nfc_softc *sc) +{ + long timeout; + int ret; + + /* + * XXX This is all TODO. We should eventually + * implement the IRQ handling and here we'd do a sleep/wakeup. + */ + timeout = 0; +#if 0 + timeout = wait_event_timeout(nfc->irq_waitq, + (nfc->irq_status & AR934X_NFC_IRQ_MASK) != 0, + msecs_to_jiffies(AR934X_NFC_DEV_READY_TIMEOUT)); +#endif + + ret = 0; + if (!timeout) { + NFC_WRITE(sc, AR934X_NFC_REG_INT_MASK, 0); + NFC_WRITE(sc, AR934X_NFC_REG_INT_STATUS, 0); + /* flush write */ + NFC_READ(sc, AR934X_NFC_REG_INT_STATUS); + + device_printf(sc->sc_dev, + "timeout waiting for interrupt, status:%08x\n", + sc->irq_status); + ret = -ETIMEDOUT; + } + + sc->irq_status = 0; + return (ret); +} + +static int +ar934x_hal_wait_done(struct ar934x_nfc_softc *sc) +{ + int ret; + + if (ar934x_hal_use_irq(sc)) + ret = ar934x_hal_wait_irq(sc); + else + ret = ar934x_hal_wait_dev_ready(sc); + + if (ret) + return (ret); + + return (ar934x_hal_wait_dma_ready(sc)); +} + + +static void +ar934x_hal_get_addr(struct ar934x_nfc_softc *sc, int column, int page_addr, + uint32_t *addr0, uint32_t *addr1) +{ + uint32_t a0, a1; + + a0 = 0; + a1 = 0; + + if (column == -1) { + /* ERASE1 */ + a0 = (page_addr & 0xffff) << 16; + a1 = (page_addr >> 16) & 0xf; + } else if (page_addr != -1) { + /* SEQIN, READ0, etc.. */ + + /* TODO: handle 16bit bus width */ + if (sc->small_page) { + a0 = column & 0xff; + a0 |= (page_addr & 0xff) << 8; + a0 |= ((page_addr >> 8) & 0xff) << 16; + a0 |= ((page_addr >> 16) & 0xff) << 24; + } else { + a0 = column & 0x0FFF; + a0 |= (page_addr & 0xffff) << 16; + + if (sc->addr_count0 > 4) + a1 = (page_addr >> 16) & 0xf; + } + } + + *addr0 = a0; + *addr1 = a1; +} + +static void +ar934x_hal_hw_init(struct ar934x_nfc_softc *sc) +{ + + /* + * XXX lock assertion + */ + NFC_LOCK_ASSERT(sc); + + /* Reset the NAND controller */ + ar71xx_reset_nfc(0); + ar71xx_reset_nfc(1); + + /* + * setup timings + * TODO: make it configurable via platform data + */ + NFC_WRITE(sc, AR934X_NFC_REG_TIME_SEQ, + AR934X_NFC_TIME_SEQ_DEFAULT); + NFC_WRITE(sc, AR934X_NFC_REG_TIMINGS_ASYN, + AR934X_NFC_TIMINGS_ASYN_DEFAULT); + NFC_WRITE(sc, AR934X_NFC_REG_TIMINGS_SYN, + AR934X_NFC_TIMINGS_SYN_DEFAULT); + + /* disable WP on all chips, and select chip 0 */ + NFC_WRITE(sc, AR934X_NFC_REG_MEM_CTRL, 0xff00); + + NFC_WRITE(sc, AR934X_NFC_REG_DMA_ADDR_OFFS, 0); + + /* initialize Control register */ + sc->ctrl_reg = AR934X_NFC_CTRL_CUSTOM_SIZE_EN; + NFC_WRITE(sc, AR934X_NFC_REG_CTRL, sc->ctrl_reg); + + if (sc->small_page) { + /* Setup generic sequence register for small page reads. */ + NFC_WRITE(sc, AR934X_NFC_REG_GEN_SEQ_CTRL, + AR934X_NFC_GENSEQ_SMALL_PAGE_READ); + } +} + +static void +ar934x_hal_send_cmd(struct ar934x_nfc_softc *sc, unsigned int command) +{ + uint32_t cmd_reg; + + NFC_LOCK_ASSERT(sc); + + cmd_reg = AR934X_NFC_CMD_INPUT_SEL_SIU | AR934X_NFC_CMD_ADDR_SEL_0 | + AR934X_NFC_CMD_SEQ_1C; + cmd_reg |= (command & AR934X_NFC_CMD_CMD0_M) << AR934X_NFC_CMD_CMD0_S; + + NFC_WRITE(sc, AR934X_NFC_REG_INT_STATUS, 0); + NFC_WRITE(sc, AR934X_NFC_REG_CTRL, sc->ctrl_reg); + + ar934x_hal_write_cmd_reg(sc, cmd_reg); + ar934x_hal_wait_dev_ready(sc); +} + + + +static void +ar934x_nfc_restart(struct ar934x_nfc_softc *sc) +{ + uint32_t ctrl_reg; + + /* XXX should have a select_cs_locked(); and do it below */ + ar934x_nfc_select_cs(sc->sc_dev, -1); + + NFC_LOCK(sc); + + ctrl_reg = sc->ctrl_reg; + ar934x_hal_hw_init(sc); + sc->ctrl_reg = ctrl_reg; + + NFC_UNLOCK(sc); + + /* XXX again, should all be under the same lock, so it's serialised */ + ar934x_nfc_select_cs(sc->sc_dev, 0); + + NFC_LOCK(sc); + ar934x_hal_send_cmd(sc, NAND_CMD_RESET); + NFC_UNLOCK(sc); +} + +static int +ar934x_hal_do_rw_command(struct ar934x_nfc_softc *sc, int column, + int page_addr, int len, uint32_t cmd_reg, uint32_t ctrl_reg, bool write) +{ + uint32_t addr0, addr1; + uint32_t dma_ctrl; +// int dir; + int err; + int retries = 0; + +// WARN_ON(len & 3); + +// if (WARN_ON(len > nfc->buf_size)) +// dev_err(nfc->parent, "len=%d > buf_size=%d", len, nfc->buf_size); + + if (write) { + dma_ctrl = AR934X_NFC_DMA_CTRL_DMA_DIR_WRITE; +// dir = DMA_TO_DEVICE; + } else { + dma_ctrl = AR934X_NFC_DMA_CTRL_DMA_DIR_READ; +// dir = DMA_FROM_DEVICE; + } + + ar934x_hal_get_addr(sc, column, page_addr, &addr0, &addr1); + + dma_ctrl |= AR934X_NFC_DMA_CTRL_DMA_START | + (AR934X_NFC_DMA_CTRL_DMA_BURST_3 << + AR934X_NFC_DMA_CTRL_DMA_BURST_S); + + cmd_reg |= AR934X_NFC_CMD_INPUT_SEL_DMA | AR934X_NFC_CMD_ADDR_SEL_0; + ctrl_reg |= AR934X_NFC_CTRL_INT_EN; + +#if 0 + nfc_dbg(nfc, "%s a0:%08x a1:%08x len:%x cmd:%08x dma:%08x ctrl:%08x\n", + (write) ? "write" : "read", +#endif + +retry: + NFC_WRITE(sc, AR934X_NFC_REG_INT_STATUS, 0); + NFC_WRITE(sc, AR934X_NFC_REG_ADDR0_0, addr0); + NFC_WRITE(sc, AR934X_NFC_REG_ADDR0_1, addr1); + NFC_WRITE(sc, AR934X_NFC_REG_DMA_ADDR, sc->buf.paddr); + NFC_WRITE(sc, AR934X_NFC_REG_DMA_COUNT, len); + NFC_WRITE(sc, AR934X_NFC_REG_DATA_SIZE, len); + NFC_WRITE(sc, AR934X_NFC_REG_CTRL, ctrl_reg); + NFC_WRITE(sc, AR934X_NFC_REG_DMA_CTRL, dma_ctrl); + NFC_WRITE(sc, AR934X_NFC_REG_ECC_CTRL, sc->ecc_ctrl_reg); + NFC_WRITE(sc, AR934X_NFC_REG_ECC_OFFSET, sc->ecc_offset_reg); + + if (ar934x_hal_use_irq(sc)) { + NFC_WRITE(sc, AR934X_NFC_REG_INT_MASK, AR934X_NFC_IRQ_MASK); + /* flush write */ + NFC_READ(sc, AR934X_NFC_REG_INT_MASK); + } + + ar934x_hal_write_cmd_reg(sc, cmd_reg); + err = ar934x_hal_wait_done(sc); + + if (err) { + device_printf(sc->sc_dev, + "%s: %s operation stuck at page %d\n", + __func__, + (write) ? "write" : "read", page_addr); + + ar934x_nfc_restart(sc); + if (retries++ < AR934X_NFC_DMA_RETRIES) + goto retry; + + device_printf(sc->sc_dev, + "%s: %s operation failed on page %d\n", + __func__, + (write) ? "write" : "read", page_addr); + } + + return (err); +} + +static int +ar934x_hal_send_readid(struct ar934x_nfc_softc *sc, unsigned int command) +{ + uint32_t cmd_reg; + int err; + +// nfc_dbg(nfc, "readid, cmd:%02x\n", command); + + cmd_reg = AR934X_NFC_CMD_SEQ_1C1AXR; + cmd_reg |= (command & AR934X_NFC_CMD_CMD0_M) << AR934X_NFC_CMD_CMD0_S; + + err = ar934x_hal_do_rw_command(sc, -1, -1, AR934X_NFC_ID_BUF_SIZE, + cmd_reg, sc->ctrl_reg, 0 /* false */); + +// nfc_debug_data("[id] ", nfc->buf, AR934X_NFC_ID_BUF_SIZE); + + return (err); +} + + +/* + * Public driver interface. + */ + +static int +ar934x_nfc_probe(device_t dev) +{ + + /* + * XXX check for the existance of the relevant resources. + */ + + device_set_desc(dev, "AR934x NAND controller"); + return (BUS_PROBE_NOWILDCARD); +} + +static int +ar934x_nfc_attach(device_t dev) +{ + struct ar934x_nfc_softc *sc; + int err, rid; + + device_printf(dev, "%s: called\n", __func__); + + sc = device_get_softc(dev); + sc->sc_busdev = device_get_parent(dev); + sc->sc_dev = dev; + + /* Allocate memory resources */ + sc->sc_mem_rid = 0; + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_mem_rid, RF_ACTIVE | RF_SHAREABLE); + + if (sc->sc_mem_res == NULL) { + device_printf(dev, "couldn't map memory\n"); + err = ENXIO; + goto fail; + } + + /* Allocate IRQ */ + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->sc_irq_res == NULL) { + device_printf(dev, "couldn't map interrupt\n"); + err = ENXIO; + goto fail; + } + + /* Lock */ + NFC_LOCK_INIT(sc); + + /* XXX Hook interrupt handler */ + + /* XXX read platform data */ + + /* hardware and buffer setup */ + NFC_LOCK(sc); + ar934x_hal_hw_init(sc); + /* XXX just set to 64k for now */ + sc->buf.buf_len = 65536; + err = ar934x_dma_buf_alloc(sc); + NFC_UNLOCK(sc); + if (err != 0) + return (err); + + /* XXX chip delay == 25 (what, ms? microseconds?) */ + + /* XXX enumeration? */ + + /* XXX for now */ + nand_init(&sc->sc_nand_dev, dev, 0, 0, NAND_ECC_SOFT, + NULL, NULL); + + err = nandbus_create(dev); +// if (err != 0) + device_printf(dev, "nandbus_create: returned %d\n", err); + + return (err); + +fail: + /* XXX Tidy up / detach as appropriate */ + device_printf(dev, "%s: failed; err=%d\n", __func__, err); + return (err); +} + +static int +ar934x_nfc_send_command(device_t dev, uint8_t command) +{ + struct ar934x_nfc_softc *sc = device_get_softc(dev); + + NFC_LOCK(sc); + + device_printf(dev, "%s: send command %02x\n", __func__, command); + + sc->read_id = false; + /* XXX NAND_CMD_PAGEPROG */ + if (command != NAND_CMD_PROG) { + ar934x_dma_buf_clear(sc); + sc->buf_index = 0; + } + + switch (command) { + case NAND_CMD_RESET: + ar934x_hal_send_cmd(sc, command); + break; + + case NAND_CMD_READ_ID: + sc->read_id = true; + ar934x_hal_send_readid(sc, command); + break; + + default: + device_printf(dev, "%s: unsupported command: %02x\n", + __func__, + command); + break; + } + +#if 0 + ar934x_nfc_set_flags(dev, NAND_SELECT | NAND_COMMAND); + ar934x_nfc_write_buf(dev, &command, 1); + ar934x_nfc_set_flags(dev, NAND_SELECT); +#endif + + NFC_UNLOCK(sc); + + return (0); +} + +/* + * XXX The AR934x controller wants the address information available + * before it sends a command. So this needs to do the same kind of + * hacky stuff that nfc_fsl.c::fsl_nfc_send_address() does - + * sending the command just caches some state, and then once the + * address is known the command+address is sent. + */ +static int +ar934x_nfc_send_address(device_t dev, uint8_t addr) +{ + + device_printf(dev, "%s: send address %02x\n", __func__, addr); +#if 0 + ar934x_nfc_set_flags(dev, NAND_SELECT | NAND_ADDRESS); + ar934x_nfc_write_buf(dev, &addr, 1); + ar934x_nfc_set_flags(dev, NAND_SELECT); +#endif + + return (0); +} + +static uint8_t +ar934x_nfc_read_byte(device_t dev) +{ + struct ar934x_nfc_softc *sc = device_get_softc(dev); + uint8_t data; + + + NFC_LOCK(sc); + +// WARN_ON(nfc->buf_index >= nfc->buf_size); + + if (sc->swap_dma || sc->read_id) + data = sc->buf.buf[sc->buf_index ^ 3]; + else + data = sc->buf.buf[sc->buf_index]; + + device_printf(dev, "%s: read byte[%d] = %02x\n", __func__, sc->buf_index, ((int) data) & 0xff); + sc->buf_index++; + + NFC_UNLOCK(sc); + + + return (data); +} + +static void +ar934x_nfc_read_buf(device_t dev, void* buf, uint32_t len) +{ + + device_printf(dev, "%s: read buf (size %d)\n", __func__, len); +#if 0 + struct ar934x_nfc_softc *sc = device_get_softc(dev); + int i; + + RB_CPLDBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev); + RB_CPLDBUS_READ_DATA(sc->sc_busdev, buf, len); + RB_CPLDBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev); + + for (i = 0; i < len; i++) { +#ifdef NAND_DEBUG + uint8_t *b = (uint8_t*)buf; + + if (!(i % 16)) + printf("%s", i == 0 ? "rb_nand:\n" : "\n"); + printf(" %02x", b[i]); + if (i == len - 1) + printf("\n"); +#endif + } + + return; +#endif +} + +static void +ar934x_nfc_write_buf(device_t dev, void* buf, uint32_t len) +{ + device_printf(dev, "%s: write buf (size %d)\n", __func__, len); +#if 0 + struct ar934x_nfc_softc *sc = device_get_softc(dev); + int i; + + RB_CPLDBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev); + RB_CPLDBUS_WRITE_DATA(sc->sc_busdev, (uint8_t *)buf, len); + RB_CPLDBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev); + + for (i = 0; i < len; i++) { +#ifdef NAND_DEBUG + uint8_t *b = (uint8_t*)buf; + + if (!(i % 16)) + printf("%s", i == 0 ? "rb_nand:\n" : "\n"); + printf(" %02x", b[i]); + if (i == len - 1) + printf("\n"); +#endif + } + + return; +#endif +} + +static int +ar934x_nfc_select_cs(device_t dev, uint8_t cs) +{ + struct ar934x_nfc_softc *sc = device_get_softc(dev); + + NFC_LOCK(sc); + device_printf(dev, "%s: select cs %02x\n", __func__, cs); + /* + * XXX This is some kind of potential external chip select + * XXX control, eg GPIO lines mapped to /CS lines on each + * XXX NAND chip. + * + * So for now, just only allow CS=0. + */ + if (cs > 0) { + NFC_UNLOCK(sc); + /* XXX EINVAL or ENODEV? */ + return (EINVAL); + } + NFC_UNLOCK(sc); +#if 0 + ar934x_nfc_set_flags(dev, NAND_SELECT); + return (0); +#endif + return (0); +} + +#if 0 +static void +ar934x_nfc_set_flags(device_t dev, uint8_t flags) +{ + struct ar934x_nfc_softc *sc = device_get_softc(dev); + uint8_t state = RB_NAND_NCE; + uint8_t res; + + if (flags & NAND_COMMAND) + state |= RB_NAND_CLE; + if (flags & NAND_ADDRESS) + state |= RB_NAND_ALE; + if (flags & NAND_SELECT) + state &= ~RB_NAND_NCE; + + RB_CPLDBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev); + res = RB_CPLDBUS_READ_GPIO(sc->sc_busdev); + res &= ~RB_NAND_GPIO_MASK; + res |= state; + RB_CPLDBUS_WRITE_GPIO(sc->sc_busdev, res); + RB_CPLDBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev); +} +#endif + +#if 0 +static int +ar934x_nfc_wait_ready(device_t dev) +{ + + return (rb_nandbusy_status()); +} +#endif Property changes on: ar934x_nfc.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: files.ar71xx =================================================================== --- files.ar71xx (revision 263220) +++ files.ar71xx (working copy) @@ -27,5 +27,6 @@ mips/atheros/ar933x_chip.c standard mips/atheros/ar934x_chip.c standard mips/atheros/ar71xx_fixup.c optional ar71xx_ath_eeprom +mips/atheros/ar934x_nfc.c optional ar934x_nfc dev/hwpmc/hwpmc_mips24k.c optional hwpmc_mips24k