scsi: ufs: ufs-exynos: Introduce ExynosAuto v9 virtual host

Introduce virtual host driver for ExynosAuto v9 UFS mHCI.

The VH (Virtual Host) only supports data transfer functions so most of
physical features must be disabled by setting the following quirks:

 - UFSHCD_QUIRK_BROKEN_UIC_CMD

 - UFSHCD_QUIRK_SKIP_PH_CONFIGURATION

Before initialization, the VH must wait until PH is ready. This wait is
currently implemented via polling.

Link: https://lore.kernel.org/r/20211018124216.153072-15-chanho61.park@samsung.com
Cc: Alim Akhtar <alim.akhtar@samsung.com>
Cc: Kiwoong Kim <kwmad.kim@samsung.com>
Cc: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
Signed-off-by: Chanho Park <chanho61.park@samsung.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Chanho Park 2021-10-18 21:42:15 +09:00 committed by Martin K. Petersen
parent b52aea54b6
commit be39f4fd8d
1 changed files with 86 additions and 0 deletions

View File

@ -121,6 +121,8 @@
#define HCI_MH_ALLOWABLE_TRAN_OF_VH 0x30C
#define HCI_MH_IID_IN_TASK_TAG 0X308
#define PH_READY_TIMEOUT_MS (5 * MSEC_PER_SEC)
enum {
UNIPRO_L1_5 = 0,/* PHY Adapter */
UNIPRO_L2, /* Data Link */
@ -1406,6 +1408,70 @@ static int exynos_ufs_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
return 0;
}
static int exynosauto_ufs_vh_link_startup_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status)
{
if (status == POST_CHANGE) {
ufshcd_set_link_active(hba);
ufshcd_set_ufs_dev_active(hba);
}
return 0;
}
static int exynosauto_ufs_vh_wait_ph_ready(struct ufs_hba *hba)
{
u32 mbox;
ktime_t start, stop;
start = ktime_get();
stop = ktime_add(start, ms_to_ktime(PH_READY_TIMEOUT_MS));
do {
mbox = ufshcd_readl(hba, PH2VH_MBOX);
/* TODO: Mailbox message protocols between the PH and VHs are
* not implemented yet. This will be supported later
*/
if ((mbox & MH_MSG_MASK) == MH_MSG_PH_READY)
return 0;
usleep_range(40, 50);
} while (ktime_before(ktime_get(), stop));
return -ETIME;
}
static int exynosauto_ufs_vh_init(struct ufs_hba *hba)
{
struct device *dev = hba->dev;
struct platform_device *pdev = to_platform_device(dev);
struct exynos_ufs *ufs;
int ret;
ufs = devm_kzalloc(dev, sizeof(*ufs), GFP_KERNEL);
if (!ufs)
return -ENOMEM;
/* exynos-specific hci */
ufs->reg_hci = devm_platform_ioremap_resource_byname(pdev, "vs_hci");
if (IS_ERR(ufs->reg_hci)) {
dev_err(dev, "cannot ioremap for hci vendor register\n");
return PTR_ERR(ufs->reg_hci);
}
ret = exynosauto_ufs_vh_wait_ph_ready(hba);
if (ret)
return ret;
ufs->drv_data = device_get_match_data(dev);
if (!ufs->drv_data)
return -ENODEV;
exynos_ufs_priv_init(hba, ufs);
return 0;
}
static struct ufs_hba_variant_ops ufs_hba_exynos_ops = {
.name = "exynos_ufs",
.init = exynos_ufs_init,
@ -1420,6 +1486,12 @@ static struct ufs_hba_variant_ops ufs_hba_exynos_ops = {
.resume = exynos_ufs_resume,
};
static struct ufs_hba_variant_ops ufs_hba_exynosauto_vh_ops = {
.name = "exynosauto_ufs_vh",
.init = exynosauto_ufs_vh_init,
.link_startup_notify = exynosauto_ufs_vh_link_startup_notify,
};
static int exynos_ufs_probe(struct platform_device *pdev)
{
int err;
@ -1488,6 +1560,18 @@ static struct exynos_ufs_drv_data exynosauto_ufs_drvs = {
.post_pwr_change = exynosauto_ufs_post_pwr_change,
};
static struct exynos_ufs_drv_data exynosauto_ufs_vh_drvs = {
.vops = &ufs_hba_exynosauto_vh_ops,
.quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN |
UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR |
UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR |
UFSHCI_QUIRK_BROKEN_HCE |
UFSHCD_QUIRK_BROKEN_UIC_CMD |
UFSHCD_QUIRK_SKIP_PH_CONFIGURATION |
UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING,
.opts = EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX,
};
static struct exynos_ufs_drv_data exynos_ufs_drvs = {
.uic_attr = &exynos7_uic_attr,
.quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN |
@ -1515,6 +1599,8 @@ static const struct of_device_id exynos_ufs_of_match[] = {
.data = &exynos_ufs_drvs },
{ .compatible = "samsung,exynosautov9-ufs",
.data = &exynosauto_ufs_drvs },
{ .compatible = "samsung,exynosautov9-ufs-vh",
.data = &exynosauto_ufs_vh_drvs },
{},
};