diff mbox series

[v4,15/16] scsi: ufs: ufs-exynos: introduce exynosauto v9 virtual host

Message ID 20211007080934.108804-16-chanho61.park@samsung.com (mailing list archive)
State Superseded
Headers show
Series [v4,01/16] scsi: ufs: add quirk to handle broken UIC command | expand

Commit Message

Chanho Park Oct. 7, 2021, 8:09 a.m. UTC
This patch introduces virtual host driver of exynosauto v9 ufs mHCI.
VH(Virtual Host) only supports data transfer functions. So, most of
physical features are broken. So, we need to set below quirks.
- UFSHCD_QUIRK_BROKEN_UIC_CMD
- UFSHCD_QUIRK_SKIP_PH_CONFIGURATION
Before initialization, the VH is necessary to wait until PH is ready.
It's implemented as polling at the moment.

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>
---
 drivers/scsi/ufs/ufs-exynos.c | 84 +++++++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)

Comments

Avri Altman Oct. 14, 2021, 7:03 p.m. UTC | #1
> 
> This patch introduces virtual host driver of exynosauto v9 ufs mHCI.
> VH(Virtual Host) only supports data transfer functions. So, most of physical
> features are broken. So, we need to set below quirks.
> - UFSHCD_QUIRK_BROKEN_UIC_CMD
> - UFSHCD_QUIRK_SKIP_PH_CONFIGURATION
Oh - you set it here.
Maybe just add a comment in your commit log of patches 1 & 2 that you are doing it later.

> Before initialization, the VH is necessary to wait until PH is ready.
> It's implemented as polling at the moment.
> 
> 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>
> ---
>  drivers/scsi/ufs/ufs-exynos.c | 84 +++++++++++++++++++++++++++++++++++
>  1 file changed, 84 insertions(+)
> 
> diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c index
> 32f73c906018..c2b654027b0f 100644
> --- a/drivers/scsi/ufs/ufs-exynos.c
> +++ b/drivers/scsi/ufs/ufs-exynos.c
> @@ -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 */
> @@ -1403,6 +1405,68 @@ 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);
> +               hba->wlun_dev_clr_ua = true;
wlun_dev_clr_ua no longer exists - needs rebase

> +       }
> +
> +       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);
> +               if ((mbox & MH_MSG_MASK) == MH_MSG_PH_READY)
> +                       return 0;
Maybe add a comment here that the mbox protocol will be defined later.

Thanks,
Avri
> +
> +               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,
> @@ -1417,6 +1481,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;
> @@ -1485,6 +1555,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 |
> @@ -1512,6 +1594,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 },
>         {},
>  };
> 
> --
> 2.33.0
Chanho Park Oct. 15, 2021, 12:13 p.m. UTC | #2
> > +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);
> > +               hba->wlun_dev_clr_ua = true;
> wlun_dev_clr_ua no longer exists - needs rebase

I found the commit. I'll remove this next patch.

> 
> > +       }
> > +
> > +       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);
> > +               if ((mbox & MH_MSG_MASK) == MH_MSG_PH_READY)
> > +                       return 0;
> Maybe add a comment here that the mbox protocol will be defined later.

Put some comments with TODO: tag.

Best Regards,
Chanho Park
diff mbox series

Patch

diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c
index 32f73c906018..c2b654027b0f 100644
--- a/drivers/scsi/ufs/ufs-exynos.c
+++ b/drivers/scsi/ufs/ufs-exynos.c
@@ -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 */
@@ -1403,6 +1405,68 @@  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);
+		hba->wlun_dev_clr_ua = true;
+	}
+
+	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);
+		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,
@@ -1417,6 +1481,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;
@@ -1485,6 +1555,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 |
@@ -1512,6 +1594,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 },
 	{},
 };