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