From 6768594326eb358de06772febc06f87fea0a9483 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 7 May 2018 13:03:26 +0530 Subject: phy: Add Allwinner A64 USB PHY driver USB PHY implementation for Allwinner SOC's can be handling in to single driver with different phy configs. This driver handle all Allwinner USB PHY's start from 4I to 50I(except 9I). Currently added A64 compatibility more will add in next coming patches. Current implementation is unable to get pinctrl, clock and reset details from DT since the dm code on these will add it future. Driver named as phy-sun4i-usb.c since the same PHY logic work for all Allwinner SOC's start from 4I to A64 except 9I with different phy configurations. Signed-off-by: Jagan Teki Acked-by: Jun Nie --- drivers/phy/allwinner/Kconfig | 13 ++ drivers/phy/allwinner/Makefile | 6 + drivers/phy/allwinner/phy-sun4i-usb.c | 400 ++++++++++++++++++++++++++++++++++ 3 files changed, 419 insertions(+) create mode 100644 drivers/phy/allwinner/Kconfig create mode 100644 drivers/phy/allwinner/Makefile create mode 100644 drivers/phy/allwinner/phy-sun4i-usb.c (limited to 'drivers/phy') diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig new file mode 100644 index 00000000000..dba3bae61c4 --- /dev/null +++ b/drivers/phy/allwinner/Kconfig @@ -0,0 +1,13 @@ +# +# Phy drivers for Allwinner platforms +# +config PHY_SUN4I_USB + bool "Allwinner Sun4I USB PHY driver" + depends on ARCH_SUNXI + select PHY + help + Enable this to support the transceiver that is part of Allwinner + sunxi SoCs. + + This driver controls the entire USB PHY block, both the USB OTG + parts, as well as the 2 regular USB 2 host PHYs. diff --git a/drivers/phy/allwinner/Makefile b/drivers/phy/allwinner/Makefile new file mode 100644 index 00000000000..5ed2702e4f0 --- /dev/null +++ b/drivers/phy/allwinner/Makefile @@ -0,0 +1,6 @@ +# Copyright (C) 2016 Amarula Solutions +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c new file mode 100644 index 00000000000..78304c1f228 --- /dev/null +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -0,0 +1,400 @@ +/* + * Allwinner sun4i USB PHY driver + * + * Copyright (C) 2017 Jagan Teki + * Copyright (C) 2015 Hans de Goede + * Copyright (C) 2014 Roman Byshko + * + * Modelled arch/arm/mach-sunxi/usb_phy.c to compatible with generic-phy. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_ISCR 0x00 +#define REG_PHYCTL_A10 0x04 +#define REG_PHYBIST 0x08 +#define REG_PHYTUNE 0x0c +#define REG_PHYCTL_A33 0x10 +#define REG_PHY_OTGCTL 0x20 +#define REG_PMU_UNK1 0x10 + +/* Common Control Bits for Both PHYs */ +#define PHY_PLL_BW 0x03 +#define PHY_RES45_CAL_EN 0x0c + +/* Private Control Bits for Each PHY */ +#define PHY_TX_AMPLITUDE_TUNE 0x20 +#define PHY_TX_SLEWRATE_TUNE 0x22 +#define PHY_DISCON_TH_SEL 0x2a + +#define PHYCTL_DATA BIT(7) +#define OTGCTL_ROUTE_MUSB BIT(0) + +#define PHY_TX_RATE BIT(4) +#define PHY_TX_MAGNITUDE BIT(2) +#define PHY_TX_AMPLITUDE_LEN 5 + +#define PHY_RES45_CAL_DATA BIT(0) +#define PHY_RES45_CAL_LEN 1 +#define PHY_DISCON_TH_LEN 2 + +#define SUNXI_AHB_ICHR8_EN BIT(10) +#define SUNXI_AHB_INCR4_BURST_EN BIT(9) +#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) +#define SUNXI_ULPI_BYPASS_EN BIT(0) + +#define MAX_PHYS 4 + +enum sun4i_usb_phy_type { + sun50i_a64_phy, +}; + +struct sun4i_usb_phy_cfg { + int num_phys; + enum sun4i_usb_phy_type type; + u32 disc_thresh; + u8 phyctl_offset; + bool enable_pmu_unk1; + bool phy0_dual_route; +}; + +struct sun4i_usb_phy_info { + const char *gpio_vbus; + const char *gpio_vbus_det; + const char *gpio_id_det; + int rst_mask; +} phy_info[] = { + { + .gpio_vbus = CONFIG_USB0_VBUS_PIN, + .gpio_vbus_det = CONFIG_USB0_VBUS_DET, + .gpio_id_det = CONFIG_USB0_ID_DET, + .rst_mask = (CCM_USB_CTRL_PHY0_RST | CCM_USB_CTRL_PHY0_CLK), + }, + { + .gpio_vbus = CONFIG_USB1_VBUS_PIN, + .gpio_vbus_det = NULL, + .gpio_id_det = NULL, + .rst_mask = (CCM_USB_CTRL_PHY1_RST | CCM_USB_CTRL_PHY1_CLK), + }, + { + .gpio_vbus = CONFIG_USB2_VBUS_PIN, + .gpio_vbus_det = NULL, + .gpio_id_det = NULL, + .rst_mask = (CCM_USB_CTRL_PHY2_RST | CCM_USB_CTRL_PHY2_CLK), + }, + { + .gpio_vbus = CONFIG_USB3_VBUS_PIN, + .gpio_vbus_det = NULL, + .gpio_id_det = NULL, + .rst_mask = (CCM_USB_CTRL_PHY3_RST | CCM_USB_CTRL_PHY3_CLK), + }, +}; + +struct sun4i_usb_phy_plat { + void __iomem *pmu; + int power_on_count; + int gpio_vbus; + int gpio_vbus_det; + int gpio_id_det; + int rst_mask; + int id; +}; + +struct sun4i_usb_phy_data { + void __iomem *base; + struct sunxi_ccm_reg *ccm; + const struct sun4i_usb_phy_cfg *cfg; + struct sun4i_usb_phy_plat *usb_phy; +}; + +static int initial_usb_scan_delay = CONFIG_INITIAL_USB_SCAN_DELAY; + +static void sun4i_usb_phy_write(struct phy *phy, u32 addr, u32 data, int len) +{ + struct sun4i_usb_phy_data *phy_data = dev_get_priv(phy->dev); + struct sun4i_usb_phy_plat *usb_phy = &phy_data->usb_phy[phy->id]; + u32 temp, usbc_bit = BIT(usb_phy->id * 2); + void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset; + int i; + + if (phy_data->cfg->phyctl_offset == REG_PHYCTL_A33) { + /* SoCs newer than A33 need us to set phyctl to 0 explicitly */ + writel(0, phyctl); + } + + for (i = 0; i < len; i++) { + temp = readl(phyctl); + + /* clear the address portion */ + temp &= ~(0xff << 8); + + /* set the address */ + temp |= ((addr + i) << 8); + writel(temp, phyctl); + + /* set the data bit and clear usbc bit*/ + temp = readb(phyctl); + if (data & 0x1) + temp |= PHYCTL_DATA; + else + temp &= ~PHYCTL_DATA; + temp &= ~usbc_bit; + writeb(temp, phyctl); + + /* pulse usbc_bit */ + temp = readb(phyctl); + temp |= usbc_bit; + writeb(temp, phyctl); + + temp = readb(phyctl); + temp &= ~usbc_bit; + writeb(temp, phyctl); + + data >>= 1; + } +} + +static void sun4i_usb_phy_passby(struct sun4i_usb_phy_plat *usb_phy, + bool enable) +{ + u32 bits, reg_value; + + if (!usb_phy->pmu) + return; + + bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN | + SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN; + reg_value = readl(usb_phy->pmu); + + if (enable) + reg_value |= bits; + else + reg_value &= ~bits; + + writel(reg_value, usb_phy->pmu); +} + +static int sun4i_usb_phy_power_on(struct phy *phy) +{ + struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); + struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; + + if (initial_usb_scan_delay) { + mdelay(initial_usb_scan_delay); + initial_usb_scan_delay = 0; + } + + usb_phy->power_on_count++; + if (usb_phy->power_on_count != 1) + return 0; + + if (usb_phy->gpio_vbus >= 0) + gpio_set_value(usb_phy->gpio_vbus, SUNXI_GPIO_PULL_UP); + + return 0; +} + +static int sun4i_usb_phy_power_off(struct phy *phy) +{ + struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); + struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; + + usb_phy->power_on_count--; + if (usb_phy->power_on_count != 0) + return 0; + + if (usb_phy->gpio_vbus >= 0) + gpio_set_value(usb_phy->gpio_vbus, SUNXI_GPIO_PULL_DISABLE); + + return 0; +} + +static void sun4i_usb_phy0_reroute(struct sun4i_usb_phy_data *data, bool id_det) +{ + u32 regval; + + regval = readl(data->base + REG_PHY_OTGCTL); + if (!id_det) { + /* Host mode. Route phy0 to EHCI/OHCI */ + regval &= ~OTGCTL_ROUTE_MUSB; + } else { + /* Peripheral mode. Route phy0 to MUSB */ + regval |= OTGCTL_ROUTE_MUSB; + } + writel(regval, data->base + REG_PHY_OTGCTL); +} + +static int sun4i_usb_phy_init(struct phy *phy) +{ + struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); + struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; + u32 val; + + setbits_le32(&data->ccm->usb_clk_cfg, usb_phy->rst_mask); + + if (usb_phy->pmu && data->cfg->enable_pmu_unk1) { + val = readl(usb_phy->pmu + REG_PMU_UNK1); + writel(val & ~2, usb_phy->pmu + REG_PMU_UNK1); + } + + if (usb_phy->id == 0) + sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, PHY_RES45_CAL_DATA, + PHY_RES45_CAL_LEN); + + /* Adjust PHY's magnitude and rate */ + sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, PHY_TX_MAGNITUDE | + PHY_TX_RATE, PHY_TX_AMPLITUDE_LEN); + + /* Disconnect threshold adjustment */ + sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->cfg->disc_thresh, + PHY_DISCON_TH_LEN); + + if (usb_phy->id != 0) + sun4i_usb_phy_passby(usb_phy, true); + + sun4i_usb_phy0_reroute(data, true); + + return 0; +} + +static int sun4i_usb_phy_exit(struct phy *phy) +{ + struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); + struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; + + sun4i_usb_phy_passby(usb_phy, false); + + clrbits_le32(&data->ccm->usb_clk_cfg, usb_phy->rst_mask); + + return 0; +} + +static int sun4i_usb_phy_xlate(struct phy *phy, + struct ofnode_phandle_args *args) +{ + struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); + + if (args->args_count >= data->cfg->num_phys) + return -EINVAL; + + if (args->args_count) + phy->id = args->args[0]; + else + phy->id = 0; + + debug("%s: phy_id = %ld\n", __func__, phy->id); + return 0; +} + +static struct phy_ops sun4i_usb_phy_ops = { + .of_xlate = sun4i_usb_phy_xlate, + .init = sun4i_usb_phy_init, + .power_on = sun4i_usb_phy_power_on, + .power_off = sun4i_usb_phy_power_off, + .exit = sun4i_usb_phy_exit, +}; + +static int sun4i_usb_phy_probe(struct udevice *dev) +{ + struct sun4i_usb_phy_plat *plat = dev_get_platdata(dev); + struct sun4i_usb_phy_data *data = dev_get_priv(dev); + int i, ret; + + data->cfg = (const struct sun4i_usb_phy_cfg *)dev_get_driver_data(dev); + if (!data->cfg) + return -EINVAL; + + data->base = (void __iomem *)devfdt_get_addr_name(dev, "phy_ctrl"); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + data->ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + if (IS_ERR(data->ccm)) + return PTR_ERR(data->ccm); + + data->usb_phy = plat; + for (i = 0; i < data->cfg->num_phys; i++) { + struct sun4i_usb_phy_plat *phy = &plat[i]; + struct sun4i_usb_phy_info *info = &phy_info[i]; + char name[16]; + + phy->gpio_vbus = sunxi_name_to_gpio(info->gpio_vbus); + if (phy->gpio_vbus >= 0) { + ret = gpio_request(phy->gpio_vbus, "usb_vbus"); + if (ret) + return ret; + ret = gpio_direction_output(phy->gpio_vbus, 0); + if (ret) + return ret; + } + + phy->gpio_vbus_det = sunxi_name_to_gpio(info->gpio_vbus_det); + if (phy->gpio_vbus_det >= 0) { + ret = gpio_request(phy->gpio_vbus_det, "usb_vbus_det"); + if (ret) + return ret; + ret = gpio_direction_input(phy->gpio_vbus_det); + if (ret) + return ret; + } + + phy->gpio_id_det = sunxi_name_to_gpio(info->gpio_id_det); + if (phy->gpio_id_det >= 0) { + ret = gpio_request(phy->gpio_id_det, "usb_id_det"); + if (ret) + return ret; + ret = gpio_direction_input(phy->gpio_id_det); + if (ret) + return ret; + sunxi_gpio_set_pull(phy->gpio_id_det, SUNXI_GPIO_PULL_UP); + } + + if (i || data->cfg->phy0_dual_route) { + snprintf(name, sizeof(name), "pmu%d", i); + phy->pmu = (void __iomem *)devfdt_get_addr_name(dev, name); + if (IS_ERR(phy->pmu)) + return PTR_ERR(phy->pmu); + } + + phy->id = i; + phy->rst_mask = info->rst_mask; + }; + + setbits_le32(&data->ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); + + debug("Allwinner Sun4I USB PHY driver loaded\n"); + return 0; +} + +static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { + .num_phys = 2, + .type = sun50i_a64_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A33, + .enable_pmu_unk1 = true, + .phy0_dual_route = true, +}; + +static const struct udevice_id sun4i_usb_phy_ids[] = { + { .compatible = "allwinner,sun50i-a64-usb-phy", .data = (ulong)&sun50i_a64_cfg}, + { } +}; + +U_BOOT_DRIVER(sun4i_usb_phy) = { + .name = "sun4i_usb_phy", + .id = UCLASS_PHY, + .of_match = sun4i_usb_phy_ids, + .ops = &sun4i_usb_phy_ops, + .probe = sun4i_usb_phy_probe, + .platdata_auto_alloc_size = sizeof(struct sun4i_usb_phy_plat[MAX_PHYS]), + .priv_auto_alloc_size = sizeof(struct sun4i_usb_phy_data), +}; -- cgit v1.2.3 From 129c45c72872cf82dde44024864a517267aed68a Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 7 May 2018 13:03:27 +0530 Subject: phy: sun4i-usb: Add id_detect and vbus_detect ops ID and VBUS detection code require when musb changing between Host and/or Peripheral modes. Signed-off-by: Jagan Teki Acked-by: Jun Nie --- drivers/phy/allwinner/phy-sun4i-usb.c | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 78304c1f228..7f2970b96bd 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -295,6 +296,44 @@ static int sun4i_usb_phy_xlate(struct phy *phy, return 0; } +int sun4i_usb_phy_vbus_detect(struct phy *phy) +{ + struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); + struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; + int err, retries = 3; + + debug("%s: id_det = %d\n", __func__, usb_phy->gpio_id_det); + + if (usb_phy->gpio_vbus_det < 0) + return usb_phy->gpio_vbus_det; + + err = gpio_get_value(usb_phy->gpio_vbus_det); + /* + * Vbus may have been provided by the board and just been turned of + * some milliseconds ago on reset, what we're measuring then is a + * residual charge on Vbus, sleep a bit and try again. + */ + while (err > 0 && retries--) { + mdelay(100); + err = gpio_get_value(usb_phy->gpio_vbus_det); + } + + return err; +} + +int sun4i_usb_phy_id_detect(struct phy *phy) +{ + struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); + struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; + + debug("%s: id_det = %d\n", __func__, usb_phy->gpio_id_det); + + if (usb_phy->gpio_id_det < 0) + return usb_phy->gpio_id_det; + + return gpio_get_value(usb_phy->gpio_id_det); +} + static struct phy_ops sun4i_usb_phy_ops = { .of_xlate = sun4i_usb_phy_xlate, .init = sun4i_usb_phy_init, -- cgit v1.2.3 From 43519c4da700a7df66dd48dd031e8a51bab1dde2 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 7 May 2018 13:03:28 +0530 Subject: phy: sun4i-usb: Add H3/H5 PHY config H3/H5 has 4 USB PHY, rest are similar to A64. Signed-off-by: Jagan Teki Acked-by: Jun Nie --- drivers/phy/allwinner/phy-sun4i-usb.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 7f2970b96bd..993f036b787 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -56,6 +56,7 @@ #define MAX_PHYS 4 enum sun4i_usb_phy_type { + sun8i_h3_phy, sun50i_a64_phy, }; @@ -414,6 +415,15 @@ static int sun4i_usb_phy_probe(struct udevice *dev) return 0; } +static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { + .num_phys = 4, + .type = sun8i_h3_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A33, + .enable_pmu_unk1 = true, + .phy0_dual_route = true, +}; + static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { .num_phys = 2, .type = sun50i_a64_phy, @@ -424,6 +434,7 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { }; static const struct udevice_id sun4i_usb_phy_ids[] = { + { .compatible = "allwinner,sun8i-h3-usb-phy", .data = (ulong)&sun8i_h3_cfg }, { .compatible = "allwinner,sun50i-a64-usb-phy", .data = (ulong)&sun50i_a64_cfg}, { } }; -- cgit v1.2.3 From bafe5e3061497a52023b24b50b1104f0da880fed Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 7 May 2018 13:03:29 +0530 Subject: phy: sun4i-usb: Add V3S PHY config V3S has 1 USB PHY, rest are similar to A64. Signed-off-by: Jagan Teki Acked-by: Jun Nie --- drivers/phy/allwinner/phy-sun4i-usb.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 993f036b787..de0a59a32c6 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -57,6 +57,7 @@ enum sun4i_usb_phy_type { sun8i_h3_phy, + sun8i_v3s_phy, sun50i_a64_phy, }; @@ -424,6 +425,15 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { .phy0_dual_route = true, }; +static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { + .num_phys = 1, + .type = sun8i_v3s_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A33, + .enable_pmu_unk1 = true, + .phy0_dual_route = true, +}; + static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { .num_phys = 2, .type = sun50i_a64_phy, @@ -435,6 +445,7 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { static const struct udevice_id sun4i_usb_phy_ids[] = { { .compatible = "allwinner,sun8i-h3-usb-phy", .data = (ulong)&sun8i_h3_cfg }, + { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = (ulong)&sun8i_v3s_cfg }, { .compatible = "allwinner,sun50i-a64-usb-phy", .data = (ulong)&sun50i_a64_cfg}, { } }; -- cgit v1.2.3 From 5f646bf1d7d0b22eefec3c22d5c9c94c6eb79228 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 7 May 2018 13:03:30 +0530 Subject: phy: sun4i-usb: Add A83T USB PHY config Unlike, other Allwinner SUN4I Phy supporting SOC, A83T has 2 USB PHY's and second one is HSIC. So phy control need to configure to handle these HSIC and SIDDQ requirement. Signed-off-by: Jagan Teki Acked-by: Jun Nie --- drivers/phy/allwinner/phy-sun4i-usb.c | 85 ++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 17 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index de0a59a32c6..01782ac97cd 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -53,9 +53,19 @@ #define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) #define SUNXI_ULPI_BYPASS_EN BIT(0) +/* A83T specific control bits for PHY0 */ +#define PHY_CTL_VBUSVLDEXT BIT(5) +#define PHY_CTL_SIDDQ BIT(3) + +/* A83T specific control bits for PHY2 HSIC */ +#define SUNXI_EHCI_HS_FORCE BIT(20) +#define SUNXI_HSIC_CONNECT_INT BIT(16) +#define SUNXI_HSIC BIT(1) + #define MAX_PHYS 4 enum sun4i_usb_phy_type { + sun8i_a83t_phy, sun8i_h3_phy, sun8i_v3s_phy, sun50i_a64_phy, @@ -92,13 +102,20 @@ struct sun4i_usb_phy_info { .gpio_vbus = CONFIG_USB2_VBUS_PIN, .gpio_vbus_det = NULL, .gpio_id_det = NULL, +#ifdef CONFIG_MACH_SUN8I_A83T + .rst_mask = (CCM_USB_CTRL_HSIC_RST | CCM_USB_CTRL_HSIC_CLK | + CCM_USB_CTRL_12M_CLK), +#else .rst_mask = (CCM_USB_CTRL_PHY2_RST | CCM_USB_CTRL_PHY2_CLK), +#endif }, { .gpio_vbus = CONFIG_USB3_VBUS_PIN, .gpio_vbus_det = NULL, .gpio_id_det = NULL, +#ifdef CONFIG_MACH_SUN6I .rst_mask = (CCM_USB_CTRL_PHY3_RST | CCM_USB_CTRL_PHY3_CLK), +#endif }, }; @@ -166,9 +183,10 @@ static void sun4i_usb_phy_write(struct phy *phy, u32 addr, u32 data, int len) } } -static void sun4i_usb_phy_passby(struct sun4i_usb_phy_plat *usb_phy, - bool enable) +static void sun4i_usb_phy_passby(struct phy *phy, bool enable) { + struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); + struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; u32 bits, reg_value; if (!usb_phy->pmu) @@ -176,6 +194,12 @@ static void sun4i_usb_phy_passby(struct sun4i_usb_phy_plat *usb_phy, bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN | SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN; + + /* A83T USB2 is HSIC */ + if (data->cfg->type == sun8i_a83t_phy && usb_phy->id == 2) + bits |= SUNXI_EHCI_HS_FORCE | SUNXI_HSIC_CONNECT_INT | + SUNXI_HSIC; + reg_value = readl(usb_phy->pmu); if (enable) @@ -244,25 +268,36 @@ static int sun4i_usb_phy_init(struct phy *phy) setbits_le32(&data->ccm->usb_clk_cfg, usb_phy->rst_mask); - if (usb_phy->pmu && data->cfg->enable_pmu_unk1) { - val = readl(usb_phy->pmu + REG_PMU_UNK1); - writel(val & ~2, usb_phy->pmu + REG_PMU_UNK1); - } + if (data->cfg->type == sun8i_a83t_phy) { + if (phy->id == 0) { + val = readl(data->base + data->cfg->phyctl_offset); + val |= PHY_CTL_VBUSVLDEXT; + val &= ~PHY_CTL_SIDDQ; + writel(val, data->base + data->cfg->phyctl_offset); + } + } else { + if (usb_phy->pmu && data->cfg->enable_pmu_unk1) { + val = readl(usb_phy->pmu + REG_PMU_UNK1); + writel(val & ~2, usb_phy->pmu + REG_PMU_UNK1); + } - if (usb_phy->id == 0) - sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, PHY_RES45_CAL_DATA, - PHY_RES45_CAL_LEN); + if (usb_phy->id == 0) + sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, + PHY_RES45_CAL_DATA, + PHY_RES45_CAL_LEN); - /* Adjust PHY's magnitude and rate */ - sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, PHY_TX_MAGNITUDE | - PHY_TX_RATE, PHY_TX_AMPLITUDE_LEN); + /* Adjust PHY's magnitude and rate */ + sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, + PHY_TX_MAGNITUDE | PHY_TX_RATE, + PHY_TX_AMPLITUDE_LEN); - /* Disconnect threshold adjustment */ - sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->cfg->disc_thresh, - PHY_DISCON_TH_LEN); + /* Disconnect threshold adjustment */ + sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, + data->cfg->disc_thresh, PHY_DISCON_TH_LEN); + } if (usb_phy->id != 0) - sun4i_usb_phy_passby(usb_phy, true); + sun4i_usb_phy_passby(phy, true); sun4i_usb_phy0_reroute(data, true); @@ -274,7 +309,16 @@ static int sun4i_usb_phy_exit(struct phy *phy) struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; - sun4i_usb_phy_passby(usb_phy, false); + if (phy->id == 0) { + if (data->cfg->type == sun8i_a83t_phy) { + void __iomem *phyctl = data->base + + data->cfg->phyctl_offset; + + writel(readl(phyctl) | PHY_CTL_SIDDQ, phyctl); + } + } + + sun4i_usb_phy_passby(phy, false); clrbits_le32(&data->ccm->usb_clk_cfg, usb_phy->rst_mask); @@ -416,6 +460,12 @@ static int sun4i_usb_phy_probe(struct udevice *dev) return 0; } +static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = { + .num_phys = 3, + .type = sun8i_a83t_phy, + .phyctl_offset = REG_PHYCTL_A33, +}; + static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { .num_phys = 4, .type = sun8i_h3_phy, @@ -444,6 +494,7 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { }; static const struct udevice_id sun4i_usb_phy_ids[] = { + { .compatible = "allwinner,sun8i-a83t-usb-phy", .data = (ulong)&sun8i_a83t_cfg }, { .compatible = "allwinner,sun8i-h3-usb-phy", .data = (ulong)&sun8i_h3_cfg }, { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = (ulong)&sun8i_v3s_cfg }, { .compatible = "allwinner,sun50i-a64-usb-phy", .data = (ulong)&sun50i_a64_cfg}, -- cgit v1.2.3 From 7f90b557c99e2b32072701a430c040b1e31c33cd Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 7 May 2018 13:03:31 +0530 Subject: phy: sun4i-usb: Add A10/A13/A20 PHY config Add PHY configs for Allwinner A10/A13/A20 which are SUN4I. Signed-off-by: Jagan Teki Acked-by: Jun Nie --- drivers/phy/allwinner/phy-sun4i-usb.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 01782ac97cd..abb3d0f0554 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -65,6 +65,7 @@ #define MAX_PHYS 4 enum sun4i_usb_phy_type { + sun4i_a10_phy, sun8i_a83t_phy, sun8i_h3_phy, sun8i_v3s_phy, @@ -460,6 +461,30 @@ static int sun4i_usb_phy_probe(struct udevice *dev) return 0; } +static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = { + .num_phys = 3, + .type = sun4i_a10_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A10, + .enable_pmu_unk1 = false, +}; + +static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { + .num_phys = 2, + .type = sun4i_a10_phy, + .disc_thresh = 2, + .phyctl_offset = REG_PHYCTL_A10, + .enable_pmu_unk1 = false, +}; + +static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { + .num_phys = 3, + .type = sun4i_a10_phy, + .disc_thresh = 2, + .phyctl_offset = REG_PHYCTL_A10, + .enable_pmu_unk1 = false, +}; + static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = { .num_phys = 3, .type = sun8i_a83t_phy, @@ -494,6 +519,9 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { }; static const struct udevice_id sun4i_usb_phy_ids[] = { + { .compatible = "allwinner,sun4i-a10-usb-phy", .data = (ulong)&sun4i_a10_cfg }, + { .compatible = "allwinner,sun5i-a13-usb-phy", .data = (ulong)&sun5i_a13_cfg }, + { .compatible = "allwinner,sun7i-a20-usb-phy", .data = (ulong)&sun7i_a20_cfg }, { .compatible = "allwinner,sun8i-a83t-usb-phy", .data = (ulong)&sun8i_a83t_cfg }, { .compatible = "allwinner,sun8i-h3-usb-phy", .data = (ulong)&sun8i_h3_cfg }, { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = (ulong)&sun8i_v3s_cfg }, -- cgit v1.2.3 From bf986d1f60730eebd12337aa8f7f1bf566fc9087 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 7 May 2018 13:03:32 +0530 Subject: phy: sun4i-usb: Add A31 PHY config Allwinner A31 has 3 USB PHY's and rest similar to A10. Signed-off-by: Jagan Teki Acked-by: Jun Nie --- drivers/phy/allwinner/phy-sun4i-usb.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index abb3d0f0554..16b399e8803 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -66,6 +66,7 @@ enum sun4i_usb_phy_type { sun4i_a10_phy, + sun6i_a31_phy, sun8i_a83t_phy, sun8i_h3_phy, sun8i_v3s_phy, @@ -477,6 +478,14 @@ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { .enable_pmu_unk1 = false, }; +static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { + .num_phys = 3, + .type = sun6i_a31_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A10, + .enable_pmu_unk1 = false, +}; + static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { .num_phys = 3, .type = sun4i_a10_phy, @@ -521,6 +530,7 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { static const struct udevice_id sun4i_usb_phy_ids[] = { { .compatible = "allwinner,sun4i-a10-usb-phy", .data = (ulong)&sun4i_a10_cfg }, { .compatible = "allwinner,sun5i-a13-usb-phy", .data = (ulong)&sun5i_a13_cfg }, + { .compatible = "allwinner,sun6i-a31-usb-phy", .data = (ulong)&sun6i_a31_cfg }, { .compatible = "allwinner,sun7i-a20-usb-phy", .data = (ulong)&sun7i_a20_cfg }, { .compatible = "allwinner,sun8i-a83t-usb-phy", .data = (ulong)&sun8i_a83t_cfg }, { .compatible = "allwinner,sun8i-h3-usb-phy", .data = (ulong)&sun8i_h3_cfg }, -- cgit v1.2.3 From 61bf0ed5dbeb944970605f829bd0c631f7d70b13 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 7 May 2018 13:03:33 +0530 Subject: phy: sun4i-usb: Add A33 USB PHY config Allwinner A33 has 2 USB PHY's and 0x10 has phy ctrl offset. Signed-off-by: Jagan Teki Acked-by: Jun Nie --- drivers/phy/allwinner/phy-sun4i-usb.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 16b399e8803..75056e250ca 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -67,6 +67,7 @@ enum sun4i_usb_phy_type { sun4i_a10_phy, sun6i_a31_phy, + sun8i_a33_phy, sun8i_a83t_phy, sun8i_h3_phy, sun8i_v3s_phy, @@ -494,6 +495,14 @@ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { .enable_pmu_unk1 = false, }; +static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { + .num_phys = 2, + .type = sun8i_a33_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A33, + .enable_pmu_unk1 = false, +}; + static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = { .num_phys = 3, .type = sun8i_a83t_phy, @@ -532,6 +541,7 @@ static const struct udevice_id sun4i_usb_phy_ids[] = { { .compatible = "allwinner,sun5i-a13-usb-phy", .data = (ulong)&sun5i_a13_cfg }, { .compatible = "allwinner,sun6i-a31-usb-phy", .data = (ulong)&sun6i_a31_cfg }, { .compatible = "allwinner,sun7i-a20-usb-phy", .data = (ulong)&sun7i_a20_cfg }, + { .compatible = "allwinner,sun8i-a33-usb-phy", .data = (ulong)&sun8i_a33_cfg }, { .compatible = "allwinner,sun8i-a83t-usb-phy", .data = (ulong)&sun8i_a83t_cfg }, { .compatible = "allwinner,sun8i-h3-usb-phy", .data = (ulong)&sun8i_h3_cfg }, { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = (ulong)&sun8i_v3s_cfg }, -- cgit v1.2.3 From 194ccb9a349c50f1fd75e2bcb67490cb5a738de4 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 7 May 2018 13:03:34 +0530 Subject: phy: sun4i-usb: Add A23 USB PHY config Allwinner A23 has 2 USB PHY's and 0x04 has phy ctrl offset. Signed-off-by: Jagan Teki Acked-by: Jun Nie --- drivers/phy/allwinner/phy-sun4i-usb.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 75056e250ca..4194a150077 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -495,6 +495,14 @@ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { .enable_pmu_unk1 = false, }; +static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { + .num_phys = 2, + .type = sun4i_a10_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A10, + .enable_pmu_unk1 = false, +}; + static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { .num_phys = 2, .type = sun8i_a33_phy, @@ -541,6 +549,7 @@ static const struct udevice_id sun4i_usb_phy_ids[] = { { .compatible = "allwinner,sun5i-a13-usb-phy", .data = (ulong)&sun5i_a13_cfg }, { .compatible = "allwinner,sun6i-a31-usb-phy", .data = (ulong)&sun6i_a31_cfg }, { .compatible = "allwinner,sun7i-a20-usb-phy", .data = (ulong)&sun7i_a20_cfg }, + { .compatible = "allwinner,sun8i-a23-usb-phy", .data = (ulong)&sun8i_a23_cfg }, { .compatible = "allwinner,sun8i-a33-usb-phy", .data = (ulong)&sun8i_a33_cfg }, { .compatible = "allwinner,sun8i-a83t-usb-phy", .data = (ulong)&sun8i_a83t_cfg }, { .compatible = "allwinner,sun8i-h3-usb-phy", .data = (ulong)&sun8i_h3_cfg }, -- cgit v1.2.3 From aa29b11b3fdeaedb9689e89e467e6bbd036780ac Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 7 May 2018 13:03:37 +0530 Subject: phy: sun4i-usb: Add a sunxi specific function for setting squelch-detect The sunxi otg phy has a bug where it wrongly detects a high speed squelch when reset on the root port gets de-asserted with a lo-speed device. The workaround for this is to disable squelch detect before de-asserting reset, and re-enabling it after the reset de-assert is done. Add a sunxi specific phy function to allow the sunxi-musb glue to do this. Signed-off-by: Jagan Teki Acked-by: Jun Nie --- drivers/phy/allwinner/phy-sun4i-usb.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 4194a150077..2b3cf48025c 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -36,6 +36,7 @@ #define PHY_TX_AMPLITUDE_TUNE 0x20 #define PHY_TX_SLEWRATE_TUNE 0x22 #define PHY_DISCON_TH_SEL 0x2a +#define PHY_SQUELCH_DETECT 0x3c #define PHYCTL_DATA BIT(7) #define OTGCTL_ROUTE_MUSB BIT(0) @@ -383,6 +384,11 @@ int sun4i_usb_phy_id_detect(struct phy *phy) return gpio_get_value(usb_phy->gpio_id_det); } +void sun4i_usb_phy_set_squelch_detect(struct phy *phy, bool enabled) +{ + sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2); +} + static struct phy_ops sun4i_usb_phy_ops = { .of_xlate = sun4i_usb_phy_xlate, .init = sun4i_usb_phy_init, -- cgit v1.2.3