Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-1.0+
0002 /*
0003  * EHCI HCD (Host Controller Driver) for USB.
0004  *
0005  * Bus Glue for PPC On-Chip EHCI driver on the of_platform bus
0006  * Tested on AMCC PPC 440EPx
0007  *
0008  * Valentine Barshak <vbarshak@ru.mvista.com>
0009  *
0010  * Based on "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de>
0011  * and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.com>
0012  *
0013  * This file is licenced under the GPL.
0014  */
0015 
0016 #include <linux/err.h>
0017 #include <linux/signal.h>
0018 
0019 #include <linux/of.h>
0020 #include <linux/of_address.h>
0021 #include <linux/of_irq.h>
0022 #include <linux/of_platform.h>
0023 
0024 
0025 static const struct hc_driver ehci_ppc_of_hc_driver = {
0026     .description        = hcd_name,
0027     .product_desc       = "OF EHCI",
0028     .hcd_priv_size      = sizeof(struct ehci_hcd),
0029 
0030     /*
0031      * generic hardware linkage
0032      */
0033     .irq            = ehci_irq,
0034     .flags          = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
0035 
0036     /*
0037      * basic lifecycle operations
0038      */
0039     .reset          = ehci_setup,
0040     .start          = ehci_run,
0041     .stop           = ehci_stop,
0042     .shutdown       = ehci_shutdown,
0043 
0044     /*
0045      * managing i/o requests and associated device resources
0046      */
0047     .urb_enqueue        = ehci_urb_enqueue,
0048     .urb_dequeue        = ehci_urb_dequeue,
0049     .endpoint_disable   = ehci_endpoint_disable,
0050     .endpoint_reset     = ehci_endpoint_reset,
0051 
0052     /*
0053      * scheduling support
0054      */
0055     .get_frame_number   = ehci_get_frame,
0056 
0057     /*
0058      * root hub support
0059      */
0060     .hub_status_data    = ehci_hub_status_data,
0061     .hub_control        = ehci_hub_control,
0062 #ifdef  CONFIG_PM
0063     .bus_suspend        = ehci_bus_suspend,
0064     .bus_resume     = ehci_bus_resume,
0065 #endif
0066     .relinquish_port    = ehci_relinquish_port,
0067     .port_handed_over   = ehci_port_handed_over,
0068 
0069     .clear_tt_buffer_complete   = ehci_clear_tt_buffer_complete,
0070 };
0071 
0072 
0073 /*
0074  * 440EPx Errata USBH_3
0075  * Fix: Enable Break Memory Transfer (BMT) in INSNREG3
0076  */
0077 #define PPC440EPX_EHCI0_INSREG_BMT  (0x1 << 0)
0078 static int
0079 ppc44x_enable_bmt(struct device_node *dn)
0080 {
0081     __iomem u32 *insreg_virt;
0082 
0083     insreg_virt = of_iomap(dn, 1);
0084     if (!insreg_virt)
0085         return  -EINVAL;
0086 
0087     out_be32(insreg_virt + 3, PPC440EPX_EHCI0_INSREG_BMT);
0088 
0089     iounmap(insreg_virt);
0090     return 0;
0091 }
0092 
0093 
0094 static int ehci_hcd_ppc_of_probe(struct platform_device *op)
0095 {
0096     struct device_node *dn = op->dev.of_node;
0097     struct usb_hcd *hcd;
0098     struct ehci_hcd *ehci = NULL;
0099     struct resource res;
0100     int irq;
0101     int rv;
0102 
0103     struct device_node *np;
0104 
0105     if (usb_disabled())
0106         return -ENODEV;
0107 
0108     dev_dbg(&op->dev, "initializing PPC-OF USB Controller\n");
0109 
0110     rv = of_address_to_resource(dn, 0, &res);
0111     if (rv)
0112         return rv;
0113 
0114     hcd = usb_create_hcd(&ehci_ppc_of_hc_driver, &op->dev, "PPC-OF USB");
0115     if (!hcd)
0116         return -ENOMEM;
0117 
0118     hcd->rsrc_start = res.start;
0119     hcd->rsrc_len = resource_size(&res);
0120 
0121     irq = irq_of_parse_and_map(dn, 0);
0122     if (irq == NO_IRQ) {
0123         dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n",
0124             __FILE__);
0125         rv = -EBUSY;
0126         goto err_irq;
0127     }
0128 
0129     hcd->regs = devm_ioremap_resource(&op->dev, &res);
0130     if (IS_ERR(hcd->regs)) {
0131         rv = PTR_ERR(hcd->regs);
0132         goto err_ioremap;
0133     }
0134 
0135     ehci = hcd_to_ehci(hcd);
0136     np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
0137     if (np != NULL) {
0138         /* claim we really affected by usb23 erratum */
0139         if (!of_address_to_resource(np, 0, &res))
0140             ehci->ohci_hcctrl_reg =
0141                 devm_ioremap(&op->dev,
0142                          res.start + OHCI_HCCTRL_OFFSET,
0143                          OHCI_HCCTRL_LEN);
0144         else
0145             pr_debug("%s: no ohci offset in fdt\n", __FILE__);
0146         if (!ehci->ohci_hcctrl_reg) {
0147             pr_debug("%s: ioremap for ohci hcctrl failed\n", __FILE__);
0148         } else {
0149             ehci->has_amcc_usb23 = 1;
0150         }
0151         of_node_put(np);
0152     }
0153 
0154     if (of_get_property(dn, "big-endian", NULL)) {
0155         ehci->big_endian_mmio = 1;
0156         ehci->big_endian_desc = 1;
0157     }
0158     if (of_get_property(dn, "big-endian-regs", NULL))
0159         ehci->big_endian_mmio = 1;
0160     if (of_get_property(dn, "big-endian-desc", NULL))
0161         ehci->big_endian_desc = 1;
0162 
0163     ehci->caps = hcd->regs;
0164 
0165     if (of_device_is_compatible(dn, "ibm,usb-ehci-440epx")) {
0166         rv = ppc44x_enable_bmt(dn);
0167         ehci_dbg(ehci, "Break Memory Transfer (BMT) is %senabled!\n",
0168                 rv ? "NOT ": "");
0169     }
0170 
0171     rv = usb_add_hcd(hcd, irq, 0);
0172     if (rv)
0173         goto err_ioremap;
0174 
0175     device_wakeup_enable(hcd->self.controller);
0176     return 0;
0177 
0178 err_ioremap:
0179     irq_dispose_mapping(irq);
0180 err_irq:
0181     usb_put_hcd(hcd);
0182 
0183     return rv;
0184 }
0185 
0186 
0187 static int ehci_hcd_ppc_of_remove(struct platform_device *op)
0188 {
0189     struct usb_hcd *hcd = platform_get_drvdata(op);
0190     struct ehci_hcd *ehci = hcd_to_ehci(hcd);
0191 
0192     struct device_node *np;
0193     struct resource res;
0194 
0195     dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n");
0196 
0197     usb_remove_hcd(hcd);
0198 
0199     irq_dispose_mapping(hcd->irq);
0200 
0201     /* use request_mem_region to test if the ohci driver is loaded.  if so
0202      * ensure the ohci core is operational.
0203      */
0204     if (ehci->has_amcc_usb23) {
0205         np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
0206         if (np != NULL) {
0207             if (!of_address_to_resource(np, 0, &res))
0208                 if (!request_mem_region(res.start,
0209                                 0x4, hcd_name))
0210                     set_ohci_hcfs(ehci, 1);
0211                 else
0212                     release_mem_region(res.start, 0x4);
0213             else
0214                 pr_debug("%s: no ohci offset in fdt\n", __FILE__);
0215             of_node_put(np);
0216         }
0217     }
0218     usb_put_hcd(hcd);
0219 
0220     return 0;
0221 }
0222 
0223 
0224 static const struct of_device_id ehci_hcd_ppc_of_match[] = {
0225     {
0226         .compatible = "usb-ehci",
0227     },
0228     {},
0229 };
0230 MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match);
0231 
0232 
0233 static struct platform_driver ehci_hcd_ppc_of_driver = {
0234     .probe      = ehci_hcd_ppc_of_probe,
0235     .remove     = ehci_hcd_ppc_of_remove,
0236     .shutdown   = usb_hcd_platform_shutdown,
0237     .driver = {
0238         .name = "ppc-of-ehci",
0239         .of_match_table = ehci_hcd_ppc_of_match,
0240     },
0241 };