1239278Sgonzo/*- 2239278Sgonzo * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org> 3239278Sgonzo * All rights reserved. 4239278Sgonzo * 5239278Sgonzo * Redistribution and use in source and binary forms, with or without 6239278Sgonzo * modification, are permitted provided that the following conditions 7239278Sgonzo * are met: 8239278Sgonzo * 1. Redistributions of source code must retain the above copyright 9239278Sgonzo * notice, this list of conditions and the following disclaimer. 10239278Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 11239278Sgonzo * notice, this list of conditions and the following disclaimer in the 12239278Sgonzo * documentation and/or other materials provided with the distribution. 13239278Sgonzo * 14239278Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15239278Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16239278Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17239278Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18239278Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19239278Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20239278Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21239278Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22239278Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23239278Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24239278Sgonzo * SUCH DAMAGE. 25239278Sgonzo * 26239278Sgonzo */ 27239278Sgonzo#include <sys/cdefs.h> 28239278Sgonzo__FBSDID("$FreeBSD: releng/11.0/sys/arm/lpc/lpc_mmc.c 297127 2016-03-21 00:52:24Z ian $"); 29239278Sgonzo 30239278Sgonzo#include <sys/param.h> 31239278Sgonzo#include <sys/systm.h> 32239278Sgonzo#include <sys/bio.h> 33239278Sgonzo#include <sys/bus.h> 34239278Sgonzo#include <sys/conf.h> 35239278Sgonzo#include <sys/endian.h> 36239278Sgonzo#include <sys/kernel.h> 37239278Sgonzo#include <sys/kthread.h> 38239278Sgonzo#include <sys/lock.h> 39239278Sgonzo#include <sys/malloc.h> 40239278Sgonzo#include <sys/module.h> 41239278Sgonzo#include <sys/mutex.h> 42239278Sgonzo#include <sys/queue.h> 43239278Sgonzo#include <sys/resource.h> 44239278Sgonzo#include <sys/rman.h> 45239278Sgonzo#include <sys/time.h> 46239278Sgonzo#include <sys/timetc.h> 47239278Sgonzo#include <sys/watchdog.h> 48239278Sgonzo 49239278Sgonzo#include <sys/kdb.h> 50239278Sgonzo 51239278Sgonzo#include <machine/bus.h> 52239278Sgonzo#include <machine/cpu.h> 53239278Sgonzo#include <machine/cpufunc.h> 54239278Sgonzo#include <machine/resource.h> 55239278Sgonzo#include <machine/intr.h> 56239278Sgonzo 57239278Sgonzo#include <dev/ofw/ofw_bus.h> 58239278Sgonzo#include <dev/ofw/ofw_bus_subr.h> 59239278Sgonzo 60239278Sgonzo#include <dev/mmc/bridge.h> 61239278Sgonzo#include <dev/mmc/mmcreg.h> 62239278Sgonzo#include <dev/mmc/mmcbrvar.h> 63239278Sgonzo 64239278Sgonzo#include <arm/lpc/lpcreg.h> 65239278Sgonzo#include <arm/lpc/lpcvar.h> 66239278Sgonzo 67239278Sgonzo#ifdef DEBUG 68239278Sgonzo#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 69239278Sgonzo printf(fmt,##args); } while (0) 70239278Sgonzo#else 71239278Sgonzo#define debugf(fmt, args...) 72239278Sgonzo#endif 73239278Sgonzo 74239278Sgonzostruct lpc_mmc_dmamap_arg { 75239278Sgonzo bus_addr_t lm_dma_busaddr; 76239278Sgonzo}; 77239278Sgonzo 78239278Sgonzostruct lpc_mmc_softc { 79239278Sgonzo device_t lm_dev; 80239278Sgonzo struct mtx lm_mtx; 81239278Sgonzo struct resource * lm_mem_res; 82239278Sgonzo struct resource * lm_irq_res; 83239278Sgonzo bus_space_tag_t lm_bst; 84239278Sgonzo bus_space_handle_t lm_bsh; 85239278Sgonzo void * lm_intrhand; 86239278Sgonzo struct mmc_host lm_host; 87239278Sgonzo struct mmc_request * lm_req; 88239278Sgonzo struct mmc_data * lm_data; 89239278Sgonzo uint32_t lm_flags; 90239278Sgonzo#define LPC_SD_FLAGS_IGNORECRC (1 << 0) 91239278Sgonzo int lm_xfer_direction; 92239278Sgonzo#define DIRECTION_READ 0 93239278Sgonzo#define DIRECTION_WRITE 1 94239278Sgonzo int lm_xfer_done; 95239278Sgonzo int lm_bus_busy; 96239278Sgonzo bus_dma_tag_t lm_dma_tag; 97239278Sgonzo bus_dmamap_t lm_dma_map; 98239278Sgonzo bus_addr_t lm_buffer_phys; 99239278Sgonzo void * lm_buffer; 100239278Sgonzo}; 101239278Sgonzo 102239278Sgonzo#define LPC_SD_MAX_BLOCKSIZE 1024 103239278Sgonzo/* XXX */ 104239278Sgonzo#define LPC_MMC_DMACH_READ 1 105239278Sgonzo#define LPC_MMC_DMACH_WRITE 0 106239278Sgonzo 107239278Sgonzo 108239278Sgonzostatic int lpc_mmc_probe(device_t); 109239278Sgonzostatic int lpc_mmc_attach(device_t); 110239278Sgonzostatic int lpc_mmc_detach(device_t); 111239278Sgonzostatic void lpc_mmc_intr(void *); 112239278Sgonzo 113239278Sgonzostatic void lpc_mmc_cmd(struct lpc_mmc_softc *, struct mmc_command *); 114239278Sgonzostatic void lpc_mmc_setup_xfer(struct lpc_mmc_softc *, struct mmc_data *); 115239278Sgonzo 116239278Sgonzostatic int lpc_mmc_update_ios(device_t, device_t); 117239278Sgonzostatic int lpc_mmc_request(device_t, device_t, struct mmc_request *); 118239278Sgonzostatic int lpc_mmc_get_ro(device_t, device_t); 119239278Sgonzostatic int lpc_mmc_acquire_host(device_t, device_t); 120239278Sgonzostatic int lpc_mmc_release_host(device_t, device_t); 121239278Sgonzo 122239278Sgonzostatic void lpc_mmc_dma_rxfinish(void *); 123239278Sgonzostatic void lpc_mmc_dma_rxerror(void *); 124239278Sgonzostatic void lpc_mmc_dma_txfinish(void *); 125239278Sgonzostatic void lpc_mmc_dma_txerror(void *); 126239278Sgonzo 127239278Sgonzostatic void lpc_mmc_dmamap_cb(void *, bus_dma_segment_t *, int, int); 128239278Sgonzo 129239278Sgonzo#define lpc_mmc_lock(_sc) \ 130239278Sgonzo mtx_lock(&_sc->lm_mtx); 131239278Sgonzo#define lpc_mmc_unlock(_sc) \ 132239278Sgonzo mtx_unlock(&_sc->lm_mtx); 133239278Sgonzo#define lpc_mmc_read_4(_sc, _reg) \ 134239278Sgonzo bus_space_read_4(_sc->lm_bst, _sc->lm_bsh, _reg) 135239278Sgonzo#define lpc_mmc_write_4(_sc, _reg, _value) \ 136239278Sgonzo bus_space_write_4(_sc->lm_bst, _sc->lm_bsh, _reg, _value) 137239278Sgonzo 138239278Sgonzostatic struct lpc_dmac_channel_config lpc_mmc_dma_rxconf = { 139239278Sgonzo .ldc_fcntl = LPC_DMAC_FLOW_D_P2M, 140239278Sgonzo .ldc_src_periph = LPC_DMAC_SD_ID, 141239278Sgonzo .ldc_src_width = LPC_DMAC_CH_CONTROL_WIDTH_4, 142239278Sgonzo .ldc_src_incr = 0, 143239278Sgonzo .ldc_src_burst = LPC_DMAC_CH_CONTROL_BURST_8, 144239278Sgonzo .ldc_dst_periph = LPC_DMAC_SD_ID, 145239278Sgonzo .ldc_dst_width = LPC_DMAC_CH_CONTROL_WIDTH_4, 146239278Sgonzo .ldc_dst_incr = 1, 147239278Sgonzo .ldc_dst_burst = LPC_DMAC_CH_CONTROL_BURST_8, 148239278Sgonzo .ldc_success_handler = lpc_mmc_dma_rxfinish, 149239278Sgonzo .ldc_error_handler = lpc_mmc_dma_rxerror, 150239278Sgonzo}; 151239278Sgonzo 152239278Sgonzostatic struct lpc_dmac_channel_config lpc_mmc_dma_txconf = { 153239278Sgonzo .ldc_fcntl = LPC_DMAC_FLOW_P_M2P, 154239278Sgonzo .ldc_src_periph = LPC_DMAC_SD_ID, 155239278Sgonzo .ldc_src_width = LPC_DMAC_CH_CONTROL_WIDTH_4, 156239278Sgonzo .ldc_src_incr = 1, 157239278Sgonzo .ldc_src_burst = LPC_DMAC_CH_CONTROL_BURST_8, 158239278Sgonzo .ldc_dst_periph = LPC_DMAC_SD_ID, 159239278Sgonzo .ldc_dst_width = LPC_DMAC_CH_CONTROL_WIDTH_4, 160239278Sgonzo .ldc_dst_incr = 0, 161239278Sgonzo .ldc_dst_burst = LPC_DMAC_CH_CONTROL_BURST_8, 162239278Sgonzo .ldc_success_handler = lpc_mmc_dma_txfinish, 163239278Sgonzo .ldc_error_handler = lpc_mmc_dma_txerror, 164239278Sgonzo}; 165239278Sgonzo 166239278Sgonzostatic int 167239278Sgonzolpc_mmc_probe(device_t dev) 168239278Sgonzo{ 169261410Sian 170261410Sian if (!ofw_bus_status_okay(dev)) 171261410Sian return (ENXIO); 172261410Sian 173239278Sgonzo if (!ofw_bus_is_compatible(dev, "lpc,mmc")) 174239278Sgonzo return (ENXIO); 175239278Sgonzo 176239278Sgonzo device_set_desc(dev, "LPC32x0 MMC/SD controller"); 177239278Sgonzo return (BUS_PROBE_DEFAULT); 178239278Sgonzo} 179239278Sgonzo 180239278Sgonzostatic int 181239278Sgonzolpc_mmc_attach(device_t dev) 182239278Sgonzo{ 183239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(dev); 184239278Sgonzo struct lpc_mmc_dmamap_arg ctx; 185239278Sgonzo device_t child; 186239278Sgonzo int rid, err; 187239278Sgonzo 188239278Sgonzo sc->lm_dev = dev; 189239278Sgonzo sc->lm_req = NULL; 190239278Sgonzo 191239278Sgonzo mtx_init(&sc->lm_mtx, "lpcmmc", "mmc", MTX_DEF); 192239278Sgonzo 193239278Sgonzo rid = 0; 194239278Sgonzo sc->lm_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 195239278Sgonzo RF_ACTIVE); 196239278Sgonzo if (!sc->lm_mem_res) { 197239278Sgonzo device_printf(dev, "cannot allocate memory window\n"); 198239278Sgonzo return (ENXIO); 199239278Sgonzo } 200239278Sgonzo 201239278Sgonzo sc->lm_bst = rman_get_bustag(sc->lm_mem_res); 202239278Sgonzo sc->lm_bsh = rman_get_bushandle(sc->lm_mem_res); 203239278Sgonzo 204239278Sgonzo debugf("virtual register space: 0x%08lx\n", sc->lm_bsh); 205239278Sgonzo 206239278Sgonzo rid = 0; 207239278Sgonzo sc->lm_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 208239278Sgonzo RF_ACTIVE); 209239278Sgonzo if (!sc->lm_irq_res) { 210239278Sgonzo device_printf(dev, "cannot allocate interrupt\n"); 211239278Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res); 212239278Sgonzo return (ENXIO); 213239278Sgonzo } 214239278Sgonzo 215239278Sgonzo if (bus_setup_intr(dev, sc->lm_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 216239278Sgonzo NULL, lpc_mmc_intr, sc, &sc->lm_intrhand)) 217239278Sgonzo { 218239278Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res); 219239278Sgonzo bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lm_irq_res); 220239278Sgonzo device_printf(dev, "cannot setup interrupt handler\n"); 221239278Sgonzo return (ENXIO); 222239278Sgonzo } 223239278Sgonzo 224239278Sgonzo sc->lm_host.f_min = 312500; 225239278Sgonzo sc->lm_host.f_max = 2500000; 226239278Sgonzo sc->lm_host.host_ocr = MMC_OCR_300_310 | MMC_OCR_310_320 | 227239278Sgonzo MMC_OCR_320_330 | MMC_OCR_330_340; 228239278Sgonzo#if 0 229239278Sgonzo sc->lm_host.caps = MMC_CAP_4_BIT_DATA; 230239278Sgonzo#endif 231239278Sgonzo 232239278Sgonzo lpc_pwr_write(dev, LPC_CLKPWR_MS_CTRL, 233239278Sgonzo LPC_CLKPWR_MS_CTRL_CLOCK_EN | LPC_CLKPWR_MS_CTRL_SD_CLOCK | 1); 234239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_POWER, LPC_SD_POWER_CTRL_ON); 235239278Sgonzo 236239278Sgonzo device_set_ivars(dev, &sc->lm_host); 237239278Sgonzo 238239278Sgonzo child = device_add_child(dev, "mmc", -1); 239239278Sgonzo if (!child) { 240239278Sgonzo device_printf(dev, "attaching MMC bus failed!\n"); 241239278Sgonzo bus_teardown_intr(dev, sc->lm_irq_res, sc->lm_intrhand); 242239278Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res); 243239278Sgonzo bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lm_irq_res); 244239278Sgonzo return (ENXIO); 245239278Sgonzo } 246239278Sgonzo 247239278Sgonzo /* Alloc DMA memory */ 248239278Sgonzo err = bus_dma_tag_create( 249239278Sgonzo bus_get_dma_tag(sc->lm_dev), 250239278Sgonzo 4, 0, /* alignment, boundary */ 251239278Sgonzo BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 252239278Sgonzo BUS_SPACE_MAXADDR, /* highaddr */ 253239278Sgonzo NULL, NULL, /* filter, filterarg */ 254239278Sgonzo LPC_SD_MAX_BLOCKSIZE, 1, /* maxsize, nsegments */ 255239278Sgonzo LPC_SD_MAX_BLOCKSIZE, 0, /* maxsegsize, flags */ 256239278Sgonzo NULL, NULL, /* lockfunc, lockarg */ 257239278Sgonzo &sc->lm_dma_tag); 258239278Sgonzo 259239278Sgonzo err = bus_dmamem_alloc(sc->lm_dma_tag, (void **)&sc->lm_buffer, 260239278Sgonzo 0, &sc->lm_dma_map); 261239278Sgonzo if (err) { 262239278Sgonzo device_printf(dev, "cannot allocate framebuffer\n"); 263239278Sgonzo goto fail; 264239278Sgonzo } 265239278Sgonzo 266239278Sgonzo err = bus_dmamap_load(sc->lm_dma_tag, sc->lm_dma_map, sc->lm_buffer, 267239278Sgonzo LPC_SD_MAX_BLOCKSIZE, lpc_mmc_dmamap_cb, &ctx, BUS_DMA_NOWAIT); 268239278Sgonzo if (err) { 269239278Sgonzo device_printf(dev, "cannot load DMA map\n"); 270239278Sgonzo goto fail; 271239278Sgonzo } 272239278Sgonzo 273239278Sgonzo sc->lm_buffer_phys = ctx.lm_dma_busaddr; 274239278Sgonzo 275239278Sgonzo lpc_mmc_dma_rxconf.ldc_handler_arg = (void *)sc; 276239278Sgonzo err = lpc_dmac_config_channel(dev, LPC_MMC_DMACH_READ, &lpc_mmc_dma_rxconf); 277239278Sgonzo if (err) { 278239278Sgonzo device_printf(dev, "cannot allocate RX DMA channel\n"); 279239278Sgonzo goto fail; 280239278Sgonzo } 281239278Sgonzo 282239278Sgonzo 283239278Sgonzo lpc_mmc_dma_txconf.ldc_handler_arg = (void *)sc; 284239278Sgonzo err = lpc_dmac_config_channel(dev, LPC_MMC_DMACH_WRITE, &lpc_mmc_dma_txconf); 285239278Sgonzo if (err) { 286239278Sgonzo device_printf(dev, "cannot allocate TX DMA channel\n"); 287239278Sgonzo goto fail; 288239278Sgonzo } 289239278Sgonzo 290239278Sgonzo bus_generic_probe(dev); 291239278Sgonzo bus_generic_attach(dev); 292239278Sgonzo 293239278Sgonzo return (0); 294239278Sgonzo 295239278Sgonzofail: 296239278Sgonzo if (sc->lm_intrhand) 297239278Sgonzo bus_teardown_intr(dev, sc->lm_irq_res, sc->lm_intrhand); 298239278Sgonzo if (sc->lm_irq_res) 299239278Sgonzo bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lm_irq_res); 300239278Sgonzo if (sc->lm_mem_res) 301239278Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res); 302239278Sgonzo return (err); 303239278Sgonzo} 304239278Sgonzo 305239278Sgonzostatic int 306239278Sgonzolpc_mmc_detach(device_t dev) 307239278Sgonzo{ 308239278Sgonzo return (EBUSY); 309239278Sgonzo} 310239278Sgonzo 311239278Sgonzostatic void 312239278Sgonzolpc_mmc_intr(void *arg) 313239278Sgonzo{ 314239278Sgonzo struct lpc_mmc_softc *sc = (struct lpc_mmc_softc *)arg; 315239278Sgonzo struct mmc_command *cmd; 316239278Sgonzo uint32_t status; 317239278Sgonzo 318239278Sgonzo status = lpc_mmc_read_4(sc, LPC_SD_STATUS); 319239278Sgonzo 320239278Sgonzo debugf("interrupt: 0x%08x\n", status); 321239278Sgonzo 322239278Sgonzo if (status & LPC_SD_STATUS_CMDCRCFAIL) { 323239278Sgonzo cmd = sc->lm_req->cmd; 324239278Sgonzo cmd->error = sc->lm_flags & LPC_SD_FLAGS_IGNORECRC 325239278Sgonzo ? MMC_ERR_NONE : MMC_ERR_BADCRC; 326239278Sgonzo cmd->resp[0] = lpc_mmc_read_4(sc, LPC_SD_RESP0); 327239278Sgonzo sc->lm_req->done(sc->lm_req); 328239278Sgonzo sc->lm_req = NULL; 329239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDCRCFAIL); 330239278Sgonzo } 331239278Sgonzo 332239278Sgonzo if (status & LPC_SD_STATUS_CMDACTIVE) 333239278Sgonzo { 334239278Sgonzo debugf("command active\n"); 335239278Sgonzo cmd = sc->lm_req->cmd; 336239278Sgonzo cmd->resp[0] = lpc_mmc_read_4(sc, LPC_SD_RESP0); 337239278Sgonzo sc->lm_req->done(sc->lm_req); 338239278Sgonzo sc->lm_req = NULL; 339239278Sgonzo } 340239278Sgonzo 341239278Sgonzo if (status & LPC_SD_STATUS_DATATIMEOUT) { 342239278Sgonzo device_printf(sc->lm_dev, "data timeout\n"); 343239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATATIMEOUT); 344239278Sgonzo } 345239278Sgonzo 346239278Sgonzo if (status & LPC_SD_STATUS_TXUNDERRUN) { 347239278Sgonzo device_printf(sc->lm_dev, "TX underrun\n"); 348239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_TXUNDERRUN); 349239278Sgonzo } 350239278Sgonzo 351239278Sgonzo if (status & LPC_SD_STATUS_CMDRESPEND) { 352239278Sgonzo debugf("command response\n"); 353239278Sgonzo cmd = sc->lm_req->cmd; 354239278Sgonzo 355239278Sgonzo if (cmd->flags & MMC_RSP_136) { 356239278Sgonzo cmd->resp[3] = lpc_mmc_read_4(sc, LPC_SD_RESP3); 357239278Sgonzo cmd->resp[2] = lpc_mmc_read_4(sc, LPC_SD_RESP2); 358239278Sgonzo cmd->resp[1] = lpc_mmc_read_4(sc, LPC_SD_RESP1); 359239278Sgonzo } 360239278Sgonzo 361239278Sgonzo cmd->resp[0] = lpc_mmc_read_4(sc, LPC_SD_RESP0); 362239278Sgonzo cmd->error = MMC_ERR_NONE; 363239278Sgonzo 364239278Sgonzo if (cmd->data && (cmd->data->flags & MMC_DATA_WRITE)) 365239278Sgonzo lpc_mmc_setup_xfer(sc, sc->lm_req->cmd->data); 366239278Sgonzo 367239278Sgonzo if (!cmd->data) { 368239278Sgonzo sc->lm_req->done(sc->lm_req); 369239278Sgonzo sc->lm_req = NULL; 370239278Sgonzo } 371239278Sgonzo 372239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDRESPEND); 373239278Sgonzo } 374239278Sgonzo 375239278Sgonzo if (status & LPC_SD_STATUS_CMDSENT) { 376239278Sgonzo debugf("command sent\n"); 377239278Sgonzo cmd = sc->lm_req->cmd; 378239278Sgonzo cmd->error = MMC_ERR_NONE; 379239278Sgonzo sc->lm_req->done(sc->lm_req); 380239278Sgonzo sc->lm_req = NULL; 381239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDSENT); 382239278Sgonzo } 383239278Sgonzo 384239278Sgonzo if (status & LPC_SD_STATUS_DATAEND) { 385239278Sgonzo if (sc->lm_xfer_direction == DIRECTION_READ) 386239278Sgonzo lpc_dmac_start_burst(sc->lm_dev, LPC_DMAC_SD_ID); 387239278Sgonzo 388239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATAEND); 389239278Sgonzo } 390239278Sgonzo 391239278Sgonzo if (status & LPC_SD_STATUS_CMDTIMEOUT) { 392239278Sgonzo device_printf(sc->lm_dev, "command response timeout\n"); 393239278Sgonzo cmd = sc->lm_req->cmd; 394239278Sgonzo cmd->error = MMC_ERR_TIMEOUT; 395239278Sgonzo sc->lm_req->done(sc->lm_req); 396239278Sgonzo sc->lm_req = NULL; 397239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDTIMEOUT); 398239278Sgonzo return; 399239278Sgonzo } 400239278Sgonzo 401239278Sgonzo if (status & LPC_SD_STATUS_STARTBITERR) { 402239278Sgonzo device_printf(sc->lm_dev, "start bit error\n"); 403239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_STARTBITERR); 404239278Sgonzo } 405239278Sgonzo 406239278Sgonzo if (status & LPC_SD_STATUS_DATACRCFAIL) { 407239278Sgonzo device_printf(sc->lm_dev, "data CRC error\n"); 408239278Sgonzo debugf("data buffer: %p\n", sc->lm_buffer); 409239278Sgonzo cmd = sc->lm_req->cmd; 410239278Sgonzo cmd->error = MMC_ERR_BADCRC; 411239278Sgonzo sc->lm_req->done(sc->lm_req); 412239278Sgonzo sc->lm_req = NULL; 413239278Sgonzo 414239278Sgonzo if (sc->lm_xfer_direction == DIRECTION_READ) 415239278Sgonzo lpc_dmac_start_burst(sc->lm_dev, LPC_DMAC_SD_ID); 416239278Sgonzo 417239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATACRCFAIL); 418239278Sgonzo } 419239278Sgonzo 420239278Sgonzo if (status & LPC_SD_STATUS_DATABLOCKEND) { 421239278Sgonzo debugf("data block end\n"); 422239278Sgonzo if (sc->lm_xfer_direction == DIRECTION_READ) 423239278Sgonzo memcpy(sc->lm_data->data, sc->lm_buffer, sc->lm_data->len); 424239278Sgonzo 425239278Sgonzo if (sc->lm_xfer_direction == DIRECTION_WRITE) { 426239278Sgonzo lpc_dmac_disable_channel(sc->lm_dev, LPC_MMC_DMACH_WRITE); 427239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_DATACTRL, 0); 428239278Sgonzo } 429239278Sgonzo 430239278Sgonzo sc->lm_req->done(sc->lm_req); 431239278Sgonzo sc->lm_req = NULL; 432239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATABLOCKEND); 433239278Sgonzo } 434239278Sgonzo 435239278Sgonzo debugf("done\n"); 436239278Sgonzo} 437239278Sgonzo 438239278Sgonzostatic int 439239278Sgonzolpc_mmc_request(device_t bus, device_t child, struct mmc_request *req) 440239278Sgonzo{ 441239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(bus); 442239278Sgonzo 443239278Sgonzo debugf("request: %p\n", req); 444239278Sgonzo 445239278Sgonzo lpc_mmc_lock(sc); 446239278Sgonzo if (sc->lm_req) 447239278Sgonzo return (EBUSY); 448239278Sgonzo 449239278Sgonzo sc->lm_req = req; 450239278Sgonzo 451239278Sgonzo if (req->cmd->data && req->cmd->data->flags & MMC_DATA_WRITE) { 452239278Sgonzo memcpy(sc->lm_buffer, req->cmd->data->data, req->cmd->data->len); 453239278Sgonzo lpc_mmc_cmd(sc, req->cmd); 454239278Sgonzo lpc_mmc_unlock(sc); 455239278Sgonzo return (0); 456239278Sgonzo } 457239278Sgonzo 458239278Sgonzo if (req->cmd->data) 459239278Sgonzo lpc_mmc_setup_xfer(sc, req->cmd->data); 460239278Sgonzo 461239278Sgonzo lpc_mmc_cmd(sc, req->cmd); 462239278Sgonzo lpc_mmc_unlock(sc); 463239278Sgonzo 464239278Sgonzo return (0); 465239278Sgonzo} 466239278Sgonzo 467239278Sgonzostatic void 468239278Sgonzolpc_mmc_cmd(struct lpc_mmc_softc *sc, struct mmc_command *cmd) 469239278Sgonzo{ 470239278Sgonzo uint32_t cmdreg = 0; 471239278Sgonzo 472239278Sgonzo debugf("cmd: %d arg: 0x%08x\n", cmd->opcode, cmd->arg); 473239278Sgonzo 474239278Sgonzo if (lpc_mmc_read_4(sc, LPC_SD_COMMAND) & LPC_SD_COMMAND_ENABLE) { 475239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_COMMAND, 0); 476239278Sgonzo DELAY(1000); 477239278Sgonzo } 478239278Sgonzo 479239278Sgonzo sc->lm_flags &= ~LPC_SD_FLAGS_IGNORECRC; 480239278Sgonzo 481239278Sgonzo if (cmd->flags & MMC_RSP_PRESENT) 482239278Sgonzo cmdreg |= LPC_SD_COMMAND_RESPONSE; 483239278Sgonzo 484239278Sgonzo if (MMC_RSP(cmd->flags) == MMC_RSP_R2) 485239278Sgonzo cmdreg |= LPC_SD_COMMAND_LONGRSP; 486239278Sgonzo 487239278Sgonzo if (MMC_RSP(cmd->flags) == MMC_RSP_R3) 488239278Sgonzo sc->lm_flags |= LPC_SD_FLAGS_IGNORECRC; 489239278Sgonzo 490239278Sgonzo cmdreg |= LPC_SD_COMMAND_ENABLE; 491239278Sgonzo cmdreg |= (cmd->opcode & LPC_SD_COMMAND_CMDINDEXMASK); 492239278Sgonzo 493239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_MASK0, 0xffffffff); 494239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_MASK1, 0xffffffff); 495239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_ARGUMENT, cmd->arg); 496239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_COMMAND, cmdreg); 497239278Sgonzo} 498239278Sgonzo 499239278Sgonzostatic void 500239278Sgonzolpc_mmc_setup_xfer(struct lpc_mmc_softc *sc, struct mmc_data *data) 501239278Sgonzo{ 502239278Sgonzo uint32_t datactrl = 0; 503239278Sgonzo int data_words = data->len / 4; 504239278Sgonzo 505239278Sgonzo sc->lm_data = data; 506239278Sgonzo sc->lm_xfer_done = 0; 507239278Sgonzo 508239278Sgonzo debugf("data: %p, len: %d, %s\n", data, 509239278Sgonzo data->len, (data->flags & MMC_DATA_READ) ? "read" : "write"); 510239278Sgonzo 511239278Sgonzo if (data->flags & MMC_DATA_READ) { 512239278Sgonzo sc->lm_xfer_direction = DIRECTION_READ; 513239278Sgonzo lpc_dmac_setup_transfer(sc->lm_dev, LPC_MMC_DMACH_READ, 514260326Sian LPC_SD_PHYS_BASE + LPC_SD_FIFO, sc->lm_buffer_phys, 515239278Sgonzo data_words, 0); 516239278Sgonzo } 517239278Sgonzo 518239278Sgonzo if (data->flags & MMC_DATA_WRITE) { 519239278Sgonzo sc->lm_xfer_direction = DIRECTION_WRITE; 520239278Sgonzo lpc_dmac_setup_transfer(sc->lm_dev, LPC_MMC_DMACH_WRITE, 521260326Sian sc->lm_buffer_phys, LPC_SD_PHYS_BASE + LPC_SD_FIFO, 522239278Sgonzo data_words, 0); 523239278Sgonzo } 524239278Sgonzo 525239278Sgonzo datactrl |= (sc->lm_xfer_direction 526239278Sgonzo ? LPC_SD_DATACTRL_WRITE 527239278Sgonzo : LPC_SD_DATACTRL_READ); 528239278Sgonzo 529239278Sgonzo datactrl |= LPC_SD_DATACTRL_DMAENABLE | LPC_SD_DATACTRL_ENABLE; 530239278Sgonzo datactrl |= (ffs(data->len) - 1) << 4; 531239278Sgonzo 532239278Sgonzo debugf("datactrl: 0x%08x\n", datactrl); 533239278Sgonzo 534239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_DATATIMER, 0xFFFF0000); 535239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_DATALENGTH, data->len); 536239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_DATACTRL, datactrl); 537239278Sgonzo} 538239278Sgonzo 539239278Sgonzostatic int 540239278Sgonzolpc_mmc_read_ivar(device_t bus, device_t child, int which, 541239278Sgonzo uintptr_t *result) 542239278Sgonzo{ 543239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(bus); 544239278Sgonzo 545239278Sgonzo switch (which) { 546239278Sgonzo default: 547239278Sgonzo return (EINVAL); 548239278Sgonzo case MMCBR_IVAR_BUS_MODE: 549239278Sgonzo *(int *)result = sc->lm_host.ios.bus_mode; 550239278Sgonzo break; 551239278Sgonzo case MMCBR_IVAR_BUS_WIDTH: 552239278Sgonzo *(int *)result = sc->lm_host.ios.bus_width; 553239278Sgonzo break; 554239278Sgonzo case MMCBR_IVAR_CHIP_SELECT: 555239278Sgonzo *(int *)result = sc->lm_host.ios.chip_select; 556239278Sgonzo break; 557239278Sgonzo case MMCBR_IVAR_CLOCK: 558239278Sgonzo *(int *)result = sc->lm_host.ios.clock; 559239278Sgonzo break; 560239278Sgonzo case MMCBR_IVAR_F_MIN: 561239278Sgonzo *(int *)result = sc->lm_host.f_min; 562239278Sgonzo break; 563239278Sgonzo case MMCBR_IVAR_F_MAX: 564239278Sgonzo *(int *)result = sc->lm_host.f_max; 565239278Sgonzo break; 566239278Sgonzo case MMCBR_IVAR_HOST_OCR: 567239278Sgonzo *(int *)result = sc->lm_host.host_ocr; 568239278Sgonzo break; 569239278Sgonzo case MMCBR_IVAR_MODE: 570239278Sgonzo *(int *)result = sc->lm_host.mode; 571239278Sgonzo break; 572239278Sgonzo case MMCBR_IVAR_OCR: 573239278Sgonzo *(int *)result = sc->lm_host.ocr; 574239278Sgonzo break; 575239278Sgonzo case MMCBR_IVAR_POWER_MODE: 576239278Sgonzo *(int *)result = sc->lm_host.ios.power_mode; 577239278Sgonzo break; 578239278Sgonzo case MMCBR_IVAR_VDD: 579239278Sgonzo *(int *)result = sc->lm_host.ios.vdd; 580239278Sgonzo break; 581239278Sgonzo case MMCBR_IVAR_CAPS: 582239278Sgonzo *(int *)result = sc->lm_host.caps; 583239278Sgonzo break; 584239278Sgonzo case MMCBR_IVAR_MAX_DATA: 585239278Sgonzo *(int *)result = 1; 586239278Sgonzo break; 587239278Sgonzo } 588239278Sgonzo 589239278Sgonzo return (0); 590239278Sgonzo} 591239278Sgonzo 592239278Sgonzostatic int 593239278Sgonzolpc_mmc_write_ivar(device_t bus, device_t child, int which, 594239278Sgonzo uintptr_t value) 595239278Sgonzo{ 596239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(bus); 597239278Sgonzo 598239278Sgonzo switch (which) { 599239278Sgonzo default: 600239278Sgonzo return (EINVAL); 601239278Sgonzo case MMCBR_IVAR_BUS_MODE: 602239278Sgonzo sc->lm_host.ios.bus_mode = value; 603239278Sgonzo break; 604239278Sgonzo case MMCBR_IVAR_BUS_WIDTH: 605239278Sgonzo sc->lm_host.ios.bus_width = value; 606239278Sgonzo break; 607239278Sgonzo case MMCBR_IVAR_CHIP_SELECT: 608239278Sgonzo sc->lm_host.ios.chip_select = value; 609239278Sgonzo break; 610239278Sgonzo case MMCBR_IVAR_CLOCK: 611239278Sgonzo sc->lm_host.ios.clock = value; 612239278Sgonzo break; 613239278Sgonzo case MMCBR_IVAR_MODE: 614239278Sgonzo sc->lm_host.mode = value; 615239278Sgonzo break; 616239278Sgonzo case MMCBR_IVAR_OCR: 617239278Sgonzo sc->lm_host.ocr = value; 618239278Sgonzo break; 619239278Sgonzo case MMCBR_IVAR_POWER_MODE: 620239278Sgonzo sc->lm_host.ios.power_mode = value; 621239278Sgonzo break; 622239278Sgonzo case MMCBR_IVAR_VDD: 623239278Sgonzo sc->lm_host.ios.vdd = value; 624239278Sgonzo break; 625239278Sgonzo /* These are read-only */ 626239278Sgonzo case MMCBR_IVAR_CAPS: 627239278Sgonzo case MMCBR_IVAR_HOST_OCR: 628239278Sgonzo case MMCBR_IVAR_F_MIN: 629239278Sgonzo case MMCBR_IVAR_F_MAX: 630239278Sgonzo case MMCBR_IVAR_MAX_DATA: 631239278Sgonzo return (EINVAL); 632239278Sgonzo } 633239278Sgonzo return (0); 634239278Sgonzo} 635239278Sgonzo 636239278Sgonzostatic int 637239278Sgonzolpc_mmc_update_ios(device_t bus, device_t child) 638239278Sgonzo{ 639239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(bus); 640239278Sgonzo struct mmc_ios *ios = &sc->lm_host.ios; 641239278Sgonzo uint32_t clkdiv = 0, pwr = 0; 642239278Sgonzo 643239278Sgonzo if (ios->bus_width == bus_width_4) 644239278Sgonzo clkdiv |= LPC_SD_CLOCK_WIDEBUS; 645239278Sgonzo 646239278Sgonzo /* Calculate clock divider */ 647239278Sgonzo clkdiv = (LPC_SD_CLK / (2 * ios->clock)) - 1; 648239278Sgonzo 649239278Sgonzo /* Clock rate should not exceed rate requested in ios */ 650239278Sgonzo if ((LPC_SD_CLK / (2 * (clkdiv + 1))) > ios->clock) 651239278Sgonzo clkdiv++; 652239278Sgonzo 653239278Sgonzo debugf("clock: %dHz, clkdiv: %d\n", ios->clock, clkdiv); 654239278Sgonzo 655239278Sgonzo if (ios->bus_width == bus_width_4) { 656239278Sgonzo debugf("using wide bus mode\n"); 657239278Sgonzo clkdiv |= LPC_SD_CLOCK_WIDEBUS; 658239278Sgonzo } 659239278Sgonzo 660239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLOCK, clkdiv | LPC_SD_CLOCK_ENABLE); 661239278Sgonzo 662239278Sgonzo switch (ios->power_mode) { 663239278Sgonzo case power_off: 664239278Sgonzo pwr |= LPC_SD_POWER_CTRL_OFF; 665239278Sgonzo break; 666239278Sgonzo case power_up: 667239278Sgonzo pwr |= LPC_SD_POWER_CTRL_UP; 668239278Sgonzo break; 669239278Sgonzo case power_on: 670239278Sgonzo pwr |= LPC_SD_POWER_CTRL_ON; 671239278Sgonzo break; 672239278Sgonzo } 673239278Sgonzo 674239278Sgonzo if (ios->bus_mode == opendrain) 675239278Sgonzo pwr |= LPC_SD_POWER_OPENDRAIN; 676239278Sgonzo 677239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_POWER, pwr); 678239278Sgonzo 679239278Sgonzo return (0); 680239278Sgonzo} 681239278Sgonzo 682239278Sgonzostatic int 683239278Sgonzolpc_mmc_get_ro(device_t bus, device_t child) 684239278Sgonzo{ 685239278Sgonzo 686239278Sgonzo return (0); 687239278Sgonzo} 688239278Sgonzo 689239278Sgonzostatic int 690239278Sgonzolpc_mmc_acquire_host(device_t bus, device_t child) 691239278Sgonzo{ 692239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(bus); 693239278Sgonzo int error = 0; 694239278Sgonzo 695239278Sgonzo lpc_mmc_lock(sc); 696239278Sgonzo while (sc->lm_bus_busy) 697239278Sgonzo error = mtx_sleep(sc, &sc->lm_mtx, PZERO, "mmcah", 0); 698239278Sgonzo 699239278Sgonzo sc->lm_bus_busy++; 700239278Sgonzo lpc_mmc_unlock(sc); 701239278Sgonzo return (error); 702239278Sgonzo} 703239278Sgonzo 704239278Sgonzostatic int 705239278Sgonzolpc_mmc_release_host(device_t bus, device_t child) 706239278Sgonzo{ 707239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(bus); 708239278Sgonzo 709239278Sgonzo lpc_mmc_lock(sc); 710239278Sgonzo sc->lm_bus_busy--; 711239278Sgonzo wakeup(sc); 712239278Sgonzo lpc_mmc_unlock(sc); 713239278Sgonzo return (0); 714239278Sgonzo} 715239278Sgonzo 716239278Sgonzostatic void lpc_mmc_dma_rxfinish(void *arg) 717239278Sgonzo{ 718239278Sgonzo} 719239278Sgonzo 720239278Sgonzostatic void lpc_mmc_dma_rxerror(void *arg) 721239278Sgonzo{ 722239278Sgonzo struct lpc_mmc_softc *sc = (struct lpc_mmc_softc *)arg; 723239278Sgonzo device_printf(sc->lm_dev, "DMA RX error\n"); 724239278Sgonzo} 725239278Sgonzo 726239278Sgonzostatic void lpc_mmc_dma_txfinish(void *arg) 727239278Sgonzo{ 728239278Sgonzo} 729239278Sgonzo 730239278Sgonzostatic void lpc_mmc_dma_txerror(void *arg) 731239278Sgonzo{ 732239278Sgonzo struct lpc_mmc_softc *sc = (struct lpc_mmc_softc *)arg; 733239278Sgonzo device_printf(sc->lm_dev, "DMA TX error\n"); 734239278Sgonzo} 735239278Sgonzo 736239278Sgonzostatic void 737239278Sgonzolpc_mmc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) 738239278Sgonzo{ 739239278Sgonzo struct lpc_mmc_dmamap_arg *ctx; 740239278Sgonzo 741239278Sgonzo if (err) 742239278Sgonzo return; 743239278Sgonzo 744239278Sgonzo ctx = (struct lpc_mmc_dmamap_arg *)arg; 745239278Sgonzo ctx->lm_dma_busaddr = segs[0].ds_addr; 746239278Sgonzo} 747239278Sgonzo 748239278Sgonzostatic device_method_t lpc_mmc_methods[] = { 749239278Sgonzo /* Device interface */ 750239278Sgonzo DEVMETHOD(device_probe, lpc_mmc_probe), 751239278Sgonzo DEVMETHOD(device_attach, lpc_mmc_attach), 752239278Sgonzo DEVMETHOD(device_detach, lpc_mmc_detach), 753239278Sgonzo 754239278Sgonzo /* Bus interface */ 755239278Sgonzo DEVMETHOD(bus_read_ivar, lpc_mmc_read_ivar), 756239278Sgonzo DEVMETHOD(bus_write_ivar, lpc_mmc_write_ivar), 757239278Sgonzo DEVMETHOD(bus_print_child, bus_generic_print_child), 758239278Sgonzo 759239278Sgonzo /* MMC bridge interface */ 760239278Sgonzo DEVMETHOD(mmcbr_update_ios, lpc_mmc_update_ios), 761239278Sgonzo DEVMETHOD(mmcbr_request, lpc_mmc_request), 762239278Sgonzo DEVMETHOD(mmcbr_get_ro, lpc_mmc_get_ro), 763239278Sgonzo DEVMETHOD(mmcbr_acquire_host, lpc_mmc_acquire_host), 764239278Sgonzo DEVMETHOD(mmcbr_release_host, lpc_mmc_release_host), 765239278Sgonzo 766239278Sgonzo { 0, 0 } 767239278Sgonzo}; 768239278Sgonzo 769239278Sgonzostatic devclass_t lpc_mmc_devclass; 770239278Sgonzo 771239278Sgonzostatic driver_t lpc_mmc_driver = { 772239278Sgonzo "lpcmmc", 773239278Sgonzo lpc_mmc_methods, 774239278Sgonzo sizeof(struct lpc_mmc_softc), 775239278Sgonzo}; 776239278Sgonzo 777239278SgonzoDRIVER_MODULE(lpcmmc, simplebus, lpc_mmc_driver, lpc_mmc_devclass, 0, 0); 778292180SianDRIVER_MODULE(mmc, lpcmmc, mmc_driver, mmc_devclass, NULL, NULL); 779297127SianMODULE_DEPEND(lpcmmc, mmc, 1, 1, 1); 780