X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fusb%2Fmusb-new%2Fsunxi.c;h=469377fe4e6ddcdc98a277b2c6a81378732569c1;hb=1df182ddf700de49fb4400ba67c3029278ea88e7;hp=c9a6a16b89dd1c15ce23c05eb83560ddbcbb9aa8;hpb=b939689c7b87773c44275a578ffc8674a867e39d;p=oweals%2Fu-boot.git diff --git a/drivers/usb/musb-new/sunxi.c b/drivers/usb/musb-new/sunxi.c index c9a6a16b89..469377fe4e 100644 --- a/drivers/usb/musb-new/sunxi.c +++ b/drivers/usb/musb-new/sunxi.c @@ -14,28 +14,20 @@ * * This file is part of the Inventra Controller Driver for Linux. * - * The Inventra Controller Driver for Linux is free software; you - * can redistribute it and/or modify it under the terms of the GNU - * General Public License version 2 as published by the Free Software - * Foundation. - * + * SPDX-License-Identifier: GPL-2.0 */ #include #include +#include #include -#include +#include #include +#include +#include +#include #include "linux-compat.h" #include "musb_core.h" -#ifdef CONFIG_AXP152_POWER -#include -#endif -#ifdef CONFIG_AXP209_POWER -#include -#endif -#ifdef CONFIG_AXP221_POWER -#include -#endif +#include "musb_uboot.h" /****************************************************************************** ****************************************************************************** @@ -104,16 +96,6 @@ static void USBC_EnableIdPullUp(__iomem void *base) musb_writel(base, USBC_REG_o_ISCR, reg_val); } -static void USBC_DisableIdPullUp(__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 = USBC_WakeUp_ClearChangeDetect(reg_val); - musb_writel(base, USBC_REG_o_ISCR, reg_val); -} - static void USBC_EnableDpDmPullUp(__iomem void *base) { u32 reg_val; @@ -124,34 +106,35 @@ static void USBC_EnableDpDmPullUp(__iomem void *base) musb_writel(base, USBC_REG_o_ISCR, reg_val); } -static void USBC_DisableDpDmPullUp(__iomem void *base) +static void USBC_ForceIdToLow(__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 &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); + reg_val |= (0x02 << USBC_BP_ISCR_FORCE_ID); reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); musb_writel(base, USBC_REG_o_ISCR, reg_val); } -static void USBC_ForceIdToLow(__iomem void *base) +static void USBC_ForceIdToHigh(__iomem void *base) { u32 reg_val; reg_val = musb_readl(base, USBC_REG_o_ISCR); reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); - reg_val |= (0x02 << USBC_BP_ISCR_FORCE_ID); + reg_val |= (0x03 << USBC_BP_ISCR_FORCE_ID); reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); musb_writel(base, USBC_REG_o_ISCR, reg_val); } -static void USBC_ForceIdToHigh(__iomem void *base) +static void USBC_ForceVbusValidToLow(__iomem void *base) { u32 reg_val; reg_val = musb_readl(base, USBC_REG_o_ISCR); - reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID); - reg_val |= (0x03 << USBC_BP_ISCR_FORCE_ID); + reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID); + reg_val |= (0x02 << USBC_BP_ISCR_FORCE_VBUS_VALID); reg_val = USBC_WakeUp_ClearChangeDetect(reg_val); musb_writel(base, USBC_REG_o_ISCR, reg_val); } @@ -178,6 +161,17 @@ static void USBC_ConfigFIFO_Base(void) writel(reg_value, SUNXI_SRAMC_BASE + 0x04); } +/****************************************************************************** + * Needed for the DFU polling magic + ******************************************************************************/ + +static u8 last_int_usb; + +bool dfu_usb_get_reset(void) +{ + return !!(last_int_usb & MUSB_INTR_RESET); +} + /****************************************************************************** * MUSB Glue code ******************************************************************************/ @@ -189,6 +183,7 @@ static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) /* read and flush interrupts */ musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + last_int_usb = musb->int_usb; if (musb->int_usb) musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); @@ -204,52 +199,73 @@ static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) return retval; } -static void sunxi_musb_enable(struct musb *musb) +/* musb_core does not call enable / disable in a balanced manner */ +static bool enabled = false; + +static int sunxi_musb_enable(struct musb *musb) { + int ret; + pr_debug("%s():\n", __func__); + musb_ep_select(musb->mregs, 0); + musb_writeb(musb->mregs, MUSB_FADDR, 0); + + if (enabled) + return 0; + /* select PIO mode */ musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); if (is_host_enabled(musb)) { - /* port power on */ - sunxi_usbc_vbus_enable(0); + ret = sunxi_usb_phy_vbus_detect(0); + if (ret == 1) { + printf("A charger is plugged into the OTG: "); + return -ENODEV; + } + ret = sunxi_usb_phy_id_detect(0); + if (ret == 1) { + printf("No host cable detected: "); + return -ENODEV; + } + sunxi_usb_phy_power_on(0); /* port power on */ } + + USBC_ForceVbusValidToHigh(musb->mregs); + + enabled = true; + return 0; } static void sunxi_musb_disable(struct musb *musb) { pr_debug("%s():\n", __func__); - /* Put the controller back in a pristane state for "usb reset" */ - if (musb->is_active) { - sunxi_usbc_disable(0); - sunxi_usbc_enable(0); - musb->is_active = 0; - } + if (!enabled) + return; + + if (is_host_enabled(musb)) + sunxi_usb_phy_power_off(0); /* port power off */ + + USBC_ForceVbusValidToLow(musb->mregs); + mdelay(200); /* Wait for the current session to timeout */ + + enabled = false; } static int sunxi_musb_init(struct musb *musb) { - int err; + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; pr_debug("%s():\n", __func__); - err = sunxi_usbc_request_resources(0); - if (err) - return err; - - if (is_host_enabled(musb)) { - err = sunxi_usbc_vbus_detect(0); - if (err) { - eprintf("Error: A charger is plugged into the OTG\n"); - sunxi_usbc_free_resources(0); - return -EIO; - } - } - musb->isr = sunxi_musb_interrupt; - sunxi_usbc_enable(0); + + setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0); +#ifdef CONFIG_SUNXI_GEN_SUN6I + setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0); +#endif + sunxi_usb_phy_init(0); USBC_ConfigFIFO_Base(); USBC_EnableDpDmPullUp(musb->mregs); @@ -267,22 +283,98 @@ static int sunxi_musb_init(struct musb *musb) return 0; } -static int sunxi_musb_exit(struct musb *musb) +static const struct musb_platform_ops sunxi_musb_ops = { + .init = sunxi_musb_init, + .enable = sunxi_musb_enable, + .disable = sunxi_musb_disable, +}; + +static struct musb_hdrc_config musb_config = { + .multipoint = 1, + .dyn_fifo = 1, + .num_eps = 6, + .ram_bits = 11, +}; + +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, +}; + +#ifdef CONFIG_USB_MUSB_HOST +static int musb_usb_remove(struct udevice *dev); + +static int musb_usb_probe(struct udevice *dev) { - pr_debug("%s():\n", __func__); + struct musb_host_data *host = dev_get_priv(dev); + struct usb_bus_priv *priv = dev_get_uclass_priv(dev); + int ret; + + priv->desc_before_addr = true; - USBC_DisableDpDmPullUp(musb->mregs); - USBC_DisableIdPullUp(musb->mregs); - sunxi_usbc_vbus_disable(0); - sunxi_usbc_disable(0); + host->host = musb_init_controller(&musb_plat, NULL, + (void *)SUNXI_USB0_BASE); + if (!host->host) + return -EIO; - return sunxi_usbc_free_resources(0); + ret = musb_lowlevel_init(host); + if (ret == 0) + printf("MUSB OTG\n"); + else + musb_usb_remove(dev); + + return ret; } -const struct musb_platform_ops sunxi_musb_ops = { - .init = sunxi_musb_init, - .exit = sunxi_musb_exit, +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; - .enable = sunxi_musb_enable, - .disable = sunxi_musb_disable, + 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); +#endif + clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0); + + free(host->host); + host->host = NULL; + + return 0; +} + +U_BOOT_DRIVER(usb_musb) = { + .name = "sunxi-musb", + .id = UCLASS_USB, + .probe = musb_usb_probe, + .remove = musb_usb_remove, + .ops = &musb_usb_ops, + .platdata_auto_alloc_size = sizeof(struct usb_platdata), + .priv_auto_alloc_size = sizeof(struct musb_host_data), }; +#endif + +void sunxi_musb_board_init(void) +{ +#ifdef CONFIG_USB_MUSB_HOST + struct udevice *dev; + + /* + * Bind the driver directly for now as musb linux kernel support is + * still pending upstream so our dts files do not have the necessary + * nodes yet. TODO: Remove this as soon as the dts nodes are in place + * and bind by compatible instead. + */ + device_bind_driver(dm_root(), "sunxi-musb", "sunxi-musb", &dev); +#else + musb_register(&musb_plat, NULL, (void *)SUNXI_USB0_BASE); +#endif +}