Home | History | Annotate | Download | only in host
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Sunxi ohci glue
      4  *
      5  * Copyright (C) 2015 Hans de Goede <hdegoede (at) redhat.com>
      6  *
      7  * Based on code from
      8  * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
      9  */
     10 
     11 #include <common.h>
     12 #include <asm/arch/clock.h>
     13 #include <asm/io.h>
     14 #include <dm.h>
     15 #include <usb.h>
     16 #include "ohci.h"
     17 #include <generic-phy.h>
     18 
     19 #ifdef CONFIG_SUNXI_GEN_SUN4I
     20 #define BASE_DIST		0x8000
     21 #define AHB_CLK_DIST		2
     22 #else
     23 #define BASE_DIST		0x1000
     24 #define AHB_CLK_DIST		1
     25 #endif
     26 
     27 #define SUN6I_AHB_RESET0_CFG_OFFSET 0x2c0
     28 #define SUN9I_AHB_RESET0_CFG_OFFSET 0x5a0
     29 
     30 struct ohci_sunxi_cfg {
     31 	bool has_reset;
     32 	u32 extra_ahb_gate_mask;
     33 	u32 extra_usb_gate_mask;
     34 	u32 reset0_cfg_offset;
     35 };
     36 
     37 struct ohci_sunxi_priv {
     38 	ohci_t ohci;
     39 	struct sunxi_ccm_reg *ccm;
     40 	u32 *reset0_cfg;
     41 	int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */
     42 	int usb_gate_mask; /* Mask of usb_clk_cfg clk gate bits for this hcd */
     43 	struct phy phy;
     44 	const struct ohci_sunxi_cfg *cfg;
     45 };
     46 
     47 static fdt_addr_t last_ohci_addr = 0;
     48 
     49 static int ohci_usb_probe(struct udevice *dev)
     50 {
     51 	struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev);
     52 	struct ohci_sunxi_priv *priv = dev_get_priv(dev);
     53 	struct ohci_regs *regs = (struct ohci_regs *)devfdt_get_addr(dev);
     54 	int extra_ahb_gate_mask = 0;
     55 	u8 reg_mask = 0;
     56 	int phys, ret;
     57 
     58 	if ((fdt_addr_t)regs > last_ohci_addr)
     59 		last_ohci_addr = (fdt_addr_t)regs;
     60 
     61 	priv->cfg = (const struct ohci_sunxi_cfg *)dev_get_driver_data(dev);
     62 	priv->ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
     63 	if (IS_ERR(priv->ccm))
     64 		return PTR_ERR(priv->ccm);
     65 
     66 	priv->reset0_cfg = (void *)priv->ccm +
     67 				   priv->cfg->reset0_cfg_offset;
     68 
     69 	phys = dev_count_phandle_with_args(dev, "phys", "#phy-cells");
     70 	if (phys < 0) {
     71 		phys = 0;
     72 		goto no_phy;
     73 	}
     74 
     75 	ret = generic_phy_get_by_name(dev, "usb", &priv->phy);
     76 	if (ret) {
     77 		pr_err("failed to get %s usb PHY\n", dev->name);
     78 		return ret;
     79 	}
     80 
     81 	ret = generic_phy_init(&priv->phy);
     82 	if (ret) {
     83 		pr_err("failed to init %s USB PHY\n", dev->name);
     84 		return ret;
     85 	}
     86 
     87 	ret = generic_phy_power_on(&priv->phy);
     88 	if (ret) {
     89 		pr_err("failed to power on %s USB PHY\n", dev->name);
     90 		return ret;
     91 	}
     92 
     93 no_phy:
     94 	bus_priv->companion = true;
     95 
     96 	/*
     97 	 * This should go away once we've moved to the driver model for
     98 	 * clocks resp. phys.
     99 	 */
    100 	reg_mask = ((uintptr_t)regs - (SUNXI_USB1_BASE + 0x400)) / BASE_DIST;
    101 	priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI0;
    102 	extra_ahb_gate_mask = priv->cfg->extra_ahb_gate_mask;
    103 	priv->usb_gate_mask = CCM_USB_CTRL_OHCI0_CLK;
    104 	priv->ahb_gate_mask <<= reg_mask * AHB_CLK_DIST;
    105 	extra_ahb_gate_mask <<= reg_mask * AHB_CLK_DIST;
    106 	priv->usb_gate_mask <<= reg_mask;
    107 
    108 	setbits_le32(&priv->ccm->ahb_gate0,
    109 		     priv->ahb_gate_mask | extra_ahb_gate_mask);
    110 	setbits_le32(&priv->ccm->usb_clk_cfg,
    111 		     priv->usb_gate_mask | priv->cfg->extra_usb_gate_mask);
    112 	if (priv->cfg->has_reset)
    113 		setbits_le32(priv->reset0_cfg,
    114 			     priv->ahb_gate_mask | extra_ahb_gate_mask);
    115 
    116 	return ohci_register(dev, regs);
    117 }
    118 
    119 static int ohci_usb_remove(struct udevice *dev)
    120 {
    121 	struct ohci_sunxi_priv *priv = dev_get_priv(dev);
    122 	fdt_addr_t base_addr = devfdt_get_addr(dev);
    123 	int ret;
    124 
    125 	if (generic_phy_valid(&priv->phy)) {
    126 		ret = generic_phy_exit(&priv->phy);
    127 		if (ret) {
    128 			pr_err("failed to exit %s USB PHY\n", dev->name);
    129 			return ret;
    130 		}
    131 	}
    132 
    133 	ret = ohci_deregister(dev);
    134 	if (ret)
    135 		return ret;
    136 
    137 	if (priv->cfg->has_reset)
    138 		clrbits_le32(priv->reset0_cfg, priv->ahb_gate_mask);
    139 	/*
    140 	 * On the A64 CLK_USB_OHCI0 is the parent of CLK_USB_OHCI1, so
    141 	 * we have to wait with bringing down any clock until the last
    142 	 * OHCI controller is removed.
    143 	 */
    144 	if (!priv->cfg->extra_usb_gate_mask || base_addr == last_ohci_addr) {
    145 		u32 usb_gate_mask = priv->usb_gate_mask;
    146 
    147 		usb_gate_mask |= priv->cfg->extra_usb_gate_mask;
    148 		clrbits_le32(&priv->ccm->usb_clk_cfg, usb_gate_mask);
    149 	}
    150 
    151 	clrbits_le32(&priv->ccm->ahb_gate0, priv->ahb_gate_mask);
    152 
    153 	return 0;
    154 }
    155 
    156 static const struct ohci_sunxi_cfg sun4i_a10_cfg = {
    157 	.has_reset = false,
    158 };
    159 
    160 static const struct ohci_sunxi_cfg sun6i_a31_cfg = {
    161 	.has_reset = true,
    162 	.reset0_cfg_offset = SUN6I_AHB_RESET0_CFG_OFFSET,
    163 };
    164 
    165 static const struct ohci_sunxi_cfg sun8i_h3_cfg = {
    166 	.has_reset = true,
    167 	.extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0,
    168 	.reset0_cfg_offset = SUN6I_AHB_RESET0_CFG_OFFSET,
    169 };
    170 
    171 static const struct ohci_sunxi_cfg sun9i_a80_cfg = {
    172 	.has_reset = true,
    173 	.reset0_cfg_offset = SUN9I_AHB_RESET0_CFG_OFFSET,
    174 };
    175 
    176 static const struct ohci_sunxi_cfg sun50i_a64_cfg = {
    177 	.has_reset = true,
    178 	.extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0,
    179 	.extra_usb_gate_mask = CCM_USB_CTRL_OHCI0_CLK,
    180 	.reset0_cfg_offset = SUN6I_AHB_RESET0_CFG_OFFSET,
    181 };
    182 
    183 static const struct udevice_id ohci_usb_ids[] = {
    184 	{
    185 		.compatible = "allwinner,sun4i-a10-ohci",
    186 		.data = (ulong)&sun4i_a10_cfg,
    187 	},
    188 	{
    189 		.compatible = "allwinner,sun5i-a13-ohci",
    190 		.data = (ulong)&sun4i_a10_cfg,
    191 	},
    192 	{
    193 		.compatible = "allwinner,sun6i-a31-ohci",
    194 		.data = (ulong)&sun6i_a31_cfg,
    195 	},
    196 	{
    197 		.compatible = "allwinner,sun7i-a20-ohci",
    198 		.data = (ulong)&sun4i_a10_cfg,
    199 	},
    200 	{
    201 		.compatible = "allwinner,sun8i-a23-ohci",
    202 		.data = (ulong)&sun6i_a31_cfg,
    203 	},
    204 	{
    205 		.compatible = "allwinner,sun8i-a83t-ohci",
    206 		.data = (ulong)&sun6i_a31_cfg,
    207 	},
    208 	{
    209 		.compatible = "allwinner,sun8i-h3-ohci",
    210 		.data = (ulong)&sun8i_h3_cfg,
    211 	},
    212 	{
    213 		.compatible = "allwinner,sun9i-a80-ohci",
    214 		.data = (ulong)&sun9i_a80_cfg,
    215 	},
    216 	{
    217 		.compatible = "allwinner,sun50i-a64-ohci",
    218 		.data = (ulong)&sun50i_a64_cfg,
    219 	},
    220 	{ /* sentinel */ }
    221 };
    222 
    223 U_BOOT_DRIVER(usb_ohci) = {
    224 	.name	= "ohci_sunxi",
    225 	.id	= UCLASS_USB,
    226 	.of_match = ohci_usb_ids,
    227 	.probe = ohci_usb_probe,
    228 	.remove = ohci_usb_remove,
    229 	.ops	= &ohci_usb_ops,
    230 	.platdata_auto_alloc_size = sizeof(struct usb_platdata),
    231 	.priv_auto_alloc_size = sizeof(struct ohci_sunxi_priv),
    232 	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
    233 };
    234