Commit cc52e153 authored by Chanho Park's avatar Chanho Park Committed by Martin K. Petersen
Browse files

scsi: ufs: ufs-exynos: Support ExynosAuto v9 UFS

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: default avatarChanho Park <chanho61.park@samsung.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 52e5035f
Loading
Loading
Loading
Loading
+118 −0
Original line number Diff line number Diff line
@@ -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"
@@ -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 */
@@ -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 },
	{},
};

+18 −0
Original line number Diff line number Diff line
@@ -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
@@ -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)