diff options
Diffstat (limited to 'drivers/phy/rockchip/phy-rockchip-pcie.c')
| -rw-r--r-- | drivers/phy/rockchip/phy-rockchip-pcie.c | 241 |
1 files changed, 69 insertions, 172 deletions
diff --git a/drivers/phy/rockchip/phy-rockchip-pcie.c b/drivers/phy/rockchip/phy-rockchip-pcie.c index 75216091d901..126306c01454 100644 --- a/drivers/phy/rockchip/phy-rockchip-pcie.c +++ b/drivers/phy/rockchip/phy-rockchip-pcie.c @@ -8,34 +8,24 @@ #include <linux/clk.h> #include <linux/delay.h> +#include <linux/hw_bitfield.h> #include <linux/io.h> #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_platform.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/reset.h> -/* - * The higher 16-bit of this register is used for write protection - * only if BIT(x + 16) set to 1 the BIT(x) can be written. - */ -#define HIWORD_UPDATE(val, mask, shift) \ - ((val) << (shift) | (mask) << ((shift) + 16)) #define PHY_MAX_LANE_NUM 4 -#define PHY_CFG_DATA_SHIFT 7 -#define PHY_CFG_ADDR_SHIFT 1 -#define PHY_CFG_DATA_MASK 0xf -#define PHY_CFG_ADDR_MASK 0x3f -#define PHY_CFG_RD_MASK 0x3ff +#define PHY_CFG_DATA_MASK GENMASK(10, 7) +#define PHY_CFG_ADDR_MASK GENMASK(6, 1) #define PHY_CFG_WR_ENABLE 1 -#define PHY_CFG_WR_DISABLE 1 -#define PHY_CFG_WR_SHIFT 0 -#define PHY_CFG_WR_MASK 1 +#define PHY_CFG_WR_DISABLE 0 +#define PHY_CFG_WR_MASK BIT(0) #define PHY_CFG_PLL_LOCK 0x10 #define PHY_CFG_CLK_TEST 0x10 #define PHY_CFG_CLK_SCC 0x12 @@ -50,11 +40,7 @@ #define PHY_LANE_RX_DET_SHIFT 11 #define PHY_LANE_RX_DET_TH 0x1 #define PHY_LANE_IDLE_OFF 0x1 -#define PHY_LANE_IDLE_MASK 0x1 -#define PHY_LANE_IDLE_A_SHIFT 3 -#define PHY_LANE_IDLE_B_SHIFT 4 -#define PHY_LANE_IDLE_C_SHIFT 5 -#define PHY_LANE_IDLE_D_SHIFT 6 +#define PHY_LANE_IDLE_MASK BIT(3) struct rockchip_pcie_data { unsigned int pcie_conf; @@ -63,7 +49,7 @@ struct rockchip_pcie_data { }; struct rockchip_pcie_phy { - struct rockchip_pcie_data *phy_data; + const struct rockchip_pcie_data *phy_data; struct regmap *reg_base; struct phy_pcie_instance { struct phy *phy; @@ -83,7 +69,7 @@ static struct rockchip_pcie_phy *to_pcie_phy(struct phy_pcie_instance *inst) } static struct phy *rockchip_pcie_phy_of_xlate(struct device *dev, - struct of_phandle_args *args) + const struct of_phandle_args *args) { struct rockchip_pcie_phy *rk_phy = dev_get_drvdata(dev); @@ -101,37 +87,14 @@ static inline void phy_wr_cfg(struct rockchip_pcie_phy *rk_phy, u32 addr, u32 data) { regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, - HIWORD_UPDATE(data, - PHY_CFG_DATA_MASK, - PHY_CFG_DATA_SHIFT) | - HIWORD_UPDATE(addr, - PHY_CFG_ADDR_MASK, - PHY_CFG_ADDR_SHIFT)); + FIELD_PREP_WM16(PHY_CFG_DATA_MASK, data) | + FIELD_PREP_WM16(PHY_CFG_ADDR_MASK, addr)); udelay(1); regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, - HIWORD_UPDATE(PHY_CFG_WR_ENABLE, - PHY_CFG_WR_MASK, - PHY_CFG_WR_SHIFT)); + FIELD_PREP_WM16(PHY_CFG_WR_MASK, PHY_CFG_WR_ENABLE)); udelay(1); regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, - HIWORD_UPDATE(PHY_CFG_WR_DISABLE, - PHY_CFG_WR_MASK, - PHY_CFG_WR_SHIFT)); -} - -static inline u32 phy_rd_cfg(struct rockchip_pcie_phy *rk_phy, - u32 addr) -{ - u32 val; - - regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, - HIWORD_UPDATE(addr, - PHY_CFG_RD_MASK, - PHY_CFG_ADDR_SHIFT)); - regmap_read(rk_phy->reg_base, - rk_phy->phy_data->pcie_status, - &val); - return val; + FIELD_PREP_WM16(PHY_CFG_WR_MASK, PHY_CFG_WR_DISABLE)); } static int rockchip_pcie_phy_power_off(struct phy *phy) @@ -140,35 +103,26 @@ static int rockchip_pcie_phy_power_off(struct phy *phy) struct rockchip_pcie_phy *rk_phy = to_pcie_phy(inst); int err = 0; - mutex_lock(&rk_phy->pcie_mutex); + guard(mutex)(&rk_phy->pcie_mutex); - regmap_write(rk_phy->reg_base, - rk_phy->phy_data->pcie_laneoff, - HIWORD_UPDATE(PHY_LANE_IDLE_OFF, - PHY_LANE_IDLE_MASK, - PHY_LANE_IDLE_A_SHIFT + inst->index)); + regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_laneoff, + FIELD_PREP_WM16(PHY_LANE_IDLE_MASK, + PHY_LANE_IDLE_OFF) << inst->index); - if (--rk_phy->pwr_cnt) - goto err_out; + if (--rk_phy->pwr_cnt) { + return 0; + } err = reset_control_assert(rk_phy->phy_rst); if (err) { dev_err(&phy->dev, "assert phy_rst err %d\n", err); - goto err_restore; + rk_phy->pwr_cnt++; + regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_laneoff, + FIELD_PREP_WM16(PHY_LANE_IDLE_MASK, + !PHY_LANE_IDLE_OFF) << inst->index); + return err; } -err_out: - mutex_unlock(&rk_phy->pcie_mutex); - return 0; - -err_restore: - rk_phy->pwr_cnt++; - regmap_write(rk_phy->reg_base, - rk_phy->phy_data->pcie_laneoff, - HIWORD_UPDATE(!PHY_LANE_IDLE_OFF, - PHY_LANE_IDLE_MASK, - PHY_LANE_IDLE_A_SHIFT + inst->index)); - mutex_unlock(&rk_phy->pcie_mutex); return err; } @@ -178,50 +132,37 @@ static int rockchip_pcie_phy_power_on(struct phy *phy) struct rockchip_pcie_phy *rk_phy = to_pcie_phy(inst); int err = 0; u32 status; - unsigned long timeout; - mutex_lock(&rk_phy->pcie_mutex); + guard(mutex)(&rk_phy->pcie_mutex); - if (rk_phy->pwr_cnt++) - goto err_out; + regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_laneoff, + FIELD_PREP_WM16(PHY_LANE_IDLE_MASK, + !PHY_LANE_IDLE_OFF) << inst->index); + + if (rk_phy->pwr_cnt++) { + return 0; + } err = reset_control_deassert(rk_phy->phy_rst); if (err) { dev_err(&phy->dev, "deassert phy_rst err %d\n", err); - goto err_pwr_cnt; + rk_phy->pwr_cnt--; + return err; } regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, - HIWORD_UPDATE(PHY_CFG_PLL_LOCK, - PHY_CFG_ADDR_MASK, - PHY_CFG_ADDR_SHIFT)); - - regmap_write(rk_phy->reg_base, - rk_phy->phy_data->pcie_laneoff, - HIWORD_UPDATE(!PHY_LANE_IDLE_OFF, - PHY_LANE_IDLE_MASK, - PHY_LANE_IDLE_A_SHIFT + inst->index)); + FIELD_PREP_WM16(PHY_CFG_ADDR_MASK, PHY_CFG_PLL_LOCK)); /* * No documented timeout value for phy operation below, * so we make it large enough here. And we use loop-break * method which should not be harmful. */ - timeout = jiffies + msecs_to_jiffies(1000); - - err = -EINVAL; - while (time_before(jiffies, timeout)) { - regmap_read(rk_phy->reg_base, - rk_phy->phy_data->pcie_status, - &status); - if (status & PHY_PLL_LOCKED) { - dev_dbg(&phy->dev, "pll locked!\n"); - err = 0; - break; - } - msleep(20); - } - + err = regmap_read_poll_timeout(rk_phy->reg_base, + rk_phy->phy_data->pcie_status, + status, + status & PHY_PLL_LOCKED, + 200, 100000); if (err) { dev_err(&phy->dev, "pll lock timeout!\n"); goto err_pll_lock; @@ -230,55 +171,34 @@ static int rockchip_pcie_phy_power_on(struct phy *phy) phy_wr_cfg(rk_phy, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE); phy_wr_cfg(rk_phy, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M); - err = -ETIMEDOUT; - while (time_before(jiffies, timeout)) { - regmap_read(rk_phy->reg_base, - rk_phy->phy_data->pcie_status, - &status); - if (!(status & PHY_PLL_OUTPUT)) { - dev_dbg(&phy->dev, "pll output enable done!\n"); - err = 0; - break; - } - msleep(20); - } - + err = regmap_read_poll_timeout(rk_phy->reg_base, + rk_phy->phy_data->pcie_status, + status, + !(status & PHY_PLL_OUTPUT), + 200, 100000); if (err) { dev_err(&phy->dev, "pll output enable timeout!\n"); goto err_pll_lock; } regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, - HIWORD_UPDATE(PHY_CFG_PLL_LOCK, - PHY_CFG_ADDR_MASK, - PHY_CFG_ADDR_SHIFT)); - err = -EINVAL; - while (time_before(jiffies, timeout)) { - regmap_read(rk_phy->reg_base, - rk_phy->phy_data->pcie_status, - &status); - if (status & PHY_PLL_LOCKED) { - dev_dbg(&phy->dev, "pll relocked!\n"); - err = 0; - break; - } - msleep(20); - } + FIELD_PREP_WM16(PHY_CFG_ADDR_MASK, PHY_CFG_PLL_LOCK)); + err = regmap_read_poll_timeout(rk_phy->reg_base, + rk_phy->phy_data->pcie_status, + status, + status & PHY_PLL_LOCKED, + 200, 100000); if (err) { dev_err(&phy->dev, "pll relock timeout!\n"); goto err_pll_lock; } -err_out: - mutex_unlock(&rk_phy->pcie_mutex); - return 0; + return err; err_pll_lock: reset_control_assert(rk_phy->phy_rst); -err_pwr_cnt: rk_phy->pwr_cnt--; - mutex_unlock(&rk_phy->pcie_mutex); return err; } @@ -288,33 +208,19 @@ static int rockchip_pcie_phy_init(struct phy *phy) struct rockchip_pcie_phy *rk_phy = to_pcie_phy(inst); int err = 0; - mutex_lock(&rk_phy->pcie_mutex); - - if (rk_phy->init_cnt++) - goto err_out; + guard(mutex)(&rk_phy->pcie_mutex); - err = clk_prepare_enable(rk_phy->clk_pciephy_ref); - if (err) { - dev_err(&phy->dev, "Fail to enable pcie ref clock.\n"); - goto err_refclk; + if (rk_phy->init_cnt++) { + return 0; } err = reset_control_assert(rk_phy->phy_rst); if (err) { dev_err(&phy->dev, "assert phy_rst err %d\n", err); - goto err_reset; + rk_phy->init_cnt--; + return err; } -err_out: - mutex_unlock(&rk_phy->pcie_mutex); - return 0; - -err_reset: - - clk_disable_unprepare(rk_phy->clk_pciephy_ref); -err_refclk: - rk_phy->init_cnt--; - mutex_unlock(&rk_phy->pcie_mutex); return err; } @@ -323,15 +229,12 @@ static int rockchip_pcie_phy_exit(struct phy *phy) struct phy_pcie_instance *inst = phy_get_drvdata(phy); struct rockchip_pcie_phy *rk_phy = to_pcie_phy(inst); - mutex_lock(&rk_phy->pcie_mutex); + guard(mutex)(&rk_phy->pcie_mutex); if (--rk_phy->init_cnt) goto err_init_cnt; - clk_disable_unprepare(rk_phy->clk_pciephy_ref); - err_init_cnt: - mutex_unlock(&rk_phy->pcie_mutex); return 0; } @@ -365,7 +268,6 @@ static int rockchip_pcie_phy_probe(struct platform_device *pdev) struct rockchip_pcie_phy *rk_phy; struct phy_provider *phy_provider; struct regmap *grf; - const struct of_device_id *of_id; int i; u32 phy_num; @@ -379,28 +281,23 @@ static int rockchip_pcie_phy_probe(struct platform_device *pdev) if (!rk_phy) return -ENOMEM; - of_id = of_match_device(rockchip_pcie_phy_dt_ids, &pdev->dev); - if (!of_id) + rk_phy->phy_data = device_get_match_data(&pdev->dev); + if (!rk_phy->phy_data) return -EINVAL; - rk_phy->phy_data = (struct rockchip_pcie_data *)of_id->data; rk_phy->reg_base = grf; mutex_init(&rk_phy->pcie_mutex); rk_phy->phy_rst = devm_reset_control_get(dev, "phy"); - if (IS_ERR(rk_phy->phy_rst)) { - if (PTR_ERR(rk_phy->phy_rst) != -EPROBE_DEFER) - dev_err(dev, - "missing phy property for reset controller\n"); - return PTR_ERR(rk_phy->phy_rst); - } - - rk_phy->clk_pciephy_ref = devm_clk_get(dev, "refclk"); - if (IS_ERR(rk_phy->clk_pciephy_ref)) { - dev_err(dev, "refclk not found.\n"); - return PTR_ERR(rk_phy->clk_pciephy_ref); - } + if (IS_ERR(rk_phy->phy_rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rk_phy->phy_rst), + "missing phy property for reset controller\n"); + + rk_phy->clk_pciephy_ref = devm_clk_get_enabled(dev, "refclk"); + if (IS_ERR(rk_phy->clk_pciephy_ref)) + return dev_err_probe(&pdev->dev, PTR_ERR(rk_phy->clk_pciephy_ref), + "failed to get phyclk\n"); /* parse #phy-cells to see if it's legacy PHY model */ if (of_property_read_u32(dev->of_node, "#phy-cells", &phy_num)) |
