Logo Search packages:      
Sourcecode: qi version File versions  Download package

hs_mmc.c

#include <qi.h>
#include "hs_mmc.h"
#include <string.h>
#include <glamo-mmc.h>

#define HCLK_OPERATION
#undef DEBUG_HSMMC
#ifdef DEBUG_HSMMC
#define dbg(x...)       printf(x)
#else
#define dbg(x...)       do { } while (0)
#endif

//#include <linux-mmc.h>
#include <linux-mmc-protocol.h>
#include <s3c6410.h>
//#include <linux/mmc/protocol.h>
//#include <asm/io.h>
//#include <movi.h>

#include "hs_mmc.h"
#include <mmc.h>

#define SDI_Tx_buffer_HSMMC         (0x51000000)
#define SDI_Rx_buffer_HSMMC         (0x51000000+(0x300000))
#define SDI_Compare_buffer_HSMMC    (0x51000000+(0x600000))

#define Card_OneBlockSize_ver1            512

#define MMC_DEFAULT_RCA             (1<<16)

/*  Global variables */

static u32 HS_DMA_END = 0;
static u32 rca = 0;

static ulong HCLK;

int movi_hc = 1; /* sdhc style block indexing */
enum card_type card_type;

/* extern functions */
extern ulong get_HCLK(void);


#define s3c_hsmmc_readl(x)    *((unsigned int *)(((ELFIN_HSMMC_BASE + (HSMMC_CHANNEL * 0x100000)) + (x))))
#define s3c_hsmmc_readw(x)    *((unsigned short *)(((ELFIN_HSMMC_BASE + (HSMMC_CHANNEL * 0x100000)) + (x))))
#define s3c_hsmmc_readb(x)    *((unsigned char *)(((ELFIN_HSMMC_BASE + (HSMMC_CHANNEL * 0x100000)) + (x))))

#define s3c_hsmmc_writel(v,x) *((unsigned int *) (((ELFIN_HSMMC_BASE + (HSMMC_CHANNEL * 0x100000)) + (x)))) = v
#define s3c_hsmmc_writew(v,x) *((unsigned short *)(((ELFIN_HSMMC_BASE + (HSMMC_CHANNEL * 0x100000)) + (x)))) = v
#define s3c_hsmmc_writeb(v,x) *((unsigned char *)(((ELFIN_HSMMC_BASE + (HSMMC_CHANNEL * 0x100000)) + (x)))) = v

#define readl(x)  *((unsigned int *)(x))
#define writel(v, x)    *((unsigned int *)(x)) = v

#define UNSTUFF_BITS(resp,start,size)                                   \
        ({                                                              \
                const int __size = size;                                \
                const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \
                const int __off = 3 - ((start) / 32);                   \
                const int __shft = (start) & 31;                        \
                u32 __res;                                              \
                                                                        \
                __res = resp[__off] >> __shft;                          \
                if (__size + __shft > 32)                               \
                        __res |= resp[__off-1] << ((32 - __shft) & 31); \
                __res & __mask;                                         \
        })

static int wait_for_cmd_done (void)
{
      u32 i;
      ushort n_int, e_int;

      dbg("wait_for_cmd_done\n");
      for (i = 0; i < 0x20000000; i++) {
            n_int = s3c_hsmmc_readw(HM_NORINTSTS);
            dbg("  HM_NORINTSTS: %04x\n", n_int);
            if (n_int & 0x8000)
                  /* any error */
                  break;
            if (n_int & 0x0001)
                  /* command complete */
                  return 0;
      }

      e_int = s3c_hsmmc_readw(HM_ERRINTSTS);
      s3c_hsmmc_writew(e_int, HM_ERRINTSTS);
      s3c_hsmmc_writew(n_int, HM_NORINTSTS);
      puts("cmd error1: 0x");
      print32(e_int);
      puts(", HM_NORINTSTS: 0x");
      print32(n_int);
      puts("\n");

      return -1;
}


static void ClearCommandCompleteStatus(void)
{
      s3c_hsmmc_writew(1 << 0, HM_NORINTSTS);
      while (s3c_hsmmc_readw(HM_NORINTSTS) & 0x1) {
            s3c_hsmmc_writew(1 << 0, HM_NORINTSTS);
      }
}

static void card_irq_enable(ushort temp)
{
      s3c_hsmmc_writew((s3c_hsmmc_readw(HM_NORINTSTSEN) & 0xFEFF) | (temp << 8), HM_NORINTSTSEN);
}

void hsmmc_reset (void)
{
      s3c_hsmmc_writeb(0x3, HM_SWRST);
}

void hsmmc_set_gpio (void)
{
      u32 reg;

      reg = readl(GPGCON) & 0xf0000000;
      writel(reg | 0x02222222, GPGCON);

      reg = readl(GPGPUD) & 0xfffff000;
      writel(reg, GPGPUD);
}

static void set_transfer_mode_register (u32 MultiBlk, u32 DataDirection, u32 AutoCmd12En, u32 BlockCntEn, u32 DmaEn)
{
      s3c_hsmmc_writew((s3c_hsmmc_readw(HM_TRNMOD) & ~(0xffff)) | (MultiBlk << 5)
            | (DataDirection << 4) | (AutoCmd12En << 2)
            | (BlockCntEn << 1) | (DmaEn << 0), HM_TRNMOD);
//    dbg("\nHM_TRNMOD = 0x%04x\n", HM_TRNMOD);
}

static void set_arg_register (u32 arg)
{
      s3c_hsmmc_writel(arg, HM_ARGUMENT);
}

static void set_blkcnt_register(ushort uBlkCnt)
{
      s3c_hsmmc_writew(uBlkCnt, HM_BLKCNT);
}

static void SetSystemAddressReg(u32 SysAddr)
{
      s3c_hsmmc_writel(SysAddr, HM_SYSAD);
}

static void set_blksize_register(ushort uDmaBufBoundary, ushort uBlkSize)
{
      s3c_hsmmc_writew((uDmaBufBoundary << 12) | (uBlkSize), HM_BLKSIZE);
}

static void ClearErrInterruptStatus(void)
{
      while (s3c_hsmmc_readw(HM_NORINTSTS) & (0x1 << 15)) {
            s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTS), HM_NORINTSTS);
            s3c_hsmmc_writew(s3c_hsmmc_readw(HM_ERRINTSTS), HM_ERRINTSTS);
      }
}

static void InterruptEnable(ushort NormalIntEn, ushort ErrorIntEn)
{
      ClearErrInterruptStatus();
      s3c_hsmmc_writew(NormalIntEn, HM_NORINTSTSEN);
      s3c_hsmmc_writew(ErrorIntEn, HM_ERRINTSTSEN);
}

static void hsmmc_clock_onoff (int on)
{
      u16 reg16;

      if (on == 0) {
            reg16 = s3c_hsmmc_readw(HM_CLKCON) & ~(0x1<<2);
            s3c_hsmmc_writew(reg16, HM_CLKCON);
      } else {
            reg16 = s3c_hsmmc_readw(HM_CLKCON);
            s3c_hsmmc_writew(reg16 | (0x1<<2), HM_CLKCON);

            while (1) {
                  reg16 = s3c_hsmmc_readw(HM_CLKCON);
                  if (reg16 & (0x1<<3))   /*  SD_CLKSRC is Stable */
                        break;
            }
      }
}

static void set_clock (u32 clksrc, u32 div)
{
      u16 reg16;
      u32 i;

      s3c_hsmmc_writel(0xC0004100 | (clksrc << 4), HM_CONTROL2);  // rx feedback control
      s3c_hsmmc_writel(0x00008080, HM_CONTROL3);                  // Low clock: 00008080
      s3c_hsmmc_writel(0x3 << 16, HM_CONTROL4);

      s3c_hsmmc_writew(s3c_hsmmc_readw(HM_CLKCON) & ~(0xff << 8), HM_CLKCON);

      /* SDCLK Value Setting + Internal Clock Enable */
      s3c_hsmmc_writew(((div<<8) | 0x1), HM_CLKCON);

      /* CheckInternalClockStable */
      for (i = 0; i < 0x10000; i++) {
            reg16 = s3c_hsmmc_readw(HM_CLKCON);
            if (reg16 & 0x2)
                  break;
      }
      if (i == 0x10000)
            puts("internal clock stabilization failed\n");

      hsmmc_clock_onoff(1);
}

static void set_cmd_register (ushort cmd, u32 data, u32 flags)
{
      ushort val = (cmd << 8);

      if (cmd == 12)
            val |= (3 << 6);

      if (flags & MMC_RSP_136)      /* Long RSP */
            val |= 0x01;
      else if (flags & MMC_RSP_BUSY)      /* R1B */
            val |= 0x03;
      else if (flags & MMC_RSP_PRESENT)   /* Normal RSP */
            val |= 0x02;

      if (flags & MMC_RSP_OPCODE)
            val |= (1<<4);

      if (flags & MMC_RSP_CRC)
            val |= (1<<3);

      if (data)
            val |= (1<<5);

//    puts("cmdreg = 0x");
//    print32(val);
//    puts("\n");
      s3c_hsmmc_writew(val, HM_CMDREG);
}

static int issue_command (ushort cmd, u32 arg, u32 data, u32 flags)
{
      int i;

/*    puts("### issue_command: ");
      printdec(cmd);
      puts(" 0x");
      print32(arg);
      puts(" ");
      printdec(data);
      puts(" 0x");
      print32(flags);
      puts("\n");
*/
      /* Check CommandInhibit_CMD */
      for (i = 0; i < 0x1000000; i++) {
            if (!(s3c_hsmmc_readl(HM_PRNSTS) & 0x1))
                  break;
      }
      if (i == 0x1000000) {
            puts("@@@@@@1 rHM_PRNSTS: ");
            printdec(s3c_hsmmc_readl(HM_PRNSTS));
            puts("\n");
      }

      /* Check CommandInhibit_DAT */
      if (flags & MMC_RSP_BUSY) {
            for (i = 0; i < 0x1000000; i++) {
                  if (!(s3c_hsmmc_readl(HM_PRNSTS) & 0x2))
                        break;
            }
            if (i == 0x1000000) {
                  puts("@@@@@@2 rHM_PRNSTS: ");
                  print32(s3c_hsmmc_readl(HM_PRNSTS));
                  puts("\n");
            }
      }

      s3c_hsmmc_writel(arg, HM_ARGUMENT);

      set_cmd_register(cmd, data, flags);

      if (wait_for_cmd_done())
            return 0;

      ClearCommandCompleteStatus();

      if (!(s3c_hsmmc_readw(HM_NORINTSTS) & 0x8000))
            return 1;

      puts("Command = ");
      printdec((s3c_hsmmc_readw(HM_CMDREG) >> 8));
      puts(", Error Stat = 0x");
      print32(s3c_hsmmc_readw(HM_ERRINTSTS));
      return 0;
}

static int check_card_status(void)
{
      if (!issue_command(MMC_SEND_STATUS, rca<<16, 0, MMC_RSP_R1))
            return 0;

      if (((s3c_hsmmc_readl(HM_RSPREG0) >> 9) & 0xf) == 4) {
//          puts("Card is transfer status\n");
            return 1;
      }

      return 1;
}

static void set_hostctl_speed (u8 mode)
{
      u8 reg8;

      reg8 = s3c_hsmmc_readb(HM_HOSTCTL) & ~(0x1<<2);
      s3c_hsmmc_writeb(reg8 | (mode<<2), HM_HOSTCTL);
}

/* return 0: OK
 * return -1: error
 */
static int set_bus_width (u32 width)
{
      u8 reg = s3c_hsmmc_readb(HM_HOSTCTL);
      u8 bitmode = 0;

      card_irq_enable(0);     // Disable sd card interrupt


      if (!issue_command(MMC_APP_CMD, rca<<16, 0, MMC_RSP_R1))
            return -1;
      else {
            if (width == 1) { // 1-bits
                  bitmode = 0;
                  if (!issue_command(MMC_SWITCH, 0, 0, MMC_RSP_R1B))
                        return -1;
            } else {    // 4-bits
                  bitmode = 1;
                  if (!issue_command(MMC_SWITCH, 2, 0, MMC_RSP_R1B))
                        return -1;
            }
      }

      if (bitmode == 2)
            reg |= 1 << 5;
      else
            reg |= bitmode << 1;

      s3c_hsmmc_writeb(reg, HM_HOSTCTL);
      card_irq_enable(1);
//    puts(" transfer rHM_HOSTCTL(0x28) = 0x");
//    print32(s3c_hsmmc_readb(HM_HOSTCTL));

      return 0;
}

static void clock_config (u32 Divisior)
{
      if (100000000 / (Divisior * 2) > 25000000)      // Higher than 25MHz, it is necessary to enable high speed mode of the host controller.
            set_hostctl_speed(HIGH);
      else
            set_hostctl_speed(NORMAL);

      hsmmc_clock_onoff(0);         // when change the sd clock frequency, need to stop sd clock.
      set_clock(SD_EPLL, Divisior);
}

static void check_dma_int (void)
{
      u32 i;

      for (i = 0; i < 0x10000000; i++) {
            if (s3c_hsmmc_readw(HM_NORINTSTS) & 0x0002) {
                  HS_DMA_END = 1;
                  s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTS) | 0x0002, HM_NORINTSTS);
                  return;
            }
            if (s3c_hsmmc_readw(HM_NORINTSTS) & 0x8000) {
                  puts("error found: ");
                  print32(s3c_hsmmc_readw(HM_ERRINTSTS));
                  return;
            }
      }

      puts("check_dma_int: timeout\n");
}


static void print_sd_cid(const struct sd_cid *cid)
{
      puts("    Card Type: ");
      switch (card_type) {
      case CARDTYPE_NONE:
            puts("(None) / ");
            break;
      case CARDTYPE_MMC:
            puts("MMC / ");
            break;
      case CARDTYPE_SD:
            puts("SD / ");
            break;
      case CARDTYPE_SD20:
            puts("SD 2.0 / ");
            break;
      case CARDTYPE_SDHC:
            puts("SD 2.0 SDHC / ");
            break;
      }

      puts("Mfr: 0x");
      print8(cid->mid);
      puts(", OEM \"");
      this_board->putc(cid->oid_0);
      this_board->putc(cid->oid_1);
      puts("\" / ");

      this_board->putc(cid->pnm_0);
      this_board->putc(cid->pnm_1);
      this_board->putc(cid->pnm_2);
      this_board->putc(cid->pnm_3);
      this_board->putc(cid->pnm_4);
      puts("\", rev ");
      printdec(cid->prv >> 4);
      puts(".");
      printdec(cid->prv & 15);
      puts(" / s/n: ");
      print32(cid->psn_0 << 24 | cid->psn_1 << 16 | cid->psn_2 << 8 |
          cid->psn_3);
      puts(" / date: ");
      printdec(cid->mdt_1 & 15);
      puts("/");
      printdec(2000 + ((cid->mdt_0 & 15) << 4)+((cid->mdt_1 & 0xf0) >> 4));
      puts("\n");
}

unsigned int s3c6410_mmc_init (int verbose)
{
      u32 reg;
      u32 width;
      int resp;
      int hcs;
      int retries = 50;
      u8 response[16];
      unsigned int r1[4];
      struct sd_cid *sd_cid = (struct sd_cid *)response;
      struct mmc_csd *csd = (struct mmc_csd *)response;
      u8 *p8 = (u8 *)&r1[0];
      unsigned int sd_sectors = 0;
      /* we need to shift result by 8 bits spread over 4 x 32-bit regs */
      u8 mangle[] = { 7, 0, 1, 2, 11, 4, 5, 6, 15, 8, 9, 10, 0, 12, 13, 14 };
      int n;

      hsmmc_set_gpio();

      hsmmc_reset();

      width = 4;

      HCLK = 33000000; /* FIXME */
      hsmmc_clock_onoff(0);

      reg = readl(SCLK_GATE);
      writel(reg | (1<<27), SCLK_GATE);

      set_clock(SD_EPLL, 0x80);
      s3c_hsmmc_writeb(0xe, HM_TIMEOUTCON);
      set_hostctl_speed(NORMAL);

      InterruptEnable(0xff, 0xff);

//    dbg("HM_NORINTSTS = %x\n", s3c_hsmmc_readw(HM_NORINTSTS));

      /* MMC_GO_IDLE_STATE */
      issue_command(MMC_GO_IDLE_STATE, 0x00, 0, 0);

      udelay(100000);
      udelay(100000);
      udelay(100000);
      udelay(100000);

      /* SDHC card? */

      resp = issue_command(SD_SEND_IF_COND, 0x000001aa,
            0, MMC_CMD_BCR | MMC_RSP_R7);
      if (resp && ((s3c_hsmmc_readl(HM_RSPREG0) & 0xff) == 0xaa)) {
            card_type = CARDTYPE_SD20; /* 2.0 SD, may not be SDHC */
            hcs = 0x40000000;
      }

      /* Well, either way let's say hello in SD card protocol */

      while (retries--) {

            udelay(100000);
            udelay(100000);
            udelay(100000);

            resp = issue_command(MMC_APP_CMD, 0x00000000, 0,
                  MMC_RSP_R1);
            if (!resp)
                  continue;
            resp = issue_command(SD_APP_OP_COND, hcs | 0x00300000, 0,
                  MMC_RSP_R3);
            if (!resp)
                  continue;

            if ((s3c_hsmmc_readl(HM_RSPREG0) >> 24) & (1 << 6)) { /* asserts block addressing */
                  retries = -2;
                  card_type = CARDTYPE_SDHC;
            }

            if ((s3c_hsmmc_readl(HM_RSPREG0) >> 24) & (1 << 7)) { /* not busy */
                  retries = -2;
                  if (card_type == CARDTYPE_NONE)
                        card_type = CARDTYPE_SD;
                  break;
            }
      }
      if (retries == -1) {
            puts("no response\n");
            return -2;
      }

      if (!issue_command(MMC_ALL_SEND_CID, 0, 0, MMC_RSP_R2)) {
            puts("CID broken\n");
            return -3;
      }

      r1[0] = s3c_hsmmc_readl(HM_RSPREG3);
      r1[1] = s3c_hsmmc_readl(HM_RSPREG2);
      r1[2] = s3c_hsmmc_readl(HM_RSPREG1);
      r1[3] = s3c_hsmmc_readl(HM_RSPREG0);

      for (n = 0; n < 16; n++)
            response[n] = p8[mangle[n]];

      switch (card_type) {
      case CARDTYPE_SD:
      case CARDTYPE_SD20:
      case CARDTYPE_SDHC:

            if (verbose)
                  print_sd_cid(sd_cid);
            resp = issue_command(SD_SEND_RELATIVE_ADDR, MMC_DEFAULT_RCA,
                        0, MMC_RSP_R6);
            rca = s3c_hsmmc_readl(HM_RSPREG0) >> 16;
            break;

      default:
            return 1;
      }

      /* grab the CSD */

      resp = issue_command(MMC_SEND_CSD, rca << 16, 0, MMC_RSP_R2);
      if (resp) {

            r1[0] = s3c_hsmmc_readl(HM_RSPREG3);
            r1[1] = s3c_hsmmc_readl(HM_RSPREG2);
            r1[2] = s3c_hsmmc_readl(HM_RSPREG1);
            r1[3] = s3c_hsmmc_readl(HM_RSPREG0);
            for (n = 0; n < 16; n++)
                  response[n] = p8[mangle[n]];

            switch (card_type) {
            case CARDTYPE_SDHC:
                  puts("    SDHC size: ");
                  sd_sectors = (UNSTUFF_BITS(((u32 *)&response[0]), 48, 22)
                                                    + 1) << 10;
                  break;
            default:
                  puts("  MMC/SD size: ");
                  sd_sectors = ((((unsigned long)1 << csd->c_size_mult1) *
                              (unsigned long)(csd->c_size)) >> 9);
            }
            printdec(sd_sectors / 2048);
            puts(" MiB\n");
      } else
            puts("CSD grab broken\n");

      resp = issue_command(MMC_SELECT_CARD, rca<<16, 0, MMC_RSP_R1);
      if (!resp)
            return 1;

      /* Operating Clock setting */
      clock_config(2);  // Divisor 1 = Base clk /2      ,Divisor 2 = Base clk /4, Divisor 4 = Base clk /8 ...

      while (set_bus_width(width));
      while (!check_card_status());

      /* MMC_SET_BLOCKLEN */
      while (!issue_command(MMC_SET_BLOCKLEN, 512, 0, MMC_RSP_R1));

      s3c_hsmmc_writew(0xffff, HM_NORINTSTS);

      return sd_sectors;
}

unsigned long s3c6410_mmc_bread(int dev_num, unsigned long start_blk, unsigned long blknum,
                                                      void *dst)
{
      u32 blksize; //j, , Addr_temp = start_blk;
      u32 dma = 0, cmd, multi; //, TotalReadByte, read_blk_cnt = 0;

      HS_DMA_END = 0;

      blksize = Card_OneBlockSize_ver1;

      while (!check_card_status());

      s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTSEN) & ~(DMA_STS_INT_EN | BLOCKGAP_EVENT_STS_INT_EN), HM_NORINTSTSEN);
      s3c_hsmmc_writew((HM_NORINTSIGEN & ~(0xffff)) | TRANSFERCOMPLETE_SIG_INT_EN, HM_NORINTSIGEN);

      SetSystemAddressReg((unsigned long)dst);  // AHB System Address For Write
      dma = 1;

      set_blksize_register(7, 512); // Maximum DMA Buffer Size, Block Size
      set_blkcnt_register(blknum);  // Block Numbers to Write

      if (movi_hc)
            set_arg_register(start_blk);        // Card Start Block Address to Write
      else
            set_arg_register(start_blk * 512);  // Card Start Block Address to Write

      cmd = (blknum > 1) ? 18 : 17;
      multi = (blknum > 1);

      set_transfer_mode_register(multi, 1, multi, 1, dma);
      set_cmd_register(cmd, 1, MMC_RSP_R1);

      if (wait_for_cmd_done()) {
            puts("Command NOT Complete\n");
            return -1;
      } else
            ClearCommandCompleteStatus();


      check_dma_int();
      while (!HS_DMA_END);

      HS_DMA_END = 0;

      return blknum;
}

Generated by  Doxygen 1.6.0   Back to index