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;
 204static struct musb *sunxi_musb;
 205
 206static int sunxi_musb_enable(struct musb *musb)
 207{
 208        pr_debug("%s():\n", __func__);
 209
 210        musb_ep_select(musb->mregs, 0);
 211        musb_writeb(musb->mregs, MUSB_FADDR, 0);
 212
 213        if (enabled)
 214                return 0;
 215
 216        /* select PIO mode */
 217        musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0);
 218
 219        if (is_host_enabled(musb)) {
 220                int id = sunxi_usb_phy_id_detect(0);
 221
 222                if (id == 1 && sunxi_usb_phy_power_is_on(0))
 223                        sunxi_usb_phy_power_off(0);
 224
 225                if (!sunxi_usb_phy_power_is_on(0)) {
 226                        int vbus = sunxi_usb_phy_vbus_detect(0);
 227                        if (vbus == 1) {
 228                                printf("A charger is plugged into the OTG: ");
 229                                return -ENODEV;
 230                        }
 231                }
 232
 233                if (id == 1) {
 234                        printf("No host cable detected: ");
 235                        return -ENODEV;
 236                }
 237
 238                if (!sunxi_usb_phy_power_is_on(0))
 239                        sunxi_usb_phy_power_on(0);
 240        }
 241
 242        USBC_ForceVbusValidToHigh(musb->mregs);
 243
 244        enabled = true;
 245        return 0;
 246}
 247
 248static void sunxi_musb_disable(struct musb *musb)
 249{
 250        pr_debug("%s():\n", __func__);
 251
 252        if (!enabled)
 253                return;
 254
 255        USBC_ForceVbusValidToLow(musb->mregs);
 256        mdelay(200); /* Wait for the current session to timeout */
 257
 258        enabled = false;
 259}
 260
 261static int sunxi_musb_init(struct musb *musb)
 262{
 263        struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
 264
 265        pr_debug("%s():\n", __func__);
 266
 267        musb->isr = sunxi_musb_interrupt;
 268
 269        setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_USB0);
 270#ifdef CONFIG_SUNXI_GEN_SUN6I
 271        setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_GATE_OFFSET_USB0);
 272#endif
 273        sunxi_usb_phy_init(0);
 274
 275        USBC_ConfigFIFO_Base();
 276        USBC_EnableDpDmPullUp(musb->mregs);
 277        USBC_EnableIdPullUp(musb->mregs);
 278
 279        if (is_host_enabled(musb)) {
 280                /* Host mode */
 281                USBC_ForceIdToLow(musb->mregs);
 282        } else {
 283                /* Peripheral mode */
 284                USBC_ForceIdToHigh(musb->mregs);
 285        }
 286        USBC_ForceVbusValidToHigh(musb->mregs);
 287
 288        return 0;
 289}
 290
 291static const struct musb_platform_ops sunxi_musb_ops = {
 292        .init           = sunxi_musb_init,
 293        .enable         = sunxi_musb_enable,
 294        .disable        = sunxi_musb_disable,
 295};
 296
 297static struct musb_hdrc_config musb_config = {
 298        .multipoint     = 1,
 299        .dyn_fifo       = 1,
 300        .num_eps        = 6,
 301        .ram_bits       = 11,
 302};
 303
 304static struct musb_hdrc_platform_data musb_plat = {
 305#if defined(CONFIG_USB_MUSB_HOST)
 306        .mode           = MUSB_HOST,
 307#else
 308        .mode           = MUSB_PERIPHERAL,
 309#endif
 310        .config         = &musb_config,
 311        .power          = 250,
 312        .platform_ops   = &sunxi_musb_ops,
 313};
 314
 315#ifdef CONFIG_USB_MUSB_HOST
 316int musb_usb_probe(struct udevice *dev)
 317{
 318        struct musb_host_data *host = dev_get_priv(dev);
 319        struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
 320        int ret;
 321
 322        priv->desc_before_addr = true;
 323
 324        if (!sunxi_musb) {
 325                sunxi_musb = musb_init_controller(&musb_plat, NULL,
 326                                                  (void *)SUNXI_USB0_BASE);
 327        }
 328
 329        host->host = sunxi_musb;
 330        if (!host->host)
 331                return -EIO;
 332
 333        ret = musb_lowlevel_init(host);
 334        if (ret == 0)
 335                printf("MUSB OTG\n");
 336
 337        return ret;
 338}
 339
 340int musb_usb_remove(struct udevice *dev)
 341{
 342        struct musb_host_data *host = dev_get_priv(dev);
 343
 344        musb_stop(host->host);
 345
 346        return 0;
 347}
 348
 349U_BOOT_DRIVER(usb_musb) = {
 350        .name   = "sunxi-musb",
 351        .id     = UCLASS_USB,
 352        .probe = musb_usb_probe,
 353        .remove = musb_usb_remove,
 354        .ops    = &musb_usb_ops,
 355        .platdata_auto_alloc_size = sizeof(struct usb_platdata),
 356        .priv_auto_alloc_size = sizeof(struct musb_host_data),
 357};
 358#endif
 359
 360void sunxi_musb_board_init(void)
 361{
 362#ifdef CONFIG_USB_MUSB_HOST
 363        struct udevice *dev;
 364
 365        /*
 366         * Bind the driver directly for now as musb linux kernel support is
 367         * still pending upstream so our dts files do not have the necessary
 368         * nodes yet. TODO: Remove this as soon as the dts nodes are in place
 369         * and bind by compatible instead.
 370         */
 371        device_bind_driver(dm_root(), "sunxi-musb", "sunxi-musb", &dev);
 372#else
 373        musb_register(&musb_plat, NULL, (void *)SUNXI_USB0_BASE);
 374#endif
 375}
 376