cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fhci-hub.c (8769B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Freescale QUICC Engine USB Host Controller Driver
      4 *
      5 * Copyright (c) Freescale Semicondutor, Inc. 2006.
      6 *               Shlomi Gridish <gridish@freescale.com>
      7 *               Jerry Huang <Chang-Ming.Huang@freescale.com>
      8 * Copyright (c) Logic Product Development, Inc. 2007
      9 *               Peter Barada <peterb@logicpd.com>
     10 * Copyright (c) MontaVista Software, Inc. 2008.
     11 *               Anton Vorontsov <avorontsov@ru.mvista.com>
     12 */
     13
     14#include <linux/kernel.h>
     15#include <linux/types.h>
     16#include <linux/spinlock.h>
     17#include <linux/delay.h>
     18#include <linux/errno.h>
     19#include <linux/io.h>
     20#include <linux/usb.h>
     21#include <linux/usb/hcd.h>
     22#include <linux/gpio.h>
     23#include <soc/fsl/qe/qe.h>
     24#include "fhci.h"
     25
     26/* virtual root hub specific descriptor */
     27static u8 root_hub_des[] = {
     28	0x09, /* blength */
     29	USB_DT_HUB, /* bDescriptorType;hub-descriptor */
     30	0x01, /* bNbrPorts */
     31	HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM, /* wHubCharacteristics */
     32	0x00, /* per-port power, no overcurrent */
     33	0x01, /* bPwrOn2pwrGood;2ms */
     34	0x00, /* bHubContrCurrent;0mA */
     35	0x00, /* DeviceRemoveable */
     36	0xff, /* PortPwrCtrlMask */
     37};
     38
     39static void fhci_gpio_set_value(struct fhci_hcd *fhci, int gpio_nr, bool on)
     40{
     41	int gpio = fhci->gpios[gpio_nr];
     42	bool alow = fhci->alow_gpios[gpio_nr];
     43
     44	if (!gpio_is_valid(gpio))
     45		return;
     46
     47	gpio_set_value(gpio, on ^ alow);
     48	mdelay(5);
     49}
     50
     51void fhci_config_transceiver(struct fhci_hcd *fhci,
     52			     enum fhci_port_status status)
     53{
     54	fhci_dbg(fhci, "-> %s: %d\n", __func__, status);
     55
     56	switch (status) {
     57	case FHCI_PORT_POWER_OFF:
     58		fhci_gpio_set_value(fhci, GPIO_POWER, false);
     59		break;
     60	case FHCI_PORT_DISABLED:
     61	case FHCI_PORT_WAITING:
     62		fhci_gpio_set_value(fhci, GPIO_POWER, true);
     63		break;
     64	case FHCI_PORT_LOW:
     65		fhci_gpio_set_value(fhci, GPIO_SPEED, false);
     66		break;
     67	case FHCI_PORT_FULL:
     68		fhci_gpio_set_value(fhci, GPIO_SPEED, true);
     69		break;
     70	default:
     71		WARN_ON(1);
     72		break;
     73	}
     74
     75	fhci_dbg(fhci, "<- %s: %d\n", __func__, status);
     76}
     77
     78/* disable the USB port by clearing the EN bit in the USBMOD register */
     79void fhci_port_disable(struct fhci_hcd *fhci)
     80{
     81	struct fhci_usb *usb = (struct fhci_usb *)fhci->usb_lld;
     82	enum fhci_port_status port_status;
     83
     84	fhci_dbg(fhci, "-> %s\n", __func__);
     85
     86	fhci_stop_sof_timer(fhci);
     87
     88	fhci_flush_all_transmissions(usb);
     89
     90	fhci_usb_disable_interrupt((struct fhci_usb *)fhci->usb_lld);
     91	port_status = usb->port_status;
     92	usb->port_status = FHCI_PORT_DISABLED;
     93
     94	/* Enable IDLE since we want to know if something comes along */
     95	usb->saved_msk |= USB_E_IDLE_MASK;
     96	out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
     97
     98	/* check if during the disconnection process attached new device */
     99	if (port_status == FHCI_PORT_WAITING)
    100		fhci_device_connected_interrupt(fhci);
    101	usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_ENABLE;
    102	usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
    103	fhci_usb_enable_interrupt((struct fhci_usb *)fhci->usb_lld);
    104
    105	fhci_dbg(fhci, "<- %s\n", __func__);
    106}
    107
    108/* enable the USB port by setting the EN bit in the USBMOD register */
    109void fhci_port_enable(void *lld)
    110{
    111	struct fhci_usb *usb = (struct fhci_usb *)lld;
    112	struct fhci_hcd *fhci = usb->fhci;
    113
    114	fhci_dbg(fhci, "-> %s\n", __func__);
    115
    116	fhci_config_transceiver(fhci, usb->port_status);
    117
    118	if ((usb->port_status != FHCI_PORT_FULL) &&
    119			(usb->port_status != FHCI_PORT_LOW))
    120		fhci_start_sof_timer(fhci);
    121
    122	usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE;
    123	usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
    124
    125	fhci_dbg(fhci, "<- %s\n", __func__);
    126}
    127
    128void fhci_io_port_generate_reset(struct fhci_hcd *fhci)
    129{
    130	fhci_dbg(fhci, "-> %s\n", __func__);
    131
    132	gpio_direction_output(fhci->gpios[GPIO_USBOE], 0);
    133	gpio_direction_output(fhci->gpios[GPIO_USBTP], 0);
    134	gpio_direction_output(fhci->gpios[GPIO_USBTN], 0);
    135
    136	mdelay(5);
    137
    138	qe_pin_set_dedicated(fhci->pins[PIN_USBOE]);
    139	qe_pin_set_dedicated(fhci->pins[PIN_USBTP]);
    140	qe_pin_set_dedicated(fhci->pins[PIN_USBTN]);
    141
    142	fhci_dbg(fhci, "<- %s\n", __func__);
    143}
    144
    145/* generate the RESET condition on the bus */
    146void fhci_port_reset(void *lld)
    147{
    148	struct fhci_usb *usb = (struct fhci_usb *)lld;
    149	struct fhci_hcd *fhci = usb->fhci;
    150	u8 mode;
    151	u16 mask;
    152
    153	fhci_dbg(fhci, "-> %s\n", __func__);
    154
    155	fhci_stop_sof_timer(fhci);
    156	/* disable the USB controller */
    157	mode = in_8(&fhci->regs->usb_usmod);
    158	out_8(&fhci->regs->usb_usmod, mode & (~USB_MODE_EN));
    159
    160	/* disable idle interrupts */
    161	mask = in_be16(&fhci->regs->usb_usbmr);
    162	out_be16(&fhci->regs->usb_usbmr, mask & (~USB_E_IDLE_MASK));
    163
    164	fhci_io_port_generate_reset(fhci);
    165
    166	/* enable interrupt on this endpoint */
    167	out_be16(&fhci->regs->usb_usbmr, mask);
    168
    169	/* enable the USB controller */
    170	mode = in_8(&fhci->regs->usb_usmod);
    171	out_8(&fhci->regs->usb_usmod, mode | USB_MODE_EN);
    172	fhci_start_sof_timer(fhci);
    173
    174	fhci_dbg(fhci, "<- %s\n", __func__);
    175}
    176
    177int fhci_hub_status_data(struct usb_hcd *hcd, char *buf)
    178{
    179	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
    180	int ret = 0;
    181	unsigned long flags;
    182
    183	fhci_dbg(fhci, "-> %s\n", __func__);
    184
    185	spin_lock_irqsave(&fhci->lock, flags);
    186
    187	if (fhci->vroot_hub->port.wPortChange & (USB_PORT_STAT_C_CONNECTION |
    188			USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_SUSPEND |
    189			USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_OVERCURRENT)) {
    190		*buf = 1 << 1;
    191		ret = 1;
    192		fhci_dbg(fhci, "-- %s\n", __func__);
    193	}
    194
    195	spin_unlock_irqrestore(&fhci->lock, flags);
    196
    197	fhci_dbg(fhci, "<- %s\n", __func__);
    198
    199	return ret;
    200}
    201
    202int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
    203			    u16 wIndex, char *buf, u16 wLength)
    204{
    205	struct fhci_hcd *fhci = hcd_to_fhci(hcd);
    206	int retval = 0;
    207	struct usb_hub_status *hub_status;
    208	struct usb_port_status *port_status;
    209	unsigned long flags;
    210
    211	spin_lock_irqsave(&fhci->lock, flags);
    212
    213	fhci_dbg(fhci, "-> %s\n", __func__);
    214
    215	switch (typeReq) {
    216	case ClearHubFeature:
    217		switch (wValue) {
    218		case C_HUB_LOCAL_POWER:
    219		case C_HUB_OVER_CURRENT:
    220			break;
    221		default:
    222			goto error;
    223		}
    224		break;
    225	case ClearPortFeature:
    226		fhci->vroot_hub->feature &= (1 << wValue);
    227
    228		switch (wValue) {
    229		case USB_PORT_FEAT_ENABLE:
    230			fhci->vroot_hub->port.wPortStatus &=
    231			    ~USB_PORT_STAT_ENABLE;
    232			fhci_port_disable(fhci);
    233			break;
    234		case USB_PORT_FEAT_C_ENABLE:
    235			fhci->vroot_hub->port.wPortChange &=
    236			    ~USB_PORT_STAT_C_ENABLE;
    237			break;
    238		case USB_PORT_FEAT_SUSPEND:
    239			fhci->vroot_hub->port.wPortStatus &=
    240			    ~USB_PORT_STAT_SUSPEND;
    241			fhci_stop_sof_timer(fhci);
    242			break;
    243		case USB_PORT_FEAT_C_SUSPEND:
    244			fhci->vroot_hub->port.wPortChange &=
    245			    ~USB_PORT_STAT_C_SUSPEND;
    246			break;
    247		case USB_PORT_FEAT_POWER:
    248			fhci->vroot_hub->port.wPortStatus &=
    249			    ~USB_PORT_STAT_POWER;
    250			fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF);
    251			break;
    252		case USB_PORT_FEAT_C_CONNECTION:
    253			fhci->vroot_hub->port.wPortChange &=
    254			    ~USB_PORT_STAT_C_CONNECTION;
    255			break;
    256		case USB_PORT_FEAT_C_OVER_CURRENT:
    257			fhci->vroot_hub->port.wPortChange &=
    258			    ~USB_PORT_STAT_C_OVERCURRENT;
    259			break;
    260		case USB_PORT_FEAT_C_RESET:
    261			fhci->vroot_hub->port.wPortChange &=
    262			    ~USB_PORT_STAT_C_RESET;
    263			break;
    264		default:
    265			goto error;
    266		}
    267		break;
    268	case GetHubDescriptor:
    269		memcpy(buf, root_hub_des, sizeof(root_hub_des));
    270		break;
    271	case GetHubStatus:
    272		hub_status = (struct usb_hub_status *)buf;
    273		hub_status->wHubStatus =
    274		    cpu_to_le16(fhci->vroot_hub->hub.wHubStatus);
    275		hub_status->wHubChange =
    276		    cpu_to_le16(fhci->vroot_hub->hub.wHubChange);
    277		break;
    278	case GetPortStatus:
    279		port_status = (struct usb_port_status *)buf;
    280		port_status->wPortStatus =
    281		    cpu_to_le16(fhci->vroot_hub->port.wPortStatus);
    282		port_status->wPortChange =
    283		    cpu_to_le16(fhci->vroot_hub->port.wPortChange);
    284		break;
    285	case SetHubFeature:
    286		switch (wValue) {
    287		case C_HUB_OVER_CURRENT:
    288		case C_HUB_LOCAL_POWER:
    289			break;
    290		default:
    291			goto error;
    292		}
    293		break;
    294	case SetPortFeature:
    295		fhci->vroot_hub->feature |= (1 << wValue);
    296
    297		switch (wValue) {
    298		case USB_PORT_FEAT_ENABLE:
    299			fhci->vroot_hub->port.wPortStatus |=
    300			    USB_PORT_STAT_ENABLE;
    301			fhci_port_enable(fhci->usb_lld);
    302			break;
    303		case USB_PORT_FEAT_SUSPEND:
    304			fhci->vroot_hub->port.wPortStatus |=
    305			    USB_PORT_STAT_SUSPEND;
    306			fhci_stop_sof_timer(fhci);
    307			break;
    308		case USB_PORT_FEAT_RESET:
    309			fhci->vroot_hub->port.wPortStatus |=
    310			    USB_PORT_STAT_RESET;
    311			fhci_port_reset(fhci->usb_lld);
    312			fhci->vroot_hub->port.wPortStatus |=
    313			    USB_PORT_STAT_ENABLE;
    314			fhci->vroot_hub->port.wPortStatus &=
    315			    ~USB_PORT_STAT_RESET;
    316			break;
    317		case USB_PORT_FEAT_POWER:
    318			fhci->vroot_hub->port.wPortStatus |=
    319			    USB_PORT_STAT_POWER;
    320			fhci_config_transceiver(fhci, FHCI_PORT_WAITING);
    321			break;
    322		default:
    323			goto error;
    324		}
    325		break;
    326	default:
    327error:
    328		retval = -EPIPE;
    329	}
    330
    331	fhci_dbg(fhci, "<- %s\n", __func__);
    332
    333	spin_unlock_irqrestore(&fhci->lock, flags);
    334
    335	return retval;
    336}