diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/Kconfig | 1 | ||||
-rw-r--r-- | drivers/usb/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 35 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 1 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.c | 10 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-exynos.c | 9 | ||||
-rw-r--r-- | drivers/usb/gadget/Kconfig | 7 | ||||
-rw-r--r-- | drivers/usb/gadget/s3c-hsotg.c | 20 | ||||
-rw-r--r-- | drivers/usb/gadget/u_ether.c | 23 | ||||
-rw-r--r-- | drivers/usb/host/ehci-s5p.c | 105 | ||||
-rw-r--r-- | drivers/usb/host/ohci-exynos.c | 81 | ||||
-rw-r--r-- | drivers/usb/host/xhci-plat.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 2 | ||||
-rw-r--r-- | drivers/usb/otg/otg.c | 3 | ||||
-rw-r--r-- | drivers/usb/phy/Kconfig | 7 | ||||
-rw-r--r-- | drivers/usb/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/phy/samsung-usbphy.c | 1004 |
17 files changed, 1229 insertions, 83 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 640ae6c6d2d..f39c215371b 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -54,6 +54,7 @@ config USB_ARCH_HAS_EHCI # some non-PCI HCDs implement xHCI config USB_ARCH_HAS_XHCI boolean + default y if ARCH_EXYNOS5 default PCI menuconfig USB_SUPPORT diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index f5ed3d75fa5..6c21c9187c3 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_OTG_UTILS) += otg/ +obj-$(CONFIG_USB_COMMON) += phy/ obj-$(CONFIG_USB_DWC3) += dwc3/ obj-$(CONFIG_USB_MON) += mon/ @@ -46,7 +47,6 @@ obj-$(CONFIG_USB_MICROTEK) += image/ obj-$(CONFIG_USB_SERIAL) += serial/ obj-$(CONFIG_USB) += misc/ -obj-$(CONFIG_USB_COMMON) += phy/ obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/ obj-$(CONFIG_USB_ATM) += atm/ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 957ed2c4148..093c33026b0 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -836,7 +836,6 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay) unsigned delay; u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); - /* Enable power on each port. Some hubs have reserved values * of LPSM (> 2) in their descriptors, even though they are * USB 2.0 hubs. Some hubs do not implement port-power switching @@ -1019,7 +1018,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) int status; bool need_debounce_delay = false; unsigned delay; - /* Continue a partial initialization */ if (type == HUB_INIT2) goto init2; @@ -1100,7 +1098,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) for (port1 = 1; port1 <= hdev->maxchild; ++port1) { struct usb_device *udev = hub->ports[port1 - 1]->child; u16 portstatus, portchange; - portstatus = portchange = 0; status = hub_port_status(hub, port1, &portstatus, &portchange); if (udev || (portstatus & USB_PORT_STAT_CONNECTION)) @@ -1386,7 +1383,7 @@ static int hub_configure(struct usb_hub *hub, } hdev->maxchild = hub->descriptor->bNbrPorts; - dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild, + dev_dbg (hub_dev, "%d port%s detected\n", hdev->maxchild, (hdev->maxchild == 1) ? "" : "s"); hub->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *), @@ -1397,7 +1394,6 @@ static int hub_configure(struct usb_hub *hub, } wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); - /* FIXME for USB 3.0, skip for now */ if ((wHubCharacteristics & HUB_CHAR_COMPOUND) && !(hub_is_superspeed(hdev))) { @@ -1410,9 +1406,9 @@ static int hub_configure(struct usb_hub *hub, ? 'F' : 'R'; portstr[hdev->maxchild] = 0; dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr); - } else + } else { dev_dbg(hub_dev, "standalone hub\n"); - + } switch (wHubCharacteristics & HUB_CHAR_LPSM) { case HUB_CHAR_COMMON_LPSM: dev_dbg(hub_dev, "ganged power switching\n"); @@ -1743,7 +1739,7 @@ descriptor_error: goto descriptor_error; /* We found a hub */ - dev_info (&intf->dev, "USB hub found\n"); + dev_dbg (&intf->dev, "USB hub found\n"); hub = kzalloc(sizeof(*hub), GFP_KERNEL); if (!hub) { @@ -1770,7 +1766,6 @@ descriptor_error: if (hub_configure(hub, endpoint) >= 0) return 0; - hub_disconnect (intf); return -ENODEV; } @@ -2062,7 +2057,7 @@ void usb_disconnect(struct usb_device **pdev) * this quiesces everything except pending urbs. */ usb_set_device_state(udev, USB_STATE_NOTATTACHED); - dev_info(&udev->dev, "USB disconnect, device number %d\n", + dev_dbg(&udev->dev, "USB disconnect, device number %d\n", udev->devnum); usb_lock_device(udev); @@ -2115,10 +2110,10 @@ static void show_string(struct usb_device *udev, char *id, char *string) static void announce_device(struct usb_device *udev) { - dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n", + dev_dbg(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n", le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct)); - dev_info(&udev->dev, + dev_dbg(&udev->dev, "New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n", udev->descriptor.iManufacturer, udev->descriptor.iProduct, @@ -2164,7 +2159,7 @@ static int usb_enumerate_device_otg(struct usb_device *udev) if (desc->bmAttributes & USB_OTG_HNP) { unsigned port1 = udev->portnum; - dev_info(&udev->dev, + dev_dbg(&udev->dev, "Dual-Role OTG device on %sHNP port\n", (port1 == bus->otg_port) ? "" : "non-"); @@ -2183,7 +2178,7 @@ static int usb_enumerate_device_otg(struct usb_device *udev) /* OTG MESSAGE: report errors here, * customize to match your product. */ - dev_info(&udev->dev, + dev_dbg(&udev->dev, "can't set HNP mode: %d\n", err); bus->b_hnp_enable = 0; @@ -2466,7 +2461,7 @@ int usb_authorize_device(struct usb_device *usb_dev) * set other configurations. */ } } - dev_info(&usb_dev->dev, "authorized to connect\n"); + dev_dbg(&usb_dev->dev, "authorized to connect\n"); error_enumerate: error_device_descriptor: @@ -3961,7 +3956,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, speed = usb_speed_string(udev->speed); if (udev->speed != USB_SPEED_SUPER) - dev_info(&udev->dev, + dev_dbg(&udev->dev, "%s %s USB device number %d using %s\n", (udev->config) ? "reset" : "new", speed, devnum, udev->bus->controller->driver->name); @@ -4075,7 +4070,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, } if (udev->speed == USB_SPEED_SUPER) { devnum = udev->devnum; - dev_info(&udev->dev, + dev_dbg(&udev->dev, "%s SuperSpeed USB device number %d using %s\n", (udev->config) ? "reset" : "new", devnum, udev->bus->controller->driver->name); @@ -4187,7 +4182,7 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1) status = usb_get_descriptor (udev, USB_DT_DEVICE_QUALIFIER, 0, qual, sizeof *qual); if (status == sizeof *qual) { - dev_info(&udev->dev, "not running at top speed; " + dev_dbg(&udev->dev, "not running at top speed; " "connect to a high speed hub\n"); /* hub LEDs are probably harder to miss than syslog */ if (hub->has_indicators) { @@ -4256,7 +4251,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, le16_to_cpu(hub->descriptor->wHubCharacteristics); struct usb_device *udev; int status, i; - dev_dbg (hub_dev, "port %d, status %04x, change %04x, %s\n", port1, portstatus, portchange, portspeed(hub, portstatus)); @@ -4605,7 +4599,6 @@ static void hub_events(void) hub->nerrors = 0; hub->error = 0; } - /* deal with port status changes */ for (i = 1; i <= hub->descriptor->bNbrPorts; i++) { if (test_bit(i, hub->busy_bits)) @@ -5008,7 +5001,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) /* Device might have changed firmware (DFU or similar) */ if (descriptors_changed(udev, &descriptor)) { - dev_info(&udev->dev, "device firmware changed\n"); + dev_dbg(&udev->dev, "device firmware changed\n"); udev->descriptor = descriptor; /* for disconnect() calls */ goto re_enumerate; } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index f81b9257273..cc7ea9fe9ce 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1002,7 +1002,6 @@ static int __init usb_init(void) pr_info("%s: USB support disabled\n", usbcore_name); return 0; } - retval = usb_debugfs_init(); if (retval) goto out; diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 3a4004a620a..171ca5ec7f7 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -365,12 +365,9 @@ static int dwc3_probe(struct platform_device *pdev) struct resource *res; struct dwc3 *dwc; struct device *dev = &pdev->dev; - int ret = -ENOMEM; - void __iomem *regs; void *mem; - u8 mode; mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); @@ -438,6 +435,8 @@ static int dwc3_probe(struct platform_device *pdev) dwc->regs = regs; dwc->regs_size = resource_size(res); dwc->dev = dev; + /* set the dma coherent mask */ + dwc->dev->coherent_dma_mask = DMA_BIT_MASK(64); if (!strncmp("super", maximum_speed, 5)) dwc->maximum_speed = DWC3_DCFG_SUPERSPEED; @@ -472,7 +471,10 @@ static int dwc3_probe(struct platform_device *pdev) goto err0; } - mode = DWC3_MODE(dwc->hwparams.hwparams0); +// mode = DWC3_MODE(dwc->hwparams.hwparams0); + /* Putting controller in Host mode here */ + + mode = DWC3_MODE_HOST; /* Just a hack for time being */ switch (mode) { case DWC3_MODE_DEVICE: diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index aae5328ac77..87f2c28600b 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -43,6 +43,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) memset(&pdata, 0x00, sizeof(pdata)); pdev = platform_device_alloc("nop_usb_xceiv", 0); + if (!pdev) return -ENOMEM; @@ -50,6 +51,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) pdata.type = USB_PHY_TYPE_USB2; ret = platform_device_add_data(exynos->usb2_phy, &pdata, sizeof(pdata)); + if (ret) goto err1; @@ -103,6 +105,13 @@ static int dwc3_exynos_probe(struct platform_device *pdev) dev_err(&pdev->dev, "not enough memory\n"); goto err0; } + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we move to full device tree support this will vanish off. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &dwc3_exynos_dma_mask; /* * Right now device-tree probed devices don't get dma_mask set. diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f74fc7f418e..c4459681f47 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -808,6 +808,13 @@ config USB_G_ANDROID Each function can be configured and enabled/disabled dynamically from userspace through a sysfs interface. +config USB_G_RNDIS_DWORD_ALIGNED + bool "RNDIS Dword Aligned support" + depends on USB_G_ANDROID + default y + help + Provides dword aligned for DMA controller. + config USB_CDC_COMPOSITE tristate "CDC Composite Device (Ethernet and ACM)" depends on NET diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 439c3f972f8..0243b52e5e4 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -28,6 +28,7 @@ #include <linux/io.h> #include <linux/slab.h> #include <linux/clk.h> +#include <linux/of.h> #include <linux/regulator/consumer.h> #include <linux/usb/ch9.h> @@ -3484,6 +3485,8 @@ static void s3c_hsotg_release(struct device *dev) { } +static u64 s3c_hsotg_dma_mask = DMA_BIT_MASK(32); + /** * s3c_hsotg_probe - probe function for hsotg driver * @pdev: The platform information for the driver @@ -3506,6 +3509,14 @@ static int s3c_hsotg_probe(struct platform_device *pdev) return -EINVAL; } + /* + * Right now device-tree probed devices don't get dma_mask set. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &s3c_hsotg_dma_mask; + if (!pdev->dev.coherent_dma_mask) + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL); if (!hsotg) { dev_err(dev, "cannot get memory\n"); @@ -3701,10 +3712,19 @@ static int s3c_hsotg_remove(struct platform_device *pdev) #define s3c_hsotg_resume NULL #endif +#ifdef CONFIG_OF +static const struct of_device_id exynos_hsotg_match[] = { + { .compatible = "samsung,exynos-hsotg" }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_hsotg_match); +#endif + static struct platform_driver s3c_hsotg_driver = { .driver = { .name = "s3c-hsotg", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(exynos_hsotg_match), }, .probe = s3c_hsotg_probe, .remove = s3c_hsotg_remove, diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 4ec3c0d7a18..13ceb2c201f 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -235,13 +235,15 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) DBG(dev, "no rx skb\n"); goto enomem; } - +#ifndef USB_G_RNDIS_DWORD_ALIGNED /* Some platforms perform better when IP packets are aligned, * but on at least one, checksumming fails otherwise. Note: * RNDIS headers involve variable numbers of LE32 values. */ skb_reserve(skb, NET_IP_ALIGN); +#endif + req->buf = skb->data; req->length = size; req->complete = rx_complete; @@ -471,6 +473,10 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req) spin_unlock(&dev->req_lock); dev_kfree_skb_any(skb); +#ifdef USB_G_RNDIS_DWORD_ALIGNED + if (req->buf != skb->data) + kfree(req->buf); +#endif atomic_dec(&dev->tx_qlen); if (netif_carrier_ok(dev->net)) netif_wake_queue(dev->net); @@ -564,7 +570,18 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, length = skb->len; } +#ifdef USB_G_RNDIS_DWORD_ALIGNED +if ((int)skb->data & 3) { + req->buf = kmalloc(skb->len, GFP_ATOMIC); + if (!req->buf) + goto drop; + memcpy((void *)req->buf, (void *)skb->data, skb->len); + } else { + req->buf = skb->data; + } +#else req->buf = skb->data; +#endif req->context = skb; req->complete = tx_complete; @@ -606,6 +623,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, dev_kfree_skb_any(skb); drop: dev->net->stats.tx_dropped++; +#ifdef USB_G_RNDIS_DWORD_ALIGNED + if (req->buf != skb->data) + kfree(req->buf); +#endif spin_lock_irqsave(&dev->req_lock, flags); if (list_empty(&dev->tx_reqs)) netif_start_queue(net); diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 319dcfaa873..ecdd2c4e9bb 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -17,6 +17,8 @@ #include <linux/platform_device.h> #include <linux/of_gpio.h> #include <linux/platform_data/usb-ehci-s5p.h> +#include <linux/usb/phy.h> +#include <linux/usb/samsung_usb_phy.h> #include <plat/usb-phy.h> #define EHCI_INSNREG00(base) (base + 0x90) @@ -32,6 +34,8 @@ struct s5p_ehci_hcd { struct device *dev; struct usb_hcd *hcd; struct clk *clk; + struct usb_phy *phy; + struct s5p_ehci_platdata *pdata; }; static const struct hc_driver s5p_ehci_hc_driver = { @@ -65,6 +69,50 @@ static const struct hc_driver s5p_ehci_hc_driver = { .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; +static void s5p_ehci_phy_enable(struct s5p_ehci_hcd *s5p_ehci) +{ + struct platform_device *pdev = to_platform_device(s5p_ehci->dev); + + if (s5p_ehci->phy) { + samsung_usbphy_set_type(s5p_ehci->phy, USB_PHY_TYPE_HOST); + usb_phy_init(s5p_ehci->phy); + } else if (s5p_ehci->pdata->phy_init) { + s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); + } +} + +static void s5p_ehci_phy_disable(struct s5p_ehci_hcd *s5p_ehci) +{ + struct platform_device *pdev = to_platform_device(s5p_ehci->dev); + + if (s5p_ehci->phy) { + samsung_usbphy_set_type(s5p_ehci->phy, USB_PHY_TYPE_HOST); + usb_phy_shutdown(s5p_ehci->phy); + } else if (s5p_ehci->pdata->phy_exit) { + s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); + } +} + +static void s5p_setup_hub_gpio(struct platform_device *pdev, const char *propname, int level) +{ + int err; + int gpio; + + if (!pdev->dev.of_node) + return; + + gpio = of_get_named_gpio(pdev->dev.of_node, propname, 0); + if (!gpio_is_valid(gpio)) + return; + + err = gpio_request_one(gpio, level, "ehci_vbus_gpio"); + + if (err) + dev_err(&pdev->dev, "can't request ehci hub-reset gpio %d", gpio); + else + gpio_free(gpio); +} + static void s5p_setup_vbus_gpio(struct platform_device *pdev) { int err; @@ -83,24 +131,19 @@ static void s5p_setup_vbus_gpio(struct platform_device *pdev) dev_err(&pdev->dev, "can't request ehci vbus gpio %d", gpio); } -static u64 ehci_s5p_dma_mask = DMA_BIT_MASK(32); +static u64 ehci_s5p_dma_mask = DMA_BIT_MASK(64); static int s5p_ehci_probe(struct platform_device *pdev) { - struct s5p_ehci_platdata *pdata; + struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; struct s5p_ehci_hcd *s5p_ehci; struct usb_hcd *hcd; struct ehci_hcd *ehci; struct resource *res; + struct usb_phy *phy; int irq; int err; - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "No platform data defined\n"); - return -EINVAL; - } - /* * Right now device-tree probed devices don't get dma_mask set. * Since shared usb code relies on it, set it here for now. @@ -109,7 +152,7 @@ static int s5p_ehci_probe(struct platform_device *pdev) if (!pdev->dev.dma_mask) pdev->dev.dma_mask = &ehci_s5p_dma_mask; if (!pdev->dev.coherent_dma_mask) - pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64); s5p_setup_vbus_gpio(pdev); @@ -118,6 +161,19 @@ static int s5p_ehci_probe(struct platform_device *pdev) if (!s5p_ehci) return -ENOMEM; + phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); + + if (IS_ERR_OR_NULL(phy)) { + /* Fallback to pdata */ + if (!pdata) { + dev_err(&pdev->dev, "no platform data or transceiver defined\n"); + return -EPROBE_DEFER; + } else { + s5p_ehci->pdata = pdata; + } + } else { + s5p_ehci->phy = phy; + } s5p_ehci->dev = &pdev->dev; hcd = usb_create_hcd(&s5p_ehci_hc_driver, &pdev->dev, @@ -163,8 +219,14 @@ static int s5p_ehci_probe(struct platform_device *pdev) goto fail_io; } - if (pdata->phy_init) - pdata->phy_init(pdev, S5P_USB_PHY_HOST); + s5p_setup_hub_gpio(pdev, "samsung,hub-reset", GPIOF_OUT_INIT_LOW); + s5p_setup_hub_gpio(pdev, "samsung,hub-connect", GPIOF_OUT_INIT_LOW); + + s5p_ehci_phy_enable(s5p_ehci); + + mdelay(1); + s5p_setup_hub_gpio(pdev, "samsung,hub-reset", GPIOF_OUT_INIT_HIGH); + s5p_setup_hub_gpio(pdev, "samsung,hub-connect", GPIOF_OUT_INIT_HIGH); ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; @@ -175,13 +237,15 @@ static int s5p_ehci_probe(struct platform_device *pdev) err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); - goto fail_io; + goto fail_add_hcd; } platform_set_drvdata(pdev, s5p_ehci); return 0; +fail_add_hcd: + s5p_ehci_phy_disable(s5p_ehci); fail_io: clk_disable_unprepare(s5p_ehci->clk); fail_clk: @@ -197,8 +261,7 @@ static int s5p_ehci_remove(struct platform_device *pdev) usb_remove_hcd(hcd); - if (pdata && pdata->phy_exit) - pdata->phy_exit(pdev, S5P_USB_PHY_HOST); + s5p_ehci_phy_disable(s5p_ehci); clk_disable_unprepare(s5p_ehci->clk); @@ -222,16 +285,12 @@ static int s5p_ehci_suspend(struct device *dev) struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); struct usb_hcd *hcd = s5p_ehci->hcd; bool do_wakeup = device_may_wakeup(dev); - struct platform_device *pdev = to_platform_device(dev); - struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; int rc; rc = ehci_suspend(hcd, do_wakeup); - if (pdata && pdata->phy_exit) - pdata->phy_exit(pdev, S5P_USB_PHY_HOST); - - clk_disable_unprepare(s5p_ehci->clk); + s5p_ehci_phy_disable(s5p_ehci); + clk_disable(s5p_ehci->clk); return rc; } @@ -240,14 +299,10 @@ static int s5p_ehci_resume(struct device *dev) { struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); struct usb_hcd *hcd = s5p_ehci->hcd; - struct platform_device *pdev = to_platform_device(dev); - struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; clk_prepare_enable(s5p_ehci->clk); - if (pdata && pdata->phy_init) - pdata->phy_init(pdev, S5P_USB_PHY_HOST); - + s5p_ehci_phy_enable(s5p_ehci); /* DMA burst Enable */ writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index aa3b8844bb9..643da7cf487 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -15,12 +15,16 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/platform_data/usb-exynos.h> +#include <linux/usb/phy.h> +#include <linux/usb/samsung_usb_phy.h> #include <plat/usb-phy.h> struct exynos_ohci_hcd { struct device *dev; struct usb_hcd *hcd; struct clk *clk; + struct usb_phy *phy; + struct exynos4_ohci_platdata *pdata; }; static int ohci_exynos_reset(struct usb_hcd *hcd) @@ -28,6 +32,30 @@ static int ohci_exynos_reset(struct usb_hcd *hcd) return ohci_init(hcd_to_ohci(hcd)); } +static void exynos_ohci_phy_enable(struct exynos_ohci_hcd *exynos_ohci) +{ + struct platform_device *pdev = to_platform_device(exynos_ohci->dev); + + if (exynos_ohci->phy) { + samsung_usbphy_set_type(exynos_ohci->phy, USB_PHY_TYPE_HOST); + usb_phy_init(exynos_ohci->phy); + } else if (exynos_ohci->pdata->phy_init) { + exynos_ohci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); + } +} + +static void exynos_ohci_phy_disable(struct exynos_ohci_hcd *exynos_ohci) +{ + struct platform_device *pdev = to_platform_device(exynos_ohci->dev); + + if (exynos_ohci->phy) { + samsung_usbphy_set_type(exynos_ohci->phy, USB_PHY_TYPE_HOST); + usb_phy_shutdown(exynos_ohci->phy); + } else if (exynos_ohci->pdata->phy_exit) { + exynos_ohci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); + } +} + static int ohci_exynos_start(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci(hcd); @@ -74,15 +102,16 @@ static const struct hc_driver exynos_ohci_hc_driver = { .start_port_reset = ohci_start_port_reset, }; -static u64 ohci_exynos_dma_mask = DMA_BIT_MASK(32); +static u64 ohci_exynos_dma_mask = DMA_BIT_MASK(64); static int exynos_ohci_probe(struct platform_device *pdev) { - struct exynos4_ohci_platdata *pdata; + struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; struct exynos_ohci_hcd *exynos_ohci; struct usb_hcd *hcd; struct ohci_hcd *ohci; struct resource *res; + struct usb_phy *phy; int irq; int err; @@ -100,13 +129,25 @@ static int exynos_ohci_probe(struct platform_device *pdev) if (!pdev->dev.dma_mask) pdev->dev.dma_mask = &ohci_exynos_dma_mask; if (!pdev->dev.coherent_dma_mask) - pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64); exynos_ohci = devm_kzalloc(&pdev->dev, sizeof(struct exynos_ohci_hcd), GFP_KERNEL); if (!exynos_ohci) return -ENOMEM; - + phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); + + if (IS_ERR_OR_NULL(phy)) { + /* Fallback to pdata */ + if (!pdata) { + dev_err(&pdev->dev, "no platform data or transceiver defined\n"); + return -EPROBE_DEFER; + } else { + exynos_ohci->pdata = pdata; + } + } else { + exynos_ohci->phy = phy; + } exynos_ohci->dev = &pdev->dev; hcd = usb_create_hcd(&exynos_ohci_hc_driver, &pdev->dev, @@ -152,22 +193,21 @@ static int exynos_ohci_probe(struct platform_device *pdev) goto fail_io; } - if (pdata->phy_init) - pdata->phy_init(pdev, S5P_USB_PHY_HOST); - + exynos_ohci_phy_enable(exynos_ohci); ohci = hcd_to_ohci(hcd); ohci_hcd_init(ohci); err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); - goto fail_io; + goto fail_add_hcd; } platform_set_drvdata(pdev, exynos_ohci); return 0; - +fail_add_hcd: + exynos_ohci_phy_disable(exynos_ohci); fail_io: clk_disable_unprepare(exynos_ohci->clk); fail_clk: @@ -177,16 +217,14 @@ fail_clk: static int exynos_ohci_remove(struct platform_device *pdev) { - struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev); struct usb_hcd *hcd = exynos_ohci->hcd; usb_remove_hcd(hcd); - if (pdata && pdata->phy_exit) - pdata->phy_exit(pdev, S5P_USB_PHY_HOST); - - clk_disable_unprepare(exynos_ohci->clk); + exynos_ohci_phy_disable(exynos_ohci); + clk_disable(exynos_ohci->clk); + clk_put(exynos_ohci->clk); usb_put_hcd(hcd); @@ -208,8 +246,6 @@ static int exynos_ohci_suspend(struct device *dev) struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev); struct usb_hcd *hcd = exynos_ohci->hcd; struct ohci_hcd *ohci = hcd_to_ohci(hcd); - struct platform_device *pdev = to_platform_device(dev); - struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; unsigned long flags; int rc = 0; @@ -228,10 +264,8 @@ static int exynos_ohci_suspend(struct device *dev) clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - if (pdata && pdata->phy_exit) - pdata->phy_exit(pdev, S5P_USB_PHY_HOST); - - clk_disable_unprepare(exynos_ohci->clk); + exynos_ohci_phy_disable(exynos_ohci); + clk_disable(exynos_ohci->clk); fail: spin_unlock_irqrestore(&ohci->lock, flags); @@ -243,13 +277,12 @@ static int exynos_ohci_resume(struct device *dev) { struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev); struct usb_hcd *hcd = exynos_ohci->hcd; - struct platform_device *pdev = to_platform_device(dev); - struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; clk_prepare_enable(exynos_ohci->clk); - if (pdata && pdata->phy_init) - pdata->phy_init(pdev, S5P_USB_PHY_HOST); + exynos_ohci_phy_enable(exynos_ohci); + /* Mark hardware accessible again as we are out of D3 state by now */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); ohci_resume(hcd, false); diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index df90fe51b4a..2d163d82153 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -93,7 +93,6 @@ static int xhci_plat_probe(struct platform_device *pdev) if (usb_disabled()) return -ENODEV; - driver = &xhci_plat_xhci_driver; irq = platform_get_irq(pdev, 0); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index f1f01a834ba..3434bd06fa4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4606,7 +4606,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) struct device *dev = hcd->self.controller; int retval; u32 temp; - /* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0; /* XHCI controllers don't stop the ep queue on short packets :| */ @@ -4699,7 +4698,6 @@ MODULE_LICENSE("GPL"); static int __init xhci_hcd_init(void) { int retval; - retval = xhci_register_pci(); if (retval < 0) { printk(KERN_DEBUG "Problem registering PCI driver."); diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index a30c0411511..29a45d7fb4b 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -28,10 +28,8 @@ static struct usb_phy *__usb_find_phy(struct list_head *list, list_for_each_entry(phy, list, head) { if (phy->type != type) continue; - return phy; } - return ERR_PTR(-ENODEV); } @@ -65,7 +63,6 @@ struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type) ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return NULL; - phy = usb_get_phy(type); if (!IS_ERR(phy)) { *ptr = phy; diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 5de6e7f39f9..989cbb8a7ac 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -45,3 +45,10 @@ config USB_RCAR_PHY To compile this driver as a module, choose M here: the module will be called rcar-phy. + +config SAMSUNG_USBPHY + bool "Samsung USB PHY controller Driver" + select USB_OTG_UTILS + help + Enable this to support Samsung USB phy controller for samsung + SoCs. diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 1a579a860a0..ec304f64240 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_USB_ISP1301) += isp1301.o obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o obj-$(CONFIG_USB_RCAR_PHY) += rcar-phy.o +obj-$(CONFIG_SAMSUNG_USBPHY) += samsung-usbphy.o diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c new file mode 100644 index 00000000000..c790c8916f0 --- /dev/null +++ b/drivers/usb/phy/samsung-usbphy.c @@ -0,0 +1,1004 @@ +/* +copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Praveen Paneri <p.paneri@samsung.com> + * + * Samsung USB-PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and + * OHCI-EXYNOS controllers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/usb/samsung_usb_phy.h> +#include <linux/platform_data/samsung-usbphy.h> + +/* Register definitions */ + +#define SAMSUNG_PHYPWR (0x00) + +#define PHYPWR_NORMAL_MASK (0x19 << 0) +#define PHYPWR_OTG_DISABLE (0x1 << 4) +#define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) +#define PHYPWR_FORCE_SUSPEND (0x1 << 1) +/* For Exynos4 */ +#define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0) +#define PHYPWR_SLEEP_PHY0 (0x1 << 5) + +#define SAMSUNG_PHYCLK (0x04) + +#define PHYCLK_MODE_USB11 (0x1 << 6) +#define PHYCLK_EXT_OSC (0x1 << 5) +#define PHYCLK_COMMON_ON_N (0x1 << 4) +#define PHYCLK_ID_PULL (0x1 << 2) +#define PHYCLK_CLKSEL_MASK (0x3 << 0) +#define PHYCLK_CLKSEL_48M (0x0 << 0) +#define PHYCLK_CLKSEL_12M (0x2 << 0) +#define PHYCLK_CLKSEL_24M (0x3 << 0) + +#define SAMSUNG_RSTCON (0x08) + +#define RSTCON_PHYLINK_SWRST (0x1 << 2) +#define RSTCON_HLINK_SWRST (0x1 << 1) +#define RSTCON_SWRST (0x1 << 0) +/* EXYNOS5 */ +#define EXYNOS5_PHY_HOST_CTRL0 (0x00) + +#define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) + +#define HOST_CTRL0_REFCLKSEL_MASK (0x3) +#define HOST_CTRL0_REFCLKSEL(_x) ((_x) << 19) +#define HOST_CTRL0_REFCLKSEL_XTAL \ + HOST_CTRL0_REFCLKSEL(0x0) +#define HOST_CTRL0_REFCLKSEL_EXTL \ + HOST_CTRL0_REFCLKSEL(0x1) +#define HOST_CTRL0_REFCLKSEL_CLKCORE \ + HOST_CTRL0_REFCLKSEL(0x2) + +#define HOST_CTRL0_FSEL_MASK (0x7 << 16) +#define HOST_CTRL0_FSEL(_x) ((_x) << 16) +#define HOST_CTRL0_FSEL_CLKSEL_50M (0x7) +#define HOST_CTRL0_FSEL_CLKSEL_24M (0x5) +#define HOST_CTRL0_FSEL_CLKSEL_20M (0x4) +#define HOST_CTRL0_FSEL_CLKSEL_19200K (0x3) +#define HOST_CTRL0_FSEL_CLKSEL_12M (0x2) +#define HOST_CTRL0_FSEL_CLKSEL_10M (0x1) +#define HOST_CTRL0_FSEL_CLKSEL_9600K (0x0) + +#define HOST_CTRL0_TESTBURNIN (0x1 << 11) +#define HOST_CTRL0_RETENABLE (0x1 << 10) +#define HOST_CTRL0_COMMONON_N (0x1 << 9) +#define HOST_CTRL0_SIDDQ (0x1 << 6) +#define HOST_CTRL0_FORCESLEEP (0x1 << 5) +#define HOST_CTRL0_FORCESUSPEND (0x1 << 4) +#define HOST_CTRL0_WORDINTERFACE (0x1 << 3) +#define HOST_CTRL0_UTMISWRST (0x1 << 2) +#define HOST_CTRL0_LINKSWRST (0x1 << 1) +#define HOST_CTRL0_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_TUNE0 (0x04) + +#define EXYNOS5_PHY_HSIC_CTRL1 (0x10) + +#define EXYNOS5_PHY_HSIC_TUNE1 (0x14) + +#define EXYNOS5_PHY_HSIC_CTRL2 (0x20) + +#define EXYNOS5_PHY_HSIC_TUNE2 (0x24) + +#define HSIC_CTRL_REFCLKSEL_MASK (0x3) +#define HSIC_CTRL_REFCLKSEL (0x2 << 23) + +#define HSIC_CTRL_REFCLKDIV_MASK (0x7f) +#define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) +#define HSIC_CTRL_REFCLKDIV_12 \ + HSIC_CTRL_REFCLKDIV(0x24) +#define HSIC_CTRL_REFCLKDIV_15 \ + HSIC_CTRL_REFCLKDIV(0x1C) +#define HSIC_CTRL_REFCLKDIV_16 \ + HSIC_CTRL_REFCLKDIV(0x1A) +#define HSIC_CTRL_REFCLKDIV_19_2 \ + HSIC_CTRL_REFCLKDIV(0x15) +#define HSIC_CTRL_REFCLKDIV_20 \ + HSIC_CTRL_REFCLKDIV(0x14) + +#define HSIC_CTRL_SIDDQ (0x1 << 6) +#define HSIC_CTRL_FORCESLEEP (0x1 << 5) +#define HSIC_CTRL_FORCESUSPEND (0x1 << 4) +#define HSIC_CTRL_WORDINTERFACE (0x1 << 3) +#define HSIC_CTRL_UTMISWRST (0x1 << 2) +#define HSIC_CTRL_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_EHCICTRL (0x30) + +#define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) +#define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) +#define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) +#define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) + +#define EXYNOS5_PHY_HOST_OHCICTRL (0x34) + +#define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) +#define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) +#define HOST_OHCICTRL_CNTSEL (0x1 << 1) +#define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) + +#define EXYNOS5_PHY_OTG_SYS (0x38) + +#define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) +#define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) +#define OTG_SYS_PHY0_SWRST (0x1 << 12) + +#define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) +#define OTG_SYS_REFCLKSEL(_x) ((_x) << 9) +#define OTG_SYS_REFCLKSEL_XTAL \ + OTG_SYS_REFCLKSEL(0x0) +#define OTG_SYS_REFCLKSEL_EXTL \ + OTG_SYS_REFCLKSEL(0x1) +#define OTG_SYS_REFCLKSEL_CLKCORE \ + OTG_SYS_REFCLKSEL(0x2) + +#define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) +#define OTG_SYS_COMMON_ON (0x1 << 7) + +#define OTG_SYS_FSEL_MASK (0x7 << 4) +#define OTG_SYS_FSEL(_x) ((_x) << 4) + +#define OTG_SYS_FORCESLEEP (0x1 << 3) +#define OTG_SYS_OTGDISABLE (0x1 << 2) +#define OTG_SYS_SIDDQ_UOTG (0x1 << 1) +#define OTG_SYS_FORCESUSPEND (0x1 << 0) + +#define EXYNOS5_PHY_OTG_TUNE (0x40) + +/* USB 3.0: DRD */ +#define EXYNOS5_DRD_LINKSYSTEM (0x04) + +#define LINKSYSTEM_FLADJ_MASK (0x3f << 1) +#define LINKSYSTEM_FLADJ(_x) ((_x) << 1) +#define LINKSYSTEM_XHCI_VERSION_CONTROL (1 << 27) + +#define EXYNOS5_DRD_PHYUTMI (0x08) + +#define PHYUTMI_OTGDISABLE (1 << 6) +#define PHYUTMI_FORCESUSPEND (1 << 1) +#define PHYUTMI_FORCESLEEP (1 << 0) + +#define EXYNOS5_DRD_PHYPIPE (0x0C) + +#define EXYNOS5_DRD_PHYCLKRST (0x10) + +#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) +#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23) + +#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) +#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21) + +#define PHYCLKRST_SSC_EN (1 << 20) +#define PHYCLKRST_REF_SSP_EN (1 << 19) +#define PHYCLKRST_REF_CLKDIV2 (1 << 18) + +#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11) +#define PHYCLKRST_MPLL_MULTIPLIER(_x) ((_x) << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF \ + PHYCLKRST_MPLL_MULTIPLIER(0x19) +#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF \ + PHYCLKRST_MPLL_MULTIPLIER(0x02) +#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF \ + PHYCLKRST_MPLL_MULTIPLIER(0x68) +#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF \ + PHYCLKRST_MPLL_MULTIPLIER(0x7d) +#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF \ + PHYCLKRST_MPLL_MULTIPLIER(0x02) + +#define PHYCLKRST_FSEL_MASK (0x3f << 5) +#define PHYCLKRST_FSEL(_x) ((_x) << 5) +#define PHYCLKRST_FSEL_PAD_100MHZ \ + PHYCLKRST_FSEL(0x27) +#define PHYCLKRST_FSEL_PAD_24MHZ \ + PHYCLKRST_FSEL(0x2a) +#define PHYCLKRST_FSEL_PAD_20MHZ \ ++ PHYCLKRST_FSEL(0x31) +#define PHYCLKRST_FSEL_PAD_19_2MHZ \ + PHYCLKRST_FSEL(0x38) + +#define PHYCLKRST_RETENABLEN (1 << 4) + +#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) +#define PHYCLKRST_REFCLKSEL(_x) ((_x) << 2) +#define PHYCLKRST_REFCLKSEL_PAD_REFCLK \ + PHYCLKRST_REFCLKSEL(2) +#define PHYCLKRST_REFCLKSEL_EXT_REFCLK \ + PHYCLKRST_REFCLKSEL(3) + +#define PHYCLKRST_PORTRESET (1 << 1) +#define PHYCLKRST_COMMONONN (1 << 0) + +#define EXYNOS5_DRD_PHYREG0 (0x14) +#define EXYNOS5_DRD_PHYREG1 (0x18) + +#define EXYNOS5_DRD_PHYPARAM0 (0x1C) + +#define PHYPARAM0_REF_USE_PAD (0x1 << 31) +#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) +#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) + +#define EXYNOS5_DRD_PHYPARAM1 (0x20) + +#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) +#define PHYPARAM1_PCS_TXDEEMPH (0x1C) + +#define EXYNOS5_DRD_PHYTERM (0x24) + +#define EXYNOS5_DRD_PHYTEST (0x28) + +#define PHYTEST_POWERDOWN_SSP (1 << 3) +#define PHYTEST_POWERDOWN_HSP (1 << 2) + +#define EXYNOS5_DRD_PHYADP (0x2C) + +#define EXYNOS5_DRD_PHYBATCHG (0x30) + +#define PHYBATCHG_UTMI_CLKSEL (0x1 << 2) + +#define EXYNOS5_DRD_PHYRESUME (0x34) +#define EXYNOS5_DRD_LINKPORT (0x44) + +#ifndef MHZ +#define MHZ (1000*1000) +#endif + +enum samsung_cpu_type { + TYPE_S3C64XX, + TYPE_EXYNOS4210, + TYPE_EXYNOS5250, +}; + +/* + * struct samsung_usbphy - transceiver driver state + * @phy: transceiver structure + * @phy3: transceiver structure for USB 3.0 + * @plat: platform data + * @dev: The parent device supplied to the probe function + * @clk: usb phy clock + * @regs: usb phy register memory base + * @regs_phy3: usb 3.0 phy register memory base + * @ref_clk_freq: reference clock frequency selection + * @cpu_type: machine identifier + * @phy_type: It keeps track of the PHY type. + * @host_usage: host_phy usage count. +*/ +struct samsung_usbphy { + struct usb_phy phy; + struct usb_phy phy3; + struct samsung_usbphy_data *plat; + struct device *dev; + struct clk *clk; + void __iomem *regs; + void __iomem *regs_phy3; + int ref_clk_freq; + int cpu_type; + enum samsung_usb_phy_type phy_type; + atomic_t host_usage; +}; + +#define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) +#define phy3_to_sphy(x) container_of((x), struct samsung_usbphy, phy3) +/* ++ * PHYs are different for USB Device and USB Host. Controllers can make ++ * sure that the correct PHY type is selected by calling this function ++ * before any PHY operation. ++ */ +int samsung_usbphy_set_type(struct usb_phy *phy, + enum samsung_usb_phy_type phy_type) +{ + struct samsung_usbphy *sphy = phy_to_sphy(phy); + + if (sphy->phy_type != phy_type) + sphy->phy_type = phy_type; + + return 0; +} + +/* + * Returns reference clock frequency selection value + */ +static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) +{ + struct clk *ref_clk; + int refclk_freq = 0; + + if (sphy->cpu_type == TYPE_EXYNOS5250) + ref_clk = clk_get(sphy->dev, "ext_xtal"); + else + ref_clk = clk_get(sphy->dev, "xusbxti"); + if (IS_ERR(ref_clk)) { + dev_err(sphy->dev, "Failed to get reference clock\n"); + return PTR_ERR(ref_clk); + } + if (sphy->cpu_type == TYPE_EXYNOS5250) { + /* set clock frequency for PLL */ + switch (clk_get_rate(ref_clk)) { + case 96 * 100000: + refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_9600K; + break; + case 10 * MHZ: + refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_10M; + break; + case 12 * MHZ: + refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_12M; + break; + case 192 * 100000: + refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_19200K; + break; + case 20 * MHZ: + refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_20M; + break; + case 50 * MHZ: + refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_50M; + break; + case 24 * MHZ: + default: + /* default reference clock */ + refclk_freq |= HOST_CTRL0_FSEL_CLKSEL_24M; + } + } else { + switch (clk_get_rate(ref_clk)) { + case 12 * MHZ: + refclk_freq = PHYCLK_CLKSEL_12M; + break; + case 24 * MHZ: + refclk_freq = PHYCLK_CLKSEL_24M; + break; + case 48 * MHZ: + refclk_freq = PHYCLK_CLKSEL_48M; + break; + default: + if (sphy->cpu_type == TYPE_S3C64XX) + refclk_freq = PHYCLK_CLKSEL_48M; + else + refclk_freq = PHYCLK_CLKSEL_24M; + break; + } + } + clk_put(ref_clk); + + return refclk_freq; +} + + /* + * Sets the phy clk as EXTREFCLK (XXTI) which is internal clock form clock core. + */ +static u32 exynos5_usbphy3_set_clock(struct samsung_usbphy *sphy) +{ + u32 reg; + u32 refclk; + + refclk = sphy->ref_clk_freq; + + reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK | + PHYCLKRST_FSEL(refclk); + + switch (refclk) { + case HOST_CTRL0_FSEL_CLKSEL_50M: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF | + PHYCLKRST_SSC_REFCLKSEL(0x00)); + break; + case HOST_CTRL0_FSEL_CLKSEL_20M: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x00)); + break; + case HOST_CTRL0_FSEL_CLKSEL_19200K: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x88)); + break; + case HOST_CTRL0_FSEL_CLKSEL_24M: + default: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x88)); + break; + } + + return reg; +} + +static int exynos5_phyhost_is_on(void *regs) +{ + return (readl(regs + EXYNOS5_PHY_HOST_CTRL0) & + HOST_CTRL0_SIDDQ) ? 0 : 1; +} + +static int samsung_exynos5_usbphy3_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs_phy3; + u32 phyparam0; + u32 phyparam1; + u32 linksystem; + u32 phybatchg; + u32 phytest; + u32 phyclkrst; + /* Reset USB 3.0 PHY */ + writel(0x0, regs + EXYNOS5_DRD_PHYREG0); + + phyparam0 = readl(regs + EXYNOS5_DRD_PHYPARAM0); + /* Select PHY CLK source */ + phyparam0 &= ~PHYPARAM0_REF_USE_PAD; + /* Set Loss-of-Signal Detector sensitivity */ + phyparam0 &= ~PHYPARAM0_REF_LOSLEVEL_MASK; + phyparam0 |= PHYPARAM0_REF_LOSLEVEL; + writel(phyparam0, regs + EXYNOS5_DRD_PHYPARAM0); + + writel(0x0, regs + EXYNOS5_DRD_PHYRESUME); + + /* + * Setting the Frame length Adj value[6:1] to default 0x20 + * See xHCI 1.0 spec, 5.2.4 + */ + linksystem = LINKSYSTEM_XHCI_VERSION_CONTROL | + LINKSYSTEM_FLADJ(0x20); + writel(linksystem, regs + EXYNOS5_DRD_LINKSYSTEM); + + phyparam1 = readl(regs + EXYNOS5_DRD_PHYPARAM1); + /* Set Tx De-Emphasis level */ + phyparam1 &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; + phyparam1 |= PHYPARAM1_PCS_TXDEEMPH; + writel(phyparam1, regs + EXYNOS5_DRD_PHYPARAM1); + + phybatchg = readl(regs + EXYNOS5_DRD_PHYBATCHG); + phybatchg |= PHYBATCHG_UTMI_CLKSEL; + writel(phybatchg, regs + EXYNOS5_DRD_PHYBATCHG); + + /* PHYTEST POWERDOWN Control */ + phytest = readl(regs + EXYNOS5_DRD_PHYTEST); + phytest &= ~(PHYTEST_POWERDOWN_SSP | + PHYTEST_POWERDOWN_HSP); + writel(phytest, regs + EXYNOS5_DRD_PHYTEST); + + /* UTMI Power Control */ + writel(PHYUTMI_OTGDISABLE, regs + EXYNOS5_DRD_PHYUTMI); + + phyclkrst = exynos5_usbphy3_set_clock(sphy); + + phyclkrst |= PHYCLKRST_PORTRESET | + /* Digital power supply in normal operating mode */ + PHYCLKRST_RETENABLEN | + /* Enable ref clock for SS function */ + PHYCLKRST_REF_SSP_EN | + /* Enable spread spectrum */ + PHYCLKRST_SSC_EN | + /* Power down HS Bias and PLL blocks in suspend mode */ + PHYCLKRST_COMMONONN; + + writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); + + udelay(10); + + phyclkrst &= ~(PHYCLKRST_PORTRESET); + writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); + + return 0; +} + +static void samsung_exynos5_usbphy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyclk = sphy->ref_clk_freq; + u32 phyhost; + u32 phyotg; + u32 phyhsic; + u32 ehcictrl; + u32 ohcictrl; + + atomic_inc(&sphy->host_usage); + + if (exynos5_phyhost_is_on(regs)) { + dev_info(sphy->dev, "Already power on PHY\n"); + return; + } + /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ + if (sphy->plat && sphy->plat->phy_cfg_sel) + sphy->plat->phy_cfg_sel(sphy->dev, USB_PHY_TYPE_HOST); + + /* Host configuration */ + phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + + /* phy reference clock configuration */ + phyhost &= ~HOST_CTRL0_FSEL_MASK; + phyhost |= HOST_CTRL0_FSEL(phyclk); + + /* host phy reset */ + phyhost &= ~(HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL | + HOST_CTRL0_SIDDQ | + /* Enable normal mode of operation */ + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP); + + /* Link reset */ + phyhost |= (HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST | + /* COMMON Block configuration during suspend */ + HOST_CTRL0_COMMONON_N); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + udelay(10); + phyhost &= ~(HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + + /* OTG configuration */ + phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); + + /* phy reference clock configuration */ + phyotg &= ~OTG_SYS_FSEL_MASK; + phyotg |= OTG_SYS_FSEL(phyclk); + + /* Enable normal mode of operation */ + phyotg &= ~(OTG_SYS_FORCESUSPEND | + OTG_SYS_SIDDQ_UOTG | + OTG_SYS_FORCESLEEP | + OTG_SYS_REFCLKSEL_MASK | + /* COMMON Block configuration during suspend */ + OTG_SYS_COMMON_ON); + + /* OTG phy & link reset */ + phyotg |= (OTG_SYS_PHY0_SWRST | + OTG_SYS_LINKSWRST_UOTG | + OTG_SYS_PHYLINK_SWRESET | + OTG_SYS_OTGDISABLE | + /* Set phy refclk */ + OTG_SYS_REFCLKSEL_CLKCORE); + + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); + udelay(10); + phyotg &= ~(OTG_SYS_PHY0_SWRST | + OTG_SYS_LINKSWRST_UOTG | + OTG_SYS_PHYLINK_SWRESET); + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); + + /* HSIC phy configuration */ + phyhsic = (HSIC_CTRL_REFCLKDIV_12 | + HSIC_CTRL_REFCLKSEL | + HSIC_CTRL_PHYSWRST); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + udelay(10); + phyhsic &= ~HSIC_CTRL_PHYSWRST; + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + + udelay(80); + + /* enable EHCI DMA burst */ + ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); + ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | + HOST_EHCICTRL_ENAINCR4 | + HOST_EHCICTRL_ENAINCR8 | + HOST_EHCICTRL_ENAINCR16); + writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); + + /* set ohci_suspend_on_n */ + ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); + ohcictrl |= HOST_OHCICTRL_SUSPLGCY; + writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); +} + +static void samsung_exynos5_usbphy3_disable(struct samsung_usbphy *sphy) +{ + u32 phyutmi; + u32 phyclkrst; + u32 phytest; + void __iomem *regs = sphy->regs_phy3; + + phyutmi = PHYUTMI_OTGDISABLE | + PHYUTMI_FORCESUSPEND | + PHYUTMI_FORCESLEEP; + writel(phyutmi, regs + EXYNOS5_DRD_PHYUTMI); + + /* Resetting the PHYCLKRST enable bits to reduce leakage current */ + phyclkrst = readl(regs + EXYNOS5_DRD_PHYCLKRST); + phyclkrst &= ~(PHYCLKRST_REF_SSP_EN | + PHYCLKRST_SSC_EN | + PHYCLKRST_COMMONONN); + writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); + + /* Control PHYTEST to remove leakage current */ + phytest = readl(regs + EXYNOS5_DRD_PHYTEST); + phytest |= (PHYTEST_POWERDOWN_SSP | + PHYTEST_POWERDOWN_HSP); + writel(phytest, regs + EXYNOS5_DRD_PHYTEST); +} + +static void samsung_exynos5_usbphy_disable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyhost; + u32 phyotg; + u32 phyhsic; + + if (atomic_dec_return(&sphy->host_usage) > 0) { + dev_info(sphy->dev, "still being used\n"); + return; + } + + phyhsic = (HSIC_CTRL_REFCLKDIV_12 | + HSIC_CTRL_REFCLKSEL | + HSIC_CTRL_SIDDQ | + HSIC_CTRL_FORCESLEEP | + HSIC_CTRL_FORCESUSPEND); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + + phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + phyhost |= (HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP | + HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + + phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); + phyotg |= (OTG_SYS_FORCESUSPEND | + OTG_SYS_SIDDQ_UOTG | + OTG_SYS_FORCESLEEP); + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); +} + +static int samsung_usbphy3_init(struct usb_phy *phy3) +{ + struct samsung_usbphy *sphy; + int ret = 0; + + sphy = phy3_to_sphy(phy3); + + if (sphy->cpu_type != TYPE_EXYNOS5250) { + dev_err(sphy->dev, "Not a valid cpu_type for USB 3.0\n"); + return -ENODEV; + } + /* setting default phy-type for USB 3.0 */ + samsung_usbphy_set_type(&sphy->phy3, USB_PHY_TYPE_DRD); + + /* Enable the phy clock */ + ret = clk_prepare_enable(sphy->clk); + if (ret) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return ret; + } + + /* Disable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(false, sphy->phy_type); + + /* Initialize usb phy registers */ + samsung_exynos5_usbphy3_enable(sphy); + + /* Disable the phy clock */ + clk_disable_unprepare(sphy->clk); + + return ret; +} + +/* + * The function passed to the usb driver for phy shutdown + */ +static void samsung_usbphy3_shutdown(struct usb_phy *phy3) +{ + struct samsung_usbphy *sphy; + + sphy = phy3_to_sphy(phy3); + + if (sphy->cpu_type != TYPE_EXYNOS5250) { + dev_err(sphy->dev, "Not a valid cpu_type for USB 3.0\n"); + return; + } + + /* setting default phy-type for USB 3.0 */ + samsung_usbphy_set_type(&sphy->phy3, USB_PHY_TYPE_DRD); + + if (clk_prepare_enable(sphy->clk)) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return; + } + + /* De-initialize usb phy registers */ + samsung_exynos5_usbphy3_disable(sphy); + + /* Enable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(true, sphy->phy_type); + + clk_disable_unprepare(sphy->clk); +} + +static void samsung_usbphy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + u32 phyclk; + u32 rstcon; + + /* set clock frequency for PLL */ + phyclk = sphy->ref_clk_freq; + phypwr = readl(regs + SAMSUNG_PHYPWR); + rstcon = readl(regs + SAMSUNG_RSTCON); + + switch (sphy->cpu_type) { + case TYPE_S3C64XX: + phyclk &= ~PHYCLK_COMMON_ON_N; + phypwr &= ~PHYPWR_NORMAL_MASK; + rstcon |= RSTCON_SWRST; + break; + case TYPE_EXYNOS4210: + phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; + rstcon |= RSTCON_SWRST; + default: + break; + } + + writel(phyclk, regs + SAMSUNG_PHYCLK); + /* set to normal of PHY0 */ + writel(phypwr, regs + SAMSUNG_PHYPWR); + /* reset all ports of PHY and Link */ + writel(rstcon, regs + SAMSUNG_RSTCON); + udelay(10); + rstcon &= ~RSTCON_SWRST; + writel(rstcon, regs + SAMSUNG_RSTCON); +} + +static void samsung_usbphy_disable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + + phypwr = readl(regs + SAMSUNG_PHYPWR); + + switch (sphy->cpu_type) { + case TYPE_S3C64XX: + phypwr |= PHYPWR_NORMAL_MASK; + break; + case TYPE_EXYNOS4210: + phypwr |= PHYPWR_NORMAL_MASK_PHY0; + default: + break; + } + + /* unset to normal of PHY0 */ + writel(phypwr, regs + SAMSUNG_PHYPWR); +} + +/* + * The function passed to the usb driver for phy initialization + */ +static int samsung_usbphy_init(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + int ret = 0; + + sphy = phy_to_sphy(phy); + + /* Enable the phy clock */ + ret = clk_prepare_enable(sphy->clk); + if (ret) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return ret; + } + + /* Disable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(false, sphy->phy_type); + + /* Initialize usb phy registers */ + if (sphy->cpu_type == TYPE_EXYNOS5250) + samsung_exynos5_usbphy_enable(sphy); + else + samsung_usbphy_enable(sphy); + /* Disable the phy clock */ + clk_disable_unprepare(sphy->clk); + return ret; +} + +/* ++ * The function passed to the usb driver for phy shutdown ++ */ +static void samsung_usbphy_shutdown(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + + sphy = phy_to_sphy(phy); + + if (clk_prepare_enable(sphy->clk)) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return; + } + + /* De-initialize usb phy registers */ + if (sphy->cpu_type == TYPE_EXYNOS5250) + samsung_exynos5_usbphy_disable(sphy); + else + samsung_usbphy_disable(sphy); + + /* Enable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(true, sphy->phy_type); + + clk_disable_unprepare(sphy->clk); +} + +static const struct of_device_id samsung_usbphy_dt_match[]; + +static inline int samsung_usbphy_get_driver_data(struct platform_device *pdev) +{ + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { + int data; + const struct of_device_id *match; + match = of_match_node(samsung_usbphy_dt_match, + pdev->dev.of_node); + data = (int) match->data; + return data; + } + + return platform_get_device_id(pdev)->driver_data; +} + +static int samsung_usbphy_probe(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy; + struct samsung_usbphy_data *pdata; + struct device *dev = &pdev->dev; + struct resource *phy_mem; + void __iomem *phy_base; + struct resource *phy3_mem; + void __iomem *phy3_base = NULL; + struct clk *clk; + int ret = 0; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "%s: no platform data defined\n", __func__); + return -EINVAL; + } + phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!phy_mem) { + dev_err(dev, "%s: missing mem resource\n", __func__); + return -ENODEV; + } + + phy_base = devm_request_and_ioremap(dev, phy_mem); + if (!phy_base) { + dev_err(dev, "%s: register mapping failed\n", __func__); + return -ENXIO; + } + + sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); + if (!sphy) + return -ENOMEM; + + sphy->dev = &pdev->dev; + sphy->plat = pdata; + sphy->regs = phy_base; + sphy->phy.dev = sphy->dev; + sphy->phy.label = "samsung-usbphy"; + sphy->phy.init = samsung_usbphy_init; + sphy->phy.shutdown = samsung_usbphy_shutdown; + sphy->cpu_type = samsung_usbphy_get_driver_data(pdev); + sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); + platform_set_drvdata(pdev, sphy); + + if (sphy->cpu_type == TYPE_EXYNOS5250) + clk = devm_clk_get(dev, "usbhost"); + else + clk = devm_clk_get(dev, "otg"); + if (IS_ERR(clk)) { + dev_err(dev, "Failed to get otg clock\n"); + return PTR_ERR(clk); + } + + sphy->clk = clk; + + if (sphy->cpu_type == TYPE_EXYNOS5250) { + phy3_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!phy3_mem) { + dev_err(dev, "%s: missing mem resource\n", __func__); + return -ENODEV; + } + phy3_base = devm_request_and_ioremap(dev, phy3_mem); + if (!phy3_base) { + dev_err(dev, "%s: register mapping failed\n", __func__); + return -ENXIO; + } + } + + sphy->regs_phy3 = phy3_base; + sphy->phy3.dev = sphy->dev; + sphy->phy3.label = "samsung-usbphy3"; + sphy->phy3.init = samsung_usbphy3_init; + sphy->phy3.shutdown = samsung_usbphy3_shutdown; + + ret = usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); + + if (ret) + return ret; + + if (sphy->cpu_type != TYPE_EXYNOS5250) { + dev_warn(dev, "Not a valid cpu_type for USB 3.0\n"); + } else { + ret = usb_add_phy(&sphy->phy3, USB_PHY_TYPE_USB3); + if (ret) + return ret; + } + return ret; +} + +static int __exit samsung_usbphy_remove(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy = platform_get_drvdata(pdev); + + usb_remove_phy(&sphy->phy); + if (sphy->cpu_type == TYPE_EXYNOS5250) + usb_remove_phy(&sphy->phy3); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id samsung_usbphy_dt_match[] = { + { + .compatible = "samsung,s3c64xx-usbphy", + .data = (void *)TYPE_S3C64XX, + }, { + .compatible = "samsung,exynos4210-usbphy", + .data = (void *)TYPE_EXYNOS4210, + },{ + .compatible = "samsung,exynos5250-usbphy", + .data = (void *)TYPE_EXYNOS5250, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); +#else +#define samsung_usbphy_dt_match NULL +#endif + +static struct platform_device_id samsung_usbphy_driver_ids[] = { + { + .name = "s3c64xx-usbphy", + .driver_data = TYPE_S3C64XX, + }, { + .name = "exynos4210-usbphy", + .driver_data = TYPE_EXYNOS4210, + }, + {}, +}; + +MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); + +static struct platform_driver samsung_usbphy_driver = { + .probe = samsung_usbphy_probe, + .remove = samsung_usbphy_remove, + .id_table = samsung_usbphy_driver_ids, + .driver = { + //.name = "samsung-usbphy", + .name = "exynos5250-usbphy", + .owner = THIS_MODULE, + .of_match_table = samsung_usbphy_dt_match, + }, +}; + +module_platform_driver(samsung_usbphy_driver); + +MODULE_DESCRIPTION("Samsung USB phy controller"); +MODULE_AUTHOR("Praveen Paneri <p.paneri@samsung.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:samsung-usbphy"); |