FreeBSD Bugzilla – Attachment 150704 Details for
Bug 196081
[PATCH] ARM: sunxi: Add driver for the MMC/SD host found in the Allwinner A10 SoC
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
sunxi MMC/SD device driver
file.patch (text/plain), 45.73 KB, created by
Martin Galvan
on 2014-12-18 02:07:45 UTC
(
hide
)
Description:
sunxi MMC/SD device driver
Filename:
MIME Type:
Creator:
Martin Galvan
Created:
2014-12-18 02:07:45 UTC
Size:
45.73 KB
patch
obsolete
>diff --git a/sys/arm/allwinner/a10_clk.c b/sys/arm/allwinner/a10_clk.c >index af7e875..63bbc0d 100644 >--- a/sys/arm/allwinner/a10_clk.c >+++ b/sys/arm/allwinner/a10_clk.c >@@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); > #include <sys/watchdog.h> > #include <machine/bus.h> > #include <machine/cpu.h> >+#include <machine/frame.h> > #include <machine/intr.h> > > #include <dev/fdt/fdt_common.h> >@@ -69,16 +70,14 @@ static struct a10_ccm_softc *a10_ccm_sc = NULL; > static int > a10_ccm_probe(device_t dev) > { >- >- if (!ofw_bus_status_okay(dev)) >- return (ENXIO); >+ int result = ENXIO; > > if (ofw_bus_is_compatible(dev, "allwinner,sun4i-ccm")) { > device_set_desc(dev, "Allwinner Clock Control Module"); >- return(BUS_PROBE_DEFAULT); >+ result = BUS_PROBE_DEFAULT; > } > >- return (ENXIO); >+ return (result); > } > > static int >@@ -87,8 +86,9 @@ a10_ccm_attach(device_t dev) > struct a10_ccm_softc *sc = device_get_softc(dev); > int rid = 0; > >- if (a10_ccm_sc) >+ if (a10_ccm_sc) { > return (ENXIO); >+ } > > sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); > if (!sc->res) { >@@ -101,6 +101,8 @@ a10_ccm_attach(device_t dev) > > a10_ccm_sc = sc; > >+ ccm_write_4(sc, CCM_PLL6_CFG, CCM_PLL6_DEFAULT_CONFIG); >+ > return (0); > } > >@@ -126,8 +128,9 @@ a10_clk_usb_activate(void) > struct a10_ccm_softc *sc = a10_ccm_sc; > uint32_t reg_value; > >- if (sc == NULL) >+ if (sc == NULL) { > return (ENXIO); >+ } > > /* Gating AHB clock for USB */ > reg_value = ccm_read_4(sc, CCM_AHB_GATING0); >@@ -153,8 +156,9 @@ a10_clk_usb_deactivate(void) > struct a10_ccm_softc *sc = a10_ccm_sc; > uint32_t reg_value; > >- if (sc == NULL) >+ if (sc == NULL) { > return (ENXIO); >+ } > > /* Disable clock for USB */ > reg_value = ccm_read_4(sc, CCM_USB_CLK); >@@ -175,6 +179,40 @@ a10_clk_usb_deactivate(void) > } > > int >+a10_clk_mmc_activate(uint32_t *mod_clk) >+{ >+ struct a10_ccm_softc *sc = a10_ccm_sc; >+ uint32_t reg_value; >+ uint32_t pll6_clk; >+ uint32_t divider; >+ uint32_t n, k; >+ >+ if (sc == NULL) { >+ return (ENXIO); >+ } >+ >+ /* Gating AHB clock for SDMMC0 */ >+ reg_value = ccm_read_4(sc, CCM_AHB_GATING0); >+ reg_value |= CCM_AHB_GATING_SDMMC0 | CCM_AHB_GATING_DMA; >+ ccm_write_4(sc, CCM_AHB_GATING0, reg_value); >+ >+ /* Config mod clock */ >+ reg_value = ccm_read_4(sc, CCM_PLL6_CFG); >+ n = (reg_value >> 8) & 0x1f; >+ k = ((reg_value >> 4) & 3) + 1; >+ pll6_clk = 24000000 * n * k / 2; >+ >+ divider = ((pll6_clk + 99999999) / 100000000) - 1; >+ >+ ccm_write_4(sc, CCM_MMC0_SCLK_CFG, >+ CCM_MMC0_GATE_PASS | CCM_MMC0_CLK_SRC_PLL6 | divider); >+ >+ *mod_clk = pll6_clk / (divider + 1); >+ >+ return (0); >+} >+ >+int > a10_clk_emac_activate(void) { > struct a10_ccm_softc *sc = a10_ccm_sc; > uint32_t reg_value; >@@ -189,4 +227,3 @@ a10_clk_emac_activate(void) { > > return (0); > } >- >diff --git a/sys/arm/allwinner/a10_clk.h b/sys/arm/allwinner/a10_clk.h >index 3794196..26315b7 100644 >--- a/sys/arm/allwinner/a10_clk.h >+++ b/sys/arm/allwinner/a10_clk.h >@@ -103,15 +103,25 @@ > #define CCM_AHB_GATING_USB0 (1 << 0) > #define CCM_AHB_GATING_EHCI0 (1 << 1) > #define CCM_AHB_GATING_EHCI1 (1 << 3) >-#define CCM_AHB_GATING_EMAC (1 << 17) >+#define CCM_AHB_GATING_EMAC (1 << 17) >+#define CCM_AHB_GATING_DMA (1 << 6) >+#define CCM_AHB_GATING_SDMMC0 (1 << 8) > > #define CCM_USB_PHY (1 << 8) > #define CCM_USB0_RESET (1 << 0) > #define CCM_USB1_RESET (1 << 1) > #define CCM_USB2_RESET (1 << 2) > >+#define CCM_MMC0_CLK_SRC_PLL5 (2U << 24) >+#define CCM_MMC0_CLK_SRC_PLL6 (0x1 << 24) >+#define CCM_MMC0_GATE_MASK (0 << 31) >+#define CCM_MMC0_GATE_PASS (1 << 31) >+ >+#define CCM_PLL6_DEFAULT_CONFIG 0xa1009911 >+ > int a10_clk_usb_activate(void); > int a10_clk_usb_deactivate(void); > int a10_clk_emac_activate(void); >+int a10_clk_mmc_activate(uint32_t *mod_clk); > > #endif /* _A10_CLK_H_ */ >diff --git a/sys/arm/allwinner/a10_mmc.c b/sys/arm/allwinner/a10_mmc.c >new file mode 100644 >index 0000000..9b9edda >--- /dev/null >+++ b/sys/arm/allwinner/a10_mmc.c >@@ -0,0 +1,906 @@ >+/*- >+ * Copyright (c) 2013 Alexander Fedorov <alexander.fedorov@rtlservice.com> >+ * All rights reserved. >+ * >+ * Copyright (c) 2013-2014: >+ * Computer Architecture Laboratory, National University of Cordoba. Cordoba, Argentina. >+ * Nicolas Vidable <njvidable@gmail.com> >+ * Martin Galvan <omgalvan.86@gmail.com> >+ * 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. >+ * >+ * >+ * MMC/SD Host Controller driver for the Allwinner A10 SoC. >+ */ >+ >+#include <sys/cdefs.h> >+__FBSDID("$FreeBSD$"); >+ >+#include <sys/param.h> >+#include <sys/systm.h> >+#include <sys/bio.h> >+#include <sys/bus.h> >+#include <sys/conf.h> >+#include <sys/endian.h> >+#include <sys/kernel.h> >+#include <sys/kthread.h> >+#include <sys/lock.h> >+#include <sys/malloc.h> >+#include <sys/module.h> >+#include <sys/mutex.h> >+#include <sys/queue.h> >+#include <sys/resource.h> >+#include <sys/rman.h> >+#include <sys/time.h> >+#include <sys/timetc.h> >+#include <sys/watchdog.h> >+ >+#include <sys/kdb.h> >+ >+#include <machine/bus.h> >+#include <machine/cpu.h> >+#include <machine/cpufunc.h> >+#include <machine/resource.h> >+#include <machine/frame.h> >+#include <machine/intr.h> >+ >+#include <dev/ofw/ofw_bus.h> >+#include <dev/ofw/ofw_bus_subr.h> >+ >+#include <dev/mmc/bridge.h> >+#include <dev/mmc/mmcreg.h> >+#include <dev/mmc/mmcbrvar.h> >+ >+#include <arm/allwinner/a10_clk.h> >+#include <arm/allwinner/a10_mmc.h> >+ >+struct a10_mmc_softc { >+ device_t device; >+ struct mtx mutex; >+ struct mmc_host mmc_host; >+ struct mmc_request * mmc_request; >+ struct resource * memory_resource; >+ struct resource * interrupt_resource; >+ void * interrupt_handler; >+ uint32_t read_interrupts; /* Interrupts we've read so far. */ >+ uint32_t mod_clock; >+ int bus_busy; >+}; >+ >+static int a10_mmc_probe(device_t); >+static int a10_mmc_attach(device_t); >+static int a10_mmc_detach(device_t); >+static void a10_mmc_release_resources(struct a10_mmc_softc *); >+static int a10_mmc_reset_controller(struct a10_mmc_softc *); >+static void a10_mmc_clear_interrupts(struct a10_mmc_softc *); >+static void a10_mmc_enable_interrupts(struct a10_mmc_softc *); >+static void a10_mmc_interrupt_handler(void *); >+static void a10_mmc_request_ok(struct a10_mmc_softc *); >+static void a10_mmc_request_error(struct a10_mmc_softc *); >+static void a10_mmc_finalize_request(struct a10_mmc_softc *); >+static void a10_mmc_send_manual_stop(struct a10_mmc_softc *, struct mmc_request *); >+static void a10_mmc_set_bus_width(struct a10_mmc_softc *, struct mmc_ios *); >+static int a10_mmc_update_ios(device_t, device_t); >+static int a10_mmc_update_clock(struct a10_mmc_softc *); >+static int a10_mmc_set_clock_enabled(struct a10_mmc_softc *, int); >+static int a10_mmc_set_clock_rate(struct a10_mmc_softc *, struct mmc_ios *); >+static int a10_mmc_request(device_t, device_t, struct mmc_request *); >+static int a10_mmc_get_ro(device_t, device_t); >+static int a10_mmc_acquire_host(device_t, device_t); >+static int a10_mmc_release_host(device_t, device_t); >+static int a10_mmc_do_programmed_io(struct a10_mmc_softc *, struct mmc_data *); >+static int a10_mmc_timeout_without_command_done(struct a10_mmc_softc *); >+static int a10_mmc_interrupt_error(struct a10_mmc_softc *); >+static int a10_mmc_interrupt_done(uint32_t, struct mmc_data *); >+static int a10_mmc_fifo_wait(struct a10_mmc_softc *, uint32_t, uint32_t); >+ >+static inline void >+a10_mmc_lock(struct a10_mmc_softc *sc) >+{ >+ mtx_lock(&sc->mutex); >+} >+ >+static inline void >+a10_mmc_unlock(struct a10_mmc_softc *sc) >+{ >+ mtx_unlock(&sc->mutex); >+} >+ >+static inline uint32_t >+a10_mmc_read_4(struct a10_mmc_softc *sc, bus_size_t offset) >+{ >+ return (bus_read_4(sc->memory_resource, offset)); >+} >+ >+static inline void >+a10_mmc_write_4(struct a10_mmc_softc *sc, bus_size_t offset, uint32_t value) >+{ >+ bus_write_4(sc->memory_resource, offset, value); >+} >+ >+static int >+a10_mmc_probe(device_t device) >+{ >+ int result = ENXIO; >+ >+ if (ofw_bus_is_compatible(device, "allwinner,sun4i-mmc")) { >+ device_set_desc(device, "Allwinner Integrated MMC/SD controller"); >+ result = BUS_PROBE_DEFAULT; >+ } >+ >+ return (result); >+} >+ >+static int >+a10_mmc_attach(device_t device) >+{ >+ struct a10_mmc_softc *sc; >+ device_t child; >+ int resource_id; >+ int error; >+ >+ sc = device_get_softc(device); >+ sc->device = device; >+ sc->mmc_request = NULL; /* Set the current request to NULL. */ >+ >+ mtx_init(&sc->mutex, device_get_nameunit(sc->device), "a10_mmc", MTX_DEF); >+ >+ /* Allocate a memory window resource. */ >+ resource_id = 0; >+ sc->memory_resource = bus_alloc_resource_any(device, SYS_RES_MEMORY, >+ &resource_id, RF_ACTIVE); >+ >+ if (sc->memory_resource == NULL) { >+ device_printf(device, "Cannot allocate memory window!\n"); >+ >+ return (ENXIO); >+ } >+ >+ /* Allocate an interrupt resource. */ >+ resource_id = 0; >+ sc->interrupt_resource = bus_alloc_resource_any(device, SYS_RES_IRQ, &resource_id, >+ RF_ACTIVE | RF_SHAREABLE); >+ >+ if (sc->interrupt_resource == NULL) { >+ device_printf(device, "Cannot allocate interrupt resource!\n"); >+ a10_mmc_release_resources(sc); >+ >+ return (ENXIO); >+ } >+ >+ /* Set the ithread interrupt handler for our interrupt resource. */ >+ error = bus_setup_intr(device, sc->interrupt_resource, INTR_TYPE_MISC | >+ INTR_MPSAFE, NULL, a10_mmc_interrupt_handler, sc, >+ &sc->interrupt_handler); >+ >+ if (error) { >+ device_printf(device, "Cannot setup interrupt handler!\n"); >+ a10_mmc_release_resources(sc); >+ >+ return (ENXIO); >+ } >+ >+ /* Activate the MMC clock. */ >+ error = a10_clk_mmc_activate(&sc->mod_clock); >+ >+ if (error) { >+ device_printf(device, "Cannot activate clock!\n"); >+ a10_mmc_release_resources(sc); >+ >+ return (ENXIO); >+ } >+ >+ /* Reset controller. */ >+ error = a10_mmc_reset_controller(sc); >+ >+ if (error) { >+ a10_mmc_release_resources(sc); >+ >+ return (ENXIO); >+ } >+ >+ /* Configure timeout register. */ >+ a10_mmc_write_4(sc, A10_MMC_TIMEOUT_REG, 0xFFFFFFFF); >+ >+ /* Clear interrupt flags. */ >+ a10_mmc_clear_interrupts(sc); >+ >+ /* Initialization black magic: there's no documentation on why these >+ * values must be written here. >+ */ >+ a10_mmc_write_4(sc, A10_MMC_DEBUG_ENABLE_REG, 0xDEB); >+ a10_mmc_write_4(sc, A10_MMC_FUNCTION_SELECT_REG, A10_MMC_CE_ATA_ON); >+ >+ /* Set minimum and maximum operating frequencies (400kHz-50MHz). */ >+ sc->mmc_host.f_min = 400000; >+ sc->mmc_host.f_max = 52000000; >+ >+ /* Set operation conditions (voltage). */ >+ sc->mmc_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340; >+ >+ /* Set additional host controller capabilities. */ >+ sc->mmc_host.caps = MMC_CAP_4_BIT_DATA | MMC_CAP_HSPEED; >+ >+ /* Set mode. */ >+ sc->mmc_host.mode = mode_sd; >+ >+ device_set_ivars(device, &sc->mmc_host); >+ >+ child = device_add_child(device, "mmc", 0); >+ >+ if (child == NULL) { >+ device_printf(device, "Attaching MMC bus failed!\n"); >+ a10_mmc_release_resources(sc); >+ >+ return (ENXIO); >+ } >+ >+ device_set_ivars(device, &sc->mmc_host); >+ bus_generic_attach(device); >+ >+ return (0); >+} >+ >+static int >+a10_mmc_detach(device_t device) >+{ >+ a10_mmc_release_resources(device_get_softc(device)); >+ >+ return (EBUSY); >+} >+ >+/* Releases allocated resources. */ >+static void >+a10_mmc_release_resources(struct a10_mmc_softc *sc) >+{ >+ if (sc->interrupt_handler != NULL) { >+ bus_teardown_intr(sc->device, sc->interrupt_resource, sc->interrupt_handler); >+ sc->interrupt_handler = NULL; >+ } >+ >+ if (sc->memory_resource != NULL) { >+ bus_release_resource(sc->device, SYS_RES_MEMORY, 0, sc->memory_resource); >+ sc->memory_resource = NULL; >+ } >+ >+ if (sc->interrupt_resource != NULL) { >+ bus_release_resource(sc->device, SYS_RES_IRQ, 0, sc->interrupt_resource); >+ sc->interrupt_resource = NULL; >+ } >+} >+ >+/* Writes the hardware reset bits to the global control register. */ >+static int >+a10_mmc_reset_controller(struct a10_mmc_softc *sc) >+{ >+ int error = 0; >+ uint32_t time_left = 0xFFFF; >+ uint32_t reg_value; >+ >+ reg_value = a10_mmc_read_4(sc, A10_MMC_GLOBAL_CONTROL_REG); >+ reg_value |= A10_MMC_HARDWARE_RESET_BITS; >+ >+ a10_mmc_write_4(sc, A10_MMC_GLOBAL_CONTROL_REG, reg_value); >+ >+ /* Wait until the reset is done or a timeout occurs. */ >+ do { >+ reg_value = a10_mmc_read_4(sc, A10_MMC_GLOBAL_CONTROL_REG); >+ } while ((reg_value & A10_MMC_HARDWARE_RESET_BITS) && (--time_left)); >+ >+ if (!time_left) { >+ device_printf(sc->device, "Reset timeout!\n"); >+ error = EIO; >+ } >+ >+ return (error); >+} >+ >+/* Clear the interrupt status flags. */ >+static void >+a10_mmc_clear_interrupts(struct a10_mmc_softc *sc) >+{ >+ sc->read_interrupts = 0; >+ a10_mmc_write_4(sc, A10_MMC_RAW_INTERRUPT_STATUS_REG, 0xFFFFFFFF); >+ a10_mmc_write_4(sc, A10_MMC_INTERRUPT_MASK_REG, 0); >+} >+ >+/* Enable interrupts. */ >+static void >+a10_mmc_enable_interrupts(struct a10_mmc_softc *sc) >+{ >+ uint32_t reg_value; >+ >+ reg_value = a10_mmc_read_4(sc, A10_MMC_GLOBAL_CONTROL_REG); >+ reg_value |= A10_MMC_INTERRUPT_ENABLE; >+ >+ a10_mmc_write_4(sc, A10_MMC_GLOBAL_CONTROL_REG, reg_value); >+} >+ >+/* Service an MMC request by sending its associated command to the card and >+ * enabling the appropriate interrupts. */ >+static int >+a10_mmc_request(device_t bus, device_t child, struct mmc_request *request) >+{ >+ struct a10_mmc_softc *sc = device_get_softc(bus); >+ struct mmc_command *command = request->cmd; >+ uint32_t command_reg_value = A10_MMC_START; >+ uint32_t interrupt_mask = A10_MMC_COMMAND_DONE | A10_MMC_INTERRUPT_ERROR_BITS; >+ uint32_t block_size; >+ int error = 0; >+ >+ a10_mmc_lock(sc); >+ >+ if (sc->mmc_request) { >+ a10_mmc_unlock(sc); >+ return (EBUSY); >+ } >+ >+ sc->mmc_request = request; >+ >+ if (command->opcode == MMC_GO_IDLE_STATE) { >+ command_reg_value |= A10_MMC_SEND_INIT_SEQUENCE; >+ } >+ >+ if (command->flags & MMC_RSP_PRESENT) { >+ command_reg_value |= A10_MMC_RESPONSE_EXPECTED; >+ } >+ >+ if (command->flags & MMC_RSP_136) { >+ command_reg_value |= A10_MMC_LONG_RESPONSE; >+ } >+ >+ if (command->flags & MMC_RSP_CRC){ >+ command_reg_value |= A10_MMC_CHECK_RESPONSE_CRC; >+ } >+ >+ if (command->flags & MMC_RSP_BUSY) { >+ interrupt_mask |= A10_MMC_DATA_READ_TIMEOUT; >+ } >+ >+ if (command->data != NULL) { >+ command_reg_value |= A10_MMC_DATA_EXPECTED | A10_MMC_WAIT_PREVIOUS_DATA_OVER; >+ >+ if (request->stop != NULL) { /* Multiple block transfer. */ >+ command_reg_value |= A10_MMC_SEND_AUTO_STOP; >+ interrupt_mask |= A10_MMC_AUTO_COMMAND_DONE; >+ } else { /* Single-block transfer. */ >+ interrupt_mask |= A10_MMC_DATA_TRANSFER_OVER; >+ } >+ >+ if (command->data->flags & MMC_DATA_WRITE) { >+ command_reg_value |= A10_MMC_WRITE; >+ } else { /* MMC_DATA_READ */ >+ command_reg_value &= ~A10_MMC_WRITE; >+ } >+ >+ block_size = min(command->data->len, 512); >+ >+ a10_mmc_write_4(sc, A10_MMC_BLOCK_SIZE_REG, block_size); >+ a10_mmc_write_4(sc, A10_MMC_BYTE_COUNT_REG, command->data->len); >+ >+ /* Choose access by AHB. */ >+ a10_mmc_write_4(sc, A10_MMC_GLOBAL_CONTROL_REG, >+ a10_mmc_read_4(sc, A10_MMC_GLOBAL_CONTROL_REG) | >+ A10_MMC_ACCESS_BY_AHB); >+ } >+ >+ a10_mmc_write_4(sc, A10_MMC_INTERRUPT_MASK_REG, interrupt_mask); >+ a10_mmc_enable_interrupts(sc); >+ >+ /* Send the command to the card. */ >+ a10_mmc_write_4(sc, A10_MMC_ARGUMENT_REG, command->arg); >+ a10_mmc_write_4(sc, A10_MMC_COMMAND_REG, command_reg_value | command->opcode); >+ >+ if (command->data != NULL) { >+ error = a10_mmc_do_programmed_io(sc, command->data); >+ } >+ >+ a10_mmc_unlock(sc); >+ >+ return (error); >+} >+ >+/* Handle an IRQ by checking the interrupt status bits and the corresponding >+ * request. */ >+static void >+a10_mmc_interrupt_handler(void *arg) >+{ >+ struct a10_mmc_softc *sc = (struct a10_mmc_softc *)arg; >+ uint32_t masked_isr; >+ >+ a10_mmc_lock(sc); >+ >+ masked_isr = a10_mmc_read_4(sc, A10_MMC_MASKED_INTERRUPT_STATUS_REG); >+ >+ if (sc->mmc_request != NULL) { >+ sc->read_interrupts |= masked_isr; >+ >+ /* After a RESPONSE_TIMEOUT, we must wait for a COMMAND_DONE. >+ * Thus, if we've only read a RESPONSE_TIMEOUT from the ISR so far, >+ * enable COMMAND_DONE interrupts now. >+ */ >+ if (a10_mmc_timeout_without_command_done(sc)) { >+ a10_mmc_write_4(sc, A10_MMC_INTERRUPT_MASK_REG, A10_MMC_COMMAND_DONE); >+ } else if (a10_mmc_interrupt_error(sc)) { >+ device_printf(sc->device, "IRQ error! Interrupts: 0x%08X\n", sc->read_interrupts); >+ a10_mmc_request_error(sc); >+ } else if (a10_mmc_interrupt_done(sc->read_interrupts, sc->mmc_request->cmd->data)) { >+ a10_mmc_request_ok(sc); >+ } >+ } else { /* NULL request */ >+ device_printf(sc->device, "NULL MMC Request; Masked ISR: 0x%08X\n", masked_isr); >+ } >+ >+ /* Clear interrupts. */ >+ a10_mmc_write_4(sc, A10_MMC_RAW_INTERRUPT_STATUS_REG, masked_isr); >+ >+ a10_mmc_unlock(sc); >+} >+ >+/* Sometimes we may get a RESPONSE_TIMEOUT interrupt without the COMMAND_DONE >+ * bit set. >+ */ >+static int >+a10_mmc_timeout_without_command_done(struct a10_mmc_softc *sc) { >+ return (sc->read_interrupts & A10_MMC_RESPONSE_TIMEOUT && >+ !(sc->read_interrupts & A10_MMC_COMMAND_DONE)); >+} >+ >+static int >+a10_mmc_interrupt_error(struct a10_mmc_softc *sc) { >+ return (sc->read_interrupts & A10_MMC_INTERRUPT_ERROR_BITS); >+} >+ >+static int >+a10_mmc_interrupt_done(uint32_t read_interrupts, struct mmc_data *command_data) { >+ return ((read_interrupts & A10_MMC_COMMAND_DONE) || >+ ((command_data != NULL) && (read_interrupts & A10_MMC_DATA_TRANSFER_OVER))); >+} >+ >+/* As we don't have DMA support yet, we have to do PIO for every transfer. */ >+static int >+a10_mmc_do_programmed_io(struct a10_mmc_softc *sc, struct mmc_data *command_data) >+{ >+ int i = 0; >+ int error = 0; >+ uint32_t *buffer = (uint32_t *)command_data->data; >+ uint32_t data_length = command_data->len >> 2; >+ >+ if (command_data->flags & MMC_DATA_READ) { >+ while (i < data_length && !error) { >+ error = a10_mmc_fifo_wait(sc, 0xFFFFFFFF, A10_MMC_FIFO_EMPTY); >+ >+ if (!error) { >+ buffer[i] = a10_mmc_read_4(sc, A10_MMC_FIFO_ACCESS_ADDRESS); >+ ++i; >+ } >+ } >+ } else { /* MMC_DATA_WRITE */ >+ while (i < data_length && !error) { >+ error = a10_mmc_fifo_wait(sc, 0xFFFFFFFF, A10_MMC_FIFO_FULL); >+ >+ if (!error) { >+ a10_mmc_write_4(sc, A10_MMC_FIFO_ACCESS_ADDRESS, buffer[i]); >+ ++i; >+ } >+ } >+ } >+ >+ return (error); >+} >+ >+/* Wait until the FIFO is ready. */ >+static int >+a10_mmc_fifo_wait(struct a10_mmc_softc *sc, uint32_t time_left, uint32_t status_bits) >+{ >+ int error = 0; >+ uint32_t status_reg_value; >+ >+ do { >+ status_reg_value = a10_mmc_read_4(sc, A10_MMC_STATUS_REG); >+ } while ((status_reg_value & status_bits) && (--time_left)); >+ >+ if (!time_left) { >+ device_printf(sc->device, "Data transfer timeout!\n"); >+ error = EIO; >+ } >+ >+ return (error); >+} >+ >+/* We successfully completed a request. */ >+static void >+a10_mmc_request_ok(struct a10_mmc_softc *sc) >+{ >+ struct mmc_command *command = sc->mmc_request->cmd; >+ uint32_t response_status; >+ >+ do { >+ response_status = a10_mmc_read_4(sc, A10_MMC_STATUS_REG); >+ } while (response_status & A10_MMC_CARD_DATA_BUSY); >+ >+ if (command->flags & MMC_RSP_136) { >+ command->resp[0] = a10_mmc_read_4(sc, A10_MMC_RESPONSE_REG_3); >+ command->resp[1] = a10_mmc_read_4(sc, A10_MMC_RESPONSE_REG_2); >+ command->resp[2] = a10_mmc_read_4(sc, A10_MMC_RESPONSE_REG_1); >+ command->resp[3] = a10_mmc_read_4(sc, A10_MMC_RESPONSE_REG_0); >+ } else { >+ command->resp[0] = a10_mmc_read_4(sc, A10_MMC_RESPONSE_REG_0); >+ } >+ >+ command->error = MMC_ERR_NONE; >+ >+ a10_mmc_finalize_request(sc); >+} >+ >+/* An error occurred when the current request was being serviced. */ >+static void >+a10_mmc_request_error(struct a10_mmc_softc *sc) >+{ >+ struct mmc_command *command = sc->mmc_request->cmd; >+ >+ device_printf(sc->device, "Error in request.\n"); >+ >+ command->error = MMC_ERR_TIMEOUT; >+ >+ if (command->data != NULL) { >+ device_printf(sc->device, "Data error, sending manual stop.\n"); >+ a10_mmc_send_manual_stop(sc, sc->mmc_request); >+ } >+ >+ if (sc->mmc_request->stop != NULL) { >+ sc->mmc_request->stop->error = MMC_ERR_TIMEOUT; >+ } >+ >+ a10_mmc_finalize_request(sc); >+} >+ >+/* Finish handling a request. */ >+static void >+a10_mmc_finalize_request(struct a10_mmc_softc *sc) >+{ >+ struct mmc_request *request = sc->mmc_request; >+ >+ a10_mmc_write_4(sc, A10_MMC_GLOBAL_CONTROL_REG, >+ a10_mmc_read_4(sc, A10_MMC_GLOBAL_CONTROL_REG) | >+ A10_MMC_FIFO_RESET); >+ >+ sc->mmc_request = NULL; >+ request->done(request); >+ a10_mmc_clear_interrupts(sc); >+} >+ >+/* This host relies on manual stop commands being send on a data transfer error. */ >+static void >+a10_mmc_send_manual_stop(struct a10_mmc_softc *sc, struct mmc_request *request) >+{ >+ uint32_t response; >+ uint32_t raw_isr; >+ uint32_t command_reg_value = A10_MMC_START | A10_MMC_RESPONSE_EXPECTED | >+ A10_MMC_STOP_ABORT_COMMAND | A10_MMC_CHECK_RESPONSE_CRC | >+ MMC_STOP_TRANSMISSION; >+ >+ a10_mmc_clear_interrupts(sc); >+ >+ a10_mmc_write_4(sc, A10_MMC_ARGUMENT_REG, 0); >+ a10_mmc_write_4(sc, A10_MMC_COMMAND_REG, command_reg_value); >+ >+ do { >+ raw_isr = a10_mmc_read_4(sc, A10_MMC_RAW_INTERRUPT_STATUS_REG); >+ } while (!(raw_isr & >+ (A10_MMC_COMMAND_DONE | A10_MMC_INTERRUPT_ERROR_BITS))); >+ >+ if (!(raw_isr & A10_MMC_COMMAND_DONE) || (raw_isr & A10_MMC_INTERRUPT_ERROR_BITS)) { >+ device_printf(sc->device, "Manual stop failed.\n"); >+ response = MMC_ERR_TIMEOUT; >+ } else { >+ response = a10_mmc_read_4(sc, A10_MMC_RESPONSE_REG_0); >+ } >+ >+ if (request->stop != NULL) { >+ request->stop->resp[0] = response; >+ } >+} >+ >+/* Return the current values of this device's instance variables. */ >+static int >+a10_mmc_read_ivar(device_t bus, device_t child, int which, >+ uintptr_t *result) >+{ >+ struct a10_mmc_softc *sc = device_get_softc(bus); >+ int error = 0; >+ >+ switch (which) { >+ case MMCBR_IVAR_BUS_MODE: >+ *(int *)result = sc->mmc_host.ios.bus_mode; >+ break; >+ case MMCBR_IVAR_BUS_WIDTH: >+ *(int *)result = sc->mmc_host.ios.bus_width; >+ break; >+ case MMCBR_IVAR_CHIP_SELECT: >+ *(int *)result = sc->mmc_host.ios.chip_select; >+ break; >+ case MMCBR_IVAR_CLOCK: >+ *(int *)result = sc->mmc_host.ios.clock; >+ break; >+ case MMCBR_IVAR_F_MIN: >+ *(int *)result = sc->mmc_host.f_min; >+ break; >+ case MMCBR_IVAR_F_MAX: >+ *(int *)result = sc->mmc_host.f_max; >+ break; >+ case MMCBR_IVAR_HOST_OCR: >+ *(int *)result = sc->mmc_host.host_ocr; >+ break; >+ case MMCBR_IVAR_MODE: >+ *(int *)result = sc->mmc_host.mode; >+ break; >+ case MMCBR_IVAR_OCR: >+ *(int *)result = sc->mmc_host.ocr; >+ break; >+ case MMCBR_IVAR_POWER_MODE: >+ *(int *)result = sc->mmc_host.ios.power_mode; >+ break; >+ case MMCBR_IVAR_VDD: >+ *(int *)result = sc->mmc_host.ios.vdd; >+ break; >+ case MMCBR_IVAR_CAPS: >+ *(int *)result = sc->mmc_host.caps; >+ break; >+ case MMCBR_IVAR_MAX_DATA: >+ *(int *)result = 8192; /* This indicates we can handle multiblock transfers. */ >+ break; >+ default: >+ error = EINVAL; >+ } >+ >+ return (error); >+} >+ >+/* Set the current values of this device's instance variables. */ >+static int >+a10_mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value) >+{ >+ struct a10_mmc_softc *sc = device_get_softc(bus); >+ int result = 0; >+ >+ switch (which) { >+ case MMCBR_IVAR_BUS_MODE: >+ sc->mmc_host.ios.bus_mode = value; >+ break; >+ case MMCBR_IVAR_BUS_WIDTH: >+ sc->mmc_host.ios.bus_width = value; >+ break; >+ case MMCBR_IVAR_CHIP_SELECT: >+ sc->mmc_host.ios.chip_select = value; >+ break; >+ case MMCBR_IVAR_CLOCK: >+ sc->mmc_host.ios.clock = value; >+ break; >+ case MMCBR_IVAR_MODE: >+ sc->mmc_host.mode = value; >+ break; >+ case MMCBR_IVAR_OCR: >+ sc->mmc_host.ocr = value; >+ break; >+ case MMCBR_IVAR_POWER_MODE: >+ sc->mmc_host.ios.power_mode = value; >+ break; >+ case MMCBR_IVAR_VDD: >+ sc->mmc_host.ios.vdd = value; >+ break; >+ /* These are read-only. */ >+ case MMCBR_IVAR_CAPS: >+ case MMCBR_IVAR_HOST_OCR: >+ case MMCBR_IVAR_F_MIN: >+ case MMCBR_IVAR_F_MAX: >+ case MMCBR_IVAR_MAX_DATA: >+ default: >+ result = EINVAL; >+ } >+ >+ return (result); >+} >+ >+/* Update the current state of the host. */ >+static int >+a10_mmc_update_ios(device_t bus, device_t child) >+{ >+ int error = 0; >+ struct a10_mmc_softc *sc = device_get_softc(bus); >+ struct mmc_ios *ios = &sc->mmc_host.ios; >+ >+ /* Set the bus width. */ >+ a10_mmc_set_bus_width(sc, ios); >+ >+ if (ios->clock) { >+ /* Disable clock. */ >+ error = a10_mmc_set_clock_enabled(sc, 0); >+ >+ if (!error) { >+ /* Set clock rate. */ >+ error = a10_mmc_set_clock_rate(sc, ios); >+ >+ if (!error) { >+ /* Enable clock. */ >+ error = a10_mmc_set_clock_enabled(sc, 1); >+ } >+ } >+ } >+ >+ return (error); >+} >+ >+/* Enable/disable the clock. */ >+static int >+a10_mmc_set_clock_enabled(struct a10_mmc_softc *sc, int enabled) >+{ >+ int error = 0; >+ uint32_t clock_control_reg_value = a10_mmc_read_4(sc, A10_MMC_CLOCK_CONTROL_REG); >+ >+ if (enabled) { >+ clock_control_reg_value |= A10_MMC_CARD_CLOCK_ON; >+ } else { >+ clock_control_reg_value &= ~A10_MMC_CARD_CLOCK_ON; >+ } >+ >+ a10_mmc_write_4(sc, A10_MMC_CLOCK_CONTROL_REG, clock_control_reg_value); >+ >+ error = a10_mmc_update_clock(sc); >+ >+ return (error); >+} >+ >+/* Set the clock rate. */ >+static int >+a10_mmc_set_clock_rate(struct a10_mmc_softc *sc, struct mmc_ios *ios) >+{ >+ int error = 0; >+ uint32_t clock_divider = 0; >+ uint32_t clock_control_reg_value = a10_mmc_read_4(sc, A10_MMC_CLOCK_CONTROL_REG); >+ >+ clock_divider = (sc->mod_clock + (ios->clock >> 1)); >+ clock_divider /= ios->clock; >+ clock_divider /= 2; >+ >+ /* Change divider. */ >+ clock_control_reg_value &= ~(0xFF); >+ clock_control_reg_value |= clock_divider; >+ a10_mmc_write_4(sc, A10_MMC_CLOCK_CONTROL_REG, clock_control_reg_value); >+ >+ error = a10_mmc_update_clock(sc); >+ >+ return (error); >+} >+ >+/* Issue a command which will update the clock registers. */ >+static int >+a10_mmc_update_clock(struct a10_mmc_softc *sc) >+{ >+ int error = 0; >+ uint32_t time_left = 0xFFFF; >+ uint32_t raw_isr; >+ uint32_t command_reg_value = A10_MMC_START | >+ A10_MMC_UPDATE_CLOCK_REGS_ONLY | >+ A10_MMC_WAIT_PREVIOUS_DATA_OVER; >+ >+ a10_mmc_write_4(sc, A10_MMC_COMMAND_REG, command_reg_value); >+ >+ do { >+ command_reg_value = a10_mmc_read_4(sc, A10_MMC_COMMAND_REG); >+ } while ((command_reg_value & A10_MMC_START) && (--time_left)); >+ >+ /* Updating the clock may set some ISR bits; clear them now. */ >+ raw_isr = a10_mmc_read_4(sc, A10_MMC_RAW_INTERRUPT_STATUS_REG); >+ a10_mmc_write_4(sc, A10_MMC_RAW_INTERRUPT_STATUS_REG, raw_isr); >+ >+ if (!time_left) { >+ device_printf(sc->device, "Clock update timeout!"); >+ error = EIO; >+ } >+ >+ return (error); >+} >+ >+static void >+a10_mmc_set_bus_width(struct a10_mmc_softc *sc, struct mmc_ios *ios) >+{ >+ switch (ios->bus_width) { >+ case bus_width_1: >+ a10_mmc_write_4(sc, A10_MMC_WIDTH_REG, A10_MMC_WIDTH1); >+ break; >+ case bus_width_4: >+ a10_mmc_write_4(sc, A10_MMC_WIDTH_REG, A10_MMC_WIDTH4); >+ break; >+ case bus_width_8: >+ a10_mmc_write_4(sc, A10_MMC_WIDTH_REG, A10_MMC_WIDTH8); >+ } >+} >+ >+static int >+a10_mmc_get_ro(device_t bus, device_t child) >+{ >+ return (0); >+} >+ >+static int >+a10_mmc_acquire_host(device_t bus, device_t child) >+{ >+ struct a10_mmc_softc *sc = device_get_softc(bus); >+ int error = 0; >+ >+ a10_mmc_lock(sc); >+ >+ while (sc->bus_busy) { >+ error = mtx_sleep(sc, &sc->mutex, PZERO, "a10_mmc", 0); >+ } >+ >+ sc->bus_busy++; >+ a10_mmc_unlock(sc); >+ >+ return (error); >+} >+ >+static int >+a10_mmc_release_host(device_t bus, device_t child) >+{ >+ struct a10_mmc_softc *sc = device_get_softc(bus); >+ >+ a10_mmc_lock(sc); >+ sc->bus_busy--; >+ wakeup(sc); >+ a10_mmc_unlock(sc); >+ >+ return (0); >+} >+ >+static device_method_t a10_mmc_methods[] = { >+ /* Device interface. */ >+ DEVMETHOD(device_probe, a10_mmc_probe), >+ DEVMETHOD(device_attach, a10_mmc_attach), >+ DEVMETHOD(device_detach, a10_mmc_detach), >+ >+ /* Bus interface. */ >+ DEVMETHOD(bus_read_ivar, a10_mmc_read_ivar), >+ DEVMETHOD(bus_write_ivar, a10_mmc_write_ivar), >+ DEVMETHOD(bus_print_child, bus_generic_print_child), >+ >+ /* MMC bridge interface. */ >+ DEVMETHOD(mmcbr_update_ios, a10_mmc_update_ios), >+ DEVMETHOD(mmcbr_request, a10_mmc_request), >+ DEVMETHOD(mmcbr_get_ro, a10_mmc_get_ro), >+ DEVMETHOD(mmcbr_acquire_host, a10_mmc_acquire_host), >+ DEVMETHOD(mmcbr_release_host, a10_mmc_release_host), >+ >+ { 0, 0 } >+}; >+ >+static devclass_t a10_mmc_devclass; >+ >+static driver_t a10_mmc_driver = { >+ "a10_mmc", >+ a10_mmc_methods, >+ sizeof(struct a10_mmc_softc) >+}; >+ >+DRIVER_MODULE(a10_mmc, simplebus, a10_mmc_driver, a10_mmc_devclass, 0, 0); >diff --git a/sys/arm/allwinner/a10_mmcreg.h b/sys/arm/allwinner/a10_mmcreg.h >new file mode 100644 >index 0000000..6268f17 >--- /dev/null >+++ b/sys/arm/allwinner/a10_mmcreg.h >@@ -0,0 +1,223 @@ >+/*- >+ * Copyright (c) 2013 Alexander Fedorov <alexander.fedorov@rtlservice.com> >+ * All rights reserved. >+ * >+ * Copyright (c) 2013-2014: >+ * Computer Architecture Laboratory, National University of Cordoba. Cordoba, Argentina. >+ * Nicolas Vidable <njvidable@gmail.com> >+ * Martin Galvan <omgalvan.86@gmail.com> >+ * 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. >+ * >+ */ >+ >+#ifndef _A10_MMC_H_ >+#define _A10_MMC_H_ >+ >+#define A10_MMC_GLOBAL_CONTROL_REG 0x00 /* Global Control Register */ >+#define A10_MMC_CLOCK_CONTROL_REG 0x04 /* Clock Control Register */ >+#define A10_MMC_TIMEOUT_REG 0x08 /* Time Out Register */ >+#define A10_MMC_WIDTH_REG 0x0C /* Bus Width Register */ >+#define A10_MMC_BLOCK_SIZE_REG 0x10 /* Block Size Register */ >+#define A10_MMC_BYTE_COUNT_REG 0x14 /* Byte Count Register */ >+#define A10_MMC_COMMAND_REG 0x18 /* Command Register */ >+#define A10_MMC_ARGUMENT_REG 0x1C /* Argument Register */ >+#define A10_MMC_RESPONSE_REG_0 0x20 /* Response Register 0 */ >+#define A10_MMC_RESPONSE_REG_1 0x24 /* Response Register 1 */ >+#define A10_MMC_RESPONSE_REG_2 0x28 /* Response Register 2 */ >+#define A10_MMC_RESPONSE_REG_3 0x2C /* Response Register 3 */ >+#define A10_MMC_INTERRUPT_MASK_REG 0x30 /* Interrupt Mask Register */ >+#define A10_MMC_MASKED_INTERRUPT_STATUS_REG 0x34 /* Masked Interrupt Status Register */ >+#define A10_MMC_RAW_INTERRUPT_STATUS_REG 0x38 /* Raw Interrupt Status Register */ >+#define A10_MMC_STATUS_REG 0x3C /* Status Register */ >+#define A10_MMC_FIFO_THRESHOLD_WATERMARK_REG 0x40 /* FIFO Threshold Watermark Register */ >+#define A10_MMC_FUNCTION_SELECT_REG 0x44 /* Function Select Register */ >+#define A10_MMC_CIU_BYTE_COUNT_REG 0x48 /* CIU Byte Count Register */ >+#define A10_MMC_BIU_BYTE_COUNT_REG 0x4C /* BIU Byte Count Register */ >+#define A10_MMC_DEBUG_ENABLE_REG 0x50 /* Debug Enable Register */ >+#define A10_MMC_IDMAC_CONTROL_REG 0x80 /* IDMAC Control Register */ >+#define A10_MMC_IDMAC_DESCRIPTOR_LIST_BASE_ADDRESS_REG 0x84 /* IDMAC Descriptor List Base Address Register */ >+#define A10_MMC_IDMAC_STATUS_REG 0x88 /* IDMAC Status Register */ >+#define A10_MMC_IDMAC_INTERRUPT_ENABLE_REG 0x8C /* IDMAC Interrupt Enable Register */ >+#define A10_MMC_CURRENT_HOST_DESCRIPTOR_ADDRESS_REG 0x90 /* IDMAC Current Host Descriptor Address */ >+#define A10_MMC_CURRENT_BUFFER_DESCRIPTOR_ADDRESS_REG 0x94 /* IDMAC Current Buffer Descriptor Address */ >+#define A10_MMC_FIFO_ACCESS_ADDRESS 0x100 /* FIFO Access Address */ >+ >+/* Global control register bits (A10_MMC_GLOBAL_CONTROL_REG) */ >+#define A10_MMC_SOFT_RESET (1 << 0) >+#define A10_MMC_FIFO_RESET (1 << 1) >+#define A10_MMC_DMA_RESET (1 << 2) >+#define A10_MMC_INTERRUPT_ENABLE (1 << 4) >+#define A10_MMC_DMA_ENABLE (1 << 5) >+#define A10_MMC_DEBOUNCE_ENABLE (1 << 8) >+#define A10_MMC_POSEDGE_LATCH_DATA (1 << 9) /* 0: Negedge; 1: Posedge */ >+#define A10_MMC_DDR_MODE (1 << 10) >+#define A10_MMC_ACCESS_DONE_DIRECT (1 << 30) >+#define A10_MMC_ACCESS_BY_AHB (1 << 31) /* 0: DMA; 1: AHB */ >+#define A10_MMC_HARDWARE_RESET_BITS \ >+ (A10_MMC_SOFT_RESET | \ >+ A10_MMC_FIFO_RESET | \ >+ A10_MMC_DMA_RESET) >+ >+/* Clock control bits (A10_MMC_CLOCK_CONTROL_REG) */ >+#define A10_MMC_CARD_CLOCK_ON (1 << 16) >+#define A10_MMC_CARD_CLOCK_OFF (0 << 16) >+#define A10_MMC_LOW_POWER_ON (1 << 17) >+#define A10_MMC_LOW_POWER_OFF (0 << 17) >+ >+/* Bus width (A10_MMC_WIDTH_REG) */ >+#define A10_MMC_WIDTH1 (0) >+#define A10_MMC_WIDTH4 (1) >+#define A10_MMC_WIDTH8 (2) >+ >+/* Command bits (A10_MMC_COMMAND_REG) */ >+#define A10_MMC_RESPONSE_EXPECTED (1 << 6) >+#define A10_MMC_LONG_RESPONSE (1 << 7) >+#define A10_MMC_CHECK_RESPONSE_CRC (1 << 8) >+#define A10_MMC_DATA_EXPECTED (1 << 9) >+#define A10_MMC_WRITE (1 << 10) >+#define A10_MMC_STREAM_MODE (1 << 11) /* 0: Block mode; 1: Stream mode */ >+#define A10_MMC_SEND_AUTO_STOP (1 << 12) >+#define A10_MMC_WAIT_PREVIOUS_DATA_OVER (1 << 13) >+#define A10_MMC_STOP_ABORT_COMMAND (1 << 14) >+#define A10_MMC_SEND_INIT_SEQUENCE (1 << 15) >+#define A10_MMC_UPDATE_CLOCK_REGS_ONLY (1 << 21) >+#define A10_MMC_READ_CEATA_DEVICE (1 << 22) >+#define A10_MMC_CCS_EXPECTED (1 << 23) /* CCS: Command Completion Signal */ >+#define A10_MMC_ENABLE_BOOT (1 << 24) >+#define A10_MMC_ALT_BOOT_OPTIONS (1 << 25) /* 0: Mandatory; 1: Alternative */ >+#define A10_MMC_BOOT_ACK_EXPIRE (1 << 26) >+#define A10_MMC_DISABLE_BOOT (1 << 27) >+#define A10_MMC_VOLTAGE_SWITCH (1 << 28) >+#define A10_MMC_START (1 << 31) >+ >+/* Interrupt bits (A10_MMC_INTERRUPT_MASK_REG, A10_MMC_MASKED_INTERRUPT_STATUS_REG >+ *and A10_MMC_RAW_INTERRUPT_STATUS_REG) >+ */ >+#define A10_MMC_RESPONSE_ERROR (1 << 1) >+#define A10_MMC_COMMAND_DONE (1 << 2) >+#define A10_MMC_DATA_TRANSFER_OVER (1 << 3) >+#define A10_MMC_TX_DATA_REQUEST (1 << 4) >+#define A10_MMC_RX_DATA_REQUEST (1 << 5) >+#define A10_MMC_RESPONSE_CRC_ERROR (1 << 6) >+#define A10_MMC_DATA_CRC_ERROR (1 << 7) >+#define A10_MMC_RESPONSE_TIMEOUT (1 << 8) >+#define A10_MMC_BOOT_ACK_RECEIVED (1 << 8) >+#define A10_MMC_DATA_READ_TIMEOUT (1 << 9) >+#define A10_MMC_BOOT_DATA_START (1 << 9) >+#define A10_MMC_DATA_STARVATION_BY_HOST (1 << 10) >+#define A10_MMC_VOLTAGE_CHANGE_DONE (1 << 10) >+#define A10_MMC_FIFO_RUN_ERROR (1 << 11) /* Underrun/overrun error */ >+#define A10_MMC_HARDWARE_LOCKED (1 << 12) >+#define A10_MMC_START_BIT_ERROR (1 << 13) >+#define A10_MMC_AUTO_COMMAND_DONE (1 << 14) >+#define A10_MMC_END_BIT_ERROR (1 << 15) >+#define A10_MMC_SDIO_INTERRUPT (1 << 16) >+#define A10_MMC_CARD_INSERT (1 << 30) >+#define A10_MMC_CARD_REMOVE (1 << 31) >+#define A10_MMC_INTERRUPT_ERROR_BITS \ >+ (A10_MMC_RESPONSE_ERROR | \ >+ A10_MMC_RESPONSE_CRC_ERROR | \ >+ A10_MMC_DATA_CRC_ERROR | \ >+ A10_MMC_RESPONSE_TIMEOUT | \ >+ A10_MMC_DATA_READ_TIMEOUT | \ >+ A10_MMC_FIFO_RUN_ERROR | \ >+ A10_MMC_HARDWARE_LOCKED | \ >+ A10_MMC_START_BIT_ERROR | \ >+ A10_MMC_END_BIT_ERROR) >+#define A10_MMC_INTERRUPT_DONE_BITS \ >+ (A10_MMC_AUTO_COMMAND_DONE | \ >+ A10_MMC_DATA_TRANSFER_OVER | \ >+ A10_MMC_COMMAND_DONE | \ >+ A10_MMC_VOLTAGE_CHANGE_DONE) >+ >+/* Status bits (A10_MMC_STATUS_REG) */ >+#define A10_MMC_RX_WATERMARK_LEVEL_REACHED (1 << 0) >+#define A10_MMC_TX_WATERMARK_LEVEL_REACHED (1 << 1) >+#define A10_MMC_FIFO_EMPTY (1 << 2) >+#define A10_MMC_FIFO_FULL (1 << 3) >+#define A10_MMC_CARD_PRESENT (1 << 8) >+#define A10_MMC_CARD_DATA_BUSY (1 << 9) >+#define A10_MMC_DATA_FSM_BUSY (1 << 10) >+#define A10_MMC_DMA_REQUEST (1 << 31) >+#define A10_MMC_FIFO_SIZE (16) >+ >+/* Function select bits (A10_MMC_FUNCTION_SELECT_REG) */ >+#define A10_MMC_CE_ATA_ON (0xCEAAU << 16) >+#define A10_MMC_SEND_IRQ_RESPONSE (1 << 0) >+#define A10_MMC_SDIO_READ_WAIT (1 << 1) >+#define A10_MMC_ABORT_READ_DATA (1 << 2) >+#define A10_MMC_SEND_CC_SD (1 << 8) >+#define A10_MMC_SEND_AUTO_STOP_CC_SD (1 << 9) >+#define A10_MMC_CE_ATA_DEVICE_INTERRUPT_ENABLE (1 << 10) >+ >+/* IDMA descriptor fields and configuration bits */ >+#define A10_MMC_ALIGNMENT 4 >+#define A10_MMC_MAX_BLOCK_SIZE 4096 >+#define A10_MMC_IDMAC_MAX_SEGMENT_SIZE 8192 >+#define A10_MMC_IDMAC_DISABLE_INTERRUPT_ON_COMPLETION (1 << 1) /* Disable interrupt on completion */ >+#define A10_MMC_IDMAC_LAST_DESCRIPTOR (1 << 2) /* Last descriptor */ >+#define A10_MMC_IDMAC_FIRST_DESCRIPTOR (1 << 3) /* First descriptor */ >+#define A10_MMC_IDMAC_SECOND_ADDRESS_CHAINED (1 << 4) /* Chain mode */ >+#define A10_MMC_IDMAC_OWNED_BY_IDMAC (1 << 31) /* 1: owned by IDMAC; 0: owned by host */ >+#define A10_MMC_IDMAC_DESCRIPTOR_CONFIG_BITS \ >+ (A10_MMC_IDMAC_SECOND_ADDRESS_CHAINED | \ >+ A10_MMC_IDMAC_OWNED_BY_IDMAC | \ >+ A10_MMC_IDMAC_DISABLE_INTERRUPT_ON_COMPLETION) >+ >+/* IDMA controller bus mod bit field */ >+#define A10_MMC_IDMAC_SOFT_RESET (1 << 0) >+#define A10_MMC_IDMAC_FIX_BURST (1 << 1) >+#define A10_MMC_IDMAC_IDMA_ON (1 << 7) >+#define A10_MMC_IDMAC_REFETCH_DES (1 << 31) >+ >+/* IDMA controller status bits (A10_MMC_IDMAC_STATUS_REG) */ >+#define A10_MMC_IDMAC_TRANSMIT_INTERRUPT (1 << 0) >+#define A10_MMC_IDMAC_RECEIVE_INTERRUPT (1 << 1) >+#define A10_MMC_IDMAC_FATAL_BUS_ERROR (1 << 2) >+#define A10_MMC_IDMAC_DESCRIPTOR_UNAVAILABLE (1 << 4) >+#define A10_MMC_IDMAC_CARD_ERROR_SUMMARY (1 << 5) >+#define A10_MMC_IDMAC_NORMAL_INTERRUPT_SUMMARY (1 << 8) >+#define A10_MMC_IDMAC_ABNORMAL_INTERRUPT_SUMMARY (1 << 9) >+#define A10_MMC_IDMAC_HOST_ABORT (1 << 10) >+#define A10_MMC_IDMAC_SUSPEND (1 << 13) /* 0: Idle; 1: Suspend */ >+#define A10_MMC_IDMAC_DESC_READ (0x2U << 13) >+#define A10_MMC_IDMAC_DESC_CHECK (0x3U << 13) >+#define A10_MMC_IDMAC_READ_REQUEST_WAIT (0x4U << 13) >+#define A10_MMC_IDMAC_WRITE_REQUEST_WAIT (0x5U << 13) >+#define A10_MMC_IDMAC_READ (0x6U << 13) >+#define A10_MMC_IDMAC_WRITE (0x7U << 13) >+#define A10_MMC_IDMAC_DESC_CLOSE (0x8U << 13) >+ >+#define A10_MMC_IDMAC_OVER_BITS \ >+ (A10_MMC_IDMAC_TRANSMIT_INTERRUPT | \ >+ A10_MMC_IDMAC_RECEIVE_INTERRUPT | \ >+ A10_MMC_IDMAC_NORMAL_INTERRUPT_SUMMARY) >+ >+#define A10_MMC_IDMAC_ERROR_BITS \ >+ (A10_MMC_IDMAC_FATAL_BUS_ERROR | \ >+ A10_MMC_IDMAC_DESCRIPTOR_UNAVAILABLE | \ >+ A10_MMC_IDMAC_CARD_ERROR_SUMMARY | \ >+ A10_MMC_IDMAC_ABNORMAL_INTERRUPT_SUMMARY) >+ >+#endif /* _A10_MMC_H_ */ >diff --git a/sys/arm/allwinner/files.a10 b/sys/arm/allwinner/files.a10 >index c14ef2e..fc8ed58 100644 >--- a/sys/arm/allwinner/files.a10 >+++ b/sys/arm/allwinner/files.a10 >@@ -15,6 +15,7 @@ arm/allwinner/a10_ehci.c optional ehci > arm/allwinner/a10_machdep.c standard > arm/allwinner/a10_sramc.c standard > arm/allwinner/a10_wdog.c standard >+arm/allwinner/a10_mmc.c optional mmc > arm/allwinner/a20/a20_cpu_cfg.c standard > arm/allwinner/aintc.c standard > arm/allwinner/if_emac.c optional emac >diff --git a/sys/arm/conf/CUBIEBOARD b/sys/arm/conf/CUBIEBOARD >index a0b318e..c53d588 100644 >--- a/sys/arm/conf/CUBIEBOARD >+++ b/sys/arm/conf/CUBIEBOARD >@@ -78,8 +78,8 @@ options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed > #options BOOTP_WIRED_TO=cpsw0 > > # MMC/SD/SDIO card slot support >-#device mmc # mmc/sd bus >-#device mmcsd # mmc/sd flash cards >+device mmc # mmc/sd bus >+device mmcsd # mmc/sd flash cards > > # Boot device is 2nd slice on MMC/SD card > options ROOTDEVNAME=\"ufs:/dev/da0s2\" >diff --git a/sys/boot/fdt/dts/arm/cubieboard.dts b/sys/boot/fdt/dts/arm/cubieboard.dts >index 636b5ce..c83f5d5 100644 >--- a/sys/boot/fdt/dts/arm/cubieboard.dts >+++ b/sys/boot/fdt/dts/arm/cubieboard.dts >@@ -60,6 +60,10 @@ > emac@01c0b000 { > status = "okay"; > }; >+ >+ mmc@0x01c0f000 { >+ status = "okay"; >+ }; > }; > > chosen { >diff --git a/sys/boot/fdt/dts/arm/sun4i-a10.dtsi b/sys/boot/fdt/dts/arm/sun4i-a10.dtsi >index 09eebaa..0301d63 100644 >--- a/sys/boot/fdt/dts/arm/sun4i-a10.dtsi >+++ b/sys/boot/fdt/dts/arm/sun4i-a10.dtsi >@@ -103,6 +103,13 @@ > interrupt-parent = <&AINTC>; > }; > >+ mmc@0x01c0f000 { >+ compatible = "allwinner,sun4i-mmc"; >+ reg = <0x01c0f000 0x1000>; >+ interrupts = <32>; >+ interrupt-parent = <&AINTC>; >+ }; >+ > sata@01c18000 { > compatible = "allwinner,sun4i-ahci"; > reg = <0x01c18000 0x1000>; >diff --git a/sys/dev/mmc/mmc.c b/sys/dev/mmc/mmc.c >index a7762ba..b8a118d 100644 >--- a/sys/dev/mmc/mmc.c >+++ b/sys/dev/mmc/mmc.c >@@ -1774,4 +1774,4 @@ DRIVER_MODULE(mmc, sdhci_pci, mmc_driver, mmc_devclass, NULL, NULL); > DRIVER_MODULE(mmc, sdhci_ti, mmc_driver, mmc_devclass, NULL, NULL); > DRIVER_MODULE(mmc, ti_mmchs, mmc_driver, mmc_devclass, NULL, NULL); > DRIVER_MODULE(mmc, dwmmc, mmc_driver, mmc_devclass, NULL, NULL); >- >+DRIVER_MODULE(mmc, a10_mmc, mmc_driver, mmc_devclass, NULL, NULL);
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
omgalvan.86
:
maintainer-approval?
(
imp
)
omgalvan.86
:
maintainer-approval?
(
ian
)
Actions:
View
|
Diff
Attachments on
bug 196081
: 150704 |
151035
|
154947
|
157917