Author: loos
Date: Thu May 21 17:39:42 2015
New Revision: 283253
URL: https://svnweb.freebsd.org/changeset/base/283253

Log:
  Add the MMC/SD driver for Allwinner SoCs.
  
  This is based on the patch sent by Alexander Fedorov with the following
  fixes/improvements:
  
   - Better error handling;
   - Clock is derived from PLL6 (obtained from netbsd);
   - No more unnecessary busy loops on interrupt handler;
   - style(9) fixes and code cleanup.
  
  I also want to thanks Martin Galvan who has sent an alternative
  implementation with some interesting fixes.
  
  Tested on CubieBoard2, Banana-Pi (thanks to netgate!) and Cubieboard1
  (Pratik Singhal).
  
  This is intended to pave the way for the upcoming GSoC work (and make
  easier the build of images for the supported boards).
  
  PR:           196081
  Submitted by: Alexander Fedorov <alexander.fedo...@rtlservice.com>

Added:
  head/sys/arm/allwinner/a10_mmc.c   (contents, props changed)
  head/sys/arm/allwinner/a10_mmc.h   (contents, props changed)
Modified:
  head/sys/arm/allwinner/a10_clk.c
  head/sys/arm/allwinner/a10_clk.h
  head/sys/arm/allwinner/files.allwinner
  head/sys/arm/conf/CUBIEBOARD
  head/sys/arm/conf/CUBIEBOARD2
  head/sys/boot/fdt/dts/arm/cubieboard.dts
  head/sys/boot/fdt/dts/arm/cubieboard2.dts
  head/sys/boot/fdt/dts/arm/sun4i-a10.dtsi
  head/sys/boot/fdt/dts/arm/sun7i-a20.dtsi
  head/sys/dev/mmc/mmc.c

Modified: head/sys/arm/allwinner/a10_clk.c
==============================================================================
--- head/sys/arm/allwinner/a10_clk.c    Thu May 21 17:39:42 2015        
(r283252)
+++ head/sys/arm/allwinner/a10_clk.c    Thu May 21 17:39:42 2015        
(r283253)
@@ -174,7 +174,8 @@ a10_clk_usb_deactivate(void)
 }
 
 int
-a10_clk_emac_activate(void) {
+a10_clk_emac_activate(void)
+{
        struct a10_ccm_softc *sc = a10_ccm_sc;
        uint32_t reg_value;
 
@@ -189,3 +190,110 @@ a10_clk_emac_activate(void) {
        return (0);
 }
 
+static void
+a10_clk_pll6_enable(void)
+{
+       struct a10_ccm_softc *sc;
+       uint32_t reg_value;
+
+       /*
+        * SATA needs PLL6 to be a 100MHz clock.
+        * The SATA output frequency is 24MHz * n * k / m / 6.
+        * To get to 100MHz, k & m must be equal and n must be 25.
+        * For other uses the output frequency is 24MHz * n * k / 2.
+        */
+       sc = a10_ccm_sc;
+       reg_value = ccm_read_4(sc, CCM_PLL6_CFG);
+       reg_value &= ~CCM_PLL_CFG_BYPASS;
+       reg_value &= ~(CCM_PLL_CFG_FACTOR_K | CCM_PLL_CFG_FACTOR_M |
+           CCM_PLL_CFG_FACTOR_N);
+       reg_value |= (25 << CCM_PLL_CFG_FACTOR_N_SHIFT);
+       reg_value |= CCM_PLL6_CFG_SATA_CLKEN;
+       reg_value |= CCM_PLL_CFG_ENABLE;
+       ccm_write_4(sc, CCM_PLL6_CFG, reg_value);
+}
+
+static unsigned int
+a10_clk_pll6_get_rate(void)
+{
+       struct a10_ccm_softc *sc;
+       uint32_t k, n, reg_value;
+
+       sc = a10_ccm_sc;
+       reg_value = ccm_read_4(sc, CCM_PLL6_CFG);
+       n = ((reg_value & CCM_PLL_CFG_FACTOR_N) >> CCM_PLL_CFG_FACTOR_N_SHIFT);
+       k = ((reg_value & CCM_PLL_CFG_FACTOR_K) >> CCM_PLL_CFG_FACTOR_K_SHIFT) +
+           1;
+
+       return ((CCM_CLK_REF_FREQ * n * k) / 2);
+}
+
+int
+a10_clk_mmc_activate(int devid)
+{
+       struct a10_ccm_softc *sc;
+       uint32_t reg_value;
+
+       sc = a10_ccm_sc;
+       if (sc == NULL)
+               return (ENXIO);
+
+       a10_clk_pll6_enable();
+
+       /* Gating AHB clock for SD/MMC */
+       reg_value = ccm_read_4(sc, CCM_AHB_GATING0);
+       reg_value |= CCM_AHB_GATING_SDMMC0 << devid;
+       ccm_write_4(sc, CCM_AHB_GATING0, reg_value);
+
+       return (0);
+}
+
+int
+a10_clk_mmc_cfg(int devid, int freq)
+{
+       struct a10_ccm_softc *sc;
+       uint32_t clksrc, m, n, ophase, phase, reg_value;
+       unsigned int pll_freq;
+
+       sc = a10_ccm_sc;
+       if (sc == NULL)
+               return (ENXIO);
+
+       freq /= 1000;
+       if (freq <= 400) {
+               pll_freq = CCM_CLK_REF_FREQ / 1000;
+               clksrc = CCM_SD_CLK_SRC_SEL_OSC24M;
+               ophase = 0;
+               phase = 0;
+               n = 2;
+       } else if (freq <= 25000) {
+               pll_freq = a10_clk_pll6_get_rate() / 1000;
+               clksrc = CCM_SD_CLK_SRC_SEL_PLL6;
+               ophase = 0;
+               phase = 5;
+               n = 2;
+       } else if (freq <= 50000) {
+               pll_freq = a10_clk_pll6_get_rate() / 1000;
+               clksrc = CCM_SD_CLK_SRC_SEL_PLL6;
+               ophase = 3;
+               phase = 5;
+               n = 0;
+       } else
+               return (EINVAL);
+       m = ((pll_freq / (1 << n)) / (freq)) - 1;
+       reg_value = ccm_read_4(sc, CCM_MMC0_SCLK_CFG + (devid * 4));
+       reg_value &= ~CCM_SD_CLK_SRC_SEL;
+       reg_value |= (clksrc << CCM_SD_CLK_SRC_SEL_SHIFT);
+       reg_value &= ~CCM_SD_CLK_PHASE_CTR;
+       reg_value |= (phase << CCM_SD_CLK_PHASE_CTR_SHIFT);
+       reg_value &= ~CCM_SD_CLK_DIV_RATIO_N;
+       reg_value |= (n << CCM_SD_CLK_DIV_RATIO_N_SHIFT);
+       reg_value &= ~CCM_SD_CLK_OPHASE_CTR;
+       reg_value |= (ophase << CCM_SD_CLK_OPHASE_CTR_SHIFT);
+       reg_value &= ~CCM_SD_CLK_DIV_RATIO_M;
+       reg_value |= m;
+       reg_value |= CCM_PLL_CFG_ENABLE;
+       ccm_write_4(sc, CCM_MMC0_SCLK_CFG + (devid * 4), reg_value);
+
+       return (0);
+}

Modified: head/sys/arm/allwinner/a10_clk.h
==============================================================================
--- head/sys/arm/allwinner/a10_clk.h    Thu May 21 17:39:42 2015        
(r283252)
+++ head/sys/arm/allwinner/a10_clk.h    Thu May 21 17:39:42 2015        
(r283253)
@@ -103,6 +103,7 @@
 #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_SDMMC0   (1 << 8)
 #define        CCM_AHB_GATING_EMAC     (1 << 17)
 
 #define        CCM_USB_PHY             (1 << 8)
@@ -110,8 +111,36 @@
 #define        CCM_USB1_RESET          (1 << 1)
 #define        CCM_USB2_RESET          (1 << 2)
 
+#define        CCM_PLL_CFG_ENABLE      (1U << 31)
+#define        CCM_PLL_CFG_BYPASS      (1U << 30)
+#define        CCM_PLL_CFG_PLL5        (1U << 25)
+#define        CCM_PLL_CFG_PLL6        (1U << 24)
+#define        CCM_PLL_CFG_FACTOR_N            0x1f00
+#define        CCM_PLL_CFG_FACTOR_N_SHIFT      8
+#define        CCM_PLL_CFG_FACTOR_K            0x30
+#define        CCM_PLL_CFG_FACTOR_K_SHIFT      4
+#define        CCM_PLL_CFG_FACTOR_M            0x3
+
+#define        CCM_PLL6_CFG_SATA_CLKEN (1U << 14)
+
+#define        CCM_SD_CLK_SRC_SEL              0x3000000
+#define        CCM_SD_CLK_SRC_SEL_SHIFT        24
+#define        CCM_SD_CLK_SRC_SEL_OSC24M       0
+#define        CCM_SD_CLK_SRC_SEL_PLL6         1
+#define        CCM_SD_CLK_PHASE_CTR            0x700000
+#define        CCM_SD_CLK_PHASE_CTR_SHIFT      20
+#define        CCM_SD_CLK_DIV_RATIO_N          0x30000
+#define        CCM_SD_CLK_DIV_RATIO_N_SHIFT    16
+#define        CCM_SD_CLK_OPHASE_CTR           0x700
+#define        CCM_SD_CLK_OPHASE_CTR_SHIFT     8
+#define        CCM_SD_CLK_DIV_RATIO_M          0xf
+
+#define        CCM_CLK_REF_FREQ        24000000U
+
 int a10_clk_usb_activate(void);
 int a10_clk_usb_deactivate(void);
 int a10_clk_emac_activate(void);
+int a10_clk_mmc_activate(int);
+int a10_clk_mmc_cfg(int, int);
 
 #endif /* _A10_CLK_H_ */

Added: head/sys/arm/allwinner/a10_mmc.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/arm/allwinner/a10_mmc.c    Thu May 21 17:39:42 2015        
(r283253)
@@ -0,0 +1,689 @@
+/*-
+ * Copyright (c) 2013 Alexander Fedorov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.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>
+
+#define        A10_MMC_MEMRES          0
+#define        A10_MMC_IRQRES          1
+#define        A10_MMC_RESSZ           2
+
+struct a10_mmc_softc {
+       bus_space_handle_t      a10_bsh;
+       bus_space_tag_t         a10_bst;
+       device_t                a10_dev;
+       int                     a10_bus_busy;
+       int                     a10_id;
+       int                     a10_resid;
+       int                     a10_timeout;
+       struct callout          a10_timeoutc;
+       struct mmc_host         a10_host;
+       struct mmc_request *    a10_req;
+       struct mtx              a10_mtx;
+       struct resource *       a10_res[A10_MMC_RESSZ];
+       uint32_t                a10_intr;
+       uint32_t                a10_intr_wait;
+       void *                  a10_intrhand;
+};
+
+static struct resource_spec a10_mmc_res_spec[] = {
+       { SYS_RES_MEMORY,       0,      RF_ACTIVE },
+       { SYS_RES_IRQ,          0,      RF_ACTIVE | RF_SHAREABLE },
+       { -1,                   0,      0 }
+};
+
+static int a10_mmc_probe(device_t);
+static int a10_mmc_attach(device_t);
+static int a10_mmc_detach(device_t);
+static int a10_mmc_reset(struct a10_mmc_softc *);
+static void a10_mmc_intr(void *);
+static int a10_mmc_update_clock(struct a10_mmc_softc *);
+
+static int a10_mmc_update_ios(device_t, device_t);
+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);
+
+#define        A10_MMC_LOCK(_sc)       mtx_lock(&(_sc)->a10_mtx)
+#define        A10_MMC_UNLOCK(_sc)     mtx_unlock(&(_sc)->a10_mtx)
+#define        A10_MMC_READ_4(_sc, _reg)                                       
\
+       bus_space_read_4((_sc)->a10_bst, (_sc)->a10_bsh, _reg)
+#define        A10_MMC_WRITE_4(_sc, _reg, _value)                              
\
+       bus_space_write_4((_sc)->a10_bst, (_sc)->a10_bsh, _reg, _value)
+
+static int
+a10_mmc_probe(device_t dev)
+{
+
+       if (!ofw_bus_status_okay(dev))
+               return (ENXIO);
+       if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-mmc"))
+               return (ENXIO);
+       device_set_desc(dev, "Allwinner Integrated MMC/SD controller");
+
+       return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a10_mmc_attach(device_t dev)
+{
+       device_t child;
+       struct a10_mmc_softc *sc;
+       struct sysctl_ctx_list *ctx;
+       struct sysctl_oid_list *tree;
+
+       sc = device_get_softc(dev);
+       sc->a10_dev = dev;
+       sc->a10_req = NULL;
+       sc->a10_id = device_get_unit(dev);
+       if (sc->a10_id > 3) {
+               device_printf(dev, "only 4 hosts are supported (0-3)\n");
+               return (ENXIO);
+       }
+       if (bus_alloc_resources(dev, a10_mmc_res_spec, sc->a10_res) != 0) {
+               device_printf(dev, "cannot allocate device resources\n");
+               return (ENXIO);
+       }
+       sc->a10_bst = rman_get_bustag(sc->a10_res[A10_MMC_MEMRES]);
+       sc->a10_bsh = rman_get_bushandle(sc->a10_res[A10_MMC_MEMRES]);
+       if (bus_setup_intr(dev, sc->a10_res[A10_MMC_IRQRES],
+           INTR_TYPE_MISC | INTR_MPSAFE, NULL, a10_mmc_intr, sc,
+           &sc->a10_intrhand)) {
+               bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res);
+               device_printf(dev, "cannot setup interrupt handler\n");
+               return (ENXIO);
+       }
+
+       /* Activate the module clock. */
+       if (a10_clk_mmc_activate(sc->a10_id) != 0) {
+               bus_teardown_intr(dev, sc->a10_res[A10_MMC_IRQRES],
+                   sc->a10_intrhand);
+               bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res);
+               device_printf(dev, "cannot activate mmc clock\n");
+               return (ENXIO);
+       }
+
+       sc->a10_timeout = 10;
+       ctx = device_get_sysctl_ctx(dev);
+       tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+       SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "req_timeout", CTLFLAG_RW,
+           &sc->a10_timeout, 0, "Request timeout in seconds");
+       mtx_init(&sc->a10_mtx, device_get_nameunit(sc->a10_dev), "a10_mmc",
+           MTX_DEF);
+       callout_init_mtx(&sc->a10_timeoutc, &sc->a10_mtx, 0);
+
+       /* Reset controller. */
+       if (a10_mmc_reset(sc) != 0) {
+               device_printf(dev, "cannot reset the controller\n");
+               goto fail;
+       }
+
+       sc->a10_host.f_min = 400000;
+       sc->a10_host.f_max = 52000000;
+       sc->a10_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
+       sc->a10_host.caps = MMC_CAP_4_BIT_DATA | MMC_CAP_HSPEED;
+       sc->a10_host.mode = mode_sd;
+
+       child = device_add_child(dev, "mmc", -1);
+       if (child == NULL) {
+               device_printf(dev, "attaching MMC bus failed!\n");
+               goto fail;
+       }
+       if (device_probe_and_attach(child) != 0) {
+               device_printf(dev, "attaching MMC child failed!\n");
+               device_delete_child(dev, child);
+               goto fail;
+       }
+
+       return (0);
+
+fail:
+       callout_drain(&sc->a10_timeoutc);
+       mtx_destroy(&sc->a10_mtx);
+       bus_teardown_intr(dev, sc->a10_res[A10_MMC_IRQRES], sc->a10_intrhand);
+       bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res);
+
+       return (ENXIO);
+}
+
+static int
+a10_mmc_detach(device_t dev)
+{
+
+       return (EBUSY);
+}
+
+static int
+a10_mmc_reset(struct a10_mmc_softc *sc)
+{
+       int timeout;
+
+       A10_MMC_WRITE_4(sc, A10_MMC_GCTRL,
+           A10_MMC_READ_4(sc, A10_MMC_GCTRL) | A10_MMC_RESET);
+       timeout = 1000;
+       while (--timeout > 0) {
+               if ((A10_MMC_READ_4(sc, A10_MMC_GCTRL) & A10_MMC_RESET) == 0)
+                       break;
+               DELAY(100);
+       }
+       if (timeout == 0)
+               return (ETIMEDOUT);
+
+       /* Set the timeout. */
+       A10_MMC_WRITE_4(sc, A10_MMC_TIMEOUT, 0xffffffff);
+
+       /* Clear pending interrupts. */
+       A10_MMC_WRITE_4(sc, A10_MMC_RINTR, 0xffffffff);
+       /* Unmask interrupts. */
+       A10_MMC_WRITE_4(sc, A10_MMC_IMASK,
+           A10_MMC_CMD_DONE | A10_MMC_INT_ERR_BIT |
+           A10_MMC_DATA_OVER | A10_MMC_AUTOCMD_DONE |
+           A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ);
+       /* Enable interrupts and AHB access. */
+       A10_MMC_WRITE_4(sc, A10_MMC_GCTRL,
+           A10_MMC_READ_4(sc, A10_MMC_GCTRL) |
+           A10_MMC_INT_ENABLE | A10_MMC_ACCESS_BY_AHB);
+
+       return (0);
+}
+
+static void
+a10_mmc_req_done(struct a10_mmc_softc *sc)
+{
+       struct mmc_command *cmd;
+       struct mmc_request *req;
+
+       cmd = sc->a10_req->cmd;
+       if (cmd->error != MMC_ERR_NONE) {
+               /* Reset the controller. */
+               a10_mmc_reset(sc);
+               a10_mmc_update_clock(sc);
+       }
+       /* Reset the FIFO. */
+       A10_MMC_WRITE_4(sc, A10_MMC_GCTRL,
+           A10_MMC_READ_4(sc, A10_MMC_GCTRL) | A10_MMC_FIFO_RESET);
+
+       req = sc->a10_req;
+       callout_stop(&sc->a10_timeoutc);
+       sc->a10_req = NULL;
+       sc->a10_intr = 0;
+       sc->a10_resid = 0;
+       sc->a10_intr_wait = 0;
+       req->done(req);
+}
+
+static void
+a10_mmc_req_ok(struct a10_mmc_softc *sc)
+{
+       int timeout;
+       struct mmc_command *cmd;
+       uint32_t status;
+
+       timeout = 1000;
+       while (--timeout > 0) {
+               status = A10_MMC_READ_4(sc, A10_MMC_STAS);
+               if ((status & A10_MMC_CARD_DATA_BUSY) == 0)
+                       break;
+               DELAY(1000);
+       }
+       cmd = sc->a10_req->cmd;
+       if (timeout == 0) {
+               cmd->error = MMC_ERR_FAILED;
+               a10_mmc_req_done(sc);
+               return;
+       }
+       if (cmd->flags & MMC_RSP_PRESENT) {
+               if (cmd->flags & MMC_RSP_136) {
+                       cmd->resp[0] = A10_MMC_READ_4(sc, A10_MMC_RESP3);
+                       cmd->resp[1] = A10_MMC_READ_4(sc, A10_MMC_RESP2);
+                       cmd->resp[2] = A10_MMC_READ_4(sc, A10_MMC_RESP1);
+                       cmd->resp[3] = A10_MMC_READ_4(sc, A10_MMC_RESP0);
+               } else
+                       cmd->resp[0] = A10_MMC_READ_4(sc, A10_MMC_RESP0);
+       }
+       /* All data has been transferred ? */
+       if (cmd->data != NULL && (sc->a10_resid << 2) < cmd->data->len)
+               cmd->error = MMC_ERR_FAILED;
+       a10_mmc_req_done(sc);
+}
+
+static void 
+a10_mmc_timeout(void *arg)
+{
+       struct a10_mmc_softc *sc;
+
+       sc = (struct a10_mmc_softc *)arg;
+       if (sc->a10_req != NULL) {
+               device_printf(sc->a10_dev, "controller timeout\n");
+               sc->a10_req->cmd->error = MMC_ERR_TIMEOUT;
+               a10_mmc_req_done(sc);
+       } else
+               device_printf(sc->a10_dev,
+                   "Spurious timeout - no active request\n");
+}
+
+static int
+a10_mmc_pio_transfer(struct a10_mmc_softc *sc, struct mmc_data *data)
+{
+       int i, write;
+       uint32_t bit, *buf;
+
+       buf = (uint32_t *)data->data;
+       write = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
+       bit = write ? A10_MMC_FIFO_FULL : A10_MMC_FIFO_EMPTY;
+       for (i = sc->a10_resid; i < (data->len >> 2); i++) {
+               if ((A10_MMC_READ_4(sc, A10_MMC_STAS) & bit))
+                       return (1);
+               if (write)
+                       A10_MMC_WRITE_4(sc, A10_MMC_FIFO, buf[i]);
+               else
+                       buf[i] = A10_MMC_READ_4(sc, A10_MMC_FIFO);
+               sc->a10_resid = i + 1;
+       }
+
+       return (0);
+}
+
+static void
+a10_mmc_intr(void *arg)
+{
+       struct a10_mmc_softc *sc;
+       struct mmc_data *data;
+       uint32_t imask, rint;
+
+       sc = (struct a10_mmc_softc *)arg;
+       A10_MMC_LOCK(sc);
+       rint = A10_MMC_READ_4(sc, A10_MMC_RINTR);
+       imask = A10_MMC_READ_4(sc, A10_MMC_IMASK);
+       if (imask == 0 && rint == 0) {
+               A10_MMC_UNLOCK(sc);
+               return;
+       }
+#ifdef DEBUG
+       device_printf(sc->a10_dev, "imask: %#x, rint: %#x\n", imask, rint);
+#endif
+       if (sc->a10_req == NULL) {
+               device_printf(sc->a10_dev,
+                   "Spurious interrupt - no active request, rint: 0x%08X\n",
+                   rint);
+               A10_MMC_WRITE_4(sc, A10_MMC_RINTR, rint);
+               A10_MMC_UNLOCK(sc);
+               return;
+       }
+       if (rint & A10_MMC_INT_ERR_BIT) {
+               device_printf(sc->a10_dev, "error rint: 0x%08X\n", rint);
+               if (rint & A10_MMC_RESP_TIMEOUT)
+                       sc->a10_req->cmd->error = MMC_ERR_TIMEOUT;
+               else
+                       sc->a10_req->cmd->error = MMC_ERR_FAILED;
+               A10_MMC_WRITE_4(sc, A10_MMC_RINTR, rint);
+               a10_mmc_req_done(sc);
+               A10_MMC_UNLOCK(sc);
+               return;
+       }
+
+       sc->a10_intr |= rint;
+       data = sc->a10_req->cmd->data;
+       if (data != NULL && (rint & (A10_MMC_DATA_OVER |
+           A10_MMC_RX_DATA_REQ | A10_MMC_TX_DATA_REQ)) != 0)
+               a10_mmc_pio_transfer(sc, data);
+       if ((sc->a10_intr & sc->a10_intr_wait) == sc->a10_intr_wait)
+               a10_mmc_req_ok(sc);
+
+       A10_MMC_WRITE_4(sc, A10_MMC_RINTR, rint);
+       A10_MMC_UNLOCK(sc);
+}
+
+static int
+a10_mmc_request(device_t bus, device_t child, struct mmc_request *req)
+{
+       int blksz;
+       struct a10_mmc_softc *sc;
+       struct mmc_command *cmd;
+       uint32_t cmdreg;
+
+       sc = device_get_softc(bus);
+       A10_MMC_LOCK(sc);
+       if (sc->a10_req) {
+               A10_MMC_UNLOCK(sc);
+               return (EBUSY);
+       }
+       sc->a10_req = req;
+       cmd = req->cmd;
+       cmdreg = A10_MMC_START;
+       if (cmd->opcode == MMC_GO_IDLE_STATE)
+               cmdreg |= A10_MMC_SEND_INIT_SEQ;
+       if (cmd->flags & MMC_RSP_PRESENT)
+               cmdreg |= A10_MMC_RESP_EXP;
+       if (cmd->flags & MMC_RSP_136)
+               cmdreg |= A10_MMC_LONG_RESP;
+       if (cmd->flags & MMC_RSP_CRC)
+               cmdreg |= A10_MMC_CHECK_RESP_CRC;
+
+       sc->a10_intr = 0;
+       sc->a10_resid = 0;
+       sc->a10_intr_wait = A10_MMC_CMD_DONE;
+       cmd->error = MMC_ERR_NONE;
+       if (cmd->data != NULL) {
+               sc->a10_intr_wait |= A10_MMC_DATA_OVER;
+               cmdreg |= A10_MMC_DATA_EXP | A10_MMC_WAIT_PREOVER;
+               if (cmd->data->flags & MMC_DATA_MULTI) {
+                       cmdreg |= A10_MMC_SEND_AUTOSTOP;
+                       sc->a10_intr_wait |= A10_MMC_AUTOCMD_DONE;
+               }
+               if (cmd->data->flags & MMC_DATA_WRITE)
+                       cmdreg |= A10_MMC_WRITE;
+               blksz = min(cmd->data->len, MMC_SECTOR_SIZE);
+               A10_MMC_WRITE_4(sc, A10_MMC_BLKSZ, blksz);
+               A10_MMC_WRITE_4(sc, A10_MMC_BCNTR, cmd->data->len);
+       }
+
+       A10_MMC_WRITE_4(sc, A10_MMC_CARG, cmd->arg);
+       A10_MMC_WRITE_4(sc, A10_MMC_CMDR, cmdreg | cmd->opcode);
+       callout_reset(&sc->a10_timeoutc, sc->a10_timeout * hz,
+           a10_mmc_timeout, sc);
+       A10_MMC_UNLOCK(sc);
+
+       return (0);
+}
+
+static int
+a10_mmc_read_ivar(device_t bus, device_t child, int which, 
+    uintptr_t *result)
+{
+       struct a10_mmc_softc *sc;
+
+       sc = device_get_softc(bus);
+       switch (which) {
+       default:
+               return (EINVAL);
+       case MMCBR_IVAR_BUS_MODE:
+               *(int *)result = sc->a10_host.ios.bus_mode;
+               break;
+       case MMCBR_IVAR_BUS_WIDTH:
+               *(int *)result = sc->a10_host.ios.bus_width;
+               break;
+       case MMCBR_IVAR_CHIP_SELECT:
+               *(int *)result = sc->a10_host.ios.chip_select;
+               break;
+       case MMCBR_IVAR_CLOCK:
+               *(int *)result = sc->a10_host.ios.clock;
+               break;
+       case MMCBR_IVAR_F_MIN:
+               *(int *)result = sc->a10_host.f_min;
+               break;
+       case MMCBR_IVAR_F_MAX:
+               *(int *)result = sc->a10_host.f_max;
+               break;
+       case MMCBR_IVAR_HOST_OCR:
+               *(int *)result = sc->a10_host.host_ocr;
+               break;
+       case MMCBR_IVAR_MODE:
+               *(int *)result = sc->a10_host.mode;
+               break;
+       case MMCBR_IVAR_OCR:
+               *(int *)result = sc->a10_host.ocr;
+               break;
+       case MMCBR_IVAR_POWER_MODE:
+               *(int *)result = sc->a10_host.ios.power_mode;
+               break;
+       case MMCBR_IVAR_VDD:
+               *(int *)result = sc->a10_host.ios.vdd;
+               break;
+       case MMCBR_IVAR_CAPS:
+               *(int *)result = sc->a10_host.caps;
+               break;
+       case MMCBR_IVAR_MAX_DATA:
+               *(int *)result = 65535;
+               break;
+       }
+
+       return (0);
+}
+
+static int
+a10_mmc_write_ivar(device_t bus, device_t child, int which,
+    uintptr_t value)
+{
+       struct a10_mmc_softc *sc;
+
+       sc = device_get_softc(bus);
+       switch (which) {
+       default:
+               return (EINVAL);
+       case MMCBR_IVAR_BUS_MODE:
+               sc->a10_host.ios.bus_mode = value;
+               break;
+       case MMCBR_IVAR_BUS_WIDTH:
+               sc->a10_host.ios.bus_width = value;
+               break;
+       case MMCBR_IVAR_CHIP_SELECT:
+               sc->a10_host.ios.chip_select = value;
+               break;
+       case MMCBR_IVAR_CLOCK:
+               sc->a10_host.ios.clock = value;
+               break;
+       case MMCBR_IVAR_MODE:
+               sc->a10_host.mode = value;
+               break;
+       case MMCBR_IVAR_OCR:
+               sc->a10_host.ocr = value;
+               break;
+       case MMCBR_IVAR_POWER_MODE:
+               sc->a10_host.ios.power_mode = value;
+               break;
+       case MMCBR_IVAR_VDD:
+               sc->a10_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:
+               return (EINVAL);
+       }
+
+       return (0);
+}
+
+static int
+a10_mmc_update_clock(struct a10_mmc_softc *sc)
+{
+       uint32_t cmdreg;
+       int retry;
+
+       cmdreg = A10_MMC_START | A10_MMC_UPCLK_ONLY |
+           A10_MMC_WAIT_PREOVER;
+       A10_MMC_WRITE_4(sc, A10_MMC_CMDR, cmdreg);
+       retry = 0xfffff;
+       while (--retry > 0) {
+               if ((A10_MMC_READ_4(sc, A10_MMC_CMDR) & A10_MMC_START) == 0) {
+                       A10_MMC_WRITE_4(sc, A10_MMC_RINTR, 0xffffffff);
+                       return (0);
+               }
+               DELAY(10);
+       }
+       A10_MMC_WRITE_4(sc, A10_MMC_RINTR, 0xffffffff);
+       device_printf(sc->a10_dev, "timeout updating clock\n");
+
+       return (ETIMEDOUT);
+}
+
+static int
+a10_mmc_update_ios(device_t bus, device_t child)
+{
+       int error;
+       struct a10_mmc_softc *sc;
+       struct mmc_ios *ios;
+       uint32_t clkcr;
+
+       sc = device_get_softc(bus);
+       clkcr = A10_MMC_READ_4(sc, A10_MMC_CLKCR);
+       if (clkcr & A10_MMC_CARD_CLK_ON) {
+               /* Disable clock. */
+               clkcr &= ~A10_MMC_CARD_CLK_ON;
+               A10_MMC_WRITE_4(sc, A10_MMC_CLKCR, clkcr);
+               error = a10_mmc_update_clock(sc);
+               if (error != 0)
+                       return (error);
+       }
+
+       ios = &sc->a10_host.ios;
+       if (ios->clock) {
+               /* Reset the divider. */
+               clkcr &= ~A10_MMC_CLKCR_DIV;
+               A10_MMC_WRITE_4(sc, A10_MMC_CLKCR, clkcr);
+               error = a10_mmc_update_clock(sc);
+               if (error != 0)
+                       return (error);
+
+               /* Set the MMC clock. */
+               error = a10_clk_mmc_cfg(sc->a10_id, ios->clock);
+               if (error != 0)
+                       return (error);
+
+               /* Enable clock. */
+               clkcr |= A10_MMC_CARD_CLK_ON;
+               A10_MMC_WRITE_4(sc, A10_MMC_CLKCR, clkcr);
+               error = a10_mmc_update_clock(sc);
+               if (error != 0)
+                       return (error);
+       }
+
+       /* Set the bus width. */
+       switch (ios->bus_width) {
+       case bus_width_1:
+               A10_MMC_WRITE_4(sc, A10_MMC_WIDTH, A10_MMC_WIDTH1);
+               break;
+       case bus_width_4:
+               A10_MMC_WRITE_4(sc, A10_MMC_WIDTH, A10_MMC_WIDTH4);
+               break;
+       case bus_width_8:
+               A10_MMC_WRITE_4(sc, A10_MMC_WIDTH, A10_MMC_WIDTH8);
+               break;
+       }
+
+       return (0);
+}
+
+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;
+       int error;
+
+       sc = device_get_softc(bus);
+       A10_MMC_LOCK(sc);
+       while (sc->a10_bus_busy) {
+               error = msleep(sc, &sc->a10_mtx, PCATCH, "mmchw", 0);
+               if (error != 0) {
+                       A10_MMC_UNLOCK(sc);
+                       return (error);
+               }
+       }
+       sc->a10_bus_busy++;
+       A10_MMC_UNLOCK(sc);
+
+       return (0);
+}
+
+static int
+a10_mmc_release_host(device_t bus, device_t child)
+{
+       struct a10_mmc_softc *sc;
+
+       sc = device_get_softc(bus);
+       A10_MMC_LOCK(sc);
+       sc->a10_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),
+
+       DEVMETHOD_END
+};
+
+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);

Added: head/sys/arm/allwinner/a10_mmc.h
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/arm/allwinner/a10_mmc.h    Thu May 21 17:39:42 2015        
(r283253)
@@ -0,0 +1,179 @@
+/*-
+ * Copyright (c) 2013 Alexander Fedorov <alexander.fedo...@rtlservice.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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef        _A10_MMC_H_
+#define        _A10_MMC_H_
+
+#define        A10_MMC_GCTRL           0x00    /* Global Control Register */
+#define        A10_MMC_CLKCR           0x04    /* Clock Control Register */
+#define        A10_MMC_TIMEOUT         0x08    /* Timeout Register */
+#define        A10_MMC_WIDTH           0x0C    /* Bus Width Register */
+#define        A10_MMC_BLKSZ           0x10    /* Block Size Register */
+#define        A10_MMC_BCNTR           0x14    /* Byte Count Register */
+#define        A10_MMC_CMDR            0x18    /* Command Register */
+#define        A10_MMC_CARG            0x1C    /* Argument Register */
+#define        A10_MMC_RESP0           0x20    /* Response Register 0 */
+#define        A10_MMC_RESP1           0x24    /* Response Register 1 */
+#define        A10_MMC_RESP2           0x28    /* Response Register 2 */
+#define        A10_MMC_RESP3           0x2C    /* Response Register 3 */
+#define        A10_MMC_IMASK           0x30    /* Interrupt Mask Register */
+#define        A10_MMC_MISTA           0x34    /* Masked Interrupt Status 
Register */
+#define        A10_MMC_RINTR           0x38    /* Raw Interrupt Status 
Register */
+#define        A10_MMC_STAS            0x3C    /* Status Register */
+#define        A10_MMC_FTRGL           0x40    /* FIFO Threshold Watermark 
Register */
+#define        A10_MMC_FUNS            0x44    /* Function Select Register */
+#define        A10_MMC_CBCR            0x48    /* CIU Byte Count Register */
+#define        A10_MMC_BBCR            0x4C    /* BIU Byte Count Register */
+#define        A10_MMC_DBGC            0x50    /* Debug Enable Register */
+#define        A10_MMC_DMAC            0x80    /* IDMAC Control Register */
+#define        A10_MMC_DLBA            0x84    /* IDMAC Desc List Base Address 
Reg */
+#define        A10_MMC_IDST            0x88    /* IDMAC Status Register */
+#define        A10_MMC_IDIE            0x8C    /* IDMAC Interrupt Enable 
Register */
+#define        A10_MMC_CHDA            0x90
+#define        A10_MMC_CBDA            0x94
+#define        A10_MMC_FIFO            0x100   /* FIFO Access Address */
+
+/* A10_MMC_GCTRL */
+#define        A10_MMC_SOFT_RESET              (1U << 0)
+#define        A10_MMC_FIFO_RESET              (1U << 1)
+#define        A10_MMC_DMA_RESET               (1U << 2)
+#define        A10_MMC_INT_ENABLE              (1U << 4)
+#define        A10_MMC_DMA_ENABLE              (1U << 5)
+#define        A10_MMC_DEBOUNCE_ENABLE         (1U << 8)
+#define        A10_MMC_DDR_MODE                (1U << 10)
+#define        A10_MMC_ACCESS_BY_DMA           (1U << 30)
+#define        A10_MMC_ACCESS_BY_AHB           (1U << 31)
+#define        A10_MMC_RESET                                   \
+       (A10_MMC_SOFT_RESET | A10_MMC_FIFO_RESET | A10_MMC_DMA_RESET)
+
+/* A10_MMC_CLKCR */
+#define        A10_MMC_CARD_CLK_ON             (1U << 16)
+#define        A10_MMC_LOW_POWER_ON            (1U << 17)
+#define        A10_MMC_CLKCR_DIV               0xff
+
+/* A10_MMC_WIDTH */
+#define        A10_MMC_WIDTH1                  0
+#define        A10_MMC_WIDTH4                  1
+#define        A10_MMC_WIDTH8                  2
+
+/* A10_MMC_CMDR */
+#define        A10_MMC_RESP_EXP                (1U << 6)
+#define        A10_MMC_LONG_RESP               (1U << 7)
+#define        A10_MMC_CHECK_RESP_CRC          (1U << 8)
+#define        A10_MMC_DATA_EXP                (1U << 9)
+#define        A10_MMC_WRITE                   (1U << 10)
+#define        A10_MMC_SEQ_MODE                (1U << 11)
+#define        A10_MMC_SEND_AUTOSTOP           (1U << 12)
+#define        A10_MMC_WAIT_PREOVER            (1U << 13)
+#define        A10_MMC_STOP_ABORT_CMD          (1U << 14)
+#define        A10_MMC_SEND_INIT_SEQ           (1U << 15)
+#define        A10_MMC_UPCLK_ONLY              (1U << 21)
+#define        A10_MMC_RDCEATADEV              (1U << 22)
+#define        A10_MMC_CCS_EXP                 (1U << 23)
+#define        A10_MMC_ENB_BOOT                (1U << 24)
+#define        A10_MMC_ALT_BOOT_OPT            (1U << 25)
+#define        A10_MMC_BOOT_ACK_EXP            (1U << 26)
+#define        A10_MMC_DISABLE_BOOT            (1U << 27)
+#define        A10_MMC_VOL_SWITCH              (1U << 28)
+#define        A10_MMC_START                   (1U << 31)
+
+/* A10_MMC_IMASK and A10_MMC_RINTR */
+#define        A10_MMC_RESP_ERR                (1U << 1)
+#define        A10_MMC_CMD_DONE                (1U << 2)
+#define        A10_MMC_DATA_OVER               (1U << 3)
+#define        A10_MMC_TX_DATA_REQ             (1U << 4)
+#define        A10_MMC_RX_DATA_REQ             (1U << 5)
+#define        A10_MMC_RESP_CRC_ERR            (1U << 6)
+#define        A10_MMC_DATA_CRC_ERR            (1U << 7)
+#define        A10_MMC_RESP_TIMEOUT            (1U << 8)
+#define        A10_MMC_ACK_RECV                (1U << 8)
+#define        A10_MMC_DATA_TIMEOUT            (1U << 9)
+#define        A10_MMC_BOOT_START              (1U << 9)
+#define        A10_MMC_DATA_STARVE             (1U << 10)
+#define        A10_MMC_VOL_CHG_DONE            (1U << 10)
+#define        A10_MMC_FIFO_RUN_ERR            (1U << 11)
+#define        A10_MMC_HARDW_LOCKED            (1U << 12)
+#define        A10_MMC_START_BIT_ERR           (1U << 13)
+#define        A10_MMC_AUTOCMD_DONE            (1U << 14)
+#define        A10_MMC_END_BIT_ERR             (1U << 15)
+#define        A10_MMC_SDIO_INT                (1U << 16)
+#define        A10_MMC_CARD_INSERT             (1U << 30)
+#define        A10_MMC_CARD_REMOVE             (1U << 31)
+#define        A10_MMC_INT_ERR_BIT                             \
+       (A10_MMC_RESP_ERR | A10_MMC_RESP_CRC_ERR |      \
+        A10_MMC_DATA_CRC_ERR | A10_MMC_RESP_TIMEOUT |  \
+        A10_MMC_FIFO_RUN_ERR | A10_MMC_HARDW_LOCKED |  \
+        A10_MMC_START_BIT_ERR | A10_MMC_END_BIT_ERR)
+
+/* A10_MMC_STAS */
+#define        A10_MMC_RX_WLFLAG               (1U << 0)
+#define        A10_MMC_TX_WLFLAG               (1U << 1)

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to