uboot/drivers/phy/bcm6368-usbh-phy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
   4 *
   5 * Derived from linux/arch/mips/bcm63xx/usb-common.c:
   6 *      Copyright 2008 Maxime Bizon <mbizon@freebox.fr>
   7 *      Copyright 2013 Florian Fainelli <florian@openwrt.org>
   8 */
   9
  10#include <common.h>
  11#include <clk.h>
  12#include <dm.h>
  13#include <generic-phy.h>
  14#include <log.h>
  15#include <malloc.h>
  16#include <power-domain.h>
  17#include <reset.h>
  18#include <asm/io.h>
  19#include <dm/device.h>
  20#include <linux/bitops.h>
  21#include <linux/delay.h>
  22
  23/* USBH PLL Control register */
  24#define USBH_PLL_REG            0x18
  25#define USBH_PLL_IDDQ_PWRDN     BIT(9)
  26#define USBH_PLL_PWRDN_DELAY    BIT(10)
  27
  28/* USBH Swap Control register */
  29#define USBH_SWAP_REG           0x1c
  30#define USBH_SWAP_OHCI_DATA     BIT(0)
  31#define USBH_SWAP_OHCI_ENDIAN   BIT(1)
  32#define USBH_SWAP_EHCI_DATA     BIT(3)
  33#define USBH_SWAP_EHCI_ENDIAN   BIT(4)
  34
  35/* USBH Setup register */
  36#define USBH_SETUP_REG          0x28
  37#define USBH_SETUP_IOC          BIT(4)
  38#define USBH_SETUP_IPP          BIT(5)
  39
  40struct bcm6368_usbh_hw {
  41        uint32_t setup_clr;
  42        uint32_t pll_clr;
  43};
  44
  45struct bcm6368_usbh_priv {
  46        const struct bcm6368_usbh_hw *hw;
  47        void __iomem *regs;
  48};
  49
  50static int bcm6368_usbh_init(struct phy *phy)
  51{
  52        struct bcm6368_usbh_priv *priv = dev_get_priv(phy->dev);
  53        const struct bcm6368_usbh_hw *hw = priv->hw;
  54
  55        /* configure to work in native cpu endian */
  56        clrsetbits_be32(priv->regs + USBH_SWAP_REG,
  57                        USBH_SWAP_EHCI_ENDIAN | USBH_SWAP_OHCI_ENDIAN,
  58                        USBH_SWAP_EHCI_DATA | USBH_SWAP_OHCI_DATA);
  59
  60        /* setup config */
  61        if (hw->setup_clr)
  62                clrbits_be32(priv->regs + USBH_SETUP_REG, hw->setup_clr);
  63
  64        setbits_be32(priv->regs + USBH_SETUP_REG, USBH_SETUP_IOC);
  65
  66        /* enable pll control */
  67        if (hw->pll_clr)
  68                clrbits_be32(priv->regs + USBH_PLL_REG, hw->pll_clr);
  69
  70        return 0;
  71}
  72
  73static struct phy_ops bcm6368_usbh_ops = {
  74        .init = bcm6368_usbh_init,
  75};
  76
  77static const struct bcm6368_usbh_hw bcm6328_hw = {
  78        .pll_clr = USBH_PLL_IDDQ_PWRDN | USBH_PLL_PWRDN_DELAY,
  79        .setup_clr = 0,
  80};
  81
  82static const struct bcm6368_usbh_hw bcm6362_hw = {
  83        .pll_clr = 0,
  84        .setup_clr = 0,
  85};
  86
  87static const struct bcm6368_usbh_hw bcm6368_hw = {
  88        .pll_clr = 0,
  89        .setup_clr = 0,
  90};
  91
  92static const struct bcm6368_usbh_hw bcm63268_hw = {
  93        .pll_clr = USBH_PLL_IDDQ_PWRDN | USBH_PLL_PWRDN_DELAY,
  94        .setup_clr = USBH_SETUP_IPP,
  95};
  96
  97static const struct udevice_id bcm6368_usbh_ids[] = {
  98        {
  99                .compatible = "brcm,bcm6328-usbh",
 100                .data = (ulong)&bcm6328_hw,
 101        }, {
 102                .compatible = "brcm,bcm6362-usbh",
 103                .data = (ulong)&bcm6362_hw,
 104        }, {
 105                .compatible = "brcm,bcm6368-usbh",
 106                .data = (ulong)&bcm6368_hw,
 107        }, {
 108                .compatible = "brcm,bcm63268-usbh",
 109                .data = (ulong)&bcm63268_hw,
 110        }, { /* sentinel */ }
 111};
 112
 113static int bcm6368_usbh_probe(struct udevice *dev)
 114{
 115        struct bcm6368_usbh_priv *priv = dev_get_priv(dev);
 116        const struct bcm6368_usbh_hw *hw =
 117                (const struct bcm6368_usbh_hw *)dev_get_driver_data(dev);
 118#if defined(CONFIG_POWER_DOMAIN)
 119        struct power_domain pwr_dom;
 120#endif
 121        struct reset_ctl rst_ctl;
 122        struct clk clk;
 123        int ret;
 124
 125        priv->regs = dev_remap_addr(dev);
 126        if (!priv->regs)
 127                return -EINVAL;
 128
 129        priv->hw = hw;
 130
 131        /* enable usbh clock */
 132        ret = clk_get_by_name(dev, "usbh", &clk);
 133        if (ret < 0)
 134                return ret;
 135
 136        ret = clk_enable(&clk);
 137        if (ret < 0)
 138                return ret;
 139
 140        ret = clk_free(&clk);
 141        if (ret < 0)
 142                return ret;
 143
 144#if defined(CONFIG_POWER_DOMAIN)
 145        /* enable power domain */
 146        ret = power_domain_get(dev, &pwr_dom);
 147        if (ret < 0)
 148                return ret;
 149
 150        ret = power_domain_on(&pwr_dom);
 151        if (ret < 0)
 152                return ret;
 153
 154        ret = power_domain_free(&pwr_dom);
 155        if (ret < 0)
 156                return ret;
 157#endif
 158
 159        /* perform reset */
 160        ret = reset_get_by_index(dev, 0, &rst_ctl);
 161        if (ret < 0)
 162                return ret;
 163
 164        ret = reset_deassert(&rst_ctl);
 165        if (ret < 0)
 166                return ret;
 167
 168        ret = reset_free(&rst_ctl);
 169        if (ret < 0)
 170                return ret;
 171
 172        /* enable usb_ref clock */
 173        ret = clk_get_by_name(dev, "usb_ref", &clk);
 174        if (!ret) {
 175                ret = clk_enable(&clk);
 176                if (ret < 0)
 177                        return ret;
 178
 179                ret = clk_free(&clk);
 180                if (ret < 0)
 181                        return ret;
 182        }
 183
 184        mdelay(100);
 185
 186        return 0;
 187}
 188
 189U_BOOT_DRIVER(bcm6368_usbh) = {
 190        .name = "bcm6368-usbh",
 191        .id = UCLASS_PHY,
 192        .of_match = bcm6368_usbh_ids,
 193        .ops = &bcm6368_usbh_ops,
 194        .priv_auto      = sizeof(struct bcm6368_usbh_priv),
 195        .probe = bcm6368_usbh_probe,
 196};
 197