18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * COMBPHY driver for HiSilicon STB SoCs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016-2017 HiSilicon Co., Ltd. http://www.hisilicon.com 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Jianguo Sun <sunjianguo1@huawei.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/of_device.h> 178c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 188c2ecf20Sopenharmony_ci#include <linux/regmap.h> 198c2ecf20Sopenharmony_ci#include <linux/reset.h> 208c2ecf20Sopenharmony_ci#include <dt-bindings/phy/phy.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define COMBPHY_MODE_PCIE 0 238c2ecf20Sopenharmony_ci#define COMBPHY_MODE_USB3 1 248c2ecf20Sopenharmony_ci#define COMBPHY_MODE_SATA 2 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define COMBPHY_CFG_REG 0x0 278c2ecf20Sopenharmony_ci#define COMBPHY_BYPASS_CODEC BIT(31) 288c2ecf20Sopenharmony_ci#define COMBPHY_TEST_WRITE BIT(24) 298c2ecf20Sopenharmony_ci#define COMBPHY_TEST_DATA_SHIFT 20 308c2ecf20Sopenharmony_ci#define COMBPHY_TEST_DATA_MASK GENMASK(23, 20) 318c2ecf20Sopenharmony_ci#define COMBPHY_TEST_ADDR_SHIFT 12 328c2ecf20Sopenharmony_ci#define COMBPHY_TEST_ADDR_MASK GENMASK(16, 12) 338c2ecf20Sopenharmony_ci#define COMBPHY_CLKREF_OUT_OEN BIT(0) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct histb_combphy_mode { 368c2ecf20Sopenharmony_ci int fixed; 378c2ecf20Sopenharmony_ci int select; 388c2ecf20Sopenharmony_ci u32 reg; 398c2ecf20Sopenharmony_ci u32 shift; 408c2ecf20Sopenharmony_ci u32 mask; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct histb_combphy_priv { 448c2ecf20Sopenharmony_ci void __iomem *mmio; 458c2ecf20Sopenharmony_ci struct regmap *syscon; 468c2ecf20Sopenharmony_ci struct reset_control *por_rst; 478c2ecf20Sopenharmony_ci struct clk *ref_clk; 488c2ecf20Sopenharmony_ci struct phy *phy; 498c2ecf20Sopenharmony_ci struct histb_combphy_mode mode; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void nano_register_write(struct histb_combphy_priv *priv, 538c2ecf20Sopenharmony_ci u32 addr, u32 data) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci void __iomem *reg = priv->mmio + COMBPHY_CFG_REG; 568c2ecf20Sopenharmony_ci u32 val; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* Set up address and data for the write */ 598c2ecf20Sopenharmony_ci val = readl(reg); 608c2ecf20Sopenharmony_ci val &= ~COMBPHY_TEST_ADDR_MASK; 618c2ecf20Sopenharmony_ci val |= addr << COMBPHY_TEST_ADDR_SHIFT; 628c2ecf20Sopenharmony_ci val &= ~COMBPHY_TEST_DATA_MASK; 638c2ecf20Sopenharmony_ci val |= data << COMBPHY_TEST_DATA_SHIFT; 648c2ecf20Sopenharmony_ci writel(val, reg); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* Flip strobe control to trigger the write */ 678c2ecf20Sopenharmony_ci val &= ~COMBPHY_TEST_WRITE; 688c2ecf20Sopenharmony_ci writel(val, reg); 698c2ecf20Sopenharmony_ci val |= COMBPHY_TEST_WRITE; 708c2ecf20Sopenharmony_ci writel(val, reg); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int is_mode_fixed(struct histb_combphy_mode *mode) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci return (mode->fixed != PHY_NONE) ? true : false; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int histb_combphy_set_mode(struct histb_combphy_priv *priv) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct histb_combphy_mode *mode = &priv->mode; 818c2ecf20Sopenharmony_ci struct regmap *syscon = priv->syscon; 828c2ecf20Sopenharmony_ci u32 hw_sel; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (is_mode_fixed(mode)) 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci switch (mode->select) { 888c2ecf20Sopenharmony_ci case PHY_TYPE_SATA: 898c2ecf20Sopenharmony_ci hw_sel = COMBPHY_MODE_SATA; 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci case PHY_TYPE_PCIE: 928c2ecf20Sopenharmony_ci hw_sel = COMBPHY_MODE_PCIE; 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci case PHY_TYPE_USB3: 958c2ecf20Sopenharmony_ci hw_sel = COMBPHY_MODE_USB3; 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci default: 988c2ecf20Sopenharmony_ci return -EINVAL; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return regmap_update_bits(syscon, mode->reg, mode->mask, 1028c2ecf20Sopenharmony_ci hw_sel << mode->shift); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int histb_combphy_init(struct phy *phy) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct histb_combphy_priv *priv = phy_get_drvdata(phy); 1088c2ecf20Sopenharmony_ci u32 val; 1098c2ecf20Sopenharmony_ci int ret; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci ret = histb_combphy_set_mode(priv); 1128c2ecf20Sopenharmony_ci if (ret) 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* Clear bypass bit to enable encoding/decoding */ 1168c2ecf20Sopenharmony_ci val = readl(priv->mmio + COMBPHY_CFG_REG); 1178c2ecf20Sopenharmony_ci val &= ~COMBPHY_BYPASS_CODEC; 1188c2ecf20Sopenharmony_ci writel(val, priv->mmio + COMBPHY_CFG_REG); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->ref_clk); 1218c2ecf20Sopenharmony_ci if (ret) 1228c2ecf20Sopenharmony_ci return ret; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci reset_control_deassert(priv->por_rst); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Enable EP clock */ 1278c2ecf20Sopenharmony_ci val = readl(priv->mmio + COMBPHY_CFG_REG); 1288c2ecf20Sopenharmony_ci val |= COMBPHY_CLKREF_OUT_OEN; 1298c2ecf20Sopenharmony_ci writel(val, priv->mmio + COMBPHY_CFG_REG); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Need to wait for EP clock stable */ 1328c2ecf20Sopenharmony_ci mdelay(5); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* Configure nano phy registers as suggested by vendor */ 1358c2ecf20Sopenharmony_ci nano_register_write(priv, 0x1, 0x8); 1368c2ecf20Sopenharmony_ci nano_register_write(priv, 0xc, 0x9); 1378c2ecf20Sopenharmony_ci nano_register_write(priv, 0x1a, 0x4); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int histb_combphy_exit(struct phy *phy) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct histb_combphy_priv *priv = phy_get_drvdata(phy); 1458c2ecf20Sopenharmony_ci u32 val; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Disable EP clock */ 1488c2ecf20Sopenharmony_ci val = readl(priv->mmio + COMBPHY_CFG_REG); 1498c2ecf20Sopenharmony_ci val &= ~COMBPHY_CLKREF_OUT_OEN; 1508c2ecf20Sopenharmony_ci writel(val, priv->mmio + COMBPHY_CFG_REG); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci reset_control_assert(priv->por_rst); 1538c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->ref_clk); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic const struct phy_ops histb_combphy_ops = { 1598c2ecf20Sopenharmony_ci .init = histb_combphy_init, 1608c2ecf20Sopenharmony_ci .exit = histb_combphy_exit, 1618c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic struct phy *histb_combphy_xlate(struct device *dev, 1658c2ecf20Sopenharmony_ci struct of_phandle_args *args) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct histb_combphy_priv *priv = dev_get_drvdata(dev); 1688c2ecf20Sopenharmony_ci struct histb_combphy_mode *mode = &priv->mode; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (args->args_count < 1) { 1718c2ecf20Sopenharmony_ci dev_err(dev, "invalid number of arguments\n"); 1728c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci mode->select = args->args[0]; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (mode->select < PHY_TYPE_SATA || mode->select > PHY_TYPE_USB3) { 1788c2ecf20Sopenharmony_ci dev_err(dev, "invalid phy mode select argument\n"); 1798c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (is_mode_fixed(mode) && mode->select != mode->fixed) { 1838c2ecf20Sopenharmony_ci dev_err(dev, "mode select %d mismatch fixed phy mode %d\n", 1848c2ecf20Sopenharmony_ci mode->select, mode->fixed); 1858c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return priv->phy; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int histb_combphy_probe(struct platform_device *pdev) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 1948c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1958c2ecf20Sopenharmony_ci struct histb_combphy_priv *priv; 1968c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 1978c2ecf20Sopenharmony_ci struct histb_combphy_mode *mode; 1988c2ecf20Sopenharmony_ci u32 vals[3]; 1998c2ecf20Sopenharmony_ci int ret; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 2028c2ecf20Sopenharmony_ci if (!priv) 2038c2ecf20Sopenharmony_ci return -ENOMEM; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci priv->mmio = devm_platform_ioremap_resource(pdev, 0); 2068c2ecf20Sopenharmony_ci if (IS_ERR(priv->mmio)) { 2078c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->mmio); 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci priv->syscon = syscon_node_to_regmap(np->parent); 2128c2ecf20Sopenharmony_ci if (IS_ERR(priv->syscon)) { 2138c2ecf20Sopenharmony_ci dev_err(dev, "failed to find peri_ctrl syscon regmap\n"); 2148c2ecf20Sopenharmony_ci return PTR_ERR(priv->syscon); 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci mode = &priv->mode; 2188c2ecf20Sopenharmony_ci mode->fixed = PHY_NONE; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "hisilicon,fixed-mode", &mode->fixed); 2218c2ecf20Sopenharmony_ci if (ret == 0) 2228c2ecf20Sopenharmony_ci dev_dbg(dev, "found fixed phy mode %d\n", mode->fixed); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(np, "hisilicon,mode-select-bits", 2258c2ecf20Sopenharmony_ci vals, ARRAY_SIZE(vals)); 2268c2ecf20Sopenharmony_ci if (ret == 0) { 2278c2ecf20Sopenharmony_ci if (is_mode_fixed(mode)) { 2288c2ecf20Sopenharmony_ci dev_err(dev, "found select bits for fixed mode phy\n"); 2298c2ecf20Sopenharmony_ci return -EINVAL; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci mode->reg = vals[0]; 2338c2ecf20Sopenharmony_ci mode->shift = vals[1]; 2348c2ecf20Sopenharmony_ci mode->mask = vals[2]; 2358c2ecf20Sopenharmony_ci dev_dbg(dev, "found mode select bits\n"); 2368c2ecf20Sopenharmony_ci } else { 2378c2ecf20Sopenharmony_ci if (!is_mode_fixed(mode)) { 2388c2ecf20Sopenharmony_ci dev_err(dev, "no valid select bits found for non-fixed phy\n"); 2398c2ecf20Sopenharmony_ci return -ENODEV; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci priv->ref_clk = devm_clk_get(dev, NULL); 2448c2ecf20Sopenharmony_ci if (IS_ERR(priv->ref_clk)) { 2458c2ecf20Sopenharmony_ci dev_err(dev, "failed to find ref clock\n"); 2468c2ecf20Sopenharmony_ci return PTR_ERR(priv->ref_clk); 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci priv->por_rst = devm_reset_control_get(dev, NULL); 2508c2ecf20Sopenharmony_ci if (IS_ERR(priv->por_rst)) { 2518c2ecf20Sopenharmony_ci dev_err(dev, "failed to get poweron reset\n"); 2528c2ecf20Sopenharmony_ci return PTR_ERR(priv->por_rst); 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci priv->phy = devm_phy_create(dev, NULL, &histb_combphy_ops); 2568c2ecf20Sopenharmony_ci if (IS_ERR(priv->phy)) { 2578c2ecf20Sopenharmony_ci dev_err(dev, "failed to create combphy\n"); 2588c2ecf20Sopenharmony_ci return PTR_ERR(priv->phy); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci dev_set_drvdata(dev, priv); 2628c2ecf20Sopenharmony_ci phy_set_drvdata(priv->phy, priv); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, histb_combphy_xlate); 2658c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic const struct of_device_id histb_combphy_of_match[] = { 2698c2ecf20Sopenharmony_ci { .compatible = "hisilicon,hi3798cv200-combphy" }, 2708c2ecf20Sopenharmony_ci { }, 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, histb_combphy_of_match); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic struct platform_driver histb_combphy_driver = { 2758c2ecf20Sopenharmony_ci .probe = histb_combphy_probe, 2768c2ecf20Sopenharmony_ci .driver = { 2778c2ecf20Sopenharmony_ci .name = "combphy", 2788c2ecf20Sopenharmony_ci .of_match_table = histb_combphy_of_match, 2798c2ecf20Sopenharmony_ci }, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_cimodule_platform_driver(histb_combphy_driver); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("HiSilicon STB COMBPHY driver"); 2848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 285