Skip to content

Commit

Permalink
scsi: ufs: ufs-exynos: Support ExynosAuto v9 UFS
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Chanho Park authored and Martin K. Petersen committed Oct 28, 2021
1 parent 52e5035 commit cc52e15
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 0 deletions.
118 changes: 118 additions & 0 deletions drivers/scsi/ufs/ufs-exynos.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
#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"
Expand Down Expand Up @@ -75,6 +77,12 @@
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 */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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 |
Expand All @@ -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 },
{},
};

Expand Down
18 changes: 18 additions & 0 deletions drivers/scsi/ufs/ufs-exynos.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@
#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
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit cc52e15

Please sign in to comment.