mirror of
https://git.codelinaro.org/clo/qsdk/oss/boot/u-boot-2016.git
synced 2025-12-10 07:44:53 +01:00
ipq807x: mmc: Added eMMC driver files
Change-Id: Icf1683ebe1c7e99c336d884983215d7b416c988b Signed-off-by: Vasudevan Murugesan <vmuruges@codeaurora.org>
This commit is contained in:
parent
df62dea585
commit
a07af4aa36
2 changed files with 439 additions and 0 deletions
321
drivers/mmc/qca_mmc.c
Normal file
321
drivers/mmc/qca_mmc.c
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* Copyright (c) 2014 The Linux Foundation. All rights reserved.
|
||||
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <mmc.h>
|
||||
#include <part.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch-qcom-common/clk.h>
|
||||
#include <asm-generic/errno.h>
|
||||
#include "../../board/qcom/common/qca_common.h"
|
||||
#include "qca_mmc.h"
|
||||
|
||||
static inline void qca_reg_wr_delay(qca_mmc *host)
|
||||
{
|
||||
while (readl(host->base + MCI_STATUS2) & MCI_MCLK_REG_WR_ACTIVE);
|
||||
}
|
||||
|
||||
static inline void
|
||||
qca_start_command_exec(qca_mmc *host, struct mmc_cmd *cmd)
|
||||
{
|
||||
unsigned int c = 0;
|
||||
unsigned int arg = cmd->cmdarg;
|
||||
|
||||
writel(MCI_CLEAR_STATIC_MASK, host->base + MMCICLEAR);
|
||||
|
||||
qca_reg_wr_delay(host);
|
||||
|
||||
c |= (cmd->cmdidx | MCI_CPSM_ENABLE);
|
||||
|
||||
if (cmd->resp_type & MMC_RSP_PRESENT) {
|
||||
if (cmd->resp_type & MMC_RSP_136)
|
||||
c |= MCI_CPSM_LONGRSP;
|
||||
c |= MCI_CPSM_RESPONSE;
|
||||
}
|
||||
|
||||
if ((cmd->resp_type & MMC_RSP_R1b) == MMC_RSP_R1b) {
|
||||
c |= MCI_CPSM_PROGENA;
|
||||
}
|
||||
|
||||
writel(arg, host->base + MMCIARGUMENT);
|
||||
writel(c, host->base + MMCICOMMAND);
|
||||
qca_reg_wr_delay(host);
|
||||
}
|
||||
|
||||
static int
|
||||
qca_start_command(qca_mmc *host, struct mmc_cmd *cmd)
|
||||
{
|
||||
unsigned int status = 0;
|
||||
int rc = 0;
|
||||
|
||||
qca_start_command_exec(host, cmd);
|
||||
|
||||
while (readl(host->base + MMCISTATUS) & MCI_CMDACTIVE);
|
||||
|
||||
status = readl(host->base + MMCISTATUS);
|
||||
|
||||
if ((cmd->resp_type != MMC_RSP_NONE)) {
|
||||
|
||||
if (status & MCI_CMDTIMEOUT) {
|
||||
rc = TIMEOUT;
|
||||
}
|
||||
/* MMC_CMD_SEND_OP_COND response doesn't have CRC. */
|
||||
if ((status & MCI_CMDCRCFAIL) && (cmd->cmdidx != MMC_CMD_SEND_OP_COND)) {
|
||||
rc = UNUSABLE_ERR;
|
||||
}
|
||||
cmd->response[0] = readl(host->base + MMCIRESPONSE0);
|
||||
/*
|
||||
* Read rest of the response registers only if
|
||||
* long response is expected for this command
|
||||
*/
|
||||
if (cmd->resp_type & MMC_RSP_136) {
|
||||
cmd->response[1] = readl(host->base + MMCIRESPONSE1);
|
||||
cmd->response[2] = readl(host->base + MMCIRESPONSE2);
|
||||
cmd->response[3] = readl(host->base + MMCIRESPONSE3);
|
||||
}
|
||||
}
|
||||
|
||||
writel(MCI_CLEAR_STATIC_MASK, host->base + MMCICLEAR);
|
||||
qca_reg_wr_delay(host);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qca_pio_read(qca_mmc *host, char *buffer, unsigned int remain)
|
||||
{
|
||||
unsigned int *ptr = (unsigned int *) buffer;
|
||||
unsigned int status = 0;
|
||||
unsigned int count = 0;
|
||||
unsigned int error = MCI_DATACRCFAIL | MCI_DATATIMEOUT | MCI_RXOVERRUN;
|
||||
unsigned int i;
|
||||
|
||||
|
||||
do {
|
||||
status = readl(host->base + MMCISTATUS);
|
||||
udelay(1);
|
||||
|
||||
if (status & error) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (status & MCI_RXDATAAVLBL ) {
|
||||
unsigned rd_cnt = 1;
|
||||
|
||||
if (status & MCI_RXFIFOHALFFULL) {
|
||||
rd_cnt = MCI_HFIFO_COUNT;
|
||||
}
|
||||
|
||||
for (i = 0; i < rd_cnt; i++) {
|
||||
*ptr = readl(host->base + MMCIFIFO +
|
||||
(count % MCI_FIFOSIZE));
|
||||
ptr++;
|
||||
count += sizeof(unsigned int);
|
||||
}
|
||||
|
||||
if (count == remain)
|
||||
break;
|
||||
|
||||
} else if (status & MCI_DATAEND) {
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int
|
||||
qca_pio_write(qca_mmc *host, const char *buffer,
|
||||
unsigned int remain)
|
||||
{
|
||||
unsigned int *ptr = (unsigned int *) buffer;
|
||||
unsigned int status = 0;
|
||||
unsigned int count = 0;
|
||||
unsigned int error = MCI_DATACRCFAIL | MCI_DATATIMEOUT | MCI_TXUNDERRUN;
|
||||
unsigned int bcnt = 0;
|
||||
unsigned int sz = 0;
|
||||
int i;
|
||||
|
||||
do {
|
||||
status = readl(host->base + MMCISTATUS);
|
||||
|
||||
bcnt = remain - count;
|
||||
|
||||
if (!bcnt)
|
||||
break;
|
||||
|
||||
if ((status & MCI_TXFIFOEMPTY) ||
|
||||
(status & MCI_TXFIFOHALFEMPTY)) {
|
||||
|
||||
sz = ((bcnt >> 2) > MCI_HFIFO_COUNT) \
|
||||
? MCI_HFIFO_COUNT : (bcnt >> 2);
|
||||
|
||||
for (i = 0; i < sz; i++) {
|
||||
writel(*ptr, host->base + MMCIFIFO);
|
||||
ptr++;
|
||||
count += sizeof(unsigned int);
|
||||
}
|
||||
}
|
||||
} while (1);
|
||||
|
||||
do {
|
||||
status = readl(host->base + MMCISTATUS);
|
||||
|
||||
if (status & error) {
|
||||
count = status;
|
||||
break;
|
||||
}
|
||||
} while (!(status & MCI_DATAEND));
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int
|
||||
qca_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
|
||||
{
|
||||
unsigned int datactrl = 0;
|
||||
qca_mmc *host = mmc->priv;
|
||||
unsigned int xfer_size ;
|
||||
int status = 0;
|
||||
|
||||
if (data) {
|
||||
writel((readl(host->base + MMCICLOCK) | MCI_CLK_FLOWENA),
|
||||
host->base + MMCICLOCK);
|
||||
xfer_size = data->blocksize * data->blocks;
|
||||
datactrl = MCI_DPSM_ENABLE | (data->blocksize << 4);
|
||||
writel(0xffffffff, host->base + MMCIDATATIMER);
|
||||
|
||||
if (data->flags & MMC_DATA_READ)
|
||||
datactrl |= (MCI_DPSM_DIRECTION | MCI_RX_DATA_PEND);
|
||||
|
||||
writel(xfer_size, host->base + MMCIDATALENGTH);
|
||||
writel(datactrl, host->base + MMCIDATACTRL);
|
||||
qca_reg_wr_delay(host);
|
||||
|
||||
status = qca_start_command(host, cmd);
|
||||
|
||||
if (data->flags & MMC_DATA_READ) {
|
||||
qca_pio_read(host, data->dest, xfer_size);
|
||||
} else {
|
||||
qca_pio_write(host, data->src, xfer_size);
|
||||
}
|
||||
writel(0, host->base + MMCIDATACTRL);
|
||||
qca_reg_wr_delay(host);
|
||||
} else if (cmd) {
|
||||
status = qca_start_command(host, cmd);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void qca_set_ios(struct mmc *mmc)
|
||||
{
|
||||
qca_mmc *host = mmc->priv;
|
||||
u32 clk = 0, pwr = 0;
|
||||
int mode;
|
||||
|
||||
if (mmc->clock <= mmc->f_min) {
|
||||
mode = MMC_IDENTIFY_MODE;
|
||||
} else {
|
||||
mode = MMC_DATA_TRANSFER_MODE;
|
||||
}
|
||||
|
||||
if (mode != host->clk_mode) {
|
||||
host->clk_mode = mode;
|
||||
emmc_clock_config(host->clk_mode);
|
||||
}
|
||||
|
||||
pwr = MCI_PWR_UP | MCI_PWR_ON;
|
||||
|
||||
writel(pwr, host->base + MMCIPOWER);
|
||||
qca_reg_wr_delay(host);
|
||||
clk = readl(host->base + MMCICLOCK);
|
||||
|
||||
clk |= MCI_CLK_ENABLE;
|
||||
clk |= MCI_CLK_SELECTIN;
|
||||
clk |= MCI_CLK_FLOWENA;
|
||||
/* feedback clock */
|
||||
clk |= (2 << 14);
|
||||
|
||||
if (mmc->bus_width == 1) {
|
||||
clk |= MCI_CLK_WIDEBUS_1;
|
||||
} else if (mmc->bus_width == 4) {
|
||||
clk |= MCI_CLK_WIDEBUS_4;
|
||||
} else if (mmc->bus_width == 8) {
|
||||
clk |= MCI_CLK_WIDEBUS_8;
|
||||
}
|
||||
|
||||
/* Select free running MCLK as input clock of cm_dll_sdc4 */
|
||||
clk |= (2 << 23);
|
||||
|
||||
/* Don't write into registers if clocks are disabled */
|
||||
writel(clk, host->base + MMCICLOCK);
|
||||
qca_reg_wr_delay(host);
|
||||
|
||||
writel((readl(host->base + MMCICLOCK) | MCI_CLK_PWRSAVE), host->base + MMCICLOCK);
|
||||
qca_reg_wr_delay(host);
|
||||
}
|
||||
|
||||
int qca_mmc_start (struct mmc *mmc)
|
||||
{
|
||||
qca_mmc *host = mmc->priv;
|
||||
|
||||
writel(readl(host->base + MMCIPOWER) | MCI_SW_RST,
|
||||
host->base + MMCIPOWER);
|
||||
qca_reg_wr_delay(host);
|
||||
|
||||
while (readl(host->base + MMCIPOWER) & MCI_SW_RST) {
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qca_mmc_init(bd_t *bis, qca_mmc *host)
|
||||
{
|
||||
struct mmc *mmc;
|
||||
|
||||
mmc = malloc(sizeof(struct mmc));
|
||||
if (!mmc) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(mmc, 0, sizeof(struct mmc));
|
||||
|
||||
sprintf(mmc->name, "qca_mmc");
|
||||
mmc->priv = host;
|
||||
mmc->send_cmd = qca_mmc_send_cmd;
|
||||
mmc->set_ios = qca_set_ios;
|
||||
mmc->init = qca_mmc_start;
|
||||
mmc->getcd = NULL;
|
||||
|
||||
mmc->f_min = 400000;
|
||||
mmc->f_max = 52000000;
|
||||
|
||||
mmc->b_max = 512;
|
||||
/* voltage either 2.7-3.6V or 1.70 -1.95V */
|
||||
mmc->voltages = 0x40FF8080;
|
||||
mmc->host_caps = MMC_MODE_8BIT;
|
||||
mmc->host_caps |= MMC_MODE_HC;
|
||||
|
||||
mmc_register(mmc);
|
||||
host->mmc = mmc;
|
||||
host->dev_num = mmc->block_dev.dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
118
drivers/mmc/qca_mmc.h
Normal file
118
drivers/mmc/qca_mmc.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (c) 2014 The Linux Foundation. All rights reserved.
|
||||
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef ___QCA_MMC_H_
|
||||
#define ___QCA_MMC_H_
|
||||
|
||||
#define MMCIPOWER 0x000
|
||||
#define MCI_PWR_OFF 0x00
|
||||
#define MCI_PWR_UP 0x02
|
||||
#define MCI_PWR_ON 0x03
|
||||
#define MCI_SW_RST (1 << 7)
|
||||
|
||||
#define MMCICLOCK 0x004
|
||||
#define MCI_CLK_ENABLE (1 << 8)
|
||||
#define MCI_CLK_PWRSAVE (1 << 9)
|
||||
#define MCI_CLK_FLOWENA (1 << 12)
|
||||
#define MCI_CLK_SELECTIN (1 << 15)
|
||||
#define MCI_CLK_WIDEBUS_1 (0 << 10)
|
||||
#define MCI_CLK_WIDEBUS_4 (2 << 10)
|
||||
#define MCI_CLK_WIDEBUS_8 (3 << 10)
|
||||
|
||||
|
||||
#define MMCIARGUMENT 0x008
|
||||
#define MMCICOMMAND 0x00c
|
||||
#define MCI_CPSM_RESPONSE (1 << 6)
|
||||
#define MCI_CPSM_LONGRSP (1 << 7)
|
||||
#define MCI_CPSM_ENABLE (1 << 10)
|
||||
#define MCI_CPSM_PROGENA (1 << 11)
|
||||
|
||||
#define MMCIRESPCMD 0x010
|
||||
#define MMCIRESPONSE0 0x014
|
||||
#define MMCIRESPONSE1 0x018
|
||||
#define MMCIRESPONSE2 0x01c
|
||||
#define MMCIRESPONSE3 0x020
|
||||
#define MMCIDATATIMER 0x024
|
||||
#define MMCIDATALENGTH 0x028
|
||||
|
||||
#define MMCIDATACTRL 0x02c
|
||||
#define MCI_DPSM_ENABLE (1 << 0)
|
||||
#define MCI_DPSM_DIRECTION (1 << 1)
|
||||
#define MCI_RX_DATA_PEND (1 << 20)
|
||||
|
||||
#define MMCIDATACNT 0x030
|
||||
#define MMCISTATUS 0x034
|
||||
#define MCI_CMDCRCFAIL (1 << 0)
|
||||
#define MCI_DATACRCFAIL (1 << 1)
|
||||
#define MCI_CMDTIMEOUT (1 << 2)
|
||||
#define MCI_DATATIMEOUT (1 << 3)
|
||||
#define MCI_TXUNDERRUN (1 << 4)
|
||||
#define MCI_RXOVERRUN (1 << 5)
|
||||
#define MCI_CMDRESPEND (1 << 6)
|
||||
#define MCI_CMDSENT (1 << 7)
|
||||
#define MCI_DATAEND (1 << 8)
|
||||
#define MCI_DATABLOCKEND (1 << 10)
|
||||
#define MCI_CMDACTIVE (1 << 11)
|
||||
#define MCI_TXACTIVE (1 << 12)
|
||||
#define MCI_RXACTIVE (1 << 13)
|
||||
#define MCI_TXFIFOHALFEMPTY (1 << 14)
|
||||
#define MCI_RXFIFOHALFFULL (1 << 15)
|
||||
#define MCI_TXFIFOFULL (1 << 16)
|
||||
#define MCI_RXFIFOFULL (1 << 17)
|
||||
#define MCI_TXFIFOEMPTY (1 << 18)
|
||||
#define MCI_RXFIFOEMPTY (1 << 19)
|
||||
#define MCI_TXDATAAVLBL (1 << 20)
|
||||
#define MCI_RXDATAAVLBL (1 << 21)
|
||||
|
||||
#define MMCICLEAR 0x038
|
||||
#define MCI_CMDCRCFAILCLR (1 << 0)
|
||||
#define MCI_DATACRCFAILCLR (1 << 1)
|
||||
#define MCI_CMDTIMEOUTCLR (1 << 2)
|
||||
#define MCI_DATATIMEOUTCLR (1 << 3)
|
||||
#define MCI_TXUNDERRUNCLR (1 << 4)
|
||||
#define MCI_RXOVERRUNCLR (1 << 5)
|
||||
#define MCI_CMDRESPENDCLR (1 << 6)
|
||||
#define MCI_CMDSENTCLR (1 << 7)
|
||||
#define MCI_DATAENDCLR (1 << 8)
|
||||
#define MCI_STARTBITERRCLR (1 << 9)
|
||||
#define MCI_DATABLOCKENDCLR (1 << 10)
|
||||
|
||||
#define MCI_SDIOINTRCLR (1 << 22)
|
||||
#define MCI_PROGDONECLR (1 << 23)
|
||||
#define MCI_ATACMDCOMPLCLR (1 << 24)
|
||||
#define MCI_SDIOINTROPECLR (1 << 25)
|
||||
#define MCI_CCSTIMEOUTCLR (1 << 26)
|
||||
|
||||
#define MCI_CLEAR_STATIC_MASK \
|
||||
(MCI_CMDCRCFAILCLR|MCI_DATACRCFAILCLR|MCI_CMDTIMEOUTCLR|\
|
||||
MCI_DATATIMEOUTCLR|MCI_TXUNDERRUNCLR|MCI_RXOVERRUNCLR| \
|
||||
MCI_CMDRESPENDCLR|MCI_CMDSENTCLR|MCI_DATAENDCLR| \
|
||||
MCI_STARTBITERRCLR|MCI_DATABLOCKENDCLR|MCI_SDIOINTRCLR| \
|
||||
MCI_SDIOINTROPECLR|MCI_PROGDONECLR|MCI_ATACMDCOMPLCLR| \
|
||||
MCI_CCSTIMEOUTCLR)
|
||||
|
||||
#define MCI_STATUS2 0x06C
|
||||
#define MCI_MCLK_REG_WR_ACTIVE (1 << 0)
|
||||
|
||||
#define MMCIFIFO 0x080 /* to 0x0bc */
|
||||
/*
|
||||
* The size of the FIFO in bytes.
|
||||
*/
|
||||
#define MCI_FIFOSIZE (16*4)
|
||||
|
||||
#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2)
|
||||
|
||||
#define MCI_FIFODEPTH 16
|
||||
#define MCI_HFIFO_COUNT (MCI_FIFODEPTH / 2)
|
||||
|
||||
#endif /* __QCA_MMC_H_ */
|
||||
Loading…
Add table
Reference in a new issue