162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OHCI HCD (Host Controller Driver) for USB. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> 662306a36Sopenharmony_ci * (C) Copyright 2000-2005 David Brownell 762306a36Sopenharmony_ci * (C) Copyright 2002 Hewlett-Packard Company 862306a36Sopenharmony_ci * (C) Copyright 2008 Magnus Damm 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * SM501 Bus Glue - based on ohci-omap.c 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This file is licenced under the GPL. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/jiffies.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1962306a36Sopenharmony_ci#include <linux/sm501.h> 2062306a36Sopenharmony_ci#include <linux/sm501-regs.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic int ohci_sm501_init(struct usb_hcd *hcd) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci return ohci_init(hcd_to_ohci(hcd)); 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int ohci_sm501_start(struct usb_hcd *hcd) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct device *dev = hcd->self.controller; 3062306a36Sopenharmony_ci int ret; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci ret = ohci_run(hcd_to_ohci(hcd)); 3362306a36Sopenharmony_ci if (ret < 0) { 3462306a36Sopenharmony_ci dev_err(dev, "can't start %s", hcd->self.bus_name); 3562306a36Sopenharmony_ci ohci_stop(hcd); 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return ret; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic const struct hc_driver ohci_sm501_hc_driver = { 4462306a36Sopenharmony_ci .description = hcd_name, 4562306a36Sopenharmony_ci .product_desc = "SM501 OHCI", 4662306a36Sopenharmony_ci .hcd_priv_size = sizeof(struct ohci_hcd), 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* 4962306a36Sopenharmony_ci * generic hardware linkage 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci .irq = ohci_irq, 5262306a36Sopenharmony_ci .flags = HCD_USB11 | HCD_MEMORY, 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* 5562306a36Sopenharmony_ci * basic lifecycle operations 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci .reset = ohci_sm501_init, 5862306a36Sopenharmony_ci .start = ohci_sm501_start, 5962306a36Sopenharmony_ci .stop = ohci_stop, 6062306a36Sopenharmony_ci .shutdown = ohci_shutdown, 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* 6362306a36Sopenharmony_ci * managing i/o requests and associated device resources 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci .urb_enqueue = ohci_urb_enqueue, 6662306a36Sopenharmony_ci .urb_dequeue = ohci_urb_dequeue, 6762306a36Sopenharmony_ci .endpoint_disable = ohci_endpoint_disable, 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* 7062306a36Sopenharmony_ci * scheduling support 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci .get_frame_number = ohci_get_frame, 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* 7562306a36Sopenharmony_ci * root hub support 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci .hub_status_data = ohci_hub_status_data, 7862306a36Sopenharmony_ci .hub_control = ohci_hub_control, 7962306a36Sopenharmony_ci#ifdef CONFIG_PM 8062306a36Sopenharmony_ci .bus_suspend = ohci_bus_suspend, 8162306a36Sopenharmony_ci .bus_resume = ohci_bus_resume, 8262306a36Sopenharmony_ci#endif 8362306a36Sopenharmony_ci .start_port_reset = ohci_start_port_reset, 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int ohci_hcd_sm501_drv_probe(struct platform_device *pdev) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci const struct hc_driver *driver = &ohci_sm501_hc_driver; 9162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 9262306a36Sopenharmony_ci struct resource *res, *mem; 9362306a36Sopenharmony_ci int retval, irq; 9462306a36Sopenharmony_ci struct usb_hcd *hcd = NULL; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci irq = retval = platform_get_irq(pdev, 0); 9762306a36Sopenharmony_ci if (retval < 0) 9862306a36Sopenharmony_ci goto err0; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); 10162306a36Sopenharmony_ci if (mem == NULL) { 10262306a36Sopenharmony_ci dev_err(dev, "no resource definition for memory\n"); 10362306a36Sopenharmony_ci retval = -ENOENT; 10462306a36Sopenharmony_ci goto err0; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { 10862306a36Sopenharmony_ci dev_err(dev, "request_mem_region failed\n"); 10962306a36Sopenharmony_ci retval = -EBUSY; 11062306a36Sopenharmony_ci goto err0; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* allocate, reserve and remap resources for registers */ 11462306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 11562306a36Sopenharmony_ci if (res == NULL) { 11662306a36Sopenharmony_ci dev_err(dev, "no resource definition for registers\n"); 11762306a36Sopenharmony_ci retval = -ENOENT; 11862306a36Sopenharmony_ci goto err1; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); 12262306a36Sopenharmony_ci if (!hcd) { 12362306a36Sopenharmony_ci retval = -ENOMEM; 12462306a36Sopenharmony_ci goto err1; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci hcd->rsrc_start = res->start; 12862306a36Sopenharmony_ci hcd->rsrc_len = resource_size(res); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, pdev->name)) { 13162306a36Sopenharmony_ci dev_err(dev, "request_mem_region failed\n"); 13262306a36Sopenharmony_ci retval = -EBUSY; 13362306a36Sopenharmony_ci goto err3; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 13762306a36Sopenharmony_ci if (hcd->regs == NULL) { 13862306a36Sopenharmony_ci dev_err(dev, "cannot remap registers\n"); 13962306a36Sopenharmony_ci retval = -ENXIO; 14062306a36Sopenharmony_ci goto err4; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ohci_hcd_init(hcd_to_ohci(hcd)); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* The sm501 chip is equipped with local memory that may be used 14662306a36Sopenharmony_ci * by on-chip devices such as the video controller and the usb host. 14762306a36Sopenharmony_ci * This driver uses genalloc so that usb allocations with 14862306a36Sopenharmony_ci * gen_pool_dma_alloc() allocate from this local memory. The dma_handle 14962306a36Sopenharmony_ci * returned by gen_pool_dma_alloc() will be an offset starting from 0 15062306a36Sopenharmony_ci * for the first local memory byte. 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * So as long as data is allocated using gen_pool_dma_alloc() all is 15362306a36Sopenharmony_ci * fine. This is however not always the case - buffers may be allocated 15462306a36Sopenharmony_ci * using kmalloc() - so the usb core needs to be told that it must copy 15562306a36Sopenharmony_ci * data into our local memory if the buffers happen to be placed in 15662306a36Sopenharmony_ci * regular memory. A non-null hcd->localmem_pool initialized by 15762306a36Sopenharmony_ci * the call to usb_hcd_setup_local_mem() below does just that. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci retval = usb_hcd_setup_local_mem(hcd, mem->start, 16162306a36Sopenharmony_ci mem->start - mem->parent->start, 16262306a36Sopenharmony_ci resource_size(mem)); 16362306a36Sopenharmony_ci if (retval < 0) 16462306a36Sopenharmony_ci goto err5; 16562306a36Sopenharmony_ci retval = usb_add_hcd(hcd, irq, IRQF_SHARED); 16662306a36Sopenharmony_ci if (retval) 16762306a36Sopenharmony_ci goto err5; 16862306a36Sopenharmony_ci device_wakeup_enable(hcd->self.controller); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* enable power and unmask interrupts */ 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 1); 17362306a36Sopenharmony_ci sm501_modify_reg(dev->parent, SM501_IRQ_MASK, 1 << 6, 0); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_cierr5: 17762306a36Sopenharmony_ci iounmap(hcd->regs); 17862306a36Sopenharmony_cierr4: 17962306a36Sopenharmony_ci release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 18062306a36Sopenharmony_cierr3: 18162306a36Sopenharmony_ci usb_put_hcd(hcd); 18262306a36Sopenharmony_cierr1: 18362306a36Sopenharmony_ci release_mem_region(mem->start, resource_size(mem)); 18462306a36Sopenharmony_cierr0: 18562306a36Sopenharmony_ci return retval; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void ohci_hcd_sm501_drv_remove(struct platform_device *pdev) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct usb_hcd *hcd = platform_get_drvdata(pdev); 19162306a36Sopenharmony_ci struct resource *mem; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci usb_remove_hcd(hcd); 19462306a36Sopenharmony_ci iounmap(hcd->regs); 19562306a36Sopenharmony_ci release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 19662306a36Sopenharmony_ci usb_put_hcd(hcd); 19762306a36Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); 19862306a36Sopenharmony_ci release_mem_region(mem->start, resource_size(mem)); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* mask interrupts and disable power */ 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci sm501_modify_reg(pdev->dev.parent, SM501_IRQ_MASK, 0, 1 << 6); 20362306a36Sopenharmony_ci sm501_unit_power(pdev->dev.parent, SM501_GATE_USB_HOST, 0); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci#ifdef CONFIG_PM 20962306a36Sopenharmony_cistatic int ohci_sm501_suspend(struct platform_device *pdev, pm_message_t msg) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 21262306a36Sopenharmony_ci struct usb_hcd *hcd = platform_get_drvdata(pdev); 21362306a36Sopenharmony_ci struct ohci_hcd *ohci = hcd_to_ohci(hcd); 21462306a36Sopenharmony_ci bool do_wakeup = device_may_wakeup(dev); 21562306a36Sopenharmony_ci int ret; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (time_before(jiffies, ohci->next_statechange)) 21862306a36Sopenharmony_ci msleep(5); 21962306a36Sopenharmony_ci ohci->next_statechange = jiffies; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci ret = ohci_suspend(hcd, do_wakeup); 22262306a36Sopenharmony_ci if (ret) 22362306a36Sopenharmony_ci return ret; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 0); 22662306a36Sopenharmony_ci return ret; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int ohci_sm501_resume(struct platform_device *pdev) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 23262306a36Sopenharmony_ci struct usb_hcd *hcd = platform_get_drvdata(pdev); 23362306a36Sopenharmony_ci struct ohci_hcd *ohci = hcd_to_ohci(hcd); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (time_before(jiffies, ohci->next_statechange)) 23662306a36Sopenharmony_ci msleep(5); 23762306a36Sopenharmony_ci ohci->next_statechange = jiffies; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 1); 24062306a36Sopenharmony_ci ohci_resume(hcd, false); 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci#else 24462306a36Sopenharmony_ci#define ohci_sm501_suspend NULL 24562306a36Sopenharmony_ci#define ohci_sm501_resume NULL 24662306a36Sopenharmony_ci#endif 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* 25162306a36Sopenharmony_ci * Driver definition to register with the SM501 bus 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_cistatic struct platform_driver ohci_hcd_sm501_driver = { 25462306a36Sopenharmony_ci .probe = ohci_hcd_sm501_drv_probe, 25562306a36Sopenharmony_ci .remove_new = ohci_hcd_sm501_drv_remove, 25662306a36Sopenharmony_ci .shutdown = usb_hcd_platform_shutdown, 25762306a36Sopenharmony_ci .suspend = ohci_sm501_suspend, 25862306a36Sopenharmony_ci .resume = ohci_sm501_resume, 25962306a36Sopenharmony_ci .driver = { 26062306a36Sopenharmony_ci .name = "sm501-usb", 26162306a36Sopenharmony_ci }, 26262306a36Sopenharmony_ci}; 26362306a36Sopenharmony_ciMODULE_ALIAS("platform:sm501-usb"); 264