162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Broadcom specific Advanced Microcontroller Bus
462306a36Sopenharmony_ci * Broadcom USB-core driver (BCMA bus glue)
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright 2011-2015 Hauke Mehrtens <hauke@hauke-m.de>
762306a36Sopenharmony_ci * Copyright 2015 Felix Fietkau <nbd@openwrt.org>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Based on ssb-ohci driver
1062306a36Sopenharmony_ci * Copyright 2007 Michael Buesch <m@bues.ch>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Derived from the OHCI-PCI driver
1362306a36Sopenharmony_ci * Copyright 1999 Roman Weissgaerber
1462306a36Sopenharmony_ci * Copyright 2000-2002 David Brownell
1562306a36Sopenharmony_ci * Copyright 1999 Linus Torvalds
1662306a36Sopenharmony_ci * Copyright 1999 Gregory P. Smith
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Derived from the USBcore related parts of Broadcom-SB
1962306a36Sopenharmony_ci * Copyright 2005-2011 Broadcom Corporation
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci#include <linux/bcma/bcma.h>
2262306a36Sopenharmony_ci#include <linux/delay.h>
2362306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2462306a36Sopenharmony_ci#include <linux/platform_device.h>
2562306a36Sopenharmony_ci#include <linux/module.h>
2662306a36Sopenharmony_ci#include <linux/slab.h>
2762306a36Sopenharmony_ci#include <linux/of.h>
2862306a36Sopenharmony_ci#include <linux/of_gpio.h>
2962306a36Sopenharmony_ci#include <linux/of_platform.h>
3062306a36Sopenharmony_ci#include <linux/usb/ehci_pdriver.h>
3162306a36Sopenharmony_ci#include <linux/usb/ohci_pdriver.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciMODULE_AUTHOR("Hauke Mehrtens");
3462306a36Sopenharmony_ciMODULE_DESCRIPTION("Common USB driver for BCMA Bus");
3562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* See BCMA_CLKCTLST_EXTRESREQ and BCMA_CLKCTLST_EXTRESST */
3862306a36Sopenharmony_ci#define USB_BCMA_CLKCTLST_USB_CLK_REQ			0x00000100
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct bcma_hcd_device {
4162306a36Sopenharmony_ci	struct bcma_device *core;
4262306a36Sopenharmony_ci	struct platform_device *ehci_dev;
4362306a36Sopenharmony_ci	struct platform_device *ohci_dev;
4462306a36Sopenharmony_ci	struct gpio_desc *gpio_desc;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Wait for bitmask in a register to get set or cleared.
4862306a36Sopenharmony_ci * timeout is in units of ten-microseconds.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cistatic int bcma_wait_bits(struct bcma_device *dev, u16 reg, u32 bitmask,
5162306a36Sopenharmony_ci			  int timeout)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	int i;
5462306a36Sopenharmony_ci	u32 val;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	for (i = 0; i < timeout; i++) {
5762306a36Sopenharmony_ci		val = bcma_read32(dev, reg);
5862306a36Sopenharmony_ci		if ((val & bitmask) == bitmask)
5962306a36Sopenharmony_ci			return 0;
6062306a36Sopenharmony_ci		udelay(10);
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return -ETIMEDOUT;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void bcma_hcd_4716wa(struct bcma_device *dev)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci#ifdef CONFIG_BCMA_DRIVER_MIPS
6962306a36Sopenharmony_ci	/* Work around for 4716 failures. */
7062306a36Sopenharmony_ci	if (dev->bus->chipinfo.id == 0x4716) {
7162306a36Sopenharmony_ci		u32 tmp;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		tmp = bcma_cpu_clock(&dev->bus->drv_mips);
7462306a36Sopenharmony_ci		if (tmp >= 480000000)
7562306a36Sopenharmony_ci			tmp = 0x1846b; /* set CDR to 0x11(fast) */
7662306a36Sopenharmony_ci		else if (tmp == 453000000)
7762306a36Sopenharmony_ci			tmp = 0x1046b; /* set CDR to 0x10(slow) */
7862306a36Sopenharmony_ci		else
7962306a36Sopenharmony_ci			tmp = 0;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci		/* Change Shim mdio control reg to fix host not acking at
8262306a36Sopenharmony_ci		 * high frequencies
8362306a36Sopenharmony_ci		 */
8462306a36Sopenharmony_ci		if (tmp) {
8562306a36Sopenharmony_ci			bcma_write32(dev, 0x524, 0x1); /* write sel to enable */
8662306a36Sopenharmony_ci			udelay(500);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci			bcma_write32(dev, 0x524, tmp);
8962306a36Sopenharmony_ci			udelay(500);
9062306a36Sopenharmony_ci			bcma_write32(dev, 0x524, 0x4ab);
9162306a36Sopenharmony_ci			udelay(500);
9262306a36Sopenharmony_ci			bcma_read32(dev, 0x528);
9362306a36Sopenharmony_ci			bcma_write32(dev, 0x528, 0x80000000);
9462306a36Sopenharmony_ci		}
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci#endif /* CONFIG_BCMA_DRIVER_MIPS */
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */
10062306a36Sopenharmony_cistatic void bcma_hcd_init_chip_mips(struct bcma_device *dev)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	u32 tmp;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/*
10562306a36Sopenharmony_ci	 * USB 2.0 special considerations:
10662306a36Sopenharmony_ci	 *
10762306a36Sopenharmony_ci	 * 1. Since the core supports both OHCI and EHCI functions, it must
10862306a36Sopenharmony_ci	 *    only be reset once.
10962306a36Sopenharmony_ci	 *
11062306a36Sopenharmony_ci	 * 2. In addition to the standard SI reset sequence, the Host Control
11162306a36Sopenharmony_ci	 *    Register must be programmed to bring the USB core and various
11262306a36Sopenharmony_ci	 *    phy components out of reset.
11362306a36Sopenharmony_ci	 */
11462306a36Sopenharmony_ci	if (!bcma_core_is_enabled(dev)) {
11562306a36Sopenharmony_ci		bcma_core_enable(dev, 0);
11662306a36Sopenharmony_ci		mdelay(10);
11762306a36Sopenharmony_ci		if (dev->id.rev >= 5) {
11862306a36Sopenharmony_ci			/* Enable Misc PLL */
11962306a36Sopenharmony_ci			tmp = bcma_read32(dev, 0x1e0);
12062306a36Sopenharmony_ci			tmp |= 0x100;
12162306a36Sopenharmony_ci			bcma_write32(dev, 0x1e0, tmp);
12262306a36Sopenharmony_ci			if (bcma_wait_bits(dev, 0x1e0, 1 << 24, 100))
12362306a36Sopenharmony_ci				printk(KERN_EMERG "Failed to enable misc PPL!\n");
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci			/* Take out of resets */
12662306a36Sopenharmony_ci			bcma_write32(dev, 0x200, 0x4ff);
12762306a36Sopenharmony_ci			udelay(25);
12862306a36Sopenharmony_ci			bcma_write32(dev, 0x200, 0x6ff);
12962306a36Sopenharmony_ci			udelay(25);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci			/* Make sure digital and AFE are locked in USB PHY */
13262306a36Sopenharmony_ci			bcma_write32(dev, 0x524, 0x6b);
13362306a36Sopenharmony_ci			udelay(50);
13462306a36Sopenharmony_ci			tmp = bcma_read32(dev, 0x524);
13562306a36Sopenharmony_ci			udelay(50);
13662306a36Sopenharmony_ci			bcma_write32(dev, 0x524, 0xab);
13762306a36Sopenharmony_ci			udelay(50);
13862306a36Sopenharmony_ci			tmp = bcma_read32(dev, 0x524);
13962306a36Sopenharmony_ci			udelay(50);
14062306a36Sopenharmony_ci			bcma_write32(dev, 0x524, 0x2b);
14162306a36Sopenharmony_ci			udelay(50);
14262306a36Sopenharmony_ci			tmp = bcma_read32(dev, 0x524);
14362306a36Sopenharmony_ci			udelay(50);
14462306a36Sopenharmony_ci			bcma_write32(dev, 0x524, 0x10ab);
14562306a36Sopenharmony_ci			udelay(50);
14662306a36Sopenharmony_ci			tmp = bcma_read32(dev, 0x524);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci			if (bcma_wait_bits(dev, 0x528, 0xc000, 10000)) {
14962306a36Sopenharmony_ci				tmp = bcma_read32(dev, 0x528);
15062306a36Sopenharmony_ci				printk(KERN_EMERG
15162306a36Sopenharmony_ci				       "USB20H mdio_rddata 0x%08x\n", tmp);
15262306a36Sopenharmony_ci			}
15362306a36Sopenharmony_ci			bcma_write32(dev, 0x528, 0x80000000);
15462306a36Sopenharmony_ci			tmp = bcma_read32(dev, 0x314);
15562306a36Sopenharmony_ci			udelay(265);
15662306a36Sopenharmony_ci			bcma_write32(dev, 0x200, 0x7ff);
15762306a36Sopenharmony_ci			udelay(10);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci			/* Take USB and HSIC out of non-driving modes */
16062306a36Sopenharmony_ci			bcma_write32(dev, 0x510, 0);
16162306a36Sopenharmony_ci		} else {
16262306a36Sopenharmony_ci			bcma_write32(dev, 0x200, 0x7ff);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci			udelay(1);
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		bcma_hcd_4716wa(dev);
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/*
17262306a36Sopenharmony_ci * bcma_hcd_usb20_old_arm_init - Initialize old USB 2.0 controller on ARM
17362306a36Sopenharmony_ci *
17462306a36Sopenharmony_ci * Old USB 2.0 core is identified as BCMA_CORE_USB20_HOST and was introduced
17562306a36Sopenharmony_ci * long before Northstar devices. It seems some cheaper chipsets like BCM53573
17662306a36Sopenharmony_ci * still use it.
17762306a36Sopenharmony_ci * Initialization of this old core differs between MIPS and ARM.
17862306a36Sopenharmony_ci */
17962306a36Sopenharmony_cistatic int bcma_hcd_usb20_old_arm_init(struct bcma_hcd_device *usb_dev)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct bcma_device *core = usb_dev->core;
18262306a36Sopenharmony_ci	struct device *dev = &core->dev;
18362306a36Sopenharmony_ci	struct bcma_device *pmu_core;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	usleep_range(10000, 20000);
18662306a36Sopenharmony_ci	if (core->id.rev < 5)
18762306a36Sopenharmony_ci		return 0;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	pmu_core = bcma_find_core(core->bus, BCMA_CORE_PMU);
19062306a36Sopenharmony_ci	if (!pmu_core) {
19162306a36Sopenharmony_ci		dev_err(dev, "Could not find PMU core\n");
19262306a36Sopenharmony_ci		return -ENOENT;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* Take USB core out of reset */
19662306a36Sopenharmony_ci	bcma_awrite32(core, BCMA_IOCTL, BCMA_IOCTL_CLK | BCMA_IOCTL_FGC);
19762306a36Sopenharmony_ci	usleep_range(100, 200);
19862306a36Sopenharmony_ci	bcma_awrite32(core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
19962306a36Sopenharmony_ci	usleep_range(100, 200);
20062306a36Sopenharmony_ci	bcma_awrite32(core, BCMA_RESET_CTL, 0);
20162306a36Sopenharmony_ci	usleep_range(100, 200);
20262306a36Sopenharmony_ci	bcma_awrite32(core, BCMA_IOCTL, BCMA_IOCTL_CLK);
20362306a36Sopenharmony_ci	usleep_range(100, 200);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Enable Misc PLL */
20662306a36Sopenharmony_ci	bcma_write32(core, BCMA_CLKCTLST, BCMA_CLKCTLST_FORCEHT |
20762306a36Sopenharmony_ci					  BCMA_CLKCTLST_HQCLKREQ |
20862306a36Sopenharmony_ci					  USB_BCMA_CLKCTLST_USB_CLK_REQ);
20962306a36Sopenharmony_ci	usleep_range(100, 200);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	bcma_write32(core, 0x510, 0xc7f85000);
21262306a36Sopenharmony_ci	bcma_write32(core, 0x510, 0xc7f85003);
21362306a36Sopenharmony_ci	usleep_range(300, 600);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Program USB PHY PLL parameters */
21662306a36Sopenharmony_ci	bcma_write32(pmu_core, BCMA_CC_PMU_PLLCTL_ADDR, 0x6);
21762306a36Sopenharmony_ci	bcma_write32(pmu_core, BCMA_CC_PMU_PLLCTL_DATA, 0x005360c1);
21862306a36Sopenharmony_ci	usleep_range(100, 200);
21962306a36Sopenharmony_ci	bcma_write32(pmu_core, BCMA_CC_PMU_PLLCTL_ADDR, 0x7);
22062306a36Sopenharmony_ci	bcma_write32(pmu_core, BCMA_CC_PMU_PLLCTL_DATA, 0x0);
22162306a36Sopenharmony_ci	usleep_range(100, 200);
22262306a36Sopenharmony_ci	bcma_set32(pmu_core, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
22362306a36Sopenharmony_ci	usleep_range(100, 200);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	bcma_write32(core, 0x510, 0x7f8d007);
22662306a36Sopenharmony_ci	udelay(1000);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* Take controller out of reset */
22962306a36Sopenharmony_ci	bcma_write32(core, 0x200, 0x4ff);
23062306a36Sopenharmony_ci	usleep_range(25, 50);
23162306a36Sopenharmony_ci	bcma_write32(core, 0x200, 0x6ff);
23262306a36Sopenharmony_ci	usleep_range(25, 50);
23362306a36Sopenharmony_ci	bcma_write32(core, 0x200, 0x7ff);
23462306a36Sopenharmony_ci	usleep_range(25, 50);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	of_platform_default_populate(dev->of_node, NULL, dev);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return 0;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic void bcma_hcd_usb20_ns_init_hc(struct bcma_device *dev)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	u32 val;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* Set packet buffer OUT threshold */
24662306a36Sopenharmony_ci	val = bcma_read32(dev, 0x94);
24762306a36Sopenharmony_ci	val &= 0xffff;
24862306a36Sopenharmony_ci	val |= 0x80 << 16;
24962306a36Sopenharmony_ci	bcma_write32(dev, 0x94, val);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* Enable break memory transfer */
25262306a36Sopenharmony_ci	val = bcma_read32(dev, 0x9c);
25362306a36Sopenharmony_ci	val |= 1;
25462306a36Sopenharmony_ci	bcma_write32(dev, 0x9c, val);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/*
25762306a36Sopenharmony_ci	 * Broadcom initializes PHY and then waits to ensure HC is ready to be
25862306a36Sopenharmony_ci	 * configured. In our case the order is reversed. We just initialized
25962306a36Sopenharmony_ci	 * controller and we let HCD initialize PHY, so let's wait (sleep) now.
26062306a36Sopenharmony_ci	 */
26162306a36Sopenharmony_ci	usleep_range(1000, 2000);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci/*
26562306a36Sopenharmony_ci * bcma_hcd_usb20_ns_init - Initialize Northstar USB 2.0 controller
26662306a36Sopenharmony_ci */
26762306a36Sopenharmony_cistatic int bcma_hcd_usb20_ns_init(struct bcma_hcd_device *bcma_hcd)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct bcma_device *core = bcma_hcd->core;
27062306a36Sopenharmony_ci	struct bcma_chipinfo *ci = &core->bus->chipinfo;
27162306a36Sopenharmony_ci	struct device *dev = &core->dev;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	bcma_core_enable(core, 0);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (ci->id == BCMA_CHIP_ID_BCM4707 ||
27662306a36Sopenharmony_ci	    ci->id == BCMA_CHIP_ID_BCM53018)
27762306a36Sopenharmony_ci		bcma_hcd_usb20_ns_init_hc(core);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	of_platform_default_populate(dev->of_node, NULL, dev);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	return 0;
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic void bcma_hci_platform_power_gpio(struct bcma_device *dev, bool val)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct bcma_hcd_device *usb_dev = bcma_get_drvdata(dev);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (!usb_dev->gpio_desc)
28962306a36Sopenharmony_ci		return;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	gpiod_set_value(usb_dev->gpio_desc, val);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic const struct usb_ehci_pdata ehci_pdata = {
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic const struct usb_ohci_pdata ohci_pdata = {
29862306a36Sopenharmony_ci};
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic struct platform_device *bcma_hcd_create_pdev(struct bcma_device *dev,
30162306a36Sopenharmony_ci						    const char *name, u32 addr,
30262306a36Sopenharmony_ci						    const void *data,
30362306a36Sopenharmony_ci						    size_t size)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct platform_device *hci_dev;
30662306a36Sopenharmony_ci	struct resource hci_res[2];
30762306a36Sopenharmony_ci	int ret;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	memset(hci_res, 0, sizeof(hci_res));
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	hci_res[0].start = addr;
31262306a36Sopenharmony_ci	hci_res[0].end = hci_res[0].start + 0x1000 - 1;
31362306a36Sopenharmony_ci	hci_res[0].flags = IORESOURCE_MEM;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	hci_res[1].start = dev->irq;
31662306a36Sopenharmony_ci	hci_res[1].flags = IORESOURCE_IRQ;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	hci_dev = platform_device_alloc(name, 0);
31962306a36Sopenharmony_ci	if (!hci_dev)
32062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	hci_dev->dev.parent = &dev->dev;
32362306a36Sopenharmony_ci	hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	ret = platform_device_add_resources(hci_dev, hci_res,
32662306a36Sopenharmony_ci					    ARRAY_SIZE(hci_res));
32762306a36Sopenharmony_ci	if (ret)
32862306a36Sopenharmony_ci		goto err_alloc;
32962306a36Sopenharmony_ci	if (data)
33062306a36Sopenharmony_ci		ret = platform_device_add_data(hci_dev, data, size);
33162306a36Sopenharmony_ci	if (ret)
33262306a36Sopenharmony_ci		goto err_alloc;
33362306a36Sopenharmony_ci	ret = platform_device_add(hci_dev);
33462306a36Sopenharmony_ci	if (ret)
33562306a36Sopenharmony_ci		goto err_alloc;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return hci_dev;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cierr_alloc:
34062306a36Sopenharmony_ci	platform_device_put(hci_dev);
34162306a36Sopenharmony_ci	return ERR_PTR(ret);
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic int bcma_hcd_usb20_init(struct bcma_hcd_device *usb_dev)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct bcma_device *dev = usb_dev->core;
34762306a36Sopenharmony_ci	struct bcma_chipinfo *chipinfo = &dev->bus->chipinfo;
34862306a36Sopenharmony_ci	u32 ohci_addr;
34962306a36Sopenharmony_ci	int err;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (dma_set_mask_and_coherent(dev->dma_dev, DMA_BIT_MASK(32)))
35262306a36Sopenharmony_ci		return -EOPNOTSUPP;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	bcma_hcd_init_chip_mips(dev);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* In AI chips EHCI is addrspace 0, OHCI is 1 */
35762306a36Sopenharmony_ci	ohci_addr = dev->addr_s[0];
35862306a36Sopenharmony_ci	if ((chipinfo->id == BCMA_CHIP_ID_BCM5357 ||
35962306a36Sopenharmony_ci	     chipinfo->id == BCMA_CHIP_ID_BCM4749)
36062306a36Sopenharmony_ci	    && chipinfo->rev == 0)
36162306a36Sopenharmony_ci		ohci_addr = 0x18009000;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	usb_dev->ohci_dev = bcma_hcd_create_pdev(dev, "ohci-platform",
36462306a36Sopenharmony_ci						 ohci_addr, &ohci_pdata,
36562306a36Sopenharmony_ci						 sizeof(ohci_pdata));
36662306a36Sopenharmony_ci	if (IS_ERR(usb_dev->ohci_dev))
36762306a36Sopenharmony_ci		return PTR_ERR(usb_dev->ohci_dev);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	usb_dev->ehci_dev = bcma_hcd_create_pdev(dev, "ehci-platform",
37062306a36Sopenharmony_ci						 dev->addr, &ehci_pdata,
37162306a36Sopenharmony_ci						 sizeof(ehci_pdata));
37262306a36Sopenharmony_ci	if (IS_ERR(usb_dev->ehci_dev)) {
37362306a36Sopenharmony_ci		err = PTR_ERR(usb_dev->ehci_dev);
37462306a36Sopenharmony_ci		goto err_unregister_ohci_dev;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	return 0;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cierr_unregister_ohci_dev:
38062306a36Sopenharmony_ci	platform_device_unregister(usb_dev->ohci_dev);
38162306a36Sopenharmony_ci	return err;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int bcma_hcd_usb30_init(struct bcma_hcd_device *bcma_hcd)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct bcma_device *core = bcma_hcd->core;
38762306a36Sopenharmony_ci	struct device *dev = &core->dev;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	bcma_core_enable(core, 0);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	of_platform_default_populate(dev->of_node, NULL, dev);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return 0;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic int bcma_hcd_probe(struct bcma_device *core)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	int err;
39962306a36Sopenharmony_ci	struct bcma_hcd_device *usb_dev;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* TODO: Probably need checks here; is the core connected? */
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	usb_dev = devm_kzalloc(&core->dev, sizeof(struct bcma_hcd_device),
40462306a36Sopenharmony_ci			       GFP_KERNEL);
40562306a36Sopenharmony_ci	if (!usb_dev)
40662306a36Sopenharmony_ci		return -ENOMEM;
40762306a36Sopenharmony_ci	usb_dev->core = core;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	usb_dev->gpio_desc = devm_gpiod_get_optional(&core->dev, "vcc",
41062306a36Sopenharmony_ci						     GPIOD_OUT_HIGH);
41162306a36Sopenharmony_ci	if (IS_ERR(usb_dev->gpio_desc))
41262306a36Sopenharmony_ci		return dev_err_probe(&core->dev, PTR_ERR(usb_dev->gpio_desc),
41362306a36Sopenharmony_ci				     "error obtaining VCC GPIO");
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	switch (core->id.id) {
41662306a36Sopenharmony_ci	case BCMA_CORE_USB20_HOST:
41762306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_ARM))
41862306a36Sopenharmony_ci			err = bcma_hcd_usb20_old_arm_init(usb_dev);
41962306a36Sopenharmony_ci		else if (IS_ENABLED(CONFIG_MIPS))
42062306a36Sopenharmony_ci			err = bcma_hcd_usb20_init(usb_dev);
42162306a36Sopenharmony_ci		else
42262306a36Sopenharmony_ci			err = -ENOTSUPP;
42362306a36Sopenharmony_ci		break;
42462306a36Sopenharmony_ci	case BCMA_CORE_NS_USB20:
42562306a36Sopenharmony_ci		err = bcma_hcd_usb20_ns_init(usb_dev);
42662306a36Sopenharmony_ci		break;
42762306a36Sopenharmony_ci	case BCMA_CORE_NS_USB30:
42862306a36Sopenharmony_ci		err = bcma_hcd_usb30_init(usb_dev);
42962306a36Sopenharmony_ci		break;
43062306a36Sopenharmony_ci	default:
43162306a36Sopenharmony_ci		return -ENODEV;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci	if (err)
43462306a36Sopenharmony_ci		return err;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	bcma_set_drvdata(core, usb_dev);
43762306a36Sopenharmony_ci	return 0;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic void bcma_hcd_remove(struct bcma_device *dev)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct bcma_hcd_device *usb_dev = bcma_get_drvdata(dev);
44362306a36Sopenharmony_ci	struct platform_device *ohci_dev = usb_dev->ohci_dev;
44462306a36Sopenharmony_ci	struct platform_device *ehci_dev = usb_dev->ehci_dev;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (ohci_dev)
44762306a36Sopenharmony_ci		platform_device_unregister(ohci_dev);
44862306a36Sopenharmony_ci	if (ehci_dev)
44962306a36Sopenharmony_ci		platform_device_unregister(ehci_dev);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	bcma_core_disable(dev, 0);
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic void bcma_hcd_shutdown(struct bcma_device *dev)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	bcma_hci_platform_power_gpio(dev, false);
45762306a36Sopenharmony_ci	bcma_core_disable(dev, 0);
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci#ifdef CONFIG_PM
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic int bcma_hcd_suspend(struct bcma_device *dev)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	bcma_hci_platform_power_gpio(dev, false);
46562306a36Sopenharmony_ci	bcma_core_disable(dev, 0);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	return 0;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic int bcma_hcd_resume(struct bcma_device *dev)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	bcma_hci_platform_power_gpio(dev, true);
47362306a36Sopenharmony_ci	bcma_core_enable(dev, 0);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	return 0;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci#else /* !CONFIG_PM */
47962306a36Sopenharmony_ci#define bcma_hcd_suspend	NULL
48062306a36Sopenharmony_ci#define bcma_hcd_resume	NULL
48162306a36Sopenharmony_ci#endif /* CONFIG_PM */
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic const struct bcma_device_id bcma_hcd_table[] = {
48462306a36Sopenharmony_ci	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_USB20_HOST, BCMA_ANY_REV, BCMA_ANY_CLASS),
48562306a36Sopenharmony_ci	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_USB20, BCMA_ANY_REV, BCMA_ANY_CLASS),
48662306a36Sopenharmony_ci	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_USB30, BCMA_ANY_REV, BCMA_ANY_CLASS),
48762306a36Sopenharmony_ci	{},
48862306a36Sopenharmony_ci};
48962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(bcma, bcma_hcd_table);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic struct bcma_driver bcma_hcd_driver = {
49262306a36Sopenharmony_ci	.name		= KBUILD_MODNAME,
49362306a36Sopenharmony_ci	.id_table	= bcma_hcd_table,
49462306a36Sopenharmony_ci	.probe		= bcma_hcd_probe,
49562306a36Sopenharmony_ci	.remove		= bcma_hcd_remove,
49662306a36Sopenharmony_ci	.shutdown	= bcma_hcd_shutdown,
49762306a36Sopenharmony_ci	.suspend	= bcma_hcd_suspend,
49862306a36Sopenharmony_ci	.resume		= bcma_hcd_resume,
49962306a36Sopenharmony_ci};
50062306a36Sopenharmony_cimodule_bcma_driver(bcma_hcd_driver);
501