diff options
Diffstat (limited to 'drivers/phy/rockchip')
-rw-r--r-- | drivers/phy/rockchip/Kconfig | 1 | ||||
-rw-r--r-- | drivers/phy/rockchip/phy-rockchip-emmc.c | 60 | ||||
-rw-r--r-- | drivers/phy/rockchip/phy-rockchip-typec.c | 160 |
3 files changed, 136 insertions, 85 deletions
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index f5325b2b679e..0e15119ddfc6 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -29,6 +29,7 @@ config PHY_ROCKCHIP_INNO_USB2 config PHY_ROCKCHIP_PCIE tristate "Rockchip PCIe PHY Driver" depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST + depends on HAS_IOMEM select GENERIC_PHY select MFD_SYSCON help diff --git a/drivers/phy/rockchip/phy-rockchip-emmc.c b/drivers/phy/rockchip/phy-rockchip-emmc.c index f1b24f18e9b2..b237360f95f6 100644 --- a/drivers/phy/rockchip/phy-rockchip-emmc.c +++ b/drivers/phy/rockchip/phy-rockchip-emmc.c @@ -76,6 +76,13 @@ #define PHYCTRL_OTAPDLYSEL_MASK 0xf #define PHYCTRL_OTAPDLYSEL_SHIFT 0x7 +#define PHYCTRL_IS_CALDONE(x) \ + ((((x) >> PHYCTRL_CALDONE_SHIFT) & \ + PHYCTRL_CALDONE_MASK) == PHYCTRL_CALDONE_DONE) +#define PHYCTRL_IS_DLLRDY(x) \ + ((((x) >> PHYCTRL_DLLRDY_SHIFT) & \ + PHYCTRL_DLLRDY_MASK) == PHYCTRL_DLLRDY_DONE) + struct rockchip_emmc_phy { unsigned int reg_offset; struct regmap *reg_base; @@ -89,7 +96,7 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) unsigned int dllrdy; unsigned int freqsel = PHYCTRL_FREQSEL_200M; unsigned long rate; - unsigned long timeout; + int ret; /* * Keep phyctrl_pdb and phyctrl_endll low to allow @@ -160,17 +167,19 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) PHYCTRL_PDB_SHIFT)); /* - * According to the user manual, it asks driver to - * wait 5us for calpad busy trimming + * According to the user manual, it asks driver to wait 5us for + * calpad busy trimming. However it is documented that this value is + * PVT(A.K.A process,voltage and temperature) relevant, so some + * failure cases are found which indicates we should be more tolerant + * to calpad busy trimming. */ - udelay(5); - regmap_read(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_STATUS, - &caldone); - caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; - if (caldone != PHYCTRL_CALDONE_DONE) { - pr_err("rockchip_emmc_phy_power: caldone timeout.\n"); - return -ETIMEDOUT; + ret = regmap_read_poll_timeout(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_STATUS, + caldone, PHYCTRL_IS_CALDONE(caldone), + 0, 50); + if (ret) { + pr_err("%s: caldone failed, ret=%d\n", __func__, ret); + return ret; } /* Set the frequency of the DLL operation */ @@ -210,28 +219,15 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) * NOTE: There appear to be corner cases where the DLL seems to take * extra long to lock for reasons that aren't understood. In some * extreme cases we've seen it take up to over 10ms (!). We'll be - * generous and give it 50ms. We still busy wait here because: - * - In most cases it should be super fast. - * - This is not called lots during normal operation so it shouldn't - * be a power or performance problem to busy wait. We expect it - * only at boot / resume. In both cases, eMMC is probably on the - * critical path so busy waiting a little extra time should be OK. + * generous and give it 50ms. */ - timeout = jiffies + msecs_to_jiffies(50); - do { - udelay(1); - - regmap_read(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_STATUS, - &dllrdy); - dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; - if (dllrdy == PHYCTRL_DLLRDY_DONE) - break; - } while (!time_after(jiffies, timeout)); - - if (dllrdy != PHYCTRL_DLLRDY_DONE) { - pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n"); - return -ETIMEDOUT; + ret = regmap_read_poll_timeout(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_STATUS, + dllrdy, PHYCTRL_IS_DLLRDY(dllrdy), + 0, 50 * USEC_PER_MSEC); + if (ret) { + pr_err("%s: dllrdy failed. ret=%d\n", __func__, ret); + return ret; } return 0; diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 7492c8978217..76a4b58ec771 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -355,11 +355,26 @@ struct usb3phy_reg { u32 write_enable; }; +/** + * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration. + * @reg: the base address for usb3-phy config. + * @typec_conn_dir: the register of type-c connector direction. + * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. + * @external_psm: the register of type-c phy external psm clock. + * @pipe_status: the register of type-c phy pipe status. + * @usb3_host_disable: the register of type-c usb3 host disable. + * @usb3_host_port: the register of type-c usb3 host port. + * @uphy_dp_sel: the register of type-c phy DP select control. + */ struct rockchip_usb3phy_port_cfg { + unsigned int reg; struct usb3phy_reg typec_conn_dir; struct usb3phy_reg usb3tousb2_en; struct usb3phy_reg external_psm; struct usb3phy_reg pipe_status; + struct usb3phy_reg usb3_host_disable; + struct usb3phy_reg usb3_host_port; + struct usb3phy_reg uphy_dp_sel; }; struct rockchip_typec_phy { @@ -372,7 +387,7 @@ struct rockchip_typec_phy { struct reset_control *uphy_rst; struct reset_control *pipe_rst; struct reset_control *tcphy_rst; - struct rockchip_usb3phy_port_cfg port_cfgs; + const struct rockchip_usb3phy_port_cfg *port_cfgs; /* mutex to protect access to individual PHYs */ struct mutex lock; @@ -424,6 +439,30 @@ struct phy_reg dp_pll_cfg[] = { { 0x4, CMN_DIAG_PLL1_INCLK_CTRL }, }; +static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { + { + .reg = 0xff7c0000, + .typec_conn_dir = { 0xe580, 0, 16 }, + .usb3tousb2_en = { 0xe580, 3, 19 }, + .external_psm = { 0xe588, 14, 30 }, + .pipe_status = { 0xe5c0, 0, 0 }, + .usb3_host_disable = { 0x2434, 0, 16 }, + .usb3_host_port = { 0x2434, 12, 28 }, + .uphy_dp_sel = { 0x6268, 19, 19 }, + }, + { + .reg = 0xff800000, + .typec_conn_dir = { 0xe58c, 0, 16 }, + .usb3tousb2_en = { 0xe58c, 3, 19 }, + .external_psm = { 0xe594, 14, 30 }, + .pipe_status = { 0xe5c0, 16, 16 }, + .usb3_host_disable = { 0x2444, 0, 16 }, + .usb3_host_port = { 0x2444, 12, 28 }, + .uphy_dp_sel = { 0x6268, 3, 19 }, + }, + { /* sentinel */ } +}; + static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy) { u32 i, rdata; @@ -691,7 +730,7 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode) { - struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; + const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs; int ret, i; u32 val; @@ -782,6 +821,9 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) u8 mode; int ret; + if (!edev) + return MODE_DFP_USB; + ufp = extcon_get_state(edev, EXTCON_USB); dp = extcon_get_state(edev, EXTCON_DISP_DP); @@ -818,10 +860,22 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) return mode; } +static int tcphy_cfg_usb3_to_usb2_only(struct rockchip_typec_phy *tcphy, + bool value) +{ + const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs; + + property_enable(tcphy, &cfg->usb3tousb2_en, value); + property_enable(tcphy, &cfg->usb3_host_disable, value); + property_enable(tcphy, &cfg->usb3_host_port, !value); + + return 0; +} + static int rockchip_usb3_phy_power_on(struct phy *phy) { struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); - struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; + const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs; const struct usb3phy_reg *reg = &cfg->pipe_status; int timeout, new_mode, ret = 0; u32 val; @@ -835,8 +889,10 @@ static int rockchip_usb3_phy_power_on(struct phy *phy) } /* DP-only mode; fall back to USB2 */ - if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB))) + if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB))) { + tcphy_cfg_usb3_to_usb2_only(tcphy, true); goto unlock_ret; + } if (tcphy->mode == new_mode) goto unlock_ret; @@ -852,6 +908,9 @@ static int rockchip_usb3_phy_power_on(struct phy *phy) regmap_read(tcphy->grf_regs, reg->offset, &val); if (!(val & BIT(reg->enable_bit))) { tcphy->mode |= new_mode & (MODE_DFP_USB | MODE_UFP_USB); + + /* enable usb3 host */ + tcphy_cfg_usb3_to_usb2_only(tcphy, false); goto unlock_ret; } usleep_range(10, 20); @@ -872,6 +931,7 @@ static int rockchip_usb3_phy_power_off(struct phy *phy) struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); mutex_lock(&tcphy->lock); + tcphy_cfg_usb3_to_usb2_only(tcphy, false); if (tcphy->mode == MODE_DISCONNECT) goto unlock; @@ -894,6 +954,7 @@ static const struct phy_ops rockchip_usb3_phy_ops = { static int rockchip_dp_phy_power_on(struct phy *phy) { struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); + const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs; int new_mode, ret = 0; u32 val; @@ -926,6 +987,8 @@ static int rockchip_dp_phy_power_on(struct phy *phy) if (ret) goto unlock_ret; + property_enable(tcphy, &cfg->uphy_dp_sel, 1); + ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL, val, val & DP_MODE_A2, 1000, PHY_MODE_SET_TIMEOUT); @@ -984,51 +1047,9 @@ static const struct phy_ops rockchip_dp_phy_ops = { .owner = THIS_MODULE, }; -static int tcphy_get_param(struct device *dev, - struct usb3phy_reg *reg, - const char *name) -{ - u32 buffer[3]; - int ret; - - ret = of_property_read_u32_array(dev->of_node, name, buffer, 3); - if (ret) { - dev_err(dev, "Can not parse %s\n", name); - return ret; - } - - reg->offset = buffer[0]; - reg->enable_bit = buffer[1]; - reg->write_enable = buffer[2]; - return 0; -} - static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy, struct device *dev) { - struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; - int ret; - - ret = tcphy_get_param(dev, &cfg->typec_conn_dir, - "rockchip,typec-conn-dir"); - if (ret) - return ret; - - ret = tcphy_get_param(dev, &cfg->usb3tousb2_en, - "rockchip,usb3tousb2-en"); - if (ret) - return ret; - - ret = tcphy_get_param(dev, &cfg->external_psm, - "rockchip,external-psm"); - if (ret) - return ret; - - ret = tcphy_get_param(dev, &cfg->pipe_status, - "rockchip,pipe-status"); - if (ret) - return ret; - tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); if (IS_ERR(tcphy->grf_regs)) { @@ -1071,7 +1092,7 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy, static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy) { - struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; + const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs; reset_control_assert(tcphy->tcphy_rst); reset_control_assert(tcphy->uphy_rst); @@ -1092,17 +1113,43 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev) struct rockchip_typec_phy *tcphy; struct phy_provider *phy_provider; struct resource *res; - int ret; + const struct rockchip_usb3phy_port_cfg *phy_cfgs; + const struct of_device_id *match; + int index, ret; tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL); if (!tcphy) return -ENOMEM; + match = of_match_device(dev->driver->of_match_table, dev); + if (!match || !match->data) { + dev_err(dev, "phy configs are not assigned!\n"); + return -EINVAL; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); tcphy->base = devm_ioremap_resource(dev, res); if (IS_ERR(tcphy->base)) return PTR_ERR(tcphy->base); + phy_cfgs = match->data; + /* find out a proper config which can be matched with dt. */ + index = 0; + while (phy_cfgs[index].reg) { + if (phy_cfgs[index].reg == res->start) { + tcphy->port_cfgs = &phy_cfgs[index]; + break; + } + + ++index; + } + + if (!tcphy->port_cfgs) { + dev_err(dev, "no phy-config can be matched with %s node\n", + np->name); + return -EINVAL; + } + ret = tcphy_parse_dt(tcphy, dev); if (ret) return ret; @@ -1115,9 +1162,13 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev) tcphy->extcon = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(tcphy->extcon)) { - if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER) - dev_err(dev, "Invalid or missing extcon\n"); - return PTR_ERR(tcphy->extcon); + if (PTR_ERR(tcphy->extcon) == -ENODEV) { + tcphy->extcon = NULL; + } else { + if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER) + dev_err(dev, "Invalid or missing extcon\n"); + return PTR_ERR(tcphy->extcon); + } } pm_runtime_enable(dev); @@ -1162,8 +1213,11 @@ static int rockchip_typec_phy_remove(struct platform_device *pdev) } static const struct of_device_id rockchip_typec_phy_dt_ids[] = { - { .compatible = "rockchip,rk3399-typec-phy" }, - {} + { + .compatible = "rockchip,rk3399-typec-phy", + .data = &rk3399_usb3phy_port_cfgs + }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids); |