diff options
author | Tom Rini <trini@konsulko.com> | 2018-06-04 08:55:00 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2018-06-04 08:55:00 -0400 |
commit | 809e0e398a91db7bf8b4d6259d9bfc6fbd6bce83 (patch) | |
tree | 7ae663a2a108d27eae7ce25f912d337282d94c66 /drivers | |
parent | b5351a439088dfffd83bfaac81f604344ee2e3bd (diff) | |
parent | 2e8dbe14e766deae16c75f357f926f08b881dc66 (diff) |
Merge branch 'master' of git://git.denx.de/u-boot-sunxi
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/allwinner/Kconfig | 13 | ||||
-rw-r--r-- | drivers/phy/allwinner/Makefile | 6 | ||||
-rw-r--r-- | drivers/phy/allwinner/phy-sun4i-usb.c | 575 | ||||
-rw-r--r-- | drivers/usb/host/ehci-sunxi.c | 66 | ||||
-rw-r--r-- | drivers/usb/host/ohci-sunxi.c | 72 | ||||
-rw-r--r-- | drivers/usb/musb-new/musb_core.h | 4 | ||||
-rw-r--r-- | drivers/usb/musb-new/musb_regs.h | 3 | ||||
-rw-r--r-- | drivers/usb/musb-new/musb_uboot.c | 22 | ||||
-rw-r--r-- | drivers/usb/musb-new/sunxi.c | 228 |
11 files changed, 894 insertions, 98 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 8424898dbdc..9e21b28750c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -62,6 +62,8 @@ source "drivers/pcmcia/Kconfig" source "drivers/phy/Kconfig" +source "drivers/phy/allwinner/Kconfig" + source "drivers/phy/marvell/Kconfig" source "drivers/pinctrl/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index d29a6e467b6..a213ea96718 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_NVME) += nvme/ obj-y += pcmcia/ obj-y += dfu/ obj-$(CONFIG_X86) += pch/ +obj-y += phy/allwinner/ obj-y += phy/marvell/ obj-y += rtc/ obj-y += scsi/ 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..2b3cf48025c --- /dev/null +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -0,0 +1,575 @@ +/* + * Allwinner sun4i USB PHY driver + * + * Copyright (C) 2017 Jagan Teki <jagan@amarulasolutions.com> + * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com> + * Copyright (C) 2014 Roman Byshko <rbyshko@gmail.com> + * + * Modelled arch/arm/mach-sunxi/usb_phy.c to compatible with generic-phy. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <dm/device.h> +#include <generic-phy.h> +#include <phy-sun4i-usb.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/cpu.h> + +#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 PHY_SQUELCH_DETECT 0x3c + +#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) + +/* 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 { + sun4i_a10_phy, + sun6i_a31_phy, + sun8i_a33_phy, + sun8i_a83t_phy, + sun8i_h3_phy, + sun8i_v3s_phy, + 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, +#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 + }, +}; + +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 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) + return; + + 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) + 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 (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); + + /* 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(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]; + + 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); + + 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; +} + +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); +} + +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, + .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 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 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, + .disc_thresh = 2, + .phyctl_offset = REG_PHYCTL_A10, + .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, + .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, + .phyctl_offset = REG_PHYCTL_A33, +}; + +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 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, + .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,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-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 }, + { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = (ulong)&sun8i_v3s_cfg }, + { .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), +}; diff --git a/drivers/usb/host/ehci-sunxi.c b/drivers/usb/host/ehci-sunxi.c index 1297fdb6a71..360efc91160 100644 --- a/drivers/usb/host/ehci-sunxi.c +++ b/drivers/usb/host/ehci-sunxi.c @@ -11,34 +11,62 @@ #include <common.h> #include <asm/arch/clock.h> -#include <asm/arch/usb_phy.h> #include <asm/io.h> #include <dm.h> #include "ehci.h" +#include <generic-phy.h> #ifdef CONFIG_SUNXI_GEN_SUN4I -#define BASE_DIST 0x8000 #define AHB_CLK_DIST 2 #else -#define BASE_DIST 0x1000 #define AHB_CLK_DIST 1 #endif struct ehci_sunxi_priv { struct ehci_ctrl ehci; + struct sunxi_ccm_reg *ccm; int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */ - int phy_index; /* Index of the usb-phy attached to this hcd */ + struct phy phy; }; static int ehci_usb_probe(struct udevice *dev) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; struct usb_platdata *plat = dev_get_platdata(dev); struct ehci_sunxi_priv *priv = dev_get_priv(dev); struct ehci_hccr *hccr = (struct ehci_hccr *)devfdt_get_addr(dev); struct ehci_hcor *hcor; int extra_ahb_gate_mask = 0; + int phys, ret; + priv->ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + if (IS_ERR(priv->ccm)) + return PTR_ERR(priv->ccm); + + phys = dev_count_phandle_with_args(dev, "phys", "#phy-cells"); + if (phys < 0) { + phys = 0; + goto no_phy; + } + + ret = generic_phy_get_by_name(dev, "usb", &priv->phy); + if (ret) { + pr_err("failed to get %s usb PHY\n", dev->name); + return ret; + } + + ret = generic_phy_init(&priv->phy); + if (ret) { + pr_err("failed to init %s USB PHY\n", dev->name); + return ret; + } + + ret = generic_phy_power_on(&priv->phy); + if (ret) { + pr_err("failed to power on %s USB PHY\n", dev->name); + return ret; + } + +no_phy: /* * This should go away once we've moved to the driver model for * clocks resp. phys. @@ -47,21 +75,16 @@ static int ehci_usb_probe(struct udevice *dev) #if defined(CONFIG_MACH_SUNXI_H3_H5) || defined(CONFIG_MACH_SUN50I) extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI0; #endif - priv->phy_index = ((uintptr_t)hccr - SUNXI_USB1_BASE) / BASE_DIST; - priv->ahb_gate_mask <<= priv->phy_index * AHB_CLK_DIST; - extra_ahb_gate_mask <<= priv->phy_index * AHB_CLK_DIST; - priv->phy_index++; /* Non otg phys start at 1 */ + priv->ahb_gate_mask <<= phys * AHB_CLK_DIST; + extra_ahb_gate_mask <<= phys * AHB_CLK_DIST; - setbits_le32(&ccm->ahb_gate0, + setbits_le32(&priv->ccm->ahb_gate0, priv->ahb_gate_mask | extra_ahb_gate_mask); #ifdef CONFIG_SUNXI_GEN_SUN6I - setbits_le32(&ccm->ahb_reset0_cfg, + setbits_le32(&priv->ccm->ahb_reset0_cfg, priv->ahb_gate_mask | extra_ahb_gate_mask); #endif - sunxi_usb_phy_init(priv->phy_index); - sunxi_usb_phy_power_on(priv->phy_index); - hcor = (struct ehci_hcor *)((uintptr_t)hccr + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); @@ -70,20 +93,25 @@ static int ehci_usb_probe(struct udevice *dev) static int ehci_usb_remove(struct udevice *dev) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; struct ehci_sunxi_priv *priv = dev_get_priv(dev); int ret; + if (generic_phy_valid(&priv->phy)) { + ret = generic_phy_exit(&priv->phy); + if (ret) { + pr_err("failed to exit %s USB PHY\n", dev->name); + return ret; + } + } + ret = ehci_deregister(dev); if (ret) return ret; - sunxi_usb_phy_exit(priv->phy_index); - #ifdef CONFIG_SUNXI_GEN_SUN6I - clrbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask); + clrbits_le32(&priv->ccm->ahb_reset0_cfg, priv->ahb_gate_mask); #endif - clrbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask); + clrbits_le32(&priv->ccm->ahb_gate0, priv->ahb_gate_mask); return 0; } diff --git a/drivers/usb/host/ohci-sunxi.c b/drivers/usb/host/ohci-sunxi.c index b78fad29a35..ce2b47a5c4b 100644 --- a/drivers/usb/host/ohci-sunxi.c +++ b/drivers/usb/host/ohci-sunxi.c @@ -10,35 +10,63 @@ #include <common.h> #include <asm/arch/clock.h> -#include <asm/arch/usb_phy.h> #include <asm/io.h> #include <dm.h> #include <usb.h> #include "ohci.h" +#include <generic-phy.h> #ifdef CONFIG_SUNXI_GEN_SUN4I -#define BASE_DIST 0x8000 #define AHB_CLK_DIST 2 #else -#define BASE_DIST 0x1000 #define AHB_CLK_DIST 1 #endif struct ohci_sunxi_priv { + struct sunxi_ccm_reg *ccm; ohci_t ohci; int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */ int usb_gate_mask; /* Mask of usb_clk_cfg clk gate bits for this hcd */ - int phy_index; /* Index of the usb-phy attached to this hcd */ + struct phy phy; }; static int ohci_usb_probe(struct udevice *dev) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev); struct ohci_sunxi_priv *priv = dev_get_priv(dev); struct ohci_regs *regs = (struct ohci_regs *)devfdt_get_addr(dev); int extra_ahb_gate_mask = 0; + int phys, ret; + priv->ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + if (IS_ERR(priv->ccm)) + return PTR_ERR(priv->ccm); + + phys = dev_count_phandle_with_args(dev, "phys", "#phy-cells"); + if (phys < 0) { + phys = 0; + goto no_phy; + } + + ret = generic_phy_get_by_name(dev, "usb", &priv->phy); + if (ret) { + pr_err("failed to get %s usb PHY\n", dev->name); + return ret; + } + + ret = generic_phy_init(&priv->phy); + if (ret) { + pr_err("failed to init %s USB PHY\n", dev->name); + return ret; + } + + ret = generic_phy_power_on(&priv->phy); + if (ret) { + pr_err("failed to power on %s USB PHY\n", dev->name); + return ret; + } + +no_phy: bus_priv->companion = true; /* @@ -50,43 +78,43 @@ static int ohci_usb_probe(struct udevice *dev) extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0; #endif priv->usb_gate_mask = CCM_USB_CTRL_OHCI0_CLK; - priv->phy_index = ((uintptr_t)regs - (SUNXI_USB1_BASE + 0x400)) / BASE_DIST; - priv->ahb_gate_mask <<= priv->phy_index * AHB_CLK_DIST; - extra_ahb_gate_mask <<= priv->phy_index * AHB_CLK_DIST; - priv->usb_gate_mask <<= priv->phy_index; - priv->phy_index++; /* Non otg phys start at 1 */ + priv->ahb_gate_mask <<= phys * AHB_CLK_DIST; + extra_ahb_gate_mask <<= phys * AHB_CLK_DIST; + priv->usb_gate_mask <<= phys; - setbits_le32(&ccm->ahb_gate0, + setbits_le32(&priv->ccm->ahb_gate0, priv->ahb_gate_mask | extra_ahb_gate_mask); - setbits_le32(&ccm->usb_clk_cfg, priv->usb_gate_mask); + setbits_le32(&priv->ccm->usb_clk_cfg, priv->usb_gate_mask); #ifdef CONFIG_SUNXI_GEN_SUN6I - setbits_le32(&ccm->ahb_reset0_cfg, + setbits_le32(&priv->ccm->ahb_reset0_cfg, priv->ahb_gate_mask | extra_ahb_gate_mask); #endif - sunxi_usb_phy_init(priv->phy_index); - sunxi_usb_phy_power_on(priv->phy_index); - return ohci_register(dev, regs); } static int ohci_usb_remove(struct udevice *dev) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; struct ohci_sunxi_priv *priv = dev_get_priv(dev); int ret; + if (generic_phy_valid(&priv->phy)) { + ret = generic_phy_exit(&priv->phy); + if (ret) { + pr_err("failed to exit %s USB PHY\n", dev->name); + return ret; + } + } + ret = ohci_deregister(dev); if (ret) return ret; - sunxi_usb_phy_exit(priv->phy_index); - #ifdef CONFIG_SUNXI_GEN_SUN6I - clrbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask); + clrbits_le32(&priv->ccm->ahb_reset0_cfg, priv->ahb_gate_mask); #endif - clrbits_le32(&ccm->usb_clk_cfg, priv->usb_gate_mask); - clrbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask); + clrbits_le32(&priv->ccm->usb_clk_cfg, priv->usb_gate_mask); + clrbits_le32(&priv->ccm->ahb_gate0, priv->ahb_gate_mask); return 0; } diff --git a/drivers/usb/musb-new/musb_core.h b/drivers/usb/musb-new/musb_core.h index fa4cf86c79b..821d0e06f61 100644 --- a/drivers/usb/musb-new/musb_core.h +++ b/drivers/usb/musb-new/musb_core.h @@ -200,6 +200,8 @@ enum musb_g_ep0_state { * @vbus_status: returns vbus status if possible * @set_vbus: forces vbus status * @adjust_channel_params: pre check for standard dma channel_program func + * @pre_root_reset_end: called before the root usb port reset flag gets cleared + * @post_root_reset_end: called after the root usb port reset flag gets cleared */ struct musb_platform_ops { int (*init)(struct musb *musb); @@ -221,6 +223,8 @@ struct musb_platform_ops { int (*adjust_channel_params)(struct dma_channel *channel, u16 packet_sz, u8 *mode, dma_addr_t *dma_addr, u32 *len); + void (*pre_root_reset_end)(struct musb *musb); + void (*post_root_reset_end)(struct musb *musb); }; /* diff --git a/drivers/usb/musb-new/musb_regs.h b/drivers/usb/musb-new/musb_regs.h index ab0d98e7c48..c4d7203b851 100644 --- a/drivers/usb/musb-new/musb_regs.h +++ b/drivers/usb/musb-new/musb_regs.h @@ -431,7 +431,8 @@ static inline u8 musb_read_ulpi_buscontrol(void __iomem *mbase) static inline u8 musb_read_configdata(void __iomem *mbase) { -#if defined CONFIG_MACH_SUN8I_A33 || defined CONFIG_MACH_SUN8I_A83T +#if defined CONFIG_MACH_SUN8I_A33 || defined CONFIG_MACH_SUN8I_A83T || \ + defined CONFIG_MACH_SUNXI_H3_H5 || defined CONFIG_MACH_SUN50I /* <Sigh> allwinner saves a reg, and we need to hardcode this */ return 0xde; #else diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index 8662c0ff707..2b04fbd046e 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -1,9 +1,6 @@ #include <common.h> #include <console.h> #include <watchdog.h> -#ifdef CONFIG_ARCH_SUNXI -#include <asm/arch/usb_phy.h> -#endif #include <linux/errno.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -193,19 +190,16 @@ static int _musb_reset_root_port(struct musb_host_data *host, power &= 0xf0; musb_writeb(mbase, MUSB_POWER, MUSB_POWER_RESET | power); mdelay(50); -#ifdef CONFIG_ARCH_SUNXI - /* - * sunxi phy has a bug and it will wrongly detect high speed squelch - * when clearing reset on low-speed devices, temporary disable - * squelch detection to work around this. - */ - sunxi_usb_phy_enable_squelch_detect(0, 0); -#endif + + if (host->host->ops->pre_root_reset_end) + host->host->ops->pre_root_reset_end(host->host); + power = musb_readb(mbase, MUSB_POWER); musb_writeb(mbase, MUSB_POWER, ~MUSB_POWER_RESET & power); -#ifdef CONFIG_ARCH_SUNXI - sunxi_usb_phy_enable_squelch_detect(0, 1); -#endif + + if (host->host->ops->post_root_reset_end) + host->host->ops->post_root_reset_end(host->host); + host->host->isr(0, host->host); host->host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ? USB_SPEED_HIGH : diff --git a/drivers/usb/musb-new/sunxi.c b/drivers/usb/musb-new/sunxi.c index f55368b6016..08de9c69c71 100644 --- a/drivers/usb/musb-new/sunxi.c +++ b/drivers/usb/musb-new/sunxi.c @@ -17,10 +17,11 @@ */ #include <common.h> #include <dm.h> +#include <generic-phy.h> +#include <phy-sun4i-usb.h> #include <asm/arch/cpu.h> #include <asm/arch/clock.h> #include <asm/arch/gpio.h> -#include <asm/arch/usb_phy.h> #include <asm-generic/gpio.h> #include <dm/lists.h> #include <dm/root.h> @@ -75,13 +76,28 @@ * From usbc/usbc.c ******************************************************************************/ +struct sunxi_musb_config { + struct musb_hdrc_config *config; + u8 rst_bit; + u8 clkgate_bit; +}; + +struct sunxi_glue { + struct musb_host_data mdata; + struct sunxi_ccm_reg *ccm; + struct sunxi_musb_config *cfg; + struct device dev; + struct phy *phy; +}; +#define to_sunxi_glue(d) container_of(d, struct sunxi_glue, dev) + static u32 USBC_WakeUp_ClearChangeDetect(u32 reg_val) { u32 temp = reg_val; - temp &= ~(1 << USBC_BP_ISCR_VBUS_CHANGE_DETECT); - temp &= ~(1 << USBC_BP_ISCR_ID_CHANGE_DETECT); - temp &= ~(1 << USBC_BP_ISCR_DPDM_CHANGE_DETECT); + temp &= ~BIT(USBC_BP_ISCR_VBUS_CHANGE_DETECT); + temp &= ~BIT(USBC_BP_ISCR_ID_CHANGE_DETECT); + temp &= ~BIT(USBC_BP_ISCR_DPDM_CHANGE_DETECT); return temp; } @@ -91,7 +107,7 @@ static void USBC_EnableIdPullUp(__iomem void *base) u32 reg_val; reg_val = musb_readl(base, USBC_REG_o_ISCR); - reg_val |= (1 << USBC_BP_ISCR_ID_PULLUP_EN); + reg_val |= BIT(USBC_BP_ISCR_ID_PULLUP_EN); reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); musb_writel(base, USBC_REG_o_ISCR, reg_val); } @@ -101,7 +117,7 @@ static void USBC_EnableDpDmPullUp(__iomem void *base) u32 reg_val; reg_val = musb_readl(base, USBC_REG_o_ISCR); - reg_val |= (1 << USBC_BP_ISCR_DPDM_PULLUP_EN); + reg_val |= BIT(USBC_BP_ISCR_DPDM_PULLUP_EN); reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); musb_writel(base, USBC_REG_o_ISCR, reg_val); } @@ -157,7 +173,7 @@ static void USBC_ConfigFIFO_Base(void) /* config usb fifo, 8kb mode */ reg_value = readl(SUNXI_SRAMC_BASE + 0x04); reg_value &= ~(0x03 << 0); - reg_value |= (1 << 0); + reg_value |= BIT(0); writel(reg_value, SUNXI_SRAMC_BASE + 0x04); } @@ -204,6 +220,7 @@ static bool enabled = false; static int sunxi_musb_enable(struct musb *musb) { + struct sunxi_glue *glue = to_sunxi_glue(musb->controller); int ret; pr_debug("%s():\n", __func__); @@ -218,17 +235,23 @@ static int sunxi_musb_enable(struct musb *musb) musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); if (is_host_enabled(musb)) { - ret = sunxi_usb_phy_vbus_detect(0); + ret = sun4i_usb_phy_vbus_detect(glue->phy); if (ret == 1) { printf("A charger is plugged into the OTG: "); return -ENODEV; } - ret = sunxi_usb_phy_id_detect(0); + + ret = sun4i_usb_phy_id_detect(glue->phy); if (ret == 1) { printf("No host cable detected: "); return -ENODEV; } - sunxi_usb_phy_power_on(0); /* port power on */ + + ret = generic_phy_power_on(glue->phy); + if (ret) { + pr_err("failed to power on USB PHY\n"); + return ret; + } } USBC_ForceVbusValidToHigh(musb->mregs); @@ -239,13 +262,21 @@ static int sunxi_musb_enable(struct musb *musb) static void sunxi_musb_disable(struct musb *musb) { + struct sunxi_glue *glue = to_sunxi_glue(musb->controller); + int ret; + pr_debug("%s():\n", __func__); if (!enabled) return; - if (is_host_enabled(musb)) - sunxi_usb_phy_power_off(0); /* port power off */ + if (is_host_enabled(musb)) { + ret = generic_phy_power_off(glue->phy); + if (ret) { + pr_err("failed to power off USB PHY\n"); + return; + } + } USBC_ForceVbusValidToLow(musb->mregs); mdelay(200); /* Wait for the current session to timeout */ @@ -255,17 +286,29 @@ static void sunxi_musb_disable(struct musb *musb) static int sunxi_musb_init(struct musb *musb) { - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_glue *glue = to_sunxi_glue(musb->controller); + int ret; pr_debug("%s():\n", __func__); + ret = generic_phy_init(glue->phy); + if (ret) { + pr_err("failed to init USB PHY\n"); + return ret; + } + musb->isr = sunxi_musb_interrupt; - setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0); + setbits_le32(&glue->ccm->ahb_gate0, BIT(AHB_GATE_OFFSET_USB0)); + if (glue->cfg->clkgate_bit) + setbits_le32(&glue->ccm->ahb_gate0, + BIT(glue->cfg->clkgate_bit)); #ifdef CONFIG_SUNXI_GEN_SUN6I - setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0); + setbits_le32(&glue->ccm->ahb_reset0_cfg, BIT(AHB_GATE_OFFSET_USB0)); + if (glue->cfg->rst_bit) + setbits_le32(&glue->ccm->ahb_reset0_cfg, + BIT(glue->cfg->rst_bit)); #endif - sunxi_usb_phy_init(0); USBC_ConfigFIFO_Base(); USBC_EnableDpDmPullUp(musb->mregs); @@ -283,44 +326,116 @@ static int sunxi_musb_init(struct musb *musb) return 0; } +static void sunxi_musb_pre_root_reset_end(struct musb *musb) +{ + struct sunxi_glue *glue = to_sunxi_glue(musb->controller); + + sun4i_usb_phy_set_squelch_detect(glue->phy, false); +} + +static void sunxi_musb_post_root_reset_end(struct musb *musb) +{ + struct sunxi_glue *glue = to_sunxi_glue(musb->controller); + + sun4i_usb_phy_set_squelch_detect(glue->phy, true); +} + static const struct musb_platform_ops sunxi_musb_ops = { .init = sunxi_musb_init, .enable = sunxi_musb_enable, .disable = sunxi_musb_disable, + .pre_root_reset_end = sunxi_musb_pre_root_reset_end, + .post_root_reset_end = sunxi_musb_post_root_reset_end, +}; + +/* Allwinner OTG supports up to 5 endpoints */ +#define SUNXI_MUSB_MAX_EP_NUM 6 +#define SUNXI_MUSB_RAM_BITS 11 + +static struct musb_fifo_cfg sunxi_musb_mode_cfg[] = { + MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512), + MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512), + MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512), + MUSB_EP_FIFO_SINGLE(2, FIFO_RX, 512), + MUSB_EP_FIFO_SINGLE(3, FIFO_TX, 512), + MUSB_EP_FIFO_SINGLE(3, FIFO_RX, 512), + MUSB_EP_FIFO_SINGLE(4, FIFO_TX, 512), + MUSB_EP_FIFO_SINGLE(4, FIFO_RX, 512), + MUSB_EP_FIFO_SINGLE(5, FIFO_TX, 512), + MUSB_EP_FIFO_SINGLE(5, FIFO_RX, 512), +}; + +/* H3/V3s OTG supports only 4 endpoints */ +#define SUNXI_MUSB_MAX_EP_NUM_H3 5 + +static struct musb_fifo_cfg sunxi_musb_mode_cfg_h3[] = { + MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512), + MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512), + MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512), + MUSB_EP_FIFO_SINGLE(2, FIFO_RX, 512), + MUSB_EP_FIFO_SINGLE(3, FIFO_TX, 512), + MUSB_EP_FIFO_SINGLE(3, FIFO_RX, 512), + MUSB_EP_FIFO_SINGLE(4, FIFO_TX, 512), + MUSB_EP_FIFO_SINGLE(4, FIFO_RX, 512), }; static struct musb_hdrc_config musb_config = { - .multipoint = 1, - .dyn_fifo = 1, - .num_eps = 6, - .ram_bits = 11, + .fifo_cfg = sunxi_musb_mode_cfg, + .fifo_cfg_size = ARRAY_SIZE(sunxi_musb_mode_cfg), + .multipoint = true, + .dyn_fifo = true, + .num_eps = SUNXI_MUSB_MAX_EP_NUM, + .ram_bits = SUNXI_MUSB_RAM_BITS, }; -static struct musb_hdrc_platform_data musb_plat = { -#if defined(CONFIG_USB_MUSB_HOST) - .mode = MUSB_HOST, -#else - .mode = MUSB_PERIPHERAL, -#endif - .config = &musb_config, - .power = 250, - .platform_ops = &sunxi_musb_ops, +static struct musb_hdrc_config musb_config_h3 = { + .fifo_cfg = sunxi_musb_mode_cfg_h3, + .fifo_cfg_size = ARRAY_SIZE(sunxi_musb_mode_cfg_h3), + .multipoint = true, + .dyn_fifo = true, + .soft_con = true, + .num_eps = SUNXI_MUSB_MAX_EP_NUM_H3, + .ram_bits = SUNXI_MUSB_RAM_BITS, }; static int musb_usb_probe(struct udevice *dev) { - struct musb_host_data *host = dev_get_priv(dev); + struct sunxi_glue *glue = dev_get_priv(dev); + struct musb_host_data *host = &glue->mdata; struct usb_bus_priv *priv = dev_get_uclass_priv(dev); + struct musb_hdrc_platform_data pdata; void *base = dev_read_addr_ptr(dev); + struct phy phy; int ret; if (!base) return -EINVAL; + glue->cfg = (struct sunxi_musb_config *)dev_get_driver_data(dev); + if (!glue->cfg) + return -EINVAL; + + glue->ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + if (IS_ERR(glue->ccm)) + return PTR_ERR(glue->ccm); + + ret = generic_phy_get_by_name(dev, "usb", &phy); + if (ret) { + pr_err("failed to get usb PHY\n"); + return ret; + } + + glue->phy = &phy; priv->desc_before_addr = true; + memset(&pdata, 0, sizeof(pdata)); + pdata.power = 250; + pdata.platform_ops = &sunxi_musb_ops; + pdata.config = glue->cfg->config; + #ifdef CONFIG_USB_MUSB_HOST - host->host = musb_init_controller(&musb_plat, NULL, base); + pdata.mode = MUSB_HOST; + host->host = musb_init_controller(&pdata, &glue->dev, base); if (!host->host) return -EIO; @@ -328,7 +443,8 @@ static int musb_usb_probe(struct udevice *dev) if (!ret) printf("Allwinner mUSB OTG (Host)\n"); #else - ret = musb_register(&musb_plat, NULL, base); + pdata.mode = MUSB_PERIPHERAL; + ret = musb_register(&pdata, &glue->dev, base); if (!ret) printf("Allwinner mUSB OTG (Peripheral)\n"); #endif @@ -338,16 +454,30 @@ static int musb_usb_probe(struct udevice *dev) static int musb_usb_remove(struct udevice *dev) { - struct musb_host_data *host = dev_get_priv(dev); - struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_glue *glue = dev_get_priv(dev); + struct musb_host_data *host = &glue->mdata; + int ret; + + if (generic_phy_valid(glue->phy)) { + ret = generic_phy_exit(glue->phy); + if (ret) { + pr_err("failed to exit %s USB PHY\n", dev->name); + return ret; + } + } musb_stop(host->host); - sunxi_usb_phy_exit(0); #ifdef CONFIG_SUNXI_GEN_SUN6I - clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0); + clrbits_le32(&glue->ccm->ahb_reset0_cfg, BIT(AHB_GATE_OFFSET_USB0)); + if (glue->cfg->rst_bit) + clrbits_le32(&glue->ccm->ahb_reset0_cfg, + BIT(glue->cfg->rst_bit)); #endif - clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0); + clrbits_le32(&glue->ccm->ahb_gate0, BIT(AHB_GATE_OFFSET_USB0)); + if (glue->cfg->clkgate_bit) + clrbits_le32(&glue->ccm->ahb_gate0, + BIT(glue->cfg->clkgate_bit)); free(host->host); host->host = NULL; @@ -355,11 +485,25 @@ static int musb_usb_remove(struct udevice *dev) return 0; } +static const struct sunxi_musb_config sun4i_a10_cfg = { + .config = &musb_config, +}; + +static const struct sunxi_musb_config sun8i_h3_cfg = { + .config = &musb_config_h3, + .rst_bit = 23, + .clkgate_bit = 23, +}; + static const struct udevice_id sunxi_musb_ids[] = { - { .compatible = "allwinner,sun4i-a10-musb" }, - { .compatible = "allwinner,sun6i-a31-musb" }, - { .compatible = "allwinner,sun8i-a33-musb" }, - { .compatible = "allwinner,sun8i-h3-musb" }, + { .compatible = "allwinner,sun4i-a10-musb", + .data = (ulong)&sun4i_a10_cfg }, + { .compatible = "allwinner,sun6i-a31-musb", + .data = (ulong)&sun4i_a10_cfg }, + { .compatible = "allwinner,sun8i-a33-musb", + .data = (ulong)&sun4i_a10_cfg }, + { .compatible = "allwinner,sun8i-h3-musb", + .data = (ulong)&sun8i_h3_cfg }, { } }; @@ -377,5 +521,5 @@ U_BOOT_DRIVER(usb_musb) = { .ops = &musb_usb_ops, #endif .platdata_auto_alloc_size = sizeof(struct usb_platdata), - .priv_auto_alloc_size = sizeof(struct musb_host_data), + .priv_auto_alloc_size = sizeof(struct sunxi_glue), }; |