2014-04-21 21:45:02

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH RFC 00/12] Add Qualcomm SD Card Controller support.

From: Srinivas Kandagatla <[email protected]>

This patch series adds Qualcomm SD Card Controller support in pl180 mmci
driver. QCom SDCC is basically a pl180, but bit more customized, some of the
register layouts and offsets are different to the ones mentioned in pl180
datasheet. The plan is to totally remove the standalone SDCC driver
drivers/mmc/host/msm_sdcc.* and start using generic mmci driver for all
Qualcomm parts, as we get chance to test on other Qcom boards.

To start using the existing mmci driver, a fake amba id for Qualcomm is added
in patches:
ARM: amba: Add Qualcomm vendor ID.
mmc: mmci: Add Qualcomm Id to amba id table.

Second change is, adding a 3 clock cycle delay for register writes on QCOM SDCC
registers, which is done in patches:
mmc: mmci: Add register read/write wrappers.
mmc: mmci: Add write delay to variant structure.
mmc: mmci: Qcomm: Add 3 clock cycle delay after each register write

Third change was to accommodate DATCTRL and MMCICLK register layout changes in
Qcom SDCC. Which is done in patches:
mmc: mmci: Add Qcom datactrl register variant
mmc: mmci: Add Qcom variations to MCICommand register.
mmc: mmci: Qcom fix MCICLK register settings.
mmc: mmci: Add clock support for Qualcomm.

Fourth major change was to add qcom specfic pio read function, the need for
this is because the way MCIFIFOCNT register behaved in QCOM SDCC is very
different to the one in pl180. This change is done in patch:
mmc: mmci: Add Qcom specific pio_read function.

Last some Qcom unrelated changes to support Qcom are done in patches:
mmc: mmci: use NSEC_PER_SEC macro
mmc: mmci: move ST specific register extensions access under condition.

This patches are tested in PIO mode on IFC8064 board with both eMMC and
external SD card. I would appreciate any feedback/suggestions on the overall
approach.

Thanks,
srini

Srinivas Kandagatla (12):
ARM: amba: Add Qualcomm vendor ID.
mmc: mmci: Add Qualcomm Id to amba id table
mmc: mmci: Add Qcom datactrl register variant
mmc: mmci: Add register read/write wrappers.
mmc: mmci: use NSEC_PER_SEC macro
mmc: mmci: Add write delay to variant structure.
mmc: mmci: Qcomm: Add 3 clock cycle delay after each register write
mmc: mmci: move ST specific register extensions access under condition.
mmc: mmci: Qcom fix MCICLK register settings.
mmc: mmci: Add clock support for Qualcomm.
mmc: mmci: Add Qcom variations to MCICommand register.
mmc: mmci: Add Qcom specific pio_read function.

drivers/mmc/host/mmci.c | 239 +++++++++++++++++++++++++++++++++-------------
drivers/mmc/host/mmci.h | 28 ++++++
include/linux/amba/bus.h | 1 +
3 files changed, 202 insertions(+), 66 deletions(-)

--
1.7.9.5


2014-04-21 21:47:22

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH RFC 01/12] ARM: amba: Add Qualcomm vendor ID.

From: Srinivas Kandagatla <[email protected]>

This patch adds Qualcomm amba vendor Id to the list. This ID is used in mmci
driver.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
include/linux/amba/bus.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h
index 63b5eff..fdd7e1b 100644
--- a/include/linux/amba/bus.h
+++ b/include/linux/amba/bus.h
@@ -47,6 +47,7 @@ struct amba_driver {
enum amba_vendor {
AMBA_VENDOR_ARM = 0x41,
AMBA_VENDOR_ST = 0x80,
+ AMBA_VENDOR_QCOM = 0x51,
};

extern struct bus_type amba_bustype;
--
1.7.9.5

2014-04-21 21:47:36

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH RFC 02/12] mmc: mmci: Add Qualcomm Id to amba id table

From: Srinivas Kandagatla <[email protected]>

This patch adds a fake Qualcomm ID 0x00051180 to the amba_ids, as Qualcomm
SDCC controller is pl180, but amba id registers read 0x0's.
The plan is to remove SDCC driver totally and use mmci as the main SD
controller driver for Qualcomm SOCs.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 771c60a..391e8d4 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -158,6 +158,15 @@ static struct variant_data variant_ux500v2 = {
.pwrreg_nopower = true,
};

+static struct variant_data variant_qcom = {
+ .fifosize = 16 * 4,
+ .fifohalfsize = 8 * 4,
+ .clkreg = MCI_CLK_ENABLE,
+ .datalength_bits = 24,
+ .blksz_datactrl4 = true,
+ .pwrreg_powerup = MCI_PWR_UP,
+};
+
static int mmci_card_busy(struct mmc_host *mmc)
{
struct mmci_host *host = mmc_priv(mmc);
@@ -1908,6 +1917,12 @@ static struct amba_id mmci_ids[] = {
.mask = 0xf0ffffff,
.data = &variant_ux500v2,
},
+ /* Qualcomm variants */
+ {
+ .id = 0x00051180,
+ .mask = 0x000fffff,
+ .data = &variant_qcom,
+ },
{ 0, 0 },
};

--
1.7.9.5

2014-04-21 21:47:50

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH RFC 03/12] mmc: mmci: Add Qcom datactrl register variant

From: Srinivas Kandagatla <[email protected]>

Instance of this IP on Qualcomm's SOCs has bit different layout for datactrl
register. Bit postion datactrl[16:4] hold the true block size instead of power
of 2.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 391e8d4..19d6b6f 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -58,6 +58,8 @@ static unsigned int fmax = 515633;
* @sdio: variant supports SDIO
* @st_clkdiv: true if using a ST-specific clock divider algorithm
* @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
+ * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl
+ * register
* @pwrreg_powerup: power up value for MMCIPOWER register
* @signal_direction: input/out direction of bus signals can be indicated
* @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
@@ -73,6 +75,7 @@ struct variant_data {
bool sdio;
bool st_clkdiv;
bool blksz_datactrl16;
+ bool blksz_datactrl4;
u32 pwrreg_powerup;
bool signal_direction;
bool pwrreg_clkgate;
@@ -162,6 +165,7 @@ static struct variant_data variant_qcom = {
.fifosize = 16 * 4,
.fifohalfsize = 8 * 4,
.clkreg = MCI_CLK_ENABLE,
+ .blksz_datactrl4 = true,
.datalength_bits = 24,
.blksz_datactrl4 = true,
.pwrreg_powerup = MCI_PWR_UP,
@@ -760,6 +764,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)

if (variant->blksz_datactrl16)
datactrl = MCI_DPSM_ENABLE | (data->blksz << 16);
+ else if (variant->blksz_datactrl4)
+ datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);
else
datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;

--
1.7.9.5

2014-04-21 21:48:06

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH RFC 04/12] mmc: mmci: Add register read/write wrappers.

From: Srinivas Kandagatla <[email protected]>

This patch adds wrappers for readl/writel functions used in the driver. The
reason for this wrappers is to accommodate SOCs like Qualcomm which has
requirement for delaying the write for few cycles when writing to its SD Card
Controller registers.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 114 +++++++++++++++++++++++++----------------------
1 file changed, 61 insertions(+), 53 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 19d6b6f..36db31e 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -171,6 +171,16 @@ static struct variant_data variant_qcom = {
.pwrreg_powerup = MCI_PWR_UP,
};

+static inline u32 mmci_readl(struct mmci_host *host, u32 off)
+{
+ return readl(host->base + off);
+}
+
+static inline void mmci_writel(struct mmci_host *host, u32 data, u32 off)
+{
+ writel(data, host->base + off);
+}
+
static int mmci_card_busy(struct mmc_host *mmc)
{
struct mmci_host *host = mmc_priv(mmc);
@@ -180,7 +190,7 @@ static int mmci_card_busy(struct mmc_host *mmc)
pm_runtime_get_sync(mmc_dev(mmc));

spin_lock_irqsave(&host->lock, flags);
- if (readl(host->base + MMCISTATUS) & MCI_ST_CARDBUSY)
+ if (mmci_readl(host, MMCISTATUS) & MCI_ST_CARDBUSY)
busy = 1;
spin_unlock_irqrestore(&host->lock, flags);

@@ -230,7 +240,7 @@ static void mmci_write_clkreg(struct mmci_host *host, u32 clk)
{
if (host->clk_reg != clk) {
host->clk_reg = clk;
- writel(clk, host->base + MMCICLOCK);
+ mmci_writel(host, clk, MMCICLOCK);
}
}

@@ -241,7 +251,7 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
{
if (host->pwr_reg != pwr) {
host->pwr_reg = pwr;
- writel(pwr, host->base + MMCIPOWER);
+ mmci_writel(host, pwr, MMCIPOWER);
}
}

@@ -255,7 +265,7 @@ static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)

if (host->datactrl_reg != datactrl) {
host->datactrl_reg = datactrl;
- writel(datactrl, host->base + MMCIDATACTRL);
+ mmci_writel(host, datactrl, MMCIDATACTRL);
}
}

@@ -321,7 +331,7 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
static void
mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
{
- writel(0, host->base + MMCICOMMAND);
+ mmci_writel(host, 0, MMCICOMMAND);

BUG_ON(host->data);

@@ -336,18 +346,16 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)

static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
{
- void __iomem *base = host->base;
-
if (host->singleirq) {
- unsigned int mask0 = readl(base + MMCIMASK0);
+ unsigned int mask0 = mmci_readl(host, MMCIMASK0);

mask0 &= ~MCI_IRQ1MASK;
mask0 |= mask;

- writel(mask0, base + MMCIMASK0);
+ mmci_writel(host, mask0, MMCIMASK0);
}

- writel(mask, base + MMCIMASK1);
+ mmci_writel(host, mask, MMCIMASK1);
}

static void mmci_stop_data(struct mmci_host *host)
@@ -498,7 +506,7 @@ static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data)

/* Wait up to 1ms for the DMA to complete */
for (i = 0; ; i++) {
- status = readl(host->base + MMCISTATUS);
+ status = mmci_readl(host, MMCISTATUS);
if (!(status & MCI_RXDATAAVLBLMASK) || i >= 100)
break;
udelay(10);
@@ -637,8 +645,8 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
* to fire next DMA request. When that happens, MMCI will
* call mmci_data_end()
*/
- writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK,
- host->base + MMCIMASK0);
+ mmci_writel(host, mmci_readl(host, MMCIMASK0) | MCI_DATAENDMASK,
+ MMCIMASK0);
return 0;
}

@@ -756,8 +764,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
timeout = data->timeout_clks + (unsigned int)clks;

base = host->base;
- writel(timeout, base + MMCIDATATIMER);
- writel(host->size, base + MMCIDATALENGTH);
+ mmci_writel(host, timeout, MMCIDATATIMER);
+ mmci_writel(host, host->size, MMCIDATALENGTH);

blksz_bits = ffs(data->blksz) - 1;
BUG_ON(1 << blksz_bits != data->blksz);
@@ -831,20 +839,19 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
}

mmci_write_datactrlreg(host, datactrl);
- writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0);
+ mmci_writel(host, mmci_readl(host, MMCIMASK0) & ~MCI_DATAENDMASK,
+ MMCIMASK0);
mmci_set_mask1(host, irqmask);
}

static void
mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
{
- void __iomem *base = host->base;
-
dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n",
cmd->opcode, cmd->arg, cmd->flags);

- if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
- writel(0, base + MMCICOMMAND);
+ if (mmci_readl(host, MMCICOMMAND) & MCI_CPSM_ENABLE) {
+ mmci_writel(host, 0, MMCICOMMAND);
udelay(1);
}

@@ -859,8 +866,8 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)

host->cmd = cmd;

- writel(cmd->arg, base + MMCIARGUMENT);
- writel(c, base + MMCICOMMAND);
+ mmci_writel(host, cmd->arg, MMCIARGUMENT);
+ mmci_writel(host, c, MMCICOMMAND);
}

static void
@@ -885,7 +892,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
* can be as much as a FIFO-worth of data ahead. This
* matters for FIFO overruns only.
*/
- remain = readl(host->base + MMCIDATACNT);
+ remain = mmci_readl(host, MMCIDATACNT);
success = data->blksz * data->blocks - remain;

dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ, status 0x%08x at 0x%08x\n",
@@ -967,10 +974,10 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
} else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) {
cmd->error = -EILSEQ;
} else {
- cmd->resp[0] = readl(base + MMCIRESPONSE0);
- cmd->resp[1] = readl(base + MMCIRESPONSE1);
- cmd->resp[2] = readl(base + MMCIRESPONSE2);
- cmd->resp[3] = readl(base + MMCIRESPONSE3);
+ cmd->resp[0] = mmci_readl(host, MMCIRESPONSE0);
+ cmd->resp[1] = mmci_readl(host, MMCIRESPONSE1);
+ cmd->resp[2] = mmci_readl(host, MMCIRESPONSE2);
+ cmd->resp[3] = mmci_readl(host, MMCIRESPONSE3);
}

if ((!sbc && !cmd->data) || cmd->error) {
@@ -1081,11 +1088,10 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
struct mmci_host *host = dev_id;
struct sg_mapping_iter *sg_miter = &host->sg_miter;
struct variant_data *variant = host->variant;
- void __iomem *base = host->base;
unsigned long flags;
u32 status;

- status = readl(base + MMCISTATUS);
+ status = mmci_readl(host, MMCISTATUS);

dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status);

@@ -1125,7 +1131,7 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
if (remain)
break;

- status = readl(base + MMCISTATUS);
+ status = mmci_readl(host, MMCISTATUS);
} while (1);

sg_miter_stop(sg_miter);
@@ -1147,7 +1153,9 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
*/
if (host->size == 0) {
mmci_set_mask1(host, 0);
- writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0);
+ mmci_writel(host,
+ mmci_readl(host, MMCIMASK0) | MCI_DATAENDMASK,
+ MMCIMASK0);
}

return IRQ_HANDLED;
@@ -1168,10 +1176,10 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
struct mmc_command *cmd;
struct mmc_data *data;

- status = readl(host->base + MMCISTATUS);
+ status = mmci_readl(host, MMCISTATUS);

if (host->singleirq) {
- if (status & readl(host->base + MMCIMASK1))
+ if (status & mmci_readl(host, MMCIMASK1))
mmci_pio_irq(irq, dev_id);

status &= ~MCI_IRQ1MASK;
@@ -1182,8 +1190,8 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
* enabled) since the HW seems to be triggering the IRQ on both
* edges while monitoring DAT0 for busy completion.
*/
- status &= readl(host->base + MMCIMASK0);
- writel(status, host->base + MMCICLEAR);
+ status &= mmci_readl(host, MMCIMASK0);
+ mmci_writel(host, status, MMCICLEAR);

dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);

@@ -1627,9 +1635,9 @@ static int mmci_probe(struct amba_device *dev,

spin_lock_init(&host->lock);

- writel(0, host->base + MMCIMASK0);
- writel(0, host->base + MMCIMASK1);
- writel(0xfff, host->base + MMCICLEAR);
+ mmci_writel(host, 0, MMCIMASK0);
+ mmci_writel(host, 0, MMCIMASK1);
+ mmci_writel(host, 0xfff, MMCICLEAR);

if (plat->gpio_cd == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
@@ -1689,7 +1697,7 @@ static int mmci_probe(struct amba_device *dev,
goto irq0_free;
}

- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ mmci_writel(host, MCI_IRQENABLE, MMCIMASK0);

amba_set_drvdata(dev, mmc);

@@ -1745,11 +1753,11 @@ static int mmci_remove(struct amba_device *dev)

mmc_remove_host(mmc);

- writel(0, host->base + MMCIMASK0);
- writel(0, host->base + MMCIMASK1);
+ mmci_writel(host, 0, MMCIMASK0);
+ mmci_writel(host, 0, MMCIMASK1);

- writel(0, host->base + MMCICOMMAND);
- writel(0, host->base + MMCIDATACTRL);
+ mmci_writel(host, 0, MMCICOMMAND);
+ mmci_writel(host, 0, MMCIDATACTRL);

mmci_dma_release(host);
free_irq(dev->irq[0], host);
@@ -1783,7 +1791,7 @@ static int mmci_suspend(struct device *dev)
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
pm_runtime_get_sync(dev);
- writel(0, host->base + MMCIMASK0);
+ mmci_writel(host, 0, MMCIMASK0);
}

return 0;
@@ -1796,7 +1804,7 @@ static int mmci_resume(struct device *dev)

if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ mmci_writel(host, MCI_IRQENABLE, MMCIMASK0);
pm_runtime_put(dev);
}

@@ -1812,10 +1820,10 @@ static void mmci_save(struct mmci_host *host)
if (host->variant->pwrreg_nopower) {
spin_lock_irqsave(&host->lock, flags);

- writel(0, host->base + MMCIMASK0);
- writel(0, host->base + MMCIDATACTRL);
- writel(0, host->base + MMCIPOWER);
- writel(0, host->base + MMCICLOCK);
+ mmci_writel(host, 0, MMCIMASK0);
+ mmci_writel(host, 0, MMCIDATACTRL);
+ mmci_writel(host, 0, MMCIPOWER);
+ mmci_writel(host, 0, MMCICLOCK);
mmci_reg_delay(host);

spin_unlock_irqrestore(&host->lock, flags);
@@ -1830,10 +1838,10 @@ static void mmci_restore(struct mmci_host *host)
if (host->variant->pwrreg_nopower) {
spin_lock_irqsave(&host->lock, flags);

- writel(host->clk_reg, host->base + MMCICLOCK);
- writel(host->datactrl_reg, host->base + MMCIDATACTRL);
- writel(host->pwr_reg, host->base + MMCIPOWER);
- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ mmci_writel(host, host->clk_reg, MMCICLOCK);
+ mmci_writel(host, host->datactrl_reg, MMCIDATACTRL);
+ mmci_writel(host, host->pwr_reg, MMCIPOWER);
+ mmci_writel(host, MCI_IRQENABLE, MMCIMASK0);
mmci_reg_delay(host);

spin_unlock_irqrestore(&host->lock, flags);
--
1.7.9.5

2014-04-21 21:48:22

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH RFC 05/12] mmc: mmci: use NSEC_PER_SEC macro

From: Srinivas Kandagatla <[email protected]>

This patch replaces a constant used in calculating timeout with a proper
macro. This is make code more readable.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 36db31e..4f8d0ba 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -759,7 +759,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
data->bytes_xfered = 0;

clks = (unsigned long long)data->timeout_ns * host->cclk;
- do_div(clks, 1000000000UL);
+ do_div(clks, NSEC_PER_SEC);

timeout = data->timeout_clks + (unsigned int)clks;

--
1.7.9.5

2014-04-21 21:48:52

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH RFC 07/12] mmc: mmci: Qcomm: Add 3 clock cycle delay after each register write

From: Srinivas Kandagatla <[email protected]>

This patch adds a 3 clock cycle delay required after writing to controller
registers on Qualcomm SOCs. Without this delay cards are either not detected
or fails as soon as card is put into data transfer mode.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 86bf330..2dc7581 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -168,6 +168,7 @@ static struct variant_data variant_qcom = {
.fifosize = 16 * 4,
.fifohalfsize = 8 * 4,
.clkreg = MCI_CLK_ENABLE,
+ .reg_write_delay = 3,
.blksz_datactrl4 = true,
.datalength_bits = 24,
.blksz_datactrl4 = true,
--
1.7.9.5

2014-04-21 21:48:38

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH RFC 06/12] mmc: mmci: Add write delay to variant structure.

From: Srinivas Kandagatla <[email protected]>

This patch adds write delay parameter required after each write to controller
registers on some of the SOCs like Qualcomm ones. The delay parameter will
provide information on how many clock cycle delay required after each write.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 4f8d0ba..86bf330 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -55,6 +55,8 @@ static unsigned int fmax = 515633;
* is asserted (likewise for RX)
* @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
* is asserted (likewise for RX)
+ * @reg_write_delay: delay in number of clock cycles required after each write
+ * to controller registers.
* @sdio: variant supports SDIO
* @st_clkdiv: true if using a ST-specific clock divider algorithm
* @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
@@ -72,6 +74,7 @@ struct variant_data {
unsigned int datalength_bits;
unsigned int fifosize;
unsigned int fifohalfsize;
+ unsigned int reg_write_delay;
bool sdio;
bool st_clkdiv;
bool blksz_datactrl16;
@@ -178,7 +181,12 @@ static inline u32 mmci_readl(struct mmci_host *host, u32 off)

static inline void mmci_writel(struct mmci_host *host, u32 data, u32 off)
{
+ struct variant_data *var = host->variant;
+
writel(data, host->base + off);
+
+ if (var->reg_write_delay && host->mclk)
+ udelay(1 + ((var->reg_write_delay * USEC_PER_SEC)/host->mclk));
}

static int mmci_card_busy(struct mmc_host *mmc)
--
1.7.9.5

2014-04-21 21:49:09

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH RFC 09/12] mmc: mmci: Qcom fix MCICLK register settings.

From: Srinivas Kandagatla <[email protected]>

MCICLK register layout is bit different to the standard pl180 register layout.
Qcom SDCC controller some setup in MCICLK register to get it going. So this
patch adds new setup and makes it specific to Qcom hw designer.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 36 ++++++++++++++++++++++++++++++------
drivers/mmc/host/mmci.h | 21 +++++++++++++++++++++
2 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 179abfb..f465eb5 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -327,13 +327,37 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
/* Set actual clock for debug */
host->mmc->actual_clock = host->cclk;

- if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
- clk |= MCI_4BIT_BUS;
- if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
- clk |= MCI_ST_8BIT_BUS;
+ if (host->hw_designer == AMBA_VENDOR_QCOM) {
+ clk |= MCI_CLK_QCOM_FLOWENA;
+ clk |= (MCI_CLK_QCOM_SEL_FEEDBACK_CLK <<
+ MCI_CLK_QCOM_SEL_IN_SHIFT); /* feedback clk */
+ if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+ clk |= MCI_CLK_QCOM_WIDEBUS_8;
+ else if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+ clk |= MCI_CLK_QCOM_WIDEBUS_4;
+ else
+ clk |= MCI_CLK_QCOM_WIDEBUS_1;
+
+ if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50) {
+ /* clear SELECT_IN field */
+ clk &= ~(MCI_CLK_QCOM_SEL_MASK <<
+ MCI_CLK_QCOM_SEL_IN_SHIFT);
+ /* set DDR timing mode */
+ clk |= (MCI_CLK_QCOM_SEL_DDR_MODE <<
+ MCI_CLK_QCOM_SEL_IN_SHIFT);
+ }
+ clk |= (MCI_CLK_SDC4_MCLK_SEL_MCLK <<
+ MCI_CLK_SDC4_MCLK_SEL_SHIFT);

- if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50)
- clk |= MCI_ST_UX500_NEG_EDGE;
+ } else {
+ if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+ clk |= MCI_4BIT_BUS;
+ if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+ clk |= MCI_ST_8BIT_BUS;
+
+ if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50)
+ clk |= MCI_ST_UX500_NEG_EDGE;
+ }

mmci_write_clkreg(host, clk);
}
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 58b1b88..0a6de1c 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -31,6 +31,27 @@
/* Modified PL180 on Versatile Express platform */
#define MCI_ARM_HWFCEN (1 << 12)

+/* Modified on Qualcomm Integrations */
+#define MCI_CLK_QCOM_WIDEBUS_1 (0 << 10)
+#define MCI_CLK_QCOM_WIDEBUS_4 (2 << 10)
+#define MCI_CLK_QCOM_WIDEBUS_8 (3 << 10)
+#define MCI_CLK_QCOM_FLOWENA (1 << 12)
+#define MCI_CLK_QCOM_INVERTOUT (1 << 13)
+
+/* select in latch data and command */
+#define MCI_CLK_QCOM_SEL_IN_SHIFT (14)
+#define MCI_CLK_QCOM_SEL_MASK (0x3)
+#define MCI_CLK_QCOM_SEL_RISING_EDGE (1)
+#define MCI_CLK_QCOM_SEL_FEEDBACK_CLK (2)
+#define MCI_CLK_QCOM_SEL_DDR_MODE (3)
+
+/* mclk selection */
+#define MCI_CLK_SDC4_MCLK_SEL_SHIFT (23)
+#define MCI_CLK_SDC4_MCLK_SEL_MASK (0x3)
+#define MCI_CLK_SDC4_MCLK_SEL_FB_CLK (1)
+#define MCI_CLK_SDC4_MCLK_SEL_MCLK (2)
+
+
#define MMCIARGUMENT 0x008
#define MMCICOMMAND 0x00c
#define MCI_CPSM_RESPONSE (1 << 6)
--
1.7.9.5

2014-04-21 21:49:17

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH RFC 10/12] mmc: mmci: Add clock support for Qualcomm.

From: Srinivas Kandagatla <[email protected]>

MCICLK going to card bus is directly driven by the clock controller, so the
driver has to set the required rates depending on the state of the card. This
bit of support is very much similar to bypass mode but there is no such thing
called bypass mode in MCICLK register of Qcom SD card controller. By default
the clock is directly driven by the clk controller.

This patch adds clock support for Qualcomm SDCC in the driver. This bit of
code is conditioned on hw designer.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index f465eb5..2cd3a8f 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -291,7 +291,18 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
host->cclk = 0;

if (desired) {
- if (desired >= host->mclk) {
+ if (desired != host->mclk &&
+ host->hw_designer == AMBA_VENDOR_QCOM) {
+ /* Qcom MCLKCLK register does not define bypass bits */
+ int rc = clk_set_rate(host->clk, desired);
+ if (rc < 0) {
+ dev_err(mmc_dev(host->mmc),
+ "Error setting clock rate (%d)\n", rc);
+ } else {
+ host->mclk = clk_get_rate(host->clk);
+ host->cclk = host->mclk;
+ }
+ } else if (desired >= host->mclk) {
clk = MCI_CLK_BYPASS;
if (variant->st_clkdiv)
clk |= MCI_ST_UX500_NEG_EDGE;
@@ -1612,7 +1623,8 @@ static int mmci_probe(struct amba_device *dev,
* of course.
*/
if (plat->f_max)
- mmc->f_max = min(host->mclk, plat->f_max);
+ mmc->f_max = (host->hw_designer == AMBA_VENDOR_QCOM) ?
+ plat->f_max : min(host->mclk, plat->f_max);
else
mmc->f_max = min(host->mclk, fmax);
dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max);
--
1.7.9.5

2014-04-21 21:49:38

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH RFC 11/12] mmc: mmci: Add Qcom variations to MCICommand register.

From: Srinivas Kandagatla <[email protected]>

Some bits which control Command Path State Machine (CPSM) are new in Qcom
integration, so this patch adds support to those bits.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 4 ++++
drivers/mmc/host/mmci.h | 7 +++++++
2 files changed, 11 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 2cd3a8f..8fcd8ef 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -910,6 +910,10 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
if (/*interrupt*/0)
c |= MCI_CPSM_INTERRUPT;

+ if (host->hw_designer == AMBA_VENDOR_QCOM &&
+ mmc_cmd_type(cmd) == MMC_CMD_ADTC)
+ c |= MCI_CSPM_QCOM_DATCMD;
+
host->cmd = cmd;

mmci_writel(host, cmd->arg, MMCIARGUMENT);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 0a6de1c..2ba0834 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -64,6 +64,13 @@
#define MCI_ST_ENCMD_COMPL (1 << 12)
#define MCI_ST_NIEN (1 << 13)
#define MCI_ST_CE_ATACMD (1 << 14)
+/* Modified on Qualcomm Integrations */
+#define MCI_CSPM_QCOM_DATCMD (1 << 12)
+#define MCI_CSPM_QCOM_MCIABORT (1 << 13)
+#define MCI_CSPM_QCOM_CCSENABLE (1 << 14)
+#define MCI_CSPM_QCOM_CCSDISABLE (1 << 15)
+#define MCI_CSPM_QCOM_AUTO_CMD19 (1 << 16)
+#define MCI_CSPM_QCOM_AUTO_CMD21 (1 << 21)

#define MMCIRESPCMD 0x010
#define MMCIRESPONSE0 0x014
--
1.7.9.5

2014-04-21 21:49:54

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH RFC 12/12] mmc: mmci: Add Qcom specific pio_read function.

From: Srinivas Kandagatla <[email protected]>

MCIFIFOCNT register behaviour on Qcom chips is very different than the other
pl180 integrations. MCIFIFOCNT register contains the number of
words that are still waiting to be transferred through the FIFO. It keeps
decrementing once the host CPU reads the MCIFIFO. With the existing logic and
the MCIFIFOCNT behaviour, mmci_pio_read will loop forever, as the FIFOCNT
register will always return transfer size before reading the FIFO.

This patch implements qcom_pio_read function so as existing mmci_pio_read is
not suitable for Qcom SOCs.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 31 +++++++++++++++++++++++++++++--
1 file changed, 29 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 8fcd8ef..585888e 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -1047,6 +1047,29 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
}
}

+static int mmci_qcom_pio_read(struct mmci_host *host, char *buffer,
+ unsigned int remain)
+{
+ uint32_t *ptr = (uint32_t *) buffer;
+ int count = 0;
+ struct variant_data *variant = host->variant;
+ int fifo_size = variant->fifosize;
+
+ if (remain % 4)
+ remain = ((remain >> 2) + 1) << 2;
+
+ while (readl(host->base + MMCISTATUS) & MCI_RXDATAAVLBL) {
+ *ptr = readl(host->base + MMCIFIFO + (count % fifo_size));
+ ptr++;
+ count += sizeof(uint32_t);
+
+ remain -= sizeof(uint32_t);
+ if (remain == 0)
+ break;
+ }
+ return count;
+}
+
static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain)
{
void __iomem *base = host->base;
@@ -1168,8 +1191,12 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
remain = sg_miter->length;

len = 0;
- if (status & MCI_RXACTIVE)
- len = mmci_pio_read(host, buffer, remain);
+ if (status & MCI_RXACTIVE) {
+ if (host->hw_designer == AMBA_VENDOR_QCOM)
+ len = mmci_qcom_pio_read(host, buffer, remain);
+ else
+ len = mmci_pio_read(host, buffer, remain);
+ }
if (status & MCI_TXACTIVE)
len = mmci_pio_write(host, buffer, remain, status);

--
1.7.9.5

2014-04-21 21:51:04

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH RFC 08/12] mmc: mmci: move ST specific register extensions access under condition.

From: Srinivas Kandagatla <[email protected]>

This patch moves some of the ST specific register extensions access under
condition, so that other SOCs like Qualcomm or ARM would not a side effect of
writing to those reserved/different purpose bits.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 2dc7581..179abfb 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -270,7 +270,8 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
{
/* Keep ST Micro busy mode if enabled */
- datactrl |= host->datactrl_reg & MCI_ST_DPSM_BUSYMODE;
+ if (host->hw_designer == AMBA_VENDOR_ST)
+ datactrl |= host->datactrl_reg & MCI_ST_DPSM_BUSYMODE;

if (host->datactrl_reg != datactrl) {
host->datactrl_reg = datactrl;
@@ -816,7 +817,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
mmci_write_clkreg(host, clk);
}

- if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50)
+ if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 &&
+ host->hw_designer == AMBA_VENDOR_ST)
datactrl |= MCI_ST_DPSM_DDRMODE;

/*
--
1.7.9.5

2014-04-21 22:10:55

by Felipe Balbi

[permalink] [raw]
Subject: Re: [PATCH RFC 06/12] mmc: mmci: Add write delay to variant structure.

Hi,

On Mon, Apr 21, 2014 at 10:48:18PM +0100, [email protected] wrote:
> From: Srinivas Kandagatla <[email protected]>
>
> This patch adds write delay parameter required after each write to controller
> registers on some of the SOCs like Qualcomm ones. The delay parameter will
> provide information on how many clock cycle delay required after each write.
>
> Signed-off-by: Srinivas Kandagatla <[email protected]>
> ---
> drivers/mmc/host/mmci.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index 4f8d0ba..86bf330 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -55,6 +55,8 @@ static unsigned int fmax = 515633;
> * is asserted (likewise for RX)
> * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
> * is asserted (likewise for RX)
> + * @reg_write_delay: delay in number of clock cycles required after each write
> + * to controller registers.
> * @sdio: variant supports SDIO
> * @st_clkdiv: true if using a ST-specific clock divider algorithm
> * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
> @@ -72,6 +74,7 @@ struct variant_data {
> unsigned int datalength_bits;
> unsigned int fifosize;
> unsigned int fifohalfsize;
> + unsigned int reg_write_delay;
> bool sdio;
> bool st_clkdiv;
> bool blksz_datactrl16;
> @@ -178,7 +181,12 @@ static inline u32 mmci_readl(struct mmci_host *host, u32 off)
>
> static inline void mmci_writel(struct mmci_host *host, u32 data, u32 off)
> {
> + struct variant_data *var = host->variant;
> +
> writel(data, host->base + off);
> +
> + if (var->reg_write_delay && host->mclk)
> + udelay(1 + ((var->reg_write_delay * USEC_PER_SEC)/host->mclk));

looks like this should be quirk flag instead of a write delay... No
strong feelings though, but it looks like the following would be better,
perhaps:

if (host_is_qualcom(host))
udelay(1 + ((3 * USEC_PER_SEC)/host->mclk));

--
balbi


Attachments:
(No filename) (2.04 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2014-04-21 22:20:52

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH RFC 10/12] mmc: mmci: Add clock support for Qualcomm.

On 04/21/14 14:49, [email protected] wrote:
> From: Srinivas Kandagatla <[email protected]>
>
> MCICLK going to card bus is directly driven by the clock controller, so the
> driver has to set the required rates depending on the state of the card. This
> bit of support is very much similar to bypass mode but there is no such thing
> called bypass mode in MCICLK register of Qcom SD card controller. By default
> the clock is directly driven by the clk controller.
>
> This patch adds clock support for Qualcomm SDCC in the driver. This bit of
> code is conditioned on hw designer.
>
> Signed-off-by: Srinivas Kandagatla <[email protected]>
> ---
> drivers/mmc/host/mmci.c | 16 ++++++++++++++--
> 1 file changed, 14 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index f465eb5..2cd3a8f 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -291,7 +291,18 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
> host->cclk = 0;
>
> if (desired) {
> - if (desired >= host->mclk) {
> + if (desired != host->mclk &&
> + host->hw_designer == AMBA_VENDOR_QCOM) {
> + /* Qcom MCLKCLK register does not define bypass bits */
> + int rc = clk_set_rate(host->clk, desired);

Please turn on lockdep (PROVE_LOCKING) and sleeping while atomic checks
(DEBUG_ATOMIC_SLEEP). You cannot call clk_set_rate() in atomic context.

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

2014-04-22 08:15:49

by Srinivas Kandagatla

[permalink] [raw]
Subject: Re: [PATCH RFC 06/12] mmc: mmci: Add write delay to variant structure.

Thanks Felipe,

On 21/04/14 23:08, Felipe Balbi wrote:
>> + if (var->reg_write_delay && host->mclk)
>> >+ udelay(1 + ((var->reg_write_delay * USEC_PER_SEC)/host->mclk));
> looks like this should be quirk flag instead of a write delay... No
> strong feelings though, but it looks like the following would be better,
> perhaps:
>
> if (host_is_qualcom(host))
> udelay(1 + ((3 * USEC_PER_SEC)/host->mclk));
Am ok with your proposal. I was wondering if someone else might need it
in future.
If not I could change it as you suggested.

Thanks,
srini
>
> --

2014-04-22 10:03:35

by Srinivas Kandagatla

[permalink] [raw]
Subject: Re: [PATCH RFC 10/12] mmc: mmci: Add clock support for Qualcomm.

Thankyou Stephen,

On 21/04/14 23:20, Stephen Boyd wrote:
> Please turn on lockdep (PROVE_LOCKING) and sleeping while atomic checks
> (DEBUG_ATOMIC_SLEEP). You cannot call clk_set_rate() in atomic context.
You are correct, there is a spinlock taken just before entering this
code. I will fix this in next version by moving clk_set_rate out of this
lock.

Thanks,
srini

2014-04-22 12:58:43

by Christopher Covington

[permalink] [raw]
Subject: Re: [PATCH RFC 00/12] Add Qualcomm SD Card Controller support.

Hi Srini,

On 04/21/2014 05:43 PM, [email protected] wrote:

> This patches are tested in PIO mode on IFC8064 board with both eMMC and
> external SD card. I would appreciate any feedback/suggestions on the overall
> approach.

Is a non-upstream device tree needed to reproduce your test setup? If so could
you please point to or post it?

Thanks,
Christopher

--
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by the Linux Foundation.

2014-04-22 14:16:32

by Srinivas Kandagatla

[permalink] [raw]
Subject: Re: [PATCH RFC 00/12] Add Qualcomm SD Card Controller support.

Hi Christopher,


On 22/04/14 13:58, Christopher Covington wrote:
> Hi Srini,
>
> On 04/21/2014 05:43 PM, [email protected] wrote:
>
>> This patches are tested in PIO mode on IFC8064 board with both eMMC and
>> external SD card. I would appreciate any feedback/suggestions on the overall
>> approach.
>
I pushed a temporary tag "mmci-pio-srini-wip" for you on my git tree
git://git.linaro.org/landing-teams/working/qualcomm/kernel.git

> Is a non-upstream device tree needed to reproduce your test setup? If so could
> you please point to or post it?

You will need to use multi_v7_defconfig.

Thanks,
srini
>
> Thanks,
> Christopher
>

2014-04-29 08:18:48

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH v1 00/11] Add Qualcomm SD Card Controller support.

From: Srinivas Kandagatla <[email protected]>

Hi Russell,

This patch series adds Qualcomm SD Card Controller support in pl180 mmci
driver. QCom SDCC is basically a pl180, but bit more customized, some of the
register layouts and offsets are different to the ones mentioned in pl180
datasheet. The plan is to totally remove the standalone SDCC driver
drivers/mmc/host/msm_sdcc.* and start using generic mmci driver for all
Qualcomm parts, as we get chance to test on other Qcom boards.

To start using the existing mmci driver, a fake amba id for Qualcomm is added
in patches:
ARM: amba: Add Qualcomm vendor ID.
mmc: mmci: Add Qualcomm Id to amba id table.

Second change is, adding a 3 clock cycle delay for register writes on QCOM
SDCC
registers, which is done in patches:
mmc: mmci: Add register read/write wrappers.
mmc: mmci: Qcomm: Add 3 clock cycle delay after each register write

Third change was to accommodate DATCTRL and MMCICLK register layout changes in
Qcom SDCC. Which is done in patches:
mmc: mmci: Add Qcom datactrl register variant
mmc: mmci: Add Qcom variations to MCICommand register.
mmc: mmci: Qcom fix MCICLK register settings.
mmc: mmci: Add clock support for Qualcomm.

Fourth major change was to add qcom specfic pio read function, the need for
this is because the way MCIFIFOCNT register behaved in QCOM SDCC is very
different to the one in pl180. This change is done in patch:
mmc: mmci: Add Qcom specific pio_read function.

Last some Qcom unrelated changes to support Qcom are done in patches:
mmc: mmci: use NSEC_PER_SEC macro
mmc: mmci: move ST specific register extensions access under condition.

This patches are tested v3.15-rc3 in PIO mode on IFC6410 board with both eMMC
and external SD card. I would appreciate any feedback on the patches.
I would like to get this for v3.16.

Thanks,
srini

Srinivas Kandagatla (11):
ARM: amba: Add Qualcomm vendor ID.
mmc: mmci: Add Qualcomm Id to amba id table
mmc: mmci: Add Qcom datactrl register variant
mmc: mmci: Add register read/write wrappers.
mmc: mmci: use NSEC_PER_SEC macro
mmc: mmci: Qcomm: Add 3 clock cycle delay after register write
mmc: mmci: move ST specific register extensions access under
condition.
mmc: mmci: Qcom fix MCICLK register settings.
mmc: mmci: Add clock support for Qualcomm.
mmc: mmci: Add Qcom variations to MCICommand register.
mmc: mmci: Add Qcom specific pio_read function.

drivers/mmc/host/mmci.c | 243 +++++++++++++++++++++++++++++++++-------------
drivers/mmc/host/mmci.h | 28 ++++++
include/linux/amba/bus.h | 1 +
3 files changed, 206 insertions(+), 66 deletions(-)

--
1.7.9.5

2014-04-29 08:19:15

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH v1 01/11] ARM: amba: Add Qualcomm vendor ID.

From: Srinivas Kandagatla <[email protected]>

This patch adds Qualcomm amba vendor Id to the list. This ID is used in mmci
driver.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
include/linux/amba/bus.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h
index 63b5eff..fdd7e1b 100644
--- a/include/linux/amba/bus.h
+++ b/include/linux/amba/bus.h
@@ -47,6 +47,7 @@ struct amba_driver {
enum amba_vendor {
AMBA_VENDOR_ARM = 0x41,
AMBA_VENDOR_ST = 0x80,
+ AMBA_VENDOR_QCOM = 0x51,
};

extern struct bus_type amba_bustype;
--
1.7.9.5

2014-04-29 08:19:32

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH v1 02/11] mmc: mmci: Add Qualcomm Id to amba id table

From: Srinivas Kandagatla <[email protected]>

This patch adds a fake Qualcomm ID 0x00051180 to the amba_ids, as Qualcomm
SDCC controller is pl180, but amba id registers read 0x0's.
The plan is to remove SDCC driver totally and use mmci as the main SD
controller driver for Qualcomm SOCs.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 771c60a..391e8d4 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -158,6 +158,15 @@ static struct variant_data variant_ux500v2 = {
.pwrreg_nopower = true,
};

+static struct variant_data variant_qcom = {
+ .fifosize = 16 * 4,
+ .fifohalfsize = 8 * 4,
+ .clkreg = MCI_CLK_ENABLE,
+ .datalength_bits = 24,
+ .blksz_datactrl4 = true,
+ .pwrreg_powerup = MCI_PWR_UP,
+};
+
static int mmci_card_busy(struct mmc_host *mmc)
{
struct mmci_host *host = mmc_priv(mmc);
@@ -1908,6 +1917,12 @@ static struct amba_id mmci_ids[] = {
.mask = 0xf0ffffff,
.data = &variant_ux500v2,
},
+ /* Qualcomm variants */
+ {
+ .id = 0x00051180,
+ .mask = 0x000fffff,
+ .data = &variant_qcom,
+ },
{ 0, 0 },
};

--
1.7.9.5

2014-04-29 08:19:45

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH v1 03/11] mmc: mmci: Add Qcom datactrl register variant

From: Srinivas Kandagatla <[email protected]>

Instance of this IP on Qualcomm's SOCs has bit different layout for datactrl
register. Bit postion datactrl[16:4] hold the true block size instead of power
of 2.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 391e8d4..19d6b6f 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -58,6 +58,8 @@ static unsigned int fmax = 515633;
* @sdio: variant supports SDIO
* @st_clkdiv: true if using a ST-specific clock divider algorithm
* @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
+ * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl
+ * register
* @pwrreg_powerup: power up value for MMCIPOWER register
* @signal_direction: input/out direction of bus signals can be indicated
* @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
@@ -73,6 +75,7 @@ struct variant_data {
bool sdio;
bool st_clkdiv;
bool blksz_datactrl16;
+ bool blksz_datactrl4;
u32 pwrreg_powerup;
bool signal_direction;
bool pwrreg_clkgate;
@@ -162,6 +165,7 @@ static struct variant_data variant_qcom = {
.fifosize = 16 * 4,
.fifohalfsize = 8 * 4,
.clkreg = MCI_CLK_ENABLE,
+ .blksz_datactrl4 = true,
.datalength_bits = 24,
.blksz_datactrl4 = true,
.pwrreg_powerup = MCI_PWR_UP,
@@ -760,6 +764,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)

if (variant->blksz_datactrl16)
datactrl = MCI_DPSM_ENABLE | (data->blksz << 16);
+ else if (variant->blksz_datactrl4)
+ datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);
else
datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;

--
1.7.9.5

2014-04-29 08:19:59

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH v1 04/11] mmc: mmci: Add register read/write wrappers.

From: Srinivas Kandagatla <[email protected]>

This patch adds wrappers for readl/writel functions used in the driver. The
reason for this wrappers is to accommodate SOCs like Qualcomm which has
requirement for delaying the write for few cycles when writing to its SD Card
Controller registers.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 114 +++++++++++++++++++++++++----------------------
1 file changed, 61 insertions(+), 53 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 19d6b6f..36db31e 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -171,6 +171,16 @@ static struct variant_data variant_qcom = {
.pwrreg_powerup = MCI_PWR_UP,
};

+static inline u32 mmci_readl(struct mmci_host *host, u32 off)
+{
+ return readl(host->base + off);
+}
+
+static inline void mmci_writel(struct mmci_host *host, u32 data, u32 off)
+{
+ writel(data, host->base + off);
+}
+
static int mmci_card_busy(struct mmc_host *mmc)
{
struct mmci_host *host = mmc_priv(mmc);
@@ -180,7 +190,7 @@ static int mmci_card_busy(struct mmc_host *mmc)
pm_runtime_get_sync(mmc_dev(mmc));

spin_lock_irqsave(&host->lock, flags);
- if (readl(host->base + MMCISTATUS) & MCI_ST_CARDBUSY)
+ if (mmci_readl(host, MMCISTATUS) & MCI_ST_CARDBUSY)
busy = 1;
spin_unlock_irqrestore(&host->lock, flags);

@@ -230,7 +240,7 @@ static void mmci_write_clkreg(struct mmci_host *host, u32 clk)
{
if (host->clk_reg != clk) {
host->clk_reg = clk;
- writel(clk, host->base + MMCICLOCK);
+ mmci_writel(host, clk, MMCICLOCK);
}
}

@@ -241,7 +251,7 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
{
if (host->pwr_reg != pwr) {
host->pwr_reg = pwr;
- writel(pwr, host->base + MMCIPOWER);
+ mmci_writel(host, pwr, MMCIPOWER);
}
}

@@ -255,7 +265,7 @@ static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)

if (host->datactrl_reg != datactrl) {
host->datactrl_reg = datactrl;
- writel(datactrl, host->base + MMCIDATACTRL);
+ mmci_writel(host, datactrl, MMCIDATACTRL);
}
}

@@ -321,7 +331,7 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
static void
mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
{
- writel(0, host->base + MMCICOMMAND);
+ mmci_writel(host, 0, MMCICOMMAND);

BUG_ON(host->data);

@@ -336,18 +346,16 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)

static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
{
- void __iomem *base = host->base;
-
if (host->singleirq) {
- unsigned int mask0 = readl(base + MMCIMASK0);
+ unsigned int mask0 = mmci_readl(host, MMCIMASK0);

mask0 &= ~MCI_IRQ1MASK;
mask0 |= mask;

- writel(mask0, base + MMCIMASK0);
+ mmci_writel(host, mask0, MMCIMASK0);
}

- writel(mask, base + MMCIMASK1);
+ mmci_writel(host, mask, MMCIMASK1);
}

static void mmci_stop_data(struct mmci_host *host)
@@ -498,7 +506,7 @@ static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data)

/* Wait up to 1ms for the DMA to complete */
for (i = 0; ; i++) {
- status = readl(host->base + MMCISTATUS);
+ status = mmci_readl(host, MMCISTATUS);
if (!(status & MCI_RXDATAAVLBLMASK) || i >= 100)
break;
udelay(10);
@@ -637,8 +645,8 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
* to fire next DMA request. When that happens, MMCI will
* call mmci_data_end()
*/
- writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK,
- host->base + MMCIMASK0);
+ mmci_writel(host, mmci_readl(host, MMCIMASK0) | MCI_DATAENDMASK,
+ MMCIMASK0);
return 0;
}

@@ -756,8 +764,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
timeout = data->timeout_clks + (unsigned int)clks;

base = host->base;
- writel(timeout, base + MMCIDATATIMER);
- writel(host->size, base + MMCIDATALENGTH);
+ mmci_writel(host, timeout, MMCIDATATIMER);
+ mmci_writel(host, host->size, MMCIDATALENGTH);

blksz_bits = ffs(data->blksz) - 1;
BUG_ON(1 << blksz_bits != data->blksz);
@@ -831,20 +839,19 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
}

mmci_write_datactrlreg(host, datactrl);
- writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0);
+ mmci_writel(host, mmci_readl(host, MMCIMASK0) & ~MCI_DATAENDMASK,
+ MMCIMASK0);
mmci_set_mask1(host, irqmask);
}

static void
mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
{
- void __iomem *base = host->base;
-
dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n",
cmd->opcode, cmd->arg, cmd->flags);

- if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
- writel(0, base + MMCICOMMAND);
+ if (mmci_readl(host, MMCICOMMAND) & MCI_CPSM_ENABLE) {
+ mmci_writel(host, 0, MMCICOMMAND);
udelay(1);
}

@@ -859,8 +866,8 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)

host->cmd = cmd;

- writel(cmd->arg, base + MMCIARGUMENT);
- writel(c, base + MMCICOMMAND);
+ mmci_writel(host, cmd->arg, MMCIARGUMENT);
+ mmci_writel(host, c, MMCICOMMAND);
}

static void
@@ -885,7 +892,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
* can be as much as a FIFO-worth of data ahead. This
* matters for FIFO overruns only.
*/
- remain = readl(host->base + MMCIDATACNT);
+ remain = mmci_readl(host, MMCIDATACNT);
success = data->blksz * data->blocks - remain;

dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ, status 0x%08x at 0x%08x\n",
@@ -967,10 +974,10 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
} else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) {
cmd->error = -EILSEQ;
} else {
- cmd->resp[0] = readl(base + MMCIRESPONSE0);
- cmd->resp[1] = readl(base + MMCIRESPONSE1);
- cmd->resp[2] = readl(base + MMCIRESPONSE2);
- cmd->resp[3] = readl(base + MMCIRESPONSE3);
+ cmd->resp[0] = mmci_readl(host, MMCIRESPONSE0);
+ cmd->resp[1] = mmci_readl(host, MMCIRESPONSE1);
+ cmd->resp[2] = mmci_readl(host, MMCIRESPONSE2);
+ cmd->resp[3] = mmci_readl(host, MMCIRESPONSE3);
}

if ((!sbc && !cmd->data) || cmd->error) {
@@ -1081,11 +1088,10 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
struct mmci_host *host = dev_id;
struct sg_mapping_iter *sg_miter = &host->sg_miter;
struct variant_data *variant = host->variant;
- void __iomem *base = host->base;
unsigned long flags;
u32 status;

- status = readl(base + MMCISTATUS);
+ status = mmci_readl(host, MMCISTATUS);

dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status);

@@ -1125,7 +1131,7 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
if (remain)
break;

- status = readl(base + MMCISTATUS);
+ status = mmci_readl(host, MMCISTATUS);
} while (1);

sg_miter_stop(sg_miter);
@@ -1147,7 +1153,9 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
*/
if (host->size == 0) {
mmci_set_mask1(host, 0);
- writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0);
+ mmci_writel(host,
+ mmci_readl(host, MMCIMASK0) | MCI_DATAENDMASK,
+ MMCIMASK0);
}

return IRQ_HANDLED;
@@ -1168,10 +1176,10 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
struct mmc_command *cmd;
struct mmc_data *data;

- status = readl(host->base + MMCISTATUS);
+ status = mmci_readl(host, MMCISTATUS);

if (host->singleirq) {
- if (status & readl(host->base + MMCIMASK1))
+ if (status & mmci_readl(host, MMCIMASK1))
mmci_pio_irq(irq, dev_id);

status &= ~MCI_IRQ1MASK;
@@ -1182,8 +1190,8 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
* enabled) since the HW seems to be triggering the IRQ on both
* edges while monitoring DAT0 for busy completion.
*/
- status &= readl(host->base + MMCIMASK0);
- writel(status, host->base + MMCICLEAR);
+ status &= mmci_readl(host, MMCIMASK0);
+ mmci_writel(host, status, MMCICLEAR);

dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);

@@ -1627,9 +1635,9 @@ static int mmci_probe(struct amba_device *dev,

spin_lock_init(&host->lock);

- writel(0, host->base + MMCIMASK0);
- writel(0, host->base + MMCIMASK1);
- writel(0xfff, host->base + MMCICLEAR);
+ mmci_writel(host, 0, MMCIMASK0);
+ mmci_writel(host, 0, MMCIMASK1);
+ mmci_writel(host, 0xfff, MMCICLEAR);

if (plat->gpio_cd == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
@@ -1689,7 +1697,7 @@ static int mmci_probe(struct amba_device *dev,
goto irq0_free;
}

- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ mmci_writel(host, MCI_IRQENABLE, MMCIMASK0);

amba_set_drvdata(dev, mmc);

@@ -1745,11 +1753,11 @@ static int mmci_remove(struct amba_device *dev)

mmc_remove_host(mmc);

- writel(0, host->base + MMCIMASK0);
- writel(0, host->base + MMCIMASK1);
+ mmci_writel(host, 0, MMCIMASK0);
+ mmci_writel(host, 0, MMCIMASK1);

- writel(0, host->base + MMCICOMMAND);
- writel(0, host->base + MMCIDATACTRL);
+ mmci_writel(host, 0, MMCICOMMAND);
+ mmci_writel(host, 0, MMCIDATACTRL);

mmci_dma_release(host);
free_irq(dev->irq[0], host);
@@ -1783,7 +1791,7 @@ static int mmci_suspend(struct device *dev)
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
pm_runtime_get_sync(dev);
- writel(0, host->base + MMCIMASK0);
+ mmci_writel(host, 0, MMCIMASK0);
}

return 0;
@@ -1796,7 +1804,7 @@ static int mmci_resume(struct device *dev)

if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ mmci_writel(host, MCI_IRQENABLE, MMCIMASK0);
pm_runtime_put(dev);
}

@@ -1812,10 +1820,10 @@ static void mmci_save(struct mmci_host *host)
if (host->variant->pwrreg_nopower) {
spin_lock_irqsave(&host->lock, flags);

- writel(0, host->base + MMCIMASK0);
- writel(0, host->base + MMCIDATACTRL);
- writel(0, host->base + MMCIPOWER);
- writel(0, host->base + MMCICLOCK);
+ mmci_writel(host, 0, MMCIMASK0);
+ mmci_writel(host, 0, MMCIDATACTRL);
+ mmci_writel(host, 0, MMCIPOWER);
+ mmci_writel(host, 0, MMCICLOCK);
mmci_reg_delay(host);

spin_unlock_irqrestore(&host->lock, flags);
@@ -1830,10 +1838,10 @@ static void mmci_restore(struct mmci_host *host)
if (host->variant->pwrreg_nopower) {
spin_lock_irqsave(&host->lock, flags);

- writel(host->clk_reg, host->base + MMCICLOCK);
- writel(host->datactrl_reg, host->base + MMCIDATACTRL);
- writel(host->pwr_reg, host->base + MMCIPOWER);
- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ mmci_writel(host, host->clk_reg, MMCICLOCK);
+ mmci_writel(host, host->datactrl_reg, MMCIDATACTRL);
+ mmci_writel(host, host->pwr_reg, MMCIPOWER);
+ mmci_writel(host, MCI_IRQENABLE, MMCIMASK0);
mmci_reg_delay(host);

spin_unlock_irqrestore(&host->lock, flags);
--
1.7.9.5

2014-04-29 08:20:13

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH v1 05/11] mmc: mmci: use NSEC_PER_SEC macro

From: Srinivas Kandagatla <[email protected]>

This patch replaces a constant used in calculating timeout with a proper
macro. This is make code more readable.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 36db31e..4f8d0ba 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -759,7 +759,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
data->bytes_xfered = 0;

clks = (unsigned long long)data->timeout_ns * host->cclk;
- do_div(clks, 1000000000UL);
+ do_div(clks, NSEC_PER_SEC);

timeout = data->timeout_clks + (unsigned int)clks;

--
1.7.9.5

2014-04-29 08:20:28

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH v1 06/11] mmc: mmci: Qcomm: Add 3 clock cycle delay after register write

From: Srinivas Kandagatla <[email protected]>

Most of the Qcomm SD card controller registers must be updated to the MCLK
domain so subsequent writes to registers will be ignored until 3 clock cycles
have passed.

This patch adds a 3 clock cycle delay required after writing to controller
registers on Qualcomm SOCs. Without this delay all the register writes are not
successfull, resulting in not detecting cards.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 4f8d0ba..f73dc48 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -179,6 +179,14 @@ static inline u32 mmci_readl(struct mmci_host *host, u32 off)
static inline void mmci_writel(struct mmci_host *host, u32 data, u32 off)
{
writel(data, host->base + off);
+
+ /*
+ * On QCom SD card controller, registers must be updated to the
+ * MCLK domain so subsequent writes to this register will be ignored
+ * for 3 clk cycles.
+ */
+ if (host->hw_designer == AMBA_VENDOR_QCOM)
+ udelay(1 + ((3 * USEC_PER_SEC)/host->mclk));
}

static int mmci_card_busy(struct mmc_host *mmc)
--
1.7.9.5

2014-04-29 08:20:40

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH v1 07/11] mmc: mmci: move ST specific register extensions access under condition.

From: Srinivas Kandagatla <[email protected]>

This patch moves some of the ST specific register extensions access under
condition, so that other SOCs like Qualcomm or ARM would not a side effect of
writing to those reserved/different purpose bits.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index f73dc48..306e0c8 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -269,7 +269,8 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
{
/* Keep ST Micro busy mode if enabled */
- datactrl |= host->datactrl_reg & MCI_ST_DPSM_BUSYMODE;
+ if (host->hw_designer == AMBA_VENDOR_ST)
+ datactrl |= host->datactrl_reg & MCI_ST_DPSM_BUSYMODE;

if (host->datactrl_reg != datactrl) {
host->datactrl_reg = datactrl;
@@ -815,7 +816,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
mmci_write_clkreg(host, clk);
}

- if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50)
+ if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 &&
+ host->hw_designer == AMBA_VENDOR_ST)
datactrl |= MCI_ST_DPSM_DDRMODE;

/*
--
1.7.9.5

2014-04-29 08:20:52

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH v1 08/11] mmc: mmci: Qcom fix MCICLK register settings.

From: Srinivas Kandagatla <[email protected]>

MCICLK register layout is bit different to the standard pl180 register layout.
Qcom SDCC controller some setup in MCICLK register to get it going. So this
patch adds new setup and makes it specific to Qcom hw designer.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 36 ++++++++++++++++++++++++++++++------
drivers/mmc/host/mmci.h | 21 +++++++++++++++++++++
2 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 306e0c8..35aed38 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -326,13 +326,37 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
/* Set actual clock for debug */
host->mmc->actual_clock = host->cclk;

- if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
- clk |= MCI_4BIT_BUS;
- if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
- clk |= MCI_ST_8BIT_BUS;
+ if (host->hw_designer == AMBA_VENDOR_QCOM) {
+ clk |= MCI_CLK_QCOM_FLOWENA;
+ clk |= (MCI_CLK_QCOM_SEL_FEEDBACK_CLK <<
+ MCI_CLK_QCOM_SEL_IN_SHIFT); /* feedback clk */
+ if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+ clk |= MCI_CLK_QCOM_WIDEBUS_8;
+ else if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+ clk |= MCI_CLK_QCOM_WIDEBUS_4;
+ else
+ clk |= MCI_CLK_QCOM_WIDEBUS_1;
+
+ if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50) {
+ /* clear SELECT_IN field */
+ clk &= ~(MCI_CLK_QCOM_SEL_MASK <<
+ MCI_CLK_QCOM_SEL_IN_SHIFT);
+ /* set DDR timing mode */
+ clk |= (MCI_CLK_QCOM_SEL_DDR_MODE <<
+ MCI_CLK_QCOM_SEL_IN_SHIFT);
+ }
+ clk |= (MCI_CLK_SDC4_MCLK_SEL_MCLK <<
+ MCI_CLK_SDC4_MCLK_SEL_SHIFT);

- if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50)
- clk |= MCI_ST_UX500_NEG_EDGE;
+ } else {
+ if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+ clk |= MCI_4BIT_BUS;
+ if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+ clk |= MCI_ST_8BIT_BUS;
+
+ if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50)
+ clk |= MCI_ST_UX500_NEG_EDGE;
+ }

mmci_write_clkreg(host, clk);
}
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 58b1b88..0a6de1c 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -31,6 +31,27 @@
/* Modified PL180 on Versatile Express platform */
#define MCI_ARM_HWFCEN (1 << 12)

+/* Modified on Qualcomm Integrations */
+#define MCI_CLK_QCOM_WIDEBUS_1 (0 << 10)
+#define MCI_CLK_QCOM_WIDEBUS_4 (2 << 10)
+#define MCI_CLK_QCOM_WIDEBUS_8 (3 << 10)
+#define MCI_CLK_QCOM_FLOWENA (1 << 12)
+#define MCI_CLK_QCOM_INVERTOUT (1 << 13)
+
+/* select in latch data and command */
+#define MCI_CLK_QCOM_SEL_IN_SHIFT (14)
+#define MCI_CLK_QCOM_SEL_MASK (0x3)
+#define MCI_CLK_QCOM_SEL_RISING_EDGE (1)
+#define MCI_CLK_QCOM_SEL_FEEDBACK_CLK (2)
+#define MCI_CLK_QCOM_SEL_DDR_MODE (3)
+
+/* mclk selection */
+#define MCI_CLK_SDC4_MCLK_SEL_SHIFT (23)
+#define MCI_CLK_SDC4_MCLK_SEL_MASK (0x3)
+#define MCI_CLK_SDC4_MCLK_SEL_FB_CLK (1)
+#define MCI_CLK_SDC4_MCLK_SEL_MCLK (2)
+
+
#define MMCIARGUMENT 0x008
#define MMCICOMMAND 0x00c
#define MCI_CPSM_RESPONSE (1 << 6)
--
1.7.9.5

2014-04-29 08:21:05

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH v1 09/11] mmc: mmci: Add clock support for Qualcomm.

From: Srinivas Kandagatla <[email protected]>

MCICLK going to card bus is directly driven by the clock controller, so the
driver has to set the required rates depending on the state of the card. This
bit of support is very much similar to bypass mode but there is no such thing
called bypass mode in MCICLK register of Qcom SD card controller. By default
the clock is directly driven by the clk controller.

This patch adds clock support for Qualcomm SDCC in the driver. This bit of
code is conditioned on hw designer.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 35aed38..da135c0 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -290,7 +290,10 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
host->cclk = 0;

if (desired) {
- if (desired >= host->mclk) {
+
+ if (host->hw_designer == AMBA_VENDOR_QCOM) {
+ host->cclk = host->mclk;
+ } else if (desired >= host->mclk) {
clk = MCI_CLK_BYPASS;
if (variant->st_clkdiv)
clk |= MCI_ST_UX500_NEG_EDGE;
@@ -1371,6 +1374,19 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (!ios->clock && variant->pwrreg_clkgate)
pwr &= ~MCI_PWR_ON;

+ if (ios->clock != host->mclk &&
+ host->hw_designer == AMBA_VENDOR_QCOM) {
+ /* Qcom MCLKCLK register does not define bypass bits */
+ int rc = clk_set_rate(host->clk, ios->clock);
+ if (rc < 0) {
+ dev_err(mmc_dev(host->mmc),
+ "Error setting clock rate (%d)\n", rc);
+ } else {
+ host->mclk = clk_get_rate(host->clk);
+ host->cclk = host->mclk;
+ }
+ }
+
spin_lock_irqsave(&host->lock, flags);

mmci_set_clkreg(host, ios->clock);
@@ -1611,7 +1627,8 @@ static int mmci_probe(struct amba_device *dev,
* of course.
*/
if (plat->f_max)
- mmc->f_max = min(host->mclk, plat->f_max);
+ mmc->f_max = (host->hw_designer == AMBA_VENDOR_QCOM) ?
+ plat->f_max : min(host->mclk, plat->f_max);
else
mmc->f_max = min(host->mclk, fmax);
dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max);
--
1.7.9.5

2014-04-29 08:21:14

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH v1 10/11] mmc: mmci: Add Qcom variations to MCICommand register.

From: Srinivas Kandagatla <[email protected]>

Some bits which control Command Path State Machine (CPSM) are new in Qcom
integration, so this patch adds support to those bits.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 4 ++++
drivers/mmc/host/mmci.h | 7 +++++++
2 files changed, 11 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index da135c0..def1b19 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -901,6 +901,10 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
if (/*interrupt*/0)
c |= MCI_CPSM_INTERRUPT;

+ if (host->hw_designer == AMBA_VENDOR_QCOM &&
+ mmc_cmd_type(cmd) == MMC_CMD_ADTC)
+ c |= MCI_CSPM_QCOM_DATCMD;
+
host->cmd = cmd;

mmci_writel(host, cmd->arg, MMCIARGUMENT);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 0a6de1c..2ba0834 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -64,6 +64,13 @@
#define MCI_ST_ENCMD_COMPL (1 << 12)
#define MCI_ST_NIEN (1 << 13)
#define MCI_ST_CE_ATACMD (1 << 14)
+/* Modified on Qualcomm Integrations */
+#define MCI_CSPM_QCOM_DATCMD (1 << 12)
+#define MCI_CSPM_QCOM_MCIABORT (1 << 13)
+#define MCI_CSPM_QCOM_CCSENABLE (1 << 14)
+#define MCI_CSPM_QCOM_CCSDISABLE (1 << 15)
+#define MCI_CSPM_QCOM_AUTO_CMD19 (1 << 16)
+#define MCI_CSPM_QCOM_AUTO_CMD21 (1 << 21)

#define MMCIRESPCMD 0x010
#define MMCIRESPONSE0 0x014
--
1.7.9.5

2014-04-29 08:21:28

by Srinivas Kandagatla

[permalink] [raw]
Subject: [PATCH v1 11/11] mmc: mmci: Add Qcom specific pio_read function.

From: Srinivas Kandagatla <[email protected]>

MCIFIFOCNT register behaviour on Qcom chips is very different than the other
pl180 integrations. MCIFIFOCNT register contains the number of
words that are still waiting to be transferred through the FIFO. It keeps
decrementing once the host CPU reads the MCIFIFO. With the existing logic and
the MCIFIFOCNT behaviour, mmci_pio_read will loop forever, as the FIFOCNT
register will always return transfer size before reading the FIFO.

Also the data sheet states that "This register is only useful for debug
purposes and should not be used for normal operation since it does not reflect
data which may or may not be in the pipeline".

This patch implements qcom_pio_read function so as existing mmci_pio_read is
not suitable for Qcom SOCs.

Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/mmc/host/mmci.c | 31 +++++++++++++++++++++++++++++--
1 file changed, 29 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index def1b19..45198b6 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -1038,6 +1038,29 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
}
}

+static int mmci_qcom_pio_read(struct mmci_host *host, char *buffer,
+ unsigned int remain)
+{
+ uint32_t *ptr = (uint32_t *) buffer;
+ int count = 0;
+ struct variant_data *variant = host->variant;
+ int fifo_size = variant->fifosize;
+
+ if (remain % 4)
+ remain = ((remain >> 2) + 1) << 2;
+
+ while (readl(host->base + MMCISTATUS) & MCI_RXDATAAVLBL) {
+ *ptr = readl(host->base + MMCIFIFO + (count % fifo_size));
+ ptr++;
+ count += sizeof(uint32_t);
+
+ remain -= sizeof(uint32_t);
+ if (remain == 0)
+ break;
+ }
+ return count;
+}
+
static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain)
{
void __iomem *base = host->base;
@@ -1159,8 +1182,12 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
remain = sg_miter->length;

len = 0;
- if (status & MCI_RXACTIVE)
- len = mmci_pio_read(host, buffer, remain);
+ if (status & MCI_RXACTIVE) {
+ if (host->hw_designer == AMBA_VENDOR_QCOM)
+ len = mmci_qcom_pio_read(host, buffer, remain);
+ else
+ len = mmci_pio_read(host, buffer, remain);
+ }
if (status & MCI_TXACTIVE)
len = mmci_pio_write(host, buffer, remain, status);

--
1.7.9.5