so i found this while digging rotten branches, initially from NetBSD. managed to refrain from cleaning up anything in the diff below.
tested to compile, runtime tests done on something that actually does run on my sunxis, it seems sunxi hasn't fixed itself on your tree, or maybe it does work, but in an emulator or w/e. -Artturi diff --git a/sys/arch/armv7/conf/GENERIC b/sys/arch/armv7/conf/GENERIC index 953984d..ad0c412 100644 --- a/sys/arch/armv7/conf/GENERIC +++ b/sys/arch/armv7/conf/GENERIC @@ -98,6 +98,8 @@ ehci* at sunxi? # EHCI (shim) usb* at ehci? #flags 0x1 #ohci* at sunxi? #usb* at ohci? +sxisdmmc* at sunxi? # SD/MMC card controller +sdmmc* at sxisdmmc? # SD/MMC bus # ARM Versatile Express vexpress0 at mainbus? diff --git a/sys/arch/armv7/sunxi/files.sunxi b/sys/arch/armv7/sunxi/files.sunxi index 521b2d7..ed0e7d5 100644 --- a/sys/arch/armv7/sunxi/files.sunxi +++ b/sys/arch/armv7/sunxi/files.sunxi @@ -48,3 +48,7 @@ file arch/armv7/sunxi/sxiuart.c sxiuart device sxie: ether, ifnet, mii, ifmedia attach sxie at sunxi file arch/armv7/sunxi/sxie.c sxie + +device sxisdmmc: sdmmcbus +attach sxisdmmc at sunxi +file arch/armv7/sunxi/sxisdmmc.c sxisdmmc diff --git a/sys/arch/armv7/sunxi/sun4i.c b/sys/arch/armv7/sunxi/sun4i.c index 6c3197a..e283316 100644 --- a/sys/arch/armv7/sunxi/sun4i.c +++ b/sys/arch/armv7/sunxi/sun4i.c @@ -155,6 +155,28 @@ struct armv7_dev sxia1x_devs[] = { .irq = { USB1_IRQ } }, + /* SD/MMC */ + { .name = "sxisdmmc", + .unit = 0, + .mem = { { SDMMCx_ADDR(0), SDMMCx_SIZE } }, + .irq = { SDMMCx_IRQ(0) } + }, + { .name = "sxisdmmc", + .unit = 1, + .mem = { { SDMMCx_ADDR(1), SDMMCx_SIZE } }, + .irq = { SDMMCx_IRQ(1) } + }, + { .name = "sxisdmmc", + .unit = 2, + .mem = { { SDMMCx_ADDR(2), SDMMCx_SIZE } }, + .irq = { SDMMCx_IRQ(2) } + }, + { .name = "sxisdmmc", + .unit = 3, + .mem = { { SDMMCx_ADDR(3), SDMMCx_SIZE } }, + .irq = { SDMMCx_IRQ(3) } + }, + /* Terminator */ { .name = NULL, .unit = 0, diff --git a/sys/arch/armv7/sunxi/sun7i.c b/sys/arch/armv7/sunxi/sun7i.c index 53978f3..6073aa0 100644 --- a/sys/arch/armv7/sunxi/sun7i.c +++ b/sys/arch/armv7/sunxi/sun7i.c @@ -137,6 +137,28 @@ struct armv7_dev sxia20_devs[] = { .irq = { USB1_IRQ } }, + /* SD/MMC */ + { .name = "sxisdmmc", + .unit = 0, + .mem = { { SDMMCx_ADDR(0), SDMMCx_SIZE } }, + .irq = { SDMMCx_IRQ(0) } + }, + { .name = "sxisdmmc", + .unit = 1, + .mem = { { SDMMCx_ADDR(1), SDMMCx_SIZE } }, + .irq = { SDMMCx_IRQ(1) } + }, + { .name = "sxisdmmc", + .unit = 2, + .mem = { { SDMMCx_ADDR(2), SDMMCx_SIZE } }, + .irq = { SDMMCx_IRQ(2) } + }, + { .name = "sxisdmmc", + .unit = 3, + .mem = { { SDMMCx_ADDR(3), SDMMCx_SIZE } }, + .irq = { SDMMCx_IRQ(3) } + }, + /* Terminator */ { .name = NULL, .unit = 0, diff --git a/sys/arch/armv7/sunxi/sunxi.c b/sys/arch/armv7/sunxi/sunxi.c index 2ed498d..0486a16 100644 --- a/sys/arch/armv7/sunxi/sunxi.c +++ b/sys/arch/armv7/sunxi/sunxi.c @@ -62,6 +62,7 @@ struct board_dev sun4i_devs[] = { { "ohci", 0 }, { "ohci", 1 }, #endif + { "sxisdmmc", 0 }, { NULL, 0 } }; @@ -86,6 +87,7 @@ struct board_dev sun7i_devs[] = { { "ohci", 0 }, { "ohci", 1 }, #endif + { "sxisdmmc", 0 }, { NULL, 0 } }; diff --git a/sys/arch/armv7/sunxi/sunxireg.h b/sys/arch/armv7/sunxi/sunxireg.h index 8153efa..a11ba86 100644 --- a/sys/arch/armv7/sunxi/sunxireg.h +++ b/sys/arch/armv7/sunxi/sunxireg.h @@ -37,6 +37,7 @@ #define SXICMS4(sc, reg, mask, bits) \ SXIWRITE4((sc), (reg), (SXIREAD4((sc), (reg)) & ~(mask)) | (bits)) +#define SUNXI_REF_FREQ (24 * 1000 * 1000) /* OSC24M */ #define TIMER0_FREQUENCY (32768) #define TIMER1_FREQUENCY (32768) #define TIMER2_FREQUENCY (32768) @@ -50,9 +51,9 @@ #define DMAC_SIZE 0x1000 #define DMAC_IRQ 27 -#define SDMMC0_ADDR 0x01c0f000 #define SDMMCx_SIZE 0x1000 -#define SDMMC0_IRQ 32 +#define SDMMCx_ADDR(x) (0x01c0f000 + ((x) * SDMMCx_SIZE)) +#define SDMMCx_IRQ(x) (32 + (x)) #define SATA_ADDR 0x01c18000 #define SATA_SIZE 0x1000 diff --git a/sys/arch/armv7/sunxi/sxiccmu.c b/sys/arch/armv7/sunxi/sxiccmu.c index ddfc415..9bc56de 100644 --- a/sys/arch/armv7/sunxi/sxiccmu.c +++ b/sys/arch/armv7/sunxi/sxiccmu.c @@ -40,7 +40,9 @@ #define CCMU_SCLK_GATING (1U << 31) #define CCMU_GET_CLK_DIV_RATIO_N(x) (((x) >> 16) & 0x03) -#define CCMU_GET_CLK_DIV_RATIO_M(x) ((x) & 0x07) +#define CCMU_GET_CLK_DIV_RATIO_M(x) ((x) & 0x0f) +#define CCMU_CLK_DIV_RATIO_N(x) (((x) & 0x03) << 16) +#define CCMU_CLK_DIV_RATIO_M(x) ((x) & 0x0f) #define CCMU_PLL6_CFG 0x28 #define CCMU_PLL6_EN (1U << 31) @@ -49,6 +51,9 @@ #define CCMU_PLL6_FACTOR_N (31 << 8) #define CCMU_PLL6_FACTOR_K (3 << 4) #define CCMU_PLL6_FACTOR_M (3 << 0) +#define CCMU_PLL6_GET_FACTOR_N(x) (((x) >> 8) & 31) +#define CCMU_PLL6_GET_FACTOR_K(x) (((x) >> 4) & 3) +#define CCMU_PLL6_GET_FACTOR_M(x) ((x) & 3) #define CCMU_AHB_GATING0 0x60 #define CCMU_AHB_GATING_USB0 (1 << 0) @@ -77,6 +82,26 @@ #define CCMU_NAND_CLK_SRC_GATING_PLL5 (2 << 24) #define CCMU_NAND_CLK_SRC_GATING_MASK (3 << 24) +#define CCMU_SDMMCx_CLK(x) (0x88 + (4 * (x))) +#define CCMU_SDMMC_CLK_SRC_GATING_OSC24M (0 << 24) +#define CCMU_SDMMC_CLK_SRC_GATING_PLL6 (1 << 24) +#define CCMU_SDMMC_CLK_SRC_GATING_PLL5 (2 << 24) +#define CCMU_SDMMC_CLK_SRC_GATING_MASK (3 << 24) +#define CCMU_SDMMC_CLK_PHASE_CTR_MASK (7 << 20) +#define CCMU_SDMMC_CLK_OUTPUT_PHASE_CTR_MASK (7 << 8) +#define CCMU_SDMMC_CLK_DIV_RATIO_N_MASK (3 << 16) +#define CCMU_SDMMC_CLK_DIV_RATIO_M_MASK (7 << 0) + +#define CCMU_SDMMC_CLK_SET_PHASE_CTR(x) (((x) & 7) << 20) +#define CCMU_SDMMC_CLK_SET_OUTPUT_PHASE_CTR(x) (((x) & 7) << 8) + +#define CCMU_SDMMC_CLK_CLR_MASK \ + (CCMU_SDMMC_CLK_SRC_GATING_MASK | \ + CCMU_SDMMC_CLK_PHASE_CTR_MASK | \ + CCMU_SDMMC_CLK_OUTPUT_PHASE_CTR_MASK | \ + CCMU_SDMMC_CLK_DIV_RATIO_N_MASK | \ + CCMU_SDMMC_CLK_DIV_RATIO_M_MASK) + #define CCMU_SATA_CLK 0xc8 #define CCMU_SATA_CLK_SRC_GATING (1 << 24) @@ -273,3 +298,63 @@ sxiccmu_disablemodule(int mod) break; } } + +/* XXX dirty because */ +inline u_int +sxiccmu_pll6_get_rate(void) +{ + struct sxiccmu_softc *sc = sxiccmu_cd.cd_devs[0]; + u_int n, k, m; + const u_int pll6 = SXIREAD4(sc, CCMU_PLL6_CFG); + n = CCMU_PLL6_GET_FACTOR_N(pll6); + k = CCMU_PLL6_GET_FACTOR_K(pll6) + 1; + m = 2; + return (SUNXI_REF_FREQ * n * k) / m; +} + +int +sxiccmu_set_sdmmc_clock(u_int sdmmc_port, u_int freq) +{ + struct sxiccmu_softc *sc = sxiccmu_cd.cd_devs[0]; + u_int odly, sdly, clksrc, n, m; + u_int osc24m_freq = SUNXI_REF_FREQ / 1000; + u_int pll_freq; + + pll_freq = sxiccmu_pll6_get_rate() / 1000; + + DPRINTF(("\n%s: freq = %d pll_freq = %d\n", __func__, freq, pll_freq)); + + if (freq <= 400) { + odly = 0; + sdly = 0; + clksrc = CCMU_SDMMC_CLK_SRC_GATING_OSC24M; + n = 2; + if (freq > 0) + m = ((osc24m_freq / (1 << n)) / freq) - 1; + else + m = 15; + } else if (freq <= 25000) { + odly = 0; + sdly = 5; + clksrc = CCMU_SDMMC_CLK_SRC_GATING_PLL6; + n = 0; + m = ((pll_freq / freq) / (1 << n)) - 1; + } else if (freq <= 50000) { + odly = 3; + sdly = 5; + clksrc = CCMU_SDMMC_CLK_SRC_GATING_PLL6; + n = 0; + m = ((pll_freq / freq) / (1 << n)) - 1; + } else { + /* UHS speeds not implemented yet */ + return EIO; + } + + SXICMS4(sc, CCMU_SDMMCx_CLK(sdmmc_port), CCMU_SDMMC_CLK_CLR_MASK, + CCMU_CLK_DIV_RATIO_M(m) | CCMU_GET_CLK_DIV_RATIO_N(n) | + CCMU_SDMMC_CLK_SET_OUTPUT_PHASE_CTR(odly) | clksrc | + CCMU_SDMMC_CLK_SET_PHASE_CTR(sdly) | CCMU_PLL6_EN); + delay(20000); + + return 0; +} diff --git a/sys/arch/armv7/sunxi/sxiccmuvar.h b/sys/arch/armv7/sunxi/sxiccmuvar.h index cf68e7c..42ac85b 100644 --- a/sys/arch/armv7/sunxi/sxiccmuvar.h +++ b/sys/arch/armv7/sunxi/sxiccmuvar.h @@ -17,6 +17,7 @@ void sxiccmu_enablemodule(int); void sxiccmu_disablemodule(int); +int sxiccmu_set_sdmmc_clock(u_int, u_int); enum CCMU_MODULES { CCMU_EHCI0, diff --git a/sys/arch/armv7/sunxi/sxisdmmc.c b/sys/arch/armv7/sunxi/sxisdmmc.c index e69de29..1d86175 100644 --- a/sys/arch/armv7/sunxi/sxisdmmc.c +++ b/sys/arch/armv7/sunxi/sxisdmmc.c @@ -0,0 +1,601 @@ +/* $OpenBSD$ */ +/* $NetBSD: awin_mmc.c,v 1.5 2014/09/07 15:38:06 jmcneill Exp $ */ + +/* + * Copyright (c) 2014 Jared D. McNeill <jmcne...@invisible.ca> + * 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 ``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 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/param.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/systm.h> + +#include <machine/bus.h> + +#include <dev/sdmmc/sdmmcchip.h> +#include <dev/sdmmc/sdmmcvar.h> + +#include <armv7/armv7/armv7var.h> + +#include <arm88k/sunxi/sunxireg.h> +#include <arm88k/sunxi/sxiccmuvar.h> + +#define SXISDMMC_DEBUG +/*#undef SXISDMMC_DEBUG*/ +#ifdef SXISDMMC_DEBUG +/* + * conditional debugging + */ +#define CD_INIT 0x00000001 /* init */ +#define CD_ERR 0x00000002 /* errors */ +#define CD_TIMO 0x00000004 /* timeout */ +#define CD_DBG 0x00000010 /* just dbg */ +#define CD_SPAM 0x00000020 /* verbose boot */ +#define CD_ALL 0xffffffff + +int sxisdmmc_debug = 0 | CD_INIT /*| CD_DBG | CD_SPAM | CD_ALL*/; + +#define DPRINTF(flg, stmt) \ +do { \ + if (sxisdmmc_debug & (flg)) \ + printf stmt; \ +} while (0) +#else +#define DPRINTF(flg, stmt) do { } while (0) +#endif /* SXISDMMC_DEBUG */ + +#define DNAME(sc) ((sc)->sc_dev.dv_xname) + +/* registers */ +#define SDMMC_GCTRL 0x0000 +#define SDMMC_CLKCR 0x0004 +#define SDMMC_TIMEOUT 0x0008 +#define SDMMC_WIDTH 0x000c +#define SDMMC_BLKSZ 0x0010 +#define SDMMC_BYTECNT 0x0014 +#define SDMMC_CMD 0x0018 +#define SDMMC_ARG 0x001c +#define SDMMC_RESP0 0x0020 +#define SDMMC_RESP1 0x0024 +#define SDMMC_RESP2 0x0028 +#define SDMMC_RESP3 0x002c +#define SDMMC_IMASK 0x0030 +#define SDMMC_MINT 0x0034 +#define SDMMC_RINT 0x0038 +#define SDMMC_STATUS 0x003c +#define SDMMC_FTRGLEVEL 0x0040 +#define SDMMC_FUNCSEL 0x0044 +#define SDMMC_CBCR 0x0048 +#define SDMMC_BBCR 0x004c +#define SDMMC_DBGC 0x0050 +#define SDMMC_DMAC 0x0080 +#define SDMMC_DLBA 0x0084 +#define SDMMC_IDST 0x0088 +#define SDMMC_IDIE 0x008c +#define SDMMC_CHDA 0x0090 +#define SDMMC_CBDA 0x0094 +#define SDMMC_FIFO 0x0100 + +/* register bitdefs */ +#define SDMMC_GCTRL_ACC_BY_AHB (1 << 31) +#define SDMMC_GCTRL_WAIT_MEM_ACC_DONE (1 << 30) +#define SDMMC_GCTRL_DDR_MODE (1 << 10) +#define SDMMC_GCTRL_DEBOUNCEEN (1 << 8) +#define SDMMC_GCTRL_DMAEN (1 << 5) +#define SDMMC_GCTRL_INTEN (1 << 4) +#define SDMMC_GCTRL_DMARESET (1 << 2) +#define SDMMC_GCTRL_FIFORESET (1 << 1) +#define SDMMC_GCTRL_SOFTRESET (1 << 0) +#define SDMMC_GCTRL_RESET \ + (SDMMC_GCTRL_SOFTRESET | \ + SDMMC_GCTRL_FIFORESET | \ + SDMMC_GCTRL_DMARESET) + +#define SDMMC_CLKCR_LOWPOWERON (1 << 17) +#define SDMMC_CLKCR_CARDCLKON (1 << 16) + +#define SDMMC_CMD_START (1 << 31) +#define SDMMC_CMD_USE_HOLD_REG (1 << 29) +#define SDMMC_CMD_VOL_SWITCH (1 << 28) +#define SDMMC_CMD_BOOT_ABORT (1 << 27) +#define SDMMC_CMD_BOOT_ACK_EXP (1 << 26) +#define SDMMC_CMD_ALT_BOOT_OPT (1 << 25) +#define SDMMC_CMD_ENBOOT (1 << 24) +#define SDMMC_CMD_CCS_EXP (1 << 23) +#define SDMMC_CMD_RD_CEATA_DEV (1 << 22) +#define SDMMC_CMD_UPCLK_ONLY (1 << 21) +#define SDMMC_CMD_SEND_INIT_SEQ (1 << 15) +#define SDMMC_CMD_STOP_ABORT_CMD (1 << 14) +#define SDMMC_CMD_WAIT_PRE_OVER (1 << 13) +#define SDMMC_CMD_SEND_AUTO_STOP (1 << 12) +#define SDMMC_CMD_SEQMOD (1 << 11) +#define SDMMC_CMD_WRITE (1 << 10) +#define SDMMC_CMD_DATA_EXP (1 << 9) +#define SDMMC_CMD_CHECK_RSP_CRC (1 << 8) +#define SDMMC_CMD_LONG_RSP (1 << 7) +#define SDMMC_CMD_RSP_EXP (1 << 6) + +#define SDMMC_INT_CARD_REMOVE (1 << 31) +#define SDMMC_INT_CARD_INSERT (1 << 30) +#define SDMMC_INT_SDIO_INT (1 << 16) +#define SDMMC_INT_END_BIT_ERR (1 << 15) +#define SDMMC_INT_AUTO_CMD_DONE (1 << 14) +#define SDMMC_INT_START_BIT_ERR (1 << 13) +#define SDMMC_INT_HW_LOCKED (1 << 12) +#define SDMMC_INT_FIFO_RUN_ERR (1 << 11) +#define SDMMC_INT_VOL_CHG_DONE (1 << 10) +#define SDMMC_INT_DATA_STARVE (1 << 10) +#define SDMMC_INT_BOOT_START (1 << 9) +#define SDMMC_INT_DATA_TIMEOUT (1 << 9) +#define SDMMC_INT_ACK_RCV (1 << 8) +#define SDMMC_INT_RESP_TIMEOUT (1 << 8) +#define SDMMC_INT_DATA_CRC_ERR (1 << 7) +#define SDMMC_INT_RESP_CRC_ERR (1 << 6) +#define SDMMC_INT_RX_DATA_REQ (1 << 5) +#define SDMMC_INT_TX_DATA_REQ (1 << 4) +#define SDMMC_INT_DATA_OVER (1 << 3) +#define SDMMC_INT_CMD_DONE (1 << 2) +#define SDMMC_INT_RESP_ERR (1 << 1) +#define SDMMC_INT_ERROR \ + (SDMMC_INT_RESP_ERR | SDMMC_INT_RESP_CRC_ERR | \ + SDMMC_INT_DATA_CRC_ERR | SDMMC_INT_RESP_TIMEOUT | \ + SDMMC_INT_FIFO_RUN_ERR | SDMMC_INT_HW_LOCKED | \ + SDMMC_INT_START_BIT_ERR | SDMMC_INT_END_BIT_ERR) + +#define SDMMC_STATUS_DMAREQ (1 << 31) +#define SDMMC_STATUS_DATA_FSM_BUSY (1 << 10) +#define SDMMC_STATUS_CARD_DATA_BUSY (1 << 9) +#define SDMMC_STATUS_CARD_PRESENT (1 << 8) +#define SDMMC_STATUS_FIFO_FULL (1 << 3) +#define SDMMC_STATUS_FIFO_EMPTY (1 << 2) +#define SDMMC_STATUS_TXWL_FLAG (1 << 1) +#define SDMMC_STATUS_RXWL_FLAG (1 << 0) + +#define SDMMC_FUNCSEL_CEATA_DEV_INTEN (1 << 10) +#define SDMMC_FUNCSEL_SEND_AUTO_STOP_CCSD (1 << 9) +#define SDMMC_FUNCSEL_SEND_CCSD (1 << 8) +#define SDMMC_FUNCSEL_ABT_RD_DATA (1 << 2) +#define SDMMC_FUNCSEL_SDIO_RD_WAIT (1 << 1) +#define SDMMC_FUNCSEL_SEND_IRQ_RSP (1 << 0) + +#define SDMMC_DMAC_REFETCH_DES (1 << 31) +#define SDMMC_DMAC_IDMA_ON (1 << 7) +#define SDMMC_DMAC_FIX_BURST (1 << 1) +#define SDMMC_DMAC_SOFTRESET (1 << 0) + +#define SDMMC_IDST_HOST_ABT (1 << 10) +#define SDMMC_IDST_ABNORMAL_INT_SUM (1 << 9) +#define SDMMC_IDST_NORMAL_INT_SUM (1 << 8) +#define SDMMC_IDST_CARD_ERR_SUM (1 << 5) +#define SDMMC_IDST_DES_INVALID (1 << 4) +#define SDMMC_IDST_FATAL_BUS_ERR (1 << 2) +#define SDMMC_IDST_RECEIVE_INT (1 << 1) +#define SDMMC_IDST_TRANSMIT_INT (1 << 0) +#define SDMMC_IDST_ERROR \ + (SDMMC_IDST_ABNORMAL_INT_SUM | \ + SDMMC_IDST_CARD_ERR_SUM | \ + SDMMC_IDST_DES_INVALID | \ + SDMMC_IDST_FATAL_BUS_ERR) +#define SDMMC_IDST_COMPLETE \ + (SDMMC_IDST_RECEIVE_INT | \ + SDMMC_IDST_TRANSMIT_INT) + +struct sxisdmmc_idma_descriptor { + u_int dma_config; +#define SDMMC_IDMA_CONFIG_DIC (1 << 1) +#define SDMMC_IDMA_CONFIG_LD (1 << 2) +#define SDMMC_IDMA_CONFIG_FD (1 << 3) +#define SDMMC_IDMA_CONFIG_CH (1 << 4) +#define SDMMC_IDMA_CONFIG_ER (1 << 5) +#define SDMMC_IDMA_CONFIG_CES (1 << 30) +#define SDMMC_IDMA_CONFIG_OWN (1 << 31) + u_int dma_buf_size; + u_int dma_buf_addr; + u_int dma_next; +}; + +#define SDMMC_NDESC 16 +#define SDMMC_DMA_FTRGLEVEL_A20 0x20070008 + +int sxisdmmc_match(struct device *, void *, void *); +void sxisdmmc_attach(struct device *, struct device *, void *); + +int sxisdmmc_host_reset(sdmmc_chipset_handle_t); +uint32_t sxisdmmc_host_ocr(sdmmc_chipset_handle_t); +int sxisdmmc_host_maxblklen(sdmmc_chipset_handle_t); +int sxisdmmc_card_detect(sdmmc_chipset_handle_t); +int sxisdmmc_bus_power(sdmmc_chipset_handle_t, uint32_t); +int sxisdmmc_bus_clock(sdmmc_chipset_handle_t, int, int); +int sxisdmmc_bus_width(sdmmc_chipset_handle_t, int); +void sxisdmmc_exec_command(sdmmc_chipset_handle_t, + struct sdmmc_command *); +void sxisdmmc_card_intr_mask(sdmmc_chipset_handle_t, int); +void sxisdmmc_card_intr_ack(sdmmc_chipset_handle_t); + +struct sdmmc_chip_functions sxisdmmc_chip_functions = { + /* host controller reset */ + sxisdmmc_host_reset, + /* host controller capabilities */ + sxisdmmc_host_ocr, + sxisdmmc_host_maxblklen, + /* card detection */ + sxisdmmc_card_detect, + /* bus power and clock frequency */ + sxisdmmc_bus_power, + sxisdmmc_bus_clock, + sxisdmmc_bus_width, + /* command execution */ + sxisdmmc_exec_command, + /* card interrupt */ + sxisdmmc_card_intr_mask, + sxisdmmc_card_intr_ack, +}; + +struct sxisdmmc_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + u_int sc_port; + struct device *sc_sdmmc_dev; +}; + +struct cfdriver sxisdmmc_cd = { + NULL, "sxisdmmc", DV_DULL +}; + +struct cfattach sxisdmmc_ca = { + sizeof(struct sxisdmmc_softc), NULL, sxisdmmc_attach +}; + +void +sxisdmmc_attach(struct device *parent, struct device *self, void *args) +{ + struct sxisdmmc_softc *sc = (struct sxisdmmc_softc *)self; + struct armv7_attach_args *aa = args; + struct sdmmcbus_attach_args saa; + + sc->sc_iot = aa->aa_iot; + sc->sc_port = aa->aa_dev->unit; + if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("sxisdmmc_attach: bus_space_map failed!"); + + printf(": SD/MMC interface\n"); + + sxisdmmc_host_reset(sc); + sxisdmmc_bus_width(sc, 1); + sxiccmu_set_sdmmc_clock(sc->sc_port, 400); + + memset(&saa, 0, sizeof(saa)); + saa.saa_busname = "sdmmc"; + saa.sct = &sxisdmmc_chip_functions; + saa.sch = sc; + saa.caps = SMC_CAPS_4BIT_MODE| + SMC_CAPS_8BIT_MODE| + SMC_CAPS_SD_HIGHSPEED| + SMC_CAPS_MMC_HIGHSPEED| + SMC_CAPS_AUTO_STOP; + + sc->sc_sdmmc_dev = config_found(self, &saa, NULL); +} + +int +sxisdmmc_host_reset(sdmmc_chipset_handle_t sch) +{ + struct sxisdmmc_softc *sc = sch; + DPRINTF(CD_INIT, ("%s: host reset\n", DNAME(sc))); + SXISET4(sc, SDMMC_GCTRL, SDMMC_GCTRL_RESET); + delay(20); /* XXX ? */ + return 0; +} + +uint32_t +sxisdmmc_host_ocr(sdmmc_chipset_handle_t sch) +{ + return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V; +} + +int +sxisdmmc_host_maxblklen(sdmmc_chipset_handle_t sch) +{ + return 4096; +} + +int +sxisdmmc_card_detect(sdmmc_chipset_handle_t sch) +{ + return 1; /* XXX impl. gpio CD */ +} + +int +sxisdmmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) +{ + return 0; +} + +int +sxisdmmc_update_clock(struct sxisdmmc_softc *sc) +{ + u_int retry; + + SXIWRITE4(sc, SDMMC_CMD, + SDMMC_CMD_START | SDMMC_CMD_UPCLK_ONLY | SDMMC_CMD_WAIT_PRE_OVER); + for (retry = 0x100000; retry > 0; retry--) { + if (!(SXIREAD4(sc, SDMMC_CMD) & SDMMC_CMD_START)) + break; + delay(10); + } + if (retry == 0) { + DPRINTF(CD_TIMO, ("%s: timeout updating clk\n", DNAME(sc))); + return ETIMEDOUT; + } + + /* clear pending interrupts */ + SXIWRITE4(sc, SDMMC_RINT, SXIREAD4(sc, SDMMC_RINT)); + + return 0; +} + +int +sxisdmmc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing) +{ + struct sxisdmmc_softc *sc = sch; + u_int clkcr; + + DPRINTF(CD_DBG, ("freq = %d\n", freq)); + + clkcr = SXIREAD4(sc, SDMMC_CLKCR); + if (clkcr & SDMMC_CLKCR_CARDCLKON) { + clkcr &= ~SDMMC_CLKCR_CARDCLKON; + SXIWRITE4(sc, SDMMC_CLKCR, clkcr); + if (sxisdmmc_update_clock(sc) != 0) + return 1; + } + + if (freq) { + clkcr &= ~0xffff; + SXIWRITE4(sc, SDMMC_CLKCR, clkcr); + if (sxisdmmc_update_clock(sc) != 0) + return 1; + + if (sxiccmu_set_sdmmc_clock(sc->sc_port, freq) != 0) + return 1; + + clkcr |= SDMMC_CLKCR_CARDCLKON; + SXIWRITE4(sc, SDMMC_CLKCR, clkcr); + if (sxisdmmc_update_clock(sc) != 0) + return 1; + } + + return 0; +} + +int +sxisdmmc_bus_width(sdmmc_chipset_handle_t sch, int width) +{ + struct sxisdmmc_softc *sc = sch; + + DPRINTF(CD_DBG, ("%s: width %d\n", DNAME(sc), width)); + + if (width != 1 && width != 4 && width != 8) + return 1; + + SXIWRITE4(sc, SDMMC_WIDTH, width >> 2); + + return 0; +} + +int +sxisdmmc_xfer_wait(struct sxisdmmc_softc *sc, struct sdmmc_command *cmd) +{ + u_int wait2clear = cmd->c_flags & SCF_CMD_READ ? + SDMMC_STATUS_FIFO_EMPTY : SDMMC_STATUS_FIFO_FULL; + u_int retry; + + for (retry = 0x100000; retry > 0; retry--) { + if (SXIREAD4(sc, SDMMC_STATUS) & wait2clear) { + delay(10); + continue; + } + return 0; + } + + return ETIMEDOUT; +} + +int +sxisdmmc_xfer_data(struct sxisdmmc_softc *sc, struct sdmmc_command *cmd) +{ + u_int *datap = (u_int *)cmd->c_buf; + int i; + + for (i = 0; i < (cmd->c_resid >> 2); i++) { + if (sxisdmmc_xfer_wait(sc, cmd)) + return ETIMEDOUT; + + if (cmd->c_flags & SCF_CMD_READ) + datap[i] = SXIREAD4(sc, SDMMC_FIFO); + else + SXIWRITE4(sc, SDMMC_FIFO, datap[i]); + } + + return 0; +} + +void +sxisdmmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) +{ + struct sxisdmmc_softc *sc = sch; + u_int cmdval = SDMMC_CMD_START; + u_int status; + int retry; + + DPRINTF(CD_SPAM, ("%s: exec opcode %d flags 0x%x data %p datalen %d\n", + DNAME(sc), cmd->c_opcode, cmd->c_flags, cmd->c_data, + cmd->c_datalen)); + + if (cmd->c_opcode == 0) + cmdval |= SDMMC_CMD_SEND_INIT_SEQ; + if (cmd->c_flags & SCF_RSP_PRESENT) + cmdval |= SDMMC_CMD_RSP_EXP; + if (cmd->c_flags & SCF_RSP_136) + cmdval |= SDMMC_CMD_LONG_RSP; + if (cmd->c_flags & SCF_RSP_CRC) + cmdval |= SDMMC_CMD_CHECK_RSP_CRC; + + if (cmd->c_datalen > 0) { + u_int nblks; + + cmdval |= SDMMC_CMD_DATA_EXP | SDMMC_CMD_WAIT_PRE_OVER; + if (!(cmd->c_flags & SCF_CMD_READ)) + cmdval |= SDMMC_CMD_WRITE; + + nblks = cmd->c_datalen / cmd->c_blklen; + if (nblks == 0 || (cmd->c_datalen % cmd->c_blklen) != 0) + ++nblks; + if (nblks > 1) + cmdval |= SDMMC_CMD_SEND_AUTO_STOP; + + SXIWRITE4(sc, SDMMC_BLKSZ, cmd->c_blklen); + SXIWRITE4(sc, SDMMC_BYTECNT, nblks * cmd->c_blklen); + } + DPRINTF(CD_SPAM, ("%s: exec cmdval %#8x\n", DNAME(sc), cmdval)); + + SXIWRITE4(sc, SDMMC_ARG, cmd->c_arg); + + if (cmd->c_datalen == 0) + SXIWRITE4(sc, SDMMC_CMD, cmdval | cmd->c_opcode); + else { + SXISET4(sc, SDMMC_GCTRL, SDMMC_GCTRL_ACC_BY_AHB); + SXIWRITE4(sc, SDMMC_CMD, cmdval | cmd->c_opcode); + cmd->c_resid = cmd->c_datalen; + cmd->c_buf = cmd->c_data; + cmd->c_error = sxisdmmc_xfer_data(sc, cmd); + if (cmd->c_error) { + DPRINTF(CD_TIMO, ("%s: exec xfer data err %d\n", + DNAME(sc), cmd->c_error)); + goto done; + } + } + + retry = 0x100000; + while (--retry > 0) { + status = SXIREAD4(sc, SDMMC_RINT); + if (status & SDMMC_INT_ERROR) { + retry = 0; + break; + } + if (status & SDMMC_INT_CMD_DONE) + break; + delay(10); + } + if (retry == 0) { + DPRINTF(CD_TIMO, ("%s: exec cmd timeout %#8x\n", + DNAME(sc), status)); + cmd->c_error = ETIMEDOUT; + goto done; + } + DPRINTF(CD_SPAM, ("%s: status %#8x\n", DNAME(sc), status)); + + if (cmd->c_datalen > 0) { + retry = 0x10000; + do { + status = SXIREAD4(sc, SDMMC_RINT); + if (status & SDMMC_INT_ERROR) { + retry = 0; + break; + } + if (cmd->c_blklen < cmd->c_datalen && + (status & SDMMC_INT_AUTO_CMD_DONE) != 0) + break; + else if (status & SDMMC_INT_DATA_OVER); + break; + delay(10); + } while (--retry > 0); + if (retry == 0) { + DPRINTF(CD_TIMO, ("%s: exec timeout 2 %#8x\n", + DNAME(sc), status)); + cmd->c_error = ETIMEDOUT; + goto done; + } + } + + if (cmd->c_flags & SCF_RSP_BSY) { + retry = 0x100000; + while (--retry > 0) { + status = SXIREAD4(sc, SDMMC_STATUS); + if (status & SDMMC_STATUS_CARD_DATA_BUSY) + break; + } + if (retry == 0) { + DPRINTF(CD_TIMO, ("%s: exec busy timeout %#8x\n", + DNAME(sc), status)); + cmd->c_error = ETIMEDOUT; + goto done; + } + } + + if (cmd->c_flags & SCF_RSP_PRESENT) { + if (cmd->c_flags & SCF_RSP_136) { + cmd->c_resp[0] = SXIREAD4(sc, SDMMC_RESP0); + cmd->c_resp[1] = SXIREAD4(sc, SDMMC_RESP1); + cmd->c_resp[2] = SXIREAD4(sc, SDMMC_RESP2); + cmd->c_resp[3] = SXIREAD4(sc, SDMMC_RESP3); + if (cmd->c_flags & SCF_RSP_CRC) { + cmd->c_resp[0] = (cmd->c_resp[0] >> 8) | + (cmd->c_resp[1] << 24); + cmd->c_resp[1] = (cmd->c_resp[1] >> 8) | + (cmd->c_resp[2] << 24); + cmd->c_resp[2] = (cmd->c_resp[2] >> 8) | + (cmd->c_resp[3] << 24); + cmd->c_resp[3] = (cmd->c_resp[3] >> 8); + } + } else + cmd->c_resp[0] = SXIREAD4(sc, SDMMC_RESP0); + } + +done: + cmd->c_flags |= SCF_ITSDONE; + + if (cmd->c_error) { + DPRINTF(CD_ERR, ("%s: exec i/o err %d\n", DNAME(sc), + cmd->c_error)); + SXIWRITE4(sc, SDMMC_GCTRL, SDMMC_GCTRL_RESET); + sxisdmmc_update_clock(sc); + } /* XXX else { ?? or return; from above block */ + SXIWRITE4(sc, SDMMC_RINT, 0xffffffff); + SXISET4(sc, SDMMC_GCTRL, SDMMC_GCTRL_FIFORESET); +/* XXX } */ +} + +void +sxisdmmc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable) +{ +} + +void +sxisdmmc_card_intr_ack(sdmmc_chipset_handle_t sch) +{ +}