root/drivers/usb/gadget/udc/aspeed-vhub/core.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ast_vhub_done
  2. ast_vhub_nuke
  3. ast_vhub_alloc_request
  4. ast_vhub_free_request
  5. ast_vhub_irq
  6. ast_vhub_init_hw
  7. ast_vhub_remove
  8. ast_vhub_probe

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

/* [<][>][^][v][top][bottom][index][help] */