/* SPDX-License-Identifier: GPL-2.0-only */ /* * UFS PHY driver for Samsung EXYNOS SoC * * Copyright (C) 2020 Samsung Electronics Co., Ltd. * Author: Seungwon Jeon * Author: Alim Akhtar * */ #ifndef _PHY_SAMSUNG_UFS_ #define _PHY_SAMSUNG_UFS_ #include #include #define PHY_COMN_BLK 1 #define PHY_TRSV_BLK 2 #define END_UFS_PHY_CFG { 0 } #define PHY_TRSV_CH_OFFSET 0x30 #define PHY_APB_ADDR(off) ((off) << 2) #define PHY_COMN_REG_CFG(o, v, d) { \ .off_0 = PHY_APB_ADDR((o)), \ .off_1 = 0, \ .val = (v), \ .desc = (d), \ .id = PHY_COMN_BLK, \ } #define PHY_TRSV_REG_CFG_OFFSET(o, v, d, c) { \ .off_0 = PHY_APB_ADDR((o)), \ .off_1 = PHY_APB_ADDR((o) + (c)), \ .val = (v), \ .desc = (d), \ .id = PHY_TRSV_BLK, \ } #define PHY_TRSV_REG_CFG(o, v, d) \ PHY_TRSV_REG_CFG_OFFSET(o, v, d, PHY_TRSV_CH_OFFSET) /* UFS PHY registers */ #define PHY_PLL_LOCK_STATUS 0x1e #define PHY_PLL_LOCK_BIT BIT(5) #define PHY_CDR_LOCK_BIT BIT(4) /* description for PHY calibration */ enum { /* applicable to any */ PWR_DESC_ANY = 0, /* mode */ PWR_DESC_PWM = 1, PWR_DESC_HS = 2, /* series */ PWR_DESC_SER_A = 1, PWR_DESC_SER_B = 2, /* gear */ PWR_DESC_G1 = 1, PWR_DESC_G2 = 2, PWR_DESC_G3 = 3, /* field mask */ MD_MASK = 0x3, SR_MASK = 0x3, GR_MASK = 0x7, }; #define PWR_MODE_HS_G1_ANY PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_ANY) #define PWR_MODE_HS_G1_SER_A PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_A) #define PWR_MODE_HS_G1_SER_B PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_B) #define PWR_MODE_HS_G2_ANY PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_ANY) #define PWR_MODE_HS_G2_SER_A PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_A) #define PWR_MODE_HS_G2_SER_B PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_B) #define PWR_MODE_HS_G3_ANY PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_ANY) #define PWR_MODE_HS_G3_SER_A PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_A) #define PWR_MODE_HS_G3_SER_B PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_B) #define PWR_MODE(g, s, m) ((((g) & GR_MASK) << 4) |\ (((s) & SR_MASK) << 2) | ((m) & MD_MASK)) #define PWR_MODE_PWM_ANY PWR_MODE(PWR_DESC_ANY,\ PWR_DESC_ANY, PWR_DESC_PWM) #define PWR_MODE_HS(g, s) ((((g) & GR_MASK) << 4) |\ (((s) & SR_MASK) << 2) | PWR_DESC_HS) #define PWR_MODE_HS_ANY PWR_MODE(PWR_DESC_ANY,\ PWR_DESC_ANY, PWR_DESC_HS) #define PWR_MODE_ANY PWR_MODE(PWR_DESC_ANY,\ PWR_DESC_ANY, PWR_DESC_ANY) /* PHY calibration point/state */ enum { CFG_PRE_INIT, CFG_POST_INIT, CFG_PRE_PWR_HS, CFG_POST_PWR_HS, CFG_TAG_MAX, }; struct samsung_ufs_phy_cfg { u32 off_0; u32 off_1; u32 val; u8 desc; u8 id; }; struct samsung_ufs_phy_pmu_isol { u32 offset; u32 mask; u32 en; }; struct samsung_ufs_phy_drvdata { const struct samsung_ufs_phy_cfg **cfgs; struct samsung_ufs_phy_pmu_isol isol; const char * const *clk_list; int num_clks; u32 cdr_lock_status_offset; /* SoC's specific operations */ int (*wait_for_cal)(struct phy *phy, u8 lane); int (*wait_for_cdr)(struct phy *phy, u8 lane); }; struct samsung_ufs_phy { struct device *dev; void __iomem *reg_pma; struct regmap *reg_pmu; struct clk_bulk_data *clks; const struct samsung_ufs_phy_drvdata *drvdata; const struct samsung_ufs_phy_cfg * const *cfgs; struct samsung_ufs_phy_pmu_isol isol; u8 lane_cnt; int ufs_phy_state; enum phy_mode mode; }; static inline struct samsung_ufs_phy *get_samsung_ufs_phy(struct phy *phy) { return (struct samsung_ufs_phy *)phy_get_drvdata(phy); } static inline void samsung_ufs_phy_ctrl_isol( struct samsung_ufs_phy *phy, u32 isol) { regmap_update_bits(phy->reg_pmu, phy->isol.offset, phy->isol.mask, isol ? 0 : phy->isol.en); } int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy, u8 lane); extern const struct samsung_ufs_phy_drvdata exynos7_ufs_phy; extern const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy; extern const struct samsung_ufs_phy_drvdata fsd_ufs_phy; #endif /* _PHY_SAMSUNG_UFS_ */