uboot/drivers/usb/musb-new/sunxi.c
<<
>>
Prefs
   1/*
   2 * Allwinner SUNXI "glue layer"
   3 *
   4 * Copyright © 2015 Hans de Goede <hdegoede@redhat.com>
   5 * Copyright © 2013 Jussi Kivilinna <jussi.kivilinna@iki.fi>
   6 *
   7 * Based on the sw_usb "Allwinner OTG Dual Role Controller" code.
   8 *  Copyright 2007-2012 (C) Allwinner Technology Co., Ltd.
   9 *  javen <javen@allwinnertech.com>
  10 *
  11 * Based on the DA8xx "glue layer" code.
  12 *  Copyright (c) 2008-2009 MontaVista Software, Inc. <source@mvista.com>
  13 *  Copyright (C) 2005-2006 by Texas Instruments
  14 *
  15 * This file is part of the Inventra Controller Driver for Linux.
  16 *
  17 * SPDX-License-Identifier:     GPL-2.0
  18 */
  19#include <common.h>
  20#include <asm/arch/cpu.h>
  21#include <asm/arch/clock.h>
  22#include <asm/arch/gpio.h>
  23#include <asm/arch/usb_phy.h>
  24#include <asm-generic/gpio.h>
  25#include <dm/lists.h>
  26#include <dm/root.h>
  27#include <linux/usb/musb.h>
  28#include "linux-compat.h"
  29#include "musb_core.h"
  30#include "musb_uboot.h"
  31
  32/******************************************************************************
  33 ******************************************************************************
  34 * From the Allwinner driver
  35 ******************************************************************************
  36 ******************************************************************************/
  37
  38/******************************************************************************
  39 * From include/sunxi_usb_bsp.h
  40 ******************************************************************************/
  41
  42/* reg offsets */
  43#define  USBC_REG_o_ISCR        0x0400
  44#define  USBC_REG_o_PHYCTL      0x0404
  45#define  USBC_REG_o_PHYBIST     0x0408
  46#define  USBC_REG_o_PHYTUNE     0x040c
  47
  48#define  USBC_REG_o_VEND0       0x0043
  49
  50/* Interface Status and Control */
  51#define  USBC_BP_ISCR_VBUS_VALID_FROM_DATA      30
  52#define  USBC_BP_ISCR_VBUS_VALID_FROM_VBUS      29
  53#define  USBC_BP_ISCR_EXT_ID_STATUS             28
  54#define  USBC_BP_ISCR_EXT_DM_STATUS             27
  55#define  USBC_BP_ISCR_EXT_DP_STATUS             26
  56#define  USBC_BP_ISCR_MERGED_VBUS_STATUS        25
  57#define  USBC_BP_ISCR_MERGED_ID_STATUS          24
  58
  59#define  USBC_BP_ISCR_ID_PULLUP_EN              17
  60#define  USBC_BP_ISCR_DPDM_PULLUP_EN            16
  61#define  USBC_BP_ISCR_FORCE_ID                  14
  62#define  USBC_BP_ISCR_FORCE_VBUS_VALID          12
  63#define  USBC_BP_ISCR_VBUS_VALID_SRC            10
  64
  65#define  USBC_BP_ISCR_HOSC_EN                   7
  66#define  USBC_BP_ISCR_VBUS_CHANGE_DETECT        6
  67#define  USBC_BP_ISCR_ID_CHANGE_DETECT          5
  68#define  USBC_BP_ISCR_DPDM_CHANGE_DETECT        4
  69#define  USBC_BP_ISCR_IRQ_ENABLE                3
  70#define  USBC_BP_ISCR_VBUS_CHANGE_DETECT_EN     2
  71#define  USBC_BP_ISCR_ID_CHANGE_DETECT_EN       1
  72#define  USBC_BP_ISCR_DPDM_CHANGE_DETECT_EN     0
  73
  74/******************************************************************************
  75 * From usbc/usbc.c
  76 ******************************************************************************/
  77
  78static u32 USBC_WakeUp_ClearChangeDetect(u32 reg_val)
  79{
  80        u32 temp = reg_val;
  81
  82        temp &= ~(1 << USBC_BP_ISCR_VBUS_CHANGE_DETECT);
  83        temp &= ~(1 << USBC_BP_ISCR_ID_CHANGE_DETECT);
  84        temp &= ~(1 << USBC_BP_ISCR_DPDM_CHANGE_DETECT);
  85
  86        return temp;
  87}
  88
  89static void USBC_EnableIdPullUp(__iomem void *base)
  90{
  91        u32 reg_val;
  92
  93        reg_val = musb_readl(base, USBC_REG_o_ISCR);
  94        reg_val |= (1 << USBC_BP_ISCR_ID_PULLUP_EN);
  95        reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
  96        musb_writel(base, USBC_REG_o_ISCR, reg_val);
  97}
  98
  99static void USBC_EnableDpDmPullUp(__iomem void *base)
 100{
 101        u32 reg_val;
 102
 103        reg_val = musb_readl(base, USBC_REG_o_ISCR);
 104        reg_val |= (1 << USBC_BP_ISCR_DPDM_PULLUP_EN);
 105        reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
 106        musb_writel(base, USBC_REG_o_ISCR, reg_val);
 107}
 108
 109static void USBC_ForceIdToLow(__iomem void *base)
 110{
 111        u32 reg_val;
 112
 113        reg_val = musb_readl(base, USBC_REG_o_ISCR);
 114        reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID);
 115        reg_val |= (0x02 << USBC_BP_ISCR_FORCE_ID);
 116        reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
 117        musb_writel(base, USBC_REG_o_ISCR, reg_val);
 118}
 119
 120static void USBC_ForceIdToHigh(__iomem void *base)
 121{
 122        u32 reg_val;
 123
 124        reg_val = musb_readl(base, USBC_REG_o_ISCR);
 125        reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_ID);
 126        reg_val |= (0x03 << USBC_BP_ISCR_FORCE_ID);
 127        reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
 128        musb_writel(base, USBC_REG_o_ISCR, reg_val);
 129}
 130
 131static void USBC_ForceVbusValidToLow(__iomem void *base)
 132{
 133        u32 reg_val;
 134
 135        reg_val = musb_readl(base, USBC_REG_o_ISCR);
 136        reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID);
 137        reg_val |= (0x02 << USBC_BP_ISCR_FORCE_VBUS_VALID);
 138        reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
 139        musb_writel(base, USBC_REG_o_ISCR, reg_val);
 140}
 141
 142static void USBC_ForceVbusValidToHigh(__iomem void *base)
 143{
 144        u32 reg_val;
 145
 146        reg_val = musb_readl(base, USBC_REG_o_ISCR);
 147        reg_val &= ~(0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID);
 148        reg_val |= (0x03 << USBC_BP_ISCR_FORCE_VBUS_VALID);
 149        reg_val = USBC_WakeUp_ClearChangeDetect(reg_val);
 150        musb_writel(base, USBC_REG_o_ISCR, reg_val);
 151}
 152
 153static void USBC_ConfigFIFO_Base(void)
 154{
 155        u32 reg_value;
 156
 157        /* config usb fifo, 8kb mode */
 158        reg_value = readl(SUNXI_SRAMC_BASE + 0x04);
 159        reg_value &= ~(0x03 << 0);
 160        reg_value |= (1 << 0);
 161        writel(reg_value, SUNXI_SRAMC_BASE + 0x04);
 162}
 163
 164/******************************************************************************
 165 * Needed for the DFU polling magic
 166 ******************************************************************************/
 167
 168static u8 last_int_usb;
 169
 170bool dfu_usb_get_reset(void)
 171{
 172        return !!(last_int_usb & MUSB_INTR_RESET);
 173}
 174
 175/******************************************************************************
 176 * MUSB Glue code
 177 ******************************************************************************/
 178
 179static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci)
 180{
 181        struct musb             *musb = __hci;
 182        irqreturn_t             retval = IRQ_NONE;
 183
 184        /* read and flush interrupts */
 185        musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
 186        last_int_usb = musb->int_usb;
 187        if (musb->int_usb)
 188                musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
 189        musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
 190        if (musb->int_tx)
 191                musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
 192        musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
 193        if (musb->int_rx)
 194                musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
 195
 196        if (musb->int_usb || musb->int_tx || musb->int_rx)
 197                retval |= musb_interrupt(musb);
 198
 199        return retval;
 200}
 201
 202/* musb_core does not call enable / disable in a balanced manner <sigh> */
 203static bool enabled = false;
 204
 205static int sunxi_musb_enable(struct musb *musb)
 206{
 207        int ret;
 208
 209        pr_debug("%s():\n", __func__);
 210
 211        musb_ep_select(musb->mregs, 0);
 212        musb_writeb(musb->mregs, MUSB_FADDR, 0);
 213
 214        if (enabled)
 215                return 0;
 216
 217        /* select PIO mode */
 218        musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0);
 219
 220        if (is_host_enabled(musb)) {
 221                ret = sunxi_usb_phy_vbus_detect(0);
 222                if (ret == 1) {
 223                        printf("A charger is plugged into the OTG: ");
 224                        return -ENODEV;
 225                }
 226                ret = sunxi_usb_phy_id_detect(0);
 227                if (ret == 1) {
 228                        printf("No host cable detected: ");
 229                        return -ENODEV;
 230                }
 231                sunxi_usb_phy_power_on(0); /* port power on */
 232        }
 233
 234        USBC_ForceVbusValidToHigh(musb->mregs);
 235
 236        enabled = true;
 237        return 0;
 238}
 239
 240static void sunxi_musb_disable(struct musb *musb)
 241{
 242        pr_debug("%s():\n", __func__);
 243
 244        if (!enabled)
 245                return;
 246
 247        if (is_host_enabled(musb))
 248                sunxi_usb_phy_power_off(0); /* port power off */
 249
 250        USBC_ForceVbusValidToLow(musb->mregs);
 251        mdelay(200); /* Wait for the current session to timeout */
 252
 253        enabled = false;
 254}
 255
 256static int sunxi_musb_init(struct musb *musb)
 257{
 258        struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
 259
 260        pr_debug("%s():\n", __func__);
 261
 262        musb->isr = sunxi_musb_interrupt;
 263
 264        setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0);
 265#ifdef CONFIG_SUNXI_GEN_SUN6I
 266        setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0);
 267#endif
 268        sunxi_usb_phy_init(0);
 269
 270        USBC_ConfigFIFO_Base();
 271        USBC_EnableDpDmPullUp(musb->mregs);
 272        USBC_EnableIdPullUp(musb->mregs);
 273
 274        if (is_host_enabled(musb)) {
 275                /* Host mode */
 276                USBC_ForceIdToLow(musb->mregs);
 277        } else {
 278                /* Peripheral mode */
 279                USBC_ForceIdToHigh(musb->mregs);
 280        }
 281        USBC_ForceVbusValidToHigh(musb->mregs);
 282
 283        return 0;
 284}
 285
 286static const struct musb_platform_ops sunxi_musb_ops = {
 287        .init           = sunxi_musb_init,
 288        .enable         = sunxi_musb_enable,
 289        .disable        = sunxi_musb_disable,
 290};
 291
 292static struct musb_hdrc_config musb_config = {
 293        .multipoint     = 1,
 294        .dyn_fifo       = 1,
 295        .num_eps        = 6,
 296        .ram_bits       = 11,
 297};
 298
 299static struct musb_hdrc_platform_data musb_plat = {
 300#if defined(CONFIG_USB_MUSB_HOST)
 301        .mode           = MUSB_HOST,
 302#else
 303        .mode           = MUSB_PERIPHERAL,
 304#endif
 305        .config         = &musb_config,
 306        .power          = 250,
 307        .platform_ops   = &sunxi_musb_ops,
 308};
 309
 310#ifdef CONFIG_USB_MUSB_HOST
 311static int musb_usb_remove(struct udevice *dev);
 312
 313static int musb_usb_probe(struct udevice *dev)
 314{
 315        struct musb_host_data *host = dev_get_priv(dev);
 316        struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
 317        int ret;
 318
 319        priv->desc_before_addr = true;
 320
 321        host->host = musb_init_controller(&musb_plat, NULL,
 322                                          (void *)SUNXI_USB0_BASE);
 323        if (!host->host)
 324                return -EIO;
 325
 326        ret = musb_lowlevel_init(host);
 327        if (ret == 0)
 328                printf("MUSB OTG\n");
 329        else
 330                musb_usb_remove(dev);
 331
 332        return ret;
 333}
 334
 335static int musb_usb_remove(struct udevice *dev)
 336{
 337        struct musb_host_data *host = dev_get_priv(dev);
 338        struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
 339
 340        musb_stop(host->host);
 341
 342        sunxi_usb_phy_exit(0);
 343#ifdef CONFIG_SUNXI_GEN_SUN6I
 344        clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0);
 345#endif
 346        clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0);
 347
 348        free(host->host);
 349        host->host = NULL;
 350
 351        return 0;
 352}
 353
 354U_BOOT_DRIVER(usb_musb) = {
 355        .name   = "sunxi-musb",
 356        .id     = UCLASS_USB,
 357        .probe = musb_usb_probe,
 358        .remove = musb_usb_remove,
 359        .ops    = &musb_usb_ops,
 360        .platdata_auto_alloc_size = sizeof(struct usb_platdata),
 361        .priv_auto_alloc_size = sizeof(struct musb_host_data),
 362};
 363#endif
 364
 365void sunxi_musb_board_init(void)
 366{
 367#ifdef CONFIG_USB_MUSB_HOST
 368        struct udevice *dev;
 369
 370        /*
 371         * Bind the driver directly for now as musb linux kernel support is
 372         * still pending upstream so our dts files do not have the necessary
 373         * nodes yet. TODO: Remove this as soon as the dts nodes are in place
 374         * and bind by compatible instead.
 375         */
 376        device_bind_driver(dm_root(), "sunxi-musb", "sunxi-musb", &dev);
 377#else
 378        musb_register(&musb_plat, NULL, (void *)SUNXI_USB0_BASE);
 379#endif
 380}
 381