162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Intel Combo-PHY driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019-2020 Intel Corporation. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bitfield.h> 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/iopoll.h> 1162306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/mutex.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/phy/phy.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/regmap.h> 1862306a36Sopenharmony_ci#include <linux/reset.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <dt-bindings/phy/phy.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define PCIE_PHY_GEN_CTRL 0x00 2362306a36Sopenharmony_ci#define PCIE_PHY_CLK_PAD BIT(17) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define PAD_DIS_CFG 0x174 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define PCS_XF_ATE_OVRD_IN_2 0x3008 2862306a36Sopenharmony_ci#define ADAPT_REQ_MSK GENMASK(5, 4) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define PCS_XF_RX_ADAPT_ACK 0x3010 3162306a36Sopenharmony_ci#define RX_ADAPT_ACK_BIT BIT(0) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define CR_ADDR(addr, lane) (((addr) + (lane) * 0x100) << 2) 3462306a36Sopenharmony_ci#define REG_COMBO_MODE(x) ((x) * 0x200) 3562306a36Sopenharmony_ci#define REG_CLK_DISABLE(x) ((x) * 0x200 + 0x124) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define COMBO_PHY_ID(x) ((x)->parent->id) 3862306a36Sopenharmony_ci#define PHY_ID(x) ((x)->id) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define CLK_100MHZ 100000000 4162306a36Sopenharmony_ci#define CLK_156_25MHZ 156250000 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic const unsigned long intel_iphy_clk_rates[] = { 4462306a36Sopenharmony_ci CLK_100MHZ, CLK_156_25MHZ, CLK_100MHZ, 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cienum { 4862306a36Sopenharmony_ci PHY_0, 4962306a36Sopenharmony_ci PHY_1, 5062306a36Sopenharmony_ci PHY_MAX_NUM 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* 5462306a36Sopenharmony_ci * Clock Register bit fields to enable clocks 5562306a36Sopenharmony_ci * for ComboPhy according to the mode. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_cienum intel_phy_mode { 5862306a36Sopenharmony_ci PHY_PCIE_MODE = 0, 5962306a36Sopenharmony_ci PHY_XPCS_MODE, 6062306a36Sopenharmony_ci PHY_SATA_MODE, 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* ComboPhy mode Register values */ 6462306a36Sopenharmony_cienum intel_combo_mode { 6562306a36Sopenharmony_ci PCIE0_PCIE1_MODE = 0, 6662306a36Sopenharmony_ci PCIE_DL_MODE, 6762306a36Sopenharmony_ci RXAUI_MODE, 6862306a36Sopenharmony_ci XPCS0_XPCS1_MODE, 6962306a36Sopenharmony_ci SATA0_SATA1_MODE, 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cienum aggregated_mode { 7362306a36Sopenharmony_ci PHY_SL_MODE, 7462306a36Sopenharmony_ci PHY_DL_MODE, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct intel_combo_phy; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistruct intel_cbphy_iphy { 8062306a36Sopenharmony_ci struct phy *phy; 8162306a36Sopenharmony_ci struct intel_combo_phy *parent; 8262306a36Sopenharmony_ci struct reset_control *app_rst; 8362306a36Sopenharmony_ci u32 id; 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistruct intel_combo_phy { 8762306a36Sopenharmony_ci struct device *dev; 8862306a36Sopenharmony_ci struct clk *core_clk; 8962306a36Sopenharmony_ci unsigned long clk_rate; 9062306a36Sopenharmony_ci void __iomem *app_base; 9162306a36Sopenharmony_ci void __iomem *cr_base; 9262306a36Sopenharmony_ci struct regmap *syscfg; 9362306a36Sopenharmony_ci struct regmap *hsiocfg; 9462306a36Sopenharmony_ci u32 id; 9562306a36Sopenharmony_ci u32 bid; 9662306a36Sopenharmony_ci struct reset_control *phy_rst; 9762306a36Sopenharmony_ci struct reset_control *core_rst; 9862306a36Sopenharmony_ci struct intel_cbphy_iphy iphy[PHY_MAX_NUM]; 9962306a36Sopenharmony_ci enum intel_phy_mode phy_mode; 10062306a36Sopenharmony_ci enum aggregated_mode aggr_mode; 10162306a36Sopenharmony_ci u32 init_cnt; 10262306a36Sopenharmony_ci struct mutex lock; 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int intel_cbphy_iphy_enable(struct intel_cbphy_iphy *iphy, bool set) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct intel_combo_phy *cbphy = iphy->parent; 10862306a36Sopenharmony_ci u32 mask = BIT(cbphy->phy_mode * 2 + iphy->id); 10962306a36Sopenharmony_ci u32 val; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* Register: 0 is enable, 1 is disable */ 11262306a36Sopenharmony_ci val = set ? 0 : mask; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return regmap_update_bits(cbphy->hsiocfg, REG_CLK_DISABLE(cbphy->bid), 11562306a36Sopenharmony_ci mask, val); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int intel_cbphy_pcie_refclk_cfg(struct intel_cbphy_iphy *iphy, bool set) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct intel_combo_phy *cbphy = iphy->parent; 12162306a36Sopenharmony_ci u32 mask = BIT(cbphy->id * 2 + iphy->id); 12262306a36Sopenharmony_ci u32 val; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Register: 0 is enable, 1 is disable */ 12562306a36Sopenharmony_ci val = set ? 0 : mask; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return regmap_update_bits(cbphy->syscfg, PAD_DIS_CFG, mask, val); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic inline void combo_phy_w32_off_mask(void __iomem *base, unsigned int reg, 13162306a36Sopenharmony_ci u32 mask, u32 val) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci u32 reg_val; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci reg_val = readl(base + reg); 13662306a36Sopenharmony_ci reg_val &= ~mask; 13762306a36Sopenharmony_ci reg_val |= val; 13862306a36Sopenharmony_ci writel(reg_val, base + reg); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int intel_cbphy_iphy_cfg(struct intel_cbphy_iphy *iphy, 14262306a36Sopenharmony_ci int (*phy_cfg)(struct intel_cbphy_iphy *)) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct intel_combo_phy *cbphy = iphy->parent; 14562306a36Sopenharmony_ci int ret; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ret = phy_cfg(iphy); 14862306a36Sopenharmony_ci if (ret) 14962306a36Sopenharmony_ci return ret; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (cbphy->aggr_mode != PHY_DL_MODE) 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return phy_cfg(&cbphy->iphy[PHY_1]); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int intel_cbphy_pcie_en_pad_refclk(struct intel_cbphy_iphy *iphy) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct intel_combo_phy *cbphy = iphy->parent; 16062306a36Sopenharmony_ci int ret; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci ret = intel_cbphy_pcie_refclk_cfg(iphy, true); 16362306a36Sopenharmony_ci if (ret) { 16462306a36Sopenharmony_ci dev_err(cbphy->dev, "Failed to enable PCIe pad refclk\n"); 16562306a36Sopenharmony_ci return ret; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (cbphy->init_cnt) 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL, 17262306a36Sopenharmony_ci PCIE_PHY_CLK_PAD, FIELD_PREP(PCIE_PHY_CLK_PAD, 0)); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Delay for stable clock PLL */ 17562306a36Sopenharmony_ci usleep_range(50, 100); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int intel_cbphy_pcie_dis_pad_refclk(struct intel_cbphy_iphy *iphy) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct intel_combo_phy *cbphy = iphy->parent; 18362306a36Sopenharmony_ci int ret; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ret = intel_cbphy_pcie_refclk_cfg(iphy, false); 18662306a36Sopenharmony_ci if (ret) { 18762306a36Sopenharmony_ci dev_err(cbphy->dev, "Failed to disable PCIe pad refclk\n"); 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (cbphy->init_cnt) 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL, 19562306a36Sopenharmony_ci PCIE_PHY_CLK_PAD, FIELD_PREP(PCIE_PHY_CLK_PAD, 1)); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic int intel_cbphy_set_mode(struct intel_combo_phy *cbphy) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci enum intel_combo_mode cb_mode; 20362306a36Sopenharmony_ci enum aggregated_mode aggr = cbphy->aggr_mode; 20462306a36Sopenharmony_ci struct device *dev = cbphy->dev; 20562306a36Sopenharmony_ci enum intel_phy_mode mode; 20662306a36Sopenharmony_ci int ret; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci mode = cbphy->phy_mode; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci switch (mode) { 21162306a36Sopenharmony_ci case PHY_PCIE_MODE: 21262306a36Sopenharmony_ci cb_mode = (aggr == PHY_DL_MODE) ? PCIE_DL_MODE : PCIE0_PCIE1_MODE; 21362306a36Sopenharmony_ci break; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci case PHY_XPCS_MODE: 21662306a36Sopenharmony_ci cb_mode = (aggr == PHY_DL_MODE) ? RXAUI_MODE : XPCS0_XPCS1_MODE; 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci case PHY_SATA_MODE: 22062306a36Sopenharmony_ci if (aggr == PHY_DL_MODE) { 22162306a36Sopenharmony_ci dev_err(dev, "Mode:%u not support dual lane!\n", mode); 22262306a36Sopenharmony_ci return -EINVAL; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci cb_mode = SATA0_SATA1_MODE; 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci default: 22862306a36Sopenharmony_ci return -EINVAL; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ret = regmap_write(cbphy->hsiocfg, REG_COMBO_MODE(cbphy->bid), cb_mode); 23262306a36Sopenharmony_ci if (ret) 23362306a36Sopenharmony_ci dev_err(dev, "Failed to set ComboPhy mode: %d\n", ret); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return ret; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic void intel_cbphy_rst_assert(struct intel_combo_phy *cbphy) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci reset_control_assert(cbphy->core_rst); 24162306a36Sopenharmony_ci reset_control_assert(cbphy->phy_rst); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic void intel_cbphy_rst_deassert(struct intel_combo_phy *cbphy) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci reset_control_deassert(cbphy->core_rst); 24762306a36Sopenharmony_ci reset_control_deassert(cbphy->phy_rst); 24862306a36Sopenharmony_ci /* Delay to ensure reset process is done */ 24962306a36Sopenharmony_ci usleep_range(10, 20); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int intel_cbphy_iphy_power_on(struct intel_cbphy_iphy *iphy) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct intel_combo_phy *cbphy = iphy->parent; 25562306a36Sopenharmony_ci int ret; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (!cbphy->init_cnt) { 25862306a36Sopenharmony_ci ret = clk_prepare_enable(cbphy->core_clk); 25962306a36Sopenharmony_ci if (ret) { 26062306a36Sopenharmony_ci dev_err(cbphy->dev, "Clock enable failed!\n"); 26162306a36Sopenharmony_ci return ret; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci ret = clk_set_rate(cbphy->core_clk, cbphy->clk_rate); 26562306a36Sopenharmony_ci if (ret) { 26662306a36Sopenharmony_ci dev_err(cbphy->dev, "Clock freq set to %lu failed!\n", 26762306a36Sopenharmony_ci cbphy->clk_rate); 26862306a36Sopenharmony_ci goto clk_err; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci intel_cbphy_rst_assert(cbphy); 27262306a36Sopenharmony_ci intel_cbphy_rst_deassert(cbphy); 27362306a36Sopenharmony_ci ret = intel_cbphy_set_mode(cbphy); 27462306a36Sopenharmony_ci if (ret) 27562306a36Sopenharmony_ci goto clk_err; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci ret = intel_cbphy_iphy_enable(iphy, true); 27962306a36Sopenharmony_ci if (ret) { 28062306a36Sopenharmony_ci dev_err(cbphy->dev, "Failed enabling PHY core\n"); 28162306a36Sopenharmony_ci goto clk_err; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci ret = reset_control_deassert(iphy->app_rst); 28562306a36Sopenharmony_ci if (ret) { 28662306a36Sopenharmony_ci dev_err(cbphy->dev, "PHY(%u:%u) reset deassert failed!\n", 28762306a36Sopenharmony_ci COMBO_PHY_ID(iphy), PHY_ID(iphy)); 28862306a36Sopenharmony_ci goto clk_err; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* Delay to ensure reset process is done */ 29262306a36Sopenharmony_ci udelay(1); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ciclk_err: 29762306a36Sopenharmony_ci clk_disable_unprepare(cbphy->core_clk); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return ret; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int intel_cbphy_iphy_power_off(struct intel_cbphy_iphy *iphy) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct intel_combo_phy *cbphy = iphy->parent; 30562306a36Sopenharmony_ci int ret; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci ret = reset_control_assert(iphy->app_rst); 30862306a36Sopenharmony_ci if (ret) { 30962306a36Sopenharmony_ci dev_err(cbphy->dev, "PHY(%u:%u) reset assert failed!\n", 31062306a36Sopenharmony_ci COMBO_PHY_ID(iphy), PHY_ID(iphy)); 31162306a36Sopenharmony_ci return ret; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ret = intel_cbphy_iphy_enable(iphy, false); 31562306a36Sopenharmony_ci if (ret) { 31662306a36Sopenharmony_ci dev_err(cbphy->dev, "Failed disabling PHY core\n"); 31762306a36Sopenharmony_ci return ret; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (cbphy->init_cnt) 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci clk_disable_unprepare(cbphy->core_clk); 32462306a36Sopenharmony_ci intel_cbphy_rst_assert(cbphy); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return 0; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic int intel_cbphy_init(struct phy *phy) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy); 33262306a36Sopenharmony_ci struct intel_combo_phy *cbphy = iphy->parent; 33362306a36Sopenharmony_ci int ret; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci mutex_lock(&cbphy->lock); 33662306a36Sopenharmony_ci ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_on); 33762306a36Sopenharmony_ci if (ret) 33862306a36Sopenharmony_ci goto err; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (cbphy->phy_mode == PHY_PCIE_MODE) { 34162306a36Sopenharmony_ci ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_en_pad_refclk); 34262306a36Sopenharmony_ci if (ret) 34362306a36Sopenharmony_ci goto err; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci cbphy->init_cnt++; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cierr: 34962306a36Sopenharmony_ci mutex_unlock(&cbphy->lock); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return ret; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int intel_cbphy_exit(struct phy *phy) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy); 35762306a36Sopenharmony_ci struct intel_combo_phy *cbphy = iphy->parent; 35862306a36Sopenharmony_ci int ret; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci mutex_lock(&cbphy->lock); 36162306a36Sopenharmony_ci cbphy->init_cnt--; 36262306a36Sopenharmony_ci if (cbphy->phy_mode == PHY_PCIE_MODE) { 36362306a36Sopenharmony_ci ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_dis_pad_refclk); 36462306a36Sopenharmony_ci if (ret) 36562306a36Sopenharmony_ci goto err; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_off); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cierr: 37162306a36Sopenharmony_ci mutex_unlock(&cbphy->lock); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return ret; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int intel_cbphy_calibrate(struct phy *phy) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy); 37962306a36Sopenharmony_ci struct intel_combo_phy *cbphy = iphy->parent; 38062306a36Sopenharmony_ci void __iomem *cr_base = cbphy->cr_base; 38162306a36Sopenharmony_ci int val, ret, id; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (cbphy->phy_mode != PHY_XPCS_MODE) 38462306a36Sopenharmony_ci return 0; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci id = PHY_ID(iphy); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* trigger auto RX adaptation */ 38962306a36Sopenharmony_ci combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id), 39062306a36Sopenharmony_ci ADAPT_REQ_MSK, FIELD_PREP(ADAPT_REQ_MSK, 3)); 39162306a36Sopenharmony_ci /* Wait RX adaptation to finish */ 39262306a36Sopenharmony_ci ret = readl_poll_timeout(cr_base + CR_ADDR(PCS_XF_RX_ADAPT_ACK, id), 39362306a36Sopenharmony_ci val, val & RX_ADAPT_ACK_BIT, 10, 5000); 39462306a36Sopenharmony_ci if (ret) 39562306a36Sopenharmony_ci dev_err(cbphy->dev, "RX Adaptation failed!\n"); 39662306a36Sopenharmony_ci else 39762306a36Sopenharmony_ci dev_dbg(cbphy->dev, "RX Adaptation success!\n"); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Stop RX adaptation */ 40062306a36Sopenharmony_ci combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id), 40162306a36Sopenharmony_ci ADAPT_REQ_MSK, FIELD_PREP(ADAPT_REQ_MSK, 0)); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return ret; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic int intel_cbphy_fwnode_parse(struct intel_combo_phy *cbphy) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct device *dev = cbphy->dev; 40962306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 41062306a36Sopenharmony_ci struct fwnode_handle *fwnode = dev_fwnode(dev); 41162306a36Sopenharmony_ci struct fwnode_reference_args ref; 41262306a36Sopenharmony_ci int ret; 41362306a36Sopenharmony_ci u32 val; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci cbphy->core_clk = devm_clk_get(dev, NULL); 41662306a36Sopenharmony_ci if (IS_ERR(cbphy->core_clk)) 41762306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(cbphy->core_clk), 41862306a36Sopenharmony_ci "Get clk failed!\n"); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci cbphy->core_rst = devm_reset_control_get_optional(dev, "core"); 42162306a36Sopenharmony_ci if (IS_ERR(cbphy->core_rst)) 42262306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(cbphy->core_rst), 42362306a36Sopenharmony_ci "Get core reset control err!\n"); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci cbphy->phy_rst = devm_reset_control_get_optional(dev, "phy"); 42662306a36Sopenharmony_ci if (IS_ERR(cbphy->phy_rst)) 42762306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(cbphy->phy_rst), 42862306a36Sopenharmony_ci "Get PHY reset control err!\n"); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci cbphy->iphy[0].app_rst = devm_reset_control_get_optional(dev, "iphy0"); 43162306a36Sopenharmony_ci if (IS_ERR(cbphy->iphy[0].app_rst)) 43262306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(cbphy->iphy[0].app_rst), 43362306a36Sopenharmony_ci "Get phy0 reset control err!\n"); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci cbphy->iphy[1].app_rst = devm_reset_control_get_optional(dev, "iphy1"); 43662306a36Sopenharmony_ci if (IS_ERR(cbphy->iphy[1].app_rst)) 43762306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(cbphy->iphy[1].app_rst), 43862306a36Sopenharmony_ci "Get phy1 reset control err!\n"); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci cbphy->app_base = devm_platform_ioremap_resource_byname(pdev, "app"); 44162306a36Sopenharmony_ci if (IS_ERR(cbphy->app_base)) 44262306a36Sopenharmony_ci return PTR_ERR(cbphy->app_base); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci cbphy->cr_base = devm_platform_ioremap_resource_byname(pdev, "core"); 44562306a36Sopenharmony_ci if (IS_ERR(cbphy->cr_base)) 44662306a36Sopenharmony_ci return PTR_ERR(cbphy->cr_base); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * syscfg and hsiocfg variables stores the handle of the registers set 45062306a36Sopenharmony_ci * in which ComboPhy subsystem specific registers are subset. Using 45162306a36Sopenharmony_ci * Register map framework to access the registers set. 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ci ret = fwnode_property_get_reference_args(fwnode, "intel,syscfg", NULL, 45462306a36Sopenharmony_ci 1, 0, &ref); 45562306a36Sopenharmony_ci if (ret < 0) 45662306a36Sopenharmony_ci return ret; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci cbphy->id = ref.args[0]; 45962306a36Sopenharmony_ci cbphy->syscfg = device_node_to_regmap(to_of_node(ref.fwnode)); 46062306a36Sopenharmony_ci fwnode_handle_put(ref.fwnode); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ret = fwnode_property_get_reference_args(fwnode, "intel,hsio", NULL, 1, 46362306a36Sopenharmony_ci 0, &ref); 46462306a36Sopenharmony_ci if (ret < 0) 46562306a36Sopenharmony_ci return ret; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci cbphy->bid = ref.args[0]; 46862306a36Sopenharmony_ci cbphy->hsiocfg = device_node_to_regmap(to_of_node(ref.fwnode)); 46962306a36Sopenharmony_ci fwnode_handle_put(ref.fwnode); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci ret = fwnode_property_read_u32_array(fwnode, "intel,phy-mode", &val, 1); 47262306a36Sopenharmony_ci if (ret) 47362306a36Sopenharmony_ci return ret; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci switch (val) { 47662306a36Sopenharmony_ci case PHY_TYPE_PCIE: 47762306a36Sopenharmony_ci cbphy->phy_mode = PHY_PCIE_MODE; 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci case PHY_TYPE_SATA: 48162306a36Sopenharmony_ci cbphy->phy_mode = PHY_SATA_MODE; 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci case PHY_TYPE_XPCS: 48562306a36Sopenharmony_ci cbphy->phy_mode = PHY_XPCS_MODE; 48662306a36Sopenharmony_ci break; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci default: 48962306a36Sopenharmony_ci dev_err(dev, "Invalid PHY mode: %u\n", val); 49062306a36Sopenharmony_ci return -EINVAL; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci cbphy->clk_rate = intel_iphy_clk_rates[cbphy->phy_mode]; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (fwnode_property_present(fwnode, "intel,aggregation")) 49662306a36Sopenharmony_ci cbphy->aggr_mode = PHY_DL_MODE; 49762306a36Sopenharmony_ci else 49862306a36Sopenharmony_ci cbphy->aggr_mode = PHY_SL_MODE; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic const struct phy_ops intel_cbphy_ops = { 50462306a36Sopenharmony_ci .init = intel_cbphy_init, 50562306a36Sopenharmony_ci .exit = intel_cbphy_exit, 50662306a36Sopenharmony_ci .calibrate = intel_cbphy_calibrate, 50762306a36Sopenharmony_ci .owner = THIS_MODULE, 50862306a36Sopenharmony_ci}; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic struct phy *intel_cbphy_xlate(struct device *dev, 51162306a36Sopenharmony_ci struct of_phandle_args *args) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct intel_combo_phy *cbphy = dev_get_drvdata(dev); 51462306a36Sopenharmony_ci u32 iphy_id; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (args->args_count < 1) { 51762306a36Sopenharmony_ci dev_err(dev, "Invalid number of arguments\n"); 51862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci iphy_id = args->args[0]; 52262306a36Sopenharmony_ci if (iphy_id >= PHY_MAX_NUM) { 52362306a36Sopenharmony_ci dev_err(dev, "Invalid phy instance %d\n", iphy_id); 52462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (cbphy->aggr_mode == PHY_DL_MODE && iphy_id == PHY_1) { 52862306a36Sopenharmony_ci dev_err(dev, "Invalid. ComboPhy is in Dual lane mode %d\n", iphy_id); 52962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return cbphy->iphy[iphy_id].phy; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int intel_cbphy_create(struct intel_combo_phy *cbphy) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct phy_provider *phy_provider; 53862306a36Sopenharmony_ci struct device *dev = cbphy->dev; 53962306a36Sopenharmony_ci struct intel_cbphy_iphy *iphy; 54062306a36Sopenharmony_ci int i; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci for (i = 0; i < PHY_MAX_NUM; i++) { 54362306a36Sopenharmony_ci iphy = &cbphy->iphy[i]; 54462306a36Sopenharmony_ci iphy->parent = cbphy; 54562306a36Sopenharmony_ci iphy->id = i; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* In dual lane mode skip phy creation for the second phy */ 54862306a36Sopenharmony_ci if (cbphy->aggr_mode == PHY_DL_MODE && iphy->id == PHY_1) 54962306a36Sopenharmony_ci continue; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci iphy->phy = devm_phy_create(dev, NULL, &intel_cbphy_ops); 55262306a36Sopenharmony_ci if (IS_ERR(iphy->phy)) { 55362306a36Sopenharmony_ci dev_err(dev, "PHY[%u:%u]: create PHY instance failed!\n", 55462306a36Sopenharmony_ci COMBO_PHY_ID(iphy), PHY_ID(iphy)); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return PTR_ERR(iphy->phy); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci phy_set_drvdata(iphy->phy, iphy); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci dev_set_drvdata(dev, cbphy); 56362306a36Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, intel_cbphy_xlate); 56462306a36Sopenharmony_ci if (IS_ERR(phy_provider)) 56562306a36Sopenharmony_ci dev_err(dev, "Register PHY provider failed!\n"); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic int intel_cbphy_probe(struct platform_device *pdev) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 57362306a36Sopenharmony_ci struct intel_combo_phy *cbphy; 57462306a36Sopenharmony_ci int ret; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci cbphy = devm_kzalloc(dev, sizeof(*cbphy), GFP_KERNEL); 57762306a36Sopenharmony_ci if (!cbphy) 57862306a36Sopenharmony_ci return -ENOMEM; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci cbphy->dev = dev; 58162306a36Sopenharmony_ci cbphy->init_cnt = 0; 58262306a36Sopenharmony_ci mutex_init(&cbphy->lock); 58362306a36Sopenharmony_ci ret = intel_cbphy_fwnode_parse(cbphy); 58462306a36Sopenharmony_ci if (ret) 58562306a36Sopenharmony_ci return ret; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci platform_set_drvdata(pdev, cbphy); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci return intel_cbphy_create(cbphy); 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic void intel_cbphy_remove(struct platform_device *pdev) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct intel_combo_phy *cbphy = platform_get_drvdata(pdev); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci intel_cbphy_rst_assert(cbphy); 59762306a36Sopenharmony_ci clk_disable_unprepare(cbphy->core_clk); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic const struct of_device_id of_intel_cbphy_match[] = { 60162306a36Sopenharmony_ci { .compatible = "intel,combo-phy" }, 60262306a36Sopenharmony_ci { .compatible = "intel,combophy-lgm" }, 60362306a36Sopenharmony_ci {} 60462306a36Sopenharmony_ci}; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic struct platform_driver intel_cbphy_driver = { 60762306a36Sopenharmony_ci .probe = intel_cbphy_probe, 60862306a36Sopenharmony_ci .remove_new = intel_cbphy_remove, 60962306a36Sopenharmony_ci .driver = { 61062306a36Sopenharmony_ci .name = "intel-combo-phy", 61162306a36Sopenharmony_ci .of_match_table = of_intel_cbphy_match, 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci}; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cimodule_platform_driver(intel_cbphy_driver); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel Combo-phy driver"); 618