scsi: ufs: ufs-exynos: Support ExynosAuto v9 UFS
authorChanho Park <chanho61.park@samsung.com>
Mon, 18 Oct 2021 12:42:12 +0000 (21:42 +0900)
committerMartin K. Petersen <martin.petersen@oracle.com>
Thu, 28 Oct 2021 03:10:12 +0000 (23:10 -0400)
Add support for the ExynosAuto v9 SoC. This requires controlling the UFS IP
shareability register via syscon and regmap.  The offset of the register
can be different according to the UFS instance and SoC specific offset
value. As a result, we need to get the offset value from DT property.

Unlike exynos7, this implementation has a different M-PHY setting which
must be configured via exynosauto_ufs_pre_link.

Link: https://lore.kernel.org/r/20211018124216.153072-12-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>
drivers/scsi/ufs/ufs-exynos.c
drivers/scsi/ufs/ufs-exynos.h

index f7d14cf075c766f8c7e000a8222ecde30d161d9c..3851447e2436362bb43a23c03773d14568b9a08e 100644 (file)
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/mfd/syscon.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
+#include <linux/regmap.h>
 
 #include "ufshcd.h"
 #include "ufshcd-pltfrm.h"
                                 UIC_TRANSPORT_NO_CONNECTION_RX |\
                                 UIC_TRANSPORT_BAD_TC)
 
+/* FSYS UFS Shareability */
+#define UFS_WR_SHARABLE                BIT(2)
+#define UFS_RD_SHARABLE                BIT(1)
+#define UFS_SHARABLE           (UFS_WR_SHARABLE | UFS_RD_SHARABLE)
+#define UFS_SHAREABILITY_OFFSET        0x710
+
 enum {
        UNIPRO_L1_5 = 0,/* PHY Adapter */
        UNIPRO_L2,      /* Data Link */
@@ -150,6 +158,89 @@ static int exynos7_ufs_drv_init(struct device *dev, struct exynos_ufs *ufs)
        return 0;
 }
 
+static int exynosauto_ufs_drv_init(struct device *dev, struct exynos_ufs *ufs)
+{
+       struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
+
+       /* IO Coherency setting */
+       if (ufs->sysreg) {
+               return regmap_update_bits(ufs->sysreg,
+                                         ufs->shareability_reg_offset,
+                                         UFS_SHARABLE, UFS_SHARABLE);
+       }
+
+       attr->tx_dif_p_nsec = 3200000;
+
+       return 0;
+}
+
+static int exynosauto_ufs_pre_link(struct exynos_ufs *ufs)
+{
+       struct ufs_hba *hba = ufs->hba;
+       int i;
+       u32 tx_line_reset_period, rx_line_reset_period;
+
+       rx_line_reset_period = (RX_LINE_RESET_TIME * ufs->mclk_rate) / NSEC_PER_MSEC;
+       tx_line_reset_period = (TX_LINE_RESET_TIME * ufs->mclk_rate) / NSEC_PER_MSEC;
+
+       ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x40);
+       for_each_ufs_rx_lane(ufs, i) {
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD, i),
+                              DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD_EN, i), 0x0);
+
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE2, i),
+                              (rx_line_reset_period >> 16) & 0xFF);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE1, i),
+                              (rx_line_reset_period >> 8) & 0xFF);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE0, i),
+                              (rx_line_reset_period) & 0xFF);
+
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x2f, i), 0x79);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x84, i), 0x1);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x25, i), 0xf6);
+       }
+
+       for_each_ufs_tx_lane(ufs, i) {
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD, i),
+                              DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
+               /* Not to affect VND_TX_LINERESET_PVALUE to VND_TX_CLK_PRD */
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD_EN, i),
+                              0x02);
+
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE2, i),
+                              (tx_line_reset_period >> 16) & 0xFF);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE1, i),
+                              (tx_line_reset_period >> 8) & 0xFF);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE0, i),
+                              (tx_line_reset_period) & 0xFF);
+
+               /* TX PWM Gear Capability / PWM_G1_ONLY */
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x04, i), 0x1);
+       }
+
+       ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x0);
+
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0x0);
+
+       ufshcd_dme_set(hba, UIC_ARG_MIB(0xa011), 0x8000);
+
+       return 0;
+}
+
+static int exynosauto_ufs_pre_pwr_change(struct exynos_ufs *ufs,
+                                        struct ufs_pa_layer_attr *pwr)
+{
+       struct ufs_hba *hba = ufs->hba;
+
+       /* PACP_PWR_req and delivered to the remote DME */
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA0), 12000);
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA1), 32000);
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA2), 16000);
+
+       return 0;
+}
+
 static int exynos7_ufs_pre_link(struct exynos_ufs *ufs)
 {
        struct ufs_hba *hba = ufs->hba;
@@ -932,6 +1023,17 @@ static int exynos_ufs_parse_dt(struct device *dev, struct exynos_ufs *ufs)
                goto out;
        }
 
+       ufs->sysreg = syscon_regmap_lookup_by_phandle(np, "samsung,sysreg");
+       if (IS_ERR(ufs->sysreg))
+               ufs->sysreg = NULL;
+       else {
+               if (of_property_read_u32_index(np, "samsung,sysreg", 1,
+                                              &ufs->shareability_reg_offset)) {
+                       dev_warn(dev, "can't get an offset from sysreg. Set to default value\n");
+                       ufs->shareability_reg_offset = UFS_SHAREABILITY_OFFSET;
+               }
+       }
+
        ufs->pclk_avail_min = PCLK_AVAIL_MIN;
        ufs->pclk_avail_max = PCLK_AVAIL_MAX;
 
@@ -1304,6 +1406,20 @@ static struct exynos_ufs_uic_attr exynos7_uic_attr = {
        .pa_dbg_option_suite            = 0x30103,
 };
 
+static struct exynos_ufs_drv_data exynosauto_ufs_drvs = {
+       .uic_attr               = &exynos7_uic_attr,
+       .quirks                 = UFSHCD_QUIRK_PRDT_BYTE_GRAN |
+                                 UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR |
+                                 UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR |
+                                 UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING,
+       .opts                   = EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL |
+                                 EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR |
+                                 EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX,
+       .drv_init               = exynosauto_ufs_drv_init,
+       .pre_link               = exynosauto_ufs_pre_link,
+       .pre_pwr_change         = exynosauto_ufs_pre_pwr_change,
+};
+
 static struct exynos_ufs_drv_data exynos_ufs_drvs = {
        .uic_attr               = &exynos7_uic_attr,
        .quirks                 = UFSHCD_QUIRK_PRDT_BYTE_GRAN |
@@ -1329,6 +1445,8 @@ static struct exynos_ufs_drv_data exynos_ufs_drvs = {
 static const struct of_device_id exynos_ufs_of_match[] = {
        { .compatible = "samsung,exynos7-ufs",
          .data       = &exynos_ufs_drvs },
+       { .compatible = "samsung,exynosautov9-ufs",
+         .data       = &exynosauto_ufs_drvs },
        {},
 };
 
index 02308faea422826af9e98ffce03e3225191740c3..1c33e5466082bf50e1610bc297449eafb6549725 100644 (file)
 #define TX_GRAN_NVAL_10_08     0x0296
 #define TX_GRAN_NVAL_H(v)      (((v) >> 8) & 0x3)
 
+#define VND_TX_CLK_PRD         0xAA
+#define VND_TX_CLK_PRD_EN      0xA9
+#define VND_TX_LINERESET_PVALUE0       0xAD
+#define VND_TX_LINERESET_PVALUE1       0xAC
+#define VND_TX_LINERESET_PVALUE2       0xAB
+
+#define TX_LINE_RESET_TIME     3200
+
+#define VND_RX_CLK_PRD         0x12
+#define VND_RX_CLK_PRD_EN      0x11
+#define VND_RX_LINERESET_VALUE0        0x1D
+#define VND_RX_LINERESET_VALUE1        0x1C
+#define VND_RX_LINERESET_VALUE2        0x1B
+
+#define RX_LINE_RESET_TIME     1000
+
 #define RX_FILLER_ENABLE       0x0316
 #define RX_FILLER_EN           (1 << 1)
 #define RX_LINERESET_VAL       0x0317
@@ -194,6 +210,8 @@ struct exynos_ufs {
        struct ufs_phy_time_cfg t_cfg;
        ktime_t entry_hibern8_t;
        const struct exynos_ufs_drv_data *drv_data;
+       struct regmap *sysreg;
+       u32 shareability_reg_offset;
 
        u32 opts;
 #define EXYNOS_UFS_OPT_HAS_APB_CLK_CTRL                BIT(0)