18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * UFS PHY driver for Samsung SoC
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2020 Samsung Electronics Co., Ltd.
68c2ecf20Sopenharmony_ci * Author: Seungwon Jeon <essuuj@gmail.com>
78c2ecf20Sopenharmony_ci * Author: Alim Akhtar <alim.akhtar@samsung.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/err.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
168c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/phy/phy.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci#include <linux/regmap.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "phy-samsung-ufs.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define for_each_phy_lane(phy, i) \
258c2ecf20Sopenharmony_ci	for (i = 0; i < (phy)->lane_cnt; i++)
268c2ecf20Sopenharmony_ci#define for_each_phy_cfg(cfg) \
278c2ecf20Sopenharmony_ci	for (; (cfg)->id; (cfg)++)
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define PHY_DEF_LANE_CNT	1
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic void samsung_ufs_phy_config(struct samsung_ufs_phy *phy,
328c2ecf20Sopenharmony_ci				   const struct samsung_ufs_phy_cfg *cfg,
338c2ecf20Sopenharmony_ci				   u8 lane)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	enum {LANE_0, LANE_1}; /* lane index */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	switch (lane) {
388c2ecf20Sopenharmony_ci	case LANE_0:
398c2ecf20Sopenharmony_ci		writel(cfg->val, (phy)->reg_pma + cfg->off_0);
408c2ecf20Sopenharmony_ci		break;
418c2ecf20Sopenharmony_ci	case LANE_1:
428c2ecf20Sopenharmony_ci		if (cfg->id == PHY_TRSV_BLK)
438c2ecf20Sopenharmony_ci			writel(cfg->val, (phy)->reg_pma + cfg->off_1);
448c2ecf20Sopenharmony_ci		break;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy);
518c2ecf20Sopenharmony_ci	const unsigned int timeout_us = 100000;
528c2ecf20Sopenharmony_ci	const unsigned int sleep_us = 10;
538c2ecf20Sopenharmony_ci	u32 val;
548c2ecf20Sopenharmony_ci	int err;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	err = readl_poll_timeout(
578c2ecf20Sopenharmony_ci			ufs_phy->reg_pma + PHY_APB_ADDR(PHY_PLL_LOCK_STATUS),
588c2ecf20Sopenharmony_ci			val, (val & PHY_PLL_LOCK_BIT), sleep_us, timeout_us);
598c2ecf20Sopenharmony_ci	if (err) {
608c2ecf20Sopenharmony_ci		dev_err(ufs_phy->dev,
618c2ecf20Sopenharmony_ci			"failed to get phy pll lock acquisition %d\n", err);
628c2ecf20Sopenharmony_ci		goto out;
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	err = readl_poll_timeout(
668c2ecf20Sopenharmony_ci			ufs_phy->reg_pma + PHY_APB_ADDR(PHY_CDR_LOCK_STATUS),
678c2ecf20Sopenharmony_ci			val, (val & PHY_CDR_LOCK_BIT), sleep_us, timeout_us);
688c2ecf20Sopenharmony_ci	if (err)
698c2ecf20Sopenharmony_ci		dev_err(ufs_phy->dev,
708c2ecf20Sopenharmony_ci			"failed to get phy cdr lock acquisition %d\n", err);
718c2ecf20Sopenharmony_ciout:
728c2ecf20Sopenharmony_ci	return err;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic int samsung_ufs_phy_calibrate(struct phy *phy)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy);
788c2ecf20Sopenharmony_ci	struct samsung_ufs_phy_cfg **cfgs = ufs_phy->cfg;
798c2ecf20Sopenharmony_ci	const struct samsung_ufs_phy_cfg *cfg;
808c2ecf20Sopenharmony_ci	int err = 0;
818c2ecf20Sopenharmony_ci	int i;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (unlikely(ufs_phy->ufs_phy_state < CFG_PRE_INIT ||
848c2ecf20Sopenharmony_ci		     ufs_phy->ufs_phy_state >= CFG_TAG_MAX)) {
858c2ecf20Sopenharmony_ci		dev_err(ufs_phy->dev, "invalid phy config index %d\n", ufs_phy->ufs_phy_state);
868c2ecf20Sopenharmony_ci		return -EINVAL;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	cfg = cfgs[ufs_phy->ufs_phy_state];
908c2ecf20Sopenharmony_ci	if (!cfg)
918c2ecf20Sopenharmony_ci		goto out;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	for_each_phy_cfg(cfg) {
948c2ecf20Sopenharmony_ci		for_each_phy_lane(ufs_phy, i) {
958c2ecf20Sopenharmony_ci			samsung_ufs_phy_config(ufs_phy, cfg, i);
968c2ecf20Sopenharmony_ci		}
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (ufs_phy->ufs_phy_state == CFG_POST_PWR_HS)
1008c2ecf20Sopenharmony_ci		err = samsung_ufs_phy_wait_for_lock_acq(phy);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/**
1038c2ecf20Sopenharmony_ci	 * In Samsung ufshci, PHY need to be calibrated at different
1048c2ecf20Sopenharmony_ci	 * stages / state mainly before Linkstartup, after Linkstartup,
1058c2ecf20Sopenharmony_ci	 * before power mode change and after power mode change.
1068c2ecf20Sopenharmony_ci	 * Below state machine to make sure to calibrate PHY in each
1078c2ecf20Sopenharmony_ci	 * state. Here after configuring PHY in a given state, will
1088c2ecf20Sopenharmony_ci	 * change the state to next state so that next state phy
1098c2ecf20Sopenharmony_ci	 * calibration value can be programed
1108c2ecf20Sopenharmony_ci	 */
1118c2ecf20Sopenharmony_ciout:
1128c2ecf20Sopenharmony_ci	switch (ufs_phy->ufs_phy_state) {
1138c2ecf20Sopenharmony_ci	case CFG_PRE_INIT:
1148c2ecf20Sopenharmony_ci		ufs_phy->ufs_phy_state = CFG_POST_INIT;
1158c2ecf20Sopenharmony_ci		break;
1168c2ecf20Sopenharmony_ci	case CFG_POST_INIT:
1178c2ecf20Sopenharmony_ci		ufs_phy->ufs_phy_state = CFG_PRE_PWR_HS;
1188c2ecf20Sopenharmony_ci		break;
1198c2ecf20Sopenharmony_ci	case CFG_PRE_PWR_HS:
1208c2ecf20Sopenharmony_ci		ufs_phy->ufs_phy_state = CFG_POST_PWR_HS;
1218c2ecf20Sopenharmony_ci		break;
1228c2ecf20Sopenharmony_ci	case CFG_POST_PWR_HS:
1238c2ecf20Sopenharmony_ci		/* Change back to INIT state */
1248c2ecf20Sopenharmony_ci		ufs_phy->ufs_phy_state = CFG_PRE_INIT;
1258c2ecf20Sopenharmony_ci		break;
1268c2ecf20Sopenharmony_ci	default:
1278c2ecf20Sopenharmony_ci		dev_err(ufs_phy->dev, "wrong state for phy calibration\n");
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return err;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic int samsung_ufs_phy_symbol_clk_init(struct samsung_ufs_phy *phy)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	int ret;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	phy->tx0_symbol_clk = devm_clk_get(phy->dev, "tx0_symbol_clk");
1388c2ecf20Sopenharmony_ci	if (IS_ERR(phy->tx0_symbol_clk)) {
1398c2ecf20Sopenharmony_ci		dev_err(phy->dev, "failed to get tx0_symbol_clk clock\n");
1408c2ecf20Sopenharmony_ci		return PTR_ERR(phy->tx0_symbol_clk);
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	phy->rx0_symbol_clk = devm_clk_get(phy->dev, "rx0_symbol_clk");
1448c2ecf20Sopenharmony_ci	if (IS_ERR(phy->rx0_symbol_clk)) {
1458c2ecf20Sopenharmony_ci		dev_err(phy->dev, "failed to get rx0_symbol_clk clock\n");
1468c2ecf20Sopenharmony_ci		return PTR_ERR(phy->rx0_symbol_clk);
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	phy->rx1_symbol_clk = devm_clk_get(phy->dev, "rx1_symbol_clk");
1508c2ecf20Sopenharmony_ci	if (IS_ERR(phy->rx1_symbol_clk)) {
1518c2ecf20Sopenharmony_ci		dev_err(phy->dev, "failed to get rx1_symbol_clk clock\n");
1528c2ecf20Sopenharmony_ci		return PTR_ERR(phy->rx1_symbol_clk);
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(phy->tx0_symbol_clk);
1568c2ecf20Sopenharmony_ci	if (ret) {
1578c2ecf20Sopenharmony_ci		dev_err(phy->dev, "%s: tx0_symbol_clk enable failed %d\n", __func__, ret);
1588c2ecf20Sopenharmony_ci		goto out;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(phy->rx0_symbol_clk);
1628c2ecf20Sopenharmony_ci	if (ret) {
1638c2ecf20Sopenharmony_ci		dev_err(phy->dev, "%s: rx0_symbol_clk enable failed %d\n", __func__, ret);
1648c2ecf20Sopenharmony_ci		goto out_disable_tx0_clk;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(phy->rx1_symbol_clk);
1688c2ecf20Sopenharmony_ci	if (ret) {
1698c2ecf20Sopenharmony_ci		dev_err(phy->dev, "%s: rx1_symbol_clk enable failed %d\n", __func__, ret);
1708c2ecf20Sopenharmony_ci		goto out_disable_rx0_clk;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ciout_disable_rx0_clk:
1768c2ecf20Sopenharmony_ci	clk_disable_unprepare(phy->rx0_symbol_clk);
1778c2ecf20Sopenharmony_ciout_disable_tx0_clk:
1788c2ecf20Sopenharmony_ci	clk_disable_unprepare(phy->tx0_symbol_clk);
1798c2ecf20Sopenharmony_ciout:
1808c2ecf20Sopenharmony_ci	return ret;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic int samsung_ufs_phy_clks_init(struct samsung_ufs_phy *phy)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	int ret;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	phy->ref_clk = devm_clk_get(phy->dev, "ref_clk");
1888c2ecf20Sopenharmony_ci	if (IS_ERR(phy->ref_clk))
1898c2ecf20Sopenharmony_ci		dev_err(phy->dev, "failed to get ref_clk clock\n");
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(phy->ref_clk);
1928c2ecf20Sopenharmony_ci	if (ret) {
1938c2ecf20Sopenharmony_ci		dev_err(phy->dev, "%s: ref_clk enable failed %d\n", __func__, ret);
1948c2ecf20Sopenharmony_ci		return ret;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	dev_dbg(phy->dev, "UFS MPHY ref_clk_rate = %ld\n", clk_get_rate(phy->ref_clk));
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	return 0;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic int samsung_ufs_phy_init(struct phy *phy)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
2058c2ecf20Sopenharmony_ci	int ret;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	ss_phy->lane_cnt = phy->attrs.bus_width;
2088c2ecf20Sopenharmony_ci	ss_phy->ufs_phy_state = CFG_PRE_INIT;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (ss_phy->drvdata->has_symbol_clk) {
2118c2ecf20Sopenharmony_ci		ret = samsung_ufs_phy_symbol_clk_init(ss_phy);
2128c2ecf20Sopenharmony_ci		if (ret)
2138c2ecf20Sopenharmony_ci			dev_err(ss_phy->dev, "failed to set ufs phy symbol clocks\n");
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	ret = samsung_ufs_phy_clks_init(ss_phy);
2178c2ecf20Sopenharmony_ci	if (ret)
2188c2ecf20Sopenharmony_ci		dev_err(ss_phy->dev, "failed to set ufs phy clocks\n");
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ret = samsung_ufs_phy_calibrate(phy);
2218c2ecf20Sopenharmony_ci	if (ret)
2228c2ecf20Sopenharmony_ci		dev_err(ss_phy->dev, "ufs phy calibration failed\n");
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return ret;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic int samsung_ufs_phy_power_on(struct phy *phy)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	samsung_ufs_phy_ctrl_isol(ss_phy, false);
2328c2ecf20Sopenharmony_ci	return 0;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic int samsung_ufs_phy_power_off(struct phy *phy)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	samsung_ufs_phy_ctrl_isol(ss_phy, true);
2408c2ecf20Sopenharmony_ci	return 0;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int samsung_ufs_phy_set_mode(struct phy *generic_phy,
2448c2ecf20Sopenharmony_ci				    enum phy_mode mode, int submode)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(generic_phy);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	ss_phy->mode = PHY_MODE_INVALID;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (mode > 0)
2518c2ecf20Sopenharmony_ci		ss_phy->mode = mode;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	return 0;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic int samsung_ufs_phy_exit(struct phy *phy)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	clk_disable_unprepare(ss_phy->ref_clk);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (ss_phy->drvdata->has_symbol_clk) {
2638c2ecf20Sopenharmony_ci		clk_disable_unprepare(ss_phy->tx0_symbol_clk);
2648c2ecf20Sopenharmony_ci		clk_disable_unprepare(ss_phy->rx0_symbol_clk);
2658c2ecf20Sopenharmony_ci		clk_disable_unprepare(ss_phy->rx1_symbol_clk);
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	return 0;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic const struct phy_ops samsung_ufs_phy_ops = {
2728c2ecf20Sopenharmony_ci	.init		= samsung_ufs_phy_init,
2738c2ecf20Sopenharmony_ci	.exit		= samsung_ufs_phy_exit,
2748c2ecf20Sopenharmony_ci	.power_on	= samsung_ufs_phy_power_on,
2758c2ecf20Sopenharmony_ci	.power_off	= samsung_ufs_phy_power_off,
2768c2ecf20Sopenharmony_ci	.calibrate	= samsung_ufs_phy_calibrate,
2778c2ecf20Sopenharmony_ci	.set_mode	= samsung_ufs_phy_set_mode,
2788c2ecf20Sopenharmony_ci	.owner          = THIS_MODULE,
2798c2ecf20Sopenharmony_ci};
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic const struct of_device_id samsung_ufs_phy_match[];
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic int samsung_ufs_phy_probe(struct platform_device *pdev)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
2868c2ecf20Sopenharmony_ci	const struct of_device_id *match;
2878c2ecf20Sopenharmony_ci	struct samsung_ufs_phy *phy;
2888c2ecf20Sopenharmony_ci	struct phy *gen_phy;
2898c2ecf20Sopenharmony_ci	struct phy_provider *phy_provider;
2908c2ecf20Sopenharmony_ci	const struct samsung_ufs_phy_drvdata *drvdata;
2918c2ecf20Sopenharmony_ci	int err = 0;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	match = of_match_node(samsung_ufs_phy_match, dev->of_node);
2948c2ecf20Sopenharmony_ci	if (!match) {
2958c2ecf20Sopenharmony_ci		err = -EINVAL;
2968c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get match_node\n");
2978c2ecf20Sopenharmony_ci		goto out;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
3018c2ecf20Sopenharmony_ci	if (!phy) {
3028c2ecf20Sopenharmony_ci		err = -ENOMEM;
3038c2ecf20Sopenharmony_ci		goto out;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	phy->reg_pma = devm_platform_ioremap_resource_byname(pdev, "phy-pma");
3078c2ecf20Sopenharmony_ci	if (IS_ERR(phy->reg_pma)) {
3088c2ecf20Sopenharmony_ci		err = PTR_ERR(phy->reg_pma);
3098c2ecf20Sopenharmony_ci		goto out;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	phy->reg_pmu = syscon_regmap_lookup_by_phandle(
3138c2ecf20Sopenharmony_ci				dev->of_node, "samsung,pmu-syscon");
3148c2ecf20Sopenharmony_ci	if (IS_ERR(phy->reg_pmu)) {
3158c2ecf20Sopenharmony_ci		err = PTR_ERR(phy->reg_pmu);
3168c2ecf20Sopenharmony_ci		dev_err(dev, "failed syscon remap for pmu\n");
3178c2ecf20Sopenharmony_ci		goto out;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	gen_phy = devm_phy_create(dev, NULL, &samsung_ufs_phy_ops);
3218c2ecf20Sopenharmony_ci	if (IS_ERR(gen_phy)) {
3228c2ecf20Sopenharmony_ci		err = PTR_ERR(gen_phy);
3238c2ecf20Sopenharmony_ci		dev_err(dev, "failed to create PHY for ufs-phy\n");
3248c2ecf20Sopenharmony_ci		goto out;
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	drvdata = match->data;
3288c2ecf20Sopenharmony_ci	phy->dev = dev;
3298c2ecf20Sopenharmony_ci	phy->drvdata = drvdata;
3308c2ecf20Sopenharmony_ci	phy->cfg = (struct samsung_ufs_phy_cfg **)drvdata->cfg;
3318c2ecf20Sopenharmony_ci	phy->isol = &drvdata->isol;
3328c2ecf20Sopenharmony_ci	phy->lane_cnt = PHY_DEF_LANE_CNT;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	phy_set_drvdata(gen_phy, phy);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
3378c2ecf20Sopenharmony_ci	if (IS_ERR(phy_provider)) {
3388c2ecf20Sopenharmony_ci		err = PTR_ERR(phy_provider);
3398c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register phy-provider\n");
3408c2ecf20Sopenharmony_ci		goto out;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ciout:
3438c2ecf20Sopenharmony_ci	return err;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic const struct of_device_id samsung_ufs_phy_match[] = {
3478c2ecf20Sopenharmony_ci	{
3488c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos7-ufs-phy",
3498c2ecf20Sopenharmony_ci		.data = &exynos7_ufs_phy,
3508c2ecf20Sopenharmony_ci	},
3518c2ecf20Sopenharmony_ci	{},
3528c2ecf20Sopenharmony_ci};
3538c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, samsung_ufs_phy_match);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic struct platform_driver samsung_ufs_phy_driver = {
3568c2ecf20Sopenharmony_ci	.probe  = samsung_ufs_phy_probe,
3578c2ecf20Sopenharmony_ci	.driver = {
3588c2ecf20Sopenharmony_ci		.name = "samsung-ufs-phy",
3598c2ecf20Sopenharmony_ci		.of_match_table = samsung_ufs_phy_match,
3608c2ecf20Sopenharmony_ci	},
3618c2ecf20Sopenharmony_ci};
3628c2ecf20Sopenharmony_cimodule_platform_driver(samsung_ufs_phy_driver);
3638c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung SoC UFS PHY Driver");
3648c2ecf20Sopenharmony_ciMODULE_AUTHOR("Seungwon Jeon <essuuj@gmail.com>");
3658c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alim Akhtar <alim.akhtar@samsung.com>");
3668c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
367