diff options
Diffstat (limited to 'drivers/phy/mediatek/phy-mtk-xsphy.c')
| -rw-r--r-- | drivers/phy/mediatek/phy-mtk-xsphy.c | 256 |
1 files changed, 135 insertions, 121 deletions
diff --git a/drivers/phy/mediatek/phy-mtk-xsphy.c b/drivers/phy/mediatek/phy-mtk-xsphy.c index 8c51131945c0..c0ddb9273cc3 100644 --- a/drivers/phy/mediatek/phy-mtk-xsphy.c +++ b/drivers/phy/mediatek/phy-mtk-xsphy.c @@ -10,12 +10,15 @@ #include <dt-bindings/phy/phy.h> #include <linux/clk.h> #include <linux/delay.h> -#include <linux/io.h> #include <linux/iopoll.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/regmap.h> + +#include "phy-mtk-io.h" /* u2 phy banks */ #define SSUSB_SIFSLV_MISC 0x000 @@ -36,7 +39,6 @@ #define XSP_U2FREQ_FMCR0 ((SSUSB_SIFSLV_U2FREQ) + 0x00) #define P2F_RG_FREQDET_EN BIT(24) #define P2F_RG_CYCLECNT GENMASK(23, 0) -#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x)) #define XSP_U2FREQ_MMONR0 ((SSUSB_SIFSLV_U2FREQ) + 0x0c) @@ -49,16 +51,12 @@ #define XSP_USBPHYACR1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x04) #define P2A1_RG_INTR_CAL GENMASK(23, 19) -#define P2A1_RG_INTR_CAL_VAL(x) ((0x1f & (x)) << 19) #define P2A1_RG_VRT_SEL GENMASK(14, 12) -#define P2A1_RG_VRT_SEL_VAL(x) ((0x7 & (x)) << 12) #define P2A1_RG_TERM_SEL GENMASK(10, 8) -#define P2A1_RG_TERM_SEL_VAL(x) ((0x7 & (x)) << 8) #define XSP_USBPHYACR5 ((SSUSB_SIFSLV_U2PHY_COM) + 0x014) #define P2A5_RG_HSTX_SRCAL_EN BIT(15) #define P2A5_RG_HSTX_SRCTRL GENMASK(14, 12) -#define P2A5_RG_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12) #define XSP_USBPHYACR6 ((SSUSB_SIFSLV_U2PHY_COM) + 0x018) #define P2A6_RG_BC11_SW_EN BIT(23) @@ -73,27 +71,34 @@ #define SSPXTP_PHYA_GLB_00 ((SSPXTP_SIFSLV_PHYA_GLB) + 0x00) #define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(21, 16) -#define RG_XTP_GLB_BIAS_INTR_CTRL_VAL(x) ((0x3f & (x)) << 16) #define SSPXTP_PHYA_LN_04 ((SSPXTP_SIFSLV_PHYA_LN) + 0x04) #define RG_XTP_LN0_TX_IMPSEL GENMASK(4, 0) -#define RG_XTP_LN0_TX_IMPSEL_VAL(x) (0x1f & (x)) #define SSPXTP_PHYA_LN_14 ((SSPXTP_SIFSLV_PHYA_LN) + 0x014) #define RG_XTP_LN0_RX_IMPSEL GENMASK(4, 0) -#define RG_XTP_LN0_RX_IMPSEL_VAL(x) (0x1f & (x)) #define XSP_REF_CLK 26 /* MHZ */ #define XSP_SLEW_RATE_COEF 17 #define XSP_SR_COEF_DIVISOR 1000 #define XSP_FM_DET_CYCLE_CNT 1024 +/* PHY switch between pcie/usb3/sgmii */ +#define USB_PHY_SWITCH_CTRL 0x0 +#define RG_PHY_SW_TYPE GENMASK(3, 0) +#define RG_PHY_SW_PCIE 0x0 +#define RG_PHY_SW_USB3 0x1 +#define RG_PHY_SW_SGMII 0x2 + struct xsphy_instance { struct phy *phy; void __iomem *port_base; struct clk *ref_clk; /* reference clock of anolog phy */ u32 index; u32 type; + struct regmap *type_sw; + u32 type_sw_reg; + u32 type_sw_index; /* only for HQA test */ int efuse_intr; int efuse_tx_imp; @@ -126,26 +131,18 @@ static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, return; /* enable USB ring oscillator */ - tmp = readl(pbase + XSP_USBPHYACR5); - tmp |= P2A5_RG_HSTX_SRCAL_EN; - writel(tmp, pbase + XSP_USBPHYACR5); + mtk_phy_set_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN); udelay(1); /* wait clock stable */ /* enable free run clock */ - tmp = readl(pbase + XSP_U2FREQ_FMMONR1); - tmp |= P2F_RG_FRCK_EN; - writel(tmp, pbase + XSP_U2FREQ_FMMONR1); + mtk_phy_set_bits(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN); /* set cycle count as 1024 */ - tmp = readl(pbase + XSP_U2FREQ_FMCR0); - tmp &= ~(P2F_RG_CYCLECNT); - tmp |= P2F_RG_CYCLECNT_VAL(XSP_FM_DET_CYCLE_CNT); - writel(tmp, pbase + XSP_U2FREQ_FMCR0); + mtk_phy_update_field(pbase + XSP_U2FREQ_FMCR0, P2F_RG_CYCLECNT, + XSP_FM_DET_CYCLE_CNT); /* enable frequency meter */ - tmp = readl(pbase + XSP_U2FREQ_FMCR0); - tmp |= P2F_RG_FREQDET_EN; - writel(tmp, pbase + XSP_U2FREQ_FMCR0); + mtk_phy_set_bits(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN); /* ignore return value */ readl_poll_timeout(pbase + XSP_U2FREQ_FMMONR1, tmp, @@ -154,14 +151,10 @@ static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, fm_out = readl(pbase + XSP_U2FREQ_MMONR0); /* disable frequency meter */ - tmp = readl(pbase + XSP_U2FREQ_FMCR0); - tmp &= ~P2F_RG_FREQDET_EN; - writel(tmp, pbase + XSP_U2FREQ_FMCR0); + mtk_phy_clear_bits(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN); /* disable free run clock */ - tmp = readl(pbase + XSP_U2FREQ_FMMONR1); - tmp &= ~P2F_RG_FRCK_EN; - writel(tmp, pbase + XSP_U2FREQ_FMMONR1); + mtk_phy_clear_bits(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN); if (fm_out) { /* (1024 / FM_OUT) x reference clock frequency x coefficient */ @@ -177,31 +170,21 @@ static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, xsphy->src_ref_clk, xsphy->src_coef); /* set HS slew rate */ - tmp = readl(pbase + XSP_USBPHYACR5); - tmp &= ~P2A5_RG_HSTX_SRCTRL; - tmp |= P2A5_RG_HSTX_SRCTRL_VAL(calib_val); - writel(tmp, pbase + XSP_USBPHYACR5); + mtk_phy_update_field(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL, calib_val); /* disable USB ring oscillator */ - tmp = readl(pbase + XSP_USBPHYACR5); - tmp &= ~P2A5_RG_HSTX_SRCAL_EN; - writel(tmp, pbase + XSP_USBPHYACR5); + mtk_phy_clear_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN); } static void u2_phy_instance_init(struct mtk_xsphy *xsphy, struct xsphy_instance *inst) { void __iomem *pbase = inst->port_base; - u32 tmp; /* DP/DM BC1.1 path Disable */ - tmp = readl(pbase + XSP_USBPHYACR6); - tmp &= ~P2A6_RG_BC11_SW_EN; - writel(tmp, pbase + XSP_USBPHYACR6); + mtk_phy_clear_bits(pbase + XSP_USBPHYACR6, P2A6_RG_BC11_SW_EN); - tmp = readl(pbase + XSP_USBPHYACR0); - tmp |= P2A0_RG_INTR_EN; - writel(tmp, pbase + XSP_USBPHYACR0); + mtk_phy_set_bits(pbase + XSP_USBPHYACR0, P2A0_RG_INTR_EN); } static void u2_phy_instance_power_on(struct mtk_xsphy *xsphy, @@ -209,16 +192,12 @@ static void u2_phy_instance_power_on(struct mtk_xsphy *xsphy, { void __iomem *pbase = inst->port_base; u32 index = inst->index; - u32 tmp; - tmp = readl(pbase + XSP_USBPHYACR6); - tmp |= P2A6_RG_OTG_VBUSCMP_EN; - writel(tmp, pbase + XSP_USBPHYACR6); + mtk_phy_set_bits(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN); - tmp = readl(pbase + XSP_U2PHYDTM1); - tmp |= P2D_RG_VBUSVALID | P2D_RG_AVALID; - tmp &= ~P2D_RG_SESSEND; - writel(tmp, pbase + XSP_U2PHYDTM1); + mtk_phy_update_bits(pbase + XSP_U2PHYDTM1, + P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND, + P2D_RG_VBUSVALID | P2D_RG_AVALID); dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); } @@ -228,16 +207,12 @@ static void u2_phy_instance_power_off(struct mtk_xsphy *xsphy, { void __iomem *pbase = inst->port_base; u32 index = inst->index; - u32 tmp; - tmp = readl(pbase + XSP_USBPHYACR6); - tmp &= ~P2A6_RG_OTG_VBUSCMP_EN; - writel(tmp, pbase + XSP_USBPHYACR6); + mtk_phy_clear_bits(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN); - tmp = readl(pbase + XSP_U2PHYDTM1); - tmp &= ~(P2D_RG_VBUSVALID | P2D_RG_AVALID); - tmp |= P2D_RG_SESSEND; - writel(tmp, pbase + XSP_U2PHYDTM1); + mtk_phy_update_bits(pbase + XSP_U2PHYDTM1, + P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND, + P2D_RG_SESSEND); dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); } @@ -296,6 +271,10 @@ static void phy_parse_property(struct mtk_xsphy *xsphy, inst->efuse_intr, inst->efuse_tx_imp, inst->efuse_rx_imp); break; + case PHY_TYPE_PCIE: + case PHY_TYPE_SGMII: + /* nothing to do */ + break; default: dev_err(xsphy->dev, "incompatible phy type\n"); return; @@ -306,63 +285,96 @@ static void u2_phy_props_set(struct mtk_xsphy *xsphy, struct xsphy_instance *inst) { void __iomem *pbase = inst->port_base; - u32 tmp; - if (inst->efuse_intr) { - tmp = readl(pbase + XSP_USBPHYACR1); - tmp &= ~P2A1_RG_INTR_CAL; - tmp |= P2A1_RG_INTR_CAL_VAL(inst->efuse_intr); - writel(tmp, pbase + XSP_USBPHYACR1); - } + if (inst->efuse_intr) + mtk_phy_update_field(pbase + XSP_USBPHYACR1, P2A1_RG_INTR_CAL, + inst->efuse_intr); - if (inst->eye_src) { - tmp = readl(pbase + XSP_USBPHYACR5); - tmp &= ~P2A5_RG_HSTX_SRCTRL; - tmp |= P2A5_RG_HSTX_SRCTRL_VAL(inst->eye_src); - writel(tmp, pbase + XSP_USBPHYACR5); - } + if (inst->eye_src) + mtk_phy_update_field(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL, + inst->eye_src); - if (inst->eye_vrt) { - tmp = readl(pbase + XSP_USBPHYACR1); - tmp &= ~P2A1_RG_VRT_SEL; - tmp |= P2A1_RG_VRT_SEL_VAL(inst->eye_vrt); - writel(tmp, pbase + XSP_USBPHYACR1); - } + if (inst->eye_vrt) + mtk_phy_update_field(pbase + XSP_USBPHYACR1, P2A1_RG_VRT_SEL, + inst->eye_vrt); - if (inst->eye_term) { - tmp = readl(pbase + XSP_USBPHYACR1); - tmp &= ~P2A1_RG_TERM_SEL; - tmp |= P2A1_RG_TERM_SEL_VAL(inst->eye_term); - writel(tmp, pbase + XSP_USBPHYACR1); - } + if (inst->eye_term) + mtk_phy_update_field(pbase + XSP_USBPHYACR1, P2A1_RG_TERM_SEL, + inst->eye_term); } static void u3_phy_props_set(struct mtk_xsphy *xsphy, struct xsphy_instance *inst) { void __iomem *pbase = inst->port_base; - u32 tmp; - if (inst->efuse_intr) { - tmp = readl(xsphy->glb_base + SSPXTP_PHYA_GLB_00); - tmp &= ~RG_XTP_GLB_BIAS_INTR_CTRL; - tmp |= RG_XTP_GLB_BIAS_INTR_CTRL_VAL(inst->efuse_intr); - writel(tmp, xsphy->glb_base + SSPXTP_PHYA_GLB_00); - } + if (inst->efuse_intr) + mtk_phy_update_field(xsphy->glb_base + SSPXTP_PHYA_GLB_00, + RG_XTP_GLB_BIAS_INTR_CTRL, inst->efuse_intr); - if (inst->efuse_tx_imp) { - tmp = readl(pbase + SSPXTP_PHYA_LN_04); - tmp &= ~RG_XTP_LN0_TX_IMPSEL; - tmp |= RG_XTP_LN0_TX_IMPSEL_VAL(inst->efuse_tx_imp); - writel(tmp, pbase + SSPXTP_PHYA_LN_04); - } + if (inst->efuse_tx_imp) + mtk_phy_update_field(pbase + SSPXTP_PHYA_LN_04, + RG_XTP_LN0_TX_IMPSEL, inst->efuse_tx_imp); + + if (inst->efuse_rx_imp) + mtk_phy_update_field(pbase + SSPXTP_PHYA_LN_14, + RG_XTP_LN0_RX_IMPSEL, inst->efuse_rx_imp); +} + +/* type switch for usb3/pcie/sgmii */ +static int phy_type_syscon_get(struct xsphy_instance *instance, + struct device_node *dn) +{ + struct of_phandle_args args; + int ret; + + /* type switch function is optional */ + if (!of_property_present(dn, "mediatek,syscon-type")) + return 0; - if (inst->efuse_rx_imp) { - tmp = readl(pbase + SSPXTP_PHYA_LN_14); - tmp &= ~RG_XTP_LN0_RX_IMPSEL; - tmp |= RG_XTP_LN0_RX_IMPSEL_VAL(inst->efuse_rx_imp); - writel(tmp, pbase + SSPXTP_PHYA_LN_14); + ret = of_parse_phandle_with_fixed_args(dn, "mediatek,syscon-type", + 2, 0, &args); + if (ret) + return ret; + + instance->type_sw_reg = args.args[0]; + instance->type_sw_index = args.args[1] & 0x3; /* <=3 */ + instance->type_sw = syscon_node_to_regmap(args.np); + of_node_put(args.np); + dev_info(&instance->phy->dev, "type_sw - reg %#x, index %d\n", + instance->type_sw_reg, instance->type_sw_index); + + return PTR_ERR_OR_ZERO(instance->type_sw); +} + +static int phy_type_set(struct xsphy_instance *instance) +{ + int type; + u32 offset; + + if (!instance->type_sw) + return 0; + + switch (instance->type) { + case PHY_TYPE_USB3: + type = RG_PHY_SW_USB3; + break; + case PHY_TYPE_PCIE: + type = RG_PHY_SW_PCIE; + break; + case PHY_TYPE_SGMII: + type = RG_PHY_SW_SGMII; + break; + case PHY_TYPE_USB2: + default: + return 0; } + + offset = instance->type_sw_index * BITS_PER_BYTE; + regmap_update_bits(instance->type_sw, instance->type_sw_reg, + RG_PHY_SW_TYPE << offset, type << offset); + + return 0; } static int mtk_phy_init(struct phy *phy) @@ -385,6 +397,10 @@ static int mtk_phy_init(struct phy *phy) case PHY_TYPE_USB3: u3_phy_props_set(xsphy, inst); break; + case PHY_TYPE_PCIE: + case PHY_TYPE_SGMII: + /* nothing to do, only used to set type */ + break; default: dev_err(xsphy->dev, "incompatible phy type\n"); clk_disable_unprepare(inst->ref_clk); @@ -438,7 +454,7 @@ static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) } static struct phy *mtk_phy_xlate(struct device *dev, - struct of_phandle_args *args) + const struct of_phandle_args *args) { struct mtk_xsphy *xsphy = dev_get_drvdata(dev); struct xsphy_instance *inst = NULL; @@ -463,12 +479,15 @@ static struct phy *mtk_phy_xlate(struct device *dev, inst->type = args->args[0]; if (!(inst->type == PHY_TYPE_USB2 || - inst->type == PHY_TYPE_USB3)) { + inst->type == PHY_TYPE_USB3 || + inst->type == PHY_TYPE_PCIE || + inst->type == PHY_TYPE_SGMII)) { dev_err(dev, "unsupported phy type: %d\n", inst->type); return ERR_PTR(-EINVAL); } phy_parse_property(xsphy, inst); + phy_type_set(inst); return inst->phy; } @@ -492,12 +511,11 @@ static int mtk_xsphy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - struct device_node *child_np; struct phy_provider *provider; struct resource *glb_res; struct mtk_xsphy *xsphy; struct resource res; - int port, retval; + int port; xsphy = devm_kzalloc(dev, sizeof(*xsphy), GFP_KERNEL); if (!xsphy) @@ -531,37 +549,34 @@ static int mtk_xsphy_probe(struct platform_device *pdev) device_property_read_u32(dev, "mediatek,src-coef", &xsphy->src_coef); port = 0; - for_each_child_of_node(np, child_np) { + for_each_child_of_node_scoped(np, child_np) { struct xsphy_instance *inst; struct phy *phy; + int retval; inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL); - if (!inst) { - retval = -ENOMEM; - goto put_child; - } + if (!inst) + return -ENOMEM; xsphy->phys[port] = inst; phy = devm_phy_create(dev, child_np, &mtk_xsphy_ops); if (IS_ERR(phy)) { dev_err(dev, "failed to create phy\n"); - retval = PTR_ERR(phy); - goto put_child; + return PTR_ERR(phy); } retval = of_address_to_resource(child_np, 0, &res); if (retval) { dev_err(dev, "failed to get address resource(id-%d)\n", port); - goto put_child; + return retval; } inst->port_base = devm_ioremap_resource(&phy->dev, &res); if (IS_ERR(inst->port_base)) { dev_err(dev, "failed to remap phy regs\n"); - retval = PTR_ERR(inst->port_base); - goto put_child; + return PTR_ERR(inst->port_base); } inst->phy = phy; @@ -572,17 +587,16 @@ static int mtk_xsphy_probe(struct platform_device *pdev) inst->ref_clk = devm_clk_get(&phy->dev, "ref"); if (IS_ERR(inst->ref_clk)) { dev_err(dev, "failed to get ref_clk(id-%d)\n", port); - retval = PTR_ERR(inst->ref_clk); - goto put_child; + return PTR_ERR(inst->ref_clk); } + + retval = phy_type_syscon_get(inst, child_np); + if (retval) + return retval; } provider = devm_of_phy_provider_register(dev, mtk_phy_xlate); return PTR_ERR_OR_ZERO(provider); - -put_child: - of_node_put(child_np); - return retval; } static struct platform_driver mtk_xsphy_driver = { |
