Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Generic UHCI HCD (Host Controller Driver) for Platform Devices
0004  *
0005  * Copyright (c) 2011 Tony Prisk <linux@prisktech.co.nz>
0006  *
0007  * This file is based on uhci-grlib.c
0008  * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
0009  */
0010 
0011 #include <linux/of.h>
0012 #include <linux/device.h>
0013 #include <linux/platform_device.h>
0014 
0015 static int uhci_platform_init(struct usb_hcd *hcd)
0016 {
0017     struct uhci_hcd *uhci = hcd_to_uhci(hcd);
0018 
0019     /* Probe number of ports if not already provided by DT */
0020     if (!uhci->rh_numports)
0021         uhci->rh_numports = uhci_count_ports(hcd);
0022 
0023     /* Set up pointers to to generic functions */
0024     uhci->reset_hc = uhci_generic_reset_hc;
0025     uhci->check_and_reset_hc = uhci_generic_check_and_reset_hc;
0026 
0027     /* No special actions need to be taken for the functions below */
0028     uhci->configure_hc = NULL;
0029     uhci->resume_detect_interrupts_are_broken = NULL;
0030     uhci->global_suspend_mode_is_broken = NULL;
0031 
0032     /* Reset if the controller isn't already safely quiescent. */
0033     check_and_reset_hc(uhci);
0034     return 0;
0035 }
0036 
0037 static const struct hc_driver uhci_platform_hc_driver = {
0038     .description =      hcd_name,
0039     .product_desc =     "Generic UHCI Host Controller",
0040     .hcd_priv_size =    sizeof(struct uhci_hcd),
0041 
0042     /* Generic hardware linkage */
0043     .irq =          uhci_irq,
0044     .flags =        HCD_MEMORY | HCD_DMA | HCD_USB11,
0045 
0046     /* Basic lifecycle operations */
0047     .reset =        uhci_platform_init,
0048     .start =        uhci_start,
0049 #ifdef CONFIG_PM
0050     .pci_suspend =      NULL,
0051     .pci_resume =       NULL,
0052     .bus_suspend =      uhci_rh_suspend,
0053     .bus_resume =       uhci_rh_resume,
0054 #endif
0055     .stop =         uhci_stop,
0056 
0057     .urb_enqueue =      uhci_urb_enqueue,
0058     .urb_dequeue =      uhci_urb_dequeue,
0059 
0060     .endpoint_disable = uhci_hcd_endpoint_disable,
0061     .get_frame_number = uhci_hcd_get_frame_number,
0062 
0063     .hub_status_data =  uhci_hub_status_data,
0064     .hub_control =      uhci_hub_control,
0065 };
0066 
0067 static int uhci_hcd_platform_probe(struct platform_device *pdev)
0068 {
0069     struct device_node *np = pdev->dev.of_node;
0070     struct usb_hcd *hcd;
0071     struct uhci_hcd *uhci;
0072     struct resource *res;
0073     int ret;
0074 
0075     if (usb_disabled())
0076         return -ENODEV;
0077 
0078     /*
0079      * Right now device-tree probed devices don't get dma_mask set.
0080      * Since shared usb code relies on it, set it here for now.
0081      * Once we have dma capability bindings this can go away.
0082      */
0083     ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
0084     if (ret)
0085         return ret;
0086 
0087     hcd = usb_create_hcd(&uhci_platform_hc_driver, &pdev->dev,
0088             pdev->name);
0089     if (!hcd)
0090         return -ENOMEM;
0091 
0092     uhci = hcd_to_uhci(hcd);
0093 
0094     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0095     hcd->regs = devm_ioremap_resource(&pdev->dev, res);
0096     if (IS_ERR(hcd->regs)) {
0097         ret = PTR_ERR(hcd->regs);
0098         goto err_rmr;
0099     }
0100     hcd->rsrc_start = res->start;
0101     hcd->rsrc_len = resource_size(res);
0102 
0103     uhci->regs = hcd->regs;
0104 
0105     /* Grab some things from the device-tree */
0106     if (np) {
0107         u32 num_ports;
0108 
0109         if (of_property_read_u32(np, "#ports", &num_ports) == 0) {
0110             uhci->rh_numports = num_ports;
0111             dev_info(&pdev->dev,
0112                 "Detected %d ports from device-tree\n",
0113                 num_ports);
0114         }
0115         if (of_device_is_compatible(np, "aspeed,ast2400-uhci") ||
0116             of_device_is_compatible(np, "aspeed,ast2500-uhci") ||
0117             of_device_is_compatible(np, "aspeed,ast2600-uhci")) {
0118             uhci->is_aspeed = 1;
0119             dev_info(&pdev->dev,
0120                  "Enabled Aspeed implementation workarounds\n");
0121         }
0122     }
0123 
0124     /* Get and enable clock if any specified */
0125     uhci->clk = devm_clk_get(&pdev->dev, NULL);
0126     if (IS_ERR(uhci->clk)) {
0127         ret = PTR_ERR(uhci->clk);
0128         goto err_rmr;
0129     }
0130     ret = clk_prepare_enable(uhci->clk);
0131     if (ret) {
0132         dev_err(&pdev->dev, "Error couldn't enable clock (%d)\n", ret);
0133         goto err_rmr;
0134     }
0135 
0136     ret = platform_get_irq(pdev, 0);
0137     if (ret < 0)
0138         goto err_clk;
0139 
0140     ret = usb_add_hcd(hcd, ret, IRQF_SHARED);
0141     if (ret)
0142         goto err_clk;
0143 
0144     device_wakeup_enable(hcd->self.controller);
0145     return 0;
0146 
0147 err_clk:
0148     clk_disable_unprepare(uhci->clk);
0149 err_rmr:
0150     usb_put_hcd(hcd);
0151 
0152     return ret;
0153 }
0154 
0155 static int uhci_hcd_platform_remove(struct platform_device *pdev)
0156 {
0157     struct usb_hcd *hcd = platform_get_drvdata(pdev);
0158     struct uhci_hcd *uhci = hcd_to_uhci(hcd);
0159 
0160     clk_disable_unprepare(uhci->clk);
0161     usb_remove_hcd(hcd);
0162     usb_put_hcd(hcd);
0163 
0164     return 0;
0165 }
0166 
0167 /* Make sure the controller is quiescent and that we're not using it
0168  * any more.  This is mainly for the benefit of programs which, like kexec,
0169  * expect the hardware to be idle: not doing DMA or generating IRQs.
0170  *
0171  * This routine may be called in a damaged or failing kernel.  Hence we
0172  * do not acquire the spinlock before shutting down the controller.
0173  */
0174 static void uhci_hcd_platform_shutdown(struct platform_device *op)
0175 {
0176     struct usb_hcd *hcd = platform_get_drvdata(op);
0177 
0178     uhci_hc_died(hcd_to_uhci(hcd));
0179 }
0180 
0181 static const struct of_device_id platform_uhci_ids[] = {
0182     { .compatible = "generic-uhci", },
0183     { .compatible = "platform-uhci", },
0184     {}
0185 };
0186 MODULE_DEVICE_TABLE(of, platform_uhci_ids);
0187 
0188 static struct platform_driver uhci_platform_driver = {
0189     .probe      = uhci_hcd_platform_probe,
0190     .remove     = uhci_hcd_platform_remove,
0191     .shutdown   = uhci_hcd_platform_shutdown,
0192     .driver = {
0193         .name = "platform-uhci",
0194         .of_match_table = platform_uhci_ids,
0195     },
0196 };