13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0 23d0407baSopenharmony_ci/* 33d0407baSopenharmony_ci * Rockchip PIPE USB3.0 PCIE SATA combphy driver 43d0407baSopenharmony_ci * 53d0407baSopenharmony_ci * Copyright (C) 2020 Rockchip Electronics Co., Ltd. 63d0407baSopenharmony_ci */ 73d0407baSopenharmony_ci 83d0407baSopenharmony_ci#include <linux/clk.h> 93d0407baSopenharmony_ci#include <linux/delay.h> 103d0407baSopenharmony_ci#include <linux/io.h> 113d0407baSopenharmony_ci#include <linux/iopoll.h> 123d0407baSopenharmony_ci#include <linux/kernel.h> 133d0407baSopenharmony_ci#include <linux/mfd/syscon.h> 143d0407baSopenharmony_ci#include <linux/module.h> 153d0407baSopenharmony_ci#include <linux/of_device.h> 163d0407baSopenharmony_ci#include <linux/phy/phy.h> 173d0407baSopenharmony_ci#include <linux/regmap.h> 183d0407baSopenharmony_ci#include <linux/reset.h> 193d0407baSopenharmony_ci#include <dt-bindings/phy/phy.h> 203d0407baSopenharmony_ci 213d0407baSopenharmony_ci#define BIT_WRITEABLE_SHIFT 16 223d0407baSopenharmony_ci 233d0407baSopenharmony_cistruct rockchip_combphy_priv; 243d0407baSopenharmony_ci 253d0407baSopenharmony_cistruct combphy_reg { 263d0407baSopenharmony_ci u16 offset; 273d0407baSopenharmony_ci u16 bitend; 283d0407baSopenharmony_ci u16 bitstart; 293d0407baSopenharmony_ci u16 disable; 303d0407baSopenharmony_ci u16 enable; 313d0407baSopenharmony_ci}; 323d0407baSopenharmony_ci 333d0407baSopenharmony_cistruct rockchip_combphy_grfcfg { 343d0407baSopenharmony_ci struct combphy_reg pcie_mode_set; 353d0407baSopenharmony_ci struct combphy_reg usb_mode_set; 363d0407baSopenharmony_ci struct combphy_reg sgmii_mode_set; 373d0407baSopenharmony_ci struct combphy_reg qsgmii_mode_set; 383d0407baSopenharmony_ci struct combphy_reg pipe_rxterm_set; 393d0407baSopenharmony_ci struct combphy_reg pipe_txelec_set; 403d0407baSopenharmony_ci struct combphy_reg pipe_txcomp_set; 413d0407baSopenharmony_ci struct combphy_reg pipe_clk_25m; 423d0407baSopenharmony_ci struct combphy_reg pipe_clk_100m; 433d0407baSopenharmony_ci struct combphy_reg pipe_phymode_sel; 443d0407baSopenharmony_ci struct combphy_reg pipe_rate_sel; 453d0407baSopenharmony_ci struct combphy_reg pipe_rxterm_sel; 463d0407baSopenharmony_ci struct combphy_reg pipe_txelec_sel; 473d0407baSopenharmony_ci struct combphy_reg pipe_txcomp_sel; 483d0407baSopenharmony_ci struct combphy_reg pipe_clk_ext; 493d0407baSopenharmony_ci struct combphy_reg pipe_sel_usb; 503d0407baSopenharmony_ci struct combphy_reg pipe_sel_qsgmii; 513d0407baSopenharmony_ci struct combphy_reg pipe_phy_status; 523d0407baSopenharmony_ci struct combphy_reg con0_for_pcie; 533d0407baSopenharmony_ci struct combphy_reg con1_for_pcie; 543d0407baSopenharmony_ci struct combphy_reg con2_for_pcie; 553d0407baSopenharmony_ci struct combphy_reg con3_for_pcie; 563d0407baSopenharmony_ci struct combphy_reg con0_for_sata; 573d0407baSopenharmony_ci struct combphy_reg con1_for_sata; 583d0407baSopenharmony_ci struct combphy_reg con2_for_sata; 593d0407baSopenharmony_ci struct combphy_reg con3_for_sata; 603d0407baSopenharmony_ci struct combphy_reg pipe_con0_for_sata; 613d0407baSopenharmony_ci struct combphy_reg pipe_con1_for_sata; 623d0407baSopenharmony_ci struct combphy_reg pipe_sgmii_mac_sel; 633d0407baSopenharmony_ci struct combphy_reg pipe_xpcs_phy_ready; 643d0407baSopenharmony_ci struct combphy_reg u3otg0_port_en; 653d0407baSopenharmony_ci struct combphy_reg u3otg1_port_en; 663d0407baSopenharmony_ci}; 673d0407baSopenharmony_ci 683d0407baSopenharmony_cistruct rockchip_combphy_cfg { 693d0407baSopenharmony_ci const int num_clks; 703d0407baSopenharmony_ci const struct clk_bulk_data *clks; 713d0407baSopenharmony_ci const struct rockchip_combphy_grfcfg *grfcfg; 723d0407baSopenharmony_ci bool force_det_out; /* Tx detect Rx errata */ 733d0407baSopenharmony_ci int (*combphy_cfg)(struct rockchip_combphy_priv *priv); 743d0407baSopenharmony_ci}; 753d0407baSopenharmony_ci 763d0407baSopenharmony_cistruct rockchip_combphy_priv { 773d0407baSopenharmony_ci u8 mode; 783d0407baSopenharmony_ci void __iomem *mmio; 793d0407baSopenharmony_ci int num_clks; 803d0407baSopenharmony_ci struct clk_bulk_data *clks; 813d0407baSopenharmony_ci struct device *dev; 823d0407baSopenharmony_ci struct regmap *pipe_grf; 833d0407baSopenharmony_ci struct regmap *phy_grf; 843d0407baSopenharmony_ci struct phy *phy; 853d0407baSopenharmony_ci struct reset_control *apb_rst; 863d0407baSopenharmony_ci struct reset_control *phy_rst; 873d0407baSopenharmony_ci const struct rockchip_combphy_cfg *cfg; 883d0407baSopenharmony_ci}; 893d0407baSopenharmony_ci 903d0407baSopenharmony_cistatic inline bool param_read(struct regmap *base, const struct combphy_reg *reg, u32 val) 913d0407baSopenharmony_ci{ 923d0407baSopenharmony_ci int ret; 933d0407baSopenharmony_ci u32 mask, orig, tmp; 943d0407baSopenharmony_ci 953d0407baSopenharmony_ci ret = regmap_read(base, reg->offset, &orig); 963d0407baSopenharmony_ci if (ret) { 973d0407baSopenharmony_ci return false; 983d0407baSopenharmony_ci } 993d0407baSopenharmony_ci 1003d0407baSopenharmony_ci mask = GENMASK(reg->bitend, reg->bitstart); 1013d0407baSopenharmony_ci tmp = (orig & mask) >> reg->bitstart; 1023d0407baSopenharmony_ci 1033d0407baSopenharmony_ci return tmp == val; 1043d0407baSopenharmony_ci} 1053d0407baSopenharmony_ci 1063d0407baSopenharmony_cistatic int param_write(struct regmap *base, const struct combphy_reg *reg, bool en) 1073d0407baSopenharmony_ci{ 1083d0407baSopenharmony_ci u32 val, mask, tmp; 1093d0407baSopenharmony_ci 1103d0407baSopenharmony_ci tmp = en ? reg->enable : reg->disable; 1113d0407baSopenharmony_ci mask = GENMASK(reg->bitend, reg->bitstart); 1123d0407baSopenharmony_ci val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); 1133d0407baSopenharmony_ci 1143d0407baSopenharmony_ci return regmap_write(base, reg->offset, val); 1153d0407baSopenharmony_ci} 1163d0407baSopenharmony_ci 1173d0407baSopenharmony_cistatic u32 rockchip_combphy_is_ready(struct rockchip_combphy_priv *priv) 1183d0407baSopenharmony_ci{ 1193d0407baSopenharmony_ci const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; 1203d0407baSopenharmony_ci u32 mask, val; 1213d0407baSopenharmony_ci 1223d0407baSopenharmony_ci mask = GENMASK(cfg->pipe_phy_status.bitend, cfg->pipe_phy_status.bitstart); 1233d0407baSopenharmony_ci 1243d0407baSopenharmony_ci regmap_read(priv->phy_grf, cfg->pipe_phy_status.offset, &val); 1253d0407baSopenharmony_ci val = (val & mask) >> cfg->pipe_phy_status.bitstart; 1263d0407baSopenharmony_ci 1273d0407baSopenharmony_ci return val; 1283d0407baSopenharmony_ci} 1293d0407baSopenharmony_ci 1303d0407baSopenharmony_cistatic int rockchip_combphy_pcie_init(struct rockchip_combphy_priv *priv) 1313d0407baSopenharmony_ci{ 1323d0407baSopenharmony_ci int ret = 0; 1333d0407baSopenharmony_ci u32 val; 1343d0407baSopenharmony_ci 1353d0407baSopenharmony_ci if (priv->cfg->combphy_cfg) { 1363d0407baSopenharmony_ci ret = priv->cfg->combphy_cfg(priv); 1373d0407baSopenharmony_ci if (ret) { 1383d0407baSopenharmony_ci dev_err(priv->dev, "failed to init phy for pcie\n"); 1393d0407baSopenharmony_ci return ret; 1403d0407baSopenharmony_ci } 1413d0407baSopenharmony_ci } 1423d0407baSopenharmony_ci 1433d0407baSopenharmony_ci if (priv->cfg->force_det_out) { 1443d0407baSopenharmony_ci val = readl(priv->mmio + (0x19 << 0x02)); 1453d0407baSopenharmony_ci val |= BIT(0x05); 1463d0407baSopenharmony_ci writel(val, priv->mmio + (0x19 << 0x02)); 1473d0407baSopenharmony_ci } 1483d0407baSopenharmony_ci 1493d0407baSopenharmony_ci return ret; 1503d0407baSopenharmony_ci} 1513d0407baSopenharmony_ci 1523d0407baSopenharmony_cistatic int rockchip_combphy_usb3_init(struct rockchip_combphy_priv *priv) 1533d0407baSopenharmony_ci{ 1543d0407baSopenharmony_ci int ret = 0; 1553d0407baSopenharmony_ci 1563d0407baSopenharmony_ci if (priv->cfg->combphy_cfg) { 1573d0407baSopenharmony_ci ret = priv->cfg->combphy_cfg(priv); 1583d0407baSopenharmony_ci if (ret) { 1593d0407baSopenharmony_ci dev_err(priv->dev, "failed to init phy for usb3\n"); 1603d0407baSopenharmony_ci return ret; 1613d0407baSopenharmony_ci } 1623d0407baSopenharmony_ci } 1633d0407baSopenharmony_ci 1643d0407baSopenharmony_ci return ret; 1653d0407baSopenharmony_ci} 1663d0407baSopenharmony_ci 1673d0407baSopenharmony_cistatic int rockchip_combphy_sata_init(struct rockchip_combphy_priv *priv) 1683d0407baSopenharmony_ci{ 1693d0407baSopenharmony_ci int ret = 0; 1703d0407baSopenharmony_ci 1713d0407baSopenharmony_ci if (priv->cfg->combphy_cfg) { 1723d0407baSopenharmony_ci ret = priv->cfg->combphy_cfg(priv); 1733d0407baSopenharmony_ci if (ret) { 1743d0407baSopenharmony_ci dev_err(priv->dev, "failed to init phy for sata\n"); 1753d0407baSopenharmony_ci return ret; 1763d0407baSopenharmony_ci } 1773d0407baSopenharmony_ci } 1783d0407baSopenharmony_ci 1793d0407baSopenharmony_ci return ret; 1803d0407baSopenharmony_ci} 1813d0407baSopenharmony_ci 1823d0407baSopenharmony_cistatic int rockchip_combphy_sgmii_init(struct rockchip_combphy_priv *priv) 1833d0407baSopenharmony_ci{ 1843d0407baSopenharmony_ci int ret = 0; 1853d0407baSopenharmony_ci 1863d0407baSopenharmony_ci if (priv->cfg->combphy_cfg) { 1873d0407baSopenharmony_ci ret = priv->cfg->combphy_cfg(priv); 1883d0407baSopenharmony_ci if (ret) { 1893d0407baSopenharmony_ci dev_err(priv->dev, "failed to init phy for sgmii\n"); 1903d0407baSopenharmony_ci return ret; 1913d0407baSopenharmony_ci } 1923d0407baSopenharmony_ci } 1933d0407baSopenharmony_ci 1943d0407baSopenharmony_ci return ret; 1953d0407baSopenharmony_ci} 1963d0407baSopenharmony_ci 1973d0407baSopenharmony_cistatic int rockchip_combphy_set_mode(struct rockchip_combphy_priv *priv) 1983d0407baSopenharmony_ci{ 1993d0407baSopenharmony_ci switch (priv->mode) { 2003d0407baSopenharmony_ci case PHY_TYPE_PCIE: 2013d0407baSopenharmony_ci rockchip_combphy_pcie_init(priv); 2023d0407baSopenharmony_ci break; 2033d0407baSopenharmony_ci case PHY_TYPE_USB3: 2043d0407baSopenharmony_ci rockchip_combphy_usb3_init(priv); 2053d0407baSopenharmony_ci break; 2063d0407baSopenharmony_ci case PHY_TYPE_SATA: 2073d0407baSopenharmony_ci rockchip_combphy_sata_init(priv); 2083d0407baSopenharmony_ci break; 2093d0407baSopenharmony_ci case PHY_TYPE_SGMII: 2103d0407baSopenharmony_ci case PHY_TYPE_QSGMII: 2113d0407baSopenharmony_ci return rockchip_combphy_sgmii_init(priv); 2123d0407baSopenharmony_ci default: 2133d0407baSopenharmony_ci dev_err(priv->dev, "incompatible PHY type\n"); 2143d0407baSopenharmony_ci return -EINVAL; 2153d0407baSopenharmony_ci } 2163d0407baSopenharmony_ci 2173d0407baSopenharmony_ci return 0; 2183d0407baSopenharmony_ci} 2193d0407baSopenharmony_ci 2203d0407baSopenharmony_cistatic int rockchip_combphy_init(struct phy *phy) 2213d0407baSopenharmony_ci{ 2223d0407baSopenharmony_ci struct rockchip_combphy_priv *priv = phy_get_drvdata(phy); 2233d0407baSopenharmony_ci const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; 2243d0407baSopenharmony_ci u32 val; 2253d0407baSopenharmony_ci int ret; 2263d0407baSopenharmony_ci 2273d0407baSopenharmony_ci ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks); 2283d0407baSopenharmony_ci if (ret) { 2293d0407baSopenharmony_ci dev_err(priv->dev, "failed to enable clks\n"); 2303d0407baSopenharmony_ci return ret; 2313d0407baSopenharmony_ci } 2323d0407baSopenharmony_ci 2333d0407baSopenharmony_ci ret = rockchip_combphy_set_mode(priv); 2343d0407baSopenharmony_ci if (ret) { 2353d0407baSopenharmony_ci goto err_clk; 2363d0407baSopenharmony_ci } 2373d0407baSopenharmony_ci 2383d0407baSopenharmony_ci ret = reset_control_deassert(priv->phy_rst); 2393d0407baSopenharmony_ci if (ret) { 2403d0407baSopenharmony_ci goto err_clk; 2413d0407baSopenharmony_ci } 2423d0407baSopenharmony_ci 2433d0407baSopenharmony_ci if (priv->mode == PHY_TYPE_USB3) { 2443d0407baSopenharmony_ci ret = readx_poll_timeout_atomic(rockchip_combphy_is_ready, priv, val, val == cfg->pipe_phy_status.enable, 0xA, 2453d0407baSopenharmony_ci 0x3E8); 2463d0407baSopenharmony_ci if (ret) { 2473d0407baSopenharmony_ci dev_warn(priv->dev, "wait phy status ready timeout\n"); 2483d0407baSopenharmony_ci } 2493d0407baSopenharmony_ci } 2503d0407baSopenharmony_ci 2513d0407baSopenharmony_ci return 0; 2523d0407baSopenharmony_ci 2533d0407baSopenharmony_cierr_clk: 2543d0407baSopenharmony_ci clk_bulk_disable_unprepare(priv->num_clks, priv->clks); 2553d0407baSopenharmony_ci 2563d0407baSopenharmony_ci return ret; 2573d0407baSopenharmony_ci} 2583d0407baSopenharmony_ci 2593d0407baSopenharmony_cistatic int rockchip_combphy_exit(struct phy *phy) 2603d0407baSopenharmony_ci{ 2613d0407baSopenharmony_ci struct rockchip_combphy_priv *priv = phy_get_drvdata(phy); 2623d0407baSopenharmony_ci 2633d0407baSopenharmony_ci clk_bulk_disable_unprepare(priv->num_clks, priv->clks); 2643d0407baSopenharmony_ci reset_control_assert(priv->phy_rst); 2653d0407baSopenharmony_ci 2663d0407baSopenharmony_ci return 0; 2673d0407baSopenharmony_ci} 2683d0407baSopenharmony_ci 2693d0407baSopenharmony_cistatic const struct phy_ops rochchip_combphy_ops = { 2703d0407baSopenharmony_ci .init = rockchip_combphy_init, 2713d0407baSopenharmony_ci .exit = rockchip_combphy_exit, 2723d0407baSopenharmony_ci .owner = THIS_MODULE, 2733d0407baSopenharmony_ci}; 2743d0407baSopenharmony_ci 2753d0407baSopenharmony_cistatic struct phy *rockchip_combphy_xlate(struct device *dev, struct of_phandle_args *args) 2763d0407baSopenharmony_ci{ 2773d0407baSopenharmony_ci struct rockchip_combphy_priv *priv = dev_get_drvdata(dev); 2783d0407baSopenharmony_ci 2793d0407baSopenharmony_ci if (args->args_count != 1) { 2803d0407baSopenharmony_ci dev_err(dev, "invalid number of arguments\n"); 2813d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 2823d0407baSopenharmony_ci } 2833d0407baSopenharmony_ci 2843d0407baSopenharmony_ci if (priv->mode != PHY_NONE && priv->mode != args->args[0]) { 2853d0407baSopenharmony_ci dev_warn(dev, "phy type select %d overwriting type %d\n", args->args[0], priv->mode); 2863d0407baSopenharmony_ci } 2873d0407baSopenharmony_ci 2883d0407baSopenharmony_ci priv->mode = args->args[0]; 2893d0407baSopenharmony_ci 2903d0407baSopenharmony_ci return priv->phy; 2913d0407baSopenharmony_ci} 2923d0407baSopenharmony_ci 2933d0407baSopenharmony_cistatic int rockchip_combphy_parse_dt(struct device *dev, struct rockchip_combphy_priv *priv) 2943d0407baSopenharmony_ci{ 2953d0407baSopenharmony_ci const struct rockchip_combphy_cfg *phy_cfg = priv->cfg; 2963d0407baSopenharmony_ci int ret, mac_id; 2973d0407baSopenharmony_ci u32 vals[4]; 2983d0407baSopenharmony_ci 2993d0407baSopenharmony_ci ret = devm_clk_bulk_get(dev, priv->num_clks, priv->clks); 3003d0407baSopenharmony_ci if (ret == -EPROBE_DEFER) { 3013d0407baSopenharmony_ci return -EPROBE_DEFER; 3023d0407baSopenharmony_ci } 3033d0407baSopenharmony_ci if (ret) { 3043d0407baSopenharmony_ci priv->num_clks = 0; 3053d0407baSopenharmony_ci } 3063d0407baSopenharmony_ci 3073d0407baSopenharmony_ci priv->pipe_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pipe-grf"); 3083d0407baSopenharmony_ci if (IS_ERR(priv->pipe_grf)) { 3093d0407baSopenharmony_ci dev_err(dev, "failed to find peri_ctrl pipe-grf regmap\n"); 3103d0407baSopenharmony_ci return PTR_ERR(priv->pipe_grf); 3113d0407baSopenharmony_ci } 3123d0407baSopenharmony_ci 3133d0407baSopenharmony_ci priv->phy_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,pipe-phy-grf"); 3143d0407baSopenharmony_ci if (IS_ERR(priv->phy_grf)) { 3153d0407baSopenharmony_ci dev_err(dev, "failed to find peri_ctrl pipe-phy-grf regmap\n"); 3163d0407baSopenharmony_ci return PTR_ERR(priv->phy_grf); 3173d0407baSopenharmony_ci } 3183d0407baSopenharmony_ci 3193d0407baSopenharmony_ci if (device_property_present(dev, "rockchip,dis-u3otg0-port")) { 3203d0407baSopenharmony_ci param_write(priv->pipe_grf, &phy_cfg->grfcfg->u3otg0_port_en, false); 3213d0407baSopenharmony_ci } else if (device_property_present(dev, "rockchip,dis-u3otg1-port")) { 3223d0407baSopenharmony_ci param_write(priv->pipe_grf, &phy_cfg->grfcfg->u3otg1_port_en, false); 3233d0407baSopenharmony_ci } 3243d0407baSopenharmony_ci 3253d0407baSopenharmony_ci if (!device_property_read_u32(dev, "rockchip,sgmii-mac-sel", &mac_id) && (mac_id > 0)) { 3263d0407baSopenharmony_ci param_write(priv->pipe_grf, &phy_cfg->grfcfg->pipe_sgmii_mac_sel, true); 3273d0407baSopenharmony_ci } 3283d0407baSopenharmony_ci 3293d0407baSopenharmony_ci if (!device_property_read_u32_array(dev, "rockchip,pcie1ln-sel-bits", vals, ARRAY_SIZE(vals))) { 3303d0407baSopenharmony_ci regmap_write(priv->pipe_grf, vals[0], (GENMASK(vals[0x02], vals[1]) << 0x10) | (vals[0x03] << vals[1])); 3313d0407baSopenharmony_ci } 3323d0407baSopenharmony_ci 3333d0407baSopenharmony_ci priv->apb_rst = devm_reset_control_get_optional(dev, "combphy-apb"); 3343d0407baSopenharmony_ci if (IS_ERR(priv->apb_rst)) { 3353d0407baSopenharmony_ci ret = PTR_ERR(priv->apb_rst); 3363d0407baSopenharmony_ci if (ret != -EPROBE_DEFER) { 3373d0407baSopenharmony_ci dev_warn(dev, "failed to get apb reset\n"); 3383d0407baSopenharmony_ci } 3393d0407baSopenharmony_ci 3403d0407baSopenharmony_ci return ret; 3413d0407baSopenharmony_ci } 3423d0407baSopenharmony_ci 3433d0407baSopenharmony_ci priv->phy_rst = devm_reset_control_get_optional(dev, "combphy"); 3443d0407baSopenharmony_ci if (IS_ERR(priv->phy_rst)) { 3453d0407baSopenharmony_ci ret = PTR_ERR(priv->phy_rst); 3463d0407baSopenharmony_ci if (ret != -EPROBE_DEFER) { 3473d0407baSopenharmony_ci dev_warn(dev, "failed to get phy reset\n"); 3483d0407baSopenharmony_ci } 3493d0407baSopenharmony_ci 3503d0407baSopenharmony_ci return ret; 3513d0407baSopenharmony_ci } 3523d0407baSopenharmony_ci 3533d0407baSopenharmony_ci return reset_control_assert(priv->phy_rst); 3543d0407baSopenharmony_ci} 3553d0407baSopenharmony_ci 3563d0407baSopenharmony_cistatic int rockchip_combphy_probe(struct platform_device *pdev) 3573d0407baSopenharmony_ci{ 3583d0407baSopenharmony_ci struct phy_provider *phy_provider; 3593d0407baSopenharmony_ci struct device *dev = &pdev->dev; 3603d0407baSopenharmony_ci struct rockchip_combphy_priv *priv; 3613d0407baSopenharmony_ci const struct rockchip_combphy_cfg *phy_cfg; 3623d0407baSopenharmony_ci struct resource *res; 3633d0407baSopenharmony_ci int ret; 3643d0407baSopenharmony_ci 3653d0407baSopenharmony_ci phy_cfg = of_device_get_match_data(dev); 3663d0407baSopenharmony_ci if (!phy_cfg) { 3673d0407baSopenharmony_ci dev_err(dev, "No OF match data provided\n"); 3683d0407baSopenharmony_ci return -EINVAL; 3693d0407baSopenharmony_ci } 3703d0407baSopenharmony_ci 3713d0407baSopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 3723d0407baSopenharmony_ci if (!priv) { 3733d0407baSopenharmony_ci return -ENOMEM; 3743d0407baSopenharmony_ci } 3753d0407baSopenharmony_ci 3763d0407baSopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3773d0407baSopenharmony_ci priv->mmio = devm_ioremap_resource(dev, res); 3783d0407baSopenharmony_ci if (IS_ERR(priv->mmio)) { 3793d0407baSopenharmony_ci ret = PTR_ERR(priv->mmio); 3803d0407baSopenharmony_ci return ret; 3813d0407baSopenharmony_ci } 3823d0407baSopenharmony_ci 3833d0407baSopenharmony_ci priv->num_clks = phy_cfg->num_clks; 3843d0407baSopenharmony_ci 3853d0407baSopenharmony_ci priv->clks = devm_kmemdup(dev, phy_cfg->clks, phy_cfg->num_clks * sizeof(struct clk_bulk_data), GFP_KERNEL); 3863d0407baSopenharmony_ci 3873d0407baSopenharmony_ci if (!priv->clks) { 3883d0407baSopenharmony_ci return -ENOMEM; 3893d0407baSopenharmony_ci } 3903d0407baSopenharmony_ci 3913d0407baSopenharmony_ci priv->dev = dev; 3923d0407baSopenharmony_ci priv->mode = PHY_NONE; 3933d0407baSopenharmony_ci priv->cfg = phy_cfg; 3943d0407baSopenharmony_ci 3953d0407baSopenharmony_ci ret = rockchip_combphy_parse_dt(dev, priv); 3963d0407baSopenharmony_ci if (ret) { 3973d0407baSopenharmony_ci return ret; 3983d0407baSopenharmony_ci } 3993d0407baSopenharmony_ci 4003d0407baSopenharmony_ci priv->phy = devm_phy_create(dev, NULL, &rochchip_combphy_ops); 4013d0407baSopenharmony_ci if (IS_ERR(priv->phy)) { 4023d0407baSopenharmony_ci dev_err(dev, "failed to create combphy\n"); 4033d0407baSopenharmony_ci return PTR_ERR(priv->phy); 4043d0407baSopenharmony_ci } 4053d0407baSopenharmony_ci 4063d0407baSopenharmony_ci dev_set_drvdata(dev, priv); 4073d0407baSopenharmony_ci phy_set_drvdata(priv->phy, priv); 4083d0407baSopenharmony_ci 4093d0407baSopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, rockchip_combphy_xlate); 4103d0407baSopenharmony_ci 4113d0407baSopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 4123d0407baSopenharmony_ci} 4133d0407baSopenharmony_ci 4143d0407baSopenharmony_cistatic int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) 4153d0407baSopenharmony_ci{ 4163d0407baSopenharmony_ci const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; 4173d0407baSopenharmony_ci struct clk *refclk = NULL; 4183d0407baSopenharmony_ci unsigned long rate; 4193d0407baSopenharmony_ci int i; 4203d0407baSopenharmony_ci u32 val; 4213d0407baSopenharmony_ci 4223d0407baSopenharmony_ci /* Configure PHY reference clock frequency */ 4233d0407baSopenharmony_ci for (i = 0; i < priv->num_clks; i++) { 4243d0407baSopenharmony_ci if (!strncmp(priv->clks[i].id, "refclk", 0x6)) { 4253d0407baSopenharmony_ci refclk = priv->clks[i].clk; 4263d0407baSopenharmony_ci break; 4273d0407baSopenharmony_ci } 4283d0407baSopenharmony_ci } 4293d0407baSopenharmony_ci 4303d0407baSopenharmony_ci if (!refclk) { 4313d0407baSopenharmony_ci dev_err(priv->dev, "No refclk found\n"); 4323d0407baSopenharmony_ci return -EINVAL; 4333d0407baSopenharmony_ci } 4343d0407baSopenharmony_ci 4353d0407baSopenharmony_ci switch (priv->mode) { 4363d0407baSopenharmony_ci case PHY_TYPE_PCIE: 4373d0407baSopenharmony_ci /* Set SSC downward spread spectrum */ 4383d0407baSopenharmony_ci val = readl(priv->mmio + (0x1f << 0x02)); 4393d0407baSopenharmony_ci val &= ~GENMASK(0x05, 0x04); 4403d0407baSopenharmony_ci val |= 0x01 << 0x04; 4413d0407baSopenharmony_ci writel(val, priv->mmio + 0x7c); 4423d0407baSopenharmony_ci 4433d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con0_for_pcie, true); 4443d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con1_for_pcie, true); 4453d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con2_for_pcie, true); 4463d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con3_for_pcie, true); 4473d0407baSopenharmony_ci break; 4483d0407baSopenharmony_ci case PHY_TYPE_USB3: 4493d0407baSopenharmony_ci /* Set SSC downward spread spectrum */ 4503d0407baSopenharmony_ci val = readl(priv->mmio + (0x1f << 0x02)); 4513d0407baSopenharmony_ci val &= ~GENMASK(0x05, 0x04); 4523d0407baSopenharmony_ci val |= 0x01 << 0x04; 4533d0407baSopenharmony_ci writel(val, priv->mmio + 0x7c); 4543d0407baSopenharmony_ci 4553d0407baSopenharmony_ci /* Enable adaptive CTLE for USB3.0 Rx */ 4563d0407baSopenharmony_ci val = readl(priv->mmio + (0x0e << 0x02)); 4573d0407baSopenharmony_ci val &= ~GENMASK(0, 0); 4583d0407baSopenharmony_ci val |= 0x01; 4593d0407baSopenharmony_ci writel(val, priv->mmio + (0x0e << 0x02)); 4603d0407baSopenharmony_ci 4613d0407baSopenharmony_ci /* Set PLL KVCO fine tuning signals */ 4623d0407baSopenharmony_ci val = readl(priv->mmio + (0x20 << 0x02)); 4633d0407baSopenharmony_ci val &= ~(0x7 << 0x02); 4643d0407baSopenharmony_ci val |= 0x2 << 0x02; 4653d0407baSopenharmony_ci writel(val, priv->mmio + (0x20 << 0x02)); 4663d0407baSopenharmony_ci 4673d0407baSopenharmony_ci /* Set PLL LPF R1 to su_trim[10:7]=1001 */ 4683d0407baSopenharmony_ci writel(0x4, priv->mmio + (0xb << 0x02)); 4693d0407baSopenharmony_ci 4703d0407baSopenharmony_ci /* Set PLL input clock divider 1/2 */ 4713d0407baSopenharmony_ci val = readl(priv->mmio + (0x5 << 2)); 4723d0407baSopenharmony_ci val &= ~(0x3 << 0x06); 4733d0407baSopenharmony_ci val |= 0x1 << 0x06; 4743d0407baSopenharmony_ci writel(val, priv->mmio + (0x5 << 0x02)); 4753d0407baSopenharmony_ci 4763d0407baSopenharmony_ci /* Set PLL loop divider */ 4773d0407baSopenharmony_ci writel(0x32, priv->mmio + (0x11 << 0x02)); 4783d0407baSopenharmony_ci 4793d0407baSopenharmony_ci /* Set PLL KVCO to min and set PLL charge pump current to max */ 4803d0407baSopenharmony_ci writel(0xf0, priv->mmio + (0xa << 0x02)); 4813d0407baSopenharmony_ci 4823d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_sel_usb, true); 4833d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false); 4843d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false); 4853d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->usb_mode_set, true); 4863d0407baSopenharmony_ci break; 4873d0407baSopenharmony_ci case PHY_TYPE_SATA: 4883d0407baSopenharmony_ci writel(0x41, priv->mmio + 0x38); 4893d0407baSopenharmony_ci writel(0x8F, priv->mmio + 0x18); 4903d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con0_for_sata, true); 4913d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con1_for_sata, true); 4923d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con2_for_sata, true); 4933d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con3_for_sata, true); 4943d0407baSopenharmony_ci param_write(priv->pipe_grf, &cfg->pipe_con0_for_sata, true); 4953d0407baSopenharmony_ci break; 4963d0407baSopenharmony_ci case PHY_TYPE_SGMII: 4973d0407baSopenharmony_ci param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true); 4983d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true); 4993d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true); 5003d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->sgmii_mode_set, true); 5013d0407baSopenharmony_ci break; 5023d0407baSopenharmony_ci case PHY_TYPE_QSGMII: 5033d0407baSopenharmony_ci param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true); 5043d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true); 5053d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_rate_sel, true); 5063d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true); 5073d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->qsgmii_mode_set, true); 5083d0407baSopenharmony_ci break; 5093d0407baSopenharmony_ci default: 5103d0407baSopenharmony_ci dev_err(priv->dev, "incompatible PHY type\n"); 5113d0407baSopenharmony_ci return -EINVAL; 5123d0407baSopenharmony_ci } 5133d0407baSopenharmony_ci 5143d0407baSopenharmony_ci rate = clk_get_rate(refclk); 5153d0407baSopenharmony_ci 5163d0407baSopenharmony_ci switch (rate) { 5173d0407baSopenharmony_ci case 0x16E3600: 5183d0407baSopenharmony_ci if (priv->mode == PHY_TYPE_USB3 || priv->mode == PHY_TYPE_SATA) { 5193d0407baSopenharmony_ci /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz */ 5203d0407baSopenharmony_ci val = readl(priv->mmio + (0x0e << 0x02)); 5213d0407baSopenharmony_ci val &= ~GENMASK(0x07, 0x06); 5223d0407baSopenharmony_ci val |= 0x01 << 0x06; 5233d0407baSopenharmony_ci writel(val, priv->mmio + (0x0e << 0x02)); 5243d0407baSopenharmony_ci 5253d0407baSopenharmony_ci val = readl(priv->mmio + (0x0f << 0x02)); 5263d0407baSopenharmony_ci val &= ~GENMASK(0x07, 0); 5273d0407baSopenharmony_ci val |= 0x5f; 5283d0407baSopenharmony_ci writel(val, priv->mmio + (0x0f << 0x02)); 5293d0407baSopenharmony_ci } 5303d0407baSopenharmony_ci break; 5313d0407baSopenharmony_ci case 0x17D7840: 5323d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_clk_25m, true); 5333d0407baSopenharmony_ci break; 5343d0407baSopenharmony_ci case 0x5F5E100: 5353d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_clk_100m, true); 5363d0407baSopenharmony_ci if (priv->mode == PHY_TYPE_PCIE) { 5373d0407baSopenharmony_ci /* PLL KVCO tuning fine */ 5383d0407baSopenharmony_ci val = readl(priv->mmio + (0x20 << 0x02)); 5393d0407baSopenharmony_ci val &= ~(0x7 << 0x02); 5403d0407baSopenharmony_ci val |= 0x2 << 0x02; 5413d0407baSopenharmony_ci writel(val, priv->mmio + (0x20 << 0x02)); 5423d0407baSopenharmony_ci 5433d0407baSopenharmony_ci /* Enable controlling random jitter, aka RMJ */ 5443d0407baSopenharmony_ci writel(0x4, priv->mmio + (0xb << 0x02)); 5453d0407baSopenharmony_ci 5463d0407baSopenharmony_ci val = readl(priv->mmio + (0x5 << 0x02)); 5473d0407baSopenharmony_ci val &= ~(0x3 << 0x06); 5483d0407baSopenharmony_ci val |= 0x1 << 0x06; 5493d0407baSopenharmony_ci writel(val, priv->mmio + (0x5 << 0x02)); 5503d0407baSopenharmony_ci 5513d0407baSopenharmony_ci writel(0x32, priv->mmio + (0x11 << 0x02)); 5523d0407baSopenharmony_ci writel(0xf0, priv->mmio + (0xa << 0x02)); 5533d0407baSopenharmony_ci } else if (priv->mode == PHY_TYPE_SATA) { 5543d0407baSopenharmony_ci /* downward spread spectrum +500ppm */ 5553d0407baSopenharmony_ci val = readl(priv->mmio + (0x1f << 0x02)); 5563d0407baSopenharmony_ci val &= ~GENMASK(0x07, 0x04); 5573d0407baSopenharmony_ci val |= 0x50; 5583d0407baSopenharmony_ci writel(val, priv->mmio + (0x1f << 0x02)); 5593d0407baSopenharmony_ci } 5603d0407baSopenharmony_ci break; 5613d0407baSopenharmony_ci default: 5623d0407baSopenharmony_ci dev_err(priv->dev, "Unsupported rate: %lu\n", rate); 5633d0407baSopenharmony_ci return -EINVAL; 5643d0407baSopenharmony_ci } 5653d0407baSopenharmony_ci 5663d0407baSopenharmony_ci if (device_property_read_bool(priv->dev, "rockchip,ext-refclk")) { 5673d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_clk_ext, true); 5683d0407baSopenharmony_ci if (priv->mode == PHY_TYPE_PCIE && rate == 0x5F5E100) { 5693d0407baSopenharmony_ci val = readl(priv->mmio + (0xc << 0x02)); 5703d0407baSopenharmony_ci val |= (0x3 << 0x04) | (0x1 << 0x07); 5713d0407baSopenharmony_ci writel(val, priv->mmio + (0xc << 0x02)); 5723d0407baSopenharmony_ci 5733d0407baSopenharmony_ci val = readl(priv->mmio + (0xd << 0x02)); 5743d0407baSopenharmony_ci val |= 0x1; 5753d0407baSopenharmony_ci writel(val, priv->mmio + (0xd << 0x02)); 5763d0407baSopenharmony_ci } 5773d0407baSopenharmony_ci } 5783d0407baSopenharmony_ci 5793d0407baSopenharmony_ci if (device_property_read_bool(priv->dev, "rockchip,enable-ssc")) { 5803d0407baSopenharmony_ci val = readl(priv->mmio + (0x7 << 0x02)); 5813d0407baSopenharmony_ci val |= BIT(0x04); 5823d0407baSopenharmony_ci writel(val, priv->mmio + (0x7 << 0x02)); 5833d0407baSopenharmony_ci } 5843d0407baSopenharmony_ci 5853d0407baSopenharmony_ci return 0; 5863d0407baSopenharmony_ci} 5873d0407baSopenharmony_ci 5883d0407baSopenharmony_cistatic const struct rockchip_combphy_grfcfg rk3568_combphy_grfcfgs = { 5893d0407baSopenharmony_ci /* pipe-phy-grf */ 5903d0407baSopenharmony_ci .pcie_mode_set = {0x0000, 5, 0, 0x00, 0x11}, 5913d0407baSopenharmony_ci .usb_mode_set = {0x0000, 5, 0, 0x00, 0x04}, 5923d0407baSopenharmony_ci .sgmii_mode_set = {0x0000, 5, 0, 0x00, 0x01}, 5933d0407baSopenharmony_ci .qsgmii_mode_set = {0x0000, 5, 0, 0x00, 0x21}, 5943d0407baSopenharmony_ci .pipe_rxterm_set = {0x0000, 12, 12, 0x00, 0x01}, 5953d0407baSopenharmony_ci .pipe_txelec_set = {0x0004, 1, 1, 0x00, 0x01}, 5963d0407baSopenharmony_ci .pipe_txcomp_set = {0x0004, 4, 4, 0x00, 0x01}, 5973d0407baSopenharmony_ci .pipe_clk_25m = {0x0004, 14, 13, 0x00, 0x01}, 5983d0407baSopenharmony_ci .pipe_clk_100m = {0x0004, 14, 13, 0x00, 0x02}, 5993d0407baSopenharmony_ci .pipe_phymode_sel = {0x0008, 1, 1, 0x00, 0x01}, 6003d0407baSopenharmony_ci .pipe_rate_sel = {0x0008, 2, 2, 0x00, 0x01}, 6013d0407baSopenharmony_ci .pipe_rxterm_sel = {0x0008, 8, 8, 0x00, 0x01}, 6023d0407baSopenharmony_ci .pipe_txelec_sel = {0x0008, 12, 12, 0x00, 0x01}, 6033d0407baSopenharmony_ci .pipe_txcomp_sel = {0x0008, 15, 15, 0x00, 0x01}, 6043d0407baSopenharmony_ci .pipe_clk_ext = {0x000c, 9, 8, 0x02, 0x01}, 6053d0407baSopenharmony_ci .pipe_sel_usb = {0x000c, 14, 13, 0x00, 0x01}, 6063d0407baSopenharmony_ci .pipe_sel_qsgmii = {0x000c, 15, 13, 0x00, 0x07}, 6073d0407baSopenharmony_ci .pipe_phy_status = {0x0034, 6, 6, 0x01, 0x00}, 6083d0407baSopenharmony_ci .con0_for_pcie = {0x0000, 15, 0, 0x00, 0x1000}, 6093d0407baSopenharmony_ci .con1_for_pcie = {0x0004, 15, 0, 0x00, 0x0000}, 6103d0407baSopenharmony_ci .con2_for_pcie = {0x0008, 15, 0, 0x00, 0x0101}, 6113d0407baSopenharmony_ci .con3_for_pcie = {0x000c, 15, 0, 0x00, 0x0200}, 6123d0407baSopenharmony_ci .con0_for_sata = {0x0000, 15, 0, 0x00, 0x0119}, 6133d0407baSopenharmony_ci .con1_for_sata = {0x0004, 15, 0, 0x00, 0x0040}, 6143d0407baSopenharmony_ci .con2_for_sata = {0x0008, 15, 0, 0x00, 0x80c3}, 6153d0407baSopenharmony_ci .con3_for_sata = {0x000c, 15, 0, 0x00, 0x4407}, 6163d0407baSopenharmony_ci /* pipe-grf */ 6173d0407baSopenharmony_ci .pipe_con0_for_sata = {0x0000, 15, 0, 0x00, 0x2220}, 6183d0407baSopenharmony_ci .pipe_sgmii_mac_sel = {0x0040, 1, 1, 0x00, 0x01}, 6193d0407baSopenharmony_ci .pipe_xpcs_phy_ready = {0x0040, 2, 2, 0x00, 0x01}, 6203d0407baSopenharmony_ci .u3otg0_port_en = {0x0104, 15, 0, 0x0181, 0x1100}, 6213d0407baSopenharmony_ci .u3otg1_port_en = {0x0144, 15, 0, 0x0181, 0x1100}, 6223d0407baSopenharmony_ci}; 6233d0407baSopenharmony_ci 6243d0407baSopenharmony_cistatic const struct clk_bulk_data rk3568_clks[] = { 6253d0407baSopenharmony_ci {.id = "refclk"}, 6263d0407baSopenharmony_ci {.id = "apbclk"}, 6273d0407baSopenharmony_ci {.id = "pipe_clk"}, 6283d0407baSopenharmony_ci}; 6293d0407baSopenharmony_ci 6303d0407baSopenharmony_cistatic const struct rockchip_combphy_cfg rk3568_combphy_cfgs = { 6313d0407baSopenharmony_ci .num_clks = ARRAY_SIZE(rk3568_clks), 6323d0407baSopenharmony_ci .clks = rk3568_clks, 6333d0407baSopenharmony_ci .grfcfg = &rk3568_combphy_grfcfgs, 6343d0407baSopenharmony_ci .combphy_cfg = rk3568_combphy_cfg, 6353d0407baSopenharmony_ci .force_det_out = true, 6363d0407baSopenharmony_ci}; 6373d0407baSopenharmony_ci 6383d0407baSopenharmony_cistatic int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv) 6393d0407baSopenharmony_ci{ 6403d0407baSopenharmony_ci const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; 6413d0407baSopenharmony_ci struct clk *refclk = NULL; 6423d0407baSopenharmony_ci unsigned long rate; 6433d0407baSopenharmony_ci int i; 6443d0407baSopenharmony_ci u32 val; 6453d0407baSopenharmony_ci 6463d0407baSopenharmony_ci /* Configure PHY reference clock frequency */ 6473d0407baSopenharmony_ci for (i = 0; i < priv->num_clks; i++) { 6483d0407baSopenharmony_ci if (!strncmp(priv->clks[i].id, "refclk", 0x06)) { 6493d0407baSopenharmony_ci refclk = priv->clks[i].clk; 6503d0407baSopenharmony_ci break; 6513d0407baSopenharmony_ci } 6523d0407baSopenharmony_ci } 6533d0407baSopenharmony_ci 6543d0407baSopenharmony_ci if (!refclk) { 6553d0407baSopenharmony_ci dev_err(priv->dev, "No refclk found\n"); 6563d0407baSopenharmony_ci return -EINVAL; 6573d0407baSopenharmony_ci } 6583d0407baSopenharmony_ci 6593d0407baSopenharmony_ci switch (priv->mode) { 6603d0407baSopenharmony_ci case PHY_TYPE_PCIE: 6613d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con0_for_pcie, true); 6623d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con1_for_pcie, true); 6633d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con2_for_pcie, true); 6643d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con3_for_pcie, true); 6653d0407baSopenharmony_ci break; 6663d0407baSopenharmony_ci case PHY_TYPE_USB3: 6673d0407baSopenharmony_ci /* Set SSC downward spread spectrum */ 6683d0407baSopenharmony_ci val = readl(priv->mmio + (0x1f << 0x02)); 6693d0407baSopenharmony_ci val &= ~GENMASK(0x05, 0x04); 6703d0407baSopenharmony_ci val |= 0x01 << 0x04; 6713d0407baSopenharmony_ci writel(val, priv->mmio + 0x7c); 6723d0407baSopenharmony_ci 6733d0407baSopenharmony_ci /* Enable adaptive CTLE for USB3.0 Rx */ 6743d0407baSopenharmony_ci val = readl(priv->mmio + (0x0e << 0x02)); 6753d0407baSopenharmony_ci val &= ~GENMASK(0, 0); 6763d0407baSopenharmony_ci val |= 0x01; 6773d0407baSopenharmony_ci writel(val, priv->mmio + (0x0e << 0x02)); 6783d0407baSopenharmony_ci 6793d0407baSopenharmony_ci /* Set PLL KVCO fine tuning signals */ 6803d0407baSopenharmony_ci val = readl(priv->mmio + (0x20 << 0x02)); 6813d0407baSopenharmony_ci val &= ~(0x7 << 0x02); 6823d0407baSopenharmony_ci val |= 0x2 << 0x02; 6833d0407baSopenharmony_ci writel(val, priv->mmio + (0x20 << 0x02)); 6843d0407baSopenharmony_ci 6853d0407baSopenharmony_ci /* Set PLL LPF R1 to su_trim[10:7]=1001 */ 6863d0407baSopenharmony_ci writel(0x4, priv->mmio + (0xb << 0x02)); 6873d0407baSopenharmony_ci 6883d0407baSopenharmony_ci /* Set PLL input clock divider 1/2 */ 6893d0407baSopenharmony_ci val = readl(priv->mmio + (0x5 << 2)); 6903d0407baSopenharmony_ci val &= ~(0x3 << 0x06); 6913d0407baSopenharmony_ci val |= 0x1 << 0x06; 6923d0407baSopenharmony_ci writel(val, priv->mmio + (0x5 << 0x02)); 6933d0407baSopenharmony_ci 6943d0407baSopenharmony_ci /* Set PLL loop divider */ 6953d0407baSopenharmony_ci writel(0x32, priv->mmio + (0x11 << 0x02)); 6963d0407baSopenharmony_ci 6973d0407baSopenharmony_ci /* Set PLL KVCO to min and set PLL charge pump current to max */ 6983d0407baSopenharmony_ci writel(0xf0, priv->mmio + (0xa << 0x02)); 6993d0407baSopenharmony_ci 7003d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false); 7013d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false); 7023d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->usb_mode_set, true); 7033d0407baSopenharmony_ci break; 7043d0407baSopenharmony_ci case PHY_TYPE_SATA: 7053d0407baSopenharmony_ci /* Enable adaptive CTLE for SATA Rx */ 7063d0407baSopenharmony_ci val = readl(priv->mmio + (0x0e << 0x02)); 7073d0407baSopenharmony_ci val &= ~GENMASK(0, 0); 7083d0407baSopenharmony_ci val |= 0x01; 7093d0407baSopenharmony_ci writel(val, priv->mmio + (0x0e << 0x02)); 7103d0407baSopenharmony_ci /* Set tx_rterm = 50 ohm and rx_rterm = 43.5 ohm */ 7113d0407baSopenharmony_ci writel(0x8F, priv->mmio + (0x06 << 0x02)); 7123d0407baSopenharmony_ci 7133d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con0_for_sata, true); 7143d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con1_for_sata, true); 7153d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con2_for_sata, true); 7163d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->con3_for_sata, true); 7173d0407baSopenharmony_ci param_write(priv->pipe_grf, &cfg->pipe_con0_for_sata, true); 7183d0407baSopenharmony_ci param_write(priv->pipe_grf, &cfg->pipe_con1_for_sata, true); 7193d0407baSopenharmony_ci break; 7203d0407baSopenharmony_ci case PHY_TYPE_SGMII: 7213d0407baSopenharmony_ci case PHY_TYPE_QSGMII: 7223d0407baSopenharmony_ci default: 7233d0407baSopenharmony_ci dev_err(priv->dev, "incompatible PHY type\n"); 7243d0407baSopenharmony_ci return -EINVAL; 7253d0407baSopenharmony_ci } 7263d0407baSopenharmony_ci 7273d0407baSopenharmony_ci rate = clk_get_rate(refclk); 7283d0407baSopenharmony_ci 7293d0407baSopenharmony_ci switch (rate) { 7303d0407baSopenharmony_ci case 0x16E3600: 7313d0407baSopenharmony_ci if (priv->mode == PHY_TYPE_USB3 || priv->mode == PHY_TYPE_SATA) { 7323d0407baSopenharmony_ci /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz */ 7333d0407baSopenharmony_ci val = readl(priv->mmio + (0x0e << 0x02)); 7343d0407baSopenharmony_ci val &= ~GENMASK(0x07, 0x06); 7353d0407baSopenharmony_ci val |= 0x01 << 0x06; 7363d0407baSopenharmony_ci writel(val, priv->mmio + (0x0e << 0x02)); 7373d0407baSopenharmony_ci 7383d0407baSopenharmony_ci val = readl(priv->mmio + (0x0f << 0x02)); 7393d0407baSopenharmony_ci val &= ~GENMASK(0x07, 0); 7403d0407baSopenharmony_ci val |= 0x5f; 7413d0407baSopenharmony_ci writel(val, priv->mmio + (0x0f << 0x02)); 7423d0407baSopenharmony_ci } 7433d0407baSopenharmony_ci break; 7443d0407baSopenharmony_ci case 0x17D7840: 7453d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_clk_25m, true); 7463d0407baSopenharmony_ci break; 7473d0407baSopenharmony_ci case 0x5F5E100: 7483d0407baSopenharmony_ci param_write(priv->phy_grf, &cfg->pipe_clk_100m, true); 7493d0407baSopenharmony_ci if (priv->mode == PHY_TYPE_PCIE) { 7503d0407baSopenharmony_ci /* PLL KVCO tuning fine */ 7513d0407baSopenharmony_ci val = readl(priv->mmio + (0x20 << 0x02)); 7523d0407baSopenharmony_ci val &= ~GENMASK(0x04, 0x02); 7533d0407baSopenharmony_ci val |= 0x4 << 0x02; 7543d0407baSopenharmony_ci writel(val, priv->mmio + (0x20 << 0x02)); 7553d0407baSopenharmony_ci 7563d0407baSopenharmony_ci /* Set up rx_trim: PLL LPF C1 85pf R1 1.25kohm */ 7573d0407baSopenharmony_ci val = 0x4c; 7583d0407baSopenharmony_ci writel(val, priv->mmio + (0x1b << 0x02)); 7593d0407baSopenharmony_ci 7603d0407baSopenharmony_ci /* Set up su_trim: */ 7613d0407baSopenharmony_ci val = 0xf0; 7623d0407baSopenharmony_ci writel(val, priv->mmio + (0xa << 0x02)); 7633d0407baSopenharmony_ci val = 0x4; 7643d0407baSopenharmony_ci writel(val, priv->mmio + (0xb << 0x02)); 7653d0407baSopenharmony_ci } else if (priv->mode == PHY_TYPE_SATA) { 7663d0407baSopenharmony_ci /* downward spread spectrum +500ppm */ 7673d0407baSopenharmony_ci val = readl(priv->mmio + (0x1f << 0x02)); 7683d0407baSopenharmony_ci val &= ~GENMASK(0x07, 0x04); 7693d0407baSopenharmony_ci val |= 0x50; 7703d0407baSopenharmony_ci writel(val, priv->mmio + (0x1f << 0x02)); 7713d0407baSopenharmony_ci } 7723d0407baSopenharmony_ci break; 7733d0407baSopenharmony_ci default: 7743d0407baSopenharmony_ci dev_err(priv->dev, "Unsupported rate: %lu\n", rate); 7753d0407baSopenharmony_ci return -EINVAL; 7763d0407baSopenharmony_ci } 7773d0407baSopenharmony_ci 7783d0407baSopenharmony_ci return 0; 7793d0407baSopenharmony_ci} 7803d0407baSopenharmony_ci 7813d0407baSopenharmony_cistatic const struct rockchip_combphy_grfcfg rk3588_combphy_grfcfgs = { 7823d0407baSopenharmony_ci /* pipe-phy-grf */ 7833d0407baSopenharmony_ci .pcie_mode_set = {0x0000, 5, 0, 0x00, 0x11}, 7843d0407baSopenharmony_ci .usb_mode_set = {0x0000, 5, 0, 0x00, 0x04}, 7853d0407baSopenharmony_ci .pipe_rxterm_set = {0x0000, 12, 12, 0x00, 0x01}, 7863d0407baSopenharmony_ci .pipe_txelec_set = {0x0004, 1, 1, 0x00, 0x01}, 7873d0407baSopenharmony_ci .pipe_txcomp_set = {0x0004, 4, 4, 0x00, 0x01}, 7883d0407baSopenharmony_ci .pipe_clk_25m = {0x0004, 14, 13, 0x00, 0x01}, 7893d0407baSopenharmony_ci .pipe_clk_100m = {0x0004, 14, 13, 0x00, 0x02}, 7903d0407baSopenharmony_ci .pipe_rxterm_sel = {0x0008, 8, 8, 0x00, 0x01}, 7913d0407baSopenharmony_ci .pipe_txelec_sel = {0x0008, 12, 12, 0x00, 0x01}, 7923d0407baSopenharmony_ci .pipe_txcomp_sel = {0x0008, 15, 15, 0x00, 0x01}, 7933d0407baSopenharmony_ci .pipe_clk_ext = {0x000c, 9, 8, 0x02, 0x01}, 7943d0407baSopenharmony_ci .pipe_phy_status = {0x0034, 6, 6, 0x01, 0x00}, 7953d0407baSopenharmony_ci .con0_for_pcie = {0x0000, 15, 0, 0x00, 0x1000}, 7963d0407baSopenharmony_ci .con1_for_pcie = {0x0004, 15, 0, 0x00, 0x0000}, 7973d0407baSopenharmony_ci .con2_for_pcie = {0x0008, 15, 0, 0x00, 0x0101}, 7983d0407baSopenharmony_ci .con3_for_pcie = {0x000c, 15, 0, 0x00, 0x0200}, 7993d0407baSopenharmony_ci .con0_for_sata = {0x0000, 15, 0, 0x00, 0x0129}, 8003d0407baSopenharmony_ci .con1_for_sata = {0x0004, 15, 0, 0x00, 0x0000}, 8013d0407baSopenharmony_ci .con2_for_sata = {0x0008, 15, 0, 0x00, 0x80c1}, 8023d0407baSopenharmony_ci .con3_for_sata = {0x000c, 15, 0, 0x00, 0x0407}, 8033d0407baSopenharmony_ci /* pipe-grf */ 8043d0407baSopenharmony_ci .pipe_con0_for_sata = {0x0000, 11, 5, 0x00, 0x22}, 8053d0407baSopenharmony_ci .pipe_con1_for_sata = {0x0000, 2, 0, 0x00, 0x2}, 8063d0407baSopenharmony_ci}; 8073d0407baSopenharmony_ci 8083d0407baSopenharmony_cistatic const struct clk_bulk_data rk3588_clks[] = { 8093d0407baSopenharmony_ci {.id = "refclk"}, 8103d0407baSopenharmony_ci {.id = "apbclk"}, 8113d0407baSopenharmony_ci {.id = "phpclk"}, 8123d0407baSopenharmony_ci}; 8133d0407baSopenharmony_ci 8143d0407baSopenharmony_cistatic const struct rockchip_combphy_cfg rk3588_combphy_cfgs = { 8153d0407baSopenharmony_ci .num_clks = ARRAY_SIZE(rk3588_clks), 8163d0407baSopenharmony_ci .clks = rk3588_clks, 8173d0407baSopenharmony_ci .grfcfg = &rk3588_combphy_grfcfgs, 8183d0407baSopenharmony_ci .combphy_cfg = rk3588_combphy_cfg, 8193d0407baSopenharmony_ci .force_det_out = true, 8203d0407baSopenharmony_ci}; 8213d0407baSopenharmony_ci 8223d0407baSopenharmony_cistatic const struct of_device_id rockchip_combphy_of_match[] = { 8233d0407baSopenharmony_ci { 8243d0407baSopenharmony_ci .compatible = "rockchip,rk3568-naneng-combphy", 8253d0407baSopenharmony_ci .data = &rk3568_combphy_cfgs, 8263d0407baSopenharmony_ci }, 8273d0407baSopenharmony_ci { 8283d0407baSopenharmony_ci .compatible = "rockchip,rk3588-naneng-combphy", 8293d0407baSopenharmony_ci .data = &rk3588_combphy_cfgs, 8303d0407baSopenharmony_ci }, 8313d0407baSopenharmony_ci {}, 8323d0407baSopenharmony_ci}; 8333d0407baSopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_combphy_of_match); 8343d0407baSopenharmony_ci 8353d0407baSopenharmony_cistatic struct platform_driver rockchip_combphy_driver = { 8363d0407baSopenharmony_ci .probe = rockchip_combphy_probe, 8373d0407baSopenharmony_ci .driver = 8383d0407baSopenharmony_ci { 8393d0407baSopenharmony_ci .name = "naneng-combphy", 8403d0407baSopenharmony_ci .of_match_table = rockchip_combphy_of_match, 8413d0407baSopenharmony_ci }, 8423d0407baSopenharmony_ci}; 8433d0407baSopenharmony_cimodule_platform_driver(rockchip_combphy_driver); 8443d0407baSopenharmony_ci 8453d0407baSopenharmony_ciMODULE_DESCRIPTION("Rockchip NANENG COMBPHY driver"); 8463d0407baSopenharmony_ciMODULE_LICENSE("GPL v2"); 847