lpc_mmc.c revision 260326
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: head/sys/arm/lpc/lpc_mmc.c 260326 2014-01-05 18:40:06Z 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{ 169239278Sgonzo if (!ofw_bus_is_compatible(dev, "lpc,mmc")) 170239278Sgonzo return (ENXIO); 171239278Sgonzo 172239278Sgonzo device_set_desc(dev, "LPC32x0 MMC/SD controller"); 173239278Sgonzo return (BUS_PROBE_DEFAULT); 174239278Sgonzo} 175239278Sgonzo 176239278Sgonzostatic int 177239278Sgonzolpc_mmc_attach(device_t dev) 178239278Sgonzo{ 179239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(dev); 180239278Sgonzo struct lpc_mmc_dmamap_arg ctx; 181239278Sgonzo device_t child; 182239278Sgonzo int rid, err; 183239278Sgonzo 184239278Sgonzo sc->lm_dev = dev; 185239278Sgonzo sc->lm_req = NULL; 186239278Sgonzo 187239278Sgonzo mtx_init(&sc->lm_mtx, "lpcmmc", "mmc", MTX_DEF); 188239278Sgonzo 189239278Sgonzo rid = 0; 190239278Sgonzo sc->lm_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 191239278Sgonzo RF_ACTIVE); 192239278Sgonzo if (!sc->lm_mem_res) { 193239278Sgonzo device_printf(dev, "cannot allocate memory window\n"); 194239278Sgonzo return (ENXIO); 195239278Sgonzo } 196239278Sgonzo 197239278Sgonzo sc->lm_bst = rman_get_bustag(sc->lm_mem_res); 198239278Sgonzo sc->lm_bsh = rman_get_bushandle(sc->lm_mem_res); 199239278Sgonzo 200239278Sgonzo debugf("virtual register space: 0x%08lx\n", sc->lm_bsh); 201239278Sgonzo 202239278Sgonzo rid = 0; 203239278Sgonzo sc->lm_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 204239278Sgonzo RF_ACTIVE); 205239278Sgonzo if (!sc->lm_irq_res) { 206239278Sgonzo device_printf(dev, "cannot allocate interrupt\n"); 207239278Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res); 208239278Sgonzo return (ENXIO); 209239278Sgonzo } 210239278Sgonzo 211239278Sgonzo if (bus_setup_intr(dev, sc->lm_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 212239278Sgonzo NULL, lpc_mmc_intr, sc, &sc->lm_intrhand)) 213239278Sgonzo { 214239278Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res); 215239278Sgonzo bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lm_irq_res); 216239278Sgonzo device_printf(dev, "cannot setup interrupt handler\n"); 217239278Sgonzo return (ENXIO); 218239278Sgonzo } 219239278Sgonzo 220239278Sgonzo sc->lm_host.f_min = 312500; 221239278Sgonzo sc->lm_host.f_max = 2500000; 222239278Sgonzo sc->lm_host.host_ocr = MMC_OCR_300_310 | MMC_OCR_310_320 | 223239278Sgonzo MMC_OCR_320_330 | MMC_OCR_330_340; 224239278Sgonzo#if 0 225239278Sgonzo sc->lm_host.caps = MMC_CAP_4_BIT_DATA; 226239278Sgonzo#endif 227239278Sgonzo 228239278Sgonzo lpc_pwr_write(dev, LPC_CLKPWR_MS_CTRL, 229239278Sgonzo LPC_CLKPWR_MS_CTRL_CLOCK_EN | LPC_CLKPWR_MS_CTRL_SD_CLOCK | 1); 230239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_POWER, LPC_SD_POWER_CTRL_ON); 231239278Sgonzo 232239278Sgonzo device_set_ivars(dev, &sc->lm_host); 233239278Sgonzo 234239278Sgonzo child = device_add_child(dev, "mmc", -1); 235239278Sgonzo if (!child) { 236239278Sgonzo device_printf(dev, "attaching MMC bus failed!\n"); 237239278Sgonzo bus_teardown_intr(dev, sc->lm_irq_res, sc->lm_intrhand); 238239278Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res); 239239278Sgonzo bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lm_irq_res); 240239278Sgonzo return (ENXIO); 241239278Sgonzo } 242239278Sgonzo 243239278Sgonzo /* Alloc DMA memory */ 244239278Sgonzo err = bus_dma_tag_create( 245239278Sgonzo bus_get_dma_tag(sc->lm_dev), 246239278Sgonzo 4, 0, /* alignment, boundary */ 247239278Sgonzo BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 248239278Sgonzo BUS_SPACE_MAXADDR, /* highaddr */ 249239278Sgonzo NULL, NULL, /* filter, filterarg */ 250239278Sgonzo LPC_SD_MAX_BLOCKSIZE, 1, /* maxsize, nsegments */ 251239278Sgonzo LPC_SD_MAX_BLOCKSIZE, 0, /* maxsegsize, flags */ 252239278Sgonzo NULL, NULL, /* lockfunc, lockarg */ 253239278Sgonzo &sc->lm_dma_tag); 254239278Sgonzo 255239278Sgonzo err = bus_dmamem_alloc(sc->lm_dma_tag, (void **)&sc->lm_buffer, 256239278Sgonzo 0, &sc->lm_dma_map); 257239278Sgonzo if (err) { 258239278Sgonzo device_printf(dev, "cannot allocate framebuffer\n"); 259239278Sgonzo goto fail; 260239278Sgonzo } 261239278Sgonzo 262239278Sgonzo err = bus_dmamap_load(sc->lm_dma_tag, sc->lm_dma_map, sc->lm_buffer, 263239278Sgonzo LPC_SD_MAX_BLOCKSIZE, lpc_mmc_dmamap_cb, &ctx, BUS_DMA_NOWAIT); 264239278Sgonzo if (err) { 265239278Sgonzo device_printf(dev, "cannot load DMA map\n"); 266239278Sgonzo goto fail; 267239278Sgonzo } 268239278Sgonzo 269239278Sgonzo sc->lm_buffer_phys = ctx.lm_dma_busaddr; 270239278Sgonzo 271239278Sgonzo lpc_mmc_dma_rxconf.ldc_handler_arg = (void *)sc; 272239278Sgonzo err = lpc_dmac_config_channel(dev, LPC_MMC_DMACH_READ, &lpc_mmc_dma_rxconf); 273239278Sgonzo if (err) { 274239278Sgonzo device_printf(dev, "cannot allocate RX DMA channel\n"); 275239278Sgonzo goto fail; 276239278Sgonzo } 277239278Sgonzo 278239278Sgonzo 279239278Sgonzo lpc_mmc_dma_txconf.ldc_handler_arg = (void *)sc; 280239278Sgonzo err = lpc_dmac_config_channel(dev, LPC_MMC_DMACH_WRITE, &lpc_mmc_dma_txconf); 281239278Sgonzo if (err) { 282239278Sgonzo device_printf(dev, "cannot allocate TX DMA channel\n"); 283239278Sgonzo goto fail; 284239278Sgonzo } 285239278Sgonzo 286239278Sgonzo bus_generic_probe(dev); 287239278Sgonzo bus_generic_attach(dev); 288239278Sgonzo 289239278Sgonzo return (0); 290239278Sgonzo 291239278Sgonzofail: 292239278Sgonzo if (sc->lm_intrhand) 293239278Sgonzo bus_teardown_intr(dev, sc->lm_irq_res, sc->lm_intrhand); 294239278Sgonzo if (sc->lm_irq_res) 295239278Sgonzo bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lm_irq_res); 296239278Sgonzo if (sc->lm_mem_res) 297239278Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res); 298239278Sgonzo return (err); 299239278Sgonzo} 300239278Sgonzo 301239278Sgonzostatic int 302239278Sgonzolpc_mmc_detach(device_t dev) 303239278Sgonzo{ 304239278Sgonzo return (EBUSY); 305239278Sgonzo} 306239278Sgonzo 307239278Sgonzostatic void 308239278Sgonzolpc_mmc_intr(void *arg) 309239278Sgonzo{ 310239278Sgonzo struct lpc_mmc_softc *sc = (struct lpc_mmc_softc *)arg; 311239278Sgonzo struct mmc_command *cmd; 312239278Sgonzo uint32_t status; 313239278Sgonzo 314239278Sgonzo status = lpc_mmc_read_4(sc, LPC_SD_STATUS); 315239278Sgonzo 316239278Sgonzo debugf("interrupt: 0x%08x\n", status); 317239278Sgonzo 318239278Sgonzo if (status & LPC_SD_STATUS_CMDCRCFAIL) { 319239278Sgonzo cmd = sc->lm_req->cmd; 320239278Sgonzo cmd->error = sc->lm_flags & LPC_SD_FLAGS_IGNORECRC 321239278Sgonzo ? MMC_ERR_NONE : MMC_ERR_BADCRC; 322239278Sgonzo cmd->resp[0] = lpc_mmc_read_4(sc, LPC_SD_RESP0); 323239278Sgonzo sc->lm_req->done(sc->lm_req); 324239278Sgonzo sc->lm_req = NULL; 325239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDCRCFAIL); 326239278Sgonzo } 327239278Sgonzo 328239278Sgonzo if (status & LPC_SD_STATUS_CMDACTIVE) 329239278Sgonzo { 330239278Sgonzo debugf("command active\n"); 331239278Sgonzo cmd = sc->lm_req->cmd; 332239278Sgonzo cmd->resp[0] = lpc_mmc_read_4(sc, LPC_SD_RESP0); 333239278Sgonzo sc->lm_req->done(sc->lm_req); 334239278Sgonzo sc->lm_req = NULL; 335239278Sgonzo } 336239278Sgonzo 337239278Sgonzo if (status & LPC_SD_STATUS_DATATIMEOUT) { 338239278Sgonzo device_printf(sc->lm_dev, "data timeout\n"); 339239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATATIMEOUT); 340239278Sgonzo } 341239278Sgonzo 342239278Sgonzo if (status & LPC_SD_STATUS_TXUNDERRUN) { 343239278Sgonzo device_printf(sc->lm_dev, "TX underrun\n"); 344239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_TXUNDERRUN); 345239278Sgonzo } 346239278Sgonzo 347239278Sgonzo if (status & LPC_SD_STATUS_CMDRESPEND) { 348239278Sgonzo debugf("command response\n"); 349239278Sgonzo cmd = sc->lm_req->cmd; 350239278Sgonzo 351239278Sgonzo if (cmd->flags & MMC_RSP_136) { 352239278Sgonzo cmd->resp[3] = lpc_mmc_read_4(sc, LPC_SD_RESP3); 353239278Sgonzo cmd->resp[2] = lpc_mmc_read_4(sc, LPC_SD_RESP2); 354239278Sgonzo cmd->resp[1] = lpc_mmc_read_4(sc, LPC_SD_RESP1); 355239278Sgonzo } 356239278Sgonzo 357239278Sgonzo cmd->resp[0] = lpc_mmc_read_4(sc, LPC_SD_RESP0); 358239278Sgonzo cmd->error = MMC_ERR_NONE; 359239278Sgonzo 360239278Sgonzo if (cmd->data && (cmd->data->flags & MMC_DATA_WRITE)) 361239278Sgonzo lpc_mmc_setup_xfer(sc, sc->lm_req->cmd->data); 362239278Sgonzo 363239278Sgonzo if (!cmd->data) { 364239278Sgonzo sc->lm_req->done(sc->lm_req); 365239278Sgonzo sc->lm_req = NULL; 366239278Sgonzo } 367239278Sgonzo 368239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDRESPEND); 369239278Sgonzo } 370239278Sgonzo 371239278Sgonzo if (status & LPC_SD_STATUS_CMDSENT) { 372239278Sgonzo debugf("command sent\n"); 373239278Sgonzo cmd = sc->lm_req->cmd; 374239278Sgonzo cmd->error = MMC_ERR_NONE; 375239278Sgonzo sc->lm_req->done(sc->lm_req); 376239278Sgonzo sc->lm_req = NULL; 377239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDSENT); 378239278Sgonzo } 379239278Sgonzo 380239278Sgonzo if (status & LPC_SD_STATUS_DATAEND) { 381239278Sgonzo if (sc->lm_xfer_direction == DIRECTION_READ) 382239278Sgonzo lpc_dmac_start_burst(sc->lm_dev, LPC_DMAC_SD_ID); 383239278Sgonzo 384239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATAEND); 385239278Sgonzo } 386239278Sgonzo 387239278Sgonzo if (status & LPC_SD_STATUS_CMDTIMEOUT) { 388239278Sgonzo device_printf(sc->lm_dev, "command response timeout\n"); 389239278Sgonzo cmd = sc->lm_req->cmd; 390239278Sgonzo cmd->error = MMC_ERR_TIMEOUT; 391239278Sgonzo sc->lm_req->done(sc->lm_req); 392239278Sgonzo sc->lm_req = NULL; 393239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDTIMEOUT); 394239278Sgonzo return; 395239278Sgonzo } 396239278Sgonzo 397239278Sgonzo if (status & LPC_SD_STATUS_STARTBITERR) { 398239278Sgonzo device_printf(sc->lm_dev, "start bit error\n"); 399239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_STARTBITERR); 400239278Sgonzo } 401239278Sgonzo 402239278Sgonzo if (status & LPC_SD_STATUS_DATACRCFAIL) { 403239278Sgonzo device_printf(sc->lm_dev, "data CRC error\n"); 404239278Sgonzo debugf("data buffer: %p\n", sc->lm_buffer); 405239278Sgonzo cmd = sc->lm_req->cmd; 406239278Sgonzo cmd->error = MMC_ERR_BADCRC; 407239278Sgonzo sc->lm_req->done(sc->lm_req); 408239278Sgonzo sc->lm_req = NULL; 409239278Sgonzo 410239278Sgonzo if (sc->lm_xfer_direction == DIRECTION_READ) 411239278Sgonzo lpc_dmac_start_burst(sc->lm_dev, LPC_DMAC_SD_ID); 412239278Sgonzo 413239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATACRCFAIL); 414239278Sgonzo } 415239278Sgonzo 416239278Sgonzo if (status & LPC_SD_STATUS_DATABLOCKEND) { 417239278Sgonzo debugf("data block end\n"); 418239278Sgonzo if (sc->lm_xfer_direction == DIRECTION_READ) 419239278Sgonzo memcpy(sc->lm_data->data, sc->lm_buffer, sc->lm_data->len); 420239278Sgonzo 421239278Sgonzo if (sc->lm_xfer_direction == DIRECTION_WRITE) { 422239278Sgonzo lpc_dmac_disable_channel(sc->lm_dev, LPC_MMC_DMACH_WRITE); 423239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_DATACTRL, 0); 424239278Sgonzo } 425239278Sgonzo 426239278Sgonzo sc->lm_req->done(sc->lm_req); 427239278Sgonzo sc->lm_req = NULL; 428239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATABLOCKEND); 429239278Sgonzo } 430239278Sgonzo 431239278Sgonzo debugf("done\n"); 432239278Sgonzo} 433239278Sgonzo 434239278Sgonzostatic int 435239278Sgonzolpc_mmc_request(device_t bus, device_t child, struct mmc_request *req) 436239278Sgonzo{ 437239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(bus); 438239278Sgonzo 439239278Sgonzo debugf("request: %p\n", req); 440239278Sgonzo 441239278Sgonzo lpc_mmc_lock(sc); 442239278Sgonzo if (sc->lm_req) 443239278Sgonzo return (EBUSY); 444239278Sgonzo 445239278Sgonzo sc->lm_req = req; 446239278Sgonzo 447239278Sgonzo if (req->cmd->data && req->cmd->data->flags & MMC_DATA_WRITE) { 448239278Sgonzo memcpy(sc->lm_buffer, req->cmd->data->data, req->cmd->data->len); 449239278Sgonzo lpc_mmc_cmd(sc, req->cmd); 450239278Sgonzo lpc_mmc_unlock(sc); 451239278Sgonzo return (0); 452239278Sgonzo } 453239278Sgonzo 454239278Sgonzo if (req->cmd->data) 455239278Sgonzo lpc_mmc_setup_xfer(sc, req->cmd->data); 456239278Sgonzo 457239278Sgonzo lpc_mmc_cmd(sc, req->cmd); 458239278Sgonzo lpc_mmc_unlock(sc); 459239278Sgonzo 460239278Sgonzo return (0); 461239278Sgonzo} 462239278Sgonzo 463239278Sgonzostatic void 464239278Sgonzolpc_mmc_cmd(struct lpc_mmc_softc *sc, struct mmc_command *cmd) 465239278Sgonzo{ 466239278Sgonzo uint32_t cmdreg = 0; 467239278Sgonzo 468239278Sgonzo debugf("cmd: %d arg: 0x%08x\n", cmd->opcode, cmd->arg); 469239278Sgonzo 470239278Sgonzo if (lpc_mmc_read_4(sc, LPC_SD_COMMAND) & LPC_SD_COMMAND_ENABLE) { 471239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_COMMAND, 0); 472239278Sgonzo DELAY(1000); 473239278Sgonzo } 474239278Sgonzo 475239278Sgonzo sc->lm_flags &= ~LPC_SD_FLAGS_IGNORECRC; 476239278Sgonzo 477239278Sgonzo if (cmd->flags & MMC_RSP_PRESENT) 478239278Sgonzo cmdreg |= LPC_SD_COMMAND_RESPONSE; 479239278Sgonzo 480239278Sgonzo if (MMC_RSP(cmd->flags) == MMC_RSP_R2) 481239278Sgonzo cmdreg |= LPC_SD_COMMAND_LONGRSP; 482239278Sgonzo 483239278Sgonzo if (MMC_RSP(cmd->flags) == MMC_RSP_R3) 484239278Sgonzo sc->lm_flags |= LPC_SD_FLAGS_IGNORECRC; 485239278Sgonzo 486239278Sgonzo cmdreg |= LPC_SD_COMMAND_ENABLE; 487239278Sgonzo cmdreg |= (cmd->opcode & LPC_SD_COMMAND_CMDINDEXMASK); 488239278Sgonzo 489239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_MASK0, 0xffffffff); 490239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_MASK1, 0xffffffff); 491239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_ARGUMENT, cmd->arg); 492239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_COMMAND, cmdreg); 493239278Sgonzo} 494239278Sgonzo 495239278Sgonzostatic void 496239278Sgonzolpc_mmc_setup_xfer(struct lpc_mmc_softc *sc, struct mmc_data *data) 497239278Sgonzo{ 498239278Sgonzo uint32_t datactrl = 0; 499239278Sgonzo int data_words = data->len / 4; 500239278Sgonzo 501239278Sgonzo sc->lm_data = data; 502239278Sgonzo sc->lm_xfer_done = 0; 503239278Sgonzo 504239278Sgonzo debugf("data: %p, len: %d, %s\n", data, 505239278Sgonzo data->len, (data->flags & MMC_DATA_READ) ? "read" : "write"); 506239278Sgonzo 507239278Sgonzo if (data->flags & MMC_DATA_READ) { 508239278Sgonzo sc->lm_xfer_direction = DIRECTION_READ; 509239278Sgonzo lpc_dmac_setup_transfer(sc->lm_dev, LPC_MMC_DMACH_READ, 510260326Sian LPC_SD_PHYS_BASE + LPC_SD_FIFO, sc->lm_buffer_phys, 511239278Sgonzo data_words, 0); 512239278Sgonzo } 513239278Sgonzo 514239278Sgonzo if (data->flags & MMC_DATA_WRITE) { 515239278Sgonzo sc->lm_xfer_direction = DIRECTION_WRITE; 516239278Sgonzo lpc_dmac_setup_transfer(sc->lm_dev, LPC_MMC_DMACH_WRITE, 517260326Sian sc->lm_buffer_phys, LPC_SD_PHYS_BASE + LPC_SD_FIFO, 518239278Sgonzo data_words, 0); 519239278Sgonzo } 520239278Sgonzo 521239278Sgonzo datactrl |= (sc->lm_xfer_direction 522239278Sgonzo ? LPC_SD_DATACTRL_WRITE 523239278Sgonzo : LPC_SD_DATACTRL_READ); 524239278Sgonzo 525239278Sgonzo datactrl |= LPC_SD_DATACTRL_DMAENABLE | LPC_SD_DATACTRL_ENABLE; 526239278Sgonzo datactrl |= (ffs(data->len) - 1) << 4; 527239278Sgonzo 528239278Sgonzo debugf("datactrl: 0x%08x\n", datactrl); 529239278Sgonzo 530239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_DATATIMER, 0xFFFF0000); 531239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_DATALENGTH, data->len); 532239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_DATACTRL, datactrl); 533239278Sgonzo} 534239278Sgonzo 535239278Sgonzostatic int 536239278Sgonzolpc_mmc_read_ivar(device_t bus, device_t child, int which, 537239278Sgonzo uintptr_t *result) 538239278Sgonzo{ 539239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(bus); 540239278Sgonzo 541239278Sgonzo switch (which) { 542239278Sgonzo default: 543239278Sgonzo return (EINVAL); 544239278Sgonzo case MMCBR_IVAR_BUS_MODE: 545239278Sgonzo *(int *)result = sc->lm_host.ios.bus_mode; 546239278Sgonzo break; 547239278Sgonzo case MMCBR_IVAR_BUS_WIDTH: 548239278Sgonzo *(int *)result = sc->lm_host.ios.bus_width; 549239278Sgonzo break; 550239278Sgonzo case MMCBR_IVAR_CHIP_SELECT: 551239278Sgonzo *(int *)result = sc->lm_host.ios.chip_select; 552239278Sgonzo break; 553239278Sgonzo case MMCBR_IVAR_CLOCK: 554239278Sgonzo *(int *)result = sc->lm_host.ios.clock; 555239278Sgonzo break; 556239278Sgonzo case MMCBR_IVAR_F_MIN: 557239278Sgonzo *(int *)result = sc->lm_host.f_min; 558239278Sgonzo break; 559239278Sgonzo case MMCBR_IVAR_F_MAX: 560239278Sgonzo *(int *)result = sc->lm_host.f_max; 561239278Sgonzo break; 562239278Sgonzo case MMCBR_IVAR_HOST_OCR: 563239278Sgonzo *(int *)result = sc->lm_host.host_ocr; 564239278Sgonzo break; 565239278Sgonzo case MMCBR_IVAR_MODE: 566239278Sgonzo *(int *)result = sc->lm_host.mode; 567239278Sgonzo break; 568239278Sgonzo case MMCBR_IVAR_OCR: 569239278Sgonzo *(int *)result = sc->lm_host.ocr; 570239278Sgonzo break; 571239278Sgonzo case MMCBR_IVAR_POWER_MODE: 572239278Sgonzo *(int *)result = sc->lm_host.ios.power_mode; 573239278Sgonzo break; 574239278Sgonzo case MMCBR_IVAR_VDD: 575239278Sgonzo *(int *)result = sc->lm_host.ios.vdd; 576239278Sgonzo break; 577239278Sgonzo case MMCBR_IVAR_CAPS: 578239278Sgonzo *(int *)result = sc->lm_host.caps; 579239278Sgonzo break; 580239278Sgonzo case MMCBR_IVAR_MAX_DATA: 581239278Sgonzo *(int *)result = 1; 582239278Sgonzo break; 583239278Sgonzo } 584239278Sgonzo 585239278Sgonzo return (0); 586239278Sgonzo} 587239278Sgonzo 588239278Sgonzostatic int 589239278Sgonzolpc_mmc_write_ivar(device_t bus, device_t child, int which, 590239278Sgonzo uintptr_t value) 591239278Sgonzo{ 592239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(bus); 593239278Sgonzo 594239278Sgonzo switch (which) { 595239278Sgonzo default: 596239278Sgonzo return (EINVAL); 597239278Sgonzo case MMCBR_IVAR_BUS_MODE: 598239278Sgonzo sc->lm_host.ios.bus_mode = value; 599239278Sgonzo break; 600239278Sgonzo case MMCBR_IVAR_BUS_WIDTH: 601239278Sgonzo sc->lm_host.ios.bus_width = value; 602239278Sgonzo break; 603239278Sgonzo case MMCBR_IVAR_CHIP_SELECT: 604239278Sgonzo sc->lm_host.ios.chip_select = value; 605239278Sgonzo break; 606239278Sgonzo case MMCBR_IVAR_CLOCK: 607239278Sgonzo sc->lm_host.ios.clock = value; 608239278Sgonzo break; 609239278Sgonzo case MMCBR_IVAR_MODE: 610239278Sgonzo sc->lm_host.mode = value; 611239278Sgonzo break; 612239278Sgonzo case MMCBR_IVAR_OCR: 613239278Sgonzo sc->lm_host.ocr = value; 614239278Sgonzo break; 615239278Sgonzo case MMCBR_IVAR_POWER_MODE: 616239278Sgonzo sc->lm_host.ios.power_mode = value; 617239278Sgonzo break; 618239278Sgonzo case MMCBR_IVAR_VDD: 619239278Sgonzo sc->lm_host.ios.vdd = value; 620239278Sgonzo break; 621239278Sgonzo /* These are read-only */ 622239278Sgonzo case MMCBR_IVAR_CAPS: 623239278Sgonzo case MMCBR_IVAR_HOST_OCR: 624239278Sgonzo case MMCBR_IVAR_F_MIN: 625239278Sgonzo case MMCBR_IVAR_F_MAX: 626239278Sgonzo case MMCBR_IVAR_MAX_DATA: 627239278Sgonzo return (EINVAL); 628239278Sgonzo } 629239278Sgonzo return (0); 630239278Sgonzo} 631239278Sgonzo 632239278Sgonzostatic int 633239278Sgonzolpc_mmc_update_ios(device_t bus, device_t child) 634239278Sgonzo{ 635239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(bus); 636239278Sgonzo struct mmc_ios *ios = &sc->lm_host.ios; 637239278Sgonzo uint32_t clkdiv = 0, pwr = 0; 638239278Sgonzo 639239278Sgonzo if (ios->bus_width == bus_width_4) 640239278Sgonzo clkdiv |= LPC_SD_CLOCK_WIDEBUS; 641239278Sgonzo 642239278Sgonzo /* Calculate clock divider */ 643239278Sgonzo clkdiv = (LPC_SD_CLK / (2 * ios->clock)) - 1; 644239278Sgonzo 645239278Sgonzo /* Clock rate should not exceed rate requested in ios */ 646239278Sgonzo if ((LPC_SD_CLK / (2 * (clkdiv + 1))) > ios->clock) 647239278Sgonzo clkdiv++; 648239278Sgonzo 649239278Sgonzo debugf("clock: %dHz, clkdiv: %d\n", ios->clock, clkdiv); 650239278Sgonzo 651239278Sgonzo if (ios->bus_width == bus_width_4) { 652239278Sgonzo debugf("using wide bus mode\n"); 653239278Sgonzo clkdiv |= LPC_SD_CLOCK_WIDEBUS; 654239278Sgonzo } 655239278Sgonzo 656239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_CLOCK, clkdiv | LPC_SD_CLOCK_ENABLE); 657239278Sgonzo 658239278Sgonzo switch (ios->power_mode) { 659239278Sgonzo case power_off: 660239278Sgonzo pwr |= LPC_SD_POWER_CTRL_OFF; 661239278Sgonzo break; 662239278Sgonzo case power_up: 663239278Sgonzo pwr |= LPC_SD_POWER_CTRL_UP; 664239278Sgonzo break; 665239278Sgonzo case power_on: 666239278Sgonzo pwr |= LPC_SD_POWER_CTRL_ON; 667239278Sgonzo break; 668239278Sgonzo } 669239278Sgonzo 670239278Sgonzo if (ios->bus_mode == opendrain) 671239278Sgonzo pwr |= LPC_SD_POWER_OPENDRAIN; 672239278Sgonzo 673239278Sgonzo lpc_mmc_write_4(sc, LPC_SD_POWER, pwr); 674239278Sgonzo 675239278Sgonzo return (0); 676239278Sgonzo} 677239278Sgonzo 678239278Sgonzostatic int 679239278Sgonzolpc_mmc_get_ro(device_t bus, device_t child) 680239278Sgonzo{ 681239278Sgonzo 682239278Sgonzo return (0); 683239278Sgonzo} 684239278Sgonzo 685239278Sgonzostatic int 686239278Sgonzolpc_mmc_acquire_host(device_t bus, device_t child) 687239278Sgonzo{ 688239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(bus); 689239278Sgonzo int error = 0; 690239278Sgonzo 691239278Sgonzo lpc_mmc_lock(sc); 692239278Sgonzo while (sc->lm_bus_busy) 693239278Sgonzo error = mtx_sleep(sc, &sc->lm_mtx, PZERO, "mmcah", 0); 694239278Sgonzo 695239278Sgonzo sc->lm_bus_busy++; 696239278Sgonzo lpc_mmc_unlock(sc); 697239278Sgonzo return (error); 698239278Sgonzo} 699239278Sgonzo 700239278Sgonzostatic int 701239278Sgonzolpc_mmc_release_host(device_t bus, device_t child) 702239278Sgonzo{ 703239278Sgonzo struct lpc_mmc_softc *sc = device_get_softc(bus); 704239278Sgonzo 705239278Sgonzo lpc_mmc_lock(sc); 706239278Sgonzo sc->lm_bus_busy--; 707239278Sgonzo wakeup(sc); 708239278Sgonzo lpc_mmc_unlock(sc); 709239278Sgonzo return (0); 710239278Sgonzo} 711239278Sgonzo 712239278Sgonzostatic void lpc_mmc_dma_rxfinish(void *arg) 713239278Sgonzo{ 714239278Sgonzo} 715239278Sgonzo 716239278Sgonzostatic void lpc_mmc_dma_rxerror(void *arg) 717239278Sgonzo{ 718239278Sgonzo struct lpc_mmc_softc *sc = (struct lpc_mmc_softc *)arg; 719239278Sgonzo device_printf(sc->lm_dev, "DMA RX error\n"); 720239278Sgonzo} 721239278Sgonzo 722239278Sgonzostatic void lpc_mmc_dma_txfinish(void *arg) 723239278Sgonzo{ 724239278Sgonzo} 725239278Sgonzo 726239278Sgonzostatic void lpc_mmc_dma_txerror(void *arg) 727239278Sgonzo{ 728239278Sgonzo struct lpc_mmc_softc *sc = (struct lpc_mmc_softc *)arg; 729239278Sgonzo device_printf(sc->lm_dev, "DMA TX error\n"); 730239278Sgonzo} 731239278Sgonzo 732239278Sgonzostatic void 733239278Sgonzolpc_mmc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) 734239278Sgonzo{ 735239278Sgonzo struct lpc_mmc_dmamap_arg *ctx; 736239278Sgonzo 737239278Sgonzo if (err) 738239278Sgonzo return; 739239278Sgonzo 740239278Sgonzo ctx = (struct lpc_mmc_dmamap_arg *)arg; 741239278Sgonzo ctx->lm_dma_busaddr = segs[0].ds_addr; 742239278Sgonzo} 743239278Sgonzo 744239278Sgonzostatic device_method_t lpc_mmc_methods[] = { 745239278Sgonzo /* Device interface */ 746239278Sgonzo DEVMETHOD(device_probe, lpc_mmc_probe), 747239278Sgonzo DEVMETHOD(device_attach, lpc_mmc_attach), 748239278Sgonzo DEVMETHOD(device_detach, lpc_mmc_detach), 749239278Sgonzo 750239278Sgonzo /* Bus interface */ 751239278Sgonzo DEVMETHOD(bus_read_ivar, lpc_mmc_read_ivar), 752239278Sgonzo DEVMETHOD(bus_write_ivar, lpc_mmc_write_ivar), 753239278Sgonzo DEVMETHOD(bus_print_child, bus_generic_print_child), 754239278Sgonzo 755239278Sgonzo /* MMC bridge interface */ 756239278Sgonzo DEVMETHOD(mmcbr_update_ios, lpc_mmc_update_ios), 757239278Sgonzo DEVMETHOD(mmcbr_request, lpc_mmc_request), 758239278Sgonzo DEVMETHOD(mmcbr_get_ro, lpc_mmc_get_ro), 759239278Sgonzo DEVMETHOD(mmcbr_acquire_host, lpc_mmc_acquire_host), 760239278Sgonzo DEVMETHOD(mmcbr_release_host, lpc_mmc_release_host), 761239278Sgonzo 762239278Sgonzo { 0, 0 } 763239278Sgonzo}; 764239278Sgonzo 765239278Sgonzostatic devclass_t lpc_mmc_devclass; 766239278Sgonzo 767239278Sgonzostatic driver_t lpc_mmc_driver = { 768239278Sgonzo "lpcmmc", 769239278Sgonzo lpc_mmc_methods, 770239278Sgonzo sizeof(struct lpc_mmc_softc), 771239278Sgonzo}; 772239278Sgonzo 773239278SgonzoDRIVER_MODULE(lpcmmc, simplebus, lpc_mmc_driver, lpc_mmc_devclass, 0, 0); 774