diff options
Diffstat (limited to 'drivers/phy/samsung')
-rw-r--r-- | drivers/phy/samsung/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/samsung/phy-exynos7-ufs.c | 12 | ||||
-rw-r--r-- | drivers/phy/samsung/phy-exynosautov9-ufs.c | 29 | ||||
-rw-r--r-- | drivers/phy/samsung/phy-fsd-ufs.c | 63 | ||||
-rw-r--r-- | drivers/phy/samsung/phy-samsung-ufs.c | 138 | ||||
-rw-r--r-- | drivers/phy/samsung/phy-samsung-ufs.h | 34 |
6 files changed, 158 insertions, 119 deletions
diff --git a/drivers/phy/samsung/Makefile b/drivers/phy/samsung/Makefile index 65e4cc59403f..afb34a153e34 100644 --- a/drivers/phy/samsung/Makefile +++ b/drivers/phy/samsung/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_PHY_SAMSUNG_UFS) += phy-exynos-ufs.o phy-exynos-ufs-y += phy-samsung-ufs.o phy-exynos-ufs-y += phy-exynos7-ufs.o phy-exynos-ufs-y += phy-exynosautov9-ufs.o +phy-exynos-ufs-y += phy-fsd-ufs.o obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o phy-exynos-usb2-y += phy-samsung-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o diff --git a/drivers/phy/samsung/phy-exynos7-ufs.c b/drivers/phy/samsung/phy-exynos7-ufs.c index 7c9008e163db..a982e7c128c5 100644 --- a/drivers/phy/samsung/phy-exynos7-ufs.c +++ b/drivers/phy/samsung/phy-exynos7-ufs.c @@ -11,6 +11,8 @@ #define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1 #define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0) +#define EXYNOS7_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS 0x5e + /* Calibration for phy initialization */ static const struct samsung_ufs_phy_cfg exynos7_pre_init_cfg[] = { PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_ANY), @@ -66,12 +68,18 @@ static const struct samsung_ufs_phy_cfg *exynos7_ufs_phy_cfgs[CFG_TAG_MAX] = { [CFG_POST_PWR_HS] = exynos7_post_pwr_hs_cfg, }; +static const char * const exynos7_ufs_phy_clks[] = { + "tx0_symbol_clk", "rx0_symbol_clk", "rx1_symbol_clk", "ref_clk", +}; + const struct samsung_ufs_phy_drvdata exynos7_ufs_phy = { - .cfg = exynos7_ufs_phy_cfgs, + .cfgs = exynos7_ufs_phy_cfgs, .isol = { .offset = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL, .mask = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_MASK, .en = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_EN, }, - .has_symbol_clk = 1, + .clk_list = exynos7_ufs_phy_clks, + .num_clks = ARRAY_SIZE(exynos7_ufs_phy_clks), + .cdr_lock_status_offset = EXYNOS7_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS, }; diff --git a/drivers/phy/samsung/phy-exynosautov9-ufs.c b/drivers/phy/samsung/phy-exynosautov9-ufs.c index 36398a15c2db..49e2bcbef0b4 100644 --- a/drivers/phy/samsung/phy-exynosautov9-ufs.c +++ b/drivers/phy/samsung/phy-exynosautov9-ufs.c @@ -10,6 +10,7 @@ #define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL 0x728 #define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1 #define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0) +#define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS 0x5e #define PHY_TRSV_REG_CFG_AUTOV9(o, v, d) \ PHY_TRSV_REG_CFG_OFFSET(o, v, d, 0x50) @@ -31,22 +32,22 @@ static const struct samsung_ufs_phy_cfg exynosautov9_pre_init_cfg[] = { PHY_COMN_REG_CFG(0x023, 0xc0, PWR_MODE_ANY), PHY_COMN_REG_CFG(0x023, 0x00, PWR_MODE_ANY), - PHY_TRSV_REG_CFG(0x042, 0x5d, PWR_MODE_ANY), - PHY_TRSV_REG_CFG(0x043, 0x80, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x042, 0x5d, PWR_MODE_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x043, 0x80, PWR_MODE_ANY), END_UFS_PHY_CFG, }; /* Calibration for HS mode series A/B */ static const struct samsung_ufs_phy_cfg exynosautov9_pre_pwr_hs_cfg[] = { - PHY_TRSV_REG_CFG(0x032, 0xbc, PWR_MODE_HS_ANY), - PHY_TRSV_REG_CFG(0x03c, 0x7f, PWR_MODE_HS_ANY), - PHY_TRSV_REG_CFG(0x048, 0xc0, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x032, 0xbc, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x03c, 0x7f, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG_AUTOV9(0x048, 0xc0, PWR_MODE_HS_ANY), - PHY_TRSV_REG_CFG(0x04a, 0x00, PWR_MODE_HS_G3_SER_B), - PHY_TRSV_REG_CFG(0x04b, 0x10, PWR_MODE_HS_G1_SER_B | - PWR_MODE_HS_G3_SER_B), - PHY_TRSV_REG_CFG(0x04d, 0x63, PWR_MODE_HS_G3_SER_B), + PHY_TRSV_REG_CFG_AUTOV9(0x04a, 0x00, PWR_MODE_HS_G3_SER_B), + PHY_TRSV_REG_CFG_AUTOV9(0x04b, 0x10, PWR_MODE_HS_G1_SER_B | + PWR_MODE_HS_G3_SER_B), + PHY_TRSV_REG_CFG_AUTOV9(0x04d, 0x63, PWR_MODE_HS_G3_SER_B), END_UFS_PHY_CFG, }; @@ -56,12 +57,18 @@ static const struct samsung_ufs_phy_cfg *exynosautov9_ufs_phy_cfgs[CFG_TAG_MAX] [CFG_PRE_PWR_HS] = exynosautov9_pre_pwr_hs_cfg, }; +static const char * const exynosautov9_ufs_phy_clks[] = { + "ref_clk", +}; + const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy = { - .cfg = exynosautov9_ufs_phy_cfgs, + .cfgs = exynosautov9_ufs_phy_cfgs, .isol = { .offset = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL, .mask = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_MASK, .en = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_EN, }, - .has_symbol_clk = 0, + .clk_list = exynosautov9_ufs_phy_clks, + .num_clks = ARRAY_SIZE(exynosautov9_ufs_phy_clks), + .cdr_lock_status_offset = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS, }; diff --git a/drivers/phy/samsung/phy-fsd-ufs.c b/drivers/phy/samsung/phy-fsd-ufs.c new file mode 100644 index 000000000000..d36cabd53434 --- /dev/null +++ b/drivers/phy/samsung/phy-fsd-ufs.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * UFS PHY driver data for FSD SoC + * + * Copyright (C) 2022 Samsung Electronics Co., Ltd. + * + */ +#include "phy-samsung-ufs.h" + +#define FSD_EMBEDDED_COMBO_PHY_CTRL 0x724 +#define FSD_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1 +#define FSD_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0) +#define FSD_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS 0x6e + +static const struct samsung_ufs_phy_cfg fsd_pre_init_cfg[] = { + PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x017, 0x94, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x035, 0x58, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x037, 0x40, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x03b, 0x83, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x04c, 0x5b, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x05c, 0x14, PWR_MODE_ANY), + END_UFS_PHY_CFG +}; + +/* Calibration for HS mode series A/B */ +static const struct samsung_ufs_phy_cfg fsd_pre_pwr_hs_cfg[] = { + END_UFS_PHY_CFG +}; + +/* Calibration for HS mode series A/B atfer PMC */ +static const struct samsung_ufs_phy_cfg fsd_post_pwr_hs_cfg[] = { + END_UFS_PHY_CFG +}; + +static const struct samsung_ufs_phy_cfg *fsd_ufs_phy_cfgs[CFG_TAG_MAX] = { + [CFG_PRE_INIT] = fsd_pre_init_cfg, + [CFG_PRE_PWR_HS] = fsd_pre_pwr_hs_cfg, + [CFG_POST_PWR_HS] = fsd_post_pwr_hs_cfg, +}; + +static const char * const fsd_ufs_phy_clks[] = { + "ref_clk", +}; + +const struct samsung_ufs_phy_drvdata fsd_ufs_phy = { + .cfgs = fsd_ufs_phy_cfgs, + .isol = { + .offset = FSD_EMBEDDED_COMBO_PHY_CTRL, + .mask = FSD_EMBEDDED_COMBO_PHY_CTRL_MASK, + .en = FSD_EMBEDDED_COMBO_PHY_CTRL_EN, + }, + .clk_list = fsd_ufs_phy_clks, + .num_clks = ARRAY_SIZE(fsd_ufs_phy_clks), + .cdr_lock_status_offset = FSD_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS, +}; diff --git a/drivers/phy/samsung/phy-samsung-ufs.c b/drivers/phy/samsung/phy-samsung-ufs.c index 602ddef259eb..183c88e3d1ec 100644 --- a/drivers/phy/samsung/phy-samsung-ufs.c +++ b/drivers/phy/samsung/phy-samsung-ufs.c @@ -63,7 +63,8 @@ static int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy) } err = readl_poll_timeout( - ufs_phy->reg_pma + PHY_APB_ADDR(PHY_CDR_LOCK_STATUS), + ufs_phy->reg_pma + + PHY_APB_ADDR(ufs_phy->drvdata->cdr_lock_status_offset), val, (val & PHY_CDR_LOCK_BIT), sleep_us, timeout_us); if (err) dev_err(ufs_phy->dev, @@ -75,7 +76,7 @@ out: static int samsung_ufs_phy_calibrate(struct phy *phy) { struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy); - struct samsung_ufs_phy_cfg **cfgs = ufs_phy->cfg; + const struct samsung_ufs_phy_cfg * const *cfgs = ufs_phy->cfgs; const struct samsung_ufs_phy_cfg *cfg; int err = 0; int i; @@ -130,113 +131,63 @@ out: return err; } -static int samsung_ufs_phy_symbol_clk_init(struct samsung_ufs_phy *phy) +static int samsung_ufs_phy_clks_init(struct samsung_ufs_phy *phy) { - int ret; - - phy->tx0_symbol_clk = devm_clk_get(phy->dev, "tx0_symbol_clk"); - if (IS_ERR(phy->tx0_symbol_clk)) { - dev_err(phy->dev, "failed to get tx0_symbol_clk clock\n"); - return PTR_ERR(phy->tx0_symbol_clk); - } + int i; + const struct samsung_ufs_phy_drvdata *drvdata = phy->drvdata; + int num_clks = drvdata->num_clks; - phy->rx0_symbol_clk = devm_clk_get(phy->dev, "rx0_symbol_clk"); - if (IS_ERR(phy->rx0_symbol_clk)) { - dev_err(phy->dev, "failed to get rx0_symbol_clk clock\n"); - return PTR_ERR(phy->rx0_symbol_clk); - } + phy->clks = devm_kcalloc(phy->dev, num_clks, sizeof(*phy->clks), + GFP_KERNEL); + if (!phy->clks) + return -ENOMEM; - phy->rx1_symbol_clk = devm_clk_get(phy->dev, "rx1_symbol_clk"); - if (IS_ERR(phy->rx1_symbol_clk)) { - dev_err(phy->dev, "failed to get rx1_symbol_clk clock\n"); - return PTR_ERR(phy->rx1_symbol_clk); - } + for (i = 0; i < num_clks; i++) + phy->clks[i].id = drvdata->clk_list[i]; - ret = clk_prepare_enable(phy->tx0_symbol_clk); - if (ret) { - dev_err(phy->dev, "%s: tx0_symbol_clk enable failed %d\n", __func__, ret); - goto out; - } + return devm_clk_bulk_get(phy->dev, num_clks, phy->clks); +} - ret = clk_prepare_enable(phy->rx0_symbol_clk); - if (ret) { - dev_err(phy->dev, "%s: rx0_symbol_clk enable failed %d\n", __func__, ret); - goto out_disable_tx0_clk; - } +static int samsung_ufs_phy_init(struct phy *phy) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); - ret = clk_prepare_enable(phy->rx1_symbol_clk); - if (ret) { - dev_err(phy->dev, "%s: rx1_symbol_clk enable failed %d\n", __func__, ret); - goto out_disable_rx0_clk; - } + ss_phy->lane_cnt = phy->attrs.bus_width; + ss_phy->ufs_phy_state = CFG_PRE_INIT; return 0; - -out_disable_rx0_clk: - clk_disable_unprepare(phy->rx0_symbol_clk); -out_disable_tx0_clk: - clk_disable_unprepare(phy->tx0_symbol_clk); -out: - return ret; } -static int samsung_ufs_phy_clks_init(struct samsung_ufs_phy *phy) +static int samsung_ufs_phy_power_on(struct phy *phy) { + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); int ret; - phy->ref_clk = devm_clk_get(phy->dev, "ref_clk"); - if (IS_ERR(phy->ref_clk)) - dev_err(phy->dev, "failed to get ref_clk clock\n"); + samsung_ufs_phy_ctrl_isol(ss_phy, false); - ret = clk_prepare_enable(phy->ref_clk); + ret = clk_bulk_prepare_enable(ss_phy->drvdata->num_clks, ss_phy->clks); if (ret) { - dev_err(phy->dev, "%s: ref_clk enable failed %d\n", __func__, ret); + dev_err(ss_phy->dev, "failed to enable ufs phy clocks\n"); return ret; } - dev_dbg(phy->dev, "UFS MPHY ref_clk_rate = %ld\n", clk_get_rate(phy->ref_clk)); - - return 0; -} - -static int samsung_ufs_phy_init(struct phy *phy) -{ - struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); - int ret; - - ss_phy->lane_cnt = phy->attrs.bus_width; - ss_phy->ufs_phy_state = CFG_PRE_INIT; - - if (ss_phy->drvdata->has_symbol_clk) { - ret = samsung_ufs_phy_symbol_clk_init(ss_phy); + if (ss_phy->ufs_phy_state == CFG_PRE_INIT) { + ret = samsung_ufs_phy_calibrate(phy); if (ret) - dev_err(ss_phy->dev, "failed to set ufs phy symbol clocks\n"); + dev_err(ss_phy->dev, "ufs phy calibration failed\n"); } - ret = samsung_ufs_phy_clks_init(ss_phy); - if (ret) - dev_err(ss_phy->dev, "failed to set ufs phy clocks\n"); - - ret = samsung_ufs_phy_calibrate(phy); - if (ret) - dev_err(ss_phy->dev, "ufs phy calibration failed\n"); - return ret; } -static int samsung_ufs_phy_power_on(struct phy *phy) -{ - struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); - - samsung_ufs_phy_ctrl_isol(ss_phy, false); - return 0; -} - static int samsung_ufs_phy_power_off(struct phy *phy) { struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); + clk_bulk_disable_unprepare(ss_phy->drvdata->num_clks, ss_phy->clks); + samsung_ufs_phy_ctrl_isol(ss_phy, true); + return 0; } @@ -257,13 +208,7 @@ static int samsung_ufs_phy_exit(struct phy *phy) { struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); - clk_disable_unprepare(ss_phy->ref_clk); - - if (ss_phy->drvdata->has_symbol_clk) { - clk_disable_unprepare(ss_phy->tx0_symbol_clk); - clk_disable_unprepare(ss_phy->rx0_symbol_clk); - clk_disable_unprepare(ss_phy->rx1_symbol_clk); - } + ss_phy->ufs_phy_state = CFG_TAG_MAX; return 0; } @@ -288,6 +233,7 @@ static int samsung_ufs_phy_probe(struct platform_device *pdev) struct phy *gen_phy; struct phy_provider *phy_provider; const struct samsung_ufs_phy_drvdata *drvdata; + u32 isol_offset; int err = 0; match = of_match_node(samsung_ufs_phy_match, dev->of_node); @@ -327,10 +273,21 @@ static int samsung_ufs_phy_probe(struct platform_device *pdev) drvdata = match->data; phy->dev = dev; phy->drvdata = drvdata; - phy->cfg = (struct samsung_ufs_phy_cfg **)drvdata->cfg; - phy->isol = &drvdata->isol; + phy->cfgs = drvdata->cfgs; + memcpy(&phy->isol, &drvdata->isol, sizeof(phy->isol)); + + if (!of_property_read_u32_index(dev->of_node, "samsung,pmu-syscon", 1, + &isol_offset)) + phy->isol.offset = isol_offset; + phy->lane_cnt = PHY_DEF_LANE_CNT; + err = samsung_ufs_phy_clks_init(phy); + if (err) { + dev_err(dev, "failed to get phy clocks\n"); + goto out; + } + phy_set_drvdata(gen_phy, phy); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); @@ -350,6 +307,9 @@ static const struct of_device_id samsung_ufs_phy_match[] = { }, { .compatible = "samsung,exynosautov9-ufs-phy", .data = &exynosautov9_ufs_phy, + }, { + .compatible = "tesla,fsd-ufs-phy", + .data = &fsd_ufs_phy, }, {}, }; diff --git a/drivers/phy/samsung/phy-samsung-ufs.h b/drivers/phy/samsung/phy-samsung-ufs.h index 91a0e9f94f98..e122960cfee8 100644 --- a/drivers/phy/samsung/phy-samsung-ufs.h +++ b/drivers/phy/samsung/phy-samsung-ufs.h @@ -40,7 +40,6 @@ /* UFS PHY registers */ #define PHY_PLL_LOCK_STATUS 0x1e -#define PHY_CDR_LOCK_STATUS 0x5e #define PHY_PLL_LOCK_BIT BIT(5) #define PHY_CDR_LOCK_BIT BIT(4) @@ -101,28 +100,28 @@ struct samsung_ufs_phy_cfg { 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 **cfg; - struct pmu_isol { - u32 offset; - u32 mask; - u32 en; - } isol; - bool has_symbol_clk; + 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; }; struct samsung_ufs_phy { struct device *dev; void __iomem *reg_pma; struct regmap *reg_pmu; - struct clk *ref_clk; - struct clk *ref_clk_parent; - struct clk *tx0_symbol_clk; - struct clk *rx0_symbol_clk; - struct clk *rx1_symbol_clk; + struct clk_bulk_data *clks; const struct samsung_ufs_phy_drvdata *drvdata; - struct samsung_ufs_phy_cfg **cfg; - const struct pmu_isol *isol; + 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; @@ -136,11 +135,12 @@ static inline struct samsung_ufs_phy *get_samsung_ufs_phy(struct phy *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); + regmap_update_bits(phy->reg_pmu, phy->isol.offset, + phy->isol.mask, isol ? 0 : phy->isol.en); } 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_ */ |