summaryrefslogtreecommitdiffstats
path: root/drivers/mci/arasan-sdhci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mci/arasan-sdhci.c')
-rw-r--r--drivers/mci/arasan-sdhci.c756
1 files changed, 567 insertions, 189 deletions
diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c
index b43a4f8ddf..b7dd98049f 100644
--- a/drivers/mci/arasan-sdhci.c
+++ b/drivers/mci/arasan-sdhci.c
@@ -6,41 +6,77 @@
#include <init.h>
#include <linux/clk.h>
#include <mci.h>
+#include <mach/zynqmp/firmware-zynqmp.h>
#include "sdhci.h"
#define SDHCI_ARASAN_HCAP_CLK_FREQ_MASK 0xFF00
#define SDHCI_ARASAN_HCAP_CLK_FREQ_SHIFT 8
#define SDHCI_INT_ADMAE BIT(29)
-#define SDHCI_ARASAN_INT_DATA_MASK SDHCI_INT_XFER_COMPLETE | \
+#define SDHCI_ARASAN_INT_DATA_MASK (SDHCI_INT_XFER_COMPLETE | \
SDHCI_INT_DMA | \
SDHCI_INT_SPACE_AVAIL | \
SDHCI_INT_DATA_AVAIL | \
SDHCI_INT_DATA_TIMEOUT | \
SDHCI_INT_DATA_CRC | \
SDHCI_INT_DATA_END_BIT | \
- SDHCI_INT_ADMAE
+ SDHCI_INT_ADMAE)
-#define SDHCI_ARASAN_INT_CMD_MASK SDHCI_INT_CMD_COMPLETE | \
+#define SDHCI_ARASAN_INT_CMD_MASK (SDHCI_INT_CMD_COMPLETE | \
SDHCI_INT_TIMEOUT | \
SDHCI_INT_CRC | \
SDHCI_INT_END_BIT | \
- SDHCI_INT_INDEX
+ SDHCI_INT_INDEX)
#define SDHCI_ARASAN_BUS_WIDTH 4
#define TIMEOUT_VAL 0xE
+#define ZYNQMP_CLK_PHASES 10
+#define ZYNQMP_CLK_PHASE_UHS_SDR104 6
+#define ZYNQMP_CLK_PHASE_HS200 9
+/* Default settings for ZynqMP Clock Phases */
+#define ZYNQMP_ICLK_PHASE {0, 63, 63, 0, 63, 0, 0, 183, 54, 0, 0}
+#define ZYNQMP_OCLK_PHASE {0, 72, 60, 0, 60, 72, 135, 48, 72, 135, 0}
+
+/**
+ * struct sdhci_arasan_clk_data - Arasan Controller Clock Data.
+ *
+ * @sdcardclk_hw: Struct for the clock we might provide to a PHY.
+ * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw.
+ * @sampleclk_hw: Struct for the clock we might provide to a PHY.
+ * @sampleclk: Pointer to normal 'struct clock' for sampleclk_hw.
+ * @clk_phase_in: Array of Input Clock Phase Delays for all speed modes
+ * @clk_phase_out: Array of Output Clock Phase Delays for all speed modes
+ * @set_clk_delays: Function pointer for setting Clock Delays
+ * @clk_of_data: Platform specific runtime clock data storage pointer
+ */
+struct sdhci_arasan_clk_data {
+ struct clk_hw sdcardclk_hw;
+ struct clk *sdcardclk;
+ struct clk_hw sampleclk_hw;
+ struct clk *sampleclk;
+ int clk_phase_in[ZYNQMP_CLK_PHASES + 1];
+ int clk_phase_out[ZYNQMP_CLK_PHASES + 1];
+ void (*set_clk_delays)(struct sdhci *host);
+ void *clk_of_data;
+};
+
struct arasan_sdhci_host {
struct mci_host mci;
struct sdhci sdhci;
- void __iomem *ioaddr;
unsigned int quirks; /* Arasan deviations from spec */
+ struct sdhci_arasan_clk_data clk_data;
/* Controller does not have CD wired and will not function normally without */
#define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0)
#define SDHCI_ARASAN_QUIRK_NO_1_8_V BIT(1)
+/*
+ * Some of the Arasan variations might not have timing requirements
+ * met at 25MHz for Default Speed mode, those controllers work at
+ * 19MHz instead
+ */
+#define SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN BIT(2)
};
-
static inline
struct arasan_sdhci_host *to_arasan_sdhci_host(struct mci_host *mci)
{
@@ -53,85 +89,86 @@ struct arasan_sdhci_host *sdhci_to_arasan(struct sdhci *sdhci)
return container_of(sdhci, struct arasan_sdhci_host, sdhci);
}
-static void arasan_sdhci_writel(struct sdhci *sdhci, int reg, u32 val)
+static int arasan_sdhci_card_present(struct mci_host *mci)
{
- struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
-
- writel(val, p->ioaddr + reg);
-}
+ struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ u32 val;
-static void arasan_sdhci_writew(struct sdhci *sdhci, int reg, u16 val)
-{
- struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+ val = sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE);
- writew(val, p->ioaddr + reg);
+ return !!(val & SDHCI_CARD_DETECT);
}
-static void arasan_sdhci_writeb(struct sdhci *sdhci, int reg, u8 val)
+static int arasan_sdhci_card_write_protected(struct mci_host *mci)
{
- struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+ struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ u32 val;
+
+ val = sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE);
- writeb(val, p->ioaddr + reg);
+ return !(val & SDHCI_WRITE_PROTECT);
}
-static u32 arasan_sdhci_readl(struct sdhci *sdhci, int reg)
+static int arasan_sdhci_reset(struct arasan_sdhci_host *host, u8 mask)
{
- struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+ int ret;
- return readl(p->ioaddr + reg);
-}
+ ret = sdhci_reset(&host->sdhci, mask);
+ if (ret)
+ return ret;
-static u16 arasan_sdhci_readw(struct sdhci *sdhci, int reg)
-{
- struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+ if (host->quirks & SDHCI_ARASAN_QUIRK_FORCE_CDTEST) {
+ u8 ctrl;
+
+ ctrl = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL);
+ ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_INS;
+ sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, ctrl);
+ }
- return readw(p->ioaddr + reg);
+ return 0;
}
-static u8 arasan_sdhci_readb(struct sdhci *sdhci, int reg)
+static void arasan_zynqmp_dll_reset(struct arasan_sdhci_host *host, u32 deviceid)
{
- struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+ u16 clk;
- return readb(p->ioaddr + reg);
-}
+ clk = sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL);
+ clk &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN);
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, clk);
-static int arasan_sdhci_card_present(struct mci_host *mci)
-{
- struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ /* Issue DLL Reset */
+ zynqmp_pm_sd_dll_reset(deviceid, PM_DLL_RESET_PULSE);
+
+ clk = sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL);
- return !!(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_CARD_DETECT);
+ sdhci_enable_clk(&host->sdhci, clk);
}
-static int arasan_sdhci_card_write_protected(struct mci_host *mci)
+static int arasan_zynqmp_execute_tuning(struct mci_host *mci, u32 opcode)
{
struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ struct clk_hw *hw = &host->clk_data.sdcardclk_hw;
+ const char *clk_name = clk_hw_get_name(hw);
+ u32 device_id = !strcmp(clk_name, "clk_out_sd0") ? NODE_SD_0 :
+ NODE_SD_1;
+ int err;
- return !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_WRITE_PROTECT);
-}
+ /* ZynqMP SD controller does not perform auto tuning in DDR50 mode */
+ if (mci->timing == MMC_TIMING_UHS_DDR50)
+ return 0;
-static int arasan_sdhci_reset(struct arasan_sdhci_host *host, u8 mask)
-{
- sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, mask);
+ arasan_zynqmp_dll_reset(host, device_id);
- /* wait for reset completion */
- if (wait_on_timeout(100 * MSECOND,
- !(sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) & mask))){
- dev_err(host->mci.hw_dev, "SDHCI reset timeout\n");
- return -ETIMEDOUT;
- }
+ err = sdhci_execute_tuning(&host->sdhci, opcode);
+ if (err)
+ return err;
- if (host->quirks & SDHCI_ARASAN_QUIRK_FORCE_CDTEST) {
- u8 ctrl;
-
- ctrl = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL);
- ctrl |= SDHCI_CARD_DETECT_TEST_LEVEL | SDHCI_CARD_DETECT_SIGNAL_SELECTION;
- sdhci_write8(&host->sdhci, ctrl, SDHCI_HOST_CONTROL);
- }
+ arasan_zynqmp_dll_reset(host, device_id);
return 0;
}
-static int arasan_sdhci_init(struct mci_host *mci, struct device_d *dev)
+static int arasan_sdhci_init(struct mci_host *mci, struct device *dev)
{
struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
int ret;
@@ -141,113 +178,66 @@ static int arasan_sdhci_init(struct mci_host *mci, struct device_d *dev)
return ret;
sdhci_write8(&host->sdhci, SDHCI_POWER_CONTROL,
- SDHCI_BUS_VOLTAGE_330 | SDHCI_BUS_POWER_EN);
+ SDHCI_BUS_VOLTAGE_330 | SDHCI_BUS_POWER_EN);
udelay(400);
sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE,
- SDHCI_ARASAN_INT_DATA_MASK |
- SDHCI_ARASAN_INT_CMD_MASK);
- sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, 0x00);
+ SDHCI_ARASAN_INT_DATA_MASK | SDHCI_ARASAN_INT_CMD_MASK);
+ sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, 0);
return 0;
}
-#define SDHCI_MAX_DIV_SPEC_300 2046
-
-static u16 arasan_sdhci_get_clock_divider(struct arasan_sdhci_host *host,
- unsigned int reqclk)
+static void arasan_sdhci_set_clock(struct mci_host *mci, unsigned int clock)
{
- u16 div;
+ struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ struct sdhci_arasan_clk_data *clk_data = &host->clk_data;
+
+ if (host->quirks & SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN) {
+ /*
+ * Some of the Arasan variations might not have timing
+ * requirements met at 25MHz for Default Speed mode,
+ * those controllers work at 19MHz instead.
+ */
+ if (clock == 25000000)
+ clock = (25000000 * 19) / 25;
+ }
- for (div = 1; div < SDHCI_MAX_DIV_SPEC_300; div += 2)
- if ((host->mci.f_max / div) <= reqclk)
- break;
- div /= 2;
+ clk_set_phase(clk_data->sampleclk,
+ clk_data->clk_phase_in[mci->mci->host->timing]);
+ clk_set_phase(clk_data->sdcardclk,
+ clk_data->clk_phase_out[mci->mci->host->timing]);
- return div;
+ sdhci_set_clock(&host->sdhci, clock, host->sdhci.max_clk);
}
-#define SDHCI_FREQ_SEL_10_BIT(x) (((x) & 0x300) >> 2)
-
static void arasan_sdhci_set_ios(struct mci_host *mci, struct mci_ios *ios)
{
struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
u16 val;
- /* stop clock */
- sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0);
-
- if (ios->clock) {
- u64 start;
-
- /* set & start clock */
- val = arasan_sdhci_get_clock_divider(host, ios->clock);
- /* Bit 6 & 7 are upperbits of 10bit divider */
- val = SDHCI_FREQ_SEL(val) | SDHCI_FREQ_SEL_10_BIT(val);
- val |= SDHCI_INTCLOCK_EN;
- sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val);
-
- start = get_time_ns();
- while (!(sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL) &
- SDHCI_INTCLOCK_STABLE)) {
- if (is_timeout(start, 20 * MSECOND)) {
- dev_err(host->mci.hw_dev,
- "SDHCI clock stable timeout\n");
- return;
- }
- }
- /* enable bus clock */
- sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL,
- val | SDHCI_SDCLOCK_EN);
- }
+ if (ios->clock)
+ arasan_sdhci_set_clock(mci, ios->clock);
- val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL) &
- ~(SDHCI_DATA_WIDTH_4BIT | SDHCI_DATA_WIDTH_8BIT);
+ sdhci_set_bus_width(&host->sdhci, ios->bus_width);
- switch (ios->bus_width) {
- case MMC_BUS_WIDTH_8:
- val |= SDHCI_DATA_WIDTH_8BIT;
- break;
- case MMC_BUS_WIDTH_4:
- val |= SDHCI_DATA_WIDTH_4BIT;
- break;
- }
+ val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL);
if (ios->clock > 26000000)
- val |= SDHCI_HIGHSPEED_EN;
+ val |= SDHCI_CTRL_HISPD;
else
- val &= ~SDHCI_HIGHSPEED_EN;
+ val &= ~SDHCI_CTRL_HISPD;
sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val);
}
-static int arasan_sdhci_wait_for_done(struct arasan_sdhci_host *host, u32 mask)
+static void print_error(struct arasan_sdhci_host *host, int cmdidx, int ret)
{
- u64 start = get_time_ns();
- u16 stat;
-
- do {
- stat = sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS);
- if (stat & SDHCI_INT_ERROR) {
- dev_err(host->mci.hw_dev, "SDHCI_INT_ERROR: 0x%08x\n",
- sdhci_read16(&host->sdhci, SDHCI_INT_ERROR_STATUS));
- return -EPERM;
- }
+ if (ret == -ETIMEDOUT)
+ return;
- if (is_timeout(start, 1000 * MSECOND)) {
- dev_err(host->mci.hw_dev,
- "SDHCI timeout while waiting for done\n");
- return -ETIMEDOUT;
- }
- } while ((stat & mask) != mask);
-
- return 0;
-}
-
-static void print_error(struct arasan_sdhci_host *host, int cmdidx)
-{
dev_err(host->mci.hw_dev,
- "error while transfering data for command %d\n", cmdidx);
+ "error while transferring data for command %d\n", cmdidx);
dev_err(host->mci.hw_dev, "state = 0x%08x , interrupt = 0x%08x\n",
sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE),
sdhci_read32(&host->sdhci, SDHCI_INT_NORMAL_STATUS));
@@ -258,90 +248,460 @@ static int arasan_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
{
struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
u32 mask, command, xfer;
+ dma_addr_t dma;
int ret;
- /* Wait for idle before next command */
- mask = SDHCI_CMD_INHIBIT_CMD;
- if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)
- mask |= SDHCI_CMD_INHIBIT_DATA;
-
- ret = wait_on_timeout(10 * MSECOND,
- !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & mask));
-
- if (ret) {
- dev_err(host->mci.hw_dev,
- "SDHCI timeout while waiting for idle\n");
+ ret = sdhci_wait_idle(&host->sdhci, cmd, data);
+ if (ret)
return ret;
- }
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
mask = SDHCI_INT_CMD_COMPLETE;
- if (data)
+ if (cmd->resp_type & MMC_RSP_BUSY)
mask |= SDHCI_INT_XFER_COMPLETE;
- sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data, false, &command, &xfer);
+ sdhci_setup_data_dma(&host->sdhci, data, &dma);
+
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
+ dma == SDHCI_NO_DMA ? false : true,
+ &command, &xfer);
sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, TIMEOUT_VAL);
- sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
- sdhci_write16(&host->sdhci, SDHCI_BLOCK_SIZE, SDHCI_DMA_BOUNDARY_512K |
- SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize));
- sdhci_write16(&host->sdhci, SDHCI_BLOCK_COUNT, data->blocks);
+ if (xfer)
+ sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
+ if (data) {
+ sdhci_write16(&host->sdhci, SDHCI_BLOCK_SIZE,
+ SDHCI_DMA_BOUNDARY_512K | SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize));
+ sdhci_write16(&host->sdhci, SDHCI_BLOCK_COUNT, data->blocks);
+ }
sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
- ret = arasan_sdhci_wait_for_done(host, mask);
- if (ret == -EPERM)
+ /* CMD19/21 generate _only_ Buffer Read Ready interrupt */
+ if (cmd->cmdidx == MMC_SEND_TUNING_BLOCK || cmd->cmdidx == MMC_SEND_TUNING_BLOCK_HS200)
+ mask = SDHCI_INT_DATA_AVAIL;
+
+ ret = sdhci_wait_for_done(&host->sdhci, mask);
+ if (ret)
goto error;
- else if (ret)
- return ret;
sdhci_read_response(&host->sdhci, cmd);
- sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, mask);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
if (data)
- ret = sdhci_transfer_data(&host->sdhci, data);
+ ret = sdhci_transfer_data_dma(&host->sdhci, data, dma);
error:
if (ret) {
- print_error(host, cmd->cmdidx);
- arasan_sdhci_reset(host, BIT(1)); // SDHCI_RESET_CMD
- arasan_sdhci_reset(host, BIT(2)); // SDHCI_RESET_DATA
+ print_error(host, cmd->cmdidx, ret);
+ arasan_sdhci_reset(host, SDHCI_RESET_CMD);
+ arasan_sdhci_reset(host, SDHCI_RESET_DATA);
}
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+
return ret;
}
+static void sdhci_arasan_set_clk_delays(struct sdhci *host)
+{
+ struct arasan_sdhci_host *arasan_sdhci = sdhci_to_arasan(host);
+ struct mci_host *mci = &arasan_sdhci->mci;
+ struct sdhci_arasan_clk_data *clk_data = &arasan_sdhci->clk_data;
+
+ clk_set_phase(clk_data->sampleclk,
+ clk_data->clk_phase_in[mci->timing]);
+ clk_set_phase(clk_data->sdcardclk,
+ clk_data->clk_phase_out[mci->timing]);
+}
-static void arasan_sdhci_set_mci_caps(struct arasan_sdhci_host *host)
+static void arasan_dt_read_clk_phase(struct device *dev,
+ struct sdhci_arasan_clk_data *clk_data,
+ unsigned int timing, const char *prop)
{
- u16 caps = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES_1);
+ struct device_node *np = dev->of_node;
- if ((caps & SDHCI_HOSTCAP_VOLTAGE_180) &&
- !(host->quirks & SDHCI_ARASAN_QUIRK_NO_1_8_V))
- host->mci.voltages |= MMC_VDD_165_195;
- if (caps & SDHCI_HOSTCAP_VOLTAGE_300)
- host->mci.voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
- if (caps & SDHCI_HOSTCAP_VOLTAGE_330)
- host->mci.voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
+ u32 clk_phase[2] = {0};
+ int ret;
- if (caps & SDHCI_HOSTCAP_HIGHSPEED)
- host->mci.host_caps |= (MMC_CAP_MMC_HIGHSPEED_52MHZ |
- MMC_CAP_MMC_HIGHSPEED |
- MMC_CAP_SD_HIGHSPEED);
+ /*
+ * Read Tap Delay values from DT, if the DT does not contain the
+ * Tap Values then use the pre-defined values.
+ */
+ ret = of_property_read_u32_array(np, prop, &clk_phase[0], 2);
+ if (ret < 0) {
+ dev_dbg(dev, "Using predefined clock phase for %s = %d %d\n",
+ prop, clk_data->clk_phase_in[timing],
+ clk_data->clk_phase_out[timing]);
+ return;
+ }
- /* parse board supported bus width capabilities */
- mci_of_parse(&host->mci);
+ /* The values read are Input and Output Clock Delays in order */
+ clk_data->clk_phase_in[timing] = clk_phase[0];
+ clk_data->clk_phase_out[timing] = clk_phase[1];
+}
+
+/**
+ * sdhci_zynqmp_sampleclk_set_phase - Set the SD Input Clock Tap Delays
+ *
+ * @hw: Pointer to the hardware clock structure.
+ * @degrees: The clock phase shift between 0 - 359.
+ *
+ * Set the SD Input Clock Tap Delays for Input path
+ *
+ * Return: 0 on success and error value on error
+ */
+static int arasan_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
+ struct arasan_sdhci_host *sdhci_arasan =
+ container_of(clk_data, struct arasan_sdhci_host, clk_data);
+ struct mci_host *host = &sdhci_arasan->mci;
+ const char *clk_name = clk_hw_get_name(hw);
+ u32 node_id = !strcmp(clk_name, "clk_in_sd0") ? NODE_SD_0 : NODE_SD_1;
+ u8 tap_delay, tap_max = 0;
+ int ret;
+
+ /* This is applicable for SDHCI_SPEC_300 and above */
+ if (sdhci_arasan->sdhci.version < SDHCI_SPEC_300)
+ return 0;
+
+ /* Assert DLL Reset */
+ zynqmp_pm_sd_dll_reset(node_id, PM_DLL_RESET_ASSERT);
+
+ switch (host->timing) {
+ case MMC_TIMING_MMC_HS:
+ case MMC_TIMING_SD_HS:
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ /* For 50MHz clock, 120 Taps are available */
+ tap_max = 120;
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ /* For 100MHz clock, 60 Taps are available */
+ tap_max = 60;
+ break;
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ /* For 200MHz clock, 30 Taps are available */
+ tap_max = 30;
+ break;
+ default:
+ break;
+ }
+
+ tap_delay = (degrees * tap_max) / 360;
+
+ /* Set the Clock Phase */
+ ret = zynqmp_pm_set_sd_tapdelay(node_id, PM_TAPDELAY_INPUT, tap_delay);
+ if (ret)
+ pr_err("Error setting Input Tap Delay\n");
+
+ return ret;
+}
+
+static unsigned long arasan_zynqmp_sampleclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
+ struct arasan_sdhci_host *sdhci_arasan =
+ container_of(clk_data, struct arasan_sdhci_host, clk_data);
+ struct mci_host *host = &sdhci_arasan->mci;
+
+ return host->actual_clock;
+};
+
+/**
+ * sdhci_zynqmp_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
+ *
+ * @hw: Pointer to the hardware clock structure.
+ * @degrees: The clock phase shift between 0 - 359.
+ *
+ * Set the SD Output Clock Tap Delays for Output path
+ *
+ * Return: 0 on success and error value on error
+ */
+static int arasan_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
+ struct arasan_sdhci_host *sdhci_arasan =
+ container_of(clk_data, struct arasan_sdhci_host, clk_data);
+ struct mci_host *host = &sdhci_arasan->mci;
+ const char *clk_name = clk_hw_get_name(hw);
+ u32 node_id = !strcmp(clk_name, "clk_out_sd0") ? NODE_SD_0 : NODE_SD_1;
+ u8 tap_delay, tap_max = 0;
+ int ret;
- /* limit bus widths to controller capabilities */
- if (!(caps & SDHCI_HOSTCAP_8BIT))
- host->mci.host_caps &= ~MMC_CAP_8_BIT_DATA;
+ /* This is applicable for SDHCI_SPEC_300 and above */
+ if (sdhci_arasan->sdhci.version < SDHCI_SPEC_300)
+ return 0;
+
+ switch (host->timing) {
+ case MMC_TIMING_MMC_HS:
+ case MMC_TIMING_SD_HS:
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ /* For 50MHz clock, 30 Taps are available */
+ tap_max = 30;
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ /* For 100MHz clock, 15 Taps are available */
+ tap_max = 15;
+ break;
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ /* For 200MHz clock, 8 Taps are available */
+ tap_max = 8;
+ break;
+ default:
+ break;
+ }
+
+ tap_delay = (degrees * tap_max) / 360;
+
+ /* Set the Clock Phase */
+ ret = zynqmp_pm_set_sd_tapdelay(node_id, PM_TAPDELAY_OUTPUT, tap_delay);
+ if (ret)
+ pr_err("Error setting Output Tap Delay\n");
+
+ /* Release DLL Reset */
+ zynqmp_pm_sd_dll_reset(node_id, PM_DLL_RESET_RELEASE);
+
+ return ret;
}
-static int arasan_sdhci_probe(struct device_d *dev)
+static unsigned long arasan_zynqmp_sdcardclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
{
- struct device_node *np = dev->device_node;
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
+ struct arasan_sdhci_host *sdhci_arasan =
+ container_of(clk_data, struct arasan_sdhci_host, clk_data);
+ struct mci_host *host = &sdhci_arasan->mci;
+
+ return host->actual_clock;
+};
+
+static const struct clk_ops clk_sampleclk_ops = {
+ .recalc_rate = arasan_zynqmp_sampleclk_recalc_rate,
+ .set_phase = arasan_zynqmp_sampleclk_set_phase,
+};
+
+static const struct clk_ops clk_sdcardclk_ops = {
+ .recalc_rate = arasan_zynqmp_sdcardclk_recalc_rate,
+ .set_phase = arasan_zynqmp_sdcardclk_set_phase,
+};
+
+/**
+ * arasan_sdhci_register_sampleclk - Register the sampleclk for a PHY to use
+ *
+ * @sdhci_arasan: Our private data structure.
+ * @clk_xin: Pointer to the functional clock
+ * @dev: Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is. In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int
+arasan_sdhci_register_sampleclk(struct sdhci_arasan_clk_data *clk_data,
+ struct clk *clk_xin,
+ struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct clk_init_data sampleclk_init = {};
+ const char *clk_name;
+ int ret;
+
+ ret = of_property_read_string_index(np, "clock-output-names", 1,
+ &sampleclk_init.name);
+ if (ret) {
+ dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
+ return ret;
+ }
+
+ clk_name = __clk_get_name(clk_xin);
+ sampleclk_init.parent_names = &clk_name;
+ sampleclk_init.num_parents = 1;
+ sampleclk_init.ops = &clk_sampleclk_ops;
+
+ clk_data->sampleclk_hw.init = &sampleclk_init;
+ clk_data->sampleclk = clk_register(dev, &clk_data->sampleclk_hw);
+ if (IS_ERR(clk_data->sampleclk))
+ return PTR_ERR(clk_data->sampleclk);
+ clk_data->sampleclk_hw.init = NULL;
+
+ ret = of_clk_add_provider(np, of_clk_src_simple_get,
+ clk_data->sampleclk);
+ if (ret)
+ dev_err(dev, "Failed to add sample clock provider\n");
+
+ return ret;
+}
+
+/**
+ * sdhci_arasan_register_sdcardclk - Register the sdcardclk for a PHY to use
+ *
+ * @sdhci_arasan: Our private data structure.
+ * @clk_xin: Pointer to the functional clock
+ * @dev: Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is. In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int
+arasan_sdhci_register_sdcardclk(struct sdhci_arasan_clk_data *clk_data,
+ struct clk *clk_xin,
+ struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct clk_init_data sdcardclk_init = {};
+ const char *clk_name;
+ int ret;
+
+ ret = of_property_read_string_index(np, "clock-output-names", 0,
+ &sdcardclk_init.name);
+ if (ret) {
+ dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
+ return ret;
+ }
+
+ clk_name = __clk_get_name(clk_xin);
+ sdcardclk_init.parent_names = &clk_name;
+ sdcardclk_init.ops = &clk_sdcardclk_ops;
+ sdcardclk_init.num_parents = 1;
+
+ clk_data->sdcardclk_hw.init = &sdcardclk_init;
+ clk_data->sdcardclk = clk_register(dev, &clk_data->sdcardclk_hw);
+ if (IS_ERR(clk_data->sdcardclk))
+ return PTR_ERR(clk_data->sdcardclk);
+ clk_data->sdcardclk_hw.init = NULL;
+
+ ret = of_clk_add_provider(np, of_clk_src_simple_get,
+ clk_data->sdcardclk);
+ if (ret)
+ dev_err(dev, "Failed to add sdcard clock provider\n");
+
+ return ret;
+}
+
+/**
+ * arasan_sdhci_register_sdclk - Register the sdcardclk for a PHY to use
+ *
+ * @sdhci_arasan: Our private data structure.
+ * @clk_xin: Pointer to the functional clock
+ * @dev: Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is. In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Note: without seriously re-architecting SDHCI's clock code and testing on
+ * all platforms, there's no way to create a totally beautiful clock here
+ * with all clock ops implemented. Instead, we'll just create a clock that can
+ * be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common clock
+ * framework that we're doing things behind its back. This should be sufficient
+ * to create nice clean device tree bindings and later (if needed) we can try
+ * re-architecting SDHCI if we see some benefit to it.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int arasan_sdhci_register_sdclk(struct sdhci_arasan_clk_data *sdhci_arasan,
+ struct clk *clk_xin,
+ struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ u32 num_clks = 0;
+ int ret;
+
+ /* Providing a clock to the PHY is optional; no error if missing */
+ if (of_property_read_u32(np, "#clock-cells", &num_clks) < 0)
+ return 0;
+
+ ret = arasan_sdhci_register_sdcardclk(sdhci_arasan, clk_xin, dev);
+ if (ret)
+ return ret;
+
+ if (num_clks)
+ return arasan_sdhci_register_sampleclk(sdhci_arasan, clk_xin,
+ dev);
+
+ return 0;
+}
+
+/**
+ * arasan_dt_parse_clk_phases - Read Clock Delay values from DT
+ *
+ * @dev: Pointer to our struct device.
+ * @clk_data: Pointer to the Clock Data structure
+ *
+ * Called at initialization to parse the values of Clock Delays.
+ */
+static void arasan_dt_parse_clk_phases(struct device *dev,
+ struct sdhci_arasan_clk_data *clk_data)
+{
+ u32 mio_bank = 0;
+ int i;
+
+ /*
+ * This has been kept as a pointer and is assigned a function here.
+ * So that different controller variants can assign their own handling
+ * function.
+ */
+ clk_data->set_clk_delays = sdhci_arasan_set_clk_delays;
+
+ if (of_device_is_compatible(dev->of_node, "xlnx,zynqmp-8.9a")) {
+ u32 zynqmp_iclk_phase[ZYNQMP_CLK_PHASES + 1] = ZYNQMP_ICLK_PHASE;
+ u32 zynqmp_oclk_phase[ZYNQMP_CLK_PHASES + 1] = ZYNQMP_OCLK_PHASE;
+
+ of_property_read_u32(dev->of_node, "xlnx,mio-bank", &mio_bank);
+ if (mio_bank == 2) {
+ zynqmp_oclk_phase[ZYNQMP_CLK_PHASE_UHS_SDR104] = 90;
+ zynqmp_oclk_phase[ZYNQMP_CLK_PHASE_HS200] = 90;
+ }
+
+ for (i = 0; i <= ZYNQMP_CLK_PHASES; i++) {
+ clk_data->clk_phase_in[i] = zynqmp_iclk_phase[i];
+ clk_data->clk_phase_out[i] = zynqmp_oclk_phase[i];
+ }
+ }
+
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
+ "clk-phase-legacy");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
+ "clk-phase-mmc-hs");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_SD_HS,
+ "clk-phase-sd-hs");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR12,
+ "clk-phase-uhs-sdr12");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR25,
+ "clk-phase-uhs-sdr25");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR50,
+ "clk-phase-uhs-sdr50");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR104,
+ "clk-phase-uhs-sdr104");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_DDR50,
+ "clk-phase-uhs-ddr50");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_DDR52,
+ "clk-phase-mmc-ddr52");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS200,
+ "clk-phase-mmc-hs200");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS400,
+ "clk-phase-mmc-hs400");
+}
+
+static int arasan_sdhci_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
struct arasan_sdhci_host *arasan_sdhci;
struct clk *clk_xin, *clk_ahb;
struct resource *iores;
@@ -355,7 +715,6 @@ static int arasan_sdhci_probe(struct device_d *dev)
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
- arasan_sdhci->ioaddr = IOMEM(iores->start);
clk_ahb = clk_get(dev, "clk_ahb");
if (IS_ERR(clk_ahb)) {
@@ -387,12 +746,14 @@ static int arasan_sdhci_probe(struct device_d *dev)
if (of_property_read_bool(np, "no-1-8-v"))
arasan_sdhci->quirks |= SDHCI_ARASAN_QUIRK_NO_1_8_V;
- arasan_sdhci->sdhci.read32 = arasan_sdhci_readl;
- arasan_sdhci->sdhci.read16 = arasan_sdhci_readw;
- arasan_sdhci->sdhci.read8 = arasan_sdhci_readb;
- arasan_sdhci->sdhci.write32 = arasan_sdhci_writel;
- arasan_sdhci->sdhci.write16 = arasan_sdhci_writew;
- arasan_sdhci->sdhci.write8 = arasan_sdhci_writeb;
+ if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a")) {
+ if (IS_ENABLED(CONFIG_MCI_TUNING))
+ mci->execute_tuning = arasan_zynqmp_execute_tuning;
+ arasan_sdhci->quirks |= SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN;
+ }
+
+ arasan_sdhci->sdhci.base = IOMEM(iores->start);
+ arasan_sdhci->sdhci.mci = mci;
mci->send_cmd = arasan_sdhci_send_cmd;
mci->set_ios = arasan_sdhci_set_ios;
mci->init = arasan_sdhci_init;
@@ -400,10 +761,26 @@ static int arasan_sdhci_probe(struct device_d *dev)
mci->card_write_protected = arasan_sdhci_card_write_protected;
mci->hw_dev = dev;
- mci->f_max = clk_get_rate(clk_xin);
+ /*
+ * clk_rates on ZynqMP are rounded wrong. For HS200 clk_get_rate retunrs
+ * 199999998 instead of 200000000
+ */
+ if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
+ mci->f_max = 200000000;
+ else
+ mci->f_max = clk_get_rate(clk_xin);
+
mci->f_min = 50000000 / 256;
- arasan_sdhci_set_mci_caps(arasan_sdhci);
+ if (IS_ENABLED(CONFIG_ARCH_ZYNQMP))
+ arasan_sdhci_register_sdclk(&arasan_sdhci->clk_data, clk_xin, dev);
+
+ arasan_dt_parse_clk_phases(dev, &arasan_sdhci->clk_data);
+
+ /* parse board supported bus width capabilities */
+ mci_of_parse(&arasan_sdhci->mci);
+
+ sdhci_setup_host(&arasan_sdhci->sdhci);
dev->priv = arasan_sdhci;
@@ -414,8 +791,9 @@ static __maybe_unused struct of_device_id arasan_sdhci_compatible[] = {
{ .compatible = "arasan,sdhci-8.9a" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, arasan_sdhci_compatible);
-static struct driver_d arasan_sdhci_driver = {
+static struct driver arasan_sdhci_driver = {
.name = "arasan-sdhci",
.probe = arasan_sdhci_probe,
.of_compatible = DRV_OF_COMPAT(arasan_sdhci_compatible),