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

core.c (11305B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
      4 *
      5 * core.c - Top level support
      6 *
      7 * Copyright 2017 IBM Corporation
      8 */
      9
     10#include <linux/kernel.h>
     11#include <linux/module.h>
     12#include <linux/platform_device.h>
     13#include <linux/delay.h>
     14#include <linux/ioport.h>
     15#include <linux/slab.h>
     16#include <linux/errno.h>
     17#include <linux/list.h>
     18#include <linux/interrupt.h>
     19#include <linux/proc_fs.h>
     20#include <linux/prefetch.h>
     21#include <linux/clk.h>
     22#include <linux/usb/gadget.h>
     23#include <linux/of.h>
     24#include <linux/of_gpio.h>
     25#include <linux/regmap.h>
     26#include <linux/dma-mapping.h>
     27
     28#include "vhub.h"
     29
     30void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
     31		   int status)
     32{
     33	bool internal = req->internal;
     34	struct ast_vhub *vhub = ep->vhub;
     35
     36	EPVDBG(ep, "completing request @%p, status %d\n", req, status);
     37
     38	list_del_init(&req->queue);
     39
     40	if (req->req.status == -EINPROGRESS)
     41		req->req.status = status;
     42
     43	if (req->req.dma) {
     44		if (!WARN_ON(!ep->dev))
     45			usb_gadget_unmap_request_by_dev(&vhub->pdev->dev,
     46						 &req->req, ep->epn.is_in);
     47		req->req.dma = 0;
     48	}
     49
     50	/*
     51	 * If this isn't an internal EP0 request, call the core
     52	 * to call the gadget completion.
     53	 */
     54	if (!internal) {
     55		spin_unlock(&ep->vhub->lock);
     56		usb_gadget_giveback_request(&ep->ep, &req->req);
     57		spin_lock(&ep->vhub->lock);
     58	}
     59}
     60
     61void ast_vhub_nuke(struct ast_vhub_ep *ep, int status)
     62{
     63	struct ast_vhub_req *req;
     64	int count = 0;
     65
     66	/* Beware, lock will be dropped & req-acquired by done() */
     67	while (!list_empty(&ep->queue)) {
     68		req = list_first_entry(&ep->queue, struct ast_vhub_req, queue);
     69		ast_vhub_done(ep, req, status);
     70		count++;
     71	}
     72	if (count)
     73		EPDBG(ep, "Nuked %d request(s)\n", count);
     74}
     75
     76struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep,
     77					   gfp_t gfp_flags)
     78{
     79	struct ast_vhub_req *req;
     80
     81	req = kzalloc(sizeof(*req), gfp_flags);
     82	if (!req)
     83		return NULL;
     84	return &req->req;
     85}
     86
     87void ast_vhub_free_request(struct usb_ep *u_ep, struct usb_request *u_req)
     88{
     89	struct ast_vhub_req *req = to_ast_req(u_req);
     90
     91	kfree(req);
     92}
     93
     94static irqreturn_t ast_vhub_irq(int irq, void *data)
     95{
     96	struct ast_vhub *vhub = data;
     97	irqreturn_t iret = IRQ_NONE;
     98	u32 i, istat;
     99
    100	/* Stale interrupt while tearing down */
    101	if (!vhub->ep0_bufs)
    102		return IRQ_NONE;
    103
    104	spin_lock(&vhub->lock);
    105
    106	/* Read and ACK interrupts */
    107	istat = readl(vhub->regs + AST_VHUB_ISR);
    108	if (!istat)
    109		goto bail;
    110	writel(istat, vhub->regs + AST_VHUB_ISR);
    111	iret = IRQ_HANDLED;
    112
    113	UDCVDBG(vhub, "irq status=%08x, ep_acks=%08x ep_nacks=%08x\n",
    114	       istat,
    115	       readl(vhub->regs + AST_VHUB_EP_ACK_ISR),
    116	       readl(vhub->regs + AST_VHUB_EP_NACK_ISR));
    117
    118	/* Handle generic EPs first */
    119	if (istat & VHUB_IRQ_EP_POOL_ACK_STALL) {
    120		u32 ep_acks = readl(vhub->regs + AST_VHUB_EP_ACK_ISR);
    121		writel(ep_acks, vhub->regs + AST_VHUB_EP_ACK_ISR);
    122
    123		for (i = 0; ep_acks && i < vhub->max_epns; i++) {
    124			u32 mask = VHUB_EP_IRQ(i);
    125			if (ep_acks & mask) {
    126				ast_vhub_epn_ack_irq(&vhub->epns[i]);
    127				ep_acks &= ~mask;
    128			}
    129		}
    130	}
    131
    132	/* Handle device interrupts */
    133	if (istat & vhub->port_irq_mask) {
    134		for (i = 0; i < vhub->max_ports; i++) {
    135			if (istat & VHUB_DEV_IRQ(i))
    136				ast_vhub_dev_irq(&vhub->ports[i].dev);
    137		}
    138	}
    139
    140	/* Handle top-level vHub EP0 interrupts */
    141	if (istat & (VHUB_IRQ_HUB_EP0_OUT_ACK_STALL |
    142		     VHUB_IRQ_HUB_EP0_IN_ACK_STALL |
    143		     VHUB_IRQ_HUB_EP0_SETUP)) {
    144		if (istat & VHUB_IRQ_HUB_EP0_IN_ACK_STALL)
    145			ast_vhub_ep0_handle_ack(&vhub->ep0, true);
    146		if (istat & VHUB_IRQ_HUB_EP0_OUT_ACK_STALL)
    147			ast_vhub_ep0_handle_ack(&vhub->ep0, false);
    148		if (istat & VHUB_IRQ_HUB_EP0_SETUP)
    149			ast_vhub_ep0_handle_setup(&vhub->ep0);
    150	}
    151
    152	/* Various top level bus events */
    153	if (istat & (VHUB_IRQ_BUS_RESUME |
    154		     VHUB_IRQ_BUS_SUSPEND |
    155		     VHUB_IRQ_BUS_RESET)) {
    156		if (istat & VHUB_IRQ_BUS_RESUME)
    157			ast_vhub_hub_resume(vhub);
    158		if (istat & VHUB_IRQ_BUS_SUSPEND)
    159			ast_vhub_hub_suspend(vhub);
    160		if (istat & VHUB_IRQ_BUS_RESET)
    161			ast_vhub_hub_reset(vhub);
    162	}
    163
    164 bail:
    165	spin_unlock(&vhub->lock);
    166	return iret;
    167}
    168
    169void ast_vhub_init_hw(struct ast_vhub *vhub)
    170{
    171	u32 ctrl, port_mask, epn_mask;
    172
    173	UDCDBG(vhub,"(Re)Starting HW ...\n");
    174
    175	/* Enable PHY */
    176	ctrl = VHUB_CTRL_PHY_CLK |
    177		VHUB_CTRL_PHY_RESET_DIS;
    178
    179       /*
    180	* We do *NOT* set the VHUB_CTRL_CLK_STOP_SUSPEND bit
    181	* to stop the logic clock during suspend because
    182	* it causes the registers to become inaccessible and
    183	* we haven't yet figured out a good wayt to bring the
    184	* controller back into life to issue a wakeup.
    185	*/
    186
    187	/*
    188	 * Set some ISO & split control bits according to Aspeed
    189	 * recommendation
    190	 *
    191	 * VHUB_CTRL_ISO_RSP_CTRL: When set tells the HW to respond
    192	 * with 0 bytes data packet to ISO IN endpoints when no data
    193	 * is available.
    194	 *
    195	 * VHUB_CTRL_SPLIT_IN: This makes a SOF complete a split IN
    196	 * transaction.
    197	 */
    198	ctrl |= VHUB_CTRL_ISO_RSP_CTRL | VHUB_CTRL_SPLIT_IN;
    199	writel(ctrl, vhub->regs + AST_VHUB_CTRL);
    200	udelay(1);
    201
    202	/* Set descriptor ring size */
    203	if (AST_VHUB_DESCS_COUNT == 256) {
    204		ctrl |= VHUB_CTRL_LONG_DESC;
    205		writel(ctrl, vhub->regs + AST_VHUB_CTRL);
    206	} else {
    207		BUILD_BUG_ON(AST_VHUB_DESCS_COUNT != 32);
    208	}
    209
    210	/* Reset all devices */
    211	port_mask = GENMASK(vhub->max_ports, 1);
    212	writel(VHUB_SW_RESET_ROOT_HUB |
    213	       VHUB_SW_RESET_DMA_CONTROLLER |
    214	       VHUB_SW_RESET_EP_POOL |
    215	       port_mask, vhub->regs + AST_VHUB_SW_RESET);
    216	udelay(1);
    217	writel(0, vhub->regs + AST_VHUB_SW_RESET);
    218
    219	/* Disable and cleanup EP ACK/NACK interrupts */
    220	epn_mask = GENMASK(vhub->max_epns - 1, 0);
    221	writel(0, vhub->regs + AST_VHUB_EP_ACK_IER);
    222	writel(0, vhub->regs + AST_VHUB_EP_NACK_IER);
    223	writel(epn_mask, vhub->regs + AST_VHUB_EP_ACK_ISR);
    224	writel(epn_mask, vhub->regs + AST_VHUB_EP_NACK_ISR);
    225
    226	/* Default settings for EP0, enable HW hub EP1 */
    227	writel(0, vhub->regs + AST_VHUB_EP0_CTRL);
    228	writel(VHUB_EP1_CTRL_RESET_TOGGLE |
    229	       VHUB_EP1_CTRL_ENABLE,
    230	       vhub->regs + AST_VHUB_EP1_CTRL);
    231	writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
    232
    233	/* Configure EP0 DMA buffer */
    234	writel(vhub->ep0.buf_dma, vhub->regs + AST_VHUB_EP0_DATA);
    235
    236	/* Clear address */
    237	writel(0, vhub->regs + AST_VHUB_CONF);
    238
    239	/* Pullup hub (activate on host) */
    240	if (vhub->force_usb1)
    241		ctrl |= VHUB_CTRL_FULL_SPEED_ONLY;
    242
    243	ctrl |= VHUB_CTRL_UPSTREAM_CONNECT;
    244	writel(ctrl, vhub->regs + AST_VHUB_CTRL);
    245
    246	/* Enable some interrupts */
    247	writel(VHUB_IRQ_HUB_EP0_IN_ACK_STALL |
    248	       VHUB_IRQ_HUB_EP0_OUT_ACK_STALL |
    249	       VHUB_IRQ_HUB_EP0_SETUP |
    250	       VHUB_IRQ_EP_POOL_ACK_STALL |
    251	       VHUB_IRQ_BUS_RESUME |
    252	       VHUB_IRQ_BUS_SUSPEND |
    253	       VHUB_IRQ_BUS_RESET,
    254	       vhub->regs + AST_VHUB_IER);
    255}
    256
    257static int ast_vhub_remove(struct platform_device *pdev)
    258{
    259	struct ast_vhub *vhub = platform_get_drvdata(pdev);
    260	unsigned long flags;
    261	int i;
    262
    263	if (!vhub || !vhub->regs)
    264		return 0;
    265
    266	/* Remove devices */
    267	for (i = 0; i < vhub->max_ports; i++)
    268		ast_vhub_del_dev(&vhub->ports[i].dev);
    269
    270	spin_lock_irqsave(&vhub->lock, flags);
    271
    272	/* Mask & ack all interrupts  */
    273	writel(0, vhub->regs + AST_VHUB_IER);
    274	writel(VHUB_IRQ_ACK_ALL, vhub->regs + AST_VHUB_ISR);
    275
    276	/* Pull device, leave PHY enabled */
    277	writel(VHUB_CTRL_PHY_CLK |
    278	       VHUB_CTRL_PHY_RESET_DIS,
    279	       vhub->regs + AST_VHUB_CTRL);
    280
    281	if (vhub->clk)
    282		clk_disable_unprepare(vhub->clk);
    283
    284	spin_unlock_irqrestore(&vhub->lock, flags);
    285
    286	if (vhub->ep0_bufs)
    287		dma_free_coherent(&pdev->dev,
    288				  AST_VHUB_EP0_MAX_PACKET *
    289				  (vhub->max_ports + 1),
    290				  vhub->ep0_bufs,
    291				  vhub->ep0_bufs_dma);
    292	vhub->ep0_bufs = NULL;
    293
    294	return 0;
    295}
    296
    297static int ast_vhub_probe(struct platform_device *pdev)
    298{
    299	enum usb_device_speed max_speed;
    300	struct ast_vhub *vhub;
    301	struct resource *res;
    302	int i, rc = 0;
    303	const struct device_node *np = pdev->dev.of_node;
    304
    305	vhub = devm_kzalloc(&pdev->dev, sizeof(*vhub), GFP_KERNEL);
    306	if (!vhub)
    307		return -ENOMEM;
    308
    309	rc = of_property_read_u32(np, "aspeed,vhub-downstream-ports",
    310				  &vhub->max_ports);
    311	if (rc < 0)
    312		vhub->max_ports = AST_VHUB_NUM_PORTS;
    313
    314	vhub->ports = devm_kcalloc(&pdev->dev, vhub->max_ports,
    315				   sizeof(*vhub->ports), GFP_KERNEL);
    316	if (!vhub->ports)
    317		return -ENOMEM;
    318
    319	rc = of_property_read_u32(np, "aspeed,vhub-generic-endpoints",
    320				  &vhub->max_epns);
    321	if (rc < 0)
    322		vhub->max_epns = AST_VHUB_NUM_GEN_EPs;
    323
    324	vhub->epns = devm_kcalloc(&pdev->dev, vhub->max_epns,
    325				  sizeof(*vhub->epns), GFP_KERNEL);
    326	if (!vhub->epns)
    327		return -ENOMEM;
    328
    329	spin_lock_init(&vhub->lock);
    330	vhub->pdev = pdev;
    331	vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1,
    332				      VHUB_IRQ_DEV1_BIT);
    333
    334	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    335	vhub->regs = devm_ioremap_resource(&pdev->dev, res);
    336	if (IS_ERR(vhub->regs)) {
    337		dev_err(&pdev->dev, "Failed to map resources\n");
    338		return PTR_ERR(vhub->regs);
    339	}
    340	UDCDBG(vhub, "vHub@%pR mapped @%p\n", res, vhub->regs);
    341
    342	platform_set_drvdata(pdev, vhub);
    343
    344	vhub->clk = devm_clk_get(&pdev->dev, NULL);
    345	if (IS_ERR(vhub->clk)) {
    346		rc = PTR_ERR(vhub->clk);
    347		goto err;
    348	}
    349	rc = clk_prepare_enable(vhub->clk);
    350	if (rc) {
    351		dev_err(&pdev->dev, "Error couldn't enable clock (%d)\n", rc);
    352		goto err;
    353	}
    354
    355	/* Check if we need to limit the HW to USB1 */
    356	max_speed = usb_get_maximum_speed(&pdev->dev);
    357	if (max_speed != USB_SPEED_UNKNOWN && max_speed < USB_SPEED_HIGH)
    358		vhub->force_usb1 = true;
    359
    360	/* Mask & ack all interrupts before installing the handler */
    361	writel(0, vhub->regs + AST_VHUB_IER);
    362	writel(VHUB_IRQ_ACK_ALL, vhub->regs + AST_VHUB_ISR);
    363
    364	/* Find interrupt and install handler */
    365	vhub->irq = platform_get_irq(pdev, 0);
    366	if (vhub->irq < 0) {
    367		rc = vhub->irq;
    368		goto err;
    369	}
    370	rc = devm_request_irq(&pdev->dev, vhub->irq, ast_vhub_irq, 0,
    371			      KBUILD_MODNAME, vhub);
    372	if (rc) {
    373		dev_err(&pdev->dev, "Failed to request interrupt\n");
    374		goto err;
    375	}
    376
    377	/*
    378	 * Allocate DMA buffers for all EP0s in one chunk,
    379	 * one per port and one for the vHub itself
    380	 */
    381	vhub->ep0_bufs = dma_alloc_coherent(&pdev->dev,
    382					    AST_VHUB_EP0_MAX_PACKET *
    383					    (vhub->max_ports + 1),
    384					    &vhub->ep0_bufs_dma, GFP_KERNEL);
    385	if (!vhub->ep0_bufs) {
    386		dev_err(&pdev->dev, "Failed to allocate EP0 DMA buffers\n");
    387		rc = -ENOMEM;
    388		goto err;
    389	}
    390	UDCVDBG(vhub, "EP0 DMA buffers @%p (DMA 0x%08x)\n",
    391		vhub->ep0_bufs, (u32)vhub->ep0_bufs_dma);
    392
    393	/* Init vHub EP0 */
    394	ast_vhub_init_ep0(vhub, &vhub->ep0, NULL);
    395
    396	/* Init devices */
    397	for (i = 0; i < vhub->max_ports && rc == 0; i++)
    398		rc = ast_vhub_init_dev(vhub, i);
    399	if (rc)
    400		goto err;
    401
    402	/* Init hub emulation */
    403	rc = ast_vhub_init_hub(vhub);
    404	if (rc)
    405		goto err;
    406
    407	/* Initialize HW */
    408	ast_vhub_init_hw(vhub);
    409
    410	dev_info(&pdev->dev, "Initialized virtual hub in USB%d mode\n",
    411		 vhub->force_usb1 ? 1 : 2);
    412
    413	return 0;
    414 err:
    415	ast_vhub_remove(pdev);
    416	return rc;
    417}
    418
    419static const struct of_device_id ast_vhub_dt_ids[] = {
    420	{
    421		.compatible = "aspeed,ast2400-usb-vhub",
    422	},
    423	{
    424		.compatible = "aspeed,ast2500-usb-vhub",
    425	},
    426	{
    427		.compatible = "aspeed,ast2600-usb-vhub",
    428	},
    429	{ }
    430};
    431MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids);
    432
    433static struct platform_driver ast_vhub_driver = {
    434	.probe		= ast_vhub_probe,
    435	.remove		= ast_vhub_remove,
    436	.driver		= {
    437		.name	= KBUILD_MODNAME,
    438		.of_match_table	= ast_vhub_dt_ids,
    439	},
    440};
    441module_platform_driver(ast_vhub_driver);
    442
    443MODULE_DESCRIPTION("Aspeed vHub udc driver");
    444MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
    445MODULE_LICENSE("GPL");