162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Freescale QUICC Engine USB Host Controller Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) Freescale Semicondutor, Inc. 2006.
662306a36Sopenharmony_ci *               Shlomi Gridish <gridish@freescale.com>
762306a36Sopenharmony_ci *               Jerry Huang <Chang-Ming.Huang@freescale.com>
862306a36Sopenharmony_ci * Copyright (c) Logic Product Development, Inc. 2007
962306a36Sopenharmony_ci *               Peter Barada <peterb@logicpd.com>
1062306a36Sopenharmony_ci * Copyright (c) MontaVista Software, Inc. 2008.
1162306a36Sopenharmony_ci *               Anton Vorontsov <avorontsov@ru.mvista.com>
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/types.h>
1662306a36Sopenharmony_ci#include <linux/spinlock.h>
1762306a36Sopenharmony_ci#include <linux/delay.h>
1862306a36Sopenharmony_ci#include <linux/errno.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <linux/usb.h>
2162306a36Sopenharmony_ci#include <linux/usb/hcd.h>
2262306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2362306a36Sopenharmony_ci#include <soc/fsl/qe/qe.h>
2462306a36Sopenharmony_ci#include "fhci.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* virtual root hub specific descriptor */
2762306a36Sopenharmony_cistatic u8 root_hub_des[] = {
2862306a36Sopenharmony_ci	0x09, /* blength */
2962306a36Sopenharmony_ci	USB_DT_HUB, /* bDescriptorType;hub-descriptor */
3062306a36Sopenharmony_ci	0x01, /* bNbrPorts */
3162306a36Sopenharmony_ci	HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM, /* wHubCharacteristics */
3262306a36Sopenharmony_ci	0x00, /* per-port power, no overcurrent */
3362306a36Sopenharmony_ci	0x01, /* bPwrOn2pwrGood;2ms */
3462306a36Sopenharmony_ci	0x00, /* bHubContrCurrent;0mA */
3562306a36Sopenharmony_ci	0x00, /* DeviceRemoveable */
3662306a36Sopenharmony_ci	0xff, /* PortPwrCtrlMask */
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic void fhci_gpio_set_value(struct fhci_hcd *fhci, int gpio_nr, bool on)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct gpio_desc *gpiod = fhci->gpiods[gpio_nr];
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (!gpiod)
4462306a36Sopenharmony_ci		return;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	gpiod_set_value(gpiod, on);
4762306a36Sopenharmony_ci	mdelay(5);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_civoid fhci_config_transceiver(struct fhci_hcd *fhci,
5162306a36Sopenharmony_ci			     enum fhci_port_status status)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	fhci_dbg(fhci, "-> %s: %d\n", __func__, status);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	switch (status) {
5662306a36Sopenharmony_ci	case FHCI_PORT_POWER_OFF:
5762306a36Sopenharmony_ci		fhci_gpio_set_value(fhci, GPIO_POWER, false);
5862306a36Sopenharmony_ci		break;
5962306a36Sopenharmony_ci	case FHCI_PORT_DISABLED:
6062306a36Sopenharmony_ci	case FHCI_PORT_WAITING:
6162306a36Sopenharmony_ci		fhci_gpio_set_value(fhci, GPIO_POWER, true);
6262306a36Sopenharmony_ci		break;
6362306a36Sopenharmony_ci	case FHCI_PORT_LOW:
6462306a36Sopenharmony_ci		fhci_gpio_set_value(fhci, GPIO_SPEED, false);
6562306a36Sopenharmony_ci		break;
6662306a36Sopenharmony_ci	case FHCI_PORT_FULL:
6762306a36Sopenharmony_ci		fhci_gpio_set_value(fhci, GPIO_SPEED, true);
6862306a36Sopenharmony_ci		break;
6962306a36Sopenharmony_ci	default:
7062306a36Sopenharmony_ci		WARN_ON(1);
7162306a36Sopenharmony_ci		break;
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	fhci_dbg(fhci, "<- %s: %d\n", __func__, status);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/* disable the USB port by clearing the EN bit in the USBMOD register */
7862306a36Sopenharmony_civoid fhci_port_disable(struct fhci_hcd *fhci)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct fhci_usb *usb = (struct fhci_usb *)fhci->usb_lld;
8162306a36Sopenharmony_ci	enum fhci_port_status port_status;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	fhci_dbg(fhci, "-> %s\n", __func__);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	fhci_stop_sof_timer(fhci);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	fhci_flush_all_transmissions(usb);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	fhci_usb_disable_interrupt((struct fhci_usb *)fhci->usb_lld);
9062306a36Sopenharmony_ci	port_status = usb->port_status;
9162306a36Sopenharmony_ci	usb->port_status = FHCI_PORT_DISABLED;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* Enable IDLE since we want to know if something comes along */
9462306a36Sopenharmony_ci	usb->saved_msk |= USB_E_IDLE_MASK;
9562306a36Sopenharmony_ci	out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/* check if during the disconnection process attached new device */
9862306a36Sopenharmony_ci	if (port_status == FHCI_PORT_WAITING)
9962306a36Sopenharmony_ci		fhci_device_connected_interrupt(fhci);
10062306a36Sopenharmony_ci	usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_ENABLE;
10162306a36Sopenharmony_ci	usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
10262306a36Sopenharmony_ci	fhci_usb_enable_interrupt((struct fhci_usb *)fhci->usb_lld);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	fhci_dbg(fhci, "<- %s\n", __func__);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/* enable the USB port by setting the EN bit in the USBMOD register */
10862306a36Sopenharmony_civoid fhci_port_enable(void *lld)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct fhci_usb *usb = (struct fhci_usb *)lld;
11162306a36Sopenharmony_ci	struct fhci_hcd *fhci = usb->fhci;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	fhci_dbg(fhci, "-> %s\n", __func__);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	fhci_config_transceiver(fhci, usb->port_status);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if ((usb->port_status != FHCI_PORT_FULL) &&
11862306a36Sopenharmony_ci			(usb->port_status != FHCI_PORT_LOW))
11962306a36Sopenharmony_ci		fhci_start_sof_timer(fhci);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE;
12262306a36Sopenharmony_ci	usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	fhci_dbg(fhci, "<- %s\n", __func__);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_civoid fhci_io_port_generate_reset(struct fhci_hcd *fhci)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	fhci_dbg(fhci, "-> %s\n", __func__);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	gpiod_direction_output(fhci->gpiods[GPIO_USBOE], 0);
13262306a36Sopenharmony_ci	gpiod_direction_output(fhci->gpiods[GPIO_USBTP], 0);
13362306a36Sopenharmony_ci	gpiod_direction_output(fhci->gpiods[GPIO_USBTN], 0);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	mdelay(5);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	qe_pin_set_dedicated(fhci->pins[PIN_USBOE]);
13862306a36Sopenharmony_ci	qe_pin_set_dedicated(fhci->pins[PIN_USBTP]);
13962306a36Sopenharmony_ci	qe_pin_set_dedicated(fhci->pins[PIN_USBTN]);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	fhci_dbg(fhci, "<- %s\n", __func__);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/* generate the RESET condition on the bus */
14562306a36Sopenharmony_civoid fhci_port_reset(void *lld)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct fhci_usb *usb = (struct fhci_usb *)lld;
14862306a36Sopenharmony_ci	struct fhci_hcd *fhci = usb->fhci;
14962306a36Sopenharmony_ci	u8 mode;
15062306a36Sopenharmony_ci	u16 mask;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	fhci_dbg(fhci, "-> %s\n", __func__);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	fhci_stop_sof_timer(fhci);
15562306a36Sopenharmony_ci	/* disable the USB controller */
15662306a36Sopenharmony_ci	mode = in_8(&fhci->regs->usb_usmod);
15762306a36Sopenharmony_ci	out_8(&fhci->regs->usb_usmod, mode & (~USB_MODE_EN));
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* disable idle interrupts */
16062306a36Sopenharmony_ci	mask = in_be16(&fhci->regs->usb_usbmr);
16162306a36Sopenharmony_ci	out_be16(&fhci->regs->usb_usbmr, mask & (~USB_E_IDLE_MASK));
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	fhci_io_port_generate_reset(fhci);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* enable interrupt on this endpoint */
16662306a36Sopenharmony_ci	out_be16(&fhci->regs->usb_usbmr, mask);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/* enable the USB controller */
16962306a36Sopenharmony_ci	mode = in_8(&fhci->regs->usb_usmod);
17062306a36Sopenharmony_ci	out_8(&fhci->regs->usb_usmod, mode | USB_MODE_EN);
17162306a36Sopenharmony_ci	fhci_start_sof_timer(fhci);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	fhci_dbg(fhci, "<- %s\n", __func__);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ciint fhci_hub_status_data(struct usb_hcd *hcd, char *buf)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
17962306a36Sopenharmony_ci	int ret = 0;
18062306a36Sopenharmony_ci	unsigned long flags;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	fhci_dbg(fhci, "-> %s\n", __func__);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	spin_lock_irqsave(&fhci->lock, flags);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (fhci->vroot_hub->port.wPortChange & (USB_PORT_STAT_C_CONNECTION |
18762306a36Sopenharmony_ci			USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_SUSPEND |
18862306a36Sopenharmony_ci			USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_OVERCURRENT)) {
18962306a36Sopenharmony_ci		*buf = 1 << 1;
19062306a36Sopenharmony_ci		ret = 1;
19162306a36Sopenharmony_ci		fhci_dbg(fhci, "-- %s\n", __func__);
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	spin_unlock_irqrestore(&fhci->lock, flags);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	fhci_dbg(fhci, "<- %s\n", __func__);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return ret;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ciint fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
20262306a36Sopenharmony_ci			    u16 wIndex, char *buf, u16 wLength)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
20562306a36Sopenharmony_ci	int retval = 0;
20662306a36Sopenharmony_ci	struct usb_hub_status *hub_status;
20762306a36Sopenharmony_ci	struct usb_port_status *port_status;
20862306a36Sopenharmony_ci	unsigned long flags;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	spin_lock_irqsave(&fhci->lock, flags);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	fhci_dbg(fhci, "-> %s\n", __func__);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	switch (typeReq) {
21562306a36Sopenharmony_ci	case ClearHubFeature:
21662306a36Sopenharmony_ci		switch (wValue) {
21762306a36Sopenharmony_ci		case C_HUB_LOCAL_POWER:
21862306a36Sopenharmony_ci		case C_HUB_OVER_CURRENT:
21962306a36Sopenharmony_ci			break;
22062306a36Sopenharmony_ci		default:
22162306a36Sopenharmony_ci			goto error;
22262306a36Sopenharmony_ci		}
22362306a36Sopenharmony_ci		break;
22462306a36Sopenharmony_ci	case ClearPortFeature:
22562306a36Sopenharmony_ci		fhci->vroot_hub->feature &= (1 << wValue);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci		switch (wValue) {
22862306a36Sopenharmony_ci		case USB_PORT_FEAT_ENABLE:
22962306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortStatus &=
23062306a36Sopenharmony_ci			    ~USB_PORT_STAT_ENABLE;
23162306a36Sopenharmony_ci			fhci_port_disable(fhci);
23262306a36Sopenharmony_ci			break;
23362306a36Sopenharmony_ci		case USB_PORT_FEAT_C_ENABLE:
23462306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortChange &=
23562306a36Sopenharmony_ci			    ~USB_PORT_STAT_C_ENABLE;
23662306a36Sopenharmony_ci			break;
23762306a36Sopenharmony_ci		case USB_PORT_FEAT_SUSPEND:
23862306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortStatus &=
23962306a36Sopenharmony_ci			    ~USB_PORT_STAT_SUSPEND;
24062306a36Sopenharmony_ci			fhci_stop_sof_timer(fhci);
24162306a36Sopenharmony_ci			break;
24262306a36Sopenharmony_ci		case USB_PORT_FEAT_C_SUSPEND:
24362306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortChange &=
24462306a36Sopenharmony_ci			    ~USB_PORT_STAT_C_SUSPEND;
24562306a36Sopenharmony_ci			break;
24662306a36Sopenharmony_ci		case USB_PORT_FEAT_POWER:
24762306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortStatus &=
24862306a36Sopenharmony_ci			    ~USB_PORT_STAT_POWER;
24962306a36Sopenharmony_ci			fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF);
25062306a36Sopenharmony_ci			break;
25162306a36Sopenharmony_ci		case USB_PORT_FEAT_C_CONNECTION:
25262306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortChange &=
25362306a36Sopenharmony_ci			    ~USB_PORT_STAT_C_CONNECTION;
25462306a36Sopenharmony_ci			break;
25562306a36Sopenharmony_ci		case USB_PORT_FEAT_C_OVER_CURRENT:
25662306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortChange &=
25762306a36Sopenharmony_ci			    ~USB_PORT_STAT_C_OVERCURRENT;
25862306a36Sopenharmony_ci			break;
25962306a36Sopenharmony_ci		case USB_PORT_FEAT_C_RESET:
26062306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortChange &=
26162306a36Sopenharmony_ci			    ~USB_PORT_STAT_C_RESET;
26262306a36Sopenharmony_ci			break;
26362306a36Sopenharmony_ci		default:
26462306a36Sopenharmony_ci			goto error;
26562306a36Sopenharmony_ci		}
26662306a36Sopenharmony_ci		break;
26762306a36Sopenharmony_ci	case GetHubDescriptor:
26862306a36Sopenharmony_ci		memcpy(buf, root_hub_des, sizeof(root_hub_des));
26962306a36Sopenharmony_ci		break;
27062306a36Sopenharmony_ci	case GetHubStatus:
27162306a36Sopenharmony_ci		hub_status = (struct usb_hub_status *)buf;
27262306a36Sopenharmony_ci		hub_status->wHubStatus =
27362306a36Sopenharmony_ci		    cpu_to_le16(fhci->vroot_hub->hub.wHubStatus);
27462306a36Sopenharmony_ci		hub_status->wHubChange =
27562306a36Sopenharmony_ci		    cpu_to_le16(fhci->vroot_hub->hub.wHubChange);
27662306a36Sopenharmony_ci		break;
27762306a36Sopenharmony_ci	case GetPortStatus:
27862306a36Sopenharmony_ci		port_status = (struct usb_port_status *)buf;
27962306a36Sopenharmony_ci		port_status->wPortStatus =
28062306a36Sopenharmony_ci		    cpu_to_le16(fhci->vroot_hub->port.wPortStatus);
28162306a36Sopenharmony_ci		port_status->wPortChange =
28262306a36Sopenharmony_ci		    cpu_to_le16(fhci->vroot_hub->port.wPortChange);
28362306a36Sopenharmony_ci		break;
28462306a36Sopenharmony_ci	case SetHubFeature:
28562306a36Sopenharmony_ci		switch (wValue) {
28662306a36Sopenharmony_ci		case C_HUB_OVER_CURRENT:
28762306a36Sopenharmony_ci		case C_HUB_LOCAL_POWER:
28862306a36Sopenharmony_ci			break;
28962306a36Sopenharmony_ci		default:
29062306a36Sopenharmony_ci			goto error;
29162306a36Sopenharmony_ci		}
29262306a36Sopenharmony_ci		break;
29362306a36Sopenharmony_ci	case SetPortFeature:
29462306a36Sopenharmony_ci		fhci->vroot_hub->feature |= (1 << wValue);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		switch (wValue) {
29762306a36Sopenharmony_ci		case USB_PORT_FEAT_ENABLE:
29862306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortStatus |=
29962306a36Sopenharmony_ci			    USB_PORT_STAT_ENABLE;
30062306a36Sopenharmony_ci			fhci_port_enable(fhci->usb_lld);
30162306a36Sopenharmony_ci			break;
30262306a36Sopenharmony_ci		case USB_PORT_FEAT_SUSPEND:
30362306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortStatus |=
30462306a36Sopenharmony_ci			    USB_PORT_STAT_SUSPEND;
30562306a36Sopenharmony_ci			fhci_stop_sof_timer(fhci);
30662306a36Sopenharmony_ci			break;
30762306a36Sopenharmony_ci		case USB_PORT_FEAT_RESET:
30862306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortStatus |=
30962306a36Sopenharmony_ci			    USB_PORT_STAT_RESET;
31062306a36Sopenharmony_ci			fhci_port_reset(fhci->usb_lld);
31162306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortStatus |=
31262306a36Sopenharmony_ci			    USB_PORT_STAT_ENABLE;
31362306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortStatus &=
31462306a36Sopenharmony_ci			    ~USB_PORT_STAT_RESET;
31562306a36Sopenharmony_ci			break;
31662306a36Sopenharmony_ci		case USB_PORT_FEAT_POWER:
31762306a36Sopenharmony_ci			fhci->vroot_hub->port.wPortStatus |=
31862306a36Sopenharmony_ci			    USB_PORT_STAT_POWER;
31962306a36Sopenharmony_ci			fhci_config_transceiver(fhci, FHCI_PORT_WAITING);
32062306a36Sopenharmony_ci			break;
32162306a36Sopenharmony_ci		default:
32262306a36Sopenharmony_ci			goto error;
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci		break;
32562306a36Sopenharmony_ci	default:
32662306a36Sopenharmony_cierror:
32762306a36Sopenharmony_ci		retval = -EPIPE;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	fhci_dbg(fhci, "<- %s\n", __func__);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	spin_unlock_irqrestore(&fhci->lock, flags);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return retval;
33562306a36Sopenharmony_ci}
336