OpenCores
URL https://opencores.org/ocsvn/sd_card_controller/sd_card_controller/trunk

Subversion Repositories sd_card_controller

[/] [sd_card_controller/] [trunk/] [sw/] [example/] [src/] [ocsdc.c] - Diff between revs 3 and 8

Only display areas with differences | Details | Blame | View Log

Rev 3 Rev 8
/*
/*
 * WISHBONE SD Card Controller IP Core
 * WISHBONE SD Card Controller IP Core
 *
 *
 * ocsdc.c
 * ocsdc.c
 *
 *
 * This file is part of the WISHBONE SD Card
 * This file is part of the WISHBONE SD Card
 * Controller IP Core project
 * Controller IP Core project
 * http://www.opencores.org/cores/xxx/
 * http://opencores.org/project,sd_card_controller
 *
 *
 * Description
 * Description
 * Driver for the WISHBONE SD Card Controller IP Core.
 * Driver for the WISHBONE SD Card Controller IP Core.
 *
 *
 * Author(s):
 * Author(s):
 *     - Marek Czerski, ma.czerski@gmail.com
 *     - Marek Czerski, ma.czerski@gmail.com
 */
 */
/*
/*
 *
 *
 * Copyright (C) 2013 Authors
 * Copyright (C) 2013 Authors
 *
 *
 * This source file may be used and distributed without
 * This source file may be used and distributed without
 * restriction provided that this copyright statement is not
 * restriction provided that this copyright statement is not
 * removed from the file and that any derivative work contains
 * removed from the file and that any derivative work contains
 * the original copyright notice and the associated disclaimer.
 * the original copyright notice and the associated disclaimer.
 *
 *
 * This source file is free software; you can redistribute it
 * This source file is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General
 * and/or modify it under the terms of the GNU Lesser General
 * Public License as published by the Free Software Foundation;
 * Public License as published by the Free Software Foundation;
 * either version 2.1 of the License, or (at your option) any
 * either version 2.1 of the License, or (at your option) any
 * later version.
 * later version.
 *
 *
 * This source is distributed in the hope that it will be
 * This source is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied
 * useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * details.
 *
 *
 * You should have received a copy of the GNU Lesser General
 * You should have received a copy of the GNU Lesser General
 * Public License along with this source; if not, download it
 * Public License along with this source; if not, download it
 * from http://www.opencores.org/lgpl.shtml
 * from http://www.opencores.org/lgpl.shtml
 */
 */
 
 
#include "mmc.h"
#include "mmc.h"
#include <malloc.h>
#include <malloc.h>
#include <stdint.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <or1k-support.h>
#include <or1k-support.h>
 
 
// Register space
// Register space
#define OCSDC_ARGUMENT           0x00
#define OCSDC_ARGUMENT           0x00
#define OCSDC_COMMAND            0x04
#define OCSDC_COMMAND            0x04
#define OCSDC_RESPONSE_1         0x08
#define OCSDC_RESPONSE_1         0x08
#define OCSDC_RESPONSE_2         0x0c
#define OCSDC_RESPONSE_2         0x0c
#define OCSDC_RESPONSE_3         0x10
#define OCSDC_RESPONSE_3         0x10
#define OCSDC_RESPONSE_4         0x14
#define OCSDC_RESPONSE_4         0x14
#define OCSDC_CONTROL                    0x1C
#define OCSDC_CONTROL                    0x1C
#define OCSDC_TIMEOUT            0x20
#define OCSDC_TIMEOUT            0x20
#define OCSDC_CLOCK_DIVIDER      0x24
#define OCSDC_CLOCK_DIVIDER      0x24
#define OCSDC_SOFTWARE_RESET     0x28
#define OCSDC_SOFTWARE_RESET     0x28
#define OCSDC_POWER_CONTROL      0x2C
#define OCSDC_POWER_CONTROL      0x2C
#define OCSDC_CAPABILITY         0x30
#define OCSDC_CAPABILITY         0x30
#define OCSDC_CMD_INT_STATUS     0x34
#define OCSDC_CMD_INT_STATUS     0x34
#define OCSDC_CMD_INT_ENABLE     0x38
#define OCSDC_CMD_INT_ENABLE     0x38
#define OCSDC_DAT_INT_STATUS     0x3C
#define OCSDC_DAT_INT_STATUS     0x3C
#define OCSDC_DAT_INT_ENABLE     0x40
#define OCSDC_DAT_INT_ENABLE     0x40
#define OCSDC_BLOCK_SIZE         0x44
#define OCSDC_BLOCK_SIZE         0x44
#define OCSDC_BLOCK_COUNT        0x48
#define OCSDC_BLOCK_COUNT        0x48
#define OCSDC_DST_SRC_ADDR       0x60
#define OCSDC_DST_SRC_ADDR       0x60
 
 
// OCSDC_CMD_INT_STATUS bits
// OCSDC_CMD_INT_STATUS bits
#define OCSDC_CMD_INT_STATUS_CC   0x0001
#define OCSDC_CMD_INT_STATUS_CC   0x0001
#define OCSDC_CMD_INT_STATUS_EI   0x0002
#define OCSDC_CMD_INT_STATUS_EI   0x0002
#define OCSDC_CMD_INT_STATUS_CTE  0x0004
#define OCSDC_CMD_INT_STATUS_CTE  0x0004
#define OCSDC_CMD_INT_STATUS_CCRC 0x0008
#define OCSDC_CMD_INT_STATUS_CCRC 0x0008
#define OCSDC_CMD_INT_STATUS_CIE  0x0010
#define OCSDC_CMD_INT_STATUS_CIE  0x0010
 
 
// SDCMSC_DAT_INT_STATUS
// SDCMSC_DAT_INT_STATUS
#define SDCMSC_DAT_INT_STATUS_TRS 0x01
#define SDCMSC_DAT_INT_STATUS_TRS 0x01
#define SDCMSC_DAT_INT_STATUS_CRC 0x02
#define SDCMSC_DAT_INT_STATUS_CRC 0x02
#define SDCMSC_DAT_INT_STATUS_OV  0x04
#define SDCMSC_DAT_INT_STATUS_OV  0x04
 
 
struct ocsdc {
struct ocsdc {
        int iobase;
        int iobase;
        int clk_freq;
        int clk_freq;
};
};
 
 
#define readl(addr) (*(volatile unsigned int *) (addr))
#define readl(addr) (*(volatile unsigned int *) (addr))
#define writel(b, addr) ((*(volatile unsigned int *) (addr)) = (b))
#define writel(b, addr) ((*(volatile unsigned int *) (addr)) = (b))
 
 
void flush_dcache_range(void * start, void * end) {
void flush_dcache_range(void * start, void * end) {
        while (start < end) {
        while (start < end) {
                or1k_dcache_flush((unsigned long)start);
                or1k_dcache_flush((unsigned long)start);
                start += 4;
                start += 4;
        }
        }
}
}
 
 
static inline uint32_t ocsdc_read(struct ocsdc *dev, int offset)
static inline uint32_t ocsdc_read(struct ocsdc *dev, int offset)
{
{
        return readl(dev->iobase + offset);
        return readl(dev->iobase + offset);
}
}
 
 
static inline void ocsdc_write(struct ocsdc *dev, int offset, uint32_t data)
static inline void ocsdc_write(struct ocsdc *dev, int offset, uint32_t data)
{
{
        writel(data, dev->iobase + offset);
        writel(data, dev->iobase + offset);
}
}
 
 
static void ocsdc_set_buswidth(struct ocsdc * dev, uint width) {
static void ocsdc_set_buswidth(struct ocsdc * dev, uint width) {
        if (width == 4)
        if (width == 4)
                ocsdc_write(dev, OCSDC_CONTROL, 1);
                ocsdc_write(dev, OCSDC_CONTROL, 1);
        else if (width == 1)
        else if (width == 1)
                ocsdc_write(dev, OCSDC_CONTROL, 0);
                ocsdc_write(dev, OCSDC_CONTROL, 0);
}
}
 
 
/* Set clock prescalar value based on the required clock in HZ */
/* Set clock prescalar value based on the required clock in HZ */
static void ocsdc_set_clock(struct ocsdc * dev, uint clock)
static void ocsdc_set_clock(struct ocsdc * dev, uint clock)
{
{
        int clk_div = dev->clk_freq / (2.0 * clock) - 1;
        int clk_div = dev->clk_freq / (2.0 * clock) - 1;
 
 
        printf("ocsdc_set_clock %d, div %d\n\r", clock, clk_div);
        printf("ocsdc_set_clock %d, div %d\n\r", clock, clk_div);
        //software reset
        //software reset
        ocsdc_write(dev, OCSDC_SOFTWARE_RESET, 1);
        ocsdc_write(dev, OCSDC_SOFTWARE_RESET, 1);
        //set clock devider
        //set clock devider
        ocsdc_write(dev, OCSDC_CLOCK_DIVIDER, clk_div);
        ocsdc_write(dev, OCSDC_CLOCK_DIVIDER, clk_div);
        //clear software reset
        //clear software reset
        ocsdc_write(dev, OCSDC_SOFTWARE_RESET, 0);
        ocsdc_write(dev, OCSDC_SOFTWARE_RESET, 0);
}
}
 
 
static int ocsdc_finish(struct ocsdc * dev, struct mmc_cmd *cmd) {
static int ocsdc_finish(struct ocsdc * dev, struct mmc_cmd *cmd) {
 
 
        int retval = 0;
        int retval = 0;
        while (1) {
        while (1) {
                int r2 = ocsdc_read(dev, OCSDC_CMD_INT_STATUS);
                int r2 = ocsdc_read(dev, OCSDC_CMD_INT_STATUS);
                //printf("ocsdc_finish: cmd %d, status %x\n", cmd->cmdidx, r2);
                //printf("ocsdc_finish: cmd %d, status %x\n", cmd->cmdidx, r2);
                if (r2 & OCSDC_CMD_INT_STATUS_EI) {
                if (r2 & OCSDC_CMD_INT_STATUS_EI) {
                        //clear interrupts
                        //clear interrupts
                        ocsdc_write(dev, OCSDC_CMD_INT_STATUS, 0);
                        ocsdc_write(dev, OCSDC_CMD_INT_STATUS, 0);
                        printf("ocsdc_finish: cmd %d, status %x\n\r", cmd->cmdidx, r2);
                        printf("ocsdc_finish: cmd %d, status %x\n\r", cmd->cmdidx, r2);
                        retval = -1;
                        retval = -1;
                        break;
                        break;
                }
                }
                else if (r2 & OCSDC_CMD_INT_STATUS_CC) {
                else if (r2 & OCSDC_CMD_INT_STATUS_CC) {
                        //clear interrupts
                        //clear interrupts
                        ocsdc_write(dev, OCSDC_CMD_INT_STATUS, 0);
                        ocsdc_write(dev, OCSDC_CMD_INT_STATUS, 0);
                        //get response
                        //get response
                        cmd->response[0] = ocsdc_read(dev, OCSDC_RESPONSE_1);
                        cmd->response[0] = ocsdc_read(dev, OCSDC_RESPONSE_1);
                        if (cmd->resp_type & MMC_RSP_136) {
                        if (cmd->resp_type & MMC_RSP_136) {
                                cmd->response[1] = ocsdc_read(dev, OCSDC_RESPONSE_2);
                                cmd->response[1] = ocsdc_read(dev, OCSDC_RESPONSE_2);
                                cmd->response[2] = ocsdc_read(dev, OCSDC_RESPONSE_3);
                                cmd->response[2] = ocsdc_read(dev, OCSDC_RESPONSE_3);
                                cmd->response[3] = ocsdc_read(dev, OCSDC_RESPONSE_4);
                                cmd->response[3] = ocsdc_read(dev, OCSDC_RESPONSE_4);
                        }
                        }
                        printf("ocsdc_finish:  %d ok\n\r", cmd->cmdidx);
                        printf("ocsdc_finish:  %d ok\n\r", cmd->cmdidx);
                        retval = 0;
                        retval = 0;
 
 
                        break;
                        break;
                }
                }
                //else if (!(r2 & OCSDC_CMD_INT_STATUS_CIE)) {
                //else if (!(r2 & OCSDC_CMD_INT_STATUS_CIE)) {
                //      printf("ocsdc_finish: cmd %d no exec %x\n", cmd->cmdidx, r2);
                //      printf("ocsdc_finish: cmd %d no exec %x\n", cmd->cmdidx, r2);
                //}
                //}
        }
        }
        return retval;
        return retval;
}
}
 
 
static int ocsdc_data_finish(struct ocsdc * dev) {
static int ocsdc_data_finish(struct ocsdc * dev) {
        int status;
        int status;
 
 
    while ((status = ocsdc_read(dev, OCSDC_DAT_INT_STATUS)) == 0);
    while ((status = ocsdc_read(dev, OCSDC_DAT_INT_STATUS)) == 0);
    ocsdc_write(dev, OCSDC_DAT_INT_STATUS, 0);
    ocsdc_write(dev, OCSDC_DAT_INT_STATUS, 0);
 
 
    if (status & SDCMSC_DAT_INT_STATUS_TRS) {
    if (status & SDCMSC_DAT_INT_STATUS_TRS) {
        printf("ocsdc_data_finish: ok\n\r");
        printf("ocsdc_data_finish: ok\n\r");
        return 0;
        return 0;
    }
    }
    else {
    else {
        printf("ocsdc_data_finish: status %x\n\r", status);
        printf("ocsdc_data_finish: status %x\n\r", status);
        return -1;
        return -1;
    }
    }
}
}
 
 
static void ocsdc_setup_data_xfer(struct ocsdc * dev, struct mmc_cmd *cmd, struct mmc_data *data) {
static void ocsdc_setup_data_xfer(struct ocsdc * dev, struct mmc_cmd *cmd, struct mmc_data *data) {
 
 
        //invalidate cache
        //invalidate cache
        if (data->flags & MMC_DATA_READ) {
        if (data->flags & MMC_DATA_READ) {
                flush_dcache_range(data->dest, data->dest+data->blocksize*data->blocks);
                flush_dcache_range(data->dest, data->dest+data->blocksize*data->blocks);
                ocsdc_write(dev, OCSDC_DST_SRC_ADDR, (uint32_t)data->dest);
                ocsdc_write(dev, OCSDC_DST_SRC_ADDR, (uint32_t)data->dest);
        }
        }
        else {
        else {
                flush_dcache_range((void *)data->src, (void *)data->src+data->blocksize*data->blocks);
                flush_dcache_range((void *)data->src, (void *)data->src+data->blocksize*data->blocks);
                ocsdc_write(dev, OCSDC_DST_SRC_ADDR, (uint32_t)data->src);
                ocsdc_write(dev, OCSDC_DST_SRC_ADDR, (uint32_t)data->src);
        }
        }
        ocsdc_write(dev, OCSDC_BLOCK_SIZE, data->blocksize);
        ocsdc_write(dev, OCSDC_BLOCK_SIZE, data->blocksize);
        ocsdc_write(dev, OCSDC_BLOCK_COUNT, data->blocks-1);
        ocsdc_write(dev, OCSDC_BLOCK_COUNT, data->blocks-1);
 
 
        //printf("ocsdc_setup_read: addr: %x\n", (uint32_t)data->dest);
        //printf("ocsdc_setup_read: addr: %x\n", (uint32_t)data->dest);
 
 
}
}
 
 
static int ocsdc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
static int ocsdc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
{
        struct ocsdc * dev = mmc->priv;
        struct ocsdc * dev = mmc->priv;
 
 
        int command = (cmd->cmdidx << 8);
        int command = (cmd->cmdidx << 8);
        if (cmd->resp_type & MMC_RSP_PRESENT) {
        if (cmd->resp_type & MMC_RSP_PRESENT) {
                if (cmd->resp_type & MMC_RSP_136)
                if (cmd->resp_type & MMC_RSP_136)
                        command |= 2;
                        command |= 2;
                else {
                else {
                        command |= 1;
                        command |= 1;
                }
                }
        }
        }
        if (cmd->resp_type & MMC_RSP_BUSY)
        if (cmd->resp_type & MMC_RSP_BUSY)
                command |= (1 << 2);
                command |= (1 << 2);
        if (cmd->resp_type & MMC_RSP_CRC)
        if (cmd->resp_type & MMC_RSP_CRC)
                command |= (1 << 3);
                command |= (1 << 3);
        if (cmd->resp_type & MMC_RSP_OPCODE)
        if (cmd->resp_type & MMC_RSP_OPCODE)
                command |= (1 << 4);
                command |= (1 << 4);
 
 
        if (data && ((data->flags & MMC_DATA_READ) || ((data->flags & MMC_DATA_WRITE))) && data->blocks) {
        if (data && ((data->flags & MMC_DATA_READ) || ((data->flags & MMC_DATA_WRITE))) && data->blocks) {
                if (data->flags & MMC_DATA_READ)
                if (data->flags & MMC_DATA_READ)
                        command |= (1 << 5);
                        command |= (1 << 5);
                if (data->flags & MMC_DATA_WRITE)
                if (data->flags & MMC_DATA_WRITE)
                        command |= (1 << 6);
                        command |= (1 << 6);
                ocsdc_setup_data_xfer(dev, cmd, data);
                ocsdc_setup_data_xfer(dev, cmd, data);
        }
        }
 
 
        printf("ocsdc_send_cmd %04x\n\r", command);
        printf("ocsdc_send_cmd %04x\n\r", command);
 
 
//      getc();
//      getc();
 
 
        ocsdc_write(dev, OCSDC_COMMAND, command);
        ocsdc_write(dev, OCSDC_COMMAND, command);
        ocsdc_write(dev, OCSDC_ARGUMENT, cmd->cmdarg);
        ocsdc_write(dev, OCSDC_ARGUMENT, cmd->cmdarg);
 
 
        if (ocsdc_finish(dev, cmd) < 0) return -1;
        if (ocsdc_finish(dev, cmd) < 0) return -1;
        if (data && data->blocks) return ocsdc_data_finish(dev);
        if (data && data->blocks) return ocsdc_data_finish(dev);
        else return 0;
        else return 0;
}
}
 
 
/* Initialize ocsdc controller */
/* Initialize ocsdc controller */
static int ocsdc_init(struct mmc *mmc)
static int ocsdc_init(struct mmc *mmc)
{
{
        struct ocsdc * dev = mmc->priv;
        struct ocsdc * dev = mmc->priv;
 
 
        //set timeout
        //set timeout
        ocsdc_write(dev, OCSDC_TIMEOUT, 0x7FFF);
        ocsdc_write(dev, OCSDC_TIMEOUT, 0x7FFF);
        //disable all interrupts
        //disable all interrupts
        ocsdc_write(dev, OCSDC_CMD_INT_ENABLE, 0);
        ocsdc_write(dev, OCSDC_CMD_INT_ENABLE, 0);
        ocsdc_write(dev, OCSDC_DAT_INT_ENABLE, 0);
        ocsdc_write(dev, OCSDC_DAT_INT_ENABLE, 0);
        //clear all interrupts
        //clear all interrupts
        ocsdc_write(dev, OCSDC_CMD_INT_STATUS, 0);
        ocsdc_write(dev, OCSDC_CMD_INT_STATUS, 0);
        ocsdc_write(dev, OCSDC_DAT_INT_STATUS, 0);
        ocsdc_write(dev, OCSDC_DAT_INT_STATUS, 0);
        //set clock to maximum (devide by 2)
        //set clock to maximum (devide by 2)
        ocsdc_set_clock(dev, dev->clk_freq/2);
        ocsdc_set_clock(dev, dev->clk_freq/2);
 
 
        return 0;
        return 0;
}
}
 
 
static void ocsdc_set_ios(struct mmc *mmc)
static void ocsdc_set_ios(struct mmc *mmc)
{
{
        /* Support only 4 bit if */
        /* Support only 4 bit if */
        ocsdc_set_buswidth(mmc->priv, mmc->bus_width);
        ocsdc_set_buswidth(mmc->priv, mmc->bus_width);
 
 
        /* Set clock speed */
        /* Set clock speed */
        if (mmc->clock)
        if (mmc->clock)
                ocsdc_set_clock(mmc->priv, mmc->clock);
                ocsdc_set_clock(mmc->priv, mmc->clock);
}
}
 
 
struct mmc * ocsdc_mmc_init(int base_addr, int clk_freq)
struct mmc * ocsdc_mmc_init(int base_addr, int clk_freq)
{
{
        struct mmc *mmc;
        struct mmc *mmc;
        struct ocsdc *priv;
        struct ocsdc *priv;
 
 
        mmc = malloc(sizeof(struct mmc));
        mmc = malloc(sizeof(struct mmc));
        if (!mmc) goto MMC_ALLOC;
        if (!mmc) goto MMC_ALLOC;
        priv = malloc(sizeof(struct ocsdc));
        priv = malloc(sizeof(struct ocsdc));
        if (!priv) goto OCSDC_ALLOC;
        if (!priv) goto OCSDC_ALLOC;
 
 
        memset(mmc, 0, sizeof(struct mmc));
        memset(mmc, 0, sizeof(struct mmc));
        memset(priv, 0, sizeof(struct ocsdc));
        memset(priv, 0, sizeof(struct ocsdc));
 
 
        priv->iobase = base_addr;
        priv->iobase = base_addr;
        priv->clk_freq = clk_freq;
        priv->clk_freq = clk_freq;
 
 
        sprintf(mmc->name, "ocsdc");
        sprintf(mmc->name, "ocsdc");
        mmc->priv = priv;
        mmc->priv = priv;
        mmc->send_cmd = ocsdc_send_cmd;
        mmc->send_cmd = ocsdc_send_cmd;
        mmc->set_ios = ocsdc_set_ios;
        mmc->set_ios = ocsdc_set_ios;
        mmc->init = ocsdc_init;
        mmc->init = ocsdc_init;
        mmc->getcd = NULL;
        mmc->getcd = NULL;
 
 
        mmc->f_min = priv->clk_freq/6; /*maximum clock division 64 */
        mmc->f_min = priv->clk_freq/6; /*maximum clock division 64 */
        mmc->f_max = priv->clk_freq/2; /*minimum clock division 2 */
        mmc->f_max = priv->clk_freq/2; /*minimum clock division 2 */
        mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
        mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
        mmc->host_caps = MMC_MODE_4BIT;//MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT;
        mmc->host_caps = MMC_MODE_4BIT;//MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT;
 
 
        mmc->b_max = 256;
        mmc->b_max = 256;
 
 
        return mmc;
        return mmc;
 
 
OCSDC_ALLOC:
OCSDC_ALLOC:
        free(mmc);
        free(mmc);
MMC_ALLOC:
MMC_ALLOC:
        return NULL;
        return NULL;
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.