uboot/drivers/mmc/bfin_sdh.c
<<
>>
Prefs
   1/*
   2 * Driver for Blackfin on-chip SDH controller
   3 *
   4 * Copyright (c) 2008-2009 Analog Devices Inc.
   5 *
   6 * Licensed under the GPL-2 or later.
   7 */
   8
   9#include <common.h>
  10#include <malloc.h>
  11#include <part.h>
  12#include <mmc.h>
  13
  14#include <asm/io.h>
  15#include <linux/errno.h>
  16#include <asm/byteorder.h>
  17#include <asm/blackfin.h>
  18#include <asm/clock.h>
  19#include <asm/portmux.h>
  20#include <asm/mach-common/bits/sdh.h>
  21#include <asm/mach-common/bits/dma.h>
  22
  23#if defined(__ADSPBF50x__) || defined(__ADSPBF51x__) || defined(__ADSPBF60x__)
  24# define bfin_read_SDH_CLK_CTL          bfin_read_RSI_CLK_CONTROL
  25# define bfin_write_SDH_CLK_CTL         bfin_write_RSI_CLK_CONTROL
  26# define bfin_write_SDH_ARGUMENT        bfin_write_RSI_ARGUMENT
  27# define bfin_write_SDH_COMMAND         bfin_write_RSI_COMMAND
  28# define bfin_read_SDH_RESPONSE0        bfin_read_RSI_RESPONSE0
  29# define bfin_read_SDH_RESPONSE1        bfin_read_RSI_RESPONSE1
  30# define bfin_read_SDH_RESPONSE2        bfin_read_RSI_RESPONSE2
  31# define bfin_read_SDH_RESPONSE3        bfin_read_RSI_RESPONSE3
  32# define bfin_write_SDH_DATA_TIMER      bfin_write_RSI_DATA_TIMER
  33# define bfin_write_SDH_DATA_LGTH       bfin_write_RSI_DATA_LGTH
  34# define bfin_read_SDH_DATA_CTL         bfin_read_RSI_DATA_CONTROL
  35# define bfin_write_SDH_DATA_CTL        bfin_write_RSI_DATA_CONTROL
  36# define bfin_read_SDH_STATUS           bfin_read_RSI_STATUS
  37# define bfin_write_SDH_STATUS_CLR      bfin_write_RSI_STATUSCL
  38# define bfin_read_SDH_CFG              bfin_read_RSI_CONFIG
  39# define bfin_write_SDH_CFG             bfin_write_RSI_CONFIG
  40# if defined(__ADSPBF60x__)
  41# define bfin_read_SDH_BLK_SIZE         bfin_read_RSI_BLKSZ
  42# define bfin_write_SDH_BLK_SIZE        bfin_write_RSI_BLKSZ
  43# define bfin_write_DMA_START_ADDR      bfin_write_DMA10_START_ADDR
  44# define bfin_write_DMA_X_COUNT         bfin_write_DMA10_X_COUNT
  45# define bfin_write_DMA_X_MODIFY        bfin_write_DMA10_X_MODIFY
  46# define bfin_write_DMA_CONFIG          bfin_write_DMA10_CONFIG
  47# else
  48# define bfin_read_SDH_PWR_CTL          bfin_read_RSI_PWR_CONTROL
  49# define bfin_write_SDH_PWR_CTL         bfin_write_RSI_PWR_CONTROL
  50# define bfin_write_DMA_START_ADDR      bfin_write_DMA4_START_ADDR
  51# define bfin_write_DMA_X_COUNT         bfin_write_DMA4_X_COUNT
  52# define bfin_write_DMA_X_MODIFY        bfin_write_DMA4_X_MODIFY
  53# define bfin_write_DMA_CONFIG          bfin_write_DMA4_CONFIG
  54# endif
  55# define PORTMUX_PINS \
  56        { P_RSI_DATA0, P_RSI_DATA1, P_RSI_DATA2, P_RSI_DATA3, P_RSI_CMD, P_RSI_CLK, 0 }
  57#elif defined(__ADSPBF54x__)
  58# define bfin_write_DMA_START_ADDR      bfin_write_DMA22_START_ADDR
  59# define bfin_write_DMA_X_COUNT         bfin_write_DMA22_X_COUNT
  60# define bfin_write_DMA_X_MODIFY        bfin_write_DMA22_X_MODIFY
  61# define bfin_write_DMA_CONFIG          bfin_write_DMA22_CONFIG
  62# define PORTMUX_PINS \
  63        { P_SD_D0, P_SD_D1, P_SD_D2, P_SD_D3, P_SD_CLK, P_SD_CMD, 0 }
  64#else
  65# error no support for this proc yet
  66#endif
  67
  68static int
  69sdh_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd)
  70{
  71        unsigned int status, timeout;
  72        int cmd = mmc_cmd->cmdidx;
  73        int flags = mmc_cmd->resp_type;
  74        int arg = mmc_cmd->cmdarg;
  75        int ret;
  76        u16 sdh_cmd;
  77
  78        sdh_cmd = cmd | CMD_E;
  79        if (flags & MMC_RSP_PRESENT)
  80                sdh_cmd |= CMD_RSP;
  81        if (flags & MMC_RSP_136)
  82                sdh_cmd |= CMD_L_RSP;
  83#ifdef RSI_BLKSZ
  84        sdh_cmd |= CMD_DATA0_BUSY;
  85#endif
  86
  87        bfin_write_SDH_ARGUMENT(arg);
  88        bfin_write_SDH_COMMAND(sdh_cmd);
  89
  90        /* wait for a while */
  91        timeout = 0;
  92        do {
  93                if (++timeout > 1000000) {
  94                        status = CMD_TIME_OUT;
  95                        break;
  96                }
  97                udelay(1);
  98                status = bfin_read_SDH_STATUS();
  99        } while (!(status & (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT |
 100                CMD_CRC_FAIL)));
 101
 102        if (flags & MMC_RSP_PRESENT) {
 103                mmc_cmd->response[0] = bfin_read_SDH_RESPONSE0();
 104                if (flags & MMC_RSP_136) {
 105                        mmc_cmd->response[1] = bfin_read_SDH_RESPONSE1();
 106                        mmc_cmd->response[2] = bfin_read_SDH_RESPONSE2();
 107                        mmc_cmd->response[3] = bfin_read_SDH_RESPONSE3();
 108                }
 109        }
 110
 111        if (status & CMD_TIME_OUT)
 112                ret = -ETIMEDOUT;
 113        else if (status & CMD_CRC_FAIL && flags & MMC_RSP_CRC)
 114                ret = -ECOMM;
 115        else
 116                ret = 0;
 117
 118        bfin_write_SDH_STATUS_CLR(CMD_SENT_STAT | CMD_RESP_END_STAT |
 119                                CMD_TIMEOUT_STAT | CMD_CRC_FAIL_STAT);
 120#ifdef RSI_BLKSZ
 121        /* wait till card ready */
 122        while (!(bfin_read_RSI_ESTAT() & SD_CARD_READY))
 123                continue;
 124        bfin_write_RSI_ESTAT(SD_CARD_READY);
 125#endif
 126
 127        return ret;
 128}
 129
 130/* set data for single block transfer */
 131static int sdh_setup_data(struct mmc *mmc, struct mmc_data *data)
 132{
 133        u16 data_ctl = 0;
 134        u16 dma_cfg = 0;
 135        unsigned long data_size = data->blocksize * data->blocks;
 136
 137        /* Don't support write yet. */
 138        if (data->flags & MMC_DATA_WRITE)
 139                return -EOPNOTSUPP;
 140#ifndef RSI_BLKSZ
 141        data_ctl |= ((ffs(data->blocksize) - 1) << 4);
 142#else
 143        bfin_write_SDH_BLK_SIZE(data->blocksize);
 144#endif
 145        data_ctl |= DTX_DIR;
 146        bfin_write_SDH_DATA_CTL(data_ctl);
 147        dma_cfg = WDSIZE_32 | PSIZE_32 | RESTART | WNR | DMAEN;
 148
 149        bfin_write_SDH_DATA_TIMER(-1);
 150
 151        blackfin_dcache_flush_invalidate_range(data->dest,
 152                        data->dest + data_size);
 153        /* configure DMA */
 154        bfin_write_DMA_START_ADDR(data->dest);
 155        bfin_write_DMA_X_COUNT(data_size / 4);
 156        bfin_write_DMA_X_MODIFY(4);
 157        bfin_write_DMA_CONFIG(dma_cfg);
 158        bfin_write_SDH_DATA_LGTH(data_size);
 159        /* kick off transfer */
 160        bfin_write_SDH_DATA_CTL(bfin_read_SDH_DATA_CTL() | DTX_DMA_E | DTX_E);
 161
 162        return 0;
 163}
 164
 165
 166static int bfin_sdh_request(struct mmc *mmc, struct mmc_cmd *cmd,
 167                struct mmc_data *data)
 168{
 169        u32 status;
 170        int ret = 0;
 171
 172        if (data) {
 173                ret = sdh_setup_data(mmc, data);
 174                if (ret)
 175                        return ret;
 176        }
 177
 178        ret = sdh_send_cmd(mmc, cmd);
 179        if (ret) {
 180                bfin_write_SDH_COMMAND(0);
 181                bfin_write_DMA_CONFIG(0);
 182                bfin_write_SDH_DATA_CTL(0);
 183                SSYNC();
 184                printf("sending CMD%d failed\n", cmd->cmdidx);
 185                return ret;
 186        }
 187
 188        if (data) {
 189                do {
 190                        udelay(1);
 191                        status = bfin_read_SDH_STATUS();
 192                } while (!(status & (DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL |
 193                         RX_OVERRUN)));
 194
 195                if (status & DAT_TIME_OUT) {
 196                        bfin_write_SDH_STATUS_CLR(DAT_TIMEOUT_STAT);
 197                        ret = -ETIMEDOUT;
 198                } else if (status & (DAT_CRC_FAIL | RX_OVERRUN)) {
 199                        bfin_write_SDH_STATUS_CLR(DAT_CRC_FAIL_STAT | RX_OVERRUN_STAT);
 200                        ret = -ECOMM;
 201                } else
 202                        bfin_write_SDH_STATUS_CLR(DAT_BLK_END_STAT | DAT_END_STAT);
 203
 204                if (ret) {
 205                        printf("tranfering data failed\n");
 206                        return ret;
 207                }
 208        }
 209        return 0;
 210}
 211
 212static void sdh_set_clk(unsigned long clk)
 213{
 214        unsigned long sys_clk;
 215        unsigned long clk_div;
 216        u16 clk_ctl = 0;
 217
 218        clk_ctl = bfin_read_SDH_CLK_CTL();
 219        if (clk) {
 220                /* setting SD_CLK */
 221                sys_clk = get_sclk();
 222                bfin_write_SDH_CLK_CTL(clk_ctl & ~CLK_E);
 223                if (sys_clk % (2 * clk) == 0)
 224                        clk_div = sys_clk / (2 * clk) - 1;
 225                else
 226                        clk_div = sys_clk / (2 * clk);
 227
 228                if (clk_div > 0xff)
 229                        clk_div = 0xff;
 230                clk_ctl |= (clk_div & 0xff);
 231                clk_ctl |= CLK_E;
 232                bfin_write_SDH_CLK_CTL(clk_ctl);
 233        } else
 234                bfin_write_SDH_CLK_CTL(clk_ctl & ~CLK_E);
 235}
 236
 237static int bfin_sdh_set_ios(struct mmc *mmc)
 238{
 239        u16 cfg = 0;
 240        u16 clk_ctl = 0;
 241
 242        if (mmc->bus_width == 4) {
 243                cfg = bfin_read_SDH_CFG();
 244#ifndef RSI_BLKSZ
 245                cfg &= ~PD_SDDAT3;
 246#endif
 247                cfg |= PUP_SDDAT3;
 248                bfin_write_SDH_CFG(cfg);
 249                clk_ctl |= WIDE_BUS_4;
 250        }
 251        bfin_write_SDH_CLK_CTL(clk_ctl);
 252        sdh_set_clk(mmc->clock);
 253
 254        return 0;
 255}
 256
 257static int bfin_sdh_init(struct mmc *mmc)
 258{
 259        const unsigned short pins[] = PORTMUX_PINS;
 260        int ret;
 261
 262        /* Initialize sdh controller */
 263        ret = peripheral_request_list(pins, "bfin_sdh");
 264        if (ret < 0)
 265                return ret;
 266#if defined(__ADSPBF54x__)
 267        bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);
 268#endif
 269        bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);
 270        /* Disable card detect pin */
 271        bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | 0x60);
 272#ifndef RSI_BLKSZ
 273        bfin_write_SDH_PWR_CTL(PWR_ON | ROD_CTL);
 274#else
 275        bfin_write_SDH_CFG(bfin_read_SDH_CFG() | PWR_ON);
 276#endif
 277        return 0;
 278}
 279
 280static const struct mmc_ops bfin_mmc_ops = {
 281        .send_cmd       = bfin_sdh_request,
 282        .set_ios        = bfin_sdh_set_ios,
 283        .init           = bfin_sdh_init,
 284};
 285
 286static struct mmc_config bfin_mmc_cfg = {
 287        .name           = "Blackfin SDH",
 288        .ops            = &bfin_mmc_ops,
 289        .host_caps      = MMC_MODE_4BIT,
 290        .voltages       = MMC_VDD_32_33 | MMC_VDD_33_34,
 291        .b_max          = CONFIG_SYS_MMC_MAX_BLK_COUNT,
 292};
 293
 294int bfin_mmc_init(bd_t *bis)
 295{
 296        struct mmc *mmc;
 297
 298        bfin_mmc_cfg.f_max = get_sclk();
 299        bfin_mmc_cfg.f_min = bfin_mmc_cfg.f_max >> 9;
 300
 301        mmc = mmc_create(&bfin_mmc_cfg, NULL);
 302        if (mmc == NULL)
 303                return -1;
 304
 305        return 0;
 306}
 307