15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
263c84552SPaul Mundt /*
363c84552SPaul Mundt * SuperH EHCI host controller driver
463c84552SPaul Mundt *
563c84552SPaul Mundt * Copyright (C) 2010 Paul Mundt
663c84552SPaul Mundt *
763c84552SPaul Mundt * Based on ohci-sh.c and ehci-atmel.c.
863c84552SPaul Mundt */
963c84552SPaul Mundt #include <linux/platform_device.h>
1063c84552SPaul Mundt #include <linux/clk.h>
1163c84552SPaul Mundt
1263c84552SPaul Mundt struct ehci_sh_priv {
1363c84552SPaul Mundt struct clk *iclk, *fclk;
1463c84552SPaul Mundt struct usb_hcd *hcd;
1563c84552SPaul Mundt };
1663c84552SPaul Mundt
ehci_sh_reset(struct usb_hcd * hcd)1763c84552SPaul Mundt static int ehci_sh_reset(struct usb_hcd *hcd)
1863c84552SPaul Mundt {
1963c84552SPaul Mundt struct ehci_hcd *ehci = hcd_to_ehci(hcd);
2063c84552SPaul Mundt
2163c84552SPaul Mundt ehci->caps = hcd->regs;
2263c84552SPaul Mundt
23c73cee71SAlan Stern return ehci_setup(hcd);
2463c84552SPaul Mundt }
2563c84552SPaul Mundt
2663c84552SPaul Mundt static const struct hc_driver ehci_sh_hc_driver = {
2763c84552SPaul Mundt .description = hcd_name,
2863c84552SPaul Mundt .product_desc = "SuperH EHCI",
2963c84552SPaul Mundt .hcd_priv_size = sizeof(struct ehci_hcd),
3063c84552SPaul Mundt
3163c84552SPaul Mundt /*
3263c84552SPaul Mundt * generic hardware linkage
3363c84552SPaul Mundt */
3463c84552SPaul Mundt .irq = ehci_irq,
357b81cb6bSChristoph Hellwig .flags = HCD_USB2 | HCD_DMA | HCD_MEMORY | HCD_BH,
3663c84552SPaul Mundt
3763c84552SPaul Mundt /*
3863c84552SPaul Mundt * basic lifecycle operations
3963c84552SPaul Mundt */
4063c84552SPaul Mundt .reset = ehci_sh_reset,
4163c84552SPaul Mundt .start = ehci_run,
4263c84552SPaul Mundt .stop = ehci_stop,
4363c84552SPaul Mundt .shutdown = ehci_shutdown,
4463c84552SPaul Mundt
4563c84552SPaul Mundt /*
4663c84552SPaul Mundt * managing i/o requests and associated device resources
4763c84552SPaul Mundt */
4863c84552SPaul Mundt .urb_enqueue = ehci_urb_enqueue,
4963c84552SPaul Mundt .urb_dequeue = ehci_urb_dequeue,
5063c84552SPaul Mundt .endpoint_disable = ehci_endpoint_disable,
516e9d4476SPaul Mundt .endpoint_reset = ehci_endpoint_reset,
5263c84552SPaul Mundt
5363c84552SPaul Mundt /*
5463c84552SPaul Mundt * scheduling support
5563c84552SPaul Mundt */
5663c84552SPaul Mundt .get_frame_number = ehci_get_frame,
5763c84552SPaul Mundt
5863c84552SPaul Mundt /*
5963c84552SPaul Mundt * root hub support
6063c84552SPaul Mundt */
6163c84552SPaul Mundt .hub_status_data = ehci_hub_status_data,
6263c84552SPaul Mundt .hub_control = ehci_hub_control,
6363c84552SPaul Mundt
6463c84552SPaul Mundt #ifdef CONFIG_PM
6563c84552SPaul Mundt .bus_suspend = ehci_bus_suspend,
6663c84552SPaul Mundt .bus_resume = ehci_bus_resume,
6763c84552SPaul Mundt #endif
6863c84552SPaul Mundt
6963c84552SPaul Mundt .relinquish_port = ehci_relinquish_port,
7063c84552SPaul Mundt .port_handed_over = ehci_port_handed_over,
716e9d4476SPaul Mundt .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
7263c84552SPaul Mundt };
7363c84552SPaul Mundt
ehci_hcd_sh_probe(struct platform_device * pdev)7463c84552SPaul Mundt static int ehci_hcd_sh_probe(struct platform_device *pdev)
7563c84552SPaul Mundt {
7663c84552SPaul Mundt struct resource *res;
7763c84552SPaul Mundt struct ehci_sh_priv *priv;
7863c84552SPaul Mundt struct usb_hcd *hcd;
7963c84552SPaul Mundt int irq, ret;
8063c84552SPaul Mundt
8163c84552SPaul Mundt if (usb_disabled())
8263c84552SPaul Mundt return -ENODEV;
8363c84552SPaul Mundt
8463c84552SPaul Mundt irq = platform_get_irq(pdev, 0);
85f2e5812fSRuan Jinjie if (irq < 0) {
86f2e5812fSRuan Jinjie ret = irq;
8763c84552SPaul Mundt goto fail_create_hcd;
8863c84552SPaul Mundt }
8963c84552SPaul Mundt
9063c84552SPaul Mundt /* initialize hcd */
9163c84552SPaul Mundt hcd = usb_create_hcd(&ehci_sh_hc_driver, &pdev->dev,
9263c84552SPaul Mundt dev_name(&pdev->dev));
9363c84552SPaul Mundt if (!hcd) {
9463c84552SPaul Mundt ret = -ENOMEM;
9563c84552SPaul Mundt goto fail_create_hcd;
9663c84552SPaul Mundt }
9763c84552SPaul Mundt
98888765e7SYangtao Li hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
99148e1134SThierry Reding if (IS_ERR(hcd->regs)) {
100148e1134SThierry Reding ret = PTR_ERR(hcd->regs);
101a926e298SJulia Lawall goto fail_request_resource;
10263c84552SPaul Mundt }
103e71827f4SVarka Bhadram hcd->rsrc_start = res->start;
104e71827f4SVarka Bhadram hcd->rsrc_len = resource_size(res);
10563c84552SPaul Mundt
106a926e298SJulia Lawall priv = devm_kzalloc(&pdev->dev, sizeof(struct ehci_sh_priv),
107a926e298SJulia Lawall GFP_KERNEL);
10863c84552SPaul Mundt if (!priv) {
10963c84552SPaul Mundt ret = -ENOMEM;
110a926e298SJulia Lawall goto fail_request_resource;
11163c84552SPaul Mundt }
11263c84552SPaul Mundt
11363c84552SPaul Mundt /* These are optional, we don't care if they fail */
114a926e298SJulia Lawall priv->fclk = devm_clk_get(&pdev->dev, "usb_fck");
11563c84552SPaul Mundt if (IS_ERR(priv->fclk))
11663c84552SPaul Mundt priv->fclk = NULL;
11763c84552SPaul Mundt
118a926e298SJulia Lawall priv->iclk = devm_clk_get(&pdev->dev, "usb_ick");
11963c84552SPaul Mundt if (IS_ERR(priv->iclk))
12063c84552SPaul Mundt priv->iclk = NULL;
12163c84552SPaul Mundt
122*e22e4df2SVitalii Mordan ret = clk_enable(priv->fclk);
123*e22e4df2SVitalii Mordan if (ret)
124*e22e4df2SVitalii Mordan goto fail_request_resource;
125*e22e4df2SVitalii Mordan ret = clk_enable(priv->iclk);
126*e22e4df2SVitalii Mordan if (ret)
127*e22e4df2SVitalii Mordan goto fail_iclk;
12863c84552SPaul Mundt
129b5dd18d8SYong Zhang ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
13063c84552SPaul Mundt if (ret != 0) {
13163c84552SPaul Mundt dev_err(&pdev->dev, "Failed to add hcd");
13263c84552SPaul Mundt goto fail_add_hcd;
13363c84552SPaul Mundt }
1343c9740a1SPeter Chen device_wakeup_enable(hcd->self.controller);
13563c84552SPaul Mundt
13663c84552SPaul Mundt priv->hcd = hcd;
13763c84552SPaul Mundt platform_set_drvdata(pdev, priv);
13863c84552SPaul Mundt
13963c84552SPaul Mundt return ret;
14063c84552SPaul Mundt
14163c84552SPaul Mundt fail_add_hcd:
14263c84552SPaul Mundt clk_disable(priv->iclk);
143*e22e4df2SVitalii Mordan fail_iclk:
14463c84552SPaul Mundt clk_disable(priv->fclk);
14563c84552SPaul Mundt
14663c84552SPaul Mundt fail_request_resource:
14763c84552SPaul Mundt usb_put_hcd(hcd);
14863c84552SPaul Mundt fail_create_hcd:
14963c84552SPaul Mundt dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret);
15063c84552SPaul Mundt
15163c84552SPaul Mundt return ret;
15263c84552SPaul Mundt }
15363c84552SPaul Mundt
ehci_hcd_sh_remove(struct platform_device * pdev)154a9a49024SUwe Kleine-König static void ehci_hcd_sh_remove(struct platform_device *pdev)
15563c84552SPaul Mundt {
15663c84552SPaul Mundt struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
15763c84552SPaul Mundt struct usb_hcd *hcd = priv->hcd;
15863c84552SPaul Mundt
15963c84552SPaul Mundt usb_remove_hcd(hcd);
16063c84552SPaul Mundt usb_put_hcd(hcd);
16163c84552SPaul Mundt
16263c84552SPaul Mundt clk_disable(priv->fclk);
16363c84552SPaul Mundt clk_disable(priv->iclk);
16463c84552SPaul Mundt }
16563c84552SPaul Mundt
ehci_hcd_sh_shutdown(struct platform_device * pdev)166c1e0774dSPaul Mundt static void ehci_hcd_sh_shutdown(struct platform_device *pdev)
167c1e0774dSPaul Mundt {
168c1e0774dSPaul Mundt struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
169c1e0774dSPaul Mundt struct usb_hcd *hcd = priv->hcd;
170c1e0774dSPaul Mundt
171c1e0774dSPaul Mundt if (hcd->driver->shutdown)
172c1e0774dSPaul Mundt hcd->driver->shutdown(hcd);
173c1e0774dSPaul Mundt }
174c1e0774dSPaul Mundt
17563c84552SPaul Mundt static struct platform_driver ehci_hcd_sh_driver = {
17663c84552SPaul Mundt .probe = ehci_hcd_sh_probe,
177a9a49024SUwe Kleine-König .remove_new = ehci_hcd_sh_remove,
178c1e0774dSPaul Mundt .shutdown = ehci_hcd_sh_shutdown,
17963c84552SPaul Mundt .driver = {
18063c84552SPaul Mundt .name = "sh_ehci",
18163c84552SPaul Mundt },
18263c84552SPaul Mundt };
18363c84552SPaul Mundt
18463c84552SPaul Mundt MODULE_ALIAS("platform:sh_ehci");
185