phy-histb-combphy.c (6880B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * COMBPHY driver for HiSilicon STB SoCs 4 * 5 * Copyright (C) 2016-2017 HiSilicon Co., Ltd. http://www.hisilicon.com 6 * 7 * Authors: Jianguo Sun <sunjianguo1@huawei.com> 8 */ 9 10#include <linux/clk.h> 11#include <linux/delay.h> 12#include <linux/io.h> 13#include <linux/kernel.h> 14#include <linux/mfd/syscon.h> 15#include <linux/module.h> 16#include <linux/of_device.h> 17#include <linux/phy/phy.h> 18#include <linux/regmap.h> 19#include <linux/reset.h> 20#include <dt-bindings/phy/phy.h> 21 22#define COMBPHY_MODE_PCIE 0 23#define COMBPHY_MODE_USB3 1 24#define COMBPHY_MODE_SATA 2 25 26#define COMBPHY_CFG_REG 0x0 27#define COMBPHY_BYPASS_CODEC BIT(31) 28#define COMBPHY_TEST_WRITE BIT(24) 29#define COMBPHY_TEST_DATA_SHIFT 20 30#define COMBPHY_TEST_DATA_MASK GENMASK(23, 20) 31#define COMBPHY_TEST_ADDR_SHIFT 12 32#define COMBPHY_TEST_ADDR_MASK GENMASK(16, 12) 33#define COMBPHY_CLKREF_OUT_OEN BIT(0) 34 35struct histb_combphy_mode { 36 int fixed; 37 int select; 38 u32 reg; 39 u32 shift; 40 u32 mask; 41}; 42 43struct histb_combphy_priv { 44 void __iomem *mmio; 45 struct regmap *syscon; 46 struct reset_control *por_rst; 47 struct clk *ref_clk; 48 struct phy *phy; 49 struct histb_combphy_mode mode; 50}; 51 52static void nano_register_write(struct histb_combphy_priv *priv, 53 u32 addr, u32 data) 54{ 55 void __iomem *reg = priv->mmio + COMBPHY_CFG_REG; 56 u32 val; 57 58 /* Set up address and data for the write */ 59 val = readl(reg); 60 val &= ~COMBPHY_TEST_ADDR_MASK; 61 val |= addr << COMBPHY_TEST_ADDR_SHIFT; 62 val &= ~COMBPHY_TEST_DATA_MASK; 63 val |= data << COMBPHY_TEST_DATA_SHIFT; 64 writel(val, reg); 65 66 /* Flip strobe control to trigger the write */ 67 val &= ~COMBPHY_TEST_WRITE; 68 writel(val, reg); 69 val |= COMBPHY_TEST_WRITE; 70 writel(val, reg); 71} 72 73static int is_mode_fixed(struct histb_combphy_mode *mode) 74{ 75 return (mode->fixed != PHY_NONE) ? true : false; 76} 77 78static int histb_combphy_set_mode(struct histb_combphy_priv *priv) 79{ 80 struct histb_combphy_mode *mode = &priv->mode; 81 struct regmap *syscon = priv->syscon; 82 u32 hw_sel; 83 84 if (is_mode_fixed(mode)) 85 return 0; 86 87 switch (mode->select) { 88 case PHY_TYPE_SATA: 89 hw_sel = COMBPHY_MODE_SATA; 90 break; 91 case PHY_TYPE_PCIE: 92 hw_sel = COMBPHY_MODE_PCIE; 93 break; 94 case PHY_TYPE_USB3: 95 hw_sel = COMBPHY_MODE_USB3; 96 break; 97 default: 98 return -EINVAL; 99 } 100 101 return regmap_update_bits(syscon, mode->reg, mode->mask, 102 hw_sel << mode->shift); 103} 104 105static int histb_combphy_init(struct phy *phy) 106{ 107 struct histb_combphy_priv *priv = phy_get_drvdata(phy); 108 u32 val; 109 int ret; 110 111 ret = histb_combphy_set_mode(priv); 112 if (ret) 113 return ret; 114 115 /* Clear bypass bit to enable encoding/decoding */ 116 val = readl(priv->mmio + COMBPHY_CFG_REG); 117 val &= ~COMBPHY_BYPASS_CODEC; 118 writel(val, priv->mmio + COMBPHY_CFG_REG); 119 120 ret = clk_prepare_enable(priv->ref_clk); 121 if (ret) 122 return ret; 123 124 reset_control_deassert(priv->por_rst); 125 126 /* Enable EP clock */ 127 val = readl(priv->mmio + COMBPHY_CFG_REG); 128 val |= COMBPHY_CLKREF_OUT_OEN; 129 writel(val, priv->mmio + COMBPHY_CFG_REG); 130 131 /* Need to wait for EP clock stable */ 132 mdelay(5); 133 134 /* Configure nano phy registers as suggested by vendor */ 135 nano_register_write(priv, 0x1, 0x8); 136 nano_register_write(priv, 0xc, 0x9); 137 nano_register_write(priv, 0x1a, 0x4); 138 139 return 0; 140} 141 142static int histb_combphy_exit(struct phy *phy) 143{ 144 struct histb_combphy_priv *priv = phy_get_drvdata(phy); 145 u32 val; 146 147 /* Disable EP clock */ 148 val = readl(priv->mmio + COMBPHY_CFG_REG); 149 val &= ~COMBPHY_CLKREF_OUT_OEN; 150 writel(val, priv->mmio + COMBPHY_CFG_REG); 151 152 reset_control_assert(priv->por_rst); 153 clk_disable_unprepare(priv->ref_clk); 154 155 return 0; 156} 157 158static const struct phy_ops histb_combphy_ops = { 159 .init = histb_combphy_init, 160 .exit = histb_combphy_exit, 161 .owner = THIS_MODULE, 162}; 163 164static struct phy *histb_combphy_xlate(struct device *dev, 165 struct of_phandle_args *args) 166{ 167 struct histb_combphy_priv *priv = dev_get_drvdata(dev); 168 struct histb_combphy_mode *mode = &priv->mode; 169 170 if (args->args_count < 1) { 171 dev_err(dev, "invalid number of arguments\n"); 172 return ERR_PTR(-EINVAL); 173 } 174 175 mode->select = args->args[0]; 176 177 if (mode->select < PHY_TYPE_SATA || mode->select > PHY_TYPE_USB3) { 178 dev_err(dev, "invalid phy mode select argument\n"); 179 return ERR_PTR(-EINVAL); 180 } 181 182 if (is_mode_fixed(mode) && mode->select != mode->fixed) { 183 dev_err(dev, "mode select %d mismatch fixed phy mode %d\n", 184 mode->select, mode->fixed); 185 return ERR_PTR(-EINVAL); 186 } 187 188 return priv->phy; 189} 190 191static int histb_combphy_probe(struct platform_device *pdev) 192{ 193 struct phy_provider *phy_provider; 194 struct device *dev = &pdev->dev; 195 struct histb_combphy_priv *priv; 196 struct device_node *np = dev->of_node; 197 struct histb_combphy_mode *mode; 198 u32 vals[3]; 199 int ret; 200 201 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 202 if (!priv) 203 return -ENOMEM; 204 205 priv->mmio = devm_platform_ioremap_resource(pdev, 0); 206 if (IS_ERR(priv->mmio)) { 207 ret = PTR_ERR(priv->mmio); 208 return ret; 209 } 210 211 priv->syscon = syscon_node_to_regmap(np->parent); 212 if (IS_ERR(priv->syscon)) { 213 dev_err(dev, "failed to find peri_ctrl syscon regmap\n"); 214 return PTR_ERR(priv->syscon); 215 } 216 217 mode = &priv->mode; 218 mode->fixed = PHY_NONE; 219 220 ret = of_property_read_u32(np, "hisilicon,fixed-mode", &mode->fixed); 221 if (ret == 0) 222 dev_dbg(dev, "found fixed phy mode %d\n", mode->fixed); 223 224 ret = of_property_read_u32_array(np, "hisilicon,mode-select-bits", 225 vals, ARRAY_SIZE(vals)); 226 if (ret == 0) { 227 if (is_mode_fixed(mode)) { 228 dev_err(dev, "found select bits for fixed mode phy\n"); 229 return -EINVAL; 230 } 231 232 mode->reg = vals[0]; 233 mode->shift = vals[1]; 234 mode->mask = vals[2]; 235 dev_dbg(dev, "found mode select bits\n"); 236 } else { 237 if (!is_mode_fixed(mode)) { 238 dev_err(dev, "no valid select bits found for non-fixed phy\n"); 239 return -ENODEV; 240 } 241 } 242 243 priv->ref_clk = devm_clk_get(dev, NULL); 244 if (IS_ERR(priv->ref_clk)) { 245 dev_err(dev, "failed to find ref clock\n"); 246 return PTR_ERR(priv->ref_clk); 247 } 248 249 priv->por_rst = devm_reset_control_get(dev, NULL); 250 if (IS_ERR(priv->por_rst)) { 251 dev_err(dev, "failed to get poweron reset\n"); 252 return PTR_ERR(priv->por_rst); 253 } 254 255 priv->phy = devm_phy_create(dev, NULL, &histb_combphy_ops); 256 if (IS_ERR(priv->phy)) { 257 dev_err(dev, "failed to create combphy\n"); 258 return PTR_ERR(priv->phy); 259 } 260 261 dev_set_drvdata(dev, priv); 262 phy_set_drvdata(priv->phy, priv); 263 264 phy_provider = devm_of_phy_provider_register(dev, histb_combphy_xlate); 265 return PTR_ERR_OR_ZERO(phy_provider); 266} 267 268static const struct of_device_id histb_combphy_of_match[] = { 269 { .compatible = "hisilicon,hi3798cv200-combphy" }, 270 { }, 271}; 272MODULE_DEVICE_TABLE(of, histb_combphy_of_match); 273 274static struct platform_driver histb_combphy_driver = { 275 .probe = histb_combphy_probe, 276 .driver = { 277 .name = "combphy", 278 .of_match_table = histb_combphy_of_match, 279 }, 280}; 281module_platform_driver(histb_combphy_driver); 282 283MODULE_DESCRIPTION("HiSilicon STB COMBPHY driver"); 284MODULE_LICENSE("GPL v2");