162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * UFS PHY driver for Samsung EXYNOS SoC
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2020 Samsung Electronics Co., Ltd.
662306a36Sopenharmony_ci * Author: Seungwon Jeon <essuuj@gmail.com>
762306a36Sopenharmony_ci * Author: Alim Akhtar <alim.akhtar@samsung.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#ifndef _PHY_SAMSUNG_UFS_
1162306a36Sopenharmony_ci#define _PHY_SAMSUNG_UFS_
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/phy/phy.h>
1462306a36Sopenharmony_ci#include <linux/regmap.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define PHY_COMN_BLK	1
1762306a36Sopenharmony_ci#define PHY_TRSV_BLK	2
1862306a36Sopenharmony_ci#define END_UFS_PHY_CFG { 0 }
1962306a36Sopenharmony_ci#define PHY_TRSV_CH_OFFSET	0x30
2062306a36Sopenharmony_ci#define PHY_APB_ADDR(off)	((off) << 2)
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define PHY_COMN_REG_CFG(o, v, d) {	\
2362306a36Sopenharmony_ci	.off_0 = PHY_APB_ADDR((o)),	\
2462306a36Sopenharmony_ci	.off_1 = 0,		\
2562306a36Sopenharmony_ci	.val = (v),		\
2662306a36Sopenharmony_ci	.desc = (d),		\
2762306a36Sopenharmony_ci	.id = PHY_COMN_BLK,	\
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define PHY_TRSV_REG_CFG_OFFSET(o, v, d, c) {	\
3162306a36Sopenharmony_ci	.off_0 = PHY_APB_ADDR((o)),	\
3262306a36Sopenharmony_ci	.off_1 = PHY_APB_ADDR((o) + (c)),	\
3362306a36Sopenharmony_ci	.val = (v),		\
3462306a36Sopenharmony_ci	.desc = (d),		\
3562306a36Sopenharmony_ci	.id = PHY_TRSV_BLK,	\
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define PHY_TRSV_REG_CFG(o, v, d)	\
3962306a36Sopenharmony_ci	PHY_TRSV_REG_CFG_OFFSET(o, v, d, PHY_TRSV_CH_OFFSET)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* UFS PHY registers */
4262306a36Sopenharmony_ci#define PHY_PLL_LOCK_STATUS	0x1e
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define PHY_PLL_LOCK_BIT	BIT(5)
4562306a36Sopenharmony_ci#define PHY_CDR_LOCK_BIT	BIT(4)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* description for PHY calibration */
4862306a36Sopenharmony_cienum {
4962306a36Sopenharmony_ci	/* applicable to any */
5062306a36Sopenharmony_ci	PWR_DESC_ANY	= 0,
5162306a36Sopenharmony_ci	/* mode */
5262306a36Sopenharmony_ci	PWR_DESC_PWM	= 1,
5362306a36Sopenharmony_ci	PWR_DESC_HS	= 2,
5462306a36Sopenharmony_ci	/* series */
5562306a36Sopenharmony_ci	PWR_DESC_SER_A	= 1,
5662306a36Sopenharmony_ci	PWR_DESC_SER_B	= 2,
5762306a36Sopenharmony_ci	/* gear */
5862306a36Sopenharmony_ci	PWR_DESC_G1	= 1,
5962306a36Sopenharmony_ci	PWR_DESC_G2	= 2,
6062306a36Sopenharmony_ci	PWR_DESC_G3	= 3,
6162306a36Sopenharmony_ci	/* field mask */
6262306a36Sopenharmony_ci	MD_MASK		= 0x3,
6362306a36Sopenharmony_ci	SR_MASK		= 0x3,
6462306a36Sopenharmony_ci	GR_MASK		= 0x7,
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define PWR_MODE_HS_G1_ANY	PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_ANY)
6862306a36Sopenharmony_ci#define PWR_MODE_HS_G1_SER_A	PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_A)
6962306a36Sopenharmony_ci#define PWR_MODE_HS_G1_SER_B	PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_B)
7062306a36Sopenharmony_ci#define PWR_MODE_HS_G2_ANY	PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_ANY)
7162306a36Sopenharmony_ci#define PWR_MODE_HS_G2_SER_A	PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_A)
7262306a36Sopenharmony_ci#define PWR_MODE_HS_G2_SER_B	PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_B)
7362306a36Sopenharmony_ci#define PWR_MODE_HS_G3_ANY	PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_ANY)
7462306a36Sopenharmony_ci#define PWR_MODE_HS_G3_SER_A	PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_A)
7562306a36Sopenharmony_ci#define PWR_MODE_HS_G3_SER_B	PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_B)
7662306a36Sopenharmony_ci#define PWR_MODE(g, s, m)	((((g) & GR_MASK) << 4) |\
7762306a36Sopenharmony_ci				 (((s) & SR_MASK) << 2) | ((m) & MD_MASK))
7862306a36Sopenharmony_ci#define PWR_MODE_PWM_ANY	PWR_MODE(PWR_DESC_ANY,\
7962306a36Sopenharmony_ci					 PWR_DESC_ANY, PWR_DESC_PWM)
8062306a36Sopenharmony_ci#define PWR_MODE_HS(g, s)	((((g) & GR_MASK) << 4) |\
8162306a36Sopenharmony_ci				 (((s) & SR_MASK) << 2) | PWR_DESC_HS)
8262306a36Sopenharmony_ci#define PWR_MODE_HS_ANY		PWR_MODE(PWR_DESC_ANY,\
8362306a36Sopenharmony_ci					 PWR_DESC_ANY, PWR_DESC_HS)
8462306a36Sopenharmony_ci#define PWR_MODE_ANY		PWR_MODE(PWR_DESC_ANY,\
8562306a36Sopenharmony_ci					 PWR_DESC_ANY, PWR_DESC_ANY)
8662306a36Sopenharmony_ci/* PHY calibration point/state */
8762306a36Sopenharmony_cienum {
8862306a36Sopenharmony_ci	CFG_PRE_INIT,
8962306a36Sopenharmony_ci	CFG_POST_INIT,
9062306a36Sopenharmony_ci	CFG_PRE_PWR_HS,
9162306a36Sopenharmony_ci	CFG_POST_PWR_HS,
9262306a36Sopenharmony_ci	CFG_TAG_MAX,
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistruct samsung_ufs_phy_cfg {
9662306a36Sopenharmony_ci	u32 off_0;
9762306a36Sopenharmony_ci	u32 off_1;
9862306a36Sopenharmony_ci	u32 val;
9962306a36Sopenharmony_ci	u8 desc;
10062306a36Sopenharmony_ci	u8 id;
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistruct samsung_ufs_phy_pmu_isol {
10462306a36Sopenharmony_ci	u32 offset;
10562306a36Sopenharmony_ci	u32 mask;
10662306a36Sopenharmony_ci	u32 en;
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistruct samsung_ufs_phy_drvdata {
11062306a36Sopenharmony_ci	const struct samsung_ufs_phy_cfg **cfgs;
11162306a36Sopenharmony_ci	struct samsung_ufs_phy_pmu_isol isol;
11262306a36Sopenharmony_ci	const char * const *clk_list;
11362306a36Sopenharmony_ci	int num_clks;
11462306a36Sopenharmony_ci	u32 cdr_lock_status_offset;
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistruct samsung_ufs_phy {
11862306a36Sopenharmony_ci	struct device *dev;
11962306a36Sopenharmony_ci	void __iomem *reg_pma;
12062306a36Sopenharmony_ci	struct regmap *reg_pmu;
12162306a36Sopenharmony_ci	struct clk_bulk_data *clks;
12262306a36Sopenharmony_ci	const struct samsung_ufs_phy_drvdata *drvdata;
12362306a36Sopenharmony_ci	const struct samsung_ufs_phy_cfg * const *cfgs;
12462306a36Sopenharmony_ci	struct samsung_ufs_phy_pmu_isol isol;
12562306a36Sopenharmony_ci	u8 lane_cnt;
12662306a36Sopenharmony_ci	int ufs_phy_state;
12762306a36Sopenharmony_ci	enum phy_mode mode;
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic inline struct samsung_ufs_phy *get_samsung_ufs_phy(struct phy *phy)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	return (struct samsung_ufs_phy *)phy_get_drvdata(phy);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic inline void samsung_ufs_phy_ctrl_isol(
13662306a36Sopenharmony_ci		struct samsung_ufs_phy *phy, u32 isol)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	regmap_update_bits(phy->reg_pmu, phy->isol.offset,
13962306a36Sopenharmony_ci			   phy->isol.mask, isol ? 0 : phy->isol.en);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ciextern const struct samsung_ufs_phy_drvdata exynos7_ufs_phy;
14362306a36Sopenharmony_ciextern const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy;
14462306a36Sopenharmony_ciextern const struct samsung_ufs_phy_drvdata fsd_ufs_phy;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci#endif /* _PHY_SAMSUNG_UFS_ */
147