mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-26 21:13:48 +00:00
035d56f238
The DLL mode supported SD reference clocks are 50 MHz, 100 MHz and 200 MHz. When user select SD frequency as 200MHz in the design, the actual frequency is going to come around ~187MHz (<= 200MHz considering the parent clock and divisor selection). We need to set SDx_BASECLK as 200 in this case, setting 187 will result in tuning failures in mmc. Set SDx_BASECLK to exact value of 200, 100 or 50 based on the frequency range. Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@xilinx.com> Signed-off-by: Michal Simek <michal.simek@xilinx.com> Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com> Link: https://lore.kernel.org/r/6c1e5eeeedd2864a0c85e6b409d182031d8c6c1a.1648210268.git.michal.simek@xilinx.com
943 lines
24 KiB
C
943 lines
24 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2013 - 2015 Xilinx, Inc.
|
|
*
|
|
* Xilinx Zynq SD Host Controller Interface
|
|
*/
|
|
|
|
#include <clk.h>
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <fdtdec.h>
|
|
#include <linux/delay.h>
|
|
#include "mmc_private.h"
|
|
#include <log.h>
|
|
#include <reset.h>
|
|
#include <dm/device_compat.h>
|
|
#include <linux/err.h>
|
|
#include <linux/libfdt.h>
|
|
#include <asm/types.h>
|
|
#include <linux/math64.h>
|
|
#include <asm/cache.h>
|
|
#include <malloc.h>
|
|
#include <sdhci.h>
|
|
#include <zynqmp_firmware.h>
|
|
|
|
#define SDHCI_ARASAN_ITAPDLY_REGISTER 0xF0F8
|
|
#define SDHCI_ARASAN_ITAPDLY_SEL_MASK GENMASK(7, 0)
|
|
#define SDHCI_ARASAN_OTAPDLY_REGISTER 0xF0FC
|
|
#define SDHCI_ARASAN_OTAPDLY_SEL_MASK GENMASK(5, 0)
|
|
#define SDHCI_ITAPDLY_CHGWIN BIT(9)
|
|
#define SDHCI_ITAPDLY_ENABLE BIT(8)
|
|
#define SDHCI_OTAPDLY_ENABLE BIT(6)
|
|
|
|
#define SDHCI_TUNING_LOOP_COUNT 40
|
|
#define MMC_BANK2 0x2
|
|
|
|
#define SD_DLL_CTRL 0xFF180358
|
|
#define SD_ITAP_DLY 0xFF180314
|
|
#define SD_OTAP_DLY 0xFF180318
|
|
#define SD0_DLL_RST BIT(2)
|
|
#define SD1_DLL_RST BIT(18)
|
|
#define SD0_ITAPCHGWIN BIT(9)
|
|
#define SD1_ITAPCHGWIN BIT(25)
|
|
#define SD0_ITAPDLYENA BIT(8)
|
|
#define SD1_ITAPDLYENA BIT(24)
|
|
#define SD0_ITAPDLYSEL_MASK GENMASK(7, 0)
|
|
#define SD1_ITAPDLYSEL_MASK GENMASK(23, 16)
|
|
#define SD0_OTAPDLYSEL_MASK GENMASK(5, 0)
|
|
#define SD1_OTAPDLYSEL_MASK GENMASK(21, 16)
|
|
|
|
struct arasan_sdhci_clk_data {
|
|
int clk_phase_in[MMC_TIMING_MMC_HS400 + 1];
|
|
int clk_phase_out[MMC_TIMING_MMC_HS400 + 1];
|
|
};
|
|
|
|
struct arasan_sdhci_plat {
|
|
struct mmc_config cfg;
|
|
struct mmc mmc;
|
|
};
|
|
|
|
struct arasan_sdhci_priv {
|
|
struct sdhci_host *host;
|
|
struct arasan_sdhci_clk_data clk_data;
|
|
u8 deviceid;
|
|
u8 bank;
|
|
u8 no_1p8;
|
|
struct reset_ctl_bulk resets;
|
|
};
|
|
|
|
/* For Versal platforms zynqmp_mmio_write() won't be available */
|
|
__weak int zynqmp_mmio_write(const u32 address, const u32 mask, const u32 value)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
__weak int xilinx_pm_request(u32 api_id, u32 arg0, u32 arg1, u32 arg2,
|
|
u32 arg3, u32 *ret_payload)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_ARCH_ZYNQMP) || defined(CONFIG_ARCH_VERSAL)
|
|
/* Default settings for ZynqMP Clock Phases */
|
|
static const u32 zynqmp_iclk_phases[] = {0, 63, 63, 0, 63, 0,
|
|
0, 183, 54, 0, 0};
|
|
static const u32 zynqmp_oclk_phases[] = {0, 72, 60, 0, 60, 72,
|
|
135, 48, 72, 135, 0};
|
|
|
|
/* Default settings for Versal Clock Phases */
|
|
static const u32 versal_iclk_phases[] = {0, 132, 132, 0, 132,
|
|
0, 0, 162, 90, 0, 0};
|
|
static const u32 versal_oclk_phases[] = {0, 60, 48, 0, 48, 72,
|
|
90, 36, 60, 90, 0};
|
|
|
|
static const u8 mode2timing[] = {
|
|
[MMC_LEGACY] = MMC_TIMING_LEGACY,
|
|
[MMC_HS] = MMC_TIMING_MMC_HS,
|
|
[SD_HS] = MMC_TIMING_SD_HS,
|
|
[MMC_HS_52] = MMC_TIMING_UHS_SDR50,
|
|
[MMC_DDR_52] = MMC_TIMING_UHS_DDR50,
|
|
[UHS_SDR12] = MMC_TIMING_UHS_SDR12,
|
|
[UHS_SDR25] = MMC_TIMING_UHS_SDR25,
|
|
[UHS_SDR50] = MMC_TIMING_UHS_SDR50,
|
|
[UHS_DDR50] = MMC_TIMING_UHS_DDR50,
|
|
[UHS_SDR104] = MMC_TIMING_UHS_SDR104,
|
|
[MMC_HS_200] = MMC_TIMING_MMC_HS200,
|
|
};
|
|
|
|
static inline int arasan_zynqmp_set_in_tapdelay(u8 node_id, u32 itap_delay)
|
|
{
|
|
int ret;
|
|
|
|
if (IS_ENABLED(CONFIG_SPL_BUILD) || current_el() == 3) {
|
|
if (node_id == NODE_SD_0) {
|
|
ret = zynqmp_mmio_write(SD_ITAP_DLY, SD0_ITAPCHGWIN,
|
|
SD0_ITAPCHGWIN);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = zynqmp_mmio_write(SD_ITAP_DLY, SD0_ITAPDLYENA,
|
|
SD0_ITAPDLYENA);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = zynqmp_mmio_write(SD_ITAP_DLY, SD0_ITAPDLYSEL_MASK,
|
|
itap_delay);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = zynqmp_mmio_write(SD_ITAP_DLY, SD0_ITAPCHGWIN, 0);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
ret = zynqmp_mmio_write(SD_ITAP_DLY, SD1_ITAPCHGWIN,
|
|
SD1_ITAPCHGWIN);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = zynqmp_mmio_write(SD_ITAP_DLY, SD1_ITAPDLYENA,
|
|
SD1_ITAPDLYENA);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = zynqmp_mmio_write(SD_ITAP_DLY, SD1_ITAPDLYSEL_MASK,
|
|
(itap_delay << 16));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = zynqmp_mmio_write(SD_ITAP_DLY, SD1_ITAPCHGWIN, 0);
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
return xilinx_pm_request(PM_IOCTL, (u32)node_id,
|
|
IOCTL_SET_SD_TAPDELAY,
|
|
PM_TAPDELAY_INPUT, itap_delay, NULL);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int arasan_zynqmp_set_out_tapdelay(u8 node_id, u32 otap_delay)
|
|
{
|
|
if (IS_ENABLED(CONFIG_SPL_BUILD) || current_el() == 3) {
|
|
if (node_id == NODE_SD_0)
|
|
return zynqmp_mmio_write(SD_OTAP_DLY,
|
|
SD0_OTAPDLYSEL_MASK,
|
|
otap_delay);
|
|
|
|
return zynqmp_mmio_write(SD_OTAP_DLY, SD1_OTAPDLYSEL_MASK,
|
|
(otap_delay << 16));
|
|
} else {
|
|
return xilinx_pm_request(PM_IOCTL, (u32)node_id,
|
|
IOCTL_SET_SD_TAPDELAY,
|
|
PM_TAPDELAY_OUTPUT, otap_delay, NULL);
|
|
}
|
|
}
|
|
|
|
static inline int zynqmp_dll_reset(u8 node_id, u32 type)
|
|
{
|
|
if (IS_ENABLED(CONFIG_SPL_BUILD) || current_el() == 3) {
|
|
if (node_id == NODE_SD_0)
|
|
return zynqmp_mmio_write(SD_DLL_CTRL, SD0_DLL_RST,
|
|
type == PM_DLL_RESET_ASSERT ?
|
|
SD0_DLL_RST : 0);
|
|
|
|
return zynqmp_mmio_write(SD_DLL_CTRL, SD1_DLL_RST,
|
|
type == PM_DLL_RESET_ASSERT ?
|
|
SD1_DLL_RST : 0);
|
|
} else {
|
|
return xilinx_pm_request(PM_IOCTL, (u32)node_id,
|
|
IOCTL_SD_DLL_RESET, type, 0, NULL);
|
|
}
|
|
}
|
|
|
|
static int arasan_zynqmp_dll_reset(struct sdhci_host *host, u8 node_id)
|
|
{
|
|
struct mmc *mmc = (struct mmc *)host->mmc;
|
|
struct udevice *dev = mmc->dev;
|
|
unsigned long timeout;
|
|
int ret;
|
|
u16 clk;
|
|
|
|
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
|
clk &= ~(SDHCI_CLOCK_CARD_EN);
|
|
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
|
|
|
/* Issue DLL Reset */
|
|
ret = zynqmp_dll_reset(node_id, PM_DLL_RESET_ASSERT);
|
|
if (ret) {
|
|
dev_err(dev, "dll_reset assert failed with err: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Allow atleast 1ms delay for proper DLL reset */
|
|
mdelay(1);
|
|
ret = zynqmp_dll_reset(node_id, PM_DLL_RESET_RELEASE);
|
|
if (ret) {
|
|
dev_err(dev, "dll_reset release failed with err: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Wait max 20 ms */
|
|
timeout = 100;
|
|
while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
|
|
& SDHCI_CLOCK_INT_STABLE)) {
|
|
if (timeout == 0) {
|
|
dev_err(dev, ": Internal clock never stabilised.\n");
|
|
return -EBUSY;
|
|
}
|
|
timeout--;
|
|
udelay(1000);
|
|
}
|
|
|
|
clk |= SDHCI_CLOCK_CARD_EN;
|
|
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int arasan_sdhci_execute_tuning(struct mmc *mmc, u8 opcode)
|
|
{
|
|
struct mmc_cmd cmd;
|
|
struct mmc_data data;
|
|
u32 ctrl;
|
|
struct sdhci_host *host;
|
|
struct arasan_sdhci_priv *priv = dev_get_priv(mmc->dev);
|
|
char tuning_loop_counter = SDHCI_TUNING_LOOP_COUNT;
|
|
u8 node_id = priv->deviceid ? NODE_SD_1 : NODE_SD_0;
|
|
|
|
dev_dbg(mmc->dev, "%s\n", __func__);
|
|
|
|
host = priv->host;
|
|
|
|
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
ctrl |= SDHCI_CTRL_EXEC_TUNING;
|
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
|
|
mdelay(1);
|
|
|
|
arasan_zynqmp_dll_reset(host, node_id);
|
|
|
|
sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE);
|
|
sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE);
|
|
|
|
do {
|
|
cmd.cmdidx = opcode;
|
|
cmd.resp_type = MMC_RSP_R1;
|
|
cmd.cmdarg = 0;
|
|
|
|
data.blocksize = 64;
|
|
data.blocks = 1;
|
|
data.flags = MMC_DATA_READ;
|
|
|
|
if (tuning_loop_counter-- == 0)
|
|
break;
|
|
|
|
if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200 &&
|
|
mmc->bus_width == 8)
|
|
data.blocksize = 128;
|
|
|
|
sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG,
|
|
data.blocksize),
|
|
SDHCI_BLOCK_SIZE);
|
|
sdhci_writew(host, data.blocks, SDHCI_BLOCK_COUNT);
|
|
sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE);
|
|
|
|
mmc_send_cmd(mmc, &cmd, NULL);
|
|
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
|
|
if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK)
|
|
udelay(1);
|
|
|
|
} while (ctrl & SDHCI_CTRL_EXEC_TUNING);
|
|
|
|
if (tuning_loop_counter < 0) {
|
|
ctrl &= ~SDHCI_CTRL_TUNED_CLK;
|
|
sdhci_writel(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
}
|
|
|
|
if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
|
|
printf("%s:Tuning failed\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
udelay(1);
|
|
arasan_zynqmp_dll_reset(host, node_id);
|
|
|
|
/* Enable only interrupts served by the SD controller */
|
|
sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK,
|
|
SDHCI_INT_ENABLE);
|
|
/* Mask all sdhci interrupt sources */
|
|
sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* sdhci_zynqmp_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
|
|
*
|
|
* @host: Pointer to the sdhci_host structure.
|
|
* @degrees: The clock phase shift between 0 - 359.
|
|
* Return: 0
|
|
*
|
|
* Set the SD Output Clock Tap Delays for Output path
|
|
*/
|
|
static int sdhci_zynqmp_sdcardclk_set_phase(struct sdhci_host *host,
|
|
int degrees)
|
|
{
|
|
struct mmc *mmc = (struct mmc *)host->mmc;
|
|
struct udevice *dev = mmc->dev;
|
|
struct arasan_sdhci_priv *priv = dev_get_priv(mmc->dev);
|
|
u8 node_id = priv->deviceid ? NODE_SD_1 : NODE_SD_0;
|
|
u8 tap_delay, tap_max = 0;
|
|
int timing = mode2timing[mmc->selected_mode];
|
|
int ret;
|
|
|
|
/*
|
|
* This is applicable for SDHCI_SPEC_300 and above
|
|
* ZynqMP does not set phase for <=25MHz clock.
|
|
* If degrees is zero, no need to do anything.
|
|
*/
|
|
if (SDHCI_GET_VERSION(host) < SDHCI_SPEC_300)
|
|
return 0;
|
|
|
|
switch (timing) {
|
|
case MMC_TIMING_MMC_HS:
|
|
case MMC_TIMING_SD_HS:
|
|
case MMC_TIMING_UHS_SDR25:
|
|
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;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
tap_delay = (degrees * tap_max) / 360;
|
|
|
|
/* Limit output tap_delay value to 6 bits */
|
|
tap_delay &= SDHCI_ARASAN_OTAPDLY_SEL_MASK;
|
|
|
|
/* Set the Clock Phase */
|
|
ret = arasan_zynqmp_set_out_tapdelay(node_id, tap_delay);
|
|
if (ret) {
|
|
dev_err(dev, "Error setting output Tap Delay\n");
|
|
return ret;
|
|
}
|
|
|
|
/* Release DLL Reset */
|
|
ret = zynqmp_dll_reset(node_id, PM_DLL_RESET_RELEASE);
|
|
if (ret) {
|
|
dev_err(dev, "dll_reset release failed with err: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* sdhci_zynqmp_sampleclk_set_phase - Set the SD Input Clock Tap Delays
|
|
*
|
|
* @host: Pointer to the sdhci_host structure.
|
|
* @degrees: The clock phase shift between 0 - 359.
|
|
* Return: 0
|
|
*
|
|
* Set the SD Input Clock Tap Delays for Input path
|
|
*/
|
|
static int sdhci_zynqmp_sampleclk_set_phase(struct sdhci_host *host,
|
|
int degrees)
|
|
{
|
|
struct mmc *mmc = (struct mmc *)host->mmc;
|
|
struct udevice *dev = mmc->dev;
|
|
struct arasan_sdhci_priv *priv = dev_get_priv(mmc->dev);
|
|
u8 node_id = priv->deviceid ? NODE_SD_1 : NODE_SD_0;
|
|
u8 tap_delay, tap_max = 0;
|
|
int timing = mode2timing[mmc->selected_mode];
|
|
int ret;
|
|
|
|
/*
|
|
* This is applicable for SDHCI_SPEC_300 and above
|
|
* ZynqMP does not set phase for <=25MHz clock.
|
|
* If degrees is zero, no need to do anything.
|
|
*/
|
|
if (SDHCI_GET_VERSION(host) < SDHCI_SPEC_300)
|
|
return 0;
|
|
|
|
/* Assert DLL Reset */
|
|
ret = zynqmp_dll_reset(node_id, PM_DLL_RESET_ASSERT);
|
|
if (ret) {
|
|
dev_err(dev, "dll_reset assert failed with err: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
switch (timing) {
|
|
case MMC_TIMING_MMC_HS:
|
|
case MMC_TIMING_SD_HS:
|
|
case MMC_TIMING_UHS_SDR25:
|
|
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;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
tap_delay = (degrees * tap_max) / 360;
|
|
|
|
/* Limit input tap_delay value to 8 bits */
|
|
tap_delay &= SDHCI_ARASAN_ITAPDLY_SEL_MASK;
|
|
|
|
ret = arasan_zynqmp_set_in_tapdelay(node_id, tap_delay);
|
|
if (ret) {
|
|
dev_err(dev, "Error setting Input Tap Delay\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* sdhci_versal_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
|
|
*
|
|
* @host: Pointer to the sdhci_host structure.
|
|
* @degrees: The clock phase shift between 0 - 359.
|
|
* Return: 0
|
|
*
|
|
* Set the SD Output Clock Tap Delays for Output path
|
|
*/
|
|
static int sdhci_versal_sdcardclk_set_phase(struct sdhci_host *host,
|
|
int degrees)
|
|
{
|
|
struct mmc *mmc = (struct mmc *)host->mmc;
|
|
u8 tap_delay, tap_max = 0;
|
|
int timing = mode2timing[mmc->selected_mode];
|
|
u32 regval;
|
|
|
|
/*
|
|
* This is applicable for SDHCI_SPEC_300 and above
|
|
* Versal does not set phase for <=25MHz clock.
|
|
* If degrees is zero, no need to do anything.
|
|
*/
|
|
if (SDHCI_GET_VERSION(host) < SDHCI_SPEC_300)
|
|
return 0;
|
|
|
|
switch (timing) {
|
|
case MMC_TIMING_MMC_HS:
|
|
case MMC_TIMING_SD_HS:
|
|
case MMC_TIMING_UHS_SDR25:
|
|
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;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
tap_delay = (degrees * tap_max) / 360;
|
|
|
|
/* Limit output tap_delay value to 6 bits */
|
|
tap_delay &= SDHCI_ARASAN_OTAPDLY_SEL_MASK;
|
|
|
|
/* Set the Clock Phase */
|
|
regval = sdhci_readl(host, SDHCI_ARASAN_OTAPDLY_REGISTER);
|
|
regval |= SDHCI_OTAPDLY_ENABLE;
|
|
sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
|
|
regval &= ~SDHCI_ARASAN_OTAPDLY_SEL_MASK;
|
|
regval |= tap_delay;
|
|
sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* sdhci_versal_sampleclk_set_phase - Set the SD Input Clock Tap Delays
|
|
*
|
|
* @host: Pointer to the sdhci_host structure.
|
|
* @degrees: The clock phase shift between 0 - 359.
|
|
* Return: 0
|
|
*
|
|
* Set the SD Input Clock Tap Delays for Input path
|
|
*/
|
|
static int sdhci_versal_sampleclk_set_phase(struct sdhci_host *host,
|
|
int degrees)
|
|
{
|
|
struct mmc *mmc = (struct mmc *)host->mmc;
|
|
u8 tap_delay, tap_max = 0;
|
|
int timing = mode2timing[mmc->selected_mode];
|
|
u32 regval;
|
|
|
|
/*
|
|
* This is applicable for SDHCI_SPEC_300 and above
|
|
* Versal does not set phase for <=25MHz clock.
|
|
* If degrees is zero, no need to do anything.
|
|
*/
|
|
if (SDHCI_GET_VERSION(host) < SDHCI_SPEC_300)
|
|
return 0;
|
|
|
|
switch (timing) {
|
|
case MMC_TIMING_MMC_HS:
|
|
case MMC_TIMING_SD_HS:
|
|
case MMC_TIMING_UHS_SDR25:
|
|
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;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
tap_delay = (degrees * tap_max) / 360;
|
|
|
|
/* Limit input tap_delay value to 8 bits */
|
|
tap_delay &= SDHCI_ARASAN_ITAPDLY_SEL_MASK;
|
|
|
|
/* Set the Clock Phase */
|
|
regval = sdhci_readl(host, SDHCI_ARASAN_ITAPDLY_REGISTER);
|
|
regval |= SDHCI_ITAPDLY_CHGWIN;
|
|
sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
|
|
regval |= SDHCI_ITAPDLY_ENABLE;
|
|
sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
|
|
regval &= ~SDHCI_ARASAN_ITAPDLY_SEL_MASK;
|
|
regval |= tap_delay;
|
|
sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
|
|
regval &= ~SDHCI_ITAPDLY_CHGWIN;
|
|
sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int arasan_sdhci_set_tapdelay(struct sdhci_host *host)
|
|
{
|
|
struct arasan_sdhci_priv *priv = dev_get_priv(host->mmc->dev);
|
|
struct arasan_sdhci_clk_data *clk_data = &priv->clk_data;
|
|
struct mmc *mmc = (struct mmc *)host->mmc;
|
|
struct udevice *dev = mmc->dev;
|
|
u8 timing = mode2timing[mmc->selected_mode];
|
|
u32 iclk_phase = clk_data->clk_phase_in[timing];
|
|
u32 oclk_phase = clk_data->clk_phase_out[timing];
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s, host:%s, mode:%d\n", __func__, host->name, timing);
|
|
|
|
if (IS_ENABLED(CONFIG_ARCH_ZYNQMP) &&
|
|
device_is_compatible(dev, "xlnx,zynqmp-8.9a")) {
|
|
ret = sdhci_zynqmp_sampleclk_set_phase(host, iclk_phase);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = sdhci_zynqmp_sdcardclk_set_phase(host, oclk_phase);
|
|
if (ret)
|
|
return ret;
|
|
} else if (IS_ENABLED(CONFIG_ARCH_VERSAL) &&
|
|
device_is_compatible(dev, "xlnx,versal-8.9a")) {
|
|
ret = sdhci_versal_sampleclk_set_phase(host, iclk_phase);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = sdhci_versal_sdcardclk_set_phase(host, oclk_phase);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void arasan_dt_read_clk_phase(struct udevice *dev, unsigned char timing,
|
|
const char *prop)
|
|
{
|
|
struct arasan_sdhci_priv *priv = dev_get_priv(dev);
|
|
struct arasan_sdhci_clk_data *clk_data = &priv->clk_data;
|
|
u32 clk_phase[2] = {0};
|
|
|
|
/*
|
|
* Read Tap Delay values from DT, if the DT does not contain the
|
|
* Tap Values then use the pre-defined values
|
|
*/
|
|
if (dev_read_u32_array(dev, prop, &clk_phase[0], 2)) {
|
|
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;
|
|
}
|
|
|
|
/* 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];
|
|
}
|
|
|
|
/**
|
|
* arasan_dt_parse_clk_phases - Read Tap Delay values from DT
|
|
*
|
|
* @dev: Pointer to our struct udevice.
|
|
*
|
|
* Called at initialization to parse the values of Tap Delays.
|
|
*/
|
|
static void arasan_dt_parse_clk_phases(struct udevice *dev)
|
|
{
|
|
struct arasan_sdhci_priv *priv = dev_get_priv(dev);
|
|
struct arasan_sdhci_clk_data *clk_data = &priv->clk_data;
|
|
int i;
|
|
|
|
if (IS_ENABLED(CONFIG_ARCH_ZYNQMP) &&
|
|
device_is_compatible(dev, "xlnx,zynqmp-8.9a")) {
|
|
for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
|
|
clk_data->clk_phase_in[i] = zynqmp_iclk_phases[i];
|
|
clk_data->clk_phase_out[i] = zynqmp_oclk_phases[i];
|
|
}
|
|
|
|
if (priv->bank == MMC_BANK2) {
|
|
clk_data->clk_phase_out[MMC_TIMING_UHS_SDR104] = 90;
|
|
clk_data->clk_phase_out[MMC_TIMING_MMC_HS200] = 90;
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_ARCH_VERSAL) &&
|
|
device_is_compatible(dev, "xlnx,versal-8.9a")) {
|
|
for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
|
|
clk_data->clk_phase_in[i] = versal_iclk_phases[i];
|
|
clk_data->clk_phase_out[i] = versal_oclk_phases[i];
|
|
}
|
|
}
|
|
|
|
arasan_dt_read_clk_phase(dev, MMC_TIMING_LEGACY,
|
|
"clk-phase-legacy");
|
|
arasan_dt_read_clk_phase(dev, MMC_TIMING_MMC_HS,
|
|
"clk-phase-mmc-hs");
|
|
arasan_dt_read_clk_phase(dev, MMC_TIMING_SD_HS,
|
|
"clk-phase-sd-hs");
|
|
arasan_dt_read_clk_phase(dev, MMC_TIMING_UHS_SDR12,
|
|
"clk-phase-uhs-sdr12");
|
|
arasan_dt_read_clk_phase(dev, MMC_TIMING_UHS_SDR25,
|
|
"clk-phase-uhs-sdr25");
|
|
arasan_dt_read_clk_phase(dev, MMC_TIMING_UHS_SDR50,
|
|
"clk-phase-uhs-sdr50");
|
|
arasan_dt_read_clk_phase(dev, MMC_TIMING_UHS_SDR104,
|
|
"clk-phase-uhs-sdr104");
|
|
arasan_dt_read_clk_phase(dev, MMC_TIMING_UHS_DDR50,
|
|
"clk-phase-uhs-ddr50");
|
|
arasan_dt_read_clk_phase(dev, MMC_TIMING_MMC_DDR52,
|
|
"clk-phase-mmc-ddr52");
|
|
arasan_dt_read_clk_phase(dev, MMC_TIMING_MMC_HS200,
|
|
"clk-phase-mmc-hs200");
|
|
arasan_dt_read_clk_phase(dev, MMC_TIMING_MMC_HS400,
|
|
"clk-phase-mmc-hs400");
|
|
}
|
|
|
|
static const struct sdhci_ops arasan_ops = {
|
|
.platform_execute_tuning = &arasan_sdhci_execute_tuning,
|
|
.set_delay = &arasan_sdhci_set_tapdelay,
|
|
.set_control_reg = &sdhci_set_control_reg,
|
|
};
|
|
#endif
|
|
|
|
#if defined(CONFIG_ARCH_ZYNQMP)
|
|
static int sdhci_zynqmp_set_dynamic_config(struct arasan_sdhci_priv *priv,
|
|
struct udevice *dev)
|
|
{
|
|
int ret;
|
|
u32 node_id = priv->deviceid ? NODE_SD_1 : NODE_SD_0;
|
|
struct clk clk;
|
|
unsigned long clock, mhz;
|
|
|
|
ret = xilinx_pm_request(PM_REQUEST_NODE, node_id, ZYNQMP_PM_CAPABILITY_ACCESS,
|
|
ZYNQMP_PM_MAX_QOS, ZYNQMP_PM_REQUEST_ACK_NO, NULL);
|
|
if (ret) {
|
|
dev_err(dev, "Request node failed for %d\n", node_id);
|
|
return ret;
|
|
}
|
|
|
|
ret = reset_get_bulk(dev, &priv->resets);
|
|
if (ret == -ENOTSUPP || ret == -ENOENT) {
|
|
dev_err(dev, "Reset not found\n");
|
|
return 0;
|
|
} else if (ret) {
|
|
dev_err(dev, "Reset failed\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = reset_assert_bulk(&priv->resets);
|
|
if (ret) {
|
|
dev_err(dev, "Reset assert failed\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_FIXED, 0);
|
|
if (ret) {
|
|
dev_err(dev, "SD_CONFIG_FIXED failed\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_EMMC_SEL,
|
|
dev_read_bool(dev, "non-removable"));
|
|
if (ret) {
|
|
dev_err(dev, "SD_CONFIG_EMMC_SEL failed\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = clk_get_by_index(dev, 0, &clk);
|
|
if (ret < 0) {
|
|
dev_err(dev, "failed to get clock\n");
|
|
return ret;
|
|
}
|
|
|
|
clock = clk_get_rate(&clk);
|
|
if (IS_ERR_VALUE(clock)) {
|
|
dev_err(dev, "failed to get rate\n");
|
|
return clock;
|
|
}
|
|
|
|
mhz = DIV64_U64_ROUND_UP(clock, 1000000);
|
|
|
|
if (mhz > 100 && mhz <= 200)
|
|
mhz = 200;
|
|
else if (mhz > 50 && mhz <= 100)
|
|
mhz = 100;
|
|
else if (mhz > 25 && mhz <= 50)
|
|
mhz = 50;
|
|
else
|
|
mhz = 25;
|
|
|
|
ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_BASECLK, mhz);
|
|
if (ret) {
|
|
dev_err(dev, "SD_CONFIG_BASECLK failed\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_8BIT,
|
|
(dev_read_u32_default(dev, "bus-width", 1) == 8));
|
|
if (ret) {
|
|
dev_err(dev, "SD_CONFIG_8BIT failed\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = reset_deassert_bulk(&priv->resets);
|
|
if (ret) {
|
|
dev_err(dev, "Reset release failed\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int arasan_sdhci_probe(struct udevice *dev)
|
|
{
|
|
struct arasan_sdhci_plat *plat = dev_get_plat(dev);
|
|
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
|
|
struct arasan_sdhci_priv *priv = dev_get_priv(dev);
|
|
struct sdhci_host *host;
|
|
struct clk clk;
|
|
unsigned long clock;
|
|
int ret;
|
|
|
|
host = priv->host;
|
|
|
|
#if defined(CONFIG_ARCH_ZYNQMP)
|
|
if (device_is_compatible(dev, "xlnx,zynqmp-8.9a")) {
|
|
ret = zynqmp_pm_is_function_supported(PM_IOCTL,
|
|
IOCTL_SET_SD_CONFIG);
|
|
if (!ret) {
|
|
ret = sdhci_zynqmp_set_dynamic_config(priv, dev);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ret = clk_get_by_index(dev, 0, &clk);
|
|
if (ret < 0) {
|
|
dev_err(dev, "failed to get clock\n");
|
|
return ret;
|
|
}
|
|
|
|
clock = clk_get_rate(&clk);
|
|
if (IS_ERR_VALUE(clock)) {
|
|
dev_err(dev, "failed to get rate\n");
|
|
return clock;
|
|
}
|
|
|
|
dev_dbg(dev, "%s: CLK %ld\n", __func__, clock);
|
|
|
|
ret = clk_enable(&clk);
|
|
if (ret) {
|
|
dev_err(dev, "failed to enable clock\n");
|
|
return ret;
|
|
}
|
|
|
|
host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD |
|
|
SDHCI_QUIRK_BROKEN_R1B;
|
|
|
|
#ifdef CONFIG_ZYNQ_HISPD_BROKEN
|
|
host->quirks |= SDHCI_QUIRK_BROKEN_HISPD_MODE;
|
|
#endif
|
|
|
|
if (priv->no_1p8)
|
|
host->quirks |= SDHCI_QUIRK_NO_1_8_V;
|
|
|
|
plat->cfg.f_max = CONFIG_ZYNQ_SDHCI_MAX_FREQ;
|
|
|
|
ret = mmc_of_parse(dev, &plat->cfg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
host->max_clk = clock;
|
|
|
|
host->mmc = &plat->mmc;
|
|
host->mmc->dev = dev;
|
|
host->mmc->priv = host;
|
|
|
|
ret = sdhci_setup_cfg(&plat->cfg, host, plat->cfg.f_max,
|
|
CONFIG_ZYNQ_SDHCI_MIN_FREQ);
|
|
if (ret)
|
|
return ret;
|
|
upriv->mmc = host->mmc;
|
|
|
|
/*
|
|
* WORKAROUND: Versal platforms have an issue with card detect state.
|
|
* Due to this, host controller is switching off voltage to sd card
|
|
* causing sd card timeout error. Workaround this by adding a wait for
|
|
* 1000msec till the card detect state gets stable.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_ARCH_ZYNQMP) || IS_ENABLED(CONFIG_ARCH_VERSAL)) {
|
|
u32 timeout = 1000000;
|
|
|
|
while (((sdhci_readl(host, SDHCI_PRESENT_STATE) &
|
|
SDHCI_CARD_STATE_STABLE) == 0) && timeout) {
|
|
udelay(1);
|
|
timeout--;
|
|
}
|
|
if (!timeout) {
|
|
dev_err(dev, "Sdhci card detect state not stable\n");
|
|
return -ETIMEDOUT;
|
|
}
|
|
}
|
|
|
|
return sdhci_probe(dev);
|
|
}
|
|
|
|
static int arasan_sdhci_of_to_plat(struct udevice *dev)
|
|
{
|
|
struct arasan_sdhci_priv *priv = dev_get_priv(dev);
|
|
|
|
priv->host = calloc(1, sizeof(struct sdhci_host));
|
|
if (!priv->host)
|
|
return -1;
|
|
|
|
priv->host->name = dev->name;
|
|
|
|
#if defined(CONFIG_ARCH_ZYNQMP) || defined(CONFIG_ARCH_VERSAL)
|
|
priv->host->ops = &arasan_ops;
|
|
arasan_dt_parse_clk_phases(dev);
|
|
#endif
|
|
|
|
priv->host->ioaddr = (void *)dev_read_addr(dev);
|
|
if (IS_ERR(priv->host->ioaddr))
|
|
return PTR_ERR(priv->host->ioaddr);
|
|
|
|
priv->deviceid = dev_read_u32_default(dev, "xlnx,device_id", -1);
|
|
priv->bank = dev_read_u32_default(dev, "xlnx,mio-bank", 0);
|
|
priv->no_1p8 = dev_read_bool(dev, "no-1-8-v");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int arasan_sdhci_bind(struct udevice *dev)
|
|
{
|
|
struct arasan_sdhci_plat *plat = dev_get_plat(dev);
|
|
|
|
return sdhci_bind(dev, &plat->mmc, &plat->cfg);
|
|
}
|
|
|
|
static const struct udevice_id arasan_sdhci_ids[] = {
|
|
{ .compatible = "arasan,sdhci-8.9a" },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(arasan_sdhci_drv) = {
|
|
.name = "arasan_sdhci",
|
|
.id = UCLASS_MMC,
|
|
.of_match = arasan_sdhci_ids,
|
|
.of_to_plat = arasan_sdhci_of_to_plat,
|
|
.ops = &sdhci_ops,
|
|
.bind = arasan_sdhci_bind,
|
|
.probe = arasan_sdhci_probe,
|
|
.priv_auto = sizeof(struct arasan_sdhci_priv),
|
|
.plat_auto = sizeof(struct arasan_sdhci_plat),
|
|
};
|