From 3391ca1dcd70a8e958984f7e95f242d36f0b9ab8 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Mon, 25 Jun 2018 15:35:18 +0800 Subject: USB: serial: cast sizeof() to int when comparing with error code Negative error code will be larger than sizeof(). Note that none of these bugs prevent errors from being detected, even if the ir-usb one would cause a less precise debug message to printed. Signed-off-by: Chengguang Xu [ johan: add comment about implications ] Signed-off-by: Johan Hovold --- drivers/usb/serial/ir-usb.c | 2 +- drivers/usb/serial/quatech2.c | 2 +- drivers/usb/serial/ssu100.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 24b06c7e5e2d..7643716b5299 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -132,7 +132,7 @@ irda_usb_find_class_desc(struct usb_serial *serial, unsigned int ifnum) 0, ifnum, desc, sizeof(*desc), 1000); dev_dbg(&serial->dev->dev, "%s - ret=%d\n", __func__, ret); - if (ret < sizeof(*desc)) { + if (ret < (int)sizeof(*desc)) { dev_dbg(&serial->dev->dev, "%s - class descriptor read %s (%d)\n", __func__, (ret < 0) ? "failed" : "too short", ret); diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 958e12e1e7c7..ff2322ea5e14 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -194,7 +194,7 @@ static inline int qt2_getregister(struct usb_device *dev, ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), QT_SET_GET_REGISTER, 0xc0, reg, uart, data, sizeof(*data), QT2_USB_TIMEOUT); - if (ret < sizeof(*data)) { + if (ret < (int)sizeof(*data)) { if (ret >= 0) ret = -EIO; } diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 2083c267787b..0900b47b5f57 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -104,7 +104,7 @@ static inline int ssu100_getregister(struct usb_device *dev, ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), QT_SET_GET_REGISTER, 0xc0, reg, uart, data, sizeof(*data), 300); - if (ret < sizeof(*data)) { + if (ret < (int)sizeof(*data)) { if (ret >= 0) ret = -EIO; } -- cgit v1.2.3 From 379cacc5e566f7197bdeb1ea3e99219d3e880c0a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 8 Jun 2018 16:59:37 -0400 Subject: USB: Report wakeup events on root-hub ports When a USB device attached to a root-hub port sends a wakeup request to a sleeping system, we do not report the wakeup event to the PM core. This is because a system resume involves waking up all suspended USB ports as quickly as possible; without the normal USB_RESUME_TIMEOUT delay, the host controller driver doesn't set the USB_PORT_STAT_C_SUSPEND flag and so usb_port_resume() doesn't realize that a wakeup request was received. However, some environments (such as Chrome OS) want to have all wakeup events reported so they can be ascribed to the appropriate device. To accommodate these environments, this patch adds a new routine to the hub driver and a corresponding new HCD method to be used when a root hub resumes. The HCD method returns a bitmap of ports that have initiated a wakeup signal but not yet completed resuming. The hub driver can then report to the PM core that the child devices attached to these ports initiated a wakeup event. Signed-off-by: Alan Stern Suggested-by: Anshuman Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 42 ++++++++++++++++++++++++++++++++++++++++++ include/linux/usb/hcd.h | 1 + 2 files changed, 43 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index fcae521df29b..fef5af7aab92 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3656,12 +3656,54 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) return 0; } +/* Report wakeup requests from the ports of a resuming root hub */ +static void report_wakeup_requests(struct usb_hub *hub) +{ + struct usb_device *hdev = hub->hdev; + struct usb_device *udev; + struct usb_hcd *hcd; + unsigned long resuming_ports; + int i; + + if (hdev->parent) + return; /* Not a root hub */ + + hcd = bus_to_hcd(hdev->bus); + if (hcd->driver->get_resuming_ports) { + + /* + * The get_resuming_ports() method returns a bitmap (origin 0) + * of ports which have started wakeup signaling but have not + * yet finished resuming. During system resume we will + * resume all the enabled ports, regardless of any wakeup + * signals, which means the wakeup requests would be lost. + * To prevent this, report them to the PM core here. + */ + resuming_ports = hcd->driver->get_resuming_ports(hcd); + for (i = 0; i < hdev->maxchild; ++i) { + if (test_bit(i, &resuming_ports)) { + udev = hub->ports[i]->child; + if (udev) + pm_wakeup_event(&udev->dev, 0); + } + } + } +} + static int hub_resume(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); dev_dbg(&intf->dev, "%s\n", __func__); hub_activate(hub, HUB_RESUME); + + /* + * This should be called only for system resume, not runtime resume. + * We can't tell the difference here, so some wakeup requests will be + * reported at the wrong time or more than once. This shouldn't + * matter much, so long as they do get reported. + */ + report_wakeup_requests(hub); return 0; } diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 34a6ded6f319..97e2ddec18b1 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -322,6 +322,7 @@ struct hc_driver { int (*bus_suspend)(struct usb_hcd *); int (*bus_resume)(struct usb_hcd *); int (*start_port_reset)(struct usb_hcd *, unsigned port_num); + unsigned long (*get_resuming_ports)(struct usb_hcd *); /* force handover of high-speed port to full-speed companion */ void (*relinquish_port)(struct usb_hcd *, int); -- cgit v1.2.3 From 00d423c8d0132915f4204b330343420c271b9142 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 8 Jun 2018 16:59:50 -0400 Subject: USB: ehci-hcd: Add get_resuming_ports method This patch adds support for the new get_resuming_ports HCD method to the ehci-hcd driver. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 1 + drivers/usb/host/ehci-hub.c | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 89c47ae5c7d3..8608ac513fb7 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1226,6 +1226,7 @@ static const struct hc_driver ehci_hc_driver = { .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + .get_resuming_ports = ehci_get_resuming_ports, /* * device support diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index d7641cbdee43..ce0eaf7d7c12 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -512,10 +512,18 @@ static int ehci_bus_resume (struct usb_hcd *hcd) return -ESHUTDOWN; } +static unsigned long ehci_get_resuming_ports(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + return ehci->resuming_ports; +} + #else #define ehci_bus_suspend NULL #define ehci_bus_resume NULL +#define ehci_get_resuming_ports NULL #endif /* CONFIG_PM */ -- cgit v1.2.3 From 8f9cc83c06d44081d7c7e179f778cbeb4d074fa7 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 8 Jun 2018 16:59:57 -0400 Subject: USB: xhci-hcd: Add get_resuming_ports method This patch adds support for the new get_resuming_ports HCD method to the xhci-hcd driver. Signed-off-by: Alan Stern Acked-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 11 +++++++++++ drivers/usb/host/xhci.c | 1 + drivers/usb/host/xhci.h | 2 ++ 3 files changed, 14 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index a4b95d019f84..7e2a531ba321 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1684,4 +1684,15 @@ int xhci_bus_resume(struct usb_hcd *hcd) return 0; } +unsigned long xhci_get_resuming_ports(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_bus_state *bus_state; + + bus_state = &xhci->bus_state[hcd_index(hcd)]; + + /* USB3 port wakeups are reported via usb_wakeup_notification() */ + return bus_state->resuming_ports; /* USB2 ports only */ +} + #endif /* CONFIG_PM */ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8c8da2d657fa..2f239cb3deaf 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -5081,6 +5081,7 @@ static const struct hc_driver xhci_hc_driver = { .hub_status_data = xhci_hub_status_data, .bus_suspend = xhci_bus_suspend, .bus_resume = xhci_bus_resume, + .get_resuming_ports = xhci_get_resuming_ports, /* * call back when device connected and addressed diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 939e2f86b595..ece5891240f0 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2110,9 +2110,11 @@ void xhci_hc_died(struct xhci_hcd *xhci); #ifdef CONFIG_PM int xhci_bus_suspend(struct usb_hcd *hcd); int xhci_bus_resume(struct usb_hcd *hcd); +unsigned long xhci_get_resuming_ports(struct usb_hcd *hcd); #else #define xhci_bus_suspend NULL #define xhci_bus_resume NULL +#define xhci_get_resuming_ports NULL #endif /* CONFIG_PM */ u32 xhci_port_state_to_neutral(u32 state); -- cgit v1.2.3 From 61ef4b90793b3c1905ed8cbec228ff63ecb1a5c5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 18 Jun 2018 16:27:45 +0200 Subject: USB: mon: use ktime_get_real_ts64 instead of getnstimeofday64 The two do the same thing, but we want to remove getnstimeofday64() to have a more consistent interface. It would be nice to use a monotonic clocksource here rather than 'real' time, but that would break the user interface. Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/mon_bin.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index ad2c082bd0fb..ac2b4fcc265f 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -95,8 +95,8 @@ struct mon_bin_hdr { unsigned short busnum; /* Bus number */ char flag_setup; char flag_data; - s64 ts_sec; /* getnstimeofday64 */ - s32 ts_usec; /* getnstimeofday64 */ + s64 ts_sec; /* ktime_get_real_ts64 */ + s32 ts_usec; /* ktime_get_real_ts64 */ int status; unsigned int len_urb; /* Length of data (submitted or actual) */ unsigned int len_cap; /* Delivered length */ @@ -497,7 +497,7 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, struct mon_bin_hdr *ep; char data_tag = 0; - getnstimeofday64(&ts); + ktime_get_real_ts64(&ts); spin_lock_irqsave(&rp->b_lock, flags); @@ -637,7 +637,7 @@ static void mon_bin_error(void *data, struct urb *urb, int error) unsigned int offset; struct mon_bin_hdr *ep; - getnstimeofday64(&ts); + ktime_get_real_ts64(&ts); spin_lock_irqsave(&rp->b_lock, flags); -- cgit v1.2.3 From cae2bc768d176bfbdad7035bbcc3cdc973eb7984 Mon Sep 17 00:00:00 2001 From: Jaejoong Kim Date: Thu, 21 Jun 2018 17:45:05 +0900 Subject: usb: cdc-acm: Decrement tty port's refcount if probe() fail The cdc-acm driver does not have a refcount of itself, but uses a tty_port's refcount. That is, if the refcount of tty_port is '0', we can clean up the cdc-acm driver by calling the .destruct() callback function of struct tty_port_operations. The problem is the destruct() callback function is not called if the probe() fails, because tty_port's refcount is not zero. So, add tty_port_put() when probe() fails. Signed-off-by: Jaejoong Kim Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 7b366a6c0b49..ed0475c6602d 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1378,6 +1378,9 @@ made_compressed_probe: if (acm == NULL) goto alloc_fail; + tty_port_init(&acm->port); + acm->port.ops = &acm_port_ops; + minor = acm_alloc_minor(acm); if (minor < 0) goto alloc_fail1; @@ -1413,22 +1416,20 @@ made_compressed_probe: acm->out = usb_sndintpipe(usb_dev, epwrite->bEndpointAddress); else acm->out = usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress); - tty_port_init(&acm->port); - acm->port.ops = &acm_port_ops; init_usb_anchor(&acm->delayed); acm->quirks = quirks; buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); if (!buf) - goto alloc_fail2; + goto alloc_fail1; acm->ctrl_buffer = buf; if (acm_write_buffers_alloc(acm) < 0) - goto alloc_fail4; + goto alloc_fail2; acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); if (!acm->ctrlurb) - goto alloc_fail5; + goto alloc_fail3; for (i = 0; i < num_rx_buf; i++) { struct acm_rb *rb = &(acm->read_buffers[i]); @@ -1437,13 +1438,13 @@ made_compressed_probe: rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL, &rb->dma); if (!rb->base) - goto alloc_fail6; + goto alloc_fail4; rb->index = i; rb->instance = acm; urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) - goto alloc_fail6; + goto alloc_fail4; urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; urb->transfer_dma = rb->dma; @@ -1465,7 +1466,7 @@ made_compressed_probe: snd->urb = usb_alloc_urb(0, GFP_KERNEL); if (snd->urb == NULL) - goto alloc_fail7; + goto alloc_fail5; if (usb_endpoint_xfer_int(epwrite)) usb_fill_int_urb(snd->urb, usb_dev, acm->out, @@ -1483,7 +1484,7 @@ made_compressed_probe: i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); if (i < 0) - goto alloc_fail7; + goto alloc_fail5; if (h.usb_cdc_country_functional_desc) { /* export the country data */ struct usb_cdc_country_functional_desc * cfd = @@ -1542,7 +1543,7 @@ skip_countries: &control_interface->dev); if (IS_ERR(tty_dev)) { rv = PTR_ERR(tty_dev); - goto alloc_fail8; + goto alloc_fail6; } if (quirks & CLEAR_HALT_CONDITIONS) { @@ -1551,7 +1552,7 @@ skip_countries: } return 0; -alloc_fail8: +alloc_fail6: if (acm->country_codes) { device_remove_file(&acm->control->dev, &dev_attr_wCountryCodes); @@ -1560,23 +1561,21 @@ alloc_fail8: kfree(acm->country_codes); } device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); -alloc_fail7: +alloc_fail5: usb_set_intfdata(intf, NULL); for (i = 0; i < ACM_NW; i++) usb_free_urb(acm->wb[i].urb); -alloc_fail6: +alloc_fail4: for (i = 0; i < num_rx_buf; i++) usb_free_urb(acm->read_urbs[i]); acm_read_buffers_free(acm); usb_free_urb(acm->ctrlurb); -alloc_fail5: +alloc_fail3: acm_write_buffers_free(acm); -alloc_fail4: - usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); alloc_fail2: - acm_release_minor(acm); + usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); alloc_fail1: - kfree(acm); + tty_port_put(&acm->port); alloc_fail: return rv; } -- cgit v1.2.3 From df44831ee2dde0d61f17ca86069f2c992c0dae95 Mon Sep 17 00:00:00 2001 From: Avi Fishman Date: Wed, 20 Jun 2018 09:33:04 +0300 Subject: USB host: Add USB ehci support for nuvoton npcm7xx platform This patch adds support for ehci controller for the Nuvoton npcm7xx platform. Most of the code was taken from ehci-spear.c + specific initialization code Signed-off-by: Avi Fishman Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 8 ++ drivers/usb/host/Makefile | 1 + drivers/usb/host/ehci-npcm7xx.c | 212 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 drivers/usb/host/ehci-npcm7xx.c (limited to 'drivers/usb') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 6e64d3a64dbb..1a4ea98cac2a 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -192,6 +192,14 @@ config USB_EHCI_MXC ---help--- Variation of ARC USB block used in some Freescale chips. +config USB_EHCI_HCD_NPCM7XX + tristate "Support for Nuvoton NPCM7XX on-chip EHCI USB controller" + depends on (USB_EHCI_HCD && ARCH_NPCM7XX) || COMPILE_TEST + default y if (USB_EHCI_HCD && ARCH_NPCM7XX) + help + Enables support for the on-chip EHCI controller on + Nuvoton NPCM7XX chips. + config USB_EHCI_HCD_OMAP tristate "EHCI support for OMAP3 and later chips" depends on ARCH_OMAP diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 9b669c9f9a48..e6235269c151 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o +obj-$(CONFIG_USB_EHCI_HCD_NPCM7XX) += ehci-npcm7xx.o obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o obj-$(CONFIG_USB_EHCI_HCD_SPEAR) += ehci-spear.o diff --git a/drivers/usb/host/ehci-npcm7xx.c b/drivers/usb/host/ehci-npcm7xx.c new file mode 100644 index 000000000000..c80a8792d3b0 --- /dev/null +++ b/drivers/usb/host/ehci-npcm7xx.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nuvoton NPCM7xx driver for EHCI HCD + * + * Copyright (C) 2018 Nuvoton Technologies, + * Avi Fishman + * Tomer Maimon + * + * Based on various ehci-spear.c driver + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ehci.h" + +#include +#include + +#define DRIVER_DESC "EHCI npcm7xx driver" + +static const char hcd_name[] = "npcm7xx-ehci"; + +#define USB2PHYCTL_OFFSET 0x144 + +#define IPSRST2_OFFSET 0x24 +#define IPSRST3_OFFSET 0x34 + + +static struct hc_driver __read_mostly ehci_npcm7xx_hc_driver; + +#ifdef CONFIG_PM_SLEEP +static int ehci_npcm7xx_drv_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + bool do_wakeup = device_may_wakeup(dev); + + return ehci_suspend(hcd, do_wakeup); +} + +static int ehci_npcm7xx_drv_resume(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + + ehci_resume(hcd, false); + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(ehci_npcm7xx_pm_ops, ehci_npcm7xx_drv_suspend, + ehci_npcm7xx_drv_resume); + +static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct resource *res; + struct regmap *gcr_regmap; + struct regmap *rst_regmap; + const struct hc_driver *driver = &ehci_npcm7xx_hc_driver; + int irq; + int retval; + + dev_dbg(&pdev->dev, "initializing npcm7xx ehci USB Controller\n"); + + gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr"); + if (IS_ERR(gcr_regmap)) { + dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-gcr\n", + __func__); + return IS_ERR(gcr_regmap); + } + + rst_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst"); + if (IS_ERR(rst_regmap)) { + dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-rst\n", + __func__); + return IS_ERR(rst_regmap); + } + + /********* phy init ******/ + // reset usb host + regmap_update_bits(rst_regmap, IPSRST2_OFFSET, + (0x1 << 26), (0x1 << 26)); + regmap_update_bits(rst_regmap, IPSRST3_OFFSET, + (0x1 << 25), (0x1 << 25)); + regmap_update_bits(gcr_regmap, USB2PHYCTL_OFFSET, + (0x1 << 28), 0); + + udelay(1); + + // enable phy + regmap_update_bits(rst_regmap, IPSRST3_OFFSET, + (0x1 << 25), 0); + + udelay(50); // enable phy + + regmap_update_bits(gcr_regmap, USB2PHYCTL_OFFSET, + (0x1 << 28), (0x1 << 28)); + + // enable host + regmap_update_bits(rst_regmap, IPSRST2_OFFSET, + (0x1 << 26), 0); + + if (usb_disabled()) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + retval = irq; + goto fail; + } + + /* + * 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 have dma capability bindings this can go away. + */ + retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (retval) + goto fail; + + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto fail; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + retval = PTR_ERR(hcd->regs); + goto err_put_hcd; + } + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + /* registers start at offset 0x0 */ + hcd_to_ehci(hcd)->caps = hcd->regs; + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval) + goto err_put_hcd; + + device_wakeup_enable(hcd->self.controller); + return retval; + +err_put_hcd: + usb_put_hcd(hcd); +fail: + dev_err(&pdev->dev, "init fail, %d\n", retval); + + return retval; +} + +static int npcm7xx_ehci_hcd_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + + usb_put_hcd(hcd); + + return 0; +} + +static const struct of_device_id npcm7xx_ehci_id_table[] = { + { .compatible = "nuvoton,npcm750-ehci" }, + { }, +}; +MODULE_DEVICE_TABLE(of, npcm7xx_ehci_id_table); + +static struct platform_driver npcm7xx_ehci_hcd_driver = { + .probe = npcm7xx_ehci_hcd_drv_probe, + .remove = npcm7xx_ehci_hcd_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "npcm7xx-ehci", + .bus = &platform_bus_type, + .pm = &ehci_npcm7xx_pm_ops, + .of_match_table = npcm7xx_ehci_id_table, + } +}; + +static int __init ehci_npcm7xx_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ehci_init_driver(&ehci_npcm7xx_hc_driver, NULL); + return platform_driver_register(&npcm7xx_ehci_hcd_driver); +} +module_init(ehci_npcm7xx_init); + +static void __exit ehci_npcm7xx_cleanup(void) +{ + platform_driver_unregister(&npcm7xx_ehci_hcd_driver); +} +module_exit(ehci_npcm7xx_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_ALIAS("platform:npcm7xx-ehci"); +MODULE_AUTHOR("Avi Fishman"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From ba0ab35a81de1da15e1691142834eefd34e34a0e Mon Sep 17 00:00:00 2001 From: Marcel Ziswiler Date: Mon, 25 Jun 2018 09:58:43 +0200 Subject: usb: chipidea: tegra: Use aligned DMA on Tegra114/124 USB Ethernet gadget now works on Tegra114 and Tegra124. Similar to commit 061e20e9899e ("usb: chipidea: tegra: Use aligned DMA on Tegra30"). Signed-off-by: Marcel Ziswiler Signed-off-by: Peter Chen --- drivers/usb/chipidea/ci_hdrc_tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c index 7f4d2b6af37a..772851bee99b 100644 --- a/drivers/usb/chipidea/ci_hdrc_tegra.c +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -33,11 +33,11 @@ static const struct tegra_udc_soc_info tegra30_udc_soc_info = { }; static const struct tegra_udc_soc_info tegra114_udc_soc_info = { - .flags = 0, + .flags = CI_HDRC_REQUIRES_ALIGNED_DMA, }; static const struct tegra_udc_soc_info tegra124_udc_soc_info = { - .flags = 0, + .flags = CI_HDRC_REQUIRES_ALIGNED_DMA, }; static const struct of_device_id tegra_udc_of_match[] = { -- cgit v1.2.3 From c75d18cc5a97e9d01d04ee7af457fddea38ca539 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Sun, 24 Jun 2018 00:32:04 +0200 Subject: USB: serial: cyberjack: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Johan Hovold --- drivers/usb/serial/cyberjack.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index dc67a2eb98d7..ebd76ab07b72 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -255,6 +255,7 @@ static void cyberjack_read_int_callback(struct urb *urb) struct device *dev = &port->dev; unsigned char *data = urb->transfer_buffer; int status = urb->status; + unsigned long flags; int result; /* the urb might have been killed. */ @@ -270,13 +271,13 @@ static void cyberjack_read_int_callback(struct urb *urb) /* This is a announcement of coming bulk_ins. */ unsigned short size = ((unsigned short)data[3]<<8)+data[2]+3; - spin_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, flags); old_rdtodo = priv->rdtodo; if (old_rdtodo > SHRT_MAX - size) { dev_dbg(dev, "To many bulk_in urbs to do.\n"); - spin_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, flags); goto resubmit; } @@ -285,7 +286,7 @@ static void cyberjack_read_int_callback(struct urb *urb) dev_dbg(dev, "%s - rdtodo: %d\n", __func__, priv->rdtodo); - spin_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, flags); if (!old_rdtodo) { result = usb_submit_urb(port->read_urb, GFP_ATOMIC); @@ -309,6 +310,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb) struct cyberjack_private *priv = usb_get_serial_port_data(port); struct device *dev = &port->dev; unsigned char *data = urb->transfer_buffer; + unsigned long flags; short todo; int result; int status = urb->status; @@ -325,7 +327,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb) tty_flip_buffer_push(&port->port); } - spin_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, flags); /* Reduce urbs to do by one. */ priv->rdtodo -= urb->actual_length; @@ -334,7 +336,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb) priv->rdtodo = 0; todo = priv->rdtodo; - spin_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, flags); dev_dbg(dev, "%s - rdtodo: %d\n", __func__, todo); @@ -354,6 +356,7 @@ static void cyberjack_write_bulk_callback(struct urb *urb) struct cyberjack_private *priv = usb_get_serial_port_data(port); struct device *dev = &port->dev; int status = urb->status; + unsigned long flags; set_bit(0, &port->write_urbs_free); if (status) { @@ -362,7 +365,7 @@ static void cyberjack_write_bulk_callback(struct urb *urb) return; } - spin_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, flags); /* only do something if we have more data to send */ if (priv->wrfilled) { @@ -406,7 +409,7 @@ static void cyberjack_write_bulk_callback(struct urb *urb) } exit: - spin_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, flags); usb_serial_port_softint(port); } -- cgit v1.2.3 From 041b7db9668a60dc7f03c53fe4fe1c8e137e935b Mon Sep 17 00:00:00 2001 From: John Ogness Date: Sun, 24 Jun 2018 00:32:05 +0200 Subject: USB: serial: digi_acceleport: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Johan Hovold --- drivers/usb/serial/digi_acceleport.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index b0526786fb02..ae512fed08af 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -984,6 +984,7 @@ static void digi_write_bulk_callback(struct urb *urb) struct usb_serial *serial; struct digi_port *priv; struct digi_serial *serial_priv; + unsigned long flags; int ret = 0; int status = urb->status; @@ -1004,15 +1005,15 @@ static void digi_write_bulk_callback(struct urb *urb) /* handle oob callback */ if (priv->dp_port_num == serial_priv->ds_oob_port_num) { dev_dbg(&port->dev, "digi_write_bulk_callback: oob callback\n"); - spin_lock(&priv->dp_port_lock); + spin_lock_irqsave(&priv->dp_port_lock, flags); priv->dp_write_urb_in_use = 0; wake_up_interruptible(&port->write_wait); - spin_unlock(&priv->dp_port_lock); + spin_unlock_irqrestore(&priv->dp_port_lock, flags); return; } /* try to send any buffered data on this port */ - spin_lock(&priv->dp_port_lock); + spin_lock_irqsave(&priv->dp_port_lock, flags); priv->dp_write_urb_in_use = 0; if (priv->dp_out_buf_len > 0) { *((unsigned char *)(port->write_urb->transfer_buffer)) @@ -1035,7 +1036,7 @@ static void digi_write_bulk_callback(struct urb *urb) /* lost the race in write_chan(). */ schedule_work(&priv->dp_wakeup_work); - spin_unlock(&priv->dp_port_lock); + spin_unlock_irqrestore(&priv->dp_port_lock, flags); if (ret && ret != -EPERM) dev_err_console(port, "%s: usb_submit_urb failed, ret=%d, port=%d\n", @@ -1381,6 +1382,7 @@ static int digi_read_inb_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct digi_port *priv = usb_get_serial_port_data(port); unsigned char *buf = urb->transfer_buffer; + unsigned long flags; int opcode; int len; int port_status; @@ -1407,7 +1409,7 @@ static int digi_read_inb_callback(struct urb *urb) return -1; } - spin_lock(&priv->dp_port_lock); + spin_lock_irqsave(&priv->dp_port_lock, flags); /* check for throttle; if set, do not resubmit read urb */ /* indicate the read chain needs to be restarted on unthrottle */ @@ -1444,7 +1446,7 @@ static int digi_read_inb_callback(struct urb *urb) tty_flip_buffer_push(&port->port); } } - spin_unlock(&priv->dp_port_lock); + spin_unlock_irqrestore(&priv->dp_port_lock, flags); if (opcode == DIGI_CMD_RECEIVE_DISABLE) dev_dbg(&port->dev, "%s: got RECEIVE_DISABLE\n", __func__); @@ -1474,6 +1476,7 @@ static int digi_read_oob_callback(struct urb *urb) struct digi_port *priv = usb_get_serial_port_data(port); unsigned char *buf = urb->transfer_buffer; int opcode, line, status, val; + unsigned long flags; int i; unsigned int rts; @@ -1506,7 +1509,7 @@ static int digi_read_oob_callback(struct urb *urb) rts = C_CRTSCTS(tty); if (tty && opcode == DIGI_CMD_READ_INPUT_SIGNALS) { - spin_lock(&priv->dp_port_lock); + spin_lock_irqsave(&priv->dp_port_lock, flags); /* convert from digi flags to termiox flags */ if (val & DIGI_READ_INPUT_SIGNALS_CTS) { priv->dp_modem_signals |= TIOCM_CTS; @@ -1530,12 +1533,12 @@ static int digi_read_oob_callback(struct urb *urb) else priv->dp_modem_signals &= ~TIOCM_CD; - spin_unlock(&priv->dp_port_lock); + spin_unlock_irqrestore(&priv->dp_port_lock, flags); } else if (opcode == DIGI_CMD_TRANSMIT_IDLE) { - spin_lock(&priv->dp_port_lock); + spin_lock_irqsave(&priv->dp_port_lock, flags); priv->dp_transmit_idle = 1; wake_up_interruptible(&priv->dp_transmit_idle_wait); - spin_unlock(&priv->dp_port_lock); + spin_unlock_irqrestore(&priv->dp_port_lock, flags); } else if (opcode == DIGI_CMD_IFLUSH_FIFO) { wake_up_interruptible(&priv->dp_flush_wait); } -- cgit v1.2.3 From dd1fae527612543e560e84f2eba4f6ef2006ac55 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Sun, 24 Jun 2018 00:32:06 +0200 Subject: USB: serial: io_edgeport: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Johan Hovold --- drivers/usb/serial/io_edgeport.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 17283f4b4779..97c69d373ca6 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -648,6 +648,7 @@ static void edge_interrupt_callback(struct urb *urb) struct usb_serial_port *port; unsigned char *data = urb->transfer_buffer; int length = urb->actual_length; + unsigned long flags; int bytes_avail; int position; int txCredits; @@ -679,7 +680,7 @@ static void edge_interrupt_callback(struct urb *urb) if (length > 1) { bytes_avail = data[0] | (data[1] << 8); if (bytes_avail) { - spin_lock(&edge_serial->es_lock); + spin_lock_irqsave(&edge_serial->es_lock, flags); edge_serial->rxBytesAvail += bytes_avail; dev_dbg(dev, "%s - bytes_avail=%d, rxBytesAvail=%d, read_in_progress=%d\n", @@ -702,7 +703,8 @@ static void edge_interrupt_callback(struct urb *urb) edge_serial->read_in_progress = false; } } - spin_unlock(&edge_serial->es_lock); + spin_unlock_irqrestore(&edge_serial->es_lock, + flags); } } /* grab the txcredits for the ports if available */ @@ -715,9 +717,11 @@ static void edge_interrupt_callback(struct urb *urb) port = edge_serial->serial->port[portNumber]; edge_port = usb_get_serial_port_data(port); if (edge_port->open) { - spin_lock(&edge_port->ep_lock); + spin_lock_irqsave(&edge_port->ep_lock, + flags); edge_port->txCredits += txCredits; - spin_unlock(&edge_port->ep_lock); + spin_unlock_irqrestore(&edge_port->ep_lock, + flags); dev_dbg(dev, "%s - txcredits for port%d = %d\n", __func__, portNumber, edge_port->txCredits); @@ -758,6 +762,7 @@ static void edge_bulk_in_callback(struct urb *urb) int retval; __u16 raw_data_length; int status = urb->status; + unsigned long flags; if (status) { dev_dbg(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n", @@ -777,7 +782,7 @@ static void edge_bulk_in_callback(struct urb *urb) usb_serial_debug_data(dev, __func__, raw_data_length, data); - spin_lock(&edge_serial->es_lock); + spin_lock_irqsave(&edge_serial->es_lock, flags); /* decrement our rxBytes available by the number that we just got */ edge_serial->rxBytesAvail -= raw_data_length; @@ -801,7 +806,7 @@ static void edge_bulk_in_callback(struct urb *urb) edge_serial->read_in_progress = false; } - spin_unlock(&edge_serial->es_lock); + spin_unlock_irqrestore(&edge_serial->es_lock, flags); } -- cgit v1.2.3 From 6778b0cbdbb44afdaa97dd16395be31e239777cf Mon Sep 17 00:00:00 2001 From: John Ogness Date: Sun, 24 Jun 2018 00:32:07 +0200 Subject: USB: serial: io_ti: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Johan Hovold --- drivers/usb/serial/io_ti.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 0fbadb37c104..6d1d6efa3055 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1729,6 +1729,7 @@ static void edge_bulk_in_callback(struct urb *urb) struct edgeport_port *edge_port = urb->context; struct device *dev = &edge_port->port->dev; unsigned char *data = urb->transfer_buffer; + unsigned long flags; int retval = 0; int port_number; int status = urb->status; @@ -1780,13 +1781,13 @@ static void edge_bulk_in_callback(struct urb *urb) exit: /* continue read unless stopped */ - spin_lock(&edge_port->ep_lock); + spin_lock_irqsave(&edge_port->ep_lock, flags); if (edge_port->ep_read_urb_state == EDGE_READ_URB_RUNNING) retval = usb_submit_urb(urb, GFP_ATOMIC); else if (edge_port->ep_read_urb_state == EDGE_READ_URB_STOPPING) edge_port->ep_read_urb_state = EDGE_READ_URB_STOPPED; - spin_unlock(&edge_port->ep_lock); + spin_unlock_irqrestore(&edge_port->ep_lock, flags); if (retval) dev_err(dev, "%s - usb_submit_urb failed with result %d\n", __func__, retval); } -- cgit v1.2.3 From f7c8a9ccc9afe3781dfd6e1977775457102c5c83 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Sun, 24 Jun 2018 00:32:08 +0200 Subject: USB: serial: mos7720: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7720.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index bd57630e67e2..8f11e759ad61 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -340,14 +340,15 @@ static void async_complete(struct urb *urb) { struct urbtracker *urbtrack = urb->context; int status = urb->status; + unsigned long flags; if (unlikely(status)) dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status); /* remove the urbtracker from the active_urbs list */ - spin_lock(&urbtrack->mos_parport->listlock); + spin_lock_irqsave(&urbtrack->mos_parport->listlock, flags); list_del(&urbtrack->urblist_entry); - spin_unlock(&urbtrack->mos_parport->listlock); + spin_unlock_irqrestore(&urbtrack->mos_parport->listlock, flags); kref_put(&urbtrack->ref_count, destroy_urbtracker); } -- cgit v1.2.3 From 19bfbf462e8921d8e520bcc0a23ebc4988223631 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Sun, 24 Jun 2018 00:32:09 +0200 Subject: USB: serial: mos7840: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index fdceb46d9fc6..4efffbbef5ae 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -802,18 +802,19 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) struct moschip_port *mos7840_port; struct usb_serial_port *port; int status = urb->status; + unsigned long flags; int i; mos7840_port = urb->context; port = mos7840_port->port; - spin_lock(&mos7840_port->pool_lock); + spin_lock_irqsave(&mos7840_port->pool_lock, flags); for (i = 0; i < NUM_URBS; i++) { if (urb == mos7840_port->write_urb_pool[i]) { mos7840_port->busy[i] = 0; break; } } - spin_unlock(&mos7840_port->pool_lock); + spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); if (status) { dev_dbg(&port->dev, "nonzero write bulk status received:%d\n", status); -- cgit v1.2.3 From 2ba02c8dd063c4396111629e96dec5a3f231f995 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Sun, 24 Jun 2018 00:32:10 +0200 Subject: USB: serial: quatech2: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Johan Hovold --- drivers/usb/serial/quatech2.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index ff2322ea5e14..b61c2a9b6b11 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -621,16 +621,17 @@ static void qt2_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port; struct qt2_port_private *port_priv; + unsigned long flags; port = urb->context; port_priv = usb_get_serial_port_data(port); - spin_lock(&port_priv->urb_lock); + spin_lock_irqsave(&port_priv->urb_lock, flags); port_priv->urb_in_use = false; usb_serial_port_softint(port); - spin_unlock(&port_priv->urb_lock); + spin_unlock_irqrestore(&port_priv->urb_lock, flags); } -- cgit v1.2.3 From e60870012e5a35b1506d7b376fddfb30e9da0b27 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Sun, 24 Jun 2018 00:32:11 +0200 Subject: USB: serial: sierra: fix potential deadlock at close The portdata spinlock can be taken in interrupt context (via sierra_outdat_callback()). Disable interrupts when taking the portdata spinlock when discarding deferred URBs during close to prevent a possible deadlock. Fixes: 014333f77c0b ("USB: sierra: fix urb and memory leak on disconnect") Cc: stable Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior [ johan: amend commit message and add fixes and stable tags ] Signed-off-by: Johan Hovold --- drivers/usb/serial/sierra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index d189f953c891..55956a638f5b 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -770,9 +770,9 @@ static void sierra_close(struct usb_serial_port *port) kfree(urb->transfer_buffer); usb_free_urb(urb); usb_autopm_put_interface_async(serial->interface); - spin_lock(&portdata->lock); + spin_lock_irq(&portdata->lock); portdata->outstanding_urbs--; - spin_unlock(&portdata->lock); + spin_unlock_irq(&portdata->lock); } sierra_stop_rx_urbs(port); -- cgit v1.2.3 From d4bf25b3fc250a5d950691e824bbfa7a732a6608 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Sun, 24 Jun 2018 00:32:12 +0200 Subject: USB: serial: sierra: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Johan Hovold --- drivers/usb/serial/sierra.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 55956a638f5b..a43263a0edd8 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -409,6 +409,7 @@ static void sierra_outdat_callback(struct urb *urb) struct sierra_port_private *portdata = usb_get_serial_port_data(port); struct sierra_intf_private *intfdata; int status = urb->status; + unsigned long flags; intfdata = usb_get_serial_data(port->serial); @@ -419,12 +420,12 @@ static void sierra_outdat_callback(struct urb *urb) dev_dbg(&port->dev, "%s - nonzero write bulk status " "received: %d\n", __func__, status); - spin_lock(&portdata->lock); + spin_lock_irqsave(&portdata->lock, flags); --portdata->outstanding_urbs; - spin_unlock(&portdata->lock); - spin_lock(&intfdata->susp_lock); + spin_unlock_irqrestore(&portdata->lock, flags); + spin_lock_irqsave(&intfdata->susp_lock, flags); --intfdata->in_flight; - spin_unlock(&intfdata->susp_lock); + spin_unlock_irqrestore(&intfdata->susp_lock, flags); usb_serial_port_softint(port); } -- cgit v1.2.3 From 5e02bfcf3f4f8c76c47718063d408c8a09d61bca Mon Sep 17 00:00:00 2001 From: John Ogness Date: Sun, 24 Jun 2018 00:32:13 +0200 Subject: USB: serial: symbolserial: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Johan Hovold --- drivers/usb/serial/symbolserial.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index cd2f8dc8b58c..6ca24e86f686 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -35,6 +35,7 @@ static void symbol_int_callback(struct urb *urb) struct symbol_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; int status = urb->status; + unsigned long flags; int result; int data_length; @@ -73,7 +74,7 @@ static void symbol_int_callback(struct urb *urb) } exit: - spin_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, flags); /* Continue trying to always read if we should */ if (!priv->throttled) { @@ -84,7 +85,7 @@ exit: __func__, result); } else priv->actually_throttled = true; - spin_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, flags); } static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port) -- cgit v1.2.3 From cf83be24b2efc423f6869a155372e3694c12a558 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Sun, 24 Jun 2018 00:32:14 +0200 Subject: USB: serial: ti_usb_3410_5052: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Johan Hovold --- drivers/usb/serial/ti_usb_3410_5052.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 6b22857f6e52..3010878f7f8e 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -1215,6 +1215,7 @@ static void ti_bulk_in_callback(struct urb *urb) struct usb_serial_port *port = tport->tp_port; struct device *dev = &urb->dev->dev; int status = urb->status; + unsigned long flags; int retval = 0; switch (status) { @@ -1247,20 +1248,20 @@ static void ti_bulk_in_callback(struct urb *urb) __func__); else ti_recv(port, urb->transfer_buffer, urb->actual_length); - spin_lock(&tport->tp_lock); + spin_lock_irqsave(&tport->tp_lock, flags); port->icount.rx += urb->actual_length; - spin_unlock(&tport->tp_lock); + spin_unlock_irqrestore(&tport->tp_lock, flags); } exit: /* continue to read unless stopping */ - spin_lock(&tport->tp_lock); + spin_lock_irqsave(&tport->tp_lock, flags); if (tport->tp_read_urb_state == TI_READ_URB_RUNNING) retval = usb_submit_urb(urb, GFP_ATOMIC); else if (tport->tp_read_urb_state == TI_READ_URB_STOPPING) tport->tp_read_urb_state = TI_READ_URB_STOPPED; - spin_unlock(&tport->tp_lock); + spin_unlock_irqrestore(&tport->tp_lock, flags); if (retval) dev_err(dev, "%s - resubmit read urb failed, %d\n", __func__, retval); -- cgit v1.2.3 From a323f94611aac0f68b710048ae53c73a53b5c7b5 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Sun, 24 Jun 2018 00:32:15 +0200 Subject: USB: serial: usb_wwan: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Johan Hovold --- drivers/usb/serial/usb_wwan.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 107e64c42e94..912472f26e4f 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -326,6 +326,7 @@ static void usb_wwan_outdat_callback(struct urb *urb) struct usb_serial_port *port; struct usb_wwan_port_private *portdata; struct usb_wwan_intf_private *intfdata; + unsigned long flags; int i; port = urb->context; @@ -334,9 +335,9 @@ static void usb_wwan_outdat_callback(struct urb *urb) usb_serial_port_softint(port); usb_autopm_put_interface_async(port->serial->interface); portdata = usb_get_serial_port_data(port); - spin_lock(&intfdata->susp_lock); + spin_lock_irqsave(&intfdata->susp_lock, flags); intfdata->in_flight--; - spin_unlock(&intfdata->susp_lock); + spin_unlock_irqrestore(&intfdata->susp_lock, flags); for (i = 0; i < N_OUT_URB; ++i) { if (portdata->out_urbs[i] == urb) { -- cgit v1.2.3 From 1d1de580a3e0b1cfacd63b4093573d8f327b0a1a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 26 Jun 2018 15:43:13 +0200 Subject: USB: serial: digi_acceleport: rename tty flag variable Add a "tty_" prefix to the tty "flag" variable to avoid any future mixups with the recently added irq-mask "flags" one. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/digi_acceleport.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index ae512fed08af..e7f244cf2c07 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1387,7 +1387,7 @@ static int digi_read_inb_callback(struct urb *urb) int len; int port_status; unsigned char *data; - int flag, throttled; + int tty_flag, throttled; /* short/multiple packet check */ if (urb->actual_length < 2) { @@ -1423,7 +1423,7 @@ static int digi_read_inb_callback(struct urb *urb) data = &buf[3]; /* get flag from port_status */ - flag = 0; + tty_flag = 0; /* overrun is special, not associated with a char */ if (port_status & DIGI_OVERRUN_ERROR) @@ -1432,17 +1432,17 @@ static int digi_read_inb_callback(struct urb *urb) /* break takes precedence over parity, */ /* which takes precedence over framing errors */ if (port_status & DIGI_BREAK_ERROR) - flag = TTY_BREAK; + tty_flag = TTY_BREAK; else if (port_status & DIGI_PARITY_ERROR) - flag = TTY_PARITY; + tty_flag = TTY_PARITY; else if (port_status & DIGI_FRAMING_ERROR) - flag = TTY_FRAME; + tty_flag = TTY_FRAME; /* data length is len-1 (one byte of len is port_status) */ --len; if (len > 0) { tty_insert_flip_string_fixed_flag(&port->port, data, - flag, len); + tty_flag, len); tty_flip_buffer_push(&port->port); } } -- cgit v1.2.3 From 4da24f4dd0bb2b88e9c84690aa769b524c63b316 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 27 Jun 2018 11:37:05 +0300 Subject: USB: host: ehci-npcm7xx: Fix some error codes in probe We accidentally return 1 instead of negative error codes. Fixes: df44831ee2dd ("USB host: Add USB ehci support for nuvoton npcm7xx platform") Signed-off-by: Dan Carpenter Signed-off-by: Avi Fishman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-npcm7xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-npcm7xx.c b/drivers/usb/host/ehci-npcm7xx.c index c80a8792d3b0..adaf8fb4b459 100644 --- a/drivers/usb/host/ehci-npcm7xx.c +++ b/drivers/usb/host/ehci-npcm7xx.c @@ -74,14 +74,14 @@ static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev) if (IS_ERR(gcr_regmap)) { dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-gcr\n", __func__); - return IS_ERR(gcr_regmap); + return PTR_ERR(gcr_regmap); } rst_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst"); if (IS_ERR(rst_regmap)) { dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-rst\n", __func__); - return IS_ERR(rst_regmap); + return PTR_ERR(rst_regmap); } /********* phy init ******/ -- cgit v1.2.3 From 4685be25a19078de6301f50bde7a755483805269 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 25 Jun 2018 00:08:34 +0200 Subject: usb: cdc-acm: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Cc: Oliver Neukum Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index ed0475c6602d..24686f8eb6d3 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -276,6 +276,7 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf) { int newctrl; int difference; + unsigned long flags; struct usb_cdc_notification *dr = (struct usb_cdc_notification *)buf; unsigned char *data = buf + sizeof(struct usb_cdc_notification); @@ -303,7 +304,7 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf) } difference = acm->ctrlin ^ newctrl; - spin_lock(&acm->read_lock); + spin_lock_irqsave(&acm->read_lock, flags); acm->ctrlin = newctrl; acm->oldcount = acm->iocount; @@ -321,7 +322,7 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf) acm->iocount.parity++; if (difference & ACM_CTRL_OVERRUN) acm->iocount.overrun++; - spin_unlock(&acm->read_lock); + spin_unlock_irqrestore(&acm->read_lock, flags); if (difference) wake_up_all(&acm->wioctl); -- cgit v1.2.3 From 31adcb0a9cba3b7e3560c6364aaa8d0060764e12 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 25 Jun 2018 00:08:36 +0200 Subject: usb: core: use irqsave() in sg_complete() complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: Sebastian Andrzej Siewior Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 1a15392326fc..228672f2c4a1 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -269,10 +269,11 @@ static void sg_clean(struct usb_sg_request *io) static void sg_complete(struct urb *urb) { + unsigned long flags; struct usb_sg_request *io = urb->context; int status = urb->status; - spin_lock(&io->lock); + spin_lock_irqsave(&io->lock, flags); /* In 2.5 we require hcds' endpoint queues not to progress after fault * reports, until the completion callback (this!) returns. That lets @@ -306,7 +307,7 @@ static void sg_complete(struct urb *urb) * unlink pending urbs so they won't rx/tx bad data. * careful: unlink can sometimes be synchronous... */ - spin_unlock(&io->lock); + spin_unlock_irqrestore(&io->lock, flags); for (i = 0, found = 0; i < io->entries; i++) { if (!io->urbs[i]) continue; @@ -323,7 +324,7 @@ static void sg_complete(struct urb *urb) } else if (urb == io->urbs[i]) found = 1; } - spin_lock(&io->lock); + spin_lock_irqsave(&io->lock, flags); } /* on the last completion, signal usb_sg_wait() */ @@ -332,7 +333,7 @@ static void sg_complete(struct urb *urb) if (!io->count) complete(&io->complete); - spin_unlock(&io->lock); + spin_unlock_irqrestore(&io->lock, flags); } -- cgit v1.2.3 From 3f38dace1a206d0ffe9d9a25e97288fbf8c79b55 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 25 Jun 2018 00:08:37 +0200 Subject: usb: usbfs: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: Sebastian Andrzej Siewior Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 476dcc5f2da3..6ce77b33da61 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -585,9 +585,10 @@ static void async_completed(struct urb *urb) struct siginfo sinfo; struct pid *pid = NULL; const struct cred *cred = NULL; + unsigned long flags; int signr; - spin_lock(&ps->lock); + spin_lock_irqsave(&ps->lock, flags); list_move_tail(&as->asynclist, &ps->async_completed); as->status = urb->status; signr = as->signr; @@ -611,7 +612,7 @@ static void async_completed(struct urb *urb) cancel_bulk_urbs(ps, as->bulk_addr); wake_up(&ps->wait); - spin_unlock(&ps->lock); + spin_unlock_irqrestore(&ps->lock, flags); if (signr) { kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred); -- cgit v1.2.3 From 0f5f7ace852ff172d77dfbbc89e6a86349f7f476 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 25 Jun 2018 00:08:38 +0200 Subject: usb: usblp: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: Sebastian Andrzej Siewior Acked-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usblp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index d058d7a31e7c..407a7a6198a2 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -292,6 +292,7 @@ static void usblp_bulk_read(struct urb *urb) { struct usblp *usblp = urb->context; int status = urb->status; + unsigned long flags; if (usblp->present && usblp->used) { if (status) @@ -299,14 +300,14 @@ static void usblp_bulk_read(struct urb *urb) "nonzero read bulk status received: %d\n", usblp->minor, status); } - spin_lock(&usblp->lock); + spin_lock_irqsave(&usblp->lock, flags); if (status < 0) usblp->rstatus = status; else usblp->rstatus = urb->actual_length; usblp->rcomplete = 1; wake_up(&usblp->rwait); - spin_unlock(&usblp->lock); + spin_unlock_irqrestore(&usblp->lock, flags); usb_free_urb(urb); } @@ -315,6 +316,7 @@ static void usblp_bulk_write(struct urb *urb) { struct usblp *usblp = urb->context; int status = urb->status; + unsigned long flags; if (usblp->present && usblp->used) { if (status) @@ -322,7 +324,7 @@ static void usblp_bulk_write(struct urb *urb) "nonzero write bulk status received: %d\n", usblp->minor, status); } - spin_lock(&usblp->lock); + spin_lock_irqsave(&usblp->lock, flags); if (status < 0) usblp->wstatus = status; else @@ -330,7 +332,7 @@ static void usblp_bulk_write(struct urb *urb) usblp->no_paper = 0; usblp->wcomplete = 1; wake_up(&usblp->wwait); - spin_unlock(&usblp->lock); + spin_unlock_irqrestore(&usblp->lock, flags); usb_free_urb(urb); } -- cgit v1.2.3 From 8982c8440f564549261fb80ca3fe1e3266635936 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Mon, 25 Jun 2018 00:08:39 +0200 Subject: usb: usb-skeleton: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usb-skeleton.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index c3ddd0f1f449..f101347e3ea3 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -159,10 +159,11 @@ static int skel_flush(struct file *file, fl_owner_t id) static void skel_read_bulk_callback(struct urb *urb) { struct usb_skel *dev; + unsigned long flags; dev = urb->context; - spin_lock(&dev->err_lock); + spin_lock_irqsave(&dev->err_lock, flags); /* sync/async unlink faults aren't errors */ if (urb->status) { if (!(urb->status == -ENOENT || @@ -177,7 +178,7 @@ static void skel_read_bulk_callback(struct urb *urb) dev->bulk_in_filled = urb->actual_length; } dev->ongoing_read = 0; - spin_unlock(&dev->err_lock); + spin_unlock_irqrestore(&dev->err_lock, flags); wake_up_interruptible(&dev->bulk_in_wait); } @@ -331,6 +332,7 @@ exit: static void skel_write_bulk_callback(struct urb *urb) { struct usb_skel *dev; + unsigned long flags; dev = urb->context; @@ -343,9 +345,9 @@ static void skel_write_bulk_callback(struct urb *urb) "%s - nonzero write bulk status received: %d\n", __func__, urb->status); - spin_lock(&dev->err_lock); + spin_lock_irqsave(&dev->err_lock, flags); dev->errors = urb->status; - spin_unlock(&dev->err_lock); + spin_unlock_irqrestore(&dev->err_lock, flags); } /* free up our allocated buffer */ -- cgit v1.2.3 From 7375fc9f5f162e783661ee9cdb85a62ec5f6b6d5 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Mon, 25 Jun 2018 00:08:41 +0200 Subject: usb: wusbcore: remove excessive irqsave wa_urb_dequeue() locks multiple spinlocks while disabling interrupts: spin_lock_irqsave(&lock1, flags); spin_lock_irqsave(&lock2, flags2); Obviously there is no need for the second irqsave and "flags2" variable since interrupts are disabled at that point. Remove the second irqsave: spin_lock_irqsave(&lock1, flags); spin_lock(&lock2); and eliminate the flags2 variable. Signed-off-by: John Ogness Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-xfer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 7fca4e7e556d..01f2f21830c0 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -1918,7 +1918,7 @@ EXPORT_SYMBOL_GPL(wa_urb_enqueue); */ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) { - unsigned long flags, flags2; + unsigned long flags; struct wa_xfer *xfer; struct wa_seg *seg; struct wa_rpipe *rpipe; @@ -1964,10 +1964,10 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) goto out_unlock; } /* Check the delayed list -> if there, release and complete */ - spin_lock_irqsave(&wa->xfer_list_lock, flags2); + spin_lock(&wa->xfer_list_lock); if (!list_empty(&xfer->list_node) && xfer->seg == NULL) goto dequeue_delayed; - spin_unlock_irqrestore(&wa->xfer_list_lock, flags2); + spin_unlock(&wa->xfer_list_lock); if (xfer->seg == NULL) /* still hasn't reached */ goto out_unlock; /* setup(), enqueue_b() completes */ /* Ok, the xfer is in flight already, it's been setup and submitted.*/ @@ -2054,7 +2054,7 @@ out_unlock: dequeue_delayed: list_del_init(&xfer->list_node); - spin_unlock_irqrestore(&wa->xfer_list_lock, flags2); + spin_unlock(&wa->xfer_list_lock); xfer->result = urb->status; spin_unlock_irqrestore(&xfer->lock, flags); wa_xfer_giveback(xfer); -- cgit v1.2.3 From 957ada71b69e6da4d8131b082c5d57558f7fa415 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 25 Jun 2018 00:08:42 +0200 Subject: usb: adutux: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/adutux.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index b3160afe0458..9465fb95d70a 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -155,11 +155,12 @@ static void adu_interrupt_in_callback(struct urb *urb) { struct adu_device *dev = urb->context; int status = urb->status; + unsigned long flags; adu_debug_data(&dev->udev->dev, __func__, urb->actual_length, urb->transfer_buffer); - spin_lock(&dev->buflock); + spin_lock_irqsave(&dev->buflock, flags); if (status != 0) { if ((status != -ENOENT) && (status != -ECONNRESET) && @@ -190,7 +191,7 @@ static void adu_interrupt_in_callback(struct urb *urb) exit: dev->read_urb_finished = 1; - spin_unlock(&dev->buflock); + spin_unlock_irqrestore(&dev->buflock, flags); /* always wake up so we recover from errors */ wake_up_interruptible(&dev->read_wait); } @@ -199,6 +200,7 @@ static void adu_interrupt_out_callback(struct urb *urb) { struct adu_device *dev = urb->context; int status = urb->status; + unsigned long flags; adu_debug_data(&dev->udev->dev, __func__, urb->actual_length, urb->transfer_buffer); @@ -213,10 +215,10 @@ static void adu_interrupt_out_callback(struct urb *urb) return; } - spin_lock(&dev->buflock); + spin_lock_irqsave(&dev->buflock, flags); dev->out_urb_finished = 1; wake_up(&dev->write_wait); - spin_unlock(&dev->buflock); + spin_unlock_irqrestore(&dev->buflock, flags); } static int adu_open(struct inode *inode, struct file *file) -- cgit v1.2.3 From efd61e94fa3fd44b72afb5c0ab89bf400d9da40b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 25 Jun 2018 00:08:43 +0200 Subject: usb: iowarrior: remove intr_idx_lock The intr_idx_lock lock is acquired only in the completion callback of the ->int_in_urb (iowarrior_callback()). There is only one URB that is scheduled / completed so there can't be more than one user of the lock. The comment says that it protects ->intr_idx and the callback is the only place in driver that writes to it. Remove the intr_idx_lock lock because it is superfluous. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/iowarrior.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 8d33187ce2af..c2991b8a65ce 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -81,7 +81,6 @@ struct iowarrior { atomic_t write_busy; /* number of write-urbs submitted */ atomic_t read_idx; atomic_t intr_idx; - spinlock_t intr_idx_lock; /* protects intr_idx */ atomic_t overflow_flag; /* signals an index 'rollover' */ int present; /* this is 1 as long as the device is connected */ int opened; /* this is 1 if the device is currently open */ @@ -166,7 +165,6 @@ static void iowarrior_callback(struct urb *urb) goto exit; } - spin_lock(&dev->intr_idx_lock); intr_idx = atomic_read(&dev->intr_idx); /* aux_idx become previous intr_idx */ aux_idx = (intr_idx == 0) ? (MAX_INTERRUPT_BUFFER - 1) : (intr_idx - 1); @@ -181,7 +179,6 @@ static void iowarrior_callback(struct urb *urb) (dev->read_queue + offset, urb->transfer_buffer, dev->report_size)) { /* equal values on interface 0 will be ignored */ - spin_unlock(&dev->intr_idx_lock); goto exit; } } @@ -202,7 +199,6 @@ static void iowarrior_callback(struct urb *urb) *(dev->read_queue + offset + (dev->report_size)) = dev->serial_number++; atomic_set(&dev->intr_idx, aux_idx); - spin_unlock(&dev->intr_idx_lock); /* tell the blocking read about the new data */ wake_up_interruptible(&dev->read_wait); @@ -762,7 +758,6 @@ static int iowarrior_probe(struct usb_interface *interface, atomic_set(&dev->intr_idx, 0); atomic_set(&dev->read_idx, 0); - spin_lock_init(&dev->intr_idx_lock); atomic_set(&dev->overflow_flag, 0); init_waitqueue_head(&dev->read_wait); atomic_set(&dev->write_busy, 0); -- cgit v1.2.3 From d7cdbdd024d978b9002a20223cc384d11a0a03ea Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 25 Jun 2018 00:08:44 +0200 Subject: usb: ldusb: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/ldusb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index c2e255f02a72..006762b72ff5 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -225,6 +225,7 @@ static void ld_usb_interrupt_in_callback(struct urb *urb) size_t *actual_buffer; unsigned int next_ring_head; int status = urb->status; + unsigned long flags; int retval; if (status) { @@ -236,12 +237,12 @@ static void ld_usb_interrupt_in_callback(struct urb *urb) dev_dbg(&dev->intf->dev, "%s: nonzero status received: %d\n", __func__, status); - spin_lock(&dev->rbsl); + spin_lock_irqsave(&dev->rbsl, flags); goto resubmit; /* maybe we can recover */ } } - spin_lock(&dev->rbsl); + spin_lock_irqsave(&dev->rbsl, flags); if (urb->actual_length > 0) { next_ring_head = (dev->ring_head+1) % ring_buffer_size; if (next_ring_head != dev->ring_tail) { @@ -270,7 +271,7 @@ resubmit: dev->buffer_overflow = 1; } } - spin_unlock(&dev->rbsl); + spin_unlock_irqrestore(&dev->rbsl, flags); exit: dev->interrupt_in_done = 1; wake_up_interruptible(&dev->read_wait); -- cgit v1.2.3 From 4327059a14bb6d9578bfb82a6b7c59b3bbf78335 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 25 Jun 2018 00:08:45 +0200 Subject: usb: legousbtower: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Cc: Juergen Stuber Cc: legousb-devel@lists.sourceforge.net Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index bf47bd8bc76f..006cf13b2199 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -722,6 +722,7 @@ static void tower_interrupt_in_callback (struct urb *urb) struct lego_usb_tower *dev = urb->context; int status = urb->status; int retval; + unsigned long flags; lego_usb_tower_debug_data(&dev->udev->dev, __func__, urb->actual_length, urb->transfer_buffer); @@ -740,7 +741,7 @@ static void tower_interrupt_in_callback (struct urb *urb) } if (urb->actual_length > 0) { - spin_lock (&dev->read_buffer_lock); + spin_lock_irqsave(&dev->read_buffer_lock, flags); if (dev->read_buffer_length + urb->actual_length < read_buffer_size) { memcpy (dev->read_buffer + dev->read_buffer_length, dev->interrupt_in_buffer, @@ -753,7 +754,7 @@ static void tower_interrupt_in_callback (struct urb *urb) pr_warn("read_buffer overflow, %d bytes dropped\n", urb->actual_length); } - spin_unlock (&dev->read_buffer_lock); + spin_unlock_irqrestore(&dev->read_buffer_lock, flags); } resubmit: -- cgit v1.2.3 From 2df6948428542c5a22fbf9c7c36c66ccc9363c7d Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 14 Jun 2018 18:36:46 +0200 Subject: USB: cdc-wdm: don't enable interrupts in USB-giveback In the code path __usb_hcd_giveback_urb() -> wdm_in_callback() -> service_outstanding_interrupt() The function service_outstanding_interrupt() will unconditionally enable interrupts during unlock and invoke usb_submit_urb() with GFP_KERNEL. If the HCD completes in BH (like ehci does) then the context remains atomic due local_bh_disable() and enabling interrupts does not change this. Defer the error case handling to a workqueue as suggested by Oliver Neukum. In case of an error the worker performs the read out and wakes the user. Fixes: c1da59dad0eb ("cdc-wdm: Clear read pipeline in case of error") Cc: Robert Foss Signed-off-by: Sebastian Andrzej Siewior Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-wdm.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index a0d284ef3f40..203bbd378858 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -96,6 +96,7 @@ struct wdm_device { struct mutex rlock; wait_queue_head_t wait; struct work_struct rxwork; + struct work_struct service_outs_intr; int werr; int rerr; int resp_count; @@ -151,9 +152,6 @@ static void wdm_out_callback(struct urb *urb) wake_up(&desc->wait); } -/* forward declaration */ -static int service_outstanding_interrupt(struct wdm_device *desc); - static void wdm_in_callback(struct urb *urb) { struct wdm_device *desc = urb->context; @@ -209,8 +207,6 @@ static void wdm_in_callback(struct urb *urb) } } skip_error: - set_bit(WDM_READ, &desc->flags); - wake_up(&desc->wait); if (desc->rerr) { /* @@ -219,9 +215,11 @@ skip_error: * We should respond to further attempts from the device to send * data, so that we can get unstuck. */ - service_outstanding_interrupt(desc); + schedule_work(&desc->service_outs_intr); + } else { + set_bit(WDM_READ, &desc->flags); + wake_up(&desc->wait); } - spin_unlock(&desc->iuspin); } @@ -758,6 +756,21 @@ static void wdm_rxwork(struct work_struct *work) } } +static void service_interrupt_work(struct work_struct *work) +{ + struct wdm_device *desc; + + desc = container_of(work, struct wdm_device, service_outs_intr); + + spin_lock_irq(&desc->iuspin); + service_outstanding_interrupt(desc); + if (!desc->resp_count) { + set_bit(WDM_READ, &desc->flags); + wake_up(&desc->wait); + } + spin_unlock_irq(&desc->iuspin); +} + /* --- hotplug --- */ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, @@ -779,6 +792,7 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber); desc->intf = intf; INIT_WORK(&desc->rxwork, wdm_rxwork); + INIT_WORK(&desc->service_outs_intr, service_interrupt_work); rv = -EINVAL; if (!usb_endpoint_is_int_in(ep)) @@ -964,6 +978,7 @@ static void wdm_disconnect(struct usb_interface *intf) mutex_lock(&desc->wlock); kill_urbs(desc); cancel_work_sync(&desc->rxwork); + cancel_work_sync(&desc->service_outs_intr); mutex_unlock(&desc->wlock); mutex_unlock(&desc->rlock); @@ -1006,6 +1021,7 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) /* callback submits work - order is essential */ kill_urbs(desc); cancel_work_sync(&desc->rxwork); + cancel_work_sync(&desc->service_outs_intr); } if (!PMSG_IS_AUTO(message)) { mutex_unlock(&desc->wlock); @@ -1065,6 +1081,7 @@ static int wdm_pre_reset(struct usb_interface *intf) mutex_lock(&desc->wlock); kill_urbs(desc); cancel_work_sync(&desc->rxwork); + cancel_work_sync(&desc->service_outs_intr); return 0; } -- cgit v1.2.3 From 579b9cca2bff170ac4c4487de6e8ebff7906b3a6 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 25 Jun 2018 00:08:35 +0200 Subject: usb: cdc-wdm: use irqsave() in USB's complete callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Cc: Oliver Neukum Cc: "Bjørn Mork" Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-wdm.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 203bbd378858..bec581fb7c63 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -142,10 +142,12 @@ found: static void wdm_out_callback(struct urb *urb) { struct wdm_device *desc; + unsigned long flags; + desc = urb->context; - spin_lock(&desc->iuspin); + spin_lock_irqsave(&desc->iuspin, flags); desc->werr = urb->status; - spin_unlock(&desc->iuspin); + spin_unlock_irqrestore(&desc->iuspin, flags); kfree(desc->outbuf); desc->outbuf = NULL; clear_bit(WDM_IN_USE, &desc->flags); @@ -154,11 +156,12 @@ static void wdm_out_callback(struct urb *urb) static void wdm_in_callback(struct urb *urb) { + unsigned long flags; struct wdm_device *desc = urb->context; int status = urb->status; int length = urb->actual_length; - spin_lock(&desc->iuspin); + spin_lock_irqsave(&desc->iuspin, flags); clear_bit(WDM_RESPONDING, &desc->flags); if (status) { @@ -220,11 +223,12 @@ skip_error: set_bit(WDM_READ, &desc->flags); wake_up(&desc->wait); } - spin_unlock(&desc->iuspin); + spin_unlock_irqrestore(&desc->iuspin, flags); } static void wdm_int_callback(struct urb *urb) { + unsigned long flags; int rv = 0; int responding; int status = urb->status; @@ -284,7 +288,7 @@ static void wdm_int_callback(struct urb *urb) goto exit; } - spin_lock(&desc->iuspin); + spin_lock_irqsave(&desc->iuspin, flags); responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); if (!desc->resp_count++ && !responding && !test_bit(WDM_DISCONNECTING, &desc->flags) @@ -292,7 +296,7 @@ static void wdm_int_callback(struct urb *urb) rv = usb_submit_urb(desc->response, GFP_ATOMIC); dev_dbg(&desc->intf->dev, "submit response URB %d\n", rv); } - spin_unlock(&desc->iuspin); + spin_unlock_irqrestore(&desc->iuspin, flags); if (rv < 0) { clear_bit(WDM_RESPONDING, &desc->flags); if (rv == -EPERM) -- cgit v1.2.3 From 8d361fa2c29dcaf258f71e70cd75ff51084c3e5e Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 25 Jun 2018 15:23:16 -0700 Subject: usb: typec: tps6598x: Remove VLA usage In the quest to remove all stack VLA usage from the kernel[1], this uses the maximum buffer size and adds a sanity check. While 25 bytes is the size of the largest current things coming through, Heikki Krogerus pointed out that the actual max in 64 bytes, as per ch 1.3.2 http://www.ti.com/lit/ug/slvuan1a/slvuan1a.pdf [1] https://lkml.kernel.org/r/CA+55aFzCG-zNmZwX4A2FQpadafLfEzK6CC=qPXydAacU1RqZWA@mail.gmail.com Signed-off-by: Kees Cook Acked-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tps6598x.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c index 4b4c8d271b27..c84c8c189e90 100644 --- a/drivers/usb/typec/tps6598x.c +++ b/drivers/usb/typec/tps6598x.c @@ -81,12 +81,21 @@ struct tps6598x { struct typec_capability typec_cap; }; +/* + * Max data bytes for Data1, Data2, and other registers. See ch 1.3.2: + * http://www.ti.com/lit/ug/slvuan1a/slvuan1a.pdf + */ +#define TPS_MAX_LEN 64 + static int tps6598x_block_read(struct tps6598x *tps, u8 reg, void *val, size_t len) { - u8 data[len + 1]; + u8 data[TPS_MAX_LEN + 1]; int ret; + if (WARN_ON(len + 1 > sizeof(data))) + return -EINVAL; + if (!tps->i2c_protocol) return regmap_raw_read(tps->regmap, reg, val, len); -- cgit v1.2.3 From 5e85a04c8c0d271d7561a770b85741f186398868 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Wed, 27 Jun 2018 07:45:22 +0800 Subject: usb: typec: add fwnode to tcpc Add fwnode handle to get the fwnode so we can get typec configs it contains. Suggested-by: Heikki Krogerus Reviewed-by: Heikki Krogerus Signed-off-by: Li Jun Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/staging/typec/tcpci.c | 7 +++++++ drivers/usb/typec/tcpm.c | 1 + include/linux/usb/tcpm.h | 2 ++ 3 files changed, 10 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/staging/typec/tcpci.c b/drivers/staging/typec/tcpci.c index dd2928824ef6..e59547a32605 100644 --- a/drivers/staging/typec/tcpci.c +++ b/drivers/staging/typec/tcpci.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -474,6 +475,12 @@ static int tcpci_parse_config(struct tcpci *tcpci) /* TODO: Populate struct tcpc_config from ACPI/device-tree */ tcpci->tcpc.config = &tcpci_tcpc_config; + tcpci->tcpc.fwnode = device_get_named_child_node(tcpci->dev, + "connector"); + if (!tcpci->tcpc.fwnode) { + dev_err(tcpci->dev, "Can't find connector node.\n"); + return -EINVAL; + } return 0; } diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index 8a201dd53d36..d22b37bf4e1a 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -4576,6 +4576,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) else port->try_role = TYPEC_NO_PREFERRED_ROLE; + port->typec_caps.fwnode = tcpc->fwnode; port->typec_caps.prefer_role = tcpc->config->default_role; port->typec_caps.type = tcpc->config->type; port->typec_caps.data = tcpc->config->data; diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index b231b9314240..193920a2e05f 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -110,6 +110,7 @@ enum tcpc_mux_mode { /** * struct tcpc_dev - Port configuration and callback functions * @config: Pointer to port configuration + * @fwnode: Pointer to port fwnode * @get_vbus: Called to read current VBUS state * @get_current_limit: * Optional; called by the tcpm core when configured as a snk @@ -138,6 +139,7 @@ enum tcpc_mux_mode { */ struct tcpc_dev { const struct tcpc_config *config; + struct fwnode_handle *fwnode; int (*init)(struct tcpc_dev *dev); int (*get_vbus)(struct tcpc_dev *dev); -- cgit v1.2.3 From 9c90e02434b66f9bcfc5c9a91a808eee5b7aa21b Mon Sep 17 00:00:00 2001 From: Li Jun Date: Wed, 27 Jun 2018 07:45:23 +0800 Subject: usb: typec: add API to get typec basic port power and data config This patch adds 3 APIs to get the typec port power and data type, and preferred power role by its name string. Reviewed-by: Heikki Krogerus Signed-off-by: Li Jun Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 58 +++++++++++++++++++++++++++++++++++++++++++---- include/linux/usb/typec.h | 3 +++ 2 files changed, 57 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 53df10df2f9d..633105917fa6 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -796,12 +796,18 @@ static const char * const typec_data_roles[] = { [TYPEC_HOST] = "host", }; -static const char * const typec_port_types[] = { +static const char * const typec_port_power_roles[] = { [TYPEC_PORT_SRC] = "source", [TYPEC_PORT_SNK] = "sink", [TYPEC_PORT_DRP] = "dual", }; +static const char * const typec_port_data_roles[] = { + [TYPEC_PORT_DFP] = "host", + [TYPEC_PORT_UFP] = "device", + [TYPEC_PORT_DRD] = "dual", +}; + static const char * const typec_port_types_drp[] = { [TYPEC_PORT_SRC] = "dual [source] sink", [TYPEC_PORT_SNK] = "dual source [sink]", @@ -932,7 +938,7 @@ static ssize_t power_role_store(struct device *dev, mutex_lock(&port->port_type_lock); if (port->port_type != TYPEC_PORT_DRP) { dev_dbg(dev, "port type fixed at \"%s\"", - typec_port_types[port->port_type]); + typec_port_power_roles[port->port_type]); ret = -EOPNOTSUPP; goto unlock_and_ret; } @@ -973,7 +979,7 @@ port_type_store(struct device *dev, struct device_attribute *attr, return -EOPNOTSUPP; } - ret = sysfs_match_string(typec_port_types, buf); + ret = sysfs_match_string(typec_port_power_roles, buf); if (ret < 0) return ret; @@ -1007,7 +1013,7 @@ port_type_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%s\n", typec_port_types_drp[port->port_type]); - return sprintf(buf, "[%s]\n", typec_port_types[port->cap->type]); + return sprintf(buf, "[%s]\n", typec_port_power_roles[port->cap->type]); } static DEVICE_ATTR_RW(port_type); @@ -1252,6 +1258,50 @@ void typec_set_pwr_opmode(struct typec_port *port, } EXPORT_SYMBOL_GPL(typec_set_pwr_opmode); +/** + * typec_find_port_power_role - Get the typec port power capability + * @name: port power capability string + * + * This routine is used to find the typec_port_type by its string name. + * + * Returns typec_port_type if success, otherwise negative error code. + */ +int typec_find_port_power_role(const char *name) +{ + return match_string(typec_port_power_roles, + ARRAY_SIZE(typec_port_power_roles), name); +} +EXPORT_SYMBOL_GPL(typec_find_port_power_role); + +/** + * typec_find_power_role - Find the typec one specific power role + * @name: power role string + * + * This routine is used to find the typec_role by its string name. + * + * Returns typec_role if success, otherwise negative error code. + */ +int typec_find_power_role(const char *name) +{ + return match_string(typec_roles, ARRAY_SIZE(typec_roles), name); +} +EXPORT_SYMBOL_GPL(typec_find_power_role); + +/** + * typec_find_port_data_role - Get the typec port data capability + * @name: port data capability string + * + * This routine is used to find the typec_port_data by its string name. + * + * Returns typec_port_data if success, otherwise negative error code. + */ +int typec_find_port_data_role(const char *name) +{ + return match_string(typec_port_data_roles, + ARRAY_SIZE(typec_port_data_roles), name); +} +EXPORT_SYMBOL_GPL(typec_find_port_data_role); + /* ------------------------------------------ */ /* API for Multiplexer/DeMultiplexer Switches */ diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 672b39bb0adc..15f8d9a50b31 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -267,4 +267,7 @@ int typec_set_orientation(struct typec_port *port, enum typec_orientation orientation); int typec_set_mode(struct typec_port *port, int mode); +int typec_find_port_power_role(const char *name); +int typec_find_power_role(const char *name); +int typec_find_port_data_role(const char *name); #endif /* __LINUX_USB_TYPEC_H */ -- cgit v1.2.3 From 96232cbc6c994c6af387c3c6e2a1f121cf5b1a2d Mon Sep 17 00:00:00 2001 From: Li Jun Date: Wed, 27 Jun 2018 07:45:24 +0800 Subject: usb: typec: tcpm: support get typec and pd config from device properties This patch adds support of get typec and power delivery config from firmware description. Reviewed-by: Heikki Krogerus Reviewed-by: Guenter Roeck Signed-off-by: Li Jun Acked-by: Adam Thomson Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm.c | 131 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 22 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index d22b37bf4e1a..9dcab8180a78 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -4236,6 +4236,81 @@ static int tcpm_copy_vdos(u32 *dest_vdo, const u32 *src_vdo, return nr_vdo; } +static int tcpm_fw_get_caps(struct tcpm_port *port, + struct fwnode_handle *fwnode) +{ + const char *cap_str; + int ret; + u32 mw; + + if (!fwnode) + return -EINVAL; + + /* USB data support is optional */ + ret = fwnode_property_read_string(fwnode, "data-role", &cap_str); + if (ret == 0) { + port->typec_caps.data = typec_find_port_data_role(cap_str); + if (port->typec_caps.data < 0) + return -EINVAL; + } + + ret = fwnode_property_read_string(fwnode, "power-role", &cap_str); + if (ret < 0) + return ret; + + port->typec_caps.type = typec_find_port_power_role(cap_str); + if (port->typec_caps.type < 0) + return -EINVAL; + port->port_type = port->typec_caps.type; + + if (port->port_type == TYPEC_PORT_SNK) + goto sink; + + /* Get source pdos */ + ret = fwnode_property_read_u32_array(fwnode, "source-pdos", + NULL, 0); + if (ret <= 0) + return -EINVAL; + + port->nr_src_pdo = min(ret, PDO_MAX_OBJECTS); + ret = fwnode_property_read_u32_array(fwnode, "source-pdos", + port->src_pdo, port->nr_src_pdo); + if ((ret < 0) || tcpm_validate_caps(port, port->src_pdo, + port->nr_src_pdo)) + return -EINVAL; + + if (port->port_type == TYPEC_PORT_SRC) + return 0; + + /* Get the preferred power role for DRP */ + ret = fwnode_property_read_string(fwnode, "try-power-role", &cap_str); + if (ret < 0) + return ret; + + port->typec_caps.prefer_role = typec_find_power_role(cap_str); + if (port->typec_caps.prefer_role < 0) + return -EINVAL; +sink: + /* Get sink pdos */ + ret = fwnode_property_read_u32_array(fwnode, "sink-pdos", + NULL, 0); + if (ret <= 0) + return -EINVAL; + + port->nr_snk_pdo = min(ret, PDO_MAX_OBJECTS); + ret = fwnode_property_read_u32_array(fwnode, "sink-pdos", + port->snk_pdo, port->nr_snk_pdo); + if ((ret < 0) || tcpm_validate_caps(port, port->snk_pdo, + port->nr_snk_pdo)) + return -EINVAL; + + if (fwnode_property_read_u32(fwnode, "op-sink-microwatt", &mw) < 0) + return -EINVAL; + port->operating_snk_mw = mw / 1000; + + return 0; +} + int tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, unsigned int nr_pdo) { @@ -4521,12 +4596,36 @@ static int devm_tcpm_psy_register(struct tcpm_port *port) return PTR_ERR_OR_ZERO(port->psy); } +static int tcpm_copy_caps(struct tcpm_port *port, + const struct tcpc_config *tcfg) +{ + if (tcpm_validate_caps(port, tcfg->src_pdo, tcfg->nr_src_pdo) || + tcpm_validate_caps(port, tcfg->snk_pdo, tcfg->nr_snk_pdo)) + return -EINVAL; + + port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcfg->src_pdo, + tcfg->nr_src_pdo); + port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcfg->snk_pdo, + tcfg->nr_snk_pdo); + + port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcfg->snk_vdo, + tcfg->nr_snk_vdo); + + port->operating_snk_mw = tcfg->operating_snk_mw; + + port->typec_caps.prefer_role = tcfg->default_role; + port->typec_caps.type = tcfg->type; + port->typec_caps.data = tcfg->data; + + return 0; +} + struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) { struct tcpm_port *port; int i, err; - if (!dev || !tcpc || !tcpc->config || + if (!dev || !tcpc || !tcpc->get_vbus || !tcpc->set_cc || !tcpc->get_cc || !tcpc->set_polarity || !tcpc->set_vconn || !tcpc->set_vbus || !tcpc->set_pd_rx || !tcpc->set_roles || !tcpc->pd_transmit) @@ -4556,30 +4655,18 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) init_completion(&port->pps_complete); tcpm_debugfs_init(port); - if (tcpm_validate_caps(port, tcpc->config->src_pdo, - tcpc->config->nr_src_pdo) || - tcpm_validate_caps(port, tcpc->config->snk_pdo, - tcpc->config->nr_snk_pdo)) { - err = -EINVAL; + err = tcpm_fw_get_caps(port, tcpc->fwnode); + if ((err < 0) && tcpc->config) + err = tcpm_copy_caps(port, tcpc->config); + if (err < 0) goto out_destroy_wq; - } - port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcpc->config->src_pdo, - tcpc->config->nr_src_pdo); - port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcpc->config->snk_pdo, - tcpc->config->nr_snk_pdo); - port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcpc->config->snk_vdo, - tcpc->config->nr_snk_vdo); - - port->operating_snk_mw = tcpc->config->operating_snk_mw; - if (!tcpc->config->try_role_hw) - port->try_role = tcpc->config->default_role; + + if (!tcpc->config || !tcpc->config->try_role_hw) + port->try_role = port->typec_caps.prefer_role; else port->try_role = TYPEC_NO_PREFERRED_ROLE; port->typec_caps.fwnode = tcpc->fwnode; - port->typec_caps.prefer_role = tcpc->config->default_role; - port->typec_caps.type = tcpc->config->type; - port->typec_caps.data = tcpc->config->data; port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */ port->typec_caps.dr_set = tcpm_dr_set; @@ -4589,7 +4676,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->typec_caps.port_type_set = tcpm_port_type_set; port->partner_desc.identity = &port->partner_ident; - port->port_type = tcpc->config->type; + port->port_type = port->typec_caps.type; port->role_sw = usb_role_switch_get(port->dev); if (IS_ERR(port->role_sw)) { @@ -4607,7 +4694,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) goto out_destroy_wq; } - if (tcpc->config->alt_modes) { + if (tcpc->config && tcpc->config->alt_modes) { const struct typec_altmode_desc *paltmode = tcpc->config->alt_modes; i = 0; -- cgit v1.2.3 From 8e04b3721cc86ce7a3d95bcbc6971d7996aa541c Mon Sep 17 00:00:00 2001 From: Li Jun Date: Wed, 27 Jun 2018 07:45:29 +0800 Subject: typec: tcpm: add starting value for drp toggling As DRP port autonomously toggles the Rp/Rd need a start value to begin with, so add one parameter for it in tcpm_start_drp_toggling. Reviewed-by: Guenter Roeck Reviewed-by: Heikki Krogerus Signed-off-by: Li Jun Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index 9dcab8180a78..f5e9558f3f9e 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -2431,15 +2431,15 @@ static int tcpm_set_charge(struct tcpm_port *port, bool charge) return 0; } -static bool tcpm_start_drp_toggling(struct tcpm_port *port) +static bool tcpm_start_drp_toggling(struct tcpm_port *port, + enum typec_cc_status cc) { int ret; if (port->tcpc->start_drp_toggling && port->port_type == TYPEC_PORT_DRP) { tcpm_log_force(port, "Start DRP toggling"); - ret = port->tcpc->start_drp_toggling(port->tcpc, - tcpm_rp_cc(port)); + ret = port->tcpc->start_drp_toggling(port->tcpc, cc); if (!ret) return true; } @@ -2747,7 +2747,7 @@ static void run_state_machine(struct tcpm_port *port) if (!port->non_pd_role_swap) tcpm_swap_complete(port, -ENOTCONN); tcpm_src_detach(port); - if (tcpm_start_drp_toggling(port)) { + if (tcpm_start_drp_toggling(port, tcpm_rp_cc(port))) { tcpm_set_state(port, DRP_TOGGLING, 0); break; } @@ -2922,7 +2922,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_swap_complete(port, -ENOTCONN); tcpm_pps_complete(port, -ENOTCONN); tcpm_snk_detach(port); - if (tcpm_start_drp_toggling(port)) { + if (tcpm_start_drp_toggling(port, TYPEC_CC_RD)) { tcpm_set_state(port, DRP_TOGGLING, 0); break; } -- cgit v1.2.3 From 990da41530b31ed7de29340ce1d78c04bee9670c Mon Sep 17 00:00:00 2001 From: Li Jun Date: Wed, 27 Jun 2018 07:45:32 +0800 Subject: staging: typec: tcpci: move tcpci drivers out of staging Move TCPCI(Typec port controller interface) driver and rt1711h driver out of staging. Signed-off-by: Li Jun Reviewed-by: Guenter Roeck Acked-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/typec/Kconfig | 22 -- drivers/staging/typec/Makefile | 2 - drivers/staging/typec/TODO | 5 - drivers/staging/typec/tcpci.c | 612 ---------------------------------- drivers/staging/typec/tcpci.h | 139 -------- drivers/staging/typec/tcpci_rt1711h.c | 312 ----------------- drivers/usb/typec/Kconfig | 15 + drivers/usb/typec/Makefile | 2 + drivers/usb/typec/tcpci.c | 612 ++++++++++++++++++++++++++++++++++ drivers/usb/typec/tcpci.h | 139 ++++++++ drivers/usb/typec/tcpci_rt1711h.c | 312 +++++++++++++++++ 13 files changed, 1080 insertions(+), 1095 deletions(-) delete mode 100644 drivers/staging/typec/Kconfig delete mode 100644 drivers/staging/typec/Makefile delete mode 100644 drivers/staging/typec/TODO delete mode 100644 drivers/staging/typec/tcpci.c delete mode 100644 drivers/staging/typec/tcpci.h delete mode 100644 drivers/staging/typec/tcpci_rt1711h.c create mode 100644 drivers/usb/typec/tcpci.c create mode 100644 drivers/usb/typec/tcpci.h create mode 100644 drivers/usb/typec/tcpci_rt1711h.c (limited to 'drivers/usb') diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 75a480497d22..af9a82f5c4b7 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -106,8 +106,6 @@ source "drivers/staging/greybus/Kconfig" source "drivers/staging/vc04_services/Kconfig" -source "drivers/staging/typec/Kconfig" - source "drivers/staging/vboxvideo/Kconfig" source "drivers/staging/pi433/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index e84959a8a684..8479d47d78d7 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -2,7 +2,6 @@ # Makefile for staging directory obj-y += media/ -obj-y += typec/ obj-$(CONFIG_PRISM2_USB) += wlan-ng/ obj-$(CONFIG_COMEDI) += comedi/ obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/ diff --git a/drivers/staging/typec/Kconfig b/drivers/staging/typec/Kconfig deleted file mode 100644 index 3aa981fbc8f5..000000000000 --- a/drivers/staging/typec/Kconfig +++ /dev/null @@ -1,22 +0,0 @@ -menu "USB Power Delivery and Type-C drivers" - -if TYPEC_TCPM - -config TYPEC_TCPCI - tristate "Type-C Port Controller Interface driver" - depends on I2C - select REGMAP_I2C - help - Type-C Port Controller driver for TCPCI-compliant controller. - -config TYPEC_RT1711H - tristate "Richtek RT1711H Type-C chip driver" - select TYPEC_TCPCI - help - Richtek RT1711H Type-C chip driver that works with - Type-C Port Controller Manager to provide USB PD and USB - Type-C functionalities. - -endif - -endmenu diff --git a/drivers/staging/typec/Makefile b/drivers/staging/typec/Makefile deleted file mode 100644 index 7803d485e1b3..000000000000 --- a/drivers/staging/typec/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o -obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o diff --git a/drivers/staging/typec/TODO b/drivers/staging/typec/TODO deleted file mode 100644 index 53fe2f726c88..000000000000 --- a/drivers/staging/typec/TODO +++ /dev/null @@ -1,5 +0,0 @@ -tcpci: -- Test with real hardware - -Please send patches to Guenter Roeck and copy -Heikki Krogerus . diff --git a/drivers/staging/typec/tcpci.c b/drivers/staging/typec/tcpci.c deleted file mode 100644 index ac6b418b15f1..000000000000 --- a/drivers/staging/typec/tcpci.c +++ /dev/null @@ -1,612 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright 2015-2017 Google, Inc - * - * USB Type-C Port Controller Interface. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tcpci.h" - -#define PD_RETRY_COUNT 3 - -struct tcpci { - struct device *dev; - - struct tcpm_port *port; - - struct regmap *regmap; - - bool controls_vbus; - - struct tcpc_dev tcpc; - struct tcpci_data *data; -}; - -struct tcpci_chip { - struct tcpci *tcpci; - struct tcpci_data data; -}; - -static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc) -{ - return container_of(tcpc, struct tcpci, tcpc); -} - -static int tcpci_read16(struct tcpci *tcpci, unsigned int reg, u16 *val) -{ - return regmap_raw_read(tcpci->regmap, reg, val, sizeof(u16)); -} - -static int tcpci_write16(struct tcpci *tcpci, unsigned int reg, u16 val) -{ - return regmap_raw_write(tcpci->regmap, reg, &val, sizeof(u16)); -} - -static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc) -{ - struct tcpci *tcpci = tcpc_to_tcpci(tcpc); - unsigned int reg; - int ret; - - switch (cc) { - case TYPEC_CC_RA: - reg = (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC2_SHIFT); - break; - case TYPEC_CC_RD: - reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); - break; - case TYPEC_CC_RP_DEF: - reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | - (TCPC_ROLE_CTRL_RP_VAL_DEF << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); - break; - case TYPEC_CC_RP_1_5: - reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | - (TCPC_ROLE_CTRL_RP_VAL_1_5 << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); - break; - case TYPEC_CC_RP_3_0: - reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | - (TCPC_ROLE_CTRL_RP_VAL_3_0 << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); - break; - case TYPEC_CC_OPEN: - default: - reg = (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT); - break; - } - - ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); - if (ret < 0) - return ret; - - return 0; -} - -static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc, - enum typec_cc_status cc) -{ - int ret; - struct tcpci *tcpci = tcpc_to_tcpci(tcpc); - unsigned int reg = TCPC_ROLE_CTRL_DRP; - - /* Handle vendor drp toggling */ - if (tcpci->data->start_drp_toggling) { - ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc); - if (ret < 0) - return ret; - } - - switch (cc) { - default: - case TYPEC_CC_RP_DEF: - reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); - break; - case TYPEC_CC_RP_1_5: - reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); - break; - case TYPEC_CC_RP_3_0: - reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); - break; - } - - if (cc == TYPEC_CC_RD) - reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); - else - reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT); - ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); - if (ret < 0) - return ret; - return regmap_write(tcpci->regmap, TCPC_COMMAND, - TCPC_CMD_LOOK4CONNECTION); -} - -static enum typec_cc_status tcpci_to_typec_cc(unsigned int cc, bool sink) -{ - switch (cc) { - case 0x1: - return sink ? TYPEC_CC_RP_DEF : TYPEC_CC_RA; - case 0x2: - return sink ? TYPEC_CC_RP_1_5 : TYPEC_CC_RD; - case 0x3: - if (sink) - return TYPEC_CC_RP_3_0; - /* fall through */ - case 0x0: - default: - return TYPEC_CC_OPEN; - } -} - -static int tcpci_get_cc(struct tcpc_dev *tcpc, - enum typec_cc_status *cc1, enum typec_cc_status *cc2) -{ - struct tcpci *tcpci = tcpc_to_tcpci(tcpc); - unsigned int reg; - int ret; - - ret = regmap_read(tcpci->regmap, TCPC_CC_STATUS, ®); - if (ret < 0) - return ret; - - *cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) & - TCPC_CC_STATUS_CC1_MASK, - reg & TCPC_CC_STATUS_TERM); - *cc2 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC2_SHIFT) & - TCPC_CC_STATUS_CC2_MASK, - reg & TCPC_CC_STATUS_TERM); - - return 0; -} - -static int tcpci_set_polarity(struct tcpc_dev *tcpc, - enum typec_cc_polarity polarity) -{ - struct tcpci *tcpci = tcpc_to_tcpci(tcpc); - unsigned int reg; - int ret; - - /* Keep the disconnect cc line open */ - ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, ®); - if (ret < 0) - return ret; - - if (polarity == TYPEC_POLARITY_CC2) - reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT; - else - reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT; - ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); - if (ret < 0) - return ret; - - return regmap_write(tcpci->regmap, TCPC_TCPC_CTRL, - (polarity == TYPEC_POLARITY_CC2) ? - TCPC_TCPC_CTRL_ORIENTATION : 0); -} - -static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable) -{ - struct tcpci *tcpci = tcpc_to_tcpci(tcpc); - int ret; - - /* Handle vendor set vconn */ - if (tcpci->data->set_vconn) { - ret = tcpci->data->set_vconn(tcpci, tcpci->data, enable); - if (ret < 0) - return ret; - } - - return regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, - TCPC_POWER_CTRL_VCONN_ENABLE, - enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0); -} - -static int tcpci_set_roles(struct tcpc_dev *tcpc, bool attached, - enum typec_role role, enum typec_data_role data) -{ - struct tcpci *tcpci = tcpc_to_tcpci(tcpc); - unsigned int reg; - int ret; - - reg = PD_REV20 << TCPC_MSG_HDR_INFO_REV_SHIFT; - if (role == TYPEC_SOURCE) - reg |= TCPC_MSG_HDR_INFO_PWR_ROLE; - if (data == TYPEC_HOST) - reg |= TCPC_MSG_HDR_INFO_DATA_ROLE; - ret = regmap_write(tcpci->regmap, TCPC_MSG_HDR_INFO, reg); - if (ret < 0) - return ret; - - return 0; -} - -static int tcpci_set_pd_rx(struct tcpc_dev *tcpc, bool enable) -{ - struct tcpci *tcpci = tcpc_to_tcpci(tcpc); - unsigned int reg = 0; - int ret; - - if (enable) - reg = TCPC_RX_DETECT_SOP | TCPC_RX_DETECT_HARD_RESET; - ret = regmap_write(tcpci->regmap, TCPC_RX_DETECT, reg); - if (ret < 0) - return ret; - - return 0; -} - -static int tcpci_get_vbus(struct tcpc_dev *tcpc) -{ - struct tcpci *tcpci = tcpc_to_tcpci(tcpc); - unsigned int reg; - int ret; - - ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, ®); - if (ret < 0) - return ret; - - return !!(reg & TCPC_POWER_STATUS_VBUS_PRES); -} - -static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink) -{ - struct tcpci *tcpci = tcpc_to_tcpci(tcpc); - int ret; - - /* Disable both source and sink first before enabling anything */ - - if (!source) { - ret = regmap_write(tcpci->regmap, TCPC_COMMAND, - TCPC_CMD_DISABLE_SRC_VBUS); - if (ret < 0) - return ret; - } - - if (!sink) { - ret = regmap_write(tcpci->regmap, TCPC_COMMAND, - TCPC_CMD_DISABLE_SINK_VBUS); - if (ret < 0) - return ret; - } - - if (source) { - ret = regmap_write(tcpci->regmap, TCPC_COMMAND, - TCPC_CMD_SRC_VBUS_DEFAULT); - if (ret < 0) - return ret; - } - - if (sink) { - ret = regmap_write(tcpci->regmap, TCPC_COMMAND, - TCPC_CMD_SINK_VBUS); - if (ret < 0) - return ret; - } - - return 0; -} - -static int tcpci_pd_transmit(struct tcpc_dev *tcpc, - enum tcpm_transmit_type type, - const struct pd_message *msg) -{ - struct tcpci *tcpci = tcpc_to_tcpci(tcpc); - u16 header = msg ? le16_to_cpu(msg->header) : 0; - unsigned int reg, cnt; - int ret; - - cnt = msg ? pd_header_cnt(header) * 4 : 0; - ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2); - if (ret < 0) - return ret; - - ret = tcpci_write16(tcpci, TCPC_TX_HDR, header); - if (ret < 0) - return ret; - - if (cnt > 0) { - ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA, - &msg->payload, cnt); - if (ret < 0) - return ret; - } - - reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) | - (type << TCPC_TRANSMIT_TYPE_SHIFT); - ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg); - if (ret < 0) - return ret; - - return 0; -} - -static int tcpci_init(struct tcpc_dev *tcpc) -{ - struct tcpci *tcpci = tcpc_to_tcpci(tcpc); - unsigned long timeout = jiffies + msecs_to_jiffies(2000); /* XXX */ - unsigned int reg; - int ret; - - while (time_before_eq(jiffies, timeout)) { - ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, ®); - if (ret < 0) - return ret; - if (!(reg & TCPC_POWER_STATUS_UNINIT)) - break; - usleep_range(10000, 20000); - } - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - - /* Handle vendor init */ - if (tcpci->data->init) { - ret = tcpci->data->init(tcpci, tcpci->data); - if (ret < 0) - return ret; - } - - /* Clear all events */ - ret = tcpci_write16(tcpci, TCPC_ALERT, 0xffff); - if (ret < 0) - return ret; - - if (tcpci->controls_vbus) - reg = TCPC_POWER_STATUS_VBUS_PRES; - else - reg = 0; - ret = regmap_write(tcpci->regmap, TCPC_POWER_STATUS_MASK, reg); - if (ret < 0) - return ret; - - /* Enable Vbus detection */ - ret = regmap_write(tcpci->regmap, TCPC_COMMAND, - TCPC_CMD_ENABLE_VBUS_DETECT); - if (ret < 0) - return ret; - - reg = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_FAILED | - TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_RX_STATUS | - TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS; - if (tcpci->controls_vbus) - reg |= TCPC_ALERT_POWER_STATUS; - return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg); -} - -irqreturn_t tcpci_irq(struct tcpci *tcpci) -{ - u16 status; - - tcpci_read16(tcpci, TCPC_ALERT, &status); - - /* - * Clear alert status for everything except RX_STATUS, which shouldn't - * be cleared until we have successfully retrieved message. - */ - if (status & ~TCPC_ALERT_RX_STATUS) - tcpci_write16(tcpci, TCPC_ALERT, - status & ~TCPC_ALERT_RX_STATUS); - - if (status & TCPC_ALERT_CC_STATUS) - tcpm_cc_change(tcpci->port); - - if (status & TCPC_ALERT_POWER_STATUS) { - unsigned int reg; - - regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, ®); - - /* - * If power status mask has been reset, then the TCPC - * has reset. - */ - if (reg == 0xff) - tcpm_tcpc_reset(tcpci->port); - else - tcpm_vbus_change(tcpci->port); - } - - if (status & TCPC_ALERT_RX_STATUS) { - struct pd_message msg; - unsigned int cnt; - u16 header; - - regmap_read(tcpci->regmap, TCPC_RX_BYTE_CNT, &cnt); - - tcpci_read16(tcpci, TCPC_RX_HDR, &header); - msg.header = cpu_to_le16(header); - - if (WARN_ON(cnt > sizeof(msg.payload))) - cnt = sizeof(msg.payload); - - if (cnt > 0) - regmap_raw_read(tcpci->regmap, TCPC_RX_DATA, - &msg.payload, cnt); - - /* Read complete, clear RX status alert bit */ - tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS); - - tcpm_pd_receive(tcpci->port, &msg); - } - - if (status & TCPC_ALERT_RX_HARD_RST) - tcpm_pd_hard_reset(tcpci->port); - - if (status & TCPC_ALERT_TX_SUCCESS) - tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_SUCCESS); - else if (status & TCPC_ALERT_TX_DISCARDED) - tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_DISCARDED); - else if (status & TCPC_ALERT_TX_FAILED) - tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_FAILED); - - return IRQ_HANDLED; -} -EXPORT_SYMBOL_GPL(tcpci_irq); - -static irqreturn_t _tcpci_irq(int irq, void *dev_id) -{ - struct tcpci_chip *chip = dev_id; - - return tcpci_irq(chip->tcpci); -} - -static const struct regmap_config tcpci_regmap_config = { - .reg_bits = 8, - .val_bits = 8, - - .max_register = 0x7F, /* 0x80 .. 0xFF are vendor defined */ -}; - -static int tcpci_parse_config(struct tcpci *tcpci) -{ - tcpci->controls_vbus = true; /* XXX */ - - tcpci->tcpc.fwnode = device_get_named_child_node(tcpci->dev, - "connector"); - if (!tcpci->tcpc.fwnode) { - dev_err(tcpci->dev, "Can't find connector node.\n"); - return -EINVAL; - } - - return 0; -} - -struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) -{ - struct tcpci *tcpci; - int err; - - tcpci = devm_kzalloc(dev, sizeof(*tcpci), GFP_KERNEL); - if (!tcpci) - return ERR_PTR(-ENOMEM); - - tcpci->dev = dev; - tcpci->data = data; - tcpci->regmap = data->regmap; - - tcpci->tcpc.init = tcpci_init; - tcpci->tcpc.get_vbus = tcpci_get_vbus; - tcpci->tcpc.set_vbus = tcpci_set_vbus; - tcpci->tcpc.set_cc = tcpci_set_cc; - tcpci->tcpc.get_cc = tcpci_get_cc; - tcpci->tcpc.set_polarity = tcpci_set_polarity; - tcpci->tcpc.set_vconn = tcpci_set_vconn; - tcpci->tcpc.start_drp_toggling = tcpci_start_drp_toggling; - - tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx; - tcpci->tcpc.set_roles = tcpci_set_roles; - tcpci->tcpc.pd_transmit = tcpci_pd_transmit; - - err = tcpci_parse_config(tcpci); - if (err < 0) - return ERR_PTR(err); - - tcpci->port = tcpm_register_port(tcpci->dev, &tcpci->tcpc); - if (IS_ERR(tcpci->port)) - return ERR_CAST(tcpci->port); - - return tcpci; -} -EXPORT_SYMBOL_GPL(tcpci_register_port); - -void tcpci_unregister_port(struct tcpci *tcpci) -{ - tcpm_unregister_port(tcpci->port); -} -EXPORT_SYMBOL_GPL(tcpci_unregister_port); - -static int tcpci_probe(struct i2c_client *client, - const struct i2c_device_id *i2c_id) -{ - struct tcpci_chip *chip; - int err; - u16 val = 0; - - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->data.regmap = devm_regmap_init_i2c(client, &tcpci_regmap_config); - if (IS_ERR(chip->data.regmap)) - return PTR_ERR(chip->data.regmap); - - i2c_set_clientdata(client, chip); - - /* Disable chip interrupts before requesting irq */ - err = regmap_raw_write(chip->data.regmap, TCPC_ALERT_MASK, &val, - sizeof(u16)); - if (err < 0) - return err; - - chip->tcpci = tcpci_register_port(&client->dev, &chip->data); - if (IS_ERR(chip->tcpci)) - return PTR_ERR(chip->tcpci); - - err = devm_request_threaded_irq(&client->dev, client->irq, NULL, - _tcpci_irq, - IRQF_ONESHOT | IRQF_TRIGGER_LOW, - dev_name(&client->dev), chip); - if (err < 0) { - tcpci_unregister_port(chip->tcpci); - return err; - } - - return 0; -} - -static int tcpci_remove(struct i2c_client *client) -{ - struct tcpci_chip *chip = i2c_get_clientdata(client); - - tcpci_unregister_port(chip->tcpci); - - return 0; -} - -static const struct i2c_device_id tcpci_id[] = { - { "tcpci", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tcpci_id); - -#ifdef CONFIG_OF -static const struct of_device_id tcpci_of_match[] = { - { .compatible = "nxp,ptn5110", }, - {}, -}; -MODULE_DEVICE_TABLE(of, tcpci_of_match); -#endif - -static struct i2c_driver tcpci_i2c_driver = { - .driver = { - .name = "tcpci", - .of_match_table = of_match_ptr(tcpci_of_match), - }, - .probe = tcpci_probe, - .remove = tcpci_remove, - .id_table = tcpci_id, -}; -module_i2c_driver(tcpci_i2c_driver); - -MODULE_DESCRIPTION("USB Type-C Port Controller Interface driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/typec/tcpci.h b/drivers/staging/typec/tcpci.h deleted file mode 100644 index 303ebde26546..000000000000 --- a/drivers/staging/typec/tcpci.h +++ /dev/null @@ -1,139 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright 2015-2017 Google, Inc - * - * USB Type-C Port Controller Interface. - */ - -#ifndef __LINUX_USB_TCPCI_H -#define __LINUX_USB_TCPCI_H - -#define TCPC_VENDOR_ID 0x0 -#define TCPC_PRODUCT_ID 0x2 -#define TCPC_BCD_DEV 0x4 -#define TCPC_TC_REV 0x6 -#define TCPC_PD_REV 0x8 -#define TCPC_PD_INT_REV 0xa - -#define TCPC_ALERT 0x10 -#define TCPC_ALERT_VBUS_DISCNCT BIT(11) -#define TCPC_ALERT_RX_BUF_OVF BIT(10) -#define TCPC_ALERT_FAULT BIT(9) -#define TCPC_ALERT_V_ALARM_LO BIT(8) -#define TCPC_ALERT_V_ALARM_HI BIT(7) -#define TCPC_ALERT_TX_SUCCESS BIT(6) -#define TCPC_ALERT_TX_DISCARDED BIT(5) -#define TCPC_ALERT_TX_FAILED BIT(4) -#define TCPC_ALERT_RX_HARD_RST BIT(3) -#define TCPC_ALERT_RX_STATUS BIT(2) -#define TCPC_ALERT_POWER_STATUS BIT(1) -#define TCPC_ALERT_CC_STATUS BIT(0) - -#define TCPC_ALERT_MASK 0x12 -#define TCPC_POWER_STATUS_MASK 0x14 -#define TCPC_FAULT_STATUS_MASK 0x15 -#define TCPC_CONFIG_STD_OUTPUT 0x18 - -#define TCPC_TCPC_CTRL 0x19 -#define TCPC_TCPC_CTRL_ORIENTATION BIT(0) - -#define TCPC_ROLE_CTRL 0x1a -#define TCPC_ROLE_CTRL_DRP BIT(6) -#define TCPC_ROLE_CTRL_RP_VAL_SHIFT 4 -#define TCPC_ROLE_CTRL_RP_VAL_MASK 0x3 -#define TCPC_ROLE_CTRL_RP_VAL_DEF 0x0 -#define TCPC_ROLE_CTRL_RP_VAL_1_5 0x1 -#define TCPC_ROLE_CTRL_RP_VAL_3_0 0x2 -#define TCPC_ROLE_CTRL_CC2_SHIFT 2 -#define TCPC_ROLE_CTRL_CC2_MASK 0x3 -#define TCPC_ROLE_CTRL_CC1_SHIFT 0 -#define TCPC_ROLE_CTRL_CC1_MASK 0x3 -#define TCPC_ROLE_CTRL_CC_RA 0x0 -#define TCPC_ROLE_CTRL_CC_RP 0x1 -#define TCPC_ROLE_CTRL_CC_RD 0x2 -#define TCPC_ROLE_CTRL_CC_OPEN 0x3 - -#define TCPC_FAULT_CTRL 0x1b - -#define TCPC_POWER_CTRL 0x1c -#define TCPC_POWER_CTRL_VCONN_ENABLE BIT(0) - -#define TCPC_CC_STATUS 0x1d -#define TCPC_CC_STATUS_TOGGLING BIT(5) -#define TCPC_CC_STATUS_TERM BIT(4) -#define TCPC_CC_STATUS_CC2_SHIFT 2 -#define TCPC_CC_STATUS_CC2_MASK 0x3 -#define TCPC_CC_STATUS_CC1_SHIFT 0 -#define TCPC_CC_STATUS_CC1_MASK 0x3 - -#define TCPC_POWER_STATUS 0x1e -#define TCPC_POWER_STATUS_UNINIT BIT(6) -#define TCPC_POWER_STATUS_VBUS_DET BIT(3) -#define TCPC_POWER_STATUS_VBUS_PRES BIT(2) - -#define TCPC_FAULT_STATUS 0x1f - -#define TCPC_COMMAND 0x23 -#define TCPC_CMD_WAKE_I2C 0x11 -#define TCPC_CMD_DISABLE_VBUS_DETECT 0x22 -#define TCPC_CMD_ENABLE_VBUS_DETECT 0x33 -#define TCPC_CMD_DISABLE_SINK_VBUS 0x44 -#define TCPC_CMD_SINK_VBUS 0x55 -#define TCPC_CMD_DISABLE_SRC_VBUS 0x66 -#define TCPC_CMD_SRC_VBUS_DEFAULT 0x77 -#define TCPC_CMD_SRC_VBUS_HIGH 0x88 -#define TCPC_CMD_LOOK4CONNECTION 0x99 -#define TCPC_CMD_RXONEMORE 0xAA -#define TCPC_CMD_I2C_IDLE 0xFF - -#define TCPC_DEV_CAP_1 0x24 -#define TCPC_DEV_CAP_2 0x26 -#define TCPC_STD_INPUT_CAP 0x28 -#define TCPC_STD_OUTPUT_CAP 0x29 - -#define TCPC_MSG_HDR_INFO 0x2e -#define TCPC_MSG_HDR_INFO_DATA_ROLE BIT(3) -#define TCPC_MSG_HDR_INFO_PWR_ROLE BIT(0) -#define TCPC_MSG_HDR_INFO_REV_SHIFT 1 -#define TCPC_MSG_HDR_INFO_REV_MASK 0x3 - -#define TCPC_RX_DETECT 0x2f -#define TCPC_RX_DETECT_HARD_RESET BIT(5) -#define TCPC_RX_DETECT_SOP BIT(0) - -#define TCPC_RX_BYTE_CNT 0x30 -#define TCPC_RX_BUF_FRAME_TYPE 0x31 -#define TCPC_RX_HDR 0x32 -#define TCPC_RX_DATA 0x34 /* through 0x4f */ - -#define TCPC_TRANSMIT 0x50 -#define TCPC_TRANSMIT_RETRY_SHIFT 4 -#define TCPC_TRANSMIT_RETRY_MASK 0x3 -#define TCPC_TRANSMIT_TYPE_SHIFT 0 -#define TCPC_TRANSMIT_TYPE_MASK 0x7 - -#define TCPC_TX_BYTE_CNT 0x51 -#define TCPC_TX_HDR 0x52 -#define TCPC_TX_DATA 0x54 /* through 0x6f */ - -#define TCPC_VBUS_VOLTAGE 0x70 -#define TCPC_VBUS_SINK_DISCONNECT_THRESH 0x72 -#define TCPC_VBUS_STOP_DISCHARGE_THRESH 0x74 -#define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG 0x76 -#define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG 0x78 - -struct tcpci; -struct tcpci_data { - struct regmap *regmap; - int (*init)(struct tcpci *tcpci, struct tcpci_data *data); - int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data, - bool enable); - int (*start_drp_toggling)(struct tcpci *tcpci, struct tcpci_data *data, - enum typec_cc_status cc); -}; - -struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data); -void tcpci_unregister_port(struct tcpci *tcpci); -irqreturn_t tcpci_irq(struct tcpci *tcpci); - -#endif /* __LINUX_USB_TCPCI_H */ diff --git a/drivers/staging/typec/tcpci_rt1711h.c b/drivers/staging/typec/tcpci_rt1711h.c deleted file mode 100644 index 017389021b96..000000000000 --- a/drivers/staging/typec/tcpci_rt1711h.c +++ /dev/null @@ -1,312 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2018, Richtek Technology Corporation - * - * Richtek RT1711H Type-C Chip Driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include "tcpci.h" - -#define RT1711H_VID 0x29CF -#define RT1711H_PID 0x1711 - -#define RT1711H_RTCTRL8 0x9B - -/* Autoidle timeout = (tout * 2 + 1) * 6.4ms */ -#define RT1711H_RTCTRL8_SET(ck300, ship_off, auto_idle, tout) \ - (((ck300) << 7) | ((ship_off) << 5) | \ - ((auto_idle) << 3) | ((tout) & 0x07)) - -#define RT1711H_RTCTRL11 0x9E - -/* I2C timeout = (tout + 1) * 12.5ms */ -#define RT1711H_RTCTRL11_SET(en, tout) \ - (((en) << 7) | ((tout) & 0x0F)) - -#define RT1711H_RTCTRL13 0xA0 -#define RT1711H_RTCTRL14 0xA1 -#define RT1711H_RTCTRL15 0xA2 -#define RT1711H_RTCTRL16 0xA3 - -struct rt1711h_chip { - struct tcpci_data data; - struct tcpci *tcpci; - struct device *dev; -}; - -static int rt1711h_read16(struct rt1711h_chip *chip, unsigned int reg, u16 *val) -{ - return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u16)); -} - -static int rt1711h_write16(struct rt1711h_chip *chip, unsigned int reg, u16 val) -{ - return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u16)); -} - -static int rt1711h_read8(struct rt1711h_chip *chip, unsigned int reg, u8 *val) -{ - return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u8)); -} - -static int rt1711h_write8(struct rt1711h_chip *chip, unsigned int reg, u8 val) -{ - return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8)); -} - -static const struct regmap_config rt1711h_regmap_config = { - .reg_bits = 8, - .val_bits = 8, - - .max_register = 0xFF, /* 0x80 .. 0xFF are vendor defined */ -}; - -static struct rt1711h_chip *tdata_to_rt1711h(struct tcpci_data *tdata) -{ - return container_of(tdata, struct rt1711h_chip, data); -} - -static int rt1711h_init(struct tcpci *tcpci, struct tcpci_data *tdata) -{ - int ret; - struct rt1711h_chip *chip = tdata_to_rt1711h(tdata); - - /* CK 300K from 320K, shipping off, auto_idle enable, tout = 32ms */ - ret = rt1711h_write8(chip, RT1711H_RTCTRL8, - RT1711H_RTCTRL8_SET(0, 1, 1, 2)); - if (ret < 0) - return ret; - - /* I2C reset : (val + 1) * 12.5ms */ - ret = rt1711h_write8(chip, RT1711H_RTCTRL11, - RT1711H_RTCTRL11_SET(1, 0x0F)); - if (ret < 0) - return ret; - - /* tTCPCfilter : (26.7 * val) us */ - ret = rt1711h_write8(chip, RT1711H_RTCTRL14, 0x0F); - if (ret < 0) - return ret; - - /* tDRP : (51.2 + 6.4 * val) ms */ - ret = rt1711h_write8(chip, RT1711H_RTCTRL15, 0x04); - if (ret < 0) - return ret; - - /* dcSRC.DRP : 33% */ - return rt1711h_write16(chip, RT1711H_RTCTRL16, 330); -} - -static int rt1711h_set_vconn(struct tcpci *tcpci, struct tcpci_data *tdata, - bool enable) -{ - struct rt1711h_chip *chip = tdata_to_rt1711h(tdata); - - return rt1711h_write8(chip, RT1711H_RTCTRL8, - RT1711H_RTCTRL8_SET(0, 1, !enable, 2)); -} - -static int rt1711h_start_drp_toggling(struct tcpci *tcpci, - struct tcpci_data *tdata, - enum typec_cc_status cc) -{ - struct rt1711h_chip *chip = tdata_to_rt1711h(tdata); - int ret; - unsigned int reg = 0; - - switch (cc) { - default: - case TYPEC_CC_RP_DEF: - reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); - break; - case TYPEC_CC_RP_1_5: - reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); - break; - case TYPEC_CC_RP_3_0: - reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 << - TCPC_ROLE_CTRL_RP_VAL_SHIFT); - break; - } - - if (cc == TYPEC_CC_RD) - reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); - else - reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | - (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT); - - ret = rt1711h_write8(chip, TCPC_ROLE_CTRL, reg); - if (ret < 0) - return ret; - usleep_range(500, 1000); - - return 0; -} - -static irqreturn_t rt1711h_irq(int irq, void *dev_id) -{ - int ret; - u16 alert; - u8 status; - struct rt1711h_chip *chip = dev_id; - - if (!chip->tcpci) - return IRQ_HANDLED; - - ret = rt1711h_read16(chip, TCPC_ALERT, &alert); - if (ret < 0) - goto out; - - if (alert & TCPC_ALERT_CC_STATUS) { - ret = rt1711h_read8(chip, TCPC_CC_STATUS, &status); - if (ret < 0) - goto out; - /* Clear cc change event triggered by starting toggling */ - if (status & TCPC_CC_STATUS_TOGGLING) - rt1711h_write8(chip, TCPC_ALERT, TCPC_ALERT_CC_STATUS); - } - -out: - return tcpci_irq(chip->tcpci); -} - -static int rt1711h_init_alert(struct rt1711h_chip *chip, - struct i2c_client *client) -{ - int ret; - - /* Disable chip interrupts before requesting irq */ - ret = rt1711h_write16(chip, TCPC_ALERT_MASK, 0); - if (ret < 0) - return ret; - - ret = devm_request_threaded_irq(chip->dev, client->irq, NULL, - rt1711h_irq, - IRQF_ONESHOT | IRQF_TRIGGER_LOW, - dev_name(chip->dev), chip); - if (ret < 0) - return ret; - enable_irq_wake(client->irq); - return 0; -} - -static int rt1711h_sw_reset(struct rt1711h_chip *chip) -{ - int ret; - - ret = rt1711h_write8(chip, RT1711H_RTCTRL13, 0x01); - if (ret < 0) - return ret; - - usleep_range(1000, 2000); - return 0; -} - -static int rt1711h_check_revision(struct i2c_client *i2c) -{ - int ret; - - ret = i2c_smbus_read_word_data(i2c, TCPC_VENDOR_ID); - if (ret < 0) - return ret; - if (ret != RT1711H_VID) { - dev_err(&i2c->dev, "vid is not correct, 0x%04x\n", ret); - return -ENODEV; - } - ret = i2c_smbus_read_word_data(i2c, TCPC_PRODUCT_ID); - if (ret < 0) - return ret; - if (ret != RT1711H_PID) { - dev_err(&i2c->dev, "pid is not correct, 0x%04x\n", ret); - return -ENODEV; - } - return 0; -} - -static int rt1711h_probe(struct i2c_client *client, - const struct i2c_device_id *i2c_id) -{ - int ret; - struct rt1711h_chip *chip; - - ret = rt1711h_check_revision(client); - if (ret < 0) { - dev_err(&client->dev, "check vid/pid fail\n"); - return ret; - } - - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->data.regmap = devm_regmap_init_i2c(client, - &rt1711h_regmap_config); - if (IS_ERR(chip->data.regmap)) - return PTR_ERR(chip->data.regmap); - - chip->dev = &client->dev; - i2c_set_clientdata(client, chip); - - ret = rt1711h_sw_reset(chip); - if (ret < 0) - return ret; - - ret = rt1711h_init_alert(chip, client); - if (ret < 0) - return ret; - - chip->data.init = rt1711h_init; - chip->data.set_vconn = rt1711h_set_vconn; - chip->data.start_drp_toggling = rt1711h_start_drp_toggling; - chip->tcpci = tcpci_register_port(chip->dev, &chip->data); - if (IS_ERR_OR_NULL(chip->tcpci)) - return PTR_ERR(chip->tcpci); - - return 0; -} - -static int rt1711h_remove(struct i2c_client *client) -{ - struct rt1711h_chip *chip = i2c_get_clientdata(client); - - tcpci_unregister_port(chip->tcpci); - return 0; -} - -static const struct i2c_device_id rt1711h_id[] = { - { "rt1711h", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, rt1711h_id); - -#ifdef CONFIG_OF -static const struct of_device_id rt1711h_of_match[] = { - { .compatible = "richtek,rt1711h", }, - {}, -}; -MODULE_DEVICE_TABLE(of, rt1711h_of_match); -#endif - -static struct i2c_driver rt1711h_i2c_driver = { - .driver = { - .name = "rt1711h", - .of_match_table = of_match_ptr(rt1711h_of_match), - }, - .probe = rt1711h_probe, - .remove = rt1711h_remove, - .id_table = rt1711h_id, -}; -module_i2c_driver(rt1711h_i2c_driver); - -MODULE_AUTHOR("ShuFan Lee "); -MODULE_DESCRIPTION("RT1711H USB Type-C Port Controller Interface Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index 2c8eab11a493..972fd1911045 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -56,6 +56,21 @@ config TYPEC_TCPM if TYPEC_TCPM +config TYPEC_TCPCI + tristate "Type-C Port Controller Interface driver" + depends on I2C + select REGMAP_I2C + help + Type-C Port Controller driver for TCPCI-compliant controller. + +config TYPEC_RT1711H + tristate "Richtek RT1711H Type-C chip driver" + select TYPEC_TCPCI + help + Richtek RT1711H Type-C chip driver that works with + Type-C Port Controller Manager to provide USB PD and USB + Type-C functionalities. + source "drivers/usb/typec/fusb302/Kconfig" config TYPEC_WCOVE diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 1f599a6c30cc..46f86ee134a2 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o obj-$(CONFIG_TYPEC_UCSI) += ucsi/ obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o obj-$(CONFIG_TYPEC) += mux/ +obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o +obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o diff --git a/drivers/usb/typec/tcpci.c b/drivers/usb/typec/tcpci.c new file mode 100644 index 000000000000..ac6b418b15f1 --- /dev/null +++ b/drivers/usb/typec/tcpci.c @@ -0,0 +1,612 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015-2017 Google, Inc + * + * USB Type-C Port Controller Interface. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tcpci.h" + +#define PD_RETRY_COUNT 3 + +struct tcpci { + struct device *dev; + + struct tcpm_port *port; + + struct regmap *regmap; + + bool controls_vbus; + + struct tcpc_dev tcpc; + struct tcpci_data *data; +}; + +struct tcpci_chip { + struct tcpci *tcpci; + struct tcpci_data data; +}; + +static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc) +{ + return container_of(tcpc, struct tcpci, tcpc); +} + +static int tcpci_read16(struct tcpci *tcpci, unsigned int reg, u16 *val) +{ + return regmap_raw_read(tcpci->regmap, reg, val, sizeof(u16)); +} + +static int tcpci_write16(struct tcpci *tcpci, unsigned int reg, u16 val) +{ + return regmap_raw_write(tcpci->regmap, reg, &val, sizeof(u16)); +} + +static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + unsigned int reg; + int ret; + + switch (cc) { + case TYPEC_CC_RA: + reg = (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC2_SHIFT); + break; + case TYPEC_CC_RD: + reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); + break; + case TYPEC_CC_RP_DEF: + reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | + (TCPC_ROLE_CTRL_RP_VAL_DEF << + TCPC_ROLE_CTRL_RP_VAL_SHIFT); + break; + case TYPEC_CC_RP_1_5: + reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | + (TCPC_ROLE_CTRL_RP_VAL_1_5 << + TCPC_ROLE_CTRL_RP_VAL_SHIFT); + break; + case TYPEC_CC_RP_3_0: + reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) | + (TCPC_ROLE_CTRL_RP_VAL_3_0 << + TCPC_ROLE_CTRL_RP_VAL_SHIFT); + break; + case TYPEC_CC_OPEN: + default: + reg = (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT); + break; + } + + ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); + if (ret < 0) + return ret; + + return 0; +} + +static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc, + enum typec_cc_status cc) +{ + int ret; + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + unsigned int reg = TCPC_ROLE_CTRL_DRP; + + /* Handle vendor drp toggling */ + if (tcpci->data->start_drp_toggling) { + ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc); + if (ret < 0) + return ret; + } + + switch (cc) { + default: + case TYPEC_CC_RP_DEF: + reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF << + TCPC_ROLE_CTRL_RP_VAL_SHIFT); + break; + case TYPEC_CC_RP_1_5: + reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 << + TCPC_ROLE_CTRL_RP_VAL_SHIFT); + break; + case TYPEC_CC_RP_3_0: + reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 << + TCPC_ROLE_CTRL_RP_VAL_SHIFT); + break; + } + + if (cc == TYPEC_CC_RD) + reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); + else + reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT); + ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); + if (ret < 0) + return ret; + return regmap_write(tcpci->regmap, TCPC_COMMAND, + TCPC_CMD_LOOK4CONNECTION); +} + +static enum typec_cc_status tcpci_to_typec_cc(unsigned int cc, bool sink) +{ + switch (cc) { + case 0x1: + return sink ? TYPEC_CC_RP_DEF : TYPEC_CC_RA; + case 0x2: + return sink ? TYPEC_CC_RP_1_5 : TYPEC_CC_RD; + case 0x3: + if (sink) + return TYPEC_CC_RP_3_0; + /* fall through */ + case 0x0: + default: + return TYPEC_CC_OPEN; + } +} + +static int tcpci_get_cc(struct tcpc_dev *tcpc, + enum typec_cc_status *cc1, enum typec_cc_status *cc2) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + unsigned int reg; + int ret; + + ret = regmap_read(tcpci->regmap, TCPC_CC_STATUS, ®); + if (ret < 0) + return ret; + + *cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) & + TCPC_CC_STATUS_CC1_MASK, + reg & TCPC_CC_STATUS_TERM); + *cc2 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC2_SHIFT) & + TCPC_CC_STATUS_CC2_MASK, + reg & TCPC_CC_STATUS_TERM); + + return 0; +} + +static int tcpci_set_polarity(struct tcpc_dev *tcpc, + enum typec_cc_polarity polarity) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + unsigned int reg; + int ret; + + /* Keep the disconnect cc line open */ + ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, ®); + if (ret < 0) + return ret; + + if (polarity == TYPEC_POLARITY_CC2) + reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT; + else + reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT; + ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); + if (ret < 0) + return ret; + + return regmap_write(tcpci->regmap, TCPC_TCPC_CTRL, + (polarity == TYPEC_POLARITY_CC2) ? + TCPC_TCPC_CTRL_ORIENTATION : 0); +} + +static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + int ret; + + /* Handle vendor set vconn */ + if (tcpci->data->set_vconn) { + ret = tcpci->data->set_vconn(tcpci, tcpci->data, enable); + if (ret < 0) + return ret; + } + + return regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, + TCPC_POWER_CTRL_VCONN_ENABLE, + enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0); +} + +static int tcpci_set_roles(struct tcpc_dev *tcpc, bool attached, + enum typec_role role, enum typec_data_role data) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + unsigned int reg; + int ret; + + reg = PD_REV20 << TCPC_MSG_HDR_INFO_REV_SHIFT; + if (role == TYPEC_SOURCE) + reg |= TCPC_MSG_HDR_INFO_PWR_ROLE; + if (data == TYPEC_HOST) + reg |= TCPC_MSG_HDR_INFO_DATA_ROLE; + ret = regmap_write(tcpci->regmap, TCPC_MSG_HDR_INFO, reg); + if (ret < 0) + return ret; + + return 0; +} + +static int tcpci_set_pd_rx(struct tcpc_dev *tcpc, bool enable) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + unsigned int reg = 0; + int ret; + + if (enable) + reg = TCPC_RX_DETECT_SOP | TCPC_RX_DETECT_HARD_RESET; + ret = regmap_write(tcpci->regmap, TCPC_RX_DETECT, reg); + if (ret < 0) + return ret; + + return 0; +} + +static int tcpci_get_vbus(struct tcpc_dev *tcpc) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + unsigned int reg; + int ret; + + ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, ®); + if (ret < 0) + return ret; + + return !!(reg & TCPC_POWER_STATUS_VBUS_PRES); +} + +static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + int ret; + + /* Disable both source and sink first before enabling anything */ + + if (!source) { + ret = regmap_write(tcpci->regmap, TCPC_COMMAND, + TCPC_CMD_DISABLE_SRC_VBUS); + if (ret < 0) + return ret; + } + + if (!sink) { + ret = regmap_write(tcpci->regmap, TCPC_COMMAND, + TCPC_CMD_DISABLE_SINK_VBUS); + if (ret < 0) + return ret; + } + + if (source) { + ret = regmap_write(tcpci->regmap, TCPC_COMMAND, + TCPC_CMD_SRC_VBUS_DEFAULT); + if (ret < 0) + return ret; + } + + if (sink) { + ret = regmap_write(tcpci->regmap, TCPC_COMMAND, + TCPC_CMD_SINK_VBUS); + if (ret < 0) + return ret; + } + + return 0; +} + +static int tcpci_pd_transmit(struct tcpc_dev *tcpc, + enum tcpm_transmit_type type, + const struct pd_message *msg) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + u16 header = msg ? le16_to_cpu(msg->header) : 0; + unsigned int reg, cnt; + int ret; + + cnt = msg ? pd_header_cnt(header) * 4 : 0; + ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2); + if (ret < 0) + return ret; + + ret = tcpci_write16(tcpci, TCPC_TX_HDR, header); + if (ret < 0) + return ret; + + if (cnt > 0) { + ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA, + &msg->payload, cnt); + if (ret < 0) + return ret; + } + + reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) | + (type << TCPC_TRANSMIT_TYPE_SHIFT); + ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg); + if (ret < 0) + return ret; + + return 0; +} + +static int tcpci_init(struct tcpc_dev *tcpc) +{ + struct tcpci *tcpci = tcpc_to_tcpci(tcpc); + unsigned long timeout = jiffies + msecs_to_jiffies(2000); /* XXX */ + unsigned int reg; + int ret; + + while (time_before_eq(jiffies, timeout)) { + ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, ®); + if (ret < 0) + return ret; + if (!(reg & TCPC_POWER_STATUS_UNINIT)) + break; + usleep_range(10000, 20000); + } + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + + /* Handle vendor init */ + if (tcpci->data->init) { + ret = tcpci->data->init(tcpci, tcpci->data); + if (ret < 0) + return ret; + } + + /* Clear all events */ + ret = tcpci_write16(tcpci, TCPC_ALERT, 0xffff); + if (ret < 0) + return ret; + + if (tcpci->controls_vbus) + reg = TCPC_POWER_STATUS_VBUS_PRES; + else + reg = 0; + ret = regmap_write(tcpci->regmap, TCPC_POWER_STATUS_MASK, reg); + if (ret < 0) + return ret; + + /* Enable Vbus detection */ + ret = regmap_write(tcpci->regmap, TCPC_COMMAND, + TCPC_CMD_ENABLE_VBUS_DETECT); + if (ret < 0) + return ret; + + reg = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_FAILED | + TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_RX_STATUS | + TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS; + if (tcpci->controls_vbus) + reg |= TCPC_ALERT_POWER_STATUS; + return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg); +} + +irqreturn_t tcpci_irq(struct tcpci *tcpci) +{ + u16 status; + + tcpci_read16(tcpci, TCPC_ALERT, &status); + + /* + * Clear alert status for everything except RX_STATUS, which shouldn't + * be cleared until we have successfully retrieved message. + */ + if (status & ~TCPC_ALERT_RX_STATUS) + tcpci_write16(tcpci, TCPC_ALERT, + status & ~TCPC_ALERT_RX_STATUS); + + if (status & TCPC_ALERT_CC_STATUS) + tcpm_cc_change(tcpci->port); + + if (status & TCPC_ALERT_POWER_STATUS) { + unsigned int reg; + + regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, ®); + + /* + * If power status mask has been reset, then the TCPC + * has reset. + */ + if (reg == 0xff) + tcpm_tcpc_reset(tcpci->port); + else + tcpm_vbus_change(tcpci->port); + } + + if (status & TCPC_ALERT_RX_STATUS) { + struct pd_message msg; + unsigned int cnt; + u16 header; + + regmap_read(tcpci->regmap, TCPC_RX_BYTE_CNT, &cnt); + + tcpci_read16(tcpci, TCPC_RX_HDR, &header); + msg.header = cpu_to_le16(header); + + if (WARN_ON(cnt > sizeof(msg.payload))) + cnt = sizeof(msg.payload); + + if (cnt > 0) + regmap_raw_read(tcpci->regmap, TCPC_RX_DATA, + &msg.payload, cnt); + + /* Read complete, clear RX status alert bit */ + tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS); + + tcpm_pd_receive(tcpci->port, &msg); + } + + if (status & TCPC_ALERT_RX_HARD_RST) + tcpm_pd_hard_reset(tcpci->port); + + if (status & TCPC_ALERT_TX_SUCCESS) + tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_SUCCESS); + else if (status & TCPC_ALERT_TX_DISCARDED) + tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_DISCARDED); + else if (status & TCPC_ALERT_TX_FAILED) + tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_FAILED); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(tcpci_irq); + +static irqreturn_t _tcpci_irq(int irq, void *dev_id) +{ + struct tcpci_chip *chip = dev_id; + + return tcpci_irq(chip->tcpci); +} + +static const struct regmap_config tcpci_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0x7F, /* 0x80 .. 0xFF are vendor defined */ +}; + +static int tcpci_parse_config(struct tcpci *tcpci) +{ + tcpci->controls_vbus = true; /* XXX */ + + tcpci->tcpc.fwnode = device_get_named_child_node(tcpci->dev, + "connector"); + if (!tcpci->tcpc.fwnode) { + dev_err(tcpci->dev, "Can't find connector node.\n"); + return -EINVAL; + } + + return 0; +} + +struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) +{ + struct tcpci *tcpci; + int err; + + tcpci = devm_kzalloc(dev, sizeof(*tcpci), GFP_KERNEL); + if (!tcpci) + return ERR_PTR(-ENOMEM); + + tcpci->dev = dev; + tcpci->data = data; + tcpci->regmap = data->regmap; + + tcpci->tcpc.init = tcpci_init; + tcpci->tcpc.get_vbus = tcpci_get_vbus; + tcpci->tcpc.set_vbus = tcpci_set_vbus; + tcpci->tcpc.set_cc = tcpci_set_cc; + tcpci->tcpc.get_cc = tcpci_get_cc; + tcpci->tcpc.set_polarity = tcpci_set_polarity; + tcpci->tcpc.set_vconn = tcpci_set_vconn; + tcpci->tcpc.start_drp_toggling = tcpci_start_drp_toggling; + + tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx; + tcpci->tcpc.set_roles = tcpci_set_roles; + tcpci->tcpc.pd_transmit = tcpci_pd_transmit; + + err = tcpci_parse_config(tcpci); + if (err < 0) + return ERR_PTR(err); + + tcpci->port = tcpm_register_port(tcpci->dev, &tcpci->tcpc); + if (IS_ERR(tcpci->port)) + return ERR_CAST(tcpci->port); + + return tcpci; +} +EXPORT_SYMBOL_GPL(tcpci_register_port); + +void tcpci_unregister_port(struct tcpci *tcpci) +{ + tcpm_unregister_port(tcpci->port); +} +EXPORT_SYMBOL_GPL(tcpci_unregister_port); + +static int tcpci_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + struct tcpci_chip *chip; + int err; + u16 val = 0; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->data.regmap = devm_regmap_init_i2c(client, &tcpci_regmap_config); + if (IS_ERR(chip->data.regmap)) + return PTR_ERR(chip->data.regmap); + + i2c_set_clientdata(client, chip); + + /* Disable chip interrupts before requesting irq */ + err = regmap_raw_write(chip->data.regmap, TCPC_ALERT_MASK, &val, + sizeof(u16)); + if (err < 0) + return err; + + chip->tcpci = tcpci_register_port(&client->dev, &chip->data); + if (IS_ERR(chip->tcpci)) + return PTR_ERR(chip->tcpci); + + err = devm_request_threaded_irq(&client->dev, client->irq, NULL, + _tcpci_irq, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + dev_name(&client->dev), chip); + if (err < 0) { + tcpci_unregister_port(chip->tcpci); + return err; + } + + return 0; +} + +static int tcpci_remove(struct i2c_client *client) +{ + struct tcpci_chip *chip = i2c_get_clientdata(client); + + tcpci_unregister_port(chip->tcpci); + + return 0; +} + +static const struct i2c_device_id tcpci_id[] = { + { "tcpci", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tcpci_id); + +#ifdef CONFIG_OF +static const struct of_device_id tcpci_of_match[] = { + { .compatible = "nxp,ptn5110", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tcpci_of_match); +#endif + +static struct i2c_driver tcpci_i2c_driver = { + .driver = { + .name = "tcpci", + .of_match_table = of_match_ptr(tcpci_of_match), + }, + .probe = tcpci_probe, + .remove = tcpci_remove, + .id_table = tcpci_id, +}; +module_i2c_driver(tcpci_i2c_driver); + +MODULE_DESCRIPTION("USB Type-C Port Controller Interface driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/typec/tcpci.h b/drivers/usb/typec/tcpci.h new file mode 100644 index 000000000000..303ebde26546 --- /dev/null +++ b/drivers/usb/typec/tcpci.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2015-2017 Google, Inc + * + * USB Type-C Port Controller Interface. + */ + +#ifndef __LINUX_USB_TCPCI_H +#define __LINUX_USB_TCPCI_H + +#define TCPC_VENDOR_ID 0x0 +#define TCPC_PRODUCT_ID 0x2 +#define TCPC_BCD_DEV 0x4 +#define TCPC_TC_REV 0x6 +#define TCPC_PD_REV 0x8 +#define TCPC_PD_INT_REV 0xa + +#define TCPC_ALERT 0x10 +#define TCPC_ALERT_VBUS_DISCNCT BIT(11) +#define TCPC_ALERT_RX_BUF_OVF BIT(10) +#define TCPC_ALERT_FAULT BIT(9) +#define TCPC_ALERT_V_ALARM_LO BIT(8) +#define TCPC_ALERT_V_ALARM_HI BIT(7) +#define TCPC_ALERT_TX_SUCCESS BIT(6) +#define TCPC_ALERT_TX_DISCARDED BIT(5) +#define TCPC_ALERT_TX_FAILED BIT(4) +#define TCPC_ALERT_RX_HARD_RST BIT(3) +#define TCPC_ALERT_RX_STATUS BIT(2) +#define TCPC_ALERT_POWER_STATUS BIT(1) +#define TCPC_ALERT_CC_STATUS BIT(0) + +#define TCPC_ALERT_MASK 0x12 +#define TCPC_POWER_STATUS_MASK 0x14 +#define TCPC_FAULT_STATUS_MASK 0x15 +#define TCPC_CONFIG_STD_OUTPUT 0x18 + +#define TCPC_TCPC_CTRL 0x19 +#define TCPC_TCPC_CTRL_ORIENTATION BIT(0) + +#define TCPC_ROLE_CTRL 0x1a +#define TCPC_ROLE_CTRL_DRP BIT(6) +#define TCPC_ROLE_CTRL_RP_VAL_SHIFT 4 +#define TCPC_ROLE_CTRL_RP_VAL_MASK 0x3 +#define TCPC_ROLE_CTRL_RP_VAL_DEF 0x0 +#define TCPC_ROLE_CTRL_RP_VAL_1_5 0x1 +#define TCPC_ROLE_CTRL_RP_VAL_3_0 0x2 +#define TCPC_ROLE_CTRL_CC2_SHIFT 2 +#define TCPC_ROLE_CTRL_CC2_MASK 0x3 +#define TCPC_ROLE_CTRL_CC1_SHIFT 0 +#define TCPC_ROLE_CTRL_CC1_MASK 0x3 +#define TCPC_ROLE_CTRL_CC_RA 0x0 +#define TCPC_ROLE_CTRL_CC_RP 0x1 +#define TCPC_ROLE_CTRL_CC_RD 0x2 +#define TCPC_ROLE_CTRL_CC_OPEN 0x3 + +#define TCPC_FAULT_CTRL 0x1b + +#define TCPC_POWER_CTRL 0x1c +#define TCPC_POWER_CTRL_VCONN_ENABLE BIT(0) + +#define TCPC_CC_STATUS 0x1d +#define TCPC_CC_STATUS_TOGGLING BIT(5) +#define TCPC_CC_STATUS_TERM BIT(4) +#define TCPC_CC_STATUS_CC2_SHIFT 2 +#define TCPC_CC_STATUS_CC2_MASK 0x3 +#define TCPC_CC_STATUS_CC1_SHIFT 0 +#define TCPC_CC_STATUS_CC1_MASK 0x3 + +#define TCPC_POWER_STATUS 0x1e +#define TCPC_POWER_STATUS_UNINIT BIT(6) +#define TCPC_POWER_STATUS_VBUS_DET BIT(3) +#define TCPC_POWER_STATUS_VBUS_PRES BIT(2) + +#define TCPC_FAULT_STATUS 0x1f + +#define TCPC_COMMAND 0x23 +#define TCPC_CMD_WAKE_I2C 0x11 +#define TCPC_CMD_DISABLE_VBUS_DETECT 0x22 +#define TCPC_CMD_ENABLE_VBUS_DETECT 0x33 +#define TCPC_CMD_DISABLE_SINK_VBUS 0x44 +#define TCPC_CMD_SINK_VBUS 0x55 +#define TCPC_CMD_DISABLE_SRC_VBUS 0x66 +#define TCPC_CMD_SRC_VBUS_DEFAULT 0x77 +#define TCPC_CMD_SRC_VBUS_HIGH 0x88 +#define TCPC_CMD_LOOK4CONNECTION 0x99 +#define TCPC_CMD_RXONEMORE 0xAA +#define TCPC_CMD_I2C_IDLE 0xFF + +#define TCPC_DEV_CAP_1 0x24 +#define TCPC_DEV_CAP_2 0x26 +#define TCPC_STD_INPUT_CAP 0x28 +#define TCPC_STD_OUTPUT_CAP 0x29 + +#define TCPC_MSG_HDR_INFO 0x2e +#define TCPC_MSG_HDR_INFO_DATA_ROLE BIT(3) +#define TCPC_MSG_HDR_INFO_PWR_ROLE BIT(0) +#define TCPC_MSG_HDR_INFO_REV_SHIFT 1 +#define TCPC_MSG_HDR_INFO_REV_MASK 0x3 + +#define TCPC_RX_DETECT 0x2f +#define TCPC_RX_DETECT_HARD_RESET BIT(5) +#define TCPC_RX_DETECT_SOP BIT(0) + +#define TCPC_RX_BYTE_CNT 0x30 +#define TCPC_RX_BUF_FRAME_TYPE 0x31 +#define TCPC_RX_HDR 0x32 +#define TCPC_RX_DATA 0x34 /* through 0x4f */ + +#define TCPC_TRANSMIT 0x50 +#define TCPC_TRANSMIT_RETRY_SHIFT 4 +#define TCPC_TRANSMIT_RETRY_MASK 0x3 +#define TCPC_TRANSMIT_TYPE_SHIFT 0 +#define TCPC_TRANSMIT_TYPE_MASK 0x7 + +#define TCPC_TX_BYTE_CNT 0x51 +#define TCPC_TX_HDR 0x52 +#define TCPC_TX_DATA 0x54 /* through 0x6f */ + +#define TCPC_VBUS_VOLTAGE 0x70 +#define TCPC_VBUS_SINK_DISCONNECT_THRESH 0x72 +#define TCPC_VBUS_STOP_DISCHARGE_THRESH 0x74 +#define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG 0x76 +#define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG 0x78 + +struct tcpci; +struct tcpci_data { + struct regmap *regmap; + int (*init)(struct tcpci *tcpci, struct tcpci_data *data); + int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data, + bool enable); + int (*start_drp_toggling)(struct tcpci *tcpci, struct tcpci_data *data, + enum typec_cc_status cc); +}; + +struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data); +void tcpci_unregister_port(struct tcpci *tcpci); +irqreturn_t tcpci_irq(struct tcpci *tcpci); + +#endif /* __LINUX_USB_TCPCI_H */ diff --git a/drivers/usb/typec/tcpci_rt1711h.c b/drivers/usb/typec/tcpci_rt1711h.c new file mode 100644 index 000000000000..017389021b96 --- /dev/null +++ b/drivers/usb/typec/tcpci_rt1711h.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, Richtek Technology Corporation + * + * Richtek RT1711H Type-C Chip Driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include "tcpci.h" + +#define RT1711H_VID 0x29CF +#define RT1711H_PID 0x1711 + +#define RT1711H_RTCTRL8 0x9B + +/* Autoidle timeout = (tout * 2 + 1) * 6.4ms */ +#define RT1711H_RTCTRL8_SET(ck300, ship_off, auto_idle, tout) \ + (((ck300) << 7) | ((ship_off) << 5) | \ + ((auto_idle) << 3) | ((tout) & 0x07)) + +#define RT1711H_RTCTRL11 0x9E + +/* I2C timeout = (tout + 1) * 12.5ms */ +#define RT1711H_RTCTRL11_SET(en, tout) \ + (((en) << 7) | ((tout) & 0x0F)) + +#define RT1711H_RTCTRL13 0xA0 +#define RT1711H_RTCTRL14 0xA1 +#define RT1711H_RTCTRL15 0xA2 +#define RT1711H_RTCTRL16 0xA3 + +struct rt1711h_chip { + struct tcpci_data data; + struct tcpci *tcpci; + struct device *dev; +}; + +static int rt1711h_read16(struct rt1711h_chip *chip, unsigned int reg, u16 *val) +{ + return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u16)); +} + +static int rt1711h_write16(struct rt1711h_chip *chip, unsigned int reg, u16 val) +{ + return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u16)); +} + +static int rt1711h_read8(struct rt1711h_chip *chip, unsigned int reg, u8 *val) +{ + return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u8)); +} + +static int rt1711h_write8(struct rt1711h_chip *chip, unsigned int reg, u8 val) +{ + return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8)); +} + +static const struct regmap_config rt1711h_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xFF, /* 0x80 .. 0xFF are vendor defined */ +}; + +static struct rt1711h_chip *tdata_to_rt1711h(struct tcpci_data *tdata) +{ + return container_of(tdata, struct rt1711h_chip, data); +} + +static int rt1711h_init(struct tcpci *tcpci, struct tcpci_data *tdata) +{ + int ret; + struct rt1711h_chip *chip = tdata_to_rt1711h(tdata); + + /* CK 300K from 320K, shipping off, auto_idle enable, tout = 32ms */ + ret = rt1711h_write8(chip, RT1711H_RTCTRL8, + RT1711H_RTCTRL8_SET(0, 1, 1, 2)); + if (ret < 0) + return ret; + + /* I2C reset : (val + 1) * 12.5ms */ + ret = rt1711h_write8(chip, RT1711H_RTCTRL11, + RT1711H_RTCTRL11_SET(1, 0x0F)); + if (ret < 0) + return ret; + + /* tTCPCfilter : (26.7 * val) us */ + ret = rt1711h_write8(chip, RT1711H_RTCTRL14, 0x0F); + if (ret < 0) + return ret; + + /* tDRP : (51.2 + 6.4 * val) ms */ + ret = rt1711h_write8(chip, RT1711H_RTCTRL15, 0x04); + if (ret < 0) + return ret; + + /* dcSRC.DRP : 33% */ + return rt1711h_write16(chip, RT1711H_RTCTRL16, 330); +} + +static int rt1711h_set_vconn(struct tcpci *tcpci, struct tcpci_data *tdata, + bool enable) +{ + struct rt1711h_chip *chip = tdata_to_rt1711h(tdata); + + return rt1711h_write8(chip, RT1711H_RTCTRL8, + RT1711H_RTCTRL8_SET(0, 1, !enable, 2)); +} + +static int rt1711h_start_drp_toggling(struct tcpci *tcpci, + struct tcpci_data *tdata, + enum typec_cc_status cc) +{ + struct rt1711h_chip *chip = tdata_to_rt1711h(tdata); + int ret; + unsigned int reg = 0; + + switch (cc) { + default: + case TYPEC_CC_RP_DEF: + reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF << + TCPC_ROLE_CTRL_RP_VAL_SHIFT); + break; + case TYPEC_CC_RP_1_5: + reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 << + TCPC_ROLE_CTRL_RP_VAL_SHIFT); + break; + case TYPEC_CC_RP_3_0: + reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 << + TCPC_ROLE_CTRL_RP_VAL_SHIFT); + break; + } + + if (cc == TYPEC_CC_RD) + reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT); + else + reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) | + (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT); + + ret = rt1711h_write8(chip, TCPC_ROLE_CTRL, reg); + if (ret < 0) + return ret; + usleep_range(500, 1000); + + return 0; +} + +static irqreturn_t rt1711h_irq(int irq, void *dev_id) +{ + int ret; + u16 alert; + u8 status; + struct rt1711h_chip *chip = dev_id; + + if (!chip->tcpci) + return IRQ_HANDLED; + + ret = rt1711h_read16(chip, TCPC_ALERT, &alert); + if (ret < 0) + goto out; + + if (alert & TCPC_ALERT_CC_STATUS) { + ret = rt1711h_read8(chip, TCPC_CC_STATUS, &status); + if (ret < 0) + goto out; + /* Clear cc change event triggered by starting toggling */ + if (status & TCPC_CC_STATUS_TOGGLING) + rt1711h_write8(chip, TCPC_ALERT, TCPC_ALERT_CC_STATUS); + } + +out: + return tcpci_irq(chip->tcpci); +} + +static int rt1711h_init_alert(struct rt1711h_chip *chip, + struct i2c_client *client) +{ + int ret; + + /* Disable chip interrupts before requesting irq */ + ret = rt1711h_write16(chip, TCPC_ALERT_MASK, 0); + if (ret < 0) + return ret; + + ret = devm_request_threaded_irq(chip->dev, client->irq, NULL, + rt1711h_irq, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + dev_name(chip->dev), chip); + if (ret < 0) + return ret; + enable_irq_wake(client->irq); + return 0; +} + +static int rt1711h_sw_reset(struct rt1711h_chip *chip) +{ + int ret; + + ret = rt1711h_write8(chip, RT1711H_RTCTRL13, 0x01); + if (ret < 0) + return ret; + + usleep_range(1000, 2000); + return 0; +} + +static int rt1711h_check_revision(struct i2c_client *i2c) +{ + int ret; + + ret = i2c_smbus_read_word_data(i2c, TCPC_VENDOR_ID); + if (ret < 0) + return ret; + if (ret != RT1711H_VID) { + dev_err(&i2c->dev, "vid is not correct, 0x%04x\n", ret); + return -ENODEV; + } + ret = i2c_smbus_read_word_data(i2c, TCPC_PRODUCT_ID); + if (ret < 0) + return ret; + if (ret != RT1711H_PID) { + dev_err(&i2c->dev, "pid is not correct, 0x%04x\n", ret); + return -ENODEV; + } + return 0; +} + +static int rt1711h_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + int ret; + struct rt1711h_chip *chip; + + ret = rt1711h_check_revision(client); + if (ret < 0) { + dev_err(&client->dev, "check vid/pid fail\n"); + return ret; + } + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->data.regmap = devm_regmap_init_i2c(client, + &rt1711h_regmap_config); + if (IS_ERR(chip->data.regmap)) + return PTR_ERR(chip->data.regmap); + + chip->dev = &client->dev; + i2c_set_clientdata(client, chip); + + ret = rt1711h_sw_reset(chip); + if (ret < 0) + return ret; + + ret = rt1711h_init_alert(chip, client); + if (ret < 0) + return ret; + + chip->data.init = rt1711h_init; + chip->data.set_vconn = rt1711h_set_vconn; + chip->data.start_drp_toggling = rt1711h_start_drp_toggling; + chip->tcpci = tcpci_register_port(chip->dev, &chip->data); + if (IS_ERR_OR_NULL(chip->tcpci)) + return PTR_ERR(chip->tcpci); + + return 0; +} + +static int rt1711h_remove(struct i2c_client *client) +{ + struct rt1711h_chip *chip = i2c_get_clientdata(client); + + tcpci_unregister_port(chip->tcpci); + return 0; +} + +static const struct i2c_device_id rt1711h_id[] = { + { "rt1711h", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt1711h_id); + +#ifdef CONFIG_OF +static const struct of_device_id rt1711h_of_match[] = { + { .compatible = "richtek,rt1711h", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt1711h_of_match); +#endif + +static struct i2c_driver rt1711h_i2c_driver = { + .driver = { + .name = "rt1711h", + .of_match_table = of_match_ptr(rt1711h_of_match), + }, + .probe = rt1711h_probe, + .remove = rt1711h_remove, + .id_table = rt1711h_id, +}; +module_i2c_driver(rt1711h_i2c_driver); + +MODULE_AUTHOR("ShuFan Lee "); +MODULE_DESCRIPTION("RT1711H USB Type-C Port Controller Interface Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From aaf3f4e925dc2bdc4715142103660285632a245c Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 27 Jun 2018 18:19:47 +0300 Subject: usb: typec: function for checking cable plug orientation This adds function typec_get_orientation() that can be used for checking the current cable plug orientation. Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 12 ++++++++++++ include/linux/usb/typec.h | 1 + 2 files changed, 13 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 633105917fa6..3ef7b99b080f 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1329,6 +1329,18 @@ int typec_set_orientation(struct typec_port *port, } EXPORT_SYMBOL_GPL(typec_set_orientation); +/** + * typec_get_orientation - Get USB Type-C cable plug orientation + * @port: USB Type-C Port + * + * Get current cable plug orientation for @port. + */ +enum typec_orientation typec_get_orientation(struct typec_port *port) +{ + return port->orientation; +} +EXPORT_SYMBOL_GPL(typec_get_orientation); + /** * typec_set_mode - Set mode of operation for USB Type-C connector * @port: USB Type-C port for the connector diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 15f8d9a50b31..6c33b53d1162 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -265,6 +265,7 @@ void typec_set_pwr_opmode(struct typec_port *port, enum typec_pwr_opmode mode); int typec_set_orientation(struct typec_port *port, enum typec_orientation orientation); +enum typec_orientation typec_get_orientation(struct typec_port *port); int typec_set_mode(struct typec_port *port, int mode); int typec_find_port_power_role(const char *name); -- cgit v1.2.3 From 0bc26314957504e82dc9e3704422959db99b38ef Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Fri, 29 Jun 2018 14:54:04 +1000 Subject: usb: update for tcpci drivers moving out of staging Signed-off-by: Stephen Rothwell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index 972fd1911045..ee808903983f 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -65,6 +65,7 @@ config TYPEC_TCPCI config TYPEC_RT1711H tristate "Richtek RT1711H Type-C chip driver" + depends on I2C select TYPEC_TCPCI help Richtek RT1711H Type-C chip driver that works with -- cgit v1.2.3 From 93dd2112c7b2fa5512cc4aff2c449420487fcb68 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 27 Jun 2018 18:19:48 +0300 Subject: usb: typec: mux: Get the mux identifier from function parameter In order for the muxes to be usable with alternate modes, the alternate mode devices will need also to be able to get a handle to the muxes on top of the port devices. To make that possible, the muxes need to be possible to request with an identifier. This will change the API so that the mux identifier is given as a function parameter to typec_mux_get(), and the hard-coded "typec-mux" is replaced with that value. Signed-off-by: Heikki Krogerus Tested-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 2 +- drivers/usb/typec/mux.c | 6 +++--- include/linux/usb/typec_mux.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 3ef7b99b080f..784e928303d7 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1407,7 +1407,7 @@ struct typec_port *typec_register_port(struct device *parent, goto err_switch; } - port->mux = typec_mux_get(cap->fwnode ? &port->dev : parent); + port->mux = typec_mux_get(parent, "typec-mux"); if (IS_ERR(port->mux)) { ret = PTR_ERR(port->mux); goto err_mux; diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index 9d8330e9c431..ddaac63ecf12 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -123,19 +123,19 @@ static void *typec_mux_match(struct device_connection *con, int ep, void *data) /** * typec_mux_get - Find USB Type-C Multiplexer * @dev: The caller device + * @name: Mux identifier * * Finds a mux linked to the caller. This function is primarily meant for the * Type-C drivers. Returns a reference to the mux on success, NULL if no * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection * was found but the mux has not been enumerated yet. */ -struct typec_mux *typec_mux_get(struct device *dev) +struct typec_mux *typec_mux_get(struct device *dev, const char *name) { struct typec_mux *mux; mutex_lock(&mux_lock); - mux = device_connection_find_match(dev, "typec-mux", NULL, - typec_mux_match); + mux = device_connection_find_match(dev, name, NULL, typec_mux_match); if (!IS_ERR_OR_NULL(mux)) get_device(mux->dev); mutex_unlock(&mux_lock); diff --git a/include/linux/usb/typec_mux.h b/include/linux/usb/typec_mux.h index 12c1b057834b..79293f630ee1 100644 --- a/include/linux/usb/typec_mux.h +++ b/include/linux/usb/typec_mux.h @@ -47,7 +47,7 @@ void typec_switch_put(struct typec_switch *sw); int typec_switch_register(struct typec_switch *sw); void typec_switch_unregister(struct typec_switch *sw); -struct typec_mux *typec_mux_get(struct device *dev); +struct typec_mux *typec_mux_get(struct device *dev, const char *name); void typec_mux_put(struct typec_mux *mux); int typec_mux_register(struct typec_mux *mux); void typec_mux_unregister(struct typec_mux *mux); -- cgit v1.2.3 From 4ab8c18d4d67321cc7b660559de17511d4fc0237 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 27 Jun 2018 18:19:49 +0300 Subject: usb: typec: Register a device for every mode Before a device was created for every discovered SVID, but this will create a device for every discovered mode of every SVID. The idea is to make it easier to create mode specific drivers once a bus for the alternate mode is added. Signed-off-by: Heikki Krogerus Tested-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 172 +++++++++++++++------------------------------- drivers/usb/typec/tcpm.c | 45 ++++++------ include/linux/usb/typec.h | 37 +++------- 3 files changed, 83 insertions(+), 171 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 784e928303d7..96dc9c4f73f0 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -13,31 +13,20 @@ #include #include -struct typec_mode { - int index; +struct typec_altmode { + struct device dev; + u16 svid; + u8 mode; + u32 vdo; char *desc; enum typec_port_type roles; - - struct typec_altmode *alt_mode; - unsigned int active:1; + struct attribute *attrs[5]; char group_name[6]; struct attribute_group group; - struct attribute *attrs[5]; - struct device_attribute vdo_attr; - struct device_attribute desc_attr; - struct device_attribute active_attr; - struct device_attribute roles_attr; -}; - -struct typec_altmode { - struct device dev; - u16 svid; - int n_modes; - struct typec_mode modes[ALTMODE_MAX_MODES]; - const struct attribute_group *mode_groups[ALTMODE_MAX_MODES]; + const struct attribute_group *groups[2]; }; struct typec_plug { @@ -177,23 +166,20 @@ static void typec_report_identity(struct device *dev) /** * typec_altmode_update_active - Report Enter/Exit mode * @alt: Handle to the alternate mode - * @mode: Mode index * @active: True when the mode has been entered * * If a partner or cable plug executes Enter/Exit Mode command successfully, the * drivers use this routine to report the updated state of the mode. */ -void typec_altmode_update_active(struct typec_altmode *alt, int mode, - bool active) +void typec_altmode_update_active(struct typec_altmode *alt, bool active) { - struct typec_mode *m = &alt->modes[mode]; char dir[6]; - if (m->active == active) + if (alt->active == active) return; - m->active = active; - snprintf(dir, sizeof(dir), "mode%d", mode); + alt->active = active; + snprintf(dir, sizeof(dir), "mode%d", alt->mode); sysfs_notify(&alt->dev.kobj, dir, "active"); kobject_uevent(&alt->dev.kobj, KOBJ_CHANGE); } @@ -220,42 +206,36 @@ struct typec_port *typec_altmode2port(struct typec_altmode *alt) EXPORT_SYMBOL_GPL(typec_altmode2port); static ssize_t -typec_altmode_vdo_show(struct device *dev, struct device_attribute *attr, - char *buf) +vdo_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_mode *mode = container_of(attr, struct typec_mode, - vdo_attr); + struct typec_altmode *alt = to_altmode(dev); - return sprintf(buf, "0x%08x\n", mode->vdo); + return sprintf(buf, "0x%08x\n", alt->vdo); } +static DEVICE_ATTR_RO(vdo); static ssize_t -typec_altmode_desc_show(struct device *dev, struct device_attribute *attr, - char *buf) +description_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_mode *mode = container_of(attr, struct typec_mode, - desc_attr); + struct typec_altmode *alt = to_altmode(dev); - return sprintf(buf, "%s\n", mode->desc ? mode->desc : ""); + return sprintf(buf, "%s\n", alt->desc ? alt->desc : ""); } +static DEVICE_ATTR_RO(description); static ssize_t -typec_altmode_active_show(struct device *dev, struct device_attribute *attr, - char *buf) +active_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_mode *mode = container_of(attr, struct typec_mode, - active_attr); + struct typec_altmode *alt = to_altmode(dev); - return sprintf(buf, "%s\n", mode->active ? "yes" : "no"); + return sprintf(buf, "%s\n", alt->active ? "yes" : "no"); } -static ssize_t -typec_altmode_active_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) +static ssize_t active_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) { - struct typec_mode *mode = container_of(attr, struct typec_mode, - active_attr); - struct typec_port *port = typec_altmode2port(mode->alt_mode); + struct typec_altmode *alt = to_altmode(dev); + struct typec_port *port = typec_altmode2port(alt); bool activate; int ret; @@ -266,22 +246,22 @@ typec_altmode_active_store(struct device *dev, struct device_attribute *attr, if (ret) return ret; - ret = port->cap->activate_mode(port->cap, mode->index, activate); + ret = port->cap->activate_mode(port->cap, alt->mode, activate); if (ret) return ret; return size; } +static DEVICE_ATTR_RW(active); static ssize_t -typec_altmode_roles_show(struct device *dev, struct device_attribute *attr, - char *buf) +supported_roles_show(struct device *dev, struct device_attribute *attr, + char *buf) { - struct typec_mode *mode = container_of(attr, struct typec_mode, - roles_attr); + struct typec_altmode *alt = to_altmode(dev); ssize_t ret; - switch (mode->roles) { + switch (alt->roles) { case TYPEC_PORT_SRC: ret = sprintf(buf, "source\n"); break; @@ -295,61 +275,13 @@ typec_altmode_roles_show(struct device *dev, struct device_attribute *attr, } return ret; } +static DEVICE_ATTR_RO(supported_roles); -static void typec_init_modes(struct typec_altmode *alt, - const struct typec_mode_desc *desc, bool is_port) +static void typec_altmode_release(struct device *dev) { - int i; - - for (i = 0; i < alt->n_modes; i++, desc++) { - struct typec_mode *mode = &alt->modes[i]; - - /* Not considering the human readable description critical */ - mode->desc = kstrdup(desc->desc, GFP_KERNEL); - if (desc->desc && !mode->desc) - dev_err(&alt->dev, "failed to copy mode%d desc\n", i); - - mode->alt_mode = alt; - mode->vdo = desc->vdo; - mode->roles = desc->roles; - mode->index = desc->index; - sprintf(mode->group_name, "mode%d", desc->index); - - sysfs_attr_init(&mode->vdo_attr.attr); - mode->vdo_attr.attr.name = "vdo"; - mode->vdo_attr.attr.mode = 0444; - mode->vdo_attr.show = typec_altmode_vdo_show; - - sysfs_attr_init(&mode->desc_attr.attr); - mode->desc_attr.attr.name = "description"; - mode->desc_attr.attr.mode = 0444; - mode->desc_attr.show = typec_altmode_desc_show; - - sysfs_attr_init(&mode->active_attr.attr); - mode->active_attr.attr.name = "active"; - mode->active_attr.attr.mode = 0644; - mode->active_attr.show = typec_altmode_active_show; - mode->active_attr.store = typec_altmode_active_store; - - mode->attrs[0] = &mode->vdo_attr.attr; - mode->attrs[1] = &mode->desc_attr.attr; - mode->attrs[2] = &mode->active_attr.attr; - - /* With ports, list the roles that the mode is supported with */ - if (is_port) { - sysfs_attr_init(&mode->roles_attr.attr); - mode->roles_attr.attr.name = "supported_roles"; - mode->roles_attr.attr.mode = 0444; - mode->roles_attr.show = typec_altmode_roles_show; - - mode->attrs[3] = &mode->roles_attr.attr; - } - - mode->group.attrs = mode->attrs; - mode->group.name = mode->group_name; + struct typec_altmode *alt = to_altmode(dev); - alt->mode_groups[i] = &mode->group; - } + kfree(alt); } static ssize_t svid_show(struct device *dev, struct device_attribute *attr, @@ -367,16 +299,6 @@ static struct attribute *typec_altmode_attrs[] = { }; ATTRIBUTE_GROUPS(typec_altmode); -static void typec_altmode_release(struct device *dev) -{ - struct typec_altmode *alt = to_altmode(dev); - int i; - - for (i = 0; i < alt->n_modes; i++) - kfree(alt->modes[i].desc); - kfree(alt); -} - static const struct device_type typec_altmode_dev_type = { .name = "typec_alternate_mode", .groups = typec_altmode_groups, @@ -395,13 +317,27 @@ typec_register_altmode(struct device *parent, return ERR_PTR(-ENOMEM); alt->svid = desc->svid; - alt->n_modes = desc->n_modes; - typec_init_modes(alt, desc->modes, is_typec_port(parent)); + alt->mode = desc->mode; + alt->vdo = desc->vdo; + alt->roles = desc->roles; + + alt->attrs[0] = &dev_attr_vdo.attr; + alt->attrs[1] = &dev_attr_description.attr; + alt->attrs[2] = &dev_attr_active.attr; + + if (is_typec_port(parent)) + alt->attrs[3] = &dev_attr_supported_roles.attr; + + sprintf(alt->group_name, "mode%d", desc->mode); + alt->group.name = alt->group_name; + alt->group.attrs = alt->attrs; + alt->groups[0] = &alt->group; alt->dev.parent = parent; - alt->dev.groups = alt->mode_groups; + alt->dev.groups = alt->groups; alt->dev.type = &typec_altmode_dev_type; - dev_set_name(&alt->dev, "svid-%04x", alt->svid); + dev_set_name(&alt->dev, "%s-%04x:%u", dev_name(parent), + alt->svid, alt->mode); ret = device_register(&alt->dev); if (ret) { diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index a18af298d96d..6b57e7132e64 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -310,8 +310,8 @@ struct tcpm_port { /* Alternate mode data */ struct pd_mode_data mode_data; - struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX]; - struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX]; + struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX * 6]; + struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX * 6]; /* Deadline in jiffies to exit src_try_wait state */ unsigned long max_wait; @@ -995,7 +995,6 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload, { struct pd_mode_data *pmdata = &port->mode_data; struct typec_altmode_desc *paltmode; - struct typec_mode_desc *pmode; int i; if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) { @@ -1003,32 +1002,28 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload, return; } - paltmode = &pmdata->altmode_desc[pmdata->altmodes]; - memset(paltmode, 0, sizeof(*paltmode)); + for (i = 1; i < cnt; i++) { + paltmode = &pmdata->altmode_desc[pmdata->altmodes]; + memset(paltmode, 0, sizeof(*paltmode)); - paltmode->svid = pmdata->svids[pmdata->svid_index]; + paltmode->svid = pmdata->svids[pmdata->svid_index]; + paltmode->mode = i; + paltmode->vdo = le32_to_cpu(payload[i]); - tcpm_log(port, " Alternate mode %d: SVID 0x%04x", - pmdata->altmodes, paltmode->svid); + tcpm_log(port, " Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x", + pmdata->altmodes, paltmode->svid, + paltmode->mode, paltmode->vdo); - for (i = 1; i < cnt && paltmode->n_modes < ALTMODE_MAX_MODES; i++) { - pmode = &paltmode->modes[paltmode->n_modes]; - memset(pmode, 0, sizeof(*pmode)); - pmode->vdo = le32_to_cpu(payload[i]); - pmode->index = i - 1; - paltmode->n_modes++; - tcpm_log(port, " VDO %d: 0x%08x", - pmode->index, pmode->vdo); - } - port->partner_altmode[pmdata->altmodes] = - typec_partner_register_altmode(port->partner, paltmode); - if (!port->partner_altmode[pmdata->altmodes]) { - tcpm_log(port, - "Failed to register alternate modes for SVID 0x%04x", - paltmode->svid); - return; + port->partner_altmode[pmdata->altmodes] = + typec_partner_register_altmode(port->partner, paltmode); + if (!port->partner_altmode[pmdata->altmodes]) { + tcpm_log(port, + "Failed to register modes for SVID 0x%04x", + paltmode->svid); + return; + } + pmdata->altmodes++; } - pmdata->altmodes++; } #define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header) diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 6c33b53d1162..2dcb1683075f 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -93,41 +93,23 @@ int typec_partner_set_identity(struct typec_partner *partner); int typec_cable_set_identity(struct typec_cable *cable); /* - * struct typec_mode_desc - Individual Mode of an Alternate Mode - * @index: Index of the Mode within the SVID + * struct typec_altmode_desc - USB Type-C Alternate Mode Descriptor + * @svid: Standard or Vendor ID + * @mode: Index of the Mode * @vdo: VDO returned by Discover Modes USB PD command - * @desc: Optional human readable description of the mode * @roles: Only for ports. DRP if the mode is available in both roles * - * Description of a mode of an Alternate Mode which a connector, cable plug or - * partner supports. Every mode will have it's own sysfs group. The details are - * the VDO returned by discover modes command, description for the mode and - * active flag telling has the mode being entered or not. + * Description of an Alternate Mode which a connector, cable plug or partner + * supports. */ -struct typec_mode_desc { - int index; +struct typec_altmode_desc { + u16 svid; + u8 mode; u32 vdo; - char *desc; /* Only used with ports */ enum typec_port_type roles; }; -/* - * struct typec_altmode_desc - USB Type-C Alternate Mode Descriptor - * @svid: Standard or Vendor ID - * @n_modes: Number of modes - * @modes: Array of modes supported by the Alternate Mode - * - * Representation of an Alternate Mode that has SVID assigned by USB-IF. The - * array of modes will list the modes of a particular SVID that are supported by - * a connector, partner of a cable plug. - */ -struct typec_altmode_desc { - u16 svid; - int n_modes; - struct typec_mode_desc modes[ALTMODE_MAX_MODES]; -}; - struct typec_altmode *typec_partner_register_altmode(struct typec_partner *partner, const struct typec_altmode_desc *desc); @@ -141,8 +123,7 @@ void typec_unregister_altmode(struct typec_altmode *altmode); struct typec_port *typec_altmode2port(struct typec_altmode *alt); -void typec_altmode_update_active(struct typec_altmode *alt, int mode, - bool active); +void typec_altmode_update_active(struct typec_altmode *alt, bool active); enum typec_plug_index { TYPEC_PLUG_SOP_P, -- cgit v1.2.3 From 8a37d87d72f0c69f837229c04d2fcd7117ea57e7 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 27 Jun 2018 18:19:50 +0300 Subject: usb: typec: Bus type for alternate modes Introducing a simple bus for the alternate modes. Bus allows binding drivers to the discovered alternate modes the partners support. Signed-off-by: Heikki Krogerus Tested-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/obsolete/sysfs-class-typec | 48 ++++ Documentation/ABI/testing/sysfs-bus-typec | 51 ++++ Documentation/ABI/testing/sysfs-class-typec | 62 +---- Documentation/driver-api/usb/typec_bus.rst | 136 +++++++++ MAINTAINERS | 11 +- drivers/usb/typec/Makefile | 2 +- drivers/usb/typec/bus.c | 401 +++++++++++++++++++++++++++ drivers/usb/typec/bus.h | 38 +++ drivers/usb/typec/class.c | 366 +++++++++++++++++++----- include/linux/mod_devicetable.h | 15 + include/linux/usb/typec.h | 14 +- include/linux/usb/typec_altmode.h | 160 +++++++++++ scripts/mod/devicetable-offsets.c | 4 + scripts/mod/file2alias.c | 13 + 14 files changed, 1174 insertions(+), 147 deletions(-) create mode 100644 Documentation/ABI/obsolete/sysfs-class-typec create mode 100644 Documentation/ABI/testing/sysfs-bus-typec create mode 100644 Documentation/driver-api/usb/typec_bus.rst create mode 100644 drivers/usb/typec/bus.c create mode 100644 drivers/usb/typec/bus.h create mode 100644 include/linux/usb/typec_altmode.h (limited to 'drivers/usb') diff --git a/Documentation/ABI/obsolete/sysfs-class-typec b/Documentation/ABI/obsolete/sysfs-class-typec new file mode 100644 index 000000000000..32623514ee87 --- /dev/null +++ b/Documentation/ABI/obsolete/sysfs-class-typec @@ -0,0 +1,48 @@ +These files are deprecated and will be removed. The same files are available +under /sys/bus/typec (see Documentation/ABI/testing/sysfs-bus-typec). + +What: /sys/class/typec///svid +Date: April 2017 +Contact: Heikki Krogerus +Description: + The SVID (Standard or Vendor ID) assigned by USB-IF for this + alternate mode. + +What: /sys/class/typec///mode/ +Date: April 2017 +Contact: Heikki Krogerus +Description: + Every supported mode will have its own directory. The name of + a mode will be "mode" (for example mode1), where + is the actual index to the mode VDO returned by Discover Modes + USB power delivery command. + +What: /sys/class/typec///mode/description +Date: April 2017 +Contact: Heikki Krogerus +Description: + Shows description of the mode. The description is optional for + the drivers, just like with the Billboard Devices. + +What: /sys/class/typec///mode/vdo +Date: April 2017 +Contact: Heikki Krogerus +Description: + Shows the VDO in hexadecimal returned by Discover Modes command + for this mode. + +What: /sys/class/typec///mode/active +Date: April 2017 +Contact: Heikki Krogerus +Description: + Shows if the mode is active or not. The attribute can be used + for entering/exiting the mode with partners and cable plugs, and + with the port alternate modes it can be used for disabling + support for specific alternate modes. Entering/exiting modes is + supported as synchronous operation so write(2) to the attribute + does not return until the enter/exit mode operation has + finished. The attribute is notified when the mode is + entered/exited so poll(2) on the attribute wakes up. + Entering/exiting a mode will also generate uevent KOBJ_CHANGE. + + Valid values: yes, no diff --git a/Documentation/ABI/testing/sysfs-bus-typec b/Documentation/ABI/testing/sysfs-bus-typec new file mode 100644 index 000000000000..205d9c91e2e1 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-typec @@ -0,0 +1,51 @@ +What: /sys/bus/typec/devices/.../active +Date: July 2018 +Contact: Heikki Krogerus +Description: + Shows if the mode is active or not. The attribute can be used + for entering/exiting the mode. Entering/exiting modes is + supported as synchronous operation so write(2) to the attribute + does not return until the enter/exit mode operation has + finished. The attribute is notified when the mode is + entered/exited so poll(2) on the attribute wakes up. + Entering/exiting a mode will also generate uevent KOBJ_CHANGE. + + Valid values are boolean. + +What: /sys/bus/typec/devices/.../description +Date: July 2018 +Contact: Heikki Krogerus +Description: + Shows description of the mode. The description is optional for + the drivers, just like with the Billboard Devices. + +What: /sys/bus/typec/devices/.../mode +Date: July 2018 +Contact: Heikki Krogerus +Description: + The index number of the mode returned by Discover Modes USB + Power Delivery command. Depending on the alternate mode, the + mode index may be significant. + + With some alternate modes (SVIDs), the mode index is assigned + for specific functionality in the specification for that + alternate mode. + + With other alternate modes, the mode index values are not + assigned, and can not be therefore used for identification. When + the mode index is not assigned, identifying the alternate mode + must be done with either mode VDO or the description. + +What: /sys/bus/typec/devices/.../svid +Date: July 2018 +Contact: Heikki Krogerus +Description: + The Standard or Vendor ID (SVID) assigned by USB-IF for this + alternate mode. + +What: /sys/bus/typec/devices/.../vdo +Date: July 2018 +Contact: Heikki Krogerus +Description: + Shows the VDO in hexadecimal returned by Discover Modes command + for this mode. diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index 5be552e255e9..d7647b258c3c 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec @@ -222,70 +222,12 @@ Description: available. The value can be polled. -Alternate Mode devices. +USB Type-C port alternate mode devices. -The alternate modes will have Standard or Vendor ID (SVID) assigned by USB-IF. -The ports, partners and cable plugs can have alternate modes. A supported SVID -will consist of a set of modes. Every SVID a port/partner/plug supports will -have a device created for it, and every supported mode for a supported SVID will -have its own directory under that device. Below refers to the device for -the alternate mode. - -What: /sys/class/typec///svid -Date: April 2017 -Contact: Heikki Krogerus -Description: - The SVID (Standard or Vendor ID) assigned by USB-IF for this - alternate mode. - -What: /sys/class/typec///mode/ -Date: April 2017 -Contact: Heikki Krogerus -Description: - Every supported mode will have its own directory. The name of - a mode will be "mode" (for example mode1), where - is the actual index to the mode VDO returned by Discover Modes - USB power delivery command. - -What: /sys/class/typec///mode/description -Date: April 2017 -Contact: Heikki Krogerus -Description: - Shows description of the mode. The description is optional for - the drivers, just like with the Billboard Devices. - -What: /sys/class/typec///mode/vdo -Date: April 2017 -Contact: Heikki Krogerus -Description: - Shows the VDO in hexadecimal returned by Discover Modes command - for this mode. - -What: /sys/class/typec///mode/active -Date: April 2017 -Contact: Heikki Krogerus -Description: - Shows if the mode is active or not. The attribute can be used - for entering/exiting the mode with partners and cable plugs, and - with the port alternate modes it can be used for disabling - support for specific alternate modes. Entering/exiting modes is - supported as synchronous operation so write(2) to the attribute - does not return until the enter/exit mode operation has - finished. The attribute is notified when the mode is - entered/exited so poll(2) on the attribute wakes up. - Entering/exiting a mode will also generate uevent KOBJ_CHANGE. - - Valid values: yes, no - -What: /sys/class/typec///mode/supported_roles +What: /sys/class/typec///supported_roles Date: April 2017 Contact: Heikki Krogerus Description: Space separated list of the supported roles. - This attribute is available for the devices describing the - alternate modes a port supports, and it will not be exposed with - the devices presenting the alternate modes the partners or cable - plugs support. - Valid values: source, sink diff --git a/Documentation/driver-api/usb/typec_bus.rst b/Documentation/driver-api/usb/typec_bus.rst new file mode 100644 index 000000000000..d5eec1715b5b --- /dev/null +++ b/Documentation/driver-api/usb/typec_bus.rst @@ -0,0 +1,136 @@ + +API for USB Type-C Alternate Mode drivers +========================================= + +Introduction +------------ + +Alternate modes require communication with the partner using Vendor Defined +Messages (VDM) as defined in USB Type-C and USB Power Delivery Specifications. +The communication is SVID (Standard or Vendor ID) specific, i.e. specific for +every alternate mode, so every alternate mode will need a custom driver. + +USB Type-C bus allows binding a driver to the discovered partner alternate +modes by using the SVID and the mode number. + +USB Type-C Connector Class provides a device for every alternate mode a port +supports, and separate device for every alternate mode the partner supports. +The drivers for the alternate modes are bound to the partner alternate mode +devices, and the port alternate mode devices must be handled by the port +drivers. + +When a new partner alternate mode device is registered, it is linked to the +alternate mode device of the port that the partner is attached to, that has +matching SVID and mode. Communication between the port driver and alternate mode +driver will happen using the same API. + +The port alternate mode devices are used as a proxy between the partner and the +alternate mode drivers, so the port drivers are only expected to pass the SVID +specific commands from the alternate mode drivers to the partner, and from the +partners to the alternate mode drivers. No direct SVID specific communication is +needed from the port drivers, but the port drivers need to provide the operation +callbacks for the port alternate mode devices, just like the alternate mode +drivers need to provide them for the partner alternate mode devices. + +Usage: +------ + +General +~~~~~~~ + +By default, the alternate mode drivers are responsible for entering the mode. +It is also possible to leave the decision about entering the mode to the user +space (See Documentation/ABI/testing/sysfs-class-typec). Port drivers should not +enter any modes on their own. + +``->vdm`` is the most important callback in the operation callbacks vector. It +will be used to deliver all the SVID specific commands from the partner to the +alternate mode driver, and vice versa in case of port drivers. The drivers send +the SVID specific commands to each other using :c:func:`typec_altmode_vmd()`. + +If the communication with the partner using the SVID specific commands results +in need to reconfigure the pins on the connector, the alternate mode driver +needs to notify the bus using :c:func:`typec_altmode_notify()`. The driver +passes the negotiated SVID specific pin configuration value to the function as +parameter. The bus driver will then configure the mux behind the connector using +that value as the state value for the mux, and also call blocking notification +chain to notify the external drivers about the state of the connector that need +to know it. + +NOTE: The SVID specific pin configuration values must always start from +``TYPEC_STATE_MODAL``. USB Type-C specification defines two default states for +the connector: ``TYPEC_STATE_USB`` and ``TYPEC_STATE_SAFE``. These values are +reserved by the bus as the first possible values for the state. When the +alternate mode is entered, the bus will put the connector into +``TYPEC_STATE_SAFE`` before sending Enter or Exit Mode command as defined in USB +Type-C Specification, and also put the connector back to ``TYPEC_STATE_USB`` +after the mode has been exited. + +An example of working definitions for SVID specific pin configurations would +look like this: + +enum { + ALTMODEX_CONF_A = TYPEC_STATE_MODAL, + ALTMODEX_CONF_B, + ... +}; + +Helper macro ``TYPEC_MODAL_STATE()`` can also be used: + +#define ALTMODEX_CONF_A = TYPEC_MODAL_STATE(0); +#define ALTMODEX_CONF_B = TYPEC_MODAL_STATE(1); + +Notification chain +~~~~~~~~~~~~~~~~~~ + +The drivers for the components that the alternate modes are designed for need to +get details regarding the results of the negotiation with the partner, and the +pin configuration of the connector. In case of DisplayPort alternate mode for +example, the GPU drivers will need to know those details. In case of +Thunderbolt alternate mode, the thunderbolt drivers will need to know them, and +so on. + +The notification chain is designed for this purpose. The drivers can register +notifiers with :c:func:`typec_altmode_register_notifier()`. + +Cable plug alternate modes +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The alternate mode drivers are not bound to cable plug alternate mode devices, +only to the partner alternate mode devices. If the alternate mode supports, or +requires, a cable that responds to SOP Prime, and optionally SOP Double Prime +messages, the driver for that alternate mode must request handle to the cable +plug alternate modes using :c:func:`typec_altmode_get_plug()`, and take over +their control. + +Driver API +---------- + +Alternate mode driver registering/unregistering +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: drivers/usb/typec/bus.c + :functions: typec_altmode_register_driver typec_altmode_unregister_driver + +Alternate mode driver operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: drivers/usb/typec/bus.c + :functions: typec_altmode_enter typec_altmode_exit typec_altmode_attention typec_altmode_vdm typec_altmode_notify + +API for the port drivers +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: drivers/usb/typec/bus.c + :functions: typec_match_altmode + +Cable Plug operations +~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: drivers/usb/typec/bus.c + :functions: typec_altmode_get_plug typec_altmode_put_plug + +Notifications +~~~~~~~~~~~~~ +.. kernel-doc:: drivers/usb/typec/class.c + :functions: typec_altmode_register_notifier typec_altmode_unregister_notifier diff --git a/MAINTAINERS b/MAINTAINERS index 07d1576fc766..f35f39f2072e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14955,7 +14955,7 @@ L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/typec/mux/pi3usb30532.c -USB TYPEC SUBSYSTEM +USB TYPEC CLASS M: Heikki Krogerus L: linux-usb@vger.kernel.org S: Maintained @@ -14964,6 +14964,15 @@ F: Documentation/driver-api/usb/typec.rst F: drivers/usb/typec/ F: include/linux/usb/typec.h +USB TYPEC BUS FOR ALTERNATE MODES +M: Heikki Krogerus +L: linux-usb@vger.kernel.org +S: Maintained +F: Documentation/ABI/testing/sysfs-bus-typec +F: Documentation/driver-api/usb/typec_bus.rst +F: drivers/usb/typec/altmodes/ +F: include/linux/usb/typec_altmode.h + USB UHCI DRIVER M: Alan Stern L: linux-usb@vger.kernel.org diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 46f86ee134a2..335ee06748fc 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TYPEC) += typec.o -typec-y := class.o mux.o +typec-y := class.o mux.o bus.o obj-$(CONFIG_TYPEC_TCPM) += tcpm.o obj-y += fusb302/ obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c new file mode 100644 index 000000000000..999d7904172a --- /dev/null +++ b/drivers/usb/typec/bus.c @@ -0,0 +1,401 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Bus for USB Type-C Alternate Modes + * + * Copyright (C) 2018 Intel Corporation + * Author: Heikki Krogerus + */ + +#include + +#include "bus.h" + +static inline int typec_altmode_set_mux(struct altmode *alt, u8 state) +{ + return alt->mux ? alt->mux->set(alt->mux, state) : 0; +} + +static int typec_altmode_set_state(struct typec_altmode *adev, int state) +{ + bool is_port = is_typec_port(adev->dev.parent); + struct altmode *port_altmode; + int ret; + + port_altmode = is_port ? to_altmode(adev) : to_altmode(adev)->partner; + + ret = typec_altmode_set_mux(port_altmode, state); + if (ret) + return ret; + + blocking_notifier_call_chain(&port_altmode->nh, state, NULL); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* Common API */ + +/** + * typec_altmode_notify - Communication between the OS and alternate mode driver + * @adev: Handle to the alternate mode + * @conf: Alternate mode specific configuration value + * @data: Alternate mode specific data + * + * The primary purpose for this function is to allow the alternate mode drivers + * to tell which pin configuration has been negotiated with the partner. That + * information will then be used for example to configure the muxes. + * Communication to the other direction is also possible, and low level device + * drivers can also send notifications to the alternate mode drivers. The actual + * communication will be specific for every SVID. + */ +int typec_altmode_notify(struct typec_altmode *adev, + unsigned long conf, void *data) +{ + bool is_port = is_typec_port(adev->dev.parent); + struct altmode *altmode; + struct altmode *partner; + int ret; + + if (!adev) + return 0; + + altmode = to_altmode(adev); + + if (!altmode->partner) + return -ENODEV; + + partner = altmode->partner; + + ret = typec_altmode_set_mux(is_port ? altmode : partner, (u8)conf); + if (ret) + return ret; + + blocking_notifier_call_chain(is_port ? &altmode->nh : &partner->nh, + conf, data); + + if (partner->adev.ops && partner->adev.ops->notify) + return partner->adev.ops->notify(&partner->adev, conf, data); + + return 0; +} +EXPORT_SYMBOL_GPL(typec_altmode_notify); + +/** + * typec_altmode_enter - Enter Mode + * @adev: The alternate mode + * + * The alternate mode drivers use this function to enter mode. The port drivers + * use this to inform the alternate mode drivers that the partner has initiated + * Enter Mode command. + */ +int typec_altmode_enter(struct typec_altmode *adev) +{ + struct altmode *partner = to_altmode(adev)->partner; + struct typec_altmode *pdev = &partner->adev; + int ret; + + if (!adev || adev->active) + return 0; + + if (!pdev->ops || !pdev->ops->enter) + return -EOPNOTSUPP; + + /* Moving to USB Safe State */ + ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE); + if (ret) + return ret; + + /* Enter Mode */ + return pdev->ops->enter(pdev); +} +EXPORT_SYMBOL_GPL(typec_altmode_enter); + +/** + * typec_altmode_exit - Exit Mode + * @adev: The alternate mode + * + * The partner of @adev has initiated Exit Mode command. + */ +int typec_altmode_exit(struct typec_altmode *adev) +{ + struct altmode *partner = to_altmode(adev)->partner; + struct typec_altmode *pdev = &partner->adev; + int ret; + + if (!adev || !adev->active) + return 0; + + if (!pdev->ops || !pdev->ops->enter) + return -EOPNOTSUPP; + + /* Moving to USB Safe State */ + ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE); + if (ret) + return ret; + + /* Exit Mode command */ + return pdev->ops->exit(pdev); +} +EXPORT_SYMBOL_GPL(typec_altmode_exit); + +/** + * typec_altmode_attention - Attention command + * @adev: The alternate mode + * @vdo: VDO for the Attention command + * + * Notifies the partner of @adev about Attention command. + */ +void typec_altmode_attention(struct typec_altmode *adev, u32 vdo) +{ + struct typec_altmode *pdev = &to_altmode(adev)->partner->adev; + + if (pdev->ops && pdev->ops->attention) + pdev->ops->attention(pdev, vdo); +} +EXPORT_SYMBOL_GPL(typec_altmode_attention); + +/** + * typec_altmode_vdm - Send Vendor Defined Messages (VDM) to the partner + * @adev: Alternate mode handle + * @header: VDM Header + * @vdo: Array of Vendor Defined Data Objects + * @count: Number of Data Objects + * + * The alternate mode drivers use this function for SVID specific communication + * with the partner. The port drivers use it to deliver the Structured VDMs + * received from the partners to the alternate mode drivers. + */ +int typec_altmode_vdm(struct typec_altmode *adev, + const u32 header, const u32 *vdo, int count) +{ + struct typec_altmode *pdev; + struct altmode *altmode; + + if (!adev) + return 0; + + altmode = to_altmode(adev); + + if (!altmode->partner) + return -ENODEV; + + pdev = &altmode->partner->adev; + + if (!pdev->ops || !pdev->ops->vdm) + return -EOPNOTSUPP; + + return pdev->ops->vdm(pdev, header, vdo, count); +} +EXPORT_SYMBOL_GPL(typec_altmode_vdm); + +const struct typec_altmode * +typec_altmode_get_partner(struct typec_altmode *adev) +{ + return &to_altmode(adev)->partner->adev; +} +EXPORT_SYMBOL_GPL(typec_altmode_get_partner); + +/* -------------------------------------------------------------------------- */ +/* API for the alternate mode drivers */ + +/** + * typec_altmode_get_plug - Find cable plug alternate mode + * @adev: Handle to partner alternate mode + * @index: Cable plug index + * + * Increment reference count for cable plug alternate mode device. Returns + * handle to the cable plug alternate mode, or NULL if none is found. + */ +struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *adev, + enum typec_plug_index index) +{ + struct altmode *port = to_altmode(adev)->partner; + + if (port->plug[index]) { + get_device(&port->plug[index]->adev.dev); + return &port->plug[index]->adev; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(typec_altmode_get_plug); + +/** + * typec_altmode_put_plug - Decrement cable plug alternate mode reference count + * @plug: Handle to the cable plug alternate mode + */ +void typec_altmode_put_plug(struct typec_altmode *plug) +{ + if (plug) + put_device(&plug->dev); +} +EXPORT_SYMBOL_GPL(typec_altmode_put_plug); + +int __typec_altmode_register_driver(struct typec_altmode_driver *drv, + struct module *module) +{ + if (!drv->probe) + return -EINVAL; + + drv->driver.owner = module; + drv->driver.bus = &typec_bus; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(__typec_altmode_register_driver); + +void typec_altmode_unregister_driver(struct typec_altmode_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(typec_altmode_unregister_driver); + +/* -------------------------------------------------------------------------- */ +/* API for the port drivers */ + +/** + * typec_match_altmode - Match SVID to an array of alternate modes + * @altmodes: Array of alternate modes + * @n: Number of elements in the array, or -1 for NULL termiated arrays + * @svid: Standard or Vendor ID to match with + * + * Return pointer to an alternate mode with SVID mathing @svid, or NULL when no + * match is found. + */ +struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes, + size_t n, u16 svid, u8 mode) +{ + int i; + + for (i = 0; i < n; i++) { + if (!altmodes[i]) + break; + if (altmodes[i]->svid == svid && altmodes[i]->mode == mode) + return altmodes[i]; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(typec_match_altmode); + +/* -------------------------------------------------------------------------- */ + +static ssize_t +description_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct typec_altmode *alt = to_typec_altmode(dev); + + return sprintf(buf, "%s\n", alt->desc ? alt->desc : ""); +} +static DEVICE_ATTR_RO(description); + +static struct attribute *typec_attrs[] = { + &dev_attr_description.attr, + NULL +}; +ATTRIBUTE_GROUPS(typec); + +static int typec_match(struct device *dev, struct device_driver *driver) +{ + struct typec_altmode_driver *drv = to_altmode_driver(driver); + struct typec_altmode *altmode = to_typec_altmode(dev); + const struct typec_device_id *id; + + for (id = drv->id_table; id->svid; id++) + if (id->svid == altmode->svid && + (id->mode == TYPEC_ANY_MODE || id->mode == altmode->mode)) + return 1; + return 0; +} + +static int typec_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct typec_altmode *altmode = to_typec_altmode(dev); + + if (add_uevent_var(env, "SVID=%04X", altmode->svid)) + return -ENOMEM; + + if (add_uevent_var(env, "MODE=%u", altmode->mode)) + return -ENOMEM; + + return add_uevent_var(env, "MODALIAS=typec:id%04Xm%02X", + altmode->svid, altmode->mode); +} + +static int typec_altmode_create_links(struct altmode *alt) +{ + struct device *port_dev = &alt->partner->adev.dev; + struct device *dev = &alt->adev.dev; + int err; + + err = sysfs_create_link(&dev->kobj, &port_dev->kobj, "port"); + if (err) + return err; + + err = sysfs_create_link(&port_dev->kobj, &dev->kobj, "partner"); + if (err) + sysfs_remove_link(&dev->kobj, "port"); + + return err; +} + +static void typec_altmode_remove_links(struct altmode *alt) +{ + sysfs_remove_link(&alt->partner->adev.dev.kobj, "partner"); + sysfs_remove_link(&alt->adev.dev.kobj, "port"); +} + +static int typec_probe(struct device *dev) +{ + struct typec_altmode_driver *drv = to_altmode_driver(dev->driver); + struct typec_altmode *adev = to_typec_altmode(dev); + struct altmode *altmode = to_altmode(adev); + int ret; + + /* Fail if the port does not support the alternate mode */ + if (!altmode->partner) + return -ENODEV; + + ret = typec_altmode_create_links(altmode); + if (ret) { + dev_warn(dev, "failed to create symlinks\n"); + return ret; + } + + ret = drv->probe(adev); + if (ret) + typec_altmode_remove_links(altmode); + + return ret; +} + +static int typec_remove(struct device *dev) +{ + struct typec_altmode_driver *drv = to_altmode_driver(dev->driver); + struct typec_altmode *adev = to_typec_altmode(dev); + struct altmode *altmode = to_altmode(adev); + + typec_altmode_remove_links(altmode); + + if (drv->remove) + drv->remove(to_typec_altmode(dev)); + + if (adev->active) { + WARN_ON(typec_altmode_set_state(adev, TYPEC_STATE_SAFE)); + typec_altmode_update_active(adev, false); + } + + adev->desc = NULL; + adev->ops = NULL; + + return 0; +} + +struct bus_type typec_bus = { + .name = "typec", + .dev_groups = typec_groups, + .match = typec_match, + .uevent = typec_uevent, + .probe = typec_probe, + .remove = typec_remove, +}; diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h new file mode 100644 index 000000000000..62aaf8b56bde --- /dev/null +++ b/drivers/usb/typec/bus.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __USB_TYPEC_ALTMODE_H__ +#define __USB_TYPEC_ALTMODE_H__ + +#include +#include + +struct bus_type; + +struct altmode { + unsigned int id; + struct typec_altmode adev; + struct typec_mux *mux; + + enum typec_port_data roles; + + struct attribute *attrs[5]; + char group_name[6]; + struct attribute_group group; + const struct attribute_group *groups[2]; + + struct altmode *partner; + struct altmode *plug[2]; + + struct blocking_notifier_head nh; +}; + +#define to_altmode(d) container_of(d, struct altmode, adev) + +extern struct bus_type typec_bus; +extern const struct device_type typec_altmode_dev_type; +extern const struct device_type typec_port_dev_type; + +#define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type) +#define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type) + +#endif /* __USB_TYPEC_ALTMODE_H__ */ diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 96dc9c4f73f0..c202975f8097 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -10,28 +10,13 @@ #include #include #include -#include -#include -struct typec_altmode { - struct device dev; - u16 svid; - u8 mode; - - u32 vdo; - char *desc; - enum typec_port_type roles; - unsigned int active:1; - - struct attribute *attrs[5]; - char group_name[6]; - struct attribute_group group; - const struct attribute_group *groups[2]; -}; +#include "bus.h" struct typec_plug { struct device dev; enum typec_plug_index index; + struct ida mode_ids; }; struct typec_cable { @@ -46,11 +31,13 @@ struct typec_partner { unsigned int usb_pd:1; struct usb_pd_identity *identity; enum typec_accessory accessory; + struct ida mode_ids; }; struct typec_port { unsigned int id; struct device dev; + struct ida mode_ids; int prefer_role; enum typec_data_role data_role; @@ -71,17 +58,14 @@ struct typec_port { #define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev) #define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev) #define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev) -#define to_altmode(_dev_) container_of(_dev_, struct typec_altmode, dev) static const struct device_type typec_partner_dev_type; static const struct device_type typec_cable_dev_type; static const struct device_type typec_plug_dev_type; -static const struct device_type typec_port_dev_type; #define is_typec_partner(_dev_) (_dev_->type == &typec_partner_dev_type) #define is_typec_cable(_dev_) (_dev_->type == &typec_cable_dev_type) #define is_typec_plug(_dev_) (_dev_->type == &typec_plug_dev_type) -#define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type) static DEFINE_IDA(typec_index_ida); static struct class *typec_class; @@ -163,25 +147,148 @@ static void typec_report_identity(struct device *dev) /* ------------------------------------------------------------------------- */ /* Alternate Modes */ +static int altmode_match(struct device *dev, void *data) +{ + struct typec_altmode *adev = to_typec_altmode(dev); + struct typec_device_id *id = data; + + if (!is_typec_altmode(dev)) + return 0; + + return ((adev->svid == id->svid) && (adev->mode == id->mode)); +} + +static void typec_altmode_set_partner(struct altmode *altmode) +{ + struct typec_altmode *adev = &altmode->adev; + struct typec_device_id id = { adev->svid, adev->mode, }; + struct typec_port *port = typec_altmode2port(adev); + struct altmode *partner; + struct device *dev; + + dev = device_find_child(&port->dev, &id, altmode_match); + if (!dev) + return; + + /* Bind the port alt mode to the partner/plug alt mode. */ + partner = to_altmode(to_typec_altmode(dev)); + altmode->partner = partner; + + /* Bind the partner/plug alt mode to the port alt mode. */ + if (is_typec_plug(adev->dev.parent)) { + struct typec_plug *plug = to_typec_plug(adev->dev.parent); + + partner->plug[plug->index] = altmode; + } else { + partner->partner = altmode; + } +} + +static void typec_altmode_put_partner(struct altmode *altmode) +{ + struct altmode *partner = altmode->partner; + struct typec_altmode *adev; + + if (!partner) + return; + + adev = &partner->adev; + + if (is_typec_plug(adev->dev.parent)) { + struct typec_plug *plug = to_typec_plug(adev->dev.parent); + + partner->plug[plug->index] = NULL; + } else { + partner->partner = NULL; + } + put_device(&adev->dev); +} + +static int __typec_port_match(struct device *dev, const void *name) +{ + return !strcmp((const char *)name, dev_name(dev)); +} + +static void *typec_port_match(struct device_connection *con, int ep, void *data) +{ + return class_find_device(typec_class, NULL, con->endpoint[ep], + __typec_port_match); +} + +struct typec_altmode * +typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode, + struct notifier_block *nb) +{ + struct typec_device_id id = { svid, mode, }; + struct device *altmode_dev; + struct device *port_dev; + struct altmode *altmode; + int ret; + + /* Find the port linked to the caller */ + port_dev = device_connection_find_match(dev, NULL, NULL, + typec_port_match); + if (IS_ERR_OR_NULL(port_dev)) + return port_dev ? ERR_CAST(port_dev) : ERR_PTR(-ENODEV); + + /* Find the altmode with matching svid */ + altmode_dev = device_find_child(port_dev, &id, altmode_match); + + put_device(port_dev); + + if (!altmode_dev) + return ERR_PTR(-ENODEV); + + altmode = to_altmode(to_typec_altmode(altmode_dev)); + + /* Register notifier */ + ret = blocking_notifier_chain_register(&altmode->nh, nb); + if (ret) { + put_device(altmode_dev); + return ERR_PTR(ret); + } + + return &altmode->adev; +} +EXPORT_SYMBOL_GPL(typec_altmode_register_notifier); + +void typec_altmode_unregister_notifier(struct typec_altmode *adev, + struct notifier_block *nb) +{ + struct altmode *altmode = to_altmode(adev); + + blocking_notifier_chain_unregister(&altmode->nh, nb); + put_device(&adev->dev); +} +EXPORT_SYMBOL_GPL(typec_altmode_unregister_notifier); + /** * typec_altmode_update_active - Report Enter/Exit mode - * @alt: Handle to the alternate mode + * @adev: Handle to the alternate mode * @active: True when the mode has been entered * * If a partner or cable plug executes Enter/Exit Mode command successfully, the * drivers use this routine to report the updated state of the mode. */ -void typec_altmode_update_active(struct typec_altmode *alt, bool active) +void typec_altmode_update_active(struct typec_altmode *adev, bool active) { char dir[6]; - if (alt->active == active) + if (adev->active == active) return; - alt->active = active; - snprintf(dir, sizeof(dir), "mode%d", alt->mode); - sysfs_notify(&alt->dev.kobj, dir, "active"); - kobject_uevent(&alt->dev.kobj, KOBJ_CHANGE); + if (!is_typec_port(adev->dev.parent)) { + if (!active) + module_put(adev->dev.driver->owner); + else + WARN_ON(!try_module_get(adev->dev.driver->owner)); + } + + adev->active = active; + snprintf(dir, sizeof(dir), "mode%d", adev->mode); + sysfs_notify(&adev->dev.kobj, dir, "active"); + sysfs_notify(&adev->dev.kobj, NULL, "active"); + kobject_uevent(&adev->dev.kobj, KOBJ_CHANGE); } EXPORT_SYMBOL_GPL(typec_altmode_update_active); @@ -208,7 +315,7 @@ EXPORT_SYMBOL_GPL(typec_altmode2port); static ssize_t vdo_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_altmode *alt = to_altmode(dev); + struct typec_altmode *alt = to_typec_altmode(dev); return sprintf(buf, "0x%08x\n", alt->vdo); } @@ -217,7 +324,7 @@ static DEVICE_ATTR_RO(vdo); static ssize_t description_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_altmode *alt = to_altmode(dev); + struct typec_altmode *alt = to_typec_altmode(dev); return sprintf(buf, "%s\n", alt->desc ? alt->desc : ""); } @@ -226,7 +333,7 @@ static DEVICE_ATTR_RO(description); static ssize_t active_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_altmode *alt = to_altmode(dev); + struct typec_altmode *alt = to_typec_altmode(dev); return sprintf(buf, "%s\n", alt->active ? "yes" : "no"); } @@ -234,21 +341,37 @@ active_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t active_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct typec_altmode *alt = to_altmode(dev); - struct typec_port *port = typec_altmode2port(alt); - bool activate; + struct typec_altmode *adev = to_typec_altmode(dev); + struct altmode *altmode = to_altmode(adev); + bool enter; int ret; - if (!port->cap->activate_mode) - return -EOPNOTSUPP; - - ret = kstrtobool(buf, &activate); + ret = kstrtobool(buf, &enter); if (ret) return ret; - ret = port->cap->activate_mode(port->cap, alt->mode, activate); - if (ret) - return ret; + if (adev->active == enter) + return size; + + if (is_typec_port(adev->dev.parent)) { + typec_altmode_update_active(adev, enter); + + /* Make sure that the partner exits the mode before disabling */ + if (altmode->partner && !enter && altmode->partner->adev.active) + typec_altmode_exit(&altmode->partner->adev); + } else if (altmode->partner) { + if (enter && !altmode->partner->adev.active) { + dev_warn(dev, "port has the mode disabled\n"); + return -EPERM; + } + } + + /* Note: If there is no driver, the mode will not be entered */ + if (adev->ops && adev->ops->activate) { + ret = adev->ops->activate(adev, enter); + if (ret) + return ret; + } return size; } @@ -258,7 +381,7 @@ static ssize_t supported_roles_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_altmode *alt = to_altmode(dev); + struct altmode *alt = to_altmode(to_typec_altmode(dev)); ssize_t ret; switch (alt->roles) { @@ -277,29 +400,72 @@ supported_roles_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(supported_roles); -static void typec_altmode_release(struct device *dev) +static ssize_t +mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_altmode *alt = to_altmode(dev); + struct typec_altmode *adev = to_typec_altmode(dev); - kfree(alt); + return sprintf(buf, "%u\n", adev->mode); } +static DEVICE_ATTR_RO(mode); -static ssize_t svid_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t +svid_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_altmode *alt = to_altmode(dev); + struct typec_altmode *adev = to_typec_altmode(dev); - return sprintf(buf, "%04x\n", alt->svid); + return sprintf(buf, "%04x\n", adev->svid); } static DEVICE_ATTR_RO(svid); static struct attribute *typec_altmode_attrs[] = { + &dev_attr_active.attr, + &dev_attr_mode.attr, &dev_attr_svid.attr, + &dev_attr_vdo.attr, NULL }; ATTRIBUTE_GROUPS(typec_altmode); -static const struct device_type typec_altmode_dev_type = { +static int altmode_id_get(struct device *dev) +{ + struct ida *ids; + + if (is_typec_partner(dev)) + ids = &to_typec_partner(dev)->mode_ids; + else if (is_typec_plug(dev)) + ids = &to_typec_plug(dev)->mode_ids; + else + ids = &to_typec_port(dev)->mode_ids; + + return ida_simple_get(ids, 0, 0, GFP_KERNEL); +} + +static void altmode_id_remove(struct device *dev, int id) +{ + struct ida *ids; + + if (is_typec_partner(dev)) + ids = &to_typec_partner(dev)->mode_ids; + else if (is_typec_plug(dev)) + ids = &to_typec_plug(dev)->mode_ids; + else + ids = &to_typec_port(dev)->mode_ids; + + ida_simple_remove(ids, id); +} + +static void typec_altmode_release(struct device *dev) +{ + struct altmode *alt = to_altmode(to_typec_altmode(dev)); + + typec_altmode_put_partner(alt); + + altmode_id_remove(alt->adev.dev.parent, alt->id); + kfree(alt); +} + +const struct device_type typec_altmode_dev_type = { .name = "typec_alternate_mode", .groups = typec_altmode_groups, .release = typec_altmode_release, @@ -309,58 +475,74 @@ static struct typec_altmode * typec_register_altmode(struct device *parent, const struct typec_altmode_desc *desc) { - struct typec_altmode *alt; + unsigned int id = altmode_id_get(parent); + bool is_port = is_typec_port(parent); + struct altmode *alt; int ret; alt = kzalloc(sizeof(*alt), GFP_KERNEL); if (!alt) return ERR_PTR(-ENOMEM); - alt->svid = desc->svid; - alt->mode = desc->mode; - alt->vdo = desc->vdo; + alt->adev.svid = desc->svid; + alt->adev.mode = desc->mode; + alt->adev.vdo = desc->vdo; alt->roles = desc->roles; + alt->id = id; alt->attrs[0] = &dev_attr_vdo.attr; alt->attrs[1] = &dev_attr_description.attr; alt->attrs[2] = &dev_attr_active.attr; - if (is_typec_port(parent)) + if (is_port) { alt->attrs[3] = &dev_attr_supported_roles.attr; + alt->adev.active = true; /* Enabled by default */ + } sprintf(alt->group_name, "mode%d", desc->mode); alt->group.name = alt->group_name; alt->group.attrs = alt->attrs; alt->groups[0] = &alt->group; - alt->dev.parent = parent; - alt->dev.groups = alt->groups; - alt->dev.type = &typec_altmode_dev_type; - dev_set_name(&alt->dev, "%s-%04x:%u", dev_name(parent), - alt->svid, alt->mode); + alt->adev.dev.parent = parent; + alt->adev.dev.groups = alt->groups; + alt->adev.dev.type = &typec_altmode_dev_type; + dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id); + + /* Link partners and plugs with the ports */ + if (is_port) + BLOCKING_INIT_NOTIFIER_HEAD(&alt->nh); + else + typec_altmode_set_partner(alt); + + /* The partners are bind to drivers */ + if (is_typec_partner(parent)) + alt->adev.dev.bus = &typec_bus; - ret = device_register(&alt->dev); + ret = device_register(&alt->adev.dev); if (ret) { dev_err(parent, "failed to register alternate mode (%d)\n", ret); - put_device(&alt->dev); + put_device(&alt->adev.dev); return ERR_PTR(ret); } - return alt; + return &alt->adev; } /** * typec_unregister_altmode - Unregister Alternate Mode - * @alt: The alternate mode to be unregistered + * @adev: The alternate mode to be unregistered * * Unregister device created with typec_partner_register_altmode(), * typec_plug_register_altmode() or typec_port_register_altmode(). */ -void typec_unregister_altmode(struct typec_altmode *alt) +void typec_unregister_altmode(struct typec_altmode *adev) { - if (!IS_ERR_OR_NULL(alt)) - device_unregister(&alt->dev); + if (IS_ERR_OR_NULL(adev)) + return; + typec_mux_put(to_altmode(adev)->mux); + device_unregister(&adev->dev); } EXPORT_SYMBOL_GPL(typec_unregister_altmode); @@ -398,6 +580,7 @@ static void typec_partner_release(struct device *dev) { struct typec_partner *partner = to_typec_partner(dev); + ida_destroy(&partner->mode_ids); kfree(partner); } @@ -463,6 +646,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, if (!partner) return ERR_PTR(-ENOMEM); + ida_init(&partner->mode_ids); partner->usb_pd = desc->usb_pd; partner->accessory = desc->accessory; @@ -511,6 +695,7 @@ static void typec_plug_release(struct device *dev) { struct typec_plug *plug = to_typec_plug(dev); + ida_destroy(&plug->mode_ids); kfree(plug); } @@ -563,6 +748,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable, sprintf(name, "plug%d", desc->index); + ida_init(&plug->mode_ids); plug->index = desc->index; plug->dev.class = typec_class; plug->dev.parent = &cable->dev; @@ -1083,12 +1269,13 @@ static void typec_release(struct device *dev) struct typec_port *port = to_typec_port(dev); ida_simple_remove(&typec_index_ida, port->id); + ida_destroy(&port->mode_ids); typec_switch_put(port->sw); typec_mux_put(port->mux); kfree(port); } -static const struct device_type typec_port_dev_type = { +const struct device_type typec_port_dev_type = { .name = "typec_port", .groups = typec_groups, .uevent = typec_uevent, @@ -1279,11 +1466,11 @@ EXPORT_SYMBOL_GPL(typec_get_orientation); /** * typec_set_mode - Set mode of operation for USB Type-C connector - * @port: USB Type-C port for the connector - * @mode: Operation mode for the connector + * @port: USB Type-C connector + * @mode: Accessory Mode, USB Operation or Safe State * - * Set mode @mode for @port. This function will configure the muxes needed to - * enter @mode. + * Configure @port for Accessory Mode @mode. This function will configure the + * muxes needed for @mode. */ int typec_set_mode(struct typec_port *port, int mode) { @@ -1297,6 +1484,7 @@ EXPORT_SYMBOL_GPL(typec_set_mode); * typec_port_register_altmode - Register USB Type-C Port Alternate Mode * @port: USB Type-C Port that supports the alternate mode * @desc: Description of the alternate mode + * @drvdata: Private pointer to driver specific info * * This routine is used to register an alternate mode that @port is capable of * supporting. @@ -1307,7 +1495,23 @@ struct typec_altmode * typec_port_register_altmode(struct typec_port *port, const struct typec_altmode_desc *desc) { - return typec_register_altmode(&port->dev, desc); + struct typec_altmode *adev; + struct typec_mux *mux; + char id[10]; + + sprintf(id, "id%04xm%02x", desc->svid, desc->mode); + + mux = typec_mux_get(port->dev.parent, id); + if (IS_ERR(mux)) + return ERR_CAST(mux); + + adev = typec_register_altmode(&port->dev, desc); + if (IS_ERR(adev)) + typec_mux_put(mux); + else + to_altmode(adev)->mux = mux; + + return adev; } EXPORT_SYMBOL_GPL(typec_port_register_altmode); @@ -1381,10 +1585,12 @@ struct typec_port *typec_register_port(struct device *parent, break; } + ida_init(&port->mode_ids); + mutex_init(&port->port_type_lock); + port->id = id; port->cap = cap; port->port_type = cap->type; - mutex_init(&port->port_type_lock); port->prefer_role = cap->prefer_role; port->dev.class = typec_class; @@ -1428,8 +1634,19 @@ EXPORT_SYMBOL_GPL(typec_unregister_port); static int __init typec_init(void) { + int ret; + + ret = bus_register(&typec_bus); + if (ret) + return ret; + typec_class = class_create(THIS_MODULE, "typec"); - return PTR_ERR_OR_ZERO(typec_class); + if (IS_ERR(typec_class)) { + bus_unregister(&typec_bus); + return PTR_ERR(typec_class); + } + + return 0; } subsys_initcall(typec_init); @@ -1437,6 +1654,7 @@ static void __exit typec_exit(void) { class_destroy(typec_class); ida_destroy(&typec_index_ida); + bus_unregister(&typec_bus); } module_exit(typec_exit); diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 96a71a648eed..1298a7daa57d 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -746,4 +746,19 @@ struct tb_service_id { #define TBSVC_MATCH_PROTOCOL_VERSION 0x0004 #define TBSVC_MATCH_PROTOCOL_REVISION 0x0008 +/* USB Type-C Alternate Modes */ + +#define TYPEC_ANY_MODE 0x7 + +/** + * struct typec_device_id - USB Type-C alternate mode identifiers + * @svid: Standard or Vendor ID + * @mode: Mode index + */ +struct typec_device_id { + __u16 svid; + __u8 mode; + kernel_ulong_t driver_data; +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 2dcb1683075f..7df4ecabc78a 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -5,21 +5,18 @@ #include -/* XXX: Once we have a header for USB Power Delivery, this belongs there */ -#define ALTMODE_MAX_MODES 6 - /* USB Type-C Specification releases */ #define USB_TYPEC_REV_1_0 0x100 /* 1.0 */ #define USB_TYPEC_REV_1_1 0x110 /* 1.1 */ #define USB_TYPEC_REV_1_2 0x120 /* 1.2 */ -struct typec_altmode; struct typec_partner; struct typec_cable; struct typec_plug; struct typec_port; struct fwnode_handle; +struct device; enum typec_port_type { TYPEC_PORT_SRC, @@ -107,7 +104,7 @@ struct typec_altmode_desc { u8 mode; u32 vdo; /* Only used with ports */ - enum typec_port_type roles; + enum typec_port_data roles; }; struct typec_altmode @@ -186,7 +183,6 @@ struct typec_partner_desc { * @dr_set: Set Data Role * @pr_set: Set Power Role * @vconn_set: Set VCONN Role - * @activate_mode: Enter/exit given Alternate Mode * @port_type_set: Set port type * * Static capabilities of a single USB Type-C port. @@ -212,12 +208,8 @@ struct typec_capability { enum typec_role); int (*vconn_set)(const struct typec_capability *, enum typec_role); - - int (*activate_mode)(const struct typec_capability *, - int mode, int activate); int (*port_type_set)(const struct typec_capability *, - enum typec_port_type); - + enum typec_port_type); }; /* Specific to try_role(). Indicates the user want's to clear the preference. */ diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h new file mode 100644 index 000000000000..9a88c74a1d0d --- /dev/null +++ b/include/linux/usb/typec_altmode.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __USB_TYPEC_ALTMODE_H +#define __USB_TYPEC_ALTMODE_H + +#include +#include +#include + +#define MODE_DISCOVERY_MAX 6 + +struct typec_altmode_ops; + +/** + * struct typec_altmode - USB Type-C alternate mode device + * @dev: Driver model's view of this device + * @svid: Standard or Vendor ID (SVID) of the alternate mode + * @mode: Index of the Mode + * @vdo: VDO returned by Discover Modes USB PD command + * @active: Tells has the mode been entered or not + * @desc: Optional human readable description of the mode + * @ops: Operations vector from the driver + */ +struct typec_altmode { + struct device dev; + u16 svid; + int mode; + u32 vdo; + unsigned int active:1; + + char *desc; + const struct typec_altmode_ops *ops; +}; + +#define to_typec_altmode(d) container_of(d, struct typec_altmode, dev) + +static inline void typec_altmode_set_drvdata(struct typec_altmode *altmode, + void *data) +{ + dev_set_drvdata(&altmode->dev, data); +} + +static inline void *typec_altmode_get_drvdata(struct typec_altmode *altmode) +{ + return dev_get_drvdata(&altmode->dev); +} + +/** + * struct typec_altmode_ops - Alternate mode specific operations vector + * @enter: Operations to be executed with Enter Mode Command + * @exit: Operations to be executed with Exit Mode Command + * @attention: Callback for Attention Command + * @vdm: Callback for SVID specific commands + * @notify: Communication channel for platform and the alternate mode + * @activate: User callback for Enter/Exit Mode + */ +struct typec_altmode_ops { + int (*enter)(struct typec_altmode *altmode); + int (*exit)(struct typec_altmode *altmode); + void (*attention)(struct typec_altmode *altmode, u32 vdo); + int (*vdm)(struct typec_altmode *altmode, const u32 hdr, + const u32 *vdo, int cnt); + int (*notify)(struct typec_altmode *altmode, unsigned long conf, + void *data); + int (*activate)(struct typec_altmode *altmode, int activate); +}; + +int typec_altmode_enter(struct typec_altmode *altmode); +int typec_altmode_exit(struct typec_altmode *altmode); +void typec_altmode_attention(struct typec_altmode *altmode, u32 vdo); +int typec_altmode_vdm(struct typec_altmode *altmode, + const u32 header, const u32 *vdo, int count); +int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf, + void *data); +const struct typec_altmode * +typec_altmode_get_partner(struct typec_altmode *altmode); + +/* + * These are the connector states (USB, Safe and Alt Mode) defined in USB Type-C + * Specification. SVID specific connector states are expected to follow and + * start from the value TYPEC_STATE_MODAL. + */ +enum { + TYPEC_STATE_SAFE, /* USB Safe State */ + TYPEC_STATE_USB, /* USB Operation */ + TYPEC_STATE_MODAL, /* Alternate Modes */ +}; + +/* + * For the muxes there is no difference between Accessory Modes and Alternate + * Modes, so the Accessory Modes are supplied with specific modal state values + * here. Unlike with Alternate Modes, where the mux will be linked with the + * alternate mode device, the mux for Accessory Modes will be linked with the + * port device instead. + * + * Port drivers can use TYPEC_MODE_AUDIO and TYPEC_MODE_DEBUG as the mode + * value for typec_set_mode() when accessory modes are supported. + */ +enum { + TYPEC_MODE_AUDIO = TYPEC_STATE_MODAL, /* Audio Accessory */ + TYPEC_MODE_DEBUG, /* Debug Accessory */ +}; + +#define TYPEC_MODAL_STATE(_state_) ((_state_) + TYPEC_STATE_MODAL) + +struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *altmode, + enum typec_plug_index index); +void typec_altmode_put_plug(struct typec_altmode *plug); + +struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes, + size_t n, u16 svid, u8 mode); + +struct typec_altmode * +typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode, + struct notifier_block *nb); + +void typec_altmode_unregister_notifier(struct typec_altmode *adev, + struct notifier_block *nb); + +/** + * typec_altmode_get_orientation - Get cable plug orientation + * altmode: Handle to the alternate mode + */ +static inline enum typec_orientation +typec_altmode_get_orientation(struct typec_altmode *altmode) +{ + return typec_get_orientation(typec_altmode2port(altmode)); +} + +/** + * struct typec_altmode_driver - USB Type-C alternate mode device driver + * @id_table: Null terminated array of SVIDs + * @probe: Callback for device binding + * @remove: Callback for device unbinding + * @driver: Device driver model driver + * + * These drivers will be bind to the partner alternate mode devices. They will + * handle all SVID specific communication. + */ +struct typec_altmode_driver { + const struct typec_device_id *id_table; + int (*probe)(struct typec_altmode *altmode); + void (*remove)(struct typec_altmode *altmode); + struct device_driver driver; +}; + +#define to_altmode_driver(d) container_of(d, struct typec_altmode_driver, \ + driver) + +#define typec_altmode_register_driver(drv) \ + __typec_altmode_register_driver(drv, THIS_MODULE) +int __typec_altmode_register_driver(struct typec_altmode_driver *drv, + struct module *module); +void typec_altmode_unregister_driver(struct typec_altmode_driver *drv); + +#define module_typec_altmode_driver(__typec_altmode_driver) \ + module_driver(__typec_altmode_driver, typec_altmode_register_driver, \ + typec_altmode_unregister_driver) + +#endif /* __USB_TYPEC_ALTMODE_H */ diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index 6667f7b491d6..293004499b4d 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -221,5 +221,9 @@ int main(void) DEVID_FIELD(tb_service_id, protocol_version); DEVID_FIELD(tb_service_id, protocol_revision); + DEVID(typec_device_id); + DEVID_FIELD(typec_device_id, svid); + DEVID_FIELD(typec_device_id, mode); + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 52fd54a8fe39..7be43697ff84 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1352,6 +1352,19 @@ static int do_tbsvc_entry(const char *filename, void *symval, char *alias) } ADD_TO_DEVTABLE("tbsvc", tb_service_id, do_tbsvc_entry); +/* Looks like: typec:idNmN */ +static int do_typec_entry(const char *filename, void *symval, char *alias) +{ + DEF_FIELD(symval, typec_device_id, svid); + DEF_FIELD(symval, typec_device_id, mode); + + sprintf(alias, "typec:id%04X", svid); + ADD(alias, "m", mode != TYPEC_ANY_MODE, mode); + + return 1; +} +ADD_TO_DEVTABLE("typec", typec_device_id, do_typec_entry); + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { -- cgit v1.2.3 From 0e3bb7d6894d9b6e67d6382bb03a46a1dc989588 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 27 Jun 2018 18:19:51 +0300 Subject: usb: typec: Add driver for DisplayPort alternate mode DisplayPort USB Type-C Alt Mode allows DisplayPort displays and adapters to be attached to the USB Type-C ports on the system. Signed-off-by: Heikki Krogerus Tested-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-driver-typec-displayport | 49 ++ drivers/usb/typec/Kconfig | 2 + drivers/usb/typec/Makefile | 1 + drivers/usb/typec/altmodes/Kconfig | 14 + drivers/usb/typec/altmodes/Makefile | 2 + drivers/usb/typec/altmodes/displayport.c | 578 +++++++++++++++++++++ include/linux/usb/typec_dp.h | 95 ++++ 7 files changed, 741 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-typec-displayport create mode 100644 drivers/usb/typec/altmodes/Kconfig create mode 100644 drivers/usb/typec/altmodes/Makefile create mode 100644 drivers/usb/typec/altmodes/displayport.c create mode 100644 include/linux/usb/typec_dp.h (limited to 'drivers/usb') diff --git a/Documentation/ABI/testing/sysfs-driver-typec-displayport b/Documentation/ABI/testing/sysfs-driver-typec-displayport new file mode 100644 index 000000000000..231471ad0d4b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-typec-displayport @@ -0,0 +1,49 @@ +What: /sys/bus/typec/devices/.../displayport/configuration +Date: July 2018 +Contact: Heikki Krogerus +Description: + Shows the current DisplayPort configuration for the connector. + Valid values are USB, source and sink. Source means DisplayPort + source, and sink means DisplayPort sink. + + All supported configurations are listed as space separated list + with the active one wrapped in square brackets. + + Source example: + + USB [source] sink + + The configuration can be changed by writing to the file + + Note. USB configuration does not equal to Exit Mode. It is + separate configuration defined in VESA DisplayPort Alt Mode on + USB Type-C Standard. Functionally it equals to the situation + where the mode has been exited (to exit the mode, see + Documentation/ABI/testing/sysfs-bus-typec, and use file + /sys/bus/typec/devices/.../active). + +What: /sys/bus/typec/devices/.../displayport/pin_assignment +Date: July 2018 +Contact: Heikki Krogerus +Description: + VESA DisplayPort Alt Mode on USB Type-C Standard defines six + different pin assignments for USB Type-C connector that are + labeled A, B, C, D, E, and F. The supported pin assignments are + listed as space separated list with the active one wrapped in + square brackets. + + Example: + + C [D] + + Pin assignment can be changed by writing to the file. It is + possible to set pin assignment before configuration has been + set, but the assignment will not be active before the + connector is actually configured. + + Note. As of VESA DisplayPort Alt Mode on USB Type-C Standard + version 1.0b, pin assignments A, B, and F are deprecated. Only + pin assignment D can now carry simultaneously one channel of + USB SuperSpeed protocol. From user perspective pin assignments C + and E are equal, where all channels on the connector are used + for carrying DisplayPort protocol (allowing higher resolutions). diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index ee808903983f..00878c386dd0 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -104,4 +104,6 @@ config TYPEC_TPS6598X source "drivers/usb/typec/mux/Kconfig" +source "drivers/usb/typec/altmodes/Kconfig" + endif # TYPEC diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 335ee06748fc..45b0aef428a8 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TYPEC) += typec.o typec-y := class.o mux.o bus.o +obj-$(CONFIG_TYPEC) += altmodes/ obj-$(CONFIG_TYPEC_TCPM) += tcpm.o obj-y += fusb302/ obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o diff --git a/drivers/usb/typec/altmodes/Kconfig b/drivers/usb/typec/altmodes/Kconfig new file mode 100644 index 000000000000..efef2a64bc51 --- /dev/null +++ b/drivers/usb/typec/altmodes/Kconfig @@ -0,0 +1,14 @@ + +menu "USB Type-C Alternate Mode drivers" + +config TYPEC_DP_ALTMODE + tristate "DisplayPort Alternate Mode driver" + help + DisplayPort USB Type-C Alternate Mode allows DisplayPort + displays and adapters to be attached to the USB Type-C + connectors on the system. + + To compile this driver as a module, choose M here: the + module will be called typec_displayport. + +endmenu diff --git a/drivers/usb/typec/altmodes/Makefile b/drivers/usb/typec/altmodes/Makefile new file mode 100644 index 000000000000..5caf094ef71a --- /dev/null +++ b/drivers/usb/typec/altmodes/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_TYPEC_DP_ALTMODE) += typec_displayport.o +typec_displayport-y := displayport.o diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c new file mode 100644 index 000000000000..ef12b15bd484 --- /dev/null +++ b/drivers/usb/typec/altmodes/displayport.c @@ -0,0 +1,578 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * USB Typec-C DisplayPort Alternate Mode driver + * + * Copyright (C) 2018 Intel Corporation + * Author: Heikki Krogerus + * + * DisplayPort is trademark of VESA (www.vesa.org) + */ + +#include +#include +#include +#include +#include + +#define DP_HEADER(cmd) (VDO(USB_TYPEC_DP_SID, 1, cmd) | \ + VDO_OPOS(USB_TYPEC_DP_MODE)) + +enum { + DP_CONF_USB, + DP_CONF_DFP_D, + DP_CONF_UFP_D, + DP_CONF_DUAL_D, +}; + +/* Helper for setting/getting the pin assignement value to the configuration */ +#define DP_CONF_SET_PIN_ASSIGN(_a_) ((_a_) << 8) +#define DP_CONF_GET_PIN_ASSIGN(_conf_) (((_conf_) & GENMASK(15, 8)) >> 8) + +/* Pin assignments that use USB3.1 Gen2 signaling to carry DP protocol */ +#define DP_PIN_ASSIGN_GEN2_BR_MASK (BIT(DP_PIN_ASSIGN_A) | \ + BIT(DP_PIN_ASSIGN_B)) + +/* Pin assignments that use DP v1.3 signaling to carry DP protocol */ +#define DP_PIN_ASSIGN_DP_BR_MASK (BIT(DP_PIN_ASSIGN_C) | \ + BIT(DP_PIN_ASSIGN_D) | \ + BIT(DP_PIN_ASSIGN_E) | \ + BIT(DP_PIN_ASSIGN_F)) + +/* DP only pin assignments */ +#define DP_PIN_ASSIGN_DP_ONLY_MASK (BIT(DP_PIN_ASSIGN_A) | \ + BIT(DP_PIN_ASSIGN_C) | \ + BIT(DP_PIN_ASSIGN_E)) + +/* Pin assignments where one channel is for USB */ +#define DP_PIN_ASSIGN_MULTI_FUNC_MASK (BIT(DP_PIN_ASSIGN_B) | \ + BIT(DP_PIN_ASSIGN_D) | \ + BIT(DP_PIN_ASSIGN_F)) + +enum dp_state { + DP_STATE_IDLE, + DP_STATE_ENTER, + DP_STATE_UPDATE, + DP_STATE_CONFIGURE, + DP_STATE_EXIT, +}; + +struct dp_altmode { + struct typec_displayport_data data; + + enum dp_state state; + + struct mutex lock; /* device lock */ + struct work_struct work; + struct typec_altmode *alt; + const struct typec_altmode *port; +}; + +static int dp_altmode_notify(struct dp_altmode *dp) +{ + u8 state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf)); + + return typec_altmode_notify(dp->alt, TYPEC_MODAL_STATE(state), + &dp->data); +} + +static int dp_altmode_configure(struct dp_altmode *dp, u8 con) +{ + u32 conf = DP_CONF_SIGNALING_DP; /* Only DP signaling supported */ + u8 pin_assign = 0; + + switch (con) { + case DP_STATUS_CON_DISABLED: + return 0; + case DP_STATUS_CON_DFP_D: + conf |= DP_CONF_UFP_U_AS_DFP_D; + pin_assign = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) & + DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo); + break; + case DP_STATUS_CON_UFP_D: + case DP_STATUS_CON_BOTH: /* NOTE: First acting as DP source */ + conf |= DP_CONF_UFP_U_AS_UFP_D; + pin_assign = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo) & + DP_CAP_UFP_D_PIN_ASSIGN(dp->port->vdo); + break; + default: + break; + } + + /* Determining the initial pin assignment. */ + if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) { + /* Is USB together with DP preferred */ + if (dp->data.status & DP_STATUS_PREFER_MULTI_FUNC && + pin_assign & DP_PIN_ASSIGN_MULTI_FUNC_MASK) + pin_assign &= DP_PIN_ASSIGN_MULTI_FUNC_MASK; + else + pin_assign &= DP_PIN_ASSIGN_DP_ONLY_MASK; + + if (!pin_assign) + return -EINVAL; + + conf |= DP_CONF_SET_PIN_ASSIGN(pin_assign); + } + + dp->data.conf = conf; + + return 0; +} + +static int dp_altmode_status_update(struct dp_altmode *dp) +{ + bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf); + u8 con = DP_STATUS_CONNECTION(dp->data.status); + int ret = 0; + + if (configured && (dp->data.status & DP_STATUS_SWITCH_TO_USB)) { + dp->data.conf = 0; + dp->state = DP_STATE_CONFIGURE; + } else if (dp->data.status & DP_STATUS_EXIT_DP_MODE) { + dp->state = DP_STATE_EXIT; + } else if (!(con & DP_CONF_CURRENTLY(dp->data.conf))) { + ret = dp_altmode_configure(dp, con); + if (!ret) + dp->state = DP_STATE_CONFIGURE; + } + + return ret; +} + +static int dp_altmode_configured(struct dp_altmode *dp) +{ + int ret; + + sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration"); + + if (!dp->data.conf) + return typec_altmode_notify(dp->alt, TYPEC_STATE_USB, + &dp->data); + + ret = dp_altmode_notify(dp); + if (ret) + return ret; + + sysfs_notify(&dp->alt->dev.kobj, "displayport", "pin_assignment"); + + return 0; +} + +static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf) +{ + u32 header = DP_HEADER(DP_CMD_CONFIGURE); + int ret; + + ret = typec_altmode_notify(dp->alt, TYPEC_STATE_SAFE, &dp->data); + if (ret) { + dev_err(&dp->alt->dev, + "unable to put to connector to safe mode\n"); + return ret; + } + + ret = typec_altmode_vdm(dp->alt, header, &conf, 2); + if (ret) { + if (DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) + dp_altmode_notify(dp); + else + typec_altmode_notify(dp->alt, TYPEC_STATE_USB, + &dp->data); + } + + return ret; +} + +static void dp_altmode_work(struct work_struct *work) +{ + struct dp_altmode *dp = container_of(work, struct dp_altmode, work); + u32 header; + u32 vdo; + int ret; + + mutex_lock(&dp->lock); + + switch (dp->state) { + case DP_STATE_ENTER: + ret = typec_altmode_enter(dp->alt); + if (ret) + dev_err(&dp->alt->dev, "failed to enter mode\n"); + break; + case DP_STATE_UPDATE: + header = DP_HEADER(DP_CMD_STATUS_UPDATE); + vdo = 1; + ret = typec_altmode_vdm(dp->alt, header, &vdo, 2); + if (ret) + dev_err(&dp->alt->dev, + "unable to send Status Update command (%d)\n", + ret); + break; + case DP_STATE_CONFIGURE: + ret = dp_altmode_configure_vdm(dp, dp->data.conf); + if (ret) + dev_err(&dp->alt->dev, + "unable to send Configure command (%d)\n", ret); + break; + case DP_STATE_EXIT: + if (typec_altmode_exit(dp->alt)) + dev_err(&dp->alt->dev, "Exit Mode Failed!\n"); + break; + default: + break; + } + + dp->state = DP_STATE_IDLE; + + mutex_unlock(&dp->lock); +} + +static void dp_altmode_attention(struct typec_altmode *alt, const u32 vdo) +{ + struct dp_altmode *dp = typec_altmode_get_drvdata(alt); + u8 old_state; + + mutex_lock(&dp->lock); + + old_state = dp->state; + dp->data.status = vdo; + + if (old_state != DP_STATE_IDLE) + dev_warn(&alt->dev, "ATTENTION while processing state %d\n", + old_state); + + if (dp_altmode_status_update(dp)) + dev_warn(&alt->dev, "%s: status update failed\n", __func__); + + if (dp_altmode_notify(dp)) + dev_err(&alt->dev, "%s: notification failed\n", __func__); + + if (old_state == DP_STATE_IDLE && dp->state != DP_STATE_IDLE) + schedule_work(&dp->work); + + mutex_unlock(&dp->lock); +} + +static int dp_altmode_vdm(struct typec_altmode *alt, + const u32 hdr, const u32 *vdo, int count) +{ + struct dp_altmode *dp = typec_altmode_get_drvdata(alt); + int cmd_type = PD_VDO_CMDT(hdr); + int cmd = PD_VDO_CMD(hdr); + int ret = 0; + + mutex_lock(&dp->lock); + + if (dp->state != DP_STATE_IDLE) { + ret = -EBUSY; + goto err_unlock; + } + + switch (cmd_type) { + case CMDT_RSP_ACK: + switch (cmd) { + case CMD_ENTER_MODE: + dp->state = DP_STATE_UPDATE; + break; + case CMD_EXIT_MODE: + dp->data.status = 0; + dp->data.conf = 0; + break; + case DP_CMD_STATUS_UPDATE: + dp->data.status = *vdo; + ret = dp_altmode_status_update(dp); + break; + case DP_CMD_CONFIGURE: + ret = dp_altmode_configured(dp); + break; + default: + break; + } + break; + case CMDT_RSP_NAK: + switch (cmd) { + case DP_CMD_CONFIGURE: + dp->data.conf = 0; + ret = dp_altmode_configured(dp); + break; + default: + break; + } + break; + default: + break; + } + + if (dp->state != DP_STATE_IDLE) + schedule_work(&dp->work); + +err_unlock: + mutex_unlock(&dp->lock); + return ret; +} + +static int dp_altmode_activate(struct typec_altmode *alt, int activate) +{ + return activate ? typec_altmode_enter(alt) : typec_altmode_exit(alt); +} + +static const struct typec_altmode_ops dp_altmode_ops = { + .attention = dp_altmode_attention, + .vdm = dp_altmode_vdm, + .activate = dp_altmode_activate, +}; + +static const char * const configurations[] = { + [DP_CONF_USB] = "USB", + [DP_CONF_DFP_D] = "source", + [DP_CONF_UFP_D] = "sink", +}; + +static ssize_t +configuration_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dp_altmode *dp = dev_get_drvdata(dev); + u32 conf; + u32 cap; + int con; + int ret; + + con = sysfs_match_string(configurations, buf); + if (con < 0) + return con; + + mutex_lock(&dp->lock); + + if (dp->state != DP_STATE_IDLE) { + ret = -EBUSY; + goto err_unlock; + } + + cap = DP_CAP_CAPABILITY(dp->alt->vdo); + + if ((con == DP_CONF_DFP_D && !(cap & DP_CAP_DFP_D)) || + (con == DP_CONF_UFP_D && !(cap & DP_CAP_UFP_D))) + return -EINVAL; + + conf = dp->data.conf & ~DP_CONF_DUAL_D; + conf |= con; + + if (dp->alt->active) { + ret = dp_altmode_configure_vdm(dp, conf); + if (ret) + goto err_unlock; + } + + dp->data.conf = conf; + +err_unlock: + mutex_unlock(&dp->lock); + + return ret ? ret : size; +} + +static ssize_t configuration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dp_altmode *dp = dev_get_drvdata(dev); + int len; + u8 cap; + u8 cur; + int i; + + mutex_lock(&dp->lock); + + cap = DP_CAP_CAPABILITY(dp->alt->vdo); + cur = DP_CONF_CURRENTLY(dp->data.conf); + + len = sprintf(buf, "%s ", cur ? "USB" : "[USB]"); + + for (i = 1; i < ARRAY_SIZE(configurations); i++) { + if (i == cur) + len += sprintf(buf + len, "[%s] ", configurations[i]); + else if ((i == DP_CONF_DFP_D && cap & DP_CAP_DFP_D) || + (i == DP_CONF_UFP_D && cap & DP_CAP_UFP_D)) + len += sprintf(buf + len, "%s ", configurations[i]); + } + + mutex_unlock(&dp->lock); + + buf[len - 1] = '\n'; + return len; +} +static DEVICE_ATTR_RW(configuration); + +static const char * const pin_assignments[] = { + [DP_PIN_ASSIGN_A] = "A", + [DP_PIN_ASSIGN_B] = "B", + [DP_PIN_ASSIGN_C] = "C", + [DP_PIN_ASSIGN_D] = "D", + [DP_PIN_ASSIGN_E] = "E", + [DP_PIN_ASSIGN_F] = "F", +}; + +static ssize_t +pin_assignment_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dp_altmode *dp = dev_get_drvdata(dev); + u8 assignments; + u32 conf; + int ret; + + ret = sysfs_match_string(pin_assignments, buf); + if (ret < 0) + return ret; + + conf = DP_CONF_SET_PIN_ASSIGN(BIT(ret)); + ret = 0; + + mutex_lock(&dp->lock); + + if (conf & dp->data.conf) + goto out_unlock; + + if (dp->state != DP_STATE_IDLE) { + ret = -EBUSY; + goto out_unlock; + } + + if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_DFP_D) + assignments = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo); + else + assignments = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo); + + if (!(DP_CONF_GET_PIN_ASSIGN(conf) & assignments)) { + ret = -EINVAL; + goto out_unlock; + } + + conf |= dp->data.conf & ~DP_CONF_PIN_ASSIGNEMENT_MASK; + + /* Only send Configure command if a configuration has been set */ + if (dp->alt->active && DP_CONF_CURRENTLY(dp->data.conf)) { + ret = dp_altmode_configure_vdm(dp, conf); + if (ret) + goto out_unlock; + } + + dp->data.conf = conf; + +out_unlock: + mutex_unlock(&dp->lock); + + return ret ? ret : size; +} + +static ssize_t pin_assignment_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dp_altmode *dp = dev_get_drvdata(dev); + u8 assignments; + int len = 0; + u8 cur; + int i; + + mutex_lock(&dp->lock); + + cur = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf)); + + if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_DFP_D) + assignments = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo); + else + assignments = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo); + + for (i = 0; assignments; assignments >>= 1, i++) { + if (assignments & 1) { + if (i == cur) + len += sprintf(buf + len, "[%s] ", + pin_assignments[i]); + else + len += sprintf(buf + len, "%s ", + pin_assignments[i]); + } + } + + mutex_unlock(&dp->lock); + + buf[len - 1] = '\n'; + return len; +} +static DEVICE_ATTR_RW(pin_assignment); + +static struct attribute *dp_altmode_attrs[] = { + &dev_attr_configuration.attr, + &dev_attr_pin_assignment.attr, + NULL +}; + +static const struct attribute_group dp_altmode_group = { + .name = "displayport", + .attrs = dp_altmode_attrs, +}; + +static int dp_altmode_probe(struct typec_altmode *alt) +{ + const struct typec_altmode *port = typec_altmode_get_partner(alt); + struct dp_altmode *dp; + int ret; + + /* FIXME: Port can only be DFP_U. */ + + /* Make sure we have compatiple pin configurations */ + if (!(DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) & + DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo)) && + !(DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) & + DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo))) + return -ENODEV; + + ret = sysfs_create_group(&alt->dev.kobj, &dp_altmode_group); + if (ret) + return ret; + + dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL); + if (!dp) + return -ENOMEM; + + INIT_WORK(&dp->work, dp_altmode_work); + mutex_init(&dp->lock); + dp->port = port; + dp->alt = alt; + + alt->desc = "DisplayPort"; + alt->ops = &dp_altmode_ops; + + typec_altmode_set_drvdata(alt, dp); + + dp->state = DP_STATE_ENTER; + schedule_work(&dp->work); + + return 0; +} + +static void dp_altmode_remove(struct typec_altmode *alt) +{ + struct dp_altmode *dp = typec_altmode_get_drvdata(alt); + + sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group); + cancel_work_sync(&dp->work); +} + +static const struct typec_device_id dp_typec_id[] = { + { USB_TYPEC_DP_SID, USB_TYPEC_DP_MODE }, + { }, +}; +MODULE_DEVICE_TABLE(typec, dp_typec_id); + +static struct typec_altmode_driver dp_altmode_driver = { + .id_table = dp_typec_id, + .probe = dp_altmode_probe, + .remove = dp_altmode_remove, + .driver = { + .name = "typec_displayport", + .owner = THIS_MODULE, + }, +}; +module_typec_altmode_driver(dp_altmode_driver); + +MODULE_AUTHOR("Heikki Krogerus "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DisplayPort Alternate Mode"); diff --git a/include/linux/usb/typec_dp.h b/include/linux/usb/typec_dp.h new file mode 100644 index 000000000000..55ae781d60a9 --- /dev/null +++ b/include/linux/usb/typec_dp.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __USB_TYPEC_DP_H +#define __USB_TYPEC_DP_H + +#include + +#define USB_TYPEC_DP_SID 0xff01 +#define USB_TYPEC_DP_MODE 1 + +/* + * Connector states matching the pin assignments in DisplayPort Alt Mode + * Specification. + * + * These values are meant primarily to be used by the mux drivers, but they are + * also used as the "value" part in the alternate mode notification chain, so + * receivers of those notifications will always see them. + * + * Note. DisplayPort USB Type-C Alt Mode Specification version 1.0b deprecated + * pin assignments A, B and F, but they are still defined here for legacy + * purposes. + */ +enum { + TYPEC_DP_STATE_A = TYPEC_STATE_MODAL, /* Not supported after v1.0b */ + TYPEC_DP_STATE_B, /* Not supported after v1.0b */ + TYPEC_DP_STATE_C, + TYPEC_DP_STATE_D, + TYPEC_DP_STATE_E, + TYPEC_DP_STATE_F, /* Not supported after v1.0b */ +}; + +/* + * struct typec_displayport_data - DisplayPort Alt Mode specific data + * @status: Status Update command VDO content + * @conf: Configure command VDO content + * + * This structure is delivered as the data part with the notifications. It + * contains the VDOs from the two DisplayPort Type-C alternate mode specific + * commands: Status Update and Configure. + * + * @status will show for example the status of the HPD signal. + */ +struct typec_displayport_data { + u32 status; + u32 conf; +}; + +enum { + DP_PIN_ASSIGN_A, /* Not supported after v1.0b */ + DP_PIN_ASSIGN_B, /* Not supported after v1.0b */ + DP_PIN_ASSIGN_C, + DP_PIN_ASSIGN_D, + DP_PIN_ASSIGN_E, + DP_PIN_ASSIGN_F, /* Not supported after v1.0b */ +}; + +/* DisplayPort alt mode specific commands */ +#define DP_CMD_STATUS_UPDATE VDO_CMD_VENDOR(0) +#define DP_CMD_CONFIGURE VDO_CMD_VENDOR(1) + +/* DisplayPort Capabilities VDO bits (returned with Discover Modes) */ +#define DP_CAP_CAPABILITY(_cap_) ((_cap_) & 3) +#define DP_CAP_UFP_D 1 +#define DP_CAP_DFP_D 2 +#define DP_CAP_DFP_D_AND_UFP_D 3 +#define DP_CAP_DP_SIGNALING BIT(2) /* Always set */ +#define DP_CAP_GEN2 BIT(3) /* Reserved after v1.0b */ +#define DP_CAP_RECEPTACLE BIT(6) +#define DP_CAP_USB BIT(7) +#define DP_CAP_DFP_D_PIN_ASSIGN(_cap_) (((_cap_) & GENMASK(15, 8)) >> 8) +#define DP_CAP_UFP_D_PIN_ASSIGN(_cap_) (((_cap_) & GENMASK(23, 16)) >> 16) + +/* DisplayPort Status Update VDO bits */ +#define DP_STATUS_CONNECTION(_status_) ((_status_) & 3) +#define DP_STATUS_CON_DISABLED 0 +#define DP_STATUS_CON_DFP_D 1 +#define DP_STATUS_CON_UFP_D 2 +#define DP_STATUS_CON_BOTH 3 +#define DP_STATUS_POWER_LOW BIT(2) +#define DP_STATUS_ENABLED BIT(3) +#define DP_STATUS_PREFER_MULTI_FUNC BIT(4) +#define DP_STATUS_SWITCH_TO_USB BIT(5) +#define DP_STATUS_EXIT_DP_MODE BIT(6) +#define DP_STATUS_HPD_STATE BIT(7) /* 0 = HPD_Low, 1 = HPD_High */ +#define DP_STATUS_IRQ_HPD BIT(8) + +/* DisplayPort Configurations VDO bits */ +#define DP_CONF_CURRENTLY(_conf_) ((_conf_) & 3) +#define DP_CONF_UFP_U_AS_DFP_D BIT(0) +#define DP_CONF_UFP_U_AS_UFP_D BIT(1) +#define DP_CONF_SIGNALING_DP BIT(2) +#define DP_CONF_SIGNALING_GEN_2 BIT(3) /* Reserved after v1.0b */ +#define DP_CONF_PIN_ASSIGNEMENT_SHIFT 8 +#define DP_CONF_PIN_ASSIGNEMENT_MASK GENMASK(15, 8) + +#endif /* __USB_TYPEC_DP_H */ -- cgit v1.2.3 From 49cbb33dfdeb7651b91c2316a61b644d8e6cfe22 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 27 Jun 2018 18:19:52 +0300 Subject: usb: typec: pi3usb30532: Start using generic state values Instead of the tcpm specific mux states, using the generic USB Type-C connector state values, and with DisplayPort using connector states defined for the DisplayPort Alt Mode. Signed-off-by: Heikki Krogerus Tested-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/pi3usb30532.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c index b0e88db60ecf..64eb5983e17a 100644 --- a/drivers/usb/typec/mux/pi3usb30532.c +++ b/drivers/usb/typec/mux/pi3usb30532.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #define PI3USB30532_CONF 0x00 @@ -83,21 +83,24 @@ static int pi3usb30532_mux_set(struct typec_mux *mux, int state) new_conf = pi->conf; switch (state) { - case TYPEC_MUX_NONE: + case TYPEC_STATE_SAFE: new_conf = PI3USB30532_CONF_OPEN; break; - case TYPEC_MUX_USB: + case TYPEC_STATE_USB: new_conf = (new_conf & PI3USB30532_CONF_SWAP) | PI3USB30532_CONF_USB3; break; - case TYPEC_MUX_DP: + case TYPEC_DP_STATE_C: + case TYPEC_DP_STATE_E: new_conf = (new_conf & PI3USB30532_CONF_SWAP) | PI3USB30532_CONF_4LANE_DP; break; - case TYPEC_MUX_DOCK: + case TYPEC_DP_STATE_D: new_conf = (new_conf & PI3USB30532_CONF_SWAP) | PI3USB30532_CONF_USB3_AND_2LANE_DP; break; + default: + break; } ret = pi3usb30532_set_conf(pi, new_conf); -- cgit v1.2.3 From e9576fe8e605c4413beb91b290b8a473985710de Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 27 Jun 2018 18:19:53 +0300 Subject: usb: typec: tcpm: Support for Alternate Modes This adds more complete handling of VDMs and registration of partner alternate modes, and introduces callbacks for alternate mode operations. Only DFP role is supported for now. Signed-off-by: Heikki Krogerus Tested-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm.c | 165 ++++++++++++++++++++++++++++++++++++++--------- include/linux/usb/tcpm.h | 9 --- 2 files changed, 135 insertions(+), 39 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index 6b57e7132e64..c732fd703961 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #define FOREACH_STATE(S) \ @@ -169,13 +169,14 @@ enum pd_msg_request { /* Alternate mode support */ #define SVID_DISCOVERY_MAX 16 +#define ALTMODE_DISCOVERY_MAX (SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX) struct pd_mode_data { int svid_index; /* current SVID index */ int nsvids; u16 svids[SVID_DISCOVERY_MAX]; int altmodes; /* number of alternate modes */ - struct typec_altmode_desc altmode_desc[SVID_DISCOVERY_MAX]; + struct typec_altmode_desc altmode_desc[ALTMODE_DISCOVERY_MAX]; }; struct pd_pps_data { @@ -310,8 +311,8 @@ struct tcpm_port { /* Alternate mode data */ struct pd_mode_data mode_data; - struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX * 6]; - struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX * 6]; + struct typec_altmode *partner_altmode[ALTMODE_DISCOVERY_MAX]; + struct typec_altmode *port_altmode[ALTMODE_DISCOVERY_MAX]; /* Deadline in jiffies to exit src_try_wait state */ unsigned long max_wait; @@ -641,14 +642,14 @@ void tcpm_pd_transmit_complete(struct tcpm_port *port, } EXPORT_SYMBOL_GPL(tcpm_pd_transmit_complete); -static int tcpm_mux_set(struct tcpm_port *port, enum tcpc_mux_mode mode, +static int tcpm_mux_set(struct tcpm_port *port, int state, enum usb_role usb_role, enum typec_orientation orientation) { int ret; - tcpm_log(port, "Requesting mux mode %d, usb-role %d, orientation %d", - mode, usb_role, orientation); + tcpm_log(port, "Requesting mux state %d, usb-role %d, orientation %d", + state, usb_role, orientation); ret = typec_set_orientation(port->typec_port, orientation); if (ret) @@ -660,7 +661,7 @@ static int tcpm_mux_set(struct tcpm_port *port, enum tcpc_mux_mode mode, return ret; } - return typec_set_mode(port->typec_port, mode); + return typec_set_mode(port->typec_port, state); } static int tcpm_set_polarity(struct tcpm_port *port, @@ -787,7 +788,7 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached, else usb_role = USB_ROLE_DEVICE; - ret = tcpm_mux_set(port, TYPEC_MUX_USB, usb_role, orientation); + ret = tcpm_mux_set(port, TYPEC_STATE_USB, usb_role, orientation); if (ret < 0) return ret; @@ -1014,36 +1015,57 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload, pmdata->altmodes, paltmode->svid, paltmode->mode, paltmode->vdo); - port->partner_altmode[pmdata->altmodes] = - typec_partner_register_altmode(port->partner, paltmode); - if (!port->partner_altmode[pmdata->altmodes]) { - tcpm_log(port, - "Failed to register modes for SVID 0x%04x", - paltmode->svid); - return; - } pmdata->altmodes++; } } +static void tcpm_register_partner_altmodes(struct tcpm_port *port) +{ + struct pd_mode_data *modep = &port->mode_data; + struct typec_altmode *altmode; + int i; + + for (i = 0; i < modep->altmodes; i++) { + altmode = typec_partner_register_altmode(port->partner, + &modep->altmode_desc[i]); + if (!altmode) + tcpm_log(port, "Failed to register partner SVID 0x%04x", + modep->altmode_desc[i].svid); + port->partner_altmode[i] = altmode; + } +} + #define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header) static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, u32 *response) { - u32 p0 = le32_to_cpu(payload[0]); - int cmd_type = PD_VDO_CMDT(p0); - int cmd = PD_VDO_CMD(p0); + struct typec_altmode *adev; + struct typec_altmode *pdev; struct pd_mode_data *modep; + u32 p[PD_MAX_PAYLOAD]; int rlen = 0; - u16 svid; + int cmd_type; + int cmd; int i; + for (i = 0; i < cnt; i++) + p[i] = le32_to_cpu(payload[i]); + + cmd_type = PD_VDO_CMDT(p[0]); + cmd = PD_VDO_CMD(p[0]); + tcpm_log(port, "Rx VDM cmd 0x%x type %d cmd %d len %d", - p0, cmd_type, cmd, cnt); + p[0], cmd_type, cmd, cnt); modep = &port->mode_data; + adev = typec_match_altmode(port->port_altmode, ALTMODE_DISCOVERY_MAX, + PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0])); + + pdev = typec_match_altmode(port->partner_altmode, ALTMODE_DISCOVERY_MAX, + PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0])); + switch (cmd_type) { case CMDT_INIT: switch (cmd) { @@ -1065,17 +1087,19 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, case CMD_EXIT_MODE: break; case CMD_ATTENTION: - break; + /* Attention command does not have response */ + typec_altmode_attention(adev, p[1]); + return 0; default: break; } if (rlen >= 1) { - response[0] = p0 | VDO_CMDT(CMDT_RSP_ACK); + response[0] = p[0] | VDO_CMDT(CMDT_RSP_ACK); } else if (rlen == 0) { - response[0] = p0 | VDO_CMDT(CMDT_RSP_NAK); + response[0] = p[0] | VDO_CMDT(CMDT_RSP_NAK); rlen = 1; } else { - response[0] = p0 | VDO_CMDT(CMDT_RSP_BUSY); + response[0] = p[0] | VDO_CMDT(CMDT_RSP_BUSY); rlen = 1; } break; @@ -1108,14 +1132,39 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, svdm_consume_modes(port, payload, cnt); modep->svid_index++; if (modep->svid_index < modep->nsvids) { - svid = modep->svids[modep->svid_index]; + u16 svid = modep->svids[modep->svid_index]; response[0] = VDO(svid, 1, CMD_DISCOVER_MODES); rlen = 1; } else { - /* enter alternate mode if/when implemented */ + tcpm_register_partner_altmodes(port); } break; case CMD_ENTER_MODE: + typec_altmode_update_active(pdev, true); + + if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) { + response[0] = VDO(adev->svid, 1, CMD_EXIT_MODE); + response[0] |= VDO_OPOS(adev->mode); + return 1; + } + return 0; + case CMD_EXIT_MODE: + typec_altmode_update_active(pdev, false); + + /* Back to USB Operation */ + WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, + NULL)); + break; + default: + break; + } + break; + case CMDT_RSP_NAK: + switch (cmd) { + case CMD_ENTER_MODE: + /* Back to USB Operation */ + WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, + NULL)); break; default: break; @@ -1125,6 +1174,9 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, break; } + /* Informing the alternate mode drivers about everything */ + typec_altmode_vdm(adev, p[0], &p[1], cnt); + return rlen; } @@ -1408,6 +1460,57 @@ static int tcpm_validate_caps(struct tcpm_port *port, const u32 *pdo, return 0; } +static int tcpm_altmode_enter(struct typec_altmode *altmode) +{ + struct tcpm_port *port = typec_altmode_get_drvdata(altmode); + u32 header; + + mutex_lock(&port->lock); + header = VDO(altmode->svid, 1, CMD_ENTER_MODE); + header |= VDO_OPOS(altmode->mode); + + tcpm_queue_vdm(port, header, NULL, 0); + mod_delayed_work(port->wq, &port->vdm_state_machine, 0); + mutex_unlock(&port->lock); + + return 0; +} + +static int tcpm_altmode_exit(struct typec_altmode *altmode) +{ + struct tcpm_port *port = typec_altmode_get_drvdata(altmode); + u32 header; + + mutex_lock(&port->lock); + header = VDO(altmode->svid, 1, CMD_EXIT_MODE); + header |= VDO_OPOS(altmode->mode); + + tcpm_queue_vdm(port, header, NULL, 0); + mod_delayed_work(port->wq, &port->vdm_state_machine, 0); + mutex_unlock(&port->lock); + + return 0; +} + +static int tcpm_altmode_vdm(struct typec_altmode *altmode, + u32 header, const u32 *data, int count) +{ + struct tcpm_port *port = typec_altmode_get_drvdata(altmode); + + mutex_lock(&port->lock); + tcpm_queue_vdm(port, header, data, count - 1); + mod_delayed_work(port->wq, &port->vdm_state_machine, 0); + mutex_unlock(&port->lock); + + return 0; +} + +static const struct typec_altmode_ops tcpm_altmode_ops = { + .enter = tcpm_altmode_enter, + .exit = tcpm_altmode_exit, + .vdm = tcpm_altmode_vdm, +}; + /* * PD (data, control) command handling functions */ @@ -2539,7 +2642,7 @@ out_disable_vconn: out_disable_pd: port->tcpc->set_pd_rx(port->tcpc, false); out_disable_mux: - tcpm_mux_set(port, TYPEC_MUX_NONE, USB_ROLE_NONE, + tcpm_mux_set(port, TYPEC_STATE_SAFE, USB_ROLE_NONE, TYPEC_ORIENTATION_NONE); return ret; } @@ -2585,7 +2688,7 @@ static void tcpm_reset_port(struct tcpm_port *port) tcpm_init_vconn(port); tcpm_set_current_limit(port, 0, 0); tcpm_set_polarity(port, TYPEC_POLARITY_CC1); - tcpm_mux_set(port, TYPEC_MUX_NONE, USB_ROLE_NONE, + tcpm_mux_set(port, TYPEC_STATE_SAFE, USB_ROLE_NONE, TYPEC_ORIENTATION_NONE); tcpm_set_attached_state(port, false); port->try_src_count = 0; @@ -4706,6 +4809,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) dev_name(dev), paltmode->svid); break; } + typec_altmode_set_drvdata(alt, port); + alt->ops = &tcpm_altmode_ops; port->port_altmode[i] = alt; i++; paltmode++; diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index 193920a2e05f..7e7fbfb84e8e 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -98,15 +98,6 @@ struct tcpc_config { #define TCPC_MUX_DP_ENABLED BIT(1) /* DP enabled */ #define TCPC_MUX_POLARITY_INVERTED BIT(2) /* Polarity inverted */ -/* Mux modes, decoded to attributes */ -enum tcpc_mux_mode { - TYPEC_MUX_NONE = 0, /* Open switch */ - TYPEC_MUX_USB = TCPC_MUX_USB_ENABLED, /* USB only */ - TYPEC_MUX_DP = TCPC_MUX_DP_ENABLED, /* DP only */ - TYPEC_MUX_DOCK = TCPC_MUX_USB_ENABLED | /* Both USB and DP */ - TCPC_MUX_DP_ENABLED, -}; - /** * struct tcpc_dev - Port configuration and callback functions * @config: Pointer to port configuration -- cgit v1.2.3 From d3ac5598c5010a8999978ebbcca3b1c6188ca36b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 1 Jul 2018 19:32:04 +0200 Subject: usb: wusbcore: security: cast sizeof to int for comparison Comparing an int to a size, which is unsigned, causes the int to become unsigned, giving the wrong result. usb_get_descriptor can return a negative error code. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ int x; expression e,e1; identifier f; @@ *x = f(...); ... when != x = e1 when != if (x < 0 || ...) { ... return ...; } *x < sizeof(e) // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/security.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c index 33d2f5d7f33b..14ac8c98ac9e 100644 --- a/drivers/usb/wusbcore/security.c +++ b/drivers/usb/wusbcore/security.c @@ -217,7 +217,7 @@ int wusb_dev_sec_add(struct wusbhc *wusbhc, result = usb_get_descriptor(usb_dev, USB_DT_SECURITY, 0, secd, sizeof(*secd)); - if (result < sizeof(*secd)) { + if (result < (int)sizeof(*secd)) { dev_err(dev, "Can't read security descriptor or " "not enough data: %d\n", result); goto out; -- cgit v1.2.3 From 169d3606dccfa3cfc5aa98b6e01705582074e1eb Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 28 Jun 2018 13:32:33 -0500 Subject: USB: musb: mark expected switch fall-throughs In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_dsps.c | 2 +- drivers/usb/musb/musb_host.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index fb871eabcc10..df827ff57b0d 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -237,7 +237,7 @@ static int dsps_check_status(struct musb *musb, void *unused) } musb_writeb(musb->mregs, MUSB_DEVCTL, 0); skip_session = 1; - /* fall */ + /* fall through */ case OTG_STATE_A_IDLE: case OTG_STATE_B_IDLE: diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 8000c7c02f79..b59ce9ad14ce 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -378,6 +378,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, qh = first_qh(head); break; } + /* else: fall through */ case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_INT: -- cgit v1.2.3 From 25b22e353c1f8d48e69fdef70a31be2f0d2bc410 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 28 Jun 2018 13:37:22 -0500 Subject: USB: mark expected switch fall-throughs In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/appledisplay.c | 1 + drivers/usb/renesas_usbhs/mod_gadget.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index b3eb8b989bd4..d746c26a8055 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -89,6 +89,7 @@ static void appledisplay_complete(struct urb *urb) dev_err(dev, "OVERFLOW with data length %d, actual length is %d\n", ACD_URB_BUFFER_LEN, pdata->urb->actual_length); + /* fall through */ case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 33d059c40616..59cac40aafcc 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -502,6 +502,7 @@ static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv, case READ_STATUS_STAGE: case WRITE_STATUS_STAGE: usbhs_dcp_control_transfer_done(pipe); + /* fall through */ default: return ret; } -- cgit v1.2.3 From 399111aaa7b9b4a2f7f20d38943ab55b6a67653e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 1 Jul 2018 14:27:20 +0200 Subject: USB: typec: fsusb302: Drop empty set_current_limit implementation The set_current_limit tcpm_dev callback is optional and the tcpm core will already log the passed in values, so there is no need to have an empty implementation of this. Signed-off-by: Hans de Goede Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/fusb302/fusb302.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/fusb302/fusb302.c b/drivers/usb/typec/fusb302/fusb302.c index 1e68da10bf17..82bed9810be6 100644 --- a/drivers/usb/typec/fusb302/fusb302.c +++ b/drivers/usb/typec/fusb302/fusb302.c @@ -864,17 +864,6 @@ done: return ret; } -static int tcpm_set_current_limit(struct tcpc_dev *dev, u32 max_ma, u32 mv) -{ - struct fusb302_chip *chip = container_of(dev, struct fusb302_chip, - tcpc_dev); - - fusb302_log(chip, "current limit: %d ma, %d mv (not implemented)", - max_ma, mv); - - return 0; -} - static int fusb302_pd_tx_flush(struct fusb302_chip *chip) { return fusb302_i2c_set_bits(chip, FUSB_REG_CONTROL0, @@ -1213,7 +1202,6 @@ static void init_tcpc_dev(struct tcpc_dev *fusb302_tcpc_dev) fusb302_tcpc_dev->set_polarity = tcpm_set_polarity; fusb302_tcpc_dev->set_vconn = tcpm_set_vconn; fusb302_tcpc_dev->set_vbus = tcpm_set_vbus; - fusb302_tcpc_dev->set_current_limit = tcpm_set_current_limit; fusb302_tcpc_dev->set_pd_rx = tcpm_set_pd_rx; fusb302_tcpc_dev->set_roles = tcpm_set_roles; fusb302_tcpc_dev->start_drp_toggling = tcpm_start_drp_toggling; -- cgit v1.2.3 From a420b5d939ee58f1d950f0ea782834056520aeaa Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Jul 2018 17:02:18 +0200 Subject: USB: serial: kobil_sct: fix modem-status error handling Make sure to return -EIO in case of a short modem-status read request. While at it, split the debug message to not include the (zeroed) transfer-buffer content in case of errors. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Johan Hovold --- drivers/usb/serial/kobil_sct.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index a31ea7e194dd..a6ebed1e0f20 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -393,12 +393,20 @@ static int kobil_tiocmget(struct tty_struct *tty) transfer_buffer_length, KOBIL_TIMEOUT); - dev_dbg(&port->dev, "%s - Send get_status_line_state URB returns: %i. Statusline: %02x\n", - __func__, result, transfer_buffer[0]); + dev_dbg(&port->dev, "Send get_status_line_state URB returns: %i\n", + result); + if (result < 1) { + if (result >= 0) + result = -EIO; + goto out_free; + } + + dev_dbg(&port->dev, "Statusline: %02x\n", transfer_buffer[0]); result = 0; if ((transfer_buffer[0] & SUSBCR_GSL_DSR) != 0) result = TIOCM_DSR; +out_free: kfree(transfer_buffer); return result; } -- cgit v1.2.3 From af846a6f6de27eabcefa81ea98b0c3bd21aa25c7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Jul 2018 17:02:19 +0200 Subject: USB: serial: kobil_sct: add missing version error handling Add missing version-request error handling and suppress printing of the (zeroed) transfer-buffer content in case of errors. Signed-off-by: Johan Hovold --- drivers/usb/serial/kobil_sct.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index a6ebed1e0f20..e9882ba20933 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -190,8 +190,10 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) KOBIL_TIMEOUT ); dev_dbg(dev, "%s - Send get_HW_version URB returns: %i\n", __func__, result); - dev_dbg(dev, "Hardware version: %i.%i.%i\n", transfer_buffer[0], - transfer_buffer[1], transfer_buffer[2]); + if (result >= 3) { + dev_dbg(dev, "Hardware version: %i.%i.%i\n", transfer_buffer[0], + transfer_buffer[1], transfer_buffer[2]); + } /* get firmware version */ result = usb_control_msg(port->serial->dev, @@ -205,8 +207,10 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) KOBIL_TIMEOUT ); dev_dbg(dev, "%s - Send get_FW_version URB returns: %i\n", __func__, result); - dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0], - transfer_buffer[1], transfer_buffer[2]); + if (result >= 3) { + dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0], + transfer_buffer[1], transfer_buffer[2]); + } if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { -- cgit v1.2.3 From 6f3fde684d0232e66ada3410f016a58e09a87689 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sun, 1 Jul 2018 17:35:44 +0200 Subject: usb: usbtest: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Cc: Greg Kroah-Hartman Acked-by: Alan Stern Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 9e1142b8b91b..c7f82310e73e 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1082,11 +1082,12 @@ static void ctrl_complete(struct urb *urb) struct usb_ctrlrequest *reqp; struct subcase *subcase; int status = urb->status; + unsigned long flags; reqp = (struct usb_ctrlrequest *)urb->setup_packet; subcase = container_of(reqp, struct subcase, setup); - spin_lock(&ctx->lock); + spin_lock_irqsave(&ctx->lock, flags); ctx->count--; ctx->pending--; @@ -1185,7 +1186,7 @@ error: /* signal completion when nothing's queued */ if (ctx->pending == 0) complete(&ctx->complete); - spin_unlock(&ctx->lock); + spin_unlock_irqrestore(&ctx->lock, flags); } static int @@ -1917,8 +1918,9 @@ struct transfer_context { static void complicated_callback(struct urb *urb) { struct transfer_context *ctx = urb->context; + unsigned long flags; - spin_lock(&ctx->lock); + spin_lock_irqsave(&ctx->lock, flags); ctx->count--; ctx->packet_count += urb->number_of_packets; @@ -1958,7 +1960,7 @@ static void complicated_callback(struct urb *urb) complete(&ctx->done); } done: - spin_unlock(&ctx->lock); + spin_unlock_irqrestore(&ctx->lock, flags); } static struct urb *iso_alloc_urb( -- cgit v1.2.3 From 9920184d78edeccd165832d54d6203fae4c860f5 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 4 Jul 2018 13:34:06 +0100 Subject: usb: typec: fix dereference before null check on adev Pointer adev is being dereferenced before it is being sanity checked with a null pointer check, hence it is possible for a null pointer dereference to occur. Fix this by dereferencing adev only once it is null checked. Detected by CoverityScan, CID#1471598 ("Dereference before null check") Fixes: 8a37d87d72f0 ("usb: typec: Bus type for alternate modes") Signed-off-by: Colin Ian King Acked-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/bus.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index 999d7904172a..95a2b10127db 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -51,7 +51,7 @@ static int typec_altmode_set_state(struct typec_altmode *adev, int state) int typec_altmode_notify(struct typec_altmode *adev, unsigned long conf, void *data) { - bool is_port = is_typec_port(adev->dev.parent); + bool is_port; struct altmode *altmode; struct altmode *partner; int ret; @@ -64,6 +64,7 @@ int typec_altmode_notify(struct typec_altmode *adev, if (!altmode->partner) return -ENODEV; + is_port = is_typec_port(adev->dev.parent); partner = altmode->partner; ret = typec_altmode_set_mux(is_port ? altmode : partner, (u8)conf); -- cgit v1.2.3 From 98a1a0c7a321a46e805df289c7d385a9b1b4c661 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 5 Jul 2018 14:36:47 +0100 Subject: usb: typec: unlock dp->lock on error exit path, and also zero ret if successful One of the error handling paths forgets to unlock dp->lock on the error exit path leading to a potential lock-up. Also the return path for a successful call to the function configuration_store can return an uninitialized error return code if dp->alt->active is false, so ensure ret is zeroed on the successful exit path to avoid garbage being returned. Detected by CoverityScan, CID#1471597 ("Unitialized scalar variable") Fixes: 0e3bb7d6894d ("usb: typec: Add driver for DisplayPort alternate mode") Signed-off-by: Colin Ian King Acked-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/altmodes/displayport.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index ef12b15bd484..3f06e94771a7 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -333,7 +333,7 @@ configuration_store(struct device *dev, struct device_attribute *attr, u32 conf; u32 cap; int con; - int ret; + int ret = 0; con = sysfs_match_string(configurations, buf); if (con < 0) @@ -349,8 +349,10 @@ configuration_store(struct device *dev, struct device_attribute *attr, cap = DP_CAP_CAPABILITY(dp->alt->vdo); if ((con == DP_CONF_DFP_D && !(cap & DP_CAP_DFP_D)) || - (con == DP_CONF_UFP_D && !(cap & DP_CAP_UFP_D))) - return -EINVAL; + (con == DP_CONF_UFP_D && !(cap & DP_CAP_UFP_D))) { + ret = -EINVAL; + goto err_unlock; + } conf = dp->data.conf & ~DP_CONF_DUAL_D; conf |= con; -- cgit v1.2.3 From 8160eac1213fb6d466dee156638647a71da7d7fb Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 6 Jul 2018 15:28:32 +0200 Subject: usb: typec: avoid format-overflow warning gcc-8 points out that the fix-byte buffer might be too small if desc->mode is a three-digit number: drivers/usb/typec/class.c: In function 'typec_register_altmode': drivers/usb/typec/class.c:502:32: error: '%d' directive writing between 1 and 3 bytes into a region of size 2 [-Werror=format-overflow=] sprintf(alt->group_name, "mode%d", desc->mode); ^~ drivers/usb/typec/class.c:502:27: note: directive argument in the range [0, 255] sprintf(alt->group_name, "mode%d", desc->mode); ^~~~~~~~ drivers/usb/typec/class.c:502:2: note: 'sprintf' output between 6 and 8 bytes into a destination of size 6 sprintf(alt->group_name, "mode%d", desc->mode); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ I assume this cannot happen in practice, but we can simply make the string long enough to avoid the warning. This uses the two padding bytes that already exist after the string. Fixes: 4ab8c18d4d67 ("usb: typec: Register a device for every mode") Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/bus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h index 62aaf8b56bde..db40e61d8b72 100644 --- a/drivers/usb/typec/bus.h +++ b/drivers/usb/typec/bus.h @@ -16,7 +16,7 @@ struct altmode { enum typec_port_data roles; struct attribute *attrs[5]; - char group_name[6]; + char group_name[8]; struct attribute_group group; const struct attribute_group *groups[2]; -- cgit v1.2.3 From fc72aa83b4a7c84cc032649380641fe5300a7c4c Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 3 Jul 2018 19:46:56 +0900 Subject: usb: host: xhci-plat: add firmware_name for R-Car Gen3 To clean up the xhci-rcar.c code later, this patch adds firmware_name "V3" for R-Car Gen3. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index c1b22fc64e38..8dc77e34a859 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -105,6 +105,7 @@ static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = { }; static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = { + .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V3, .init_quirk = xhci_rcar_init_quirk, .plat_start = xhci_rcar_start, .resume_quirk = xhci_rcar_resume_quirk, -- cgit v1.2.3 From ed8603e11124ea2f5626e29d09680afcac71a9bb Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 3 Jul 2018 19:46:57 +0900 Subject: usb: host: xhci-rcar: Simplify getting the firmware name for R-Car Gen3 This patch simplifies getting the firmware name for R-Car Gen3. Almost all R-Car Gen3 USB3.0 Host controllers use "V3". But, r8a7795 ES1.x SoCs only should use "V2". Since the xhci-plat already has the firmware_name of R-Car Gen3 as "V3", the xhci-rcar doesn't need some members of rcar_quirks_match. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-rcar.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index f33ffc2bc4ed..4a22f25ce970 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -17,9 +17,8 @@ #include "xhci-rcar.h" /* -* - The V3 firmware is for r8a7796 (with good performance) and r8a7795 es2.0 -* or later. -* - The V2 firmware can be used on both r8a7795 (es1.x) and r8a7796. +* - The V3 firmware is for almost all R-Car Gen3 (except r8a7795 ES1.x) +* - The V2 firmware is for r8a7795 ES1.x. * - The V2 firmware is possible to use on R-Car Gen2. However, the V2 causes * performance degradation. So, this driver continues to use the V1 if R-Car * Gen2. @@ -75,18 +74,6 @@ static const struct soc_device_attribute rcar_quirks_match[] = { .soc_id = "r8a7795", .revision = "ES1.*", .data = (void *)RCAR_XHCI_FIRMWARE_V2, }, - { - .soc_id = "r8a7795", - .data = (void *)RCAR_XHCI_FIRMWARE_V3, - }, - { - .soc_id = "r8a7796", - .data = (void *)RCAR_XHCI_FIRMWARE_V3, - }, - { - .soc_id = "r8a77965", - .data = (void *)RCAR_XHCI_FIRMWARE_V3, - }, { /* sentinel */ }, }; -- cgit v1.2.3 From 5f9810730cf4c0315f30e2fa0daf273fb2d3cadb Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 3 Jul 2018 19:46:58 +0900 Subject: usb: host: xhci-rcar: Add a condition check about PLL active This patch adds a condition check about the PLL acvice of this controller. Otherwise, the controller might cause hang when any USB clocks are not supplied yet and accesses the xHCI registers. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-rcar.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index 4a22f25ce970..a6e463715779 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -29,6 +29,7 @@ MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V2); MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V3); /*** Register Offset ***/ +#define RCAR_USB3_AXH_STA 0x104 /* AXI Host Control Status */ #define RCAR_USB3_INT_ENA 0x224 /* Interrupt Enable */ #define RCAR_USB3_DL_CTRL 0x250 /* FW Download Control & Status */ #define RCAR_USB3_FW_DATA0 0x258 /* FW Data0 */ @@ -41,6 +42,12 @@ MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V3); #define RCAR_USB3_TX_POL 0xab8 /* USB3.0 TX Polarity */ /*** Register Settings ***/ +/* AXI Host Control Status */ +#define RCAR_USB3_AXH_STA_B3_PLL_ACTIVE 0x00010000 +#define RCAR_USB3_AXH_STA_B2_PLL_ACTIVE 0x00000001 +#define RCAR_USB3_AXH_STA_PLL_ACTIVE_MASK (RCAR_USB3_AXH_STA_B3_PLL_ACTIVE | \ + RCAR_USB3_AXH_STA_B2_PLL_ACTIVE) + /* Interrupt Enable */ #define RCAR_USB3_INT_XHC_ENA 0x00000001 #define RCAR_USB3_INT_PME_ENA 0x00000002 @@ -200,6 +207,22 @@ static int xhci_rcar_download_firmware(struct usb_hcd *hcd) return retval; } +static bool xhci_rcar_wait_for_pll_active(struct usb_hcd *hcd) +{ + int timeout = 1000; + u32 val, mask = RCAR_USB3_AXH_STA_PLL_ACTIVE_MASK; + + while (timeout > 0) { + val = readl(hcd->regs + RCAR_USB3_AXH_STA); + if ((val & mask) == mask) + return true; + udelay(1); + timeout--; + } + + return false; +} + /* This function needs to initialize a "phy" of usb before */ int xhci_rcar_init_quirk(struct usb_hcd *hcd) { @@ -220,6 +243,9 @@ int xhci_rcar_init_quirk(struct usb_hcd *hcd) xhci_rcar_is_gen3(hcd->self.controller)) xhci->quirks |= XHCI_NO_64BIT_SUPPORT; + if (!xhci_rcar_wait_for_pll_active(hcd)) + return -ETIMEDOUT; + return xhci_rcar_download_firmware(hcd); } -- cgit v1.2.3 From d118851a4d1f2e67ef9442cd3caa35e555bb370e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 5 Jul 2018 12:38:32 +0200 Subject: USB: serial: kl5kusb105: remove KLSI device id This driver was apparently never tested with an actual KLSI device. In fact, even the device-id entry which was supposed to allow for this had a typo in it. Tests now reveal that the predicted firmware differences with the PalmConnect adapters are real and that the driver does not support KLSI devices with PID 0x000c, so let's remove the broken entry. Reported-by: Chris Jakob Signed-off-by: Johan Hovold --- drivers/usb/serial/kl5kusb105.c | 1 - drivers/usb/serial/kl5kusb105.h | 3 --- 2 files changed, 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 5046ffd53cde..5ee48b0650c4 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -67,7 +67,6 @@ static int klsi_105_prepare_write_buffer(struct usb_serial_port *port, */ static const struct usb_device_id id_table[] = { { USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) }, - { USB_DEVICE(KLSI_VID, KLSI_KL5KUSB105D_PID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/kl5kusb105.h b/drivers/usb/serial/kl5kusb105.h index 41c9bf60fbf0..dbe98d85ca8e 100644 --- a/drivers/usb/serial/kl5kusb105.h +++ b/drivers/usb/serial/kl5kusb105.h @@ -7,9 +7,6 @@ #define PALMCONNECT_VID 0x0830 #define PALMCONNECT_PID 0x0080 -#define KLSI_VID 0x05e9 -#define KLSI_KL5KUSB105D_PID 0x00c0 - /* Vendor commands: */ -- cgit v1.2.3 From a7d8205eaee70b55b0fe45fbd77c11e1ad3d7a7e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 13 Jul 2018 11:45:01 +0100 Subject: usb: usbip: remove redundant pointer ep Pointer ep is being assigned but is never used hence it is redundant and can be removed. Cleans up clang warning: warning: variable 'ep' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vudc_dev.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index 1b9a4f87db59..1634d8698e15 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -279,12 +279,10 @@ static int vep_disable(struct usb_ep *_ep) static struct usb_request *vep_alloc_request(struct usb_ep *_ep, gfp_t mem_flags) { - struct vep *ep; struct vrequest *req; if (!_ep) return NULL; - ep = to_vep(_ep); req = kzalloc(sizeof(*req), mem_flags); if (!req) -- cgit v1.2.3 From c588f1a46aa57f58148239d74bb6aec898e214c9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 13 Jul 2018 10:58:44 +0100 Subject: usb: misc: uss720: remove redundant pointer usbdev Pointer usbdev is being assigned but is never used hence it is redundant and can be removed. Cleans up clang warning: warning: variable 'usbdev' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/uss720.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index de9a502491c2..82f220631bd7 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -748,13 +748,11 @@ static void uss720_disconnect(struct usb_interface *intf) { struct parport *pp = usb_get_intfdata(intf); struct parport_uss720_private *priv; - struct usb_device *usbdev; dev_dbg(&intf->dev, "disconnect\n"); usb_set_intfdata(intf, NULL); if (pp) { priv = pp->private_data; - usbdev = priv->usbdev; priv->usbdev = NULL; priv->pp = NULL; dev_dbg(&intf->dev, "parport_remove_port\n"); -- cgit v1.2.3 From 795a8075e9a53f441229c3890f702bf96508406b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 13 Jul 2018 10:55:30 +0100 Subject: usb-misc: sisusbvga: remove redundant variable modey Variable modey is being assigned but is never used hence it is redundant and can be removed. Cleans up clang warning: warning: variable 'modey' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/sisusbvga/sisusb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index f92c5df26320..3198d0477cf8 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -1750,7 +1750,7 @@ static int sisusb_setup_screen(struct sisusb_usb_data *sisusb, static int sisusb_set_default_mode(struct sisusb_usb_data *sisusb, int touchengines) { - int ret = 0, i, j, modex, modey, bpp, du; + int ret = 0, i, j, modex, bpp, du; u8 sr31, cr63, tmp8; static const char attrdata[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -1773,7 +1773,7 @@ static int sisusb_set_default_mode(struct sisusb_usb_data *sisusb, 0x00 }; - modex = 640; modey = 480; bpp = 2; + modex = 640; bpp = 2; GETIREG(SISSR, 0x31, &sr31); GETIREG(SISCR, 0x63, &cr63); -- cgit v1.2.3 From 260560322616472e3e26ece9e9bd044b19031c6c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 13 Jul 2018 10:51:51 +0100 Subject: usb: isp1760: remove redundant variable 'selector' Variable 'selector' is being assigned but is never used hence it is redundant and can be removed. Cleans up clang warning: warning: variable 'selector' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/usb/isp1760/isp1760-hcd.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c index 1045521be293..8142c6b4c4cf 100644 --- a/drivers/usb/isp1760/isp1760-hcd.c +++ b/drivers/usb/isp1760/isp1760-hcd.c @@ -1817,7 +1817,6 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq, u32 temp, status; unsigned long flags; int retval = 0; - unsigned selector; /* * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. @@ -2010,7 +2009,6 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq, } break; case SetPortFeature: - selector = wIndex >> 8; wIndex &= 0xff; if (!wIndex || wIndex > ports) goto error; -- cgit v1.2.3 From 6c7dbe36198e23099209cb2938f9675849fdde68 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 13 Jul 2018 10:48:07 +0100 Subject: usb: xhci: dbc: remove redundant pointer dbc Pointer dbc is being assigned but is never used hence it is redundant and can be removed. Cleans up clang warning: variable 'dbc' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-dbgcap.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 1fbfd89d0a0f..73d5a0e2a225 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -909,11 +909,9 @@ static ssize_t dbc_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct xhci_dbc *dbc; struct xhci_hcd *xhci; xhci = hcd_to_xhci(dev_get_drvdata(dev)); - dbc = xhci->dbc; if (!strncmp(buf, "enable", 6)) xhci_dbc_start(xhci); -- cgit v1.2.3 From bebee48e7fd6e917b1625487342bfe3b3f5c1c56 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 13 Jul 2018 10:43:01 +0100 Subject: USB: host: whci: remove redundant variable t Variable t is being assigned but is never used hence it is redundant and can be removed. Cleans up clang warning: warning: variable 't' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/whci/pzl.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c index bb84366f7bd3..ef52aeb02fde 100644 --- a/drivers/usb/host/whci/pzl.c +++ b/drivers/usb/host/whci/pzl.c @@ -96,9 +96,7 @@ static enum whc_update pzl_process_qset(struct whc *whc, struct whc_qset *qset) while (qset->ntds) { struct whc_qtd *td; - int t; - t = qset->td_start; td = &qset->qtd[qset->td_start]; status = le32_to_cpu(td->status); -- cgit v1.2.3 From a99e72095c957d79edeff23c59c92b6320d186ba Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 13 Jul 2018 10:36:43 +0100 Subject: usb: host: u132-hcd: remove redundant variable num_ports Variable num_ports is being assigned but is never used hence it is redundant and can be removed. Cleans up clang warning: warning: variable 'num_ports' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/u132-hcd.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 032b8652910a..072bd5d5738e 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -3062,7 +3062,6 @@ static int u132_probe(struct platform_device *pdev) int retval; u32 control; u32 rh_a = -1; - u32 num_ports; msleep(100); if (u132_exiting > 0) @@ -3077,7 +3076,6 @@ static int u132_probe(struct platform_device *pdev) retval = ftdi_read_pcimem(pdev, roothub.a, &rh_a); if (retval) return retval; - num_ports = rh_a & RH_A_NDP; /* refuse to confuse usbcore */ if (pdev->dev.dma_mask) return -EINVAL; -- cgit v1.2.3 From ec81419310f56524d7428a73ef813be6cd3761b9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 13 Jul 2018 10:29:56 +0100 Subject: usb: host: ehci-sched: remove redundant pointer dev Pointer dev is being assigned but is never used hence it is redundant and can be removed. Cleans up clang warning: warning: variable 'dev' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sched.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 1d87295682b8..da7b00a6110b 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1835,7 +1835,6 @@ static bool itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd) unsigned uframe; int urb_index = -1; struct ehci_iso_stream *stream = itd->stream; - struct usb_device *dev; bool retval = false; /* for each uframe with a packet */ @@ -1886,7 +1885,6 @@ static bool itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd) */ /* give urb back to the driver; completion often (re)submits */ - dev = urb->dev; ehci_urb_done(ehci, urb, 0); retval = true; urb = NULL; @@ -2230,7 +2228,6 @@ static bool sitd_complete(struct ehci_hcd *ehci, struct ehci_sitd *sitd) u32 t; int urb_index; struct ehci_iso_stream *stream = sitd->stream; - struct usb_device *dev; bool retval = false; urb_index = sitd->index; @@ -2268,7 +2265,6 @@ static bool sitd_complete(struct ehci_hcd *ehci, struct ehci_sitd *sitd) */ /* give urb back to the driver; completion often (re)submits */ - dev = urb->dev; ehci_urb_done(ehci, urb, 0); retval = true; urb = NULL; -- cgit v1.2.3 From db9fc500e85a5292872c4b7c7f6a8230be6e016a Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 13 Jul 2018 10:24:33 +0100 Subject: usb: dwc3: gadget: remove redundant variable maxpacket Variable maxpacket is being assigned but is never used hence it is redundant and can be removed. Cleans up clang warning: warning: variable 'maxpacket' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/ep0.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index c77ff50a88a2..8efde178eef4 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -973,15 +973,12 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, ret = dwc3_ep0_start_trans(dep); } else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) && req->request.length && req->request.zero) { - u32 maxpacket; ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request, dep->number); if (ret) return; - maxpacket = dep->endpoint.maxpacket; - /* prepare normal TRB */ dwc3_ep0_prepare_one_trb(dep, req->request.dma, req->request.length, -- cgit v1.2.3 From 1e2ae1d7e43657f6d1a08a7e52ef85ed735cc538 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 13 Jul 2018 11:08:31 +0100 Subject: USB: serial: mos7720: remove redundant variables iflag, mask and serial Variables iflag, mask and serial are being assigned but are never used hence are redundant and can be removed. Cleans up clang warnings: warning: variable 'iflag' set but not used [-Wunused-but-set-variable] warning: variable 'mask' set but not used [-Wunused-but-set-variable] warning: variable 'serial' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7720.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 8f11e759ad61..27109522fd8b 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1527,8 +1527,6 @@ static void change_port_settings(struct tty_struct *tty, struct usb_serial *serial; int baud; unsigned cflag; - unsigned iflag; - __u8 mask = 0xff; __u8 lData; __u8 lParity; __u8 lStop; @@ -1552,23 +1550,19 @@ static void change_port_settings(struct tty_struct *tty, lParity = 0x00; /* No parity */ cflag = tty->termios.c_cflag; - iflag = tty->termios.c_iflag; /* Change the number of bits */ switch (cflag & CSIZE) { case CS5: lData = UART_LCR_WLEN5; - mask = 0x1f; break; case CS6: lData = UART_LCR_WLEN6; - mask = 0x3f; break; case CS7: lData = UART_LCR_WLEN7; - mask = 0x7f; break; default: case CS8: @@ -1686,11 +1680,8 @@ static void mos7720_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { int status; - struct usb_serial *serial; struct moschip_port *mos7720_port; - serial = port->serial; - mos7720_port = usb_get_serial_port_data(port); if (mos7720_port == NULL) -- cgit v1.2.3 From b8f6515445c170016014154f2e84cc863b4cc7a4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 16 Jul 2018 13:51:55 +0200 Subject: USB: serial: iuu_phoenix: drop unused driver-data baud rate Drop unused driver-data baud rate. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/iuu_phoenix.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 2fb71303ec3a..87c8dd064205 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -58,7 +58,6 @@ struct iuu_private { u8 *buf; /* used for initialize speed */ u8 len; int vcc; /* vcc (either 3 or 5 V) */ - u32 baud; u32 boost; u32 clk; }; @@ -991,7 +990,6 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) if (boost < 100) boost = 100; priv->boost = boost; - priv->baud = baud; switch (clockmode) { case 2: /* 3.680 Mhz */ priv->clk = IUU_CLK_3680000; -- cgit v1.2.3 From 3528651e89aa044347ca06db6e9a106aa9aff914 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 16 Jul 2018 13:51:56 +0200 Subject: USB: serial: iuu_phoenix: drop redundant input-speed re-encoding Drop redundant input-speed re-encoding at every open(). The output and input speeds are initialised to the same value and are kept in sync on termios updates. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/iuu_phoenix.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 87c8dd064205..449e89db9cea 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -962,9 +962,6 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) struct iuu_private *priv = usb_get_serial_port_data(port); baud = tty->termios.c_ospeed; - tty->termios.c_ispeed = baud; - /* Re-encode speed */ - tty_encode_baud_rate(tty, baud, baud); dev_dbg(dev, "%s - baud %d\n", __func__, baud); usb_clear_halt(serial->dev, port->write_urb->pipe); -- cgit v1.2.3 From 95fd4f47c857cf887ec0f6718ffb6a6ec3b62bd6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 18 Jul 2018 14:24:57 +0200 Subject: USB: serial: cp210x: make line-speed quantisation data driven Older cp210x devices only support a fixed set of line speeds to which a requested speed is mapped. Reimplement this mapping using a table instead of a long if-else construct. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/cp210x.c | 99 +++++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 43 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index eb6c26cbe579..1b380309f653 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -752,48 +752,6 @@ static int cp210x_get_line_ctl(struct usb_serial_port *port, u16 *ctl) return 0; } -/* - * cp210x_quantise_baudrate - * Quantises the baud rate as per AN205 Table 1 - */ -static unsigned int cp210x_quantise_baudrate(unsigned int baud) -{ - if (baud <= 300) - baud = 300; - else if (baud <= 600) baud = 600; - else if (baud <= 1200) baud = 1200; - else if (baud <= 1800) baud = 1800; - else if (baud <= 2400) baud = 2400; - else if (baud <= 4000) baud = 4000; - else if (baud <= 4803) baud = 4800; - else if (baud <= 7207) baud = 7200; - else if (baud <= 9612) baud = 9600; - else if (baud <= 14428) baud = 14400; - else if (baud <= 16062) baud = 16000; - else if (baud <= 19250) baud = 19200; - else if (baud <= 28912) baud = 28800; - else if (baud <= 38601) baud = 38400; - else if (baud <= 51558) baud = 51200; - else if (baud <= 56280) baud = 56000; - else if (baud <= 58053) baud = 57600; - else if (baud <= 64111) baud = 64000; - else if (baud <= 77608) baud = 76800; - else if (baud <= 117028) baud = 115200; - else if (baud <= 129347) baud = 128000; - else if (baud <= 156868) baud = 153600; - else if (baud <= 237832) baud = 230400; - else if (baud <= 254234) baud = 250000; - else if (baud <= 273066) baud = 256000; - else if (baud <= 491520) baud = 460800; - else if (baud <= 567138) baud = 500000; - else if (baud <= 670254) baud = 576000; - else if (baud < 1000000) - baud = 921600; - else if (baud > 2000000) - baud = 2000000; - return baud; -} - static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) { int result; @@ -1013,6 +971,58 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, *cflagp = cflag; } +struct cp210x_rate { + speed_t rate; + speed_t high; +}; + +static const struct cp210x_rate cp210x_an205_table1[] = { + { 300, 300 }, + { 600, 600 }, + { 1200, 1200 }, + { 1800, 1800 }, + { 2400, 2400 }, + { 4000, 4000 }, + { 4800, 4803 }, + { 7200, 7207 }, + { 9600, 9612 }, + { 14400, 14428 }, + { 16000, 16062 }, + { 19200, 19250 }, + { 28800, 28912 }, + { 38400, 38601 }, + { 51200, 51558 }, + { 56000, 56280 }, + { 57600, 58053 }, + { 64000, 64111 }, + { 76800, 77608 }, + { 115200, 117028 }, + { 128000, 129347 }, + { 153600, 156868 }, + { 230400, 237832 }, + { 250000, 254234 }, + { 256000, 273066 }, + { 460800, 491520 }, + { 500000, 567138 }, + { 576000, 670254 }, + { 921600, UINT_MAX } +}; + +/* + * Quantises the baud rate as per AN205 Table 1 + */ +static speed_t cp210x_get_an205_rate(speed_t baud) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cp210x_an205_table1); ++i) { + if (baud <= cp210x_an205_table1[i].high) + break; + } + + return cp210x_an205_table1[i].rate; +} + /* * CP2101 supports the following baud rates: * @@ -1051,7 +1061,10 @@ static void cp210x_change_speed(struct tty_struct *tty, * * NOTE: B0 is not implemented. */ - baud = cp210x_quantise_baudrate(baud); + if (baud < 1000000) + baud = cp210x_get_an205_rate(baud); + else if (baud > 2000000) + baud = 2000000; dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud); if (cp210x_write_u32_reg(port, CP210X_SET_BAUDRATE, baud)) { -- cgit v1.2.3 From d4706c05c59d7afdadd8e7cfc1bf470356938c89 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 18 Jul 2018 14:24:58 +0200 Subject: USB: serial: cp210x: honour device-type maximum line speed Newer cp210x devices support higher line speeds than the older ones which supported a discrete set of speeds up to 921.6 kbaud. To support these higher speeds, we have for some time mapped speeds lower than 1 Mbaud to the speeds supported by older devices, while allowing the device to pick the closest possible rate for higher speeds (without trying to guess and report back what rate was actually chosen). As this implementation can lead to undefined behaviour for older devices which do not support the higher rates, let's use the later-added device-type detection to determine the maximum supported speed. This will also be useful when adding support for cp2102n which can handle rates up to 3 Mbaud. As per the data sheets the following maximum speeds are used cp2101 921.6 kbaud cp2102/3 1 Mbaud cp2104/8 2 Mbaud cp2105 - ECI port 2 Mbaud - SCI port 921.6 kbaud while keeping the maximum 2 Mbaud for unknown device types in order to avoid any regressions. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/cp210x.c | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 1b380309f653..4281f2bfe0e1 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -229,6 +229,7 @@ struct cp210x_serial_private { bool gpio_registered; #endif u8 partnum; + speed_t max_speed; }; struct cp210x_port_private { @@ -1052,19 +1053,20 @@ static speed_t cp210x_get_an205_rate(speed_t baud) static void cp210x_change_speed(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { + struct cp210x_serial_private *priv = usb_get_serial_data(port->serial); u32 baud; baud = tty->termios.c_ospeed; /* This maps the requested rate to a rate valid on cp2102 or cp2103, - * or to an arbitrary rate in [1M,2M]. + * or to an arbitrary rate in [1M, max_speed] * * NOTE: B0 is not implemented. */ if (baud < 1000000) baud = cp210x_get_an205_rate(baud); - else if (baud > 2000000) - baud = 2000000; + else if (baud > priv->max_speed) + baud = priv->max_speed; dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud); if (cp210x_write_u32_reg(port, CP210X_SET_BAUDRATE, baud)) { @@ -1495,6 +1497,37 @@ static int cp210x_port_remove(struct usb_serial_port *port) return 0; } +static void cp210x_init_max_speed(struct usb_serial *serial) +{ + struct cp210x_serial_private *priv = usb_get_serial_data(serial); + speed_t max; + + switch (priv->partnum) { + case CP210X_PARTNUM_CP2101: + max = 921600; + break; + case CP210X_PARTNUM_CP2102: + case CP210X_PARTNUM_CP2103: + max = 1000000; + break; + case CP210X_PARTNUM_CP2104: + case CP210X_PARTNUM_CP2108: + max = 2000000; + break; + case CP210X_PARTNUM_CP2105: + if (cp210x_interface_num(serial) == 0) + max = 2000000; /* ECI */ + else + max = 921600; /* SCI */ + break; + default: + max = 2000000; + break; + } + + priv->max_speed = max; +} + static int cp210x_attach(struct usb_serial *serial) { int result; @@ -1515,6 +1548,8 @@ static int cp210x_attach(struct usb_serial *serial) usb_set_serial_data(serial, priv); + cp210x_init_max_speed(serial); + if (priv->partnum == CP210X_PARTNUM_CP2105) { result = cp2105_shared_gpio_init(serial); if (result < 0) { -- cgit v1.2.3 From 6f0bcf720ea81e90e6066d14d5506565049a2eb5 Mon Sep 17 00:00:00 2001 From: Karoly Pados Date: Wed, 18 Jul 2018 14:24:59 +0200 Subject: USB: serial: cp210x: improve baudrate support for CP2102N CP2102N devices support a lot more baudrates than earlier chips by SiLabs. These devices are not constrained anymore by the table in AN205, and are able to generate almost any baudrate in the supported range with only minimal errors. This has also been verified with a scope on a physical device. This patch adds support for all baudrates supported by the CP2102N. Signed-off-by: Karoly Pados [johan: rework on top of an205 and max-speed patches ] Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/cp210x.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 4281f2bfe0e1..3778685c7b99 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -355,6 +355,9 @@ static struct usb_serial_driver * const serial_drivers[] = { #define CP210X_PARTNUM_CP2104 0x04 #define CP210X_PARTNUM_CP2105 0x05 #define CP210X_PARTNUM_CP2108 0x08 +#define CP210X_PARTNUM_CP2102N_QFN28 0x20 +#define CP210X_PARTNUM_CP2102N_QFN24 0x21 +#define CP210X_PARTNUM_CP2102N_QFN20 0x22 #define CP210X_PARTNUM_UNKNOWN 0xFF /* CP210X_GET_COMM_STATUS returns these 0x13 bytes */ @@ -454,6 +457,15 @@ struct cp210x_gpio_write { u8 state; } __packed; +static bool cp210x_is_cp2102n(struct usb_serial *serial) +{ + struct cp210x_serial_private *priv = usb_get_serial_data(serial); + + return (priv->partnum == CP210X_PARTNUM_CP2102N_QFN28) || + (priv->partnum == CP210X_PARTNUM_CP2102N_QFN24) || + (priv->partnum == CP210X_PARTNUM_CP2102N_QFN20); +} + /* * Helper to get interface number when we only have struct usb_serial. */ @@ -1053,20 +1065,32 @@ static speed_t cp210x_get_an205_rate(speed_t baud) static void cp210x_change_speed(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { - struct cp210x_serial_private *priv = usb_get_serial_data(port->serial); + struct usb_serial *serial = port->serial; + struct cp210x_serial_private *priv = usb_get_serial_data(serial); u32 baud; baud = tty->termios.c_ospeed; - /* This maps the requested rate to a rate valid on cp2102 or cp2103, - * or to an arbitrary rate in [1M, max_speed] + /* + * This maps the requested rate to the actual rate on cp2102n, a valid + * rate on cp2102 or cp2103, or to an arbitrary rate in + * [1M, max_speed]. * * NOTE: B0 is not implemented. */ - if (baud < 1000000) + if (cp210x_is_cp2102n(serial)) { + int clk_div; + int prescaler; + + baud = clamp(baud, 300u, priv->max_speed); + prescaler = (baud <= 365) ? 4 : 1; + clk_div = DIV_ROUND_CLOSEST(48000000, 2 * prescaler * baud); + baud = 48000000 / (2 * prescaler * clk_div); + } else if (baud < 1000000) { baud = cp210x_get_an205_rate(baud); - else if (baud > priv->max_speed) + } else if (baud > priv->max_speed) { baud = priv->max_speed; + } dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud); if (cp210x_write_u32_reg(port, CP210X_SET_BAUDRATE, baud)) { @@ -1520,6 +1544,11 @@ static void cp210x_init_max_speed(struct usb_serial *serial) else max = 921600; /* SCI */ break; + case CP210X_PARTNUM_CP2102N_QFN28: + case CP210X_PARTNUM_CP2102N_QFN24: + case CP210X_PARTNUM_CP2102N_QFN20: + max = 3000000; + break; default: max = 2000000; break; -- cgit v1.2.3 From 7aecd7fc5d95f1447611ab5e14db5ab9549f979c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 18 Jul 2018 14:25:00 +0200 Subject: USB: serial: cp210x: generalise CP2102N line-speed handling The CP2102N equations for determining the actual baud rate can be used also for other device types, so let's factor it out. Note that this removes the now unused cp210x_is_cp2102n() helper. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/cp210x.c | 50 ++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 23 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 3778685c7b99..957406aac9bd 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -230,6 +230,7 @@ struct cp210x_serial_private { #endif u8 partnum; speed_t max_speed; + bool use_actual_rate; }; struct cp210x_port_private { @@ -457,15 +458,6 @@ struct cp210x_gpio_write { u8 state; } __packed; -static bool cp210x_is_cp2102n(struct usb_serial *serial) -{ - struct cp210x_serial_private *priv = usb_get_serial_data(serial); - - return (priv->partnum == CP210X_PARTNUM_CP2102N_QFN28) || - (priv->partnum == CP210X_PARTNUM_CP2102N_QFN24) || - (priv->partnum == CP210X_PARTNUM_CP2102N_QFN20); -} - /* * Helper to get interface number when we only have struct usb_serial. */ @@ -1036,6 +1028,23 @@ static speed_t cp210x_get_an205_rate(speed_t baud) return cp210x_an205_table1[i].rate; } +static speed_t cp210x_get_actual_rate(struct usb_serial *serial, speed_t baud) +{ + struct cp210x_serial_private *priv = usb_get_serial_data(serial); + unsigned int prescale = 1; + unsigned int div; + + baud = clamp(baud, 300u, priv->max_speed); + + if (baud <= 365) + prescale = 4; + + div = DIV_ROUND_CLOSEST(48000000, 2 * prescale * baud); + baud = 48000000 / (2 * prescale * div); + + return baud; +} + /* * CP2101 supports the following baud rates: * @@ -1072,25 +1081,17 @@ static void cp210x_change_speed(struct tty_struct *tty, baud = tty->termios.c_ospeed; /* - * This maps the requested rate to the actual rate on cp2102n, a valid - * rate on cp2102 or cp2103, or to an arbitrary rate in - * [1M, max_speed]. + * This maps the requested rate to the actual rate, a valid rate on + * cp2102 or cp2103, or to an arbitrary rate in [1M, max_speed]. * * NOTE: B0 is not implemented. */ - if (cp210x_is_cp2102n(serial)) { - int clk_div; - int prescaler; - - baud = clamp(baud, 300u, priv->max_speed); - prescaler = (baud <= 365) ? 4 : 1; - clk_div = DIV_ROUND_CLOSEST(48000000, 2 * prescaler * baud); - baud = 48000000 / (2 * prescaler * clk_div); - } else if (baud < 1000000) { + if (priv->use_actual_rate) + baud = cp210x_get_actual_rate(serial, baud); + else if (baud < 1000000) baud = cp210x_get_an205_rate(baud); - } else if (baud > priv->max_speed) { + else if (baud > priv->max_speed) baud = priv->max_speed; - } dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud); if (cp210x_write_u32_reg(port, CP210X_SET_BAUDRATE, baud)) { @@ -1524,6 +1525,7 @@ static int cp210x_port_remove(struct usb_serial_port *port) static void cp210x_init_max_speed(struct usb_serial *serial) { struct cp210x_serial_private *priv = usb_get_serial_data(serial); + bool use_actual_rate = false; speed_t max; switch (priv->partnum) { @@ -1547,6 +1549,7 @@ static void cp210x_init_max_speed(struct usb_serial *serial) case CP210X_PARTNUM_CP2102N_QFN28: case CP210X_PARTNUM_CP2102N_QFN24: case CP210X_PARTNUM_CP2102N_QFN20: + use_actual_rate = true; max = 3000000; break; default: @@ -1555,6 +1558,7 @@ static void cp210x_init_max_speed(struct usb_serial *serial) } priv->max_speed = max; + priv->use_actual_rate = use_actual_rate; } static int cp210x_attach(struct usb_serial *serial) -- cgit v1.2.3 From 5edb65a33710bbf10f38b42e0d497b35ec1ed908 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 18 Jul 2018 14:25:01 +0200 Subject: USB: serial: cp210x: improve line-speed handling for CP2104 and CP2105 CP2104 and the ECI interface of CP2105 support further baud rates than the ones specified in AN205 table 1, and we can use the same equations as for CP2102N to determine and report back the actual baud rates used. Note that this could eventually be generalised also to CP2108, which uses a different base clock. There appears to be an error in the CP2108 equations which needs to be confirmed on actual hardware first however (specifically, the subtraction of one from the divisor appears to be incorrect as it introduces larger errors). Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/cp210x.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 957406aac9bd..4a118eb13590 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -1537,14 +1537,19 @@ static void cp210x_init_max_speed(struct usb_serial *serial) max = 1000000; break; case CP210X_PARTNUM_CP2104: + use_actual_rate = true; + max = 2000000; + break; case CP210X_PARTNUM_CP2108: max = 2000000; break; case CP210X_PARTNUM_CP2105: - if (cp210x_interface_num(serial) == 0) + if (cp210x_interface_num(serial) == 0) { + use_actual_rate = true; max = 2000000; /* ECI */ - else + } else { max = 921600; /* SCI */ + } break; case CP210X_PARTNUM_CP2102N_QFN28: case CP210X_PARTNUM_CP2102N_QFN24: -- cgit v1.2.3 From c8acfe0aadbeb78f65826959891be15cc0a709a3 Mon Sep 17 00:00:00 2001 From: Karoly Pados Date: Fri, 20 Jul 2018 12:52:40 +0200 Subject: USB: serial: cp210x: implement GPIO support for CP2102N This patch adds GPIO support for CP2102N devices. It introduces new generic code to support emulating separate input and outputs directions even though these devices only know output modes (open-drain and pushpull). Existing GPIO support for CP2105 has been migrated over to the new code structure. Only limitation is that for the QFN28 variant, only 4 out of 7 GPIOs are supported. This is because the config array locations of the last 3 pins are not documented, and reverse engineering revealed offsets that conflicted with other documented functions. Hence we'll play it safe instead until somebody clears this up further. Signed-off-by: Karoly Pados [ johan: fix style issues and a couple of minor bugs; use Karoly's updated commit message ] Acked-by: Martyn Welch Signed-off-by: Johan Hovold --- drivers/usb/serial/cp210x.c | 245 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 209 insertions(+), 36 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 4a118eb13590..b9bc80700be7 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -224,9 +224,10 @@ MODULE_DEVICE_TABLE(usb, id_table); struct cp210x_serial_private { #ifdef CONFIG_GPIOLIB struct gpio_chip gc; - u8 config; - u8 gpio_mode; bool gpio_registered; + u8 gpio_pushpull; + u8 gpio_altfunc; + u8 gpio_input; #endif u8 partnum; speed_t max_speed; @@ -343,6 +344,7 @@ static struct usb_serial_driver * const serial_drivers[] = { #define CONTROL_WRITE_RTS 0x0200 /* CP210X_VENDOR_SPECIFIC values */ +#define CP210X_READ_2NCONFIG 0x000E #define CP210X_READ_LATCH 0x00C2 #define CP210X_GET_PARTNUM 0x370B #define CP210X_GET_PORTCONFIG 0x370C @@ -452,6 +454,12 @@ struct cp210x_config { #define CP2105_GPIO1_RXLED_MODE BIT(1) #define CP2105_GPIO1_RS485_MODE BIT(2) +/* CP2102N configuration array indices */ +#define CP210X_2NCONFIG_CONFIG_VERSION_IDX 2 +#define CP210X_2NCONFIG_GPIO_MODE_IDX 581 +#define CP210X_2NCONFIG_GPIO_RSTLATCH_IDX 587 +#define CP210X_2NCONFIG_GPIO_CONTROL_IDX 600 + /* CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these 0x2 bytes. */ struct cp210x_gpio_write { u8 mask; @@ -1313,17 +1321,8 @@ static int cp210x_gpio_request(struct gpio_chip *gc, unsigned int offset) struct usb_serial *serial = gpiochip_get_data(gc); struct cp210x_serial_private *priv = usb_get_serial_data(serial); - switch (offset) { - case 0: - if (priv->config & CP2105_GPIO0_TXLED_MODE) - return -ENODEV; - break; - case 1: - if (priv->config & (CP2105_GPIO1_RXLED_MODE | - CP2105_GPIO1_RS485_MODE)) - return -ENODEV; - break; - } + if (priv->gpio_altfunc & BIT(offset)) + return -ENODEV; return 0; } @@ -1331,10 +1330,15 @@ static int cp210x_gpio_request(struct gpio_chip *gc, unsigned int offset) static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio) { struct usb_serial *serial = gpiochip_get_data(gc); + struct cp210x_serial_private *priv = usb_get_serial_data(serial); + u8 req_type = REQTYPE_DEVICE_TO_HOST; int result; u8 buf; - result = cp210x_read_vendor_block(serial, REQTYPE_INTERFACE_TO_HOST, + if (priv->partnum == CP210X_PARTNUM_CP2105) + req_type = REQTYPE_INTERFACE_TO_HOST; + + result = cp210x_read_vendor_block(serial, req_type, CP210X_READ_LATCH, &buf, sizeof(buf)); if (result < 0) return result; @@ -1345,7 +1349,9 @@ static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio) static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) { struct usb_serial *serial = gpiochip_get_data(gc); + struct cp210x_serial_private *priv = usb_get_serial_data(serial); struct cp210x_gpio_write buf; + int result; if (value == 1) buf.state = BIT(gpio); @@ -1354,25 +1360,68 @@ static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) buf.mask = BIT(gpio); - cp210x_write_vendor_block(serial, REQTYPE_HOST_TO_INTERFACE, - CP210X_WRITE_LATCH, &buf, sizeof(buf)); + if (priv->partnum == CP210X_PARTNUM_CP2105) { + result = cp210x_write_vendor_block(serial, + REQTYPE_HOST_TO_INTERFACE, + CP210X_WRITE_LATCH, &buf, + sizeof(buf)); + } else { + u16 wIndex = buf.state << 8 | buf.mask; + + result = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + CP210X_VENDOR_SPECIFIC, + REQTYPE_HOST_TO_DEVICE, + CP210X_WRITE_LATCH, + wIndex, + NULL, 0, USB_CTRL_SET_TIMEOUT); + } + + if (result < 0) { + dev_err(&serial->interface->dev, "failed to set GPIO value: %d\n", + result); + } } static int cp210x_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio) { - /* Hardware does not support an input mode */ - return 0; + struct usb_serial *serial = gpiochip_get_data(gc); + struct cp210x_serial_private *priv = usb_get_serial_data(serial); + + return priv->gpio_input & BIT(gpio); } static int cp210x_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio) { - /* Hardware does not support an input mode */ - return -ENOTSUPP; + struct usb_serial *serial = gpiochip_get_data(gc); + struct cp210x_serial_private *priv = usb_get_serial_data(serial); + + if (priv->partnum == CP210X_PARTNUM_CP2105) { + /* hardware does not support an input mode */ + return -ENOTSUPP; + } + + /* push-pull pins cannot be changed to be inputs */ + if (priv->gpio_pushpull & BIT(gpio)) + return -EINVAL; + + /* make sure to release pin if it is being driven low */ + cp210x_gpio_set(gc, gpio, 1); + + priv->gpio_input |= BIT(gpio); + + return 0; } static int cp210x_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio, int value) { + struct usb_serial *serial = gpiochip_get_data(gc); + struct cp210x_serial_private *priv = usb_get_serial_data(serial); + + priv->gpio_input &= ~BIT(gpio); + cp210x_gpio_set(gc, gpio, value); + return 0; } @@ -1385,11 +1434,11 @@ static int cp210x_gpio_set_config(struct gpio_chip *gc, unsigned int gpio, /* Succeed only if in correct mode (this can't be set at runtime) */ if ((param == PIN_CONFIG_DRIVE_PUSH_PULL) && - (priv->gpio_mode & BIT(gpio))) + (priv->gpio_pushpull & BIT(gpio))) return 0; if ((param == PIN_CONFIG_DRIVE_OPEN_DRAIN) && - !(priv->gpio_mode & BIT(gpio))) + !(priv->gpio_pushpull & BIT(gpio))) return 0; return -ENOTSUPP; @@ -1402,12 +1451,13 @@ static int cp210x_gpio_set_config(struct gpio_chip *gc, unsigned int gpio, * this driver that provide GPIO do so in a way that does not impact other * signals and are thus expected to have very different initialisation. */ -static int cp2105_shared_gpio_init(struct usb_serial *serial) +static int cp2105_gpioconf_init(struct usb_serial *serial) { struct cp210x_serial_private *priv = usb_get_serial_data(serial); struct cp210x_pin_mode mode; struct cp210x_config config; u8 intf_num = cp210x_interface_num(serial); + u8 iface_config; int result; result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST, @@ -1424,20 +1474,26 @@ static int cp2105_shared_gpio_init(struct usb_serial *serial) /* 2 banks of GPIO - One for the pins taken from each serial port */ if (intf_num == 0) { - if (mode.eci == CP210X_PIN_MODE_MODEM) + if (mode.eci == CP210X_PIN_MODE_MODEM) { + /* mark all GPIOs of this interface as reserved */ + priv->gpio_altfunc = 0xff; return 0; + } - priv->config = config.eci_cfg; - priv->gpio_mode = (u8)((le16_to_cpu(config.gpio_mode) & + iface_config = config.eci_cfg; + priv->gpio_pushpull = (u8)((le16_to_cpu(config.gpio_mode) & CP210X_ECI_GPIO_MODE_MASK) >> CP210X_ECI_GPIO_MODE_OFFSET); priv->gc.ngpio = 2; } else if (intf_num == 1) { - if (mode.sci == CP210X_PIN_MODE_MODEM) + if (mode.sci == CP210X_PIN_MODE_MODEM) { + /* mark all GPIOs of this interface as reserved */ + priv->gpio_altfunc = 0xff; return 0; + } - priv->config = config.sci_cfg; - priv->gpio_mode = (u8)((le16_to_cpu(config.gpio_mode) & + iface_config = config.sci_cfg; + priv->gpio_pushpull = (u8)((le16_to_cpu(config.gpio_mode) & CP210X_SCI_GPIO_MODE_MASK) >> CP210X_SCI_GPIO_MODE_OFFSET); priv->gc.ngpio = 3; @@ -1445,6 +1501,125 @@ static int cp2105_shared_gpio_init(struct usb_serial *serial) return -ENODEV; } + /* mark all pins which are not in GPIO mode */ + if (iface_config & CP2105_GPIO0_TXLED_MODE) /* GPIO 0 */ + priv->gpio_altfunc |= BIT(0); + if (iface_config & (CP2105_GPIO1_RXLED_MODE | /* GPIO 1 */ + CP2105_GPIO1_RS485_MODE)) + priv->gpio_altfunc |= BIT(1); + + /* driver implementation for CP2105 only supports outputs */ + priv->gpio_input = 0; + + return 0; +} + +static int cp2102n_gpioconf_init(struct usb_serial *serial) +{ + struct cp210x_serial_private *priv = usb_get_serial_data(serial); + const u16 config_size = 0x02a6; + u8 gpio_rst_latch; + u8 config_version; + u8 gpio_pushpull; + u8 *config_buf; + u8 gpio_latch; + u8 gpio_ctrl; + int result; + u8 i; + + /* + * Retrieve device configuration from the device. + * The array received contains all customization settings done at the + * factory/manufacturer. Format of the array is documented at the + * time of writing at: + * https://www.silabs.com/community/interface/knowledge-base.entry.html/2017/03/31/cp2102n_setconfig-xsfa + */ + config_buf = kmalloc(config_size, GFP_KERNEL); + if (!config_buf) + return -ENOMEM; + + result = cp210x_read_vendor_block(serial, + REQTYPE_DEVICE_TO_HOST, + CP210X_READ_2NCONFIG, + config_buf, + config_size); + if (result < 0) { + kfree(config_buf); + return result; + } + + config_version = config_buf[CP210X_2NCONFIG_CONFIG_VERSION_IDX]; + gpio_pushpull = config_buf[CP210X_2NCONFIG_GPIO_MODE_IDX]; + gpio_ctrl = config_buf[CP210X_2NCONFIG_GPIO_CONTROL_IDX]; + gpio_rst_latch = config_buf[CP210X_2NCONFIG_GPIO_RSTLATCH_IDX]; + + kfree(config_buf); + + /* Make sure this is a config format we understand. */ + if (config_version != 0x01) + return -ENOTSUPP; + + /* + * We only support 4 GPIOs even on the QFN28 package, because + * config locations of GPIOs 4-6 determined using reverse + * engineering revealed conflicting offsets with other + * documented functions. So we'll just play it safe for now. + */ + priv->gc.ngpio = 4; + + /* + * Get default pin states after reset. Needed so we can determine + * the direction of an open-drain pin. + */ + gpio_latch = (gpio_rst_latch >> 3) & 0x0f; + + /* 0 indicates open-drain mode, 1 is push-pull */ + priv->gpio_pushpull = (gpio_pushpull >> 3) & 0x0f; + + /* 0 indicates GPIO mode, 1 is alternate function */ + priv->gpio_altfunc = (gpio_ctrl >> 2) & 0x0f; + + /* + * The CP2102N does not strictly has input and output pin modes, + * it only knows open-drain and push-pull modes which is set at + * factory. An open-drain pin can function both as an + * input or an output. We emulate input mode for open-drain pins + * by making sure they are not driven low, and we do not allow + * push-pull pins to be set as an input. + */ + for (i = 0; i < priv->gc.ngpio; ++i) { + /* + * Set direction to "input" iff pin is open-drain and reset + * value is 1. + */ + if (!(priv->gpio_pushpull & BIT(i)) && (gpio_latch & BIT(i))) + priv->gpio_input |= BIT(i); + } + + return 0; +} + +static int cp210x_gpio_init(struct usb_serial *serial) +{ + struct cp210x_serial_private *priv = usb_get_serial_data(serial); + int result; + + switch (priv->partnum) { + case CP210X_PARTNUM_CP2105: + result = cp2105_gpioconf_init(serial); + break; + case CP210X_PARTNUM_CP2102N_QFN28: + case CP210X_PARTNUM_CP2102N_QFN24: + case CP210X_PARTNUM_CP2102N_QFN20: + result = cp2102n_gpioconf_init(serial); + break; + default: + return 0; + } + + if (result < 0) + return result; + priv->gc.label = "cp210x"; priv->gc.request = cp210x_gpio_request; priv->gc.get_direction = cp210x_gpio_direction_get; @@ -1477,7 +1652,7 @@ static void cp210x_gpio_remove(struct usb_serial *serial) #else -static int cp2105_shared_gpio_init(struct usb_serial *serial) +static int cp210x_gpio_init(struct usb_serial *serial) { return 0; } @@ -1588,12 +1763,10 @@ static int cp210x_attach(struct usb_serial *serial) cp210x_init_max_speed(serial); - if (priv->partnum == CP210X_PARTNUM_CP2105) { - result = cp2105_shared_gpio_init(serial); - if (result < 0) { - dev_err(&serial->interface->dev, - "GPIO initialisation failed, continuing without GPIO support\n"); - } + result = cp210x_gpio_init(serial); + if (result < 0) { + dev_err(&serial->interface->dev, "GPIO initialisation failed: %d\n", + result); } return 0; -- cgit v1.2.3 From 04b453c859d3be8f3f992f929bfa2957db184e4b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 15 Jul 2018 10:37:37 -0700 Subject: usb/phy: fix PPC64 build errors in phy-fsl-usb.c Fix build errors when built for PPC64: These variables are only used on PPC32 so they don't need to be initialized for PPC64. ../drivers/usb/phy/phy-fsl-usb.c: In function 'usb_otg_start': ../drivers/usb/phy/phy-fsl-usb.c:865:3: error: '_fsl_readl' undeclared (first use in this function); did you mean 'fsl_readl'? _fsl_readl = _fsl_readl_be; ../drivers/usb/phy/phy-fsl-usb.c:865:16: error: '_fsl_readl_be' undeclared (first use in this function); did you mean 'fsl_readl'? _fsl_readl = _fsl_readl_be; ../drivers/usb/phy/phy-fsl-usb.c:866:3: error: '_fsl_writel' undeclared (first use in this function); did you mean 'fsl_writel'? _fsl_writel = _fsl_writel_be; ../drivers/usb/phy/phy-fsl-usb.c:866:17: error: '_fsl_writel_be' undeclared (first use in this function); did you mean 'fsl_writel'? _fsl_writel = _fsl_writel_be; ../drivers/usb/phy/phy-fsl-usb.c:868:16: error: '_fsl_readl_le' undeclared (first use in this function); did you mean 'fsl_readl'? _fsl_readl = _fsl_readl_le; ../drivers/usb/phy/phy-fsl-usb.c:869:17: error: '_fsl_writel_le' undeclared (first use in this function); did you mean 'fsl_writel'? _fsl_writel = _fsl_writel_le; and the sysfs "show" function return type should be ssize_t, not int: ../drivers/usb/phy/phy-fsl-usb.c:1042:49: error: initialization of 'ssize_t (*)(struct device *, struct device_attribute *, char *)' {aka 'long int (*)(struct device *, struct device_attribute *, char *)'} from incompatible pointer type 'int (*)(struct device *, struct device_attribute *, char *)' [-Werror=incompatible-pointer-types] static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL); Signed-off-by: Randy Dunlap Cc: Felipe Balbi Cc: linux-usb@vger.kernel.org Cc: Michael Ellerman Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-fsl-usb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index 900875f326d7..f7c96d209eda 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -861,6 +861,7 @@ int usb_otg_start(struct platform_device *pdev) if (pdata->init && pdata->init(pdev) != 0) return -EINVAL; +#ifdef CONFIG_PPC32 if (pdata->big_endian_mmio) { _fsl_readl = _fsl_readl_be; _fsl_writel = _fsl_writel_be; @@ -868,6 +869,7 @@ int usb_otg_start(struct platform_device *pdev) _fsl_readl = _fsl_readl_le; _fsl_writel = _fsl_writel_le; } +#endif /* request irq */ p_otg->irq = platform_get_irq(pdev, 0); @@ -958,7 +960,7 @@ int usb_otg_start(struct platform_device *pdev) /* * state file in sysfs */ -static int show_fsl_usb2_otg_state(struct device *dev, +static ssize_t show_fsl_usb2_otg_state(struct device *dev, struct device_attribute *attr, char *buf) { struct otg_fsm *fsm = &fsl_otg_dev->fsm; -- cgit v1.2.3 From 4f3c8d6eddc272b386464524235440a418ed2029 Mon Sep 17 00:00:00 2001 From: Guido Kiener Date: Wed, 18 Jul 2018 10:45:34 +0200 Subject: usb: usbtmc: Support Read Status Byte with SRQ per file Add 'struct usbtmc_file_data' for each file handle to cache last srq_byte (=Status Byte with SRQ) received by usbtmc_interrupt(..) usbtmc488_ioctl_read_stb returns cached srq_byte when available for each file handle to avoid race conditions of concurrent applications. SRQ now sets EPOLLPRI instead of EPOLLIN since EPOLLIN is now reserved for asynchronous reads Tested-by: Dave Penkler Reviewed-by: Steve Bayless Signed-off-by: Guido Kiener Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 136 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 105 insertions(+), 31 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 529295a17579..db58b84d43ee 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -67,6 +67,7 @@ struct usbtmc_device_data { const struct usb_device_id *id; struct usb_device *usb_dev; struct usb_interface *intf; + struct list_head file_list; unsigned int bulk_in; unsigned int bulk_out; @@ -87,7 +88,6 @@ struct usbtmc_device_data { int iin_interval; struct urb *iin_urb; u16 iin_wMaxPacketSize; - atomic_t srq_asserted; /* coalesced usb488_caps from usbtmc_dev_capabilities */ __u8 usb488_caps; @@ -104,9 +104,21 @@ struct usbtmc_device_data { struct mutex io_mutex; /* only one i/o function running at a time */ wait_queue_head_t waitq; struct fasync_struct *fasync; + spinlock_t dev_lock; /* lock for file_list */ }; #define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref) +/* + * This structure holds private data for each USBTMC file handle. + */ +struct usbtmc_file_data { + struct usbtmc_device_data *data; + struct list_head file_elem; + + u8 srq_byte; + atomic_t srq_asserted; +}; + /* Forward declarations */ static struct usb_driver usbtmc_driver; @@ -122,7 +134,7 @@ static int usbtmc_open(struct inode *inode, struct file *filp) { struct usb_interface *intf; struct usbtmc_device_data *data; - int retval = 0; + struct usbtmc_file_data *file_data; intf = usb_find_interface(&usbtmc_driver, iminor(inode)); if (!intf) { @@ -130,21 +142,45 @@ static int usbtmc_open(struct inode *inode, struct file *filp) return -ENODEV; } + file_data = kzalloc(sizeof(*file_data), GFP_KERNEL); + if (!file_data) + return -ENOMEM; + data = usb_get_intfdata(intf); /* Protect reference to data from file structure until release */ kref_get(&data->kref); + mutex_lock(&data->io_mutex); + file_data->data = data; + + INIT_LIST_HEAD(&file_data->file_elem); + spin_lock_irq(&data->dev_lock); + list_add_tail(&file_data->file_elem, &data->file_list); + spin_unlock_irq(&data->dev_lock); + mutex_unlock(&data->io_mutex); + /* Store pointer in file structure's private data field */ - filp->private_data = data; + filp->private_data = file_data; - return retval; + return 0; } static int usbtmc_release(struct inode *inode, struct file *file) { - struct usbtmc_device_data *data = file->private_data; + struct usbtmc_file_data *file_data = file->private_data; - kref_put(&data->kref, usbtmc_delete); + /* prevent IO _AND_ usbtmc_interrupt */ + mutex_lock(&file_data->data->io_mutex); + spin_lock_irq(&file_data->data->dev_lock); + + list_del(&file_data->file_elem); + + spin_unlock_irq(&file_data->data->dev_lock); + mutex_unlock(&file_data->data->io_mutex); + + kref_put(&file_data->data->kref, usbtmc_delete); + file_data->data = NULL; + kfree(file_data); return 0; } @@ -369,10 +405,12 @@ exit: return rv; } -static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data, +static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data, void __user *arg) { + struct usbtmc_device_data *data = file_data->data; struct device *dev = &data->intf->dev; + int srq_asserted = 0; u8 *buffer; u8 tag; __u8 stb; @@ -381,15 +419,25 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data, dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n", data->iin_ep_present); + spin_lock_irq(&data->dev_lock); + srq_asserted = atomic_xchg(&file_data->srq_asserted, srq_asserted); + if (srq_asserted) { + /* a STB with SRQ is already received */ + stb = file_data->srq_byte; + spin_unlock_irq(&data->dev_lock); + rv = put_user(stb, (__u8 __user *)arg); + dev_dbg(dev, "stb:0x%02x with srq received %d\n", + (unsigned int)stb, rv); + return rv; + } + spin_unlock_irq(&data->dev_lock); + buffer = kmalloc(8, GFP_KERNEL); if (!buffer) return -ENOMEM; atomic_set(&data->iin_data_valid, 0); - /* must issue read_stb before using poll or select */ - atomic_set(&data->srq_asserted, 0); - rv = usb_control_msg(data->usb_dev, usb_rcvctrlpipe(data->usb_dev, 0), USBTMC488_REQUEST_READ_STATUS_BYTE, @@ -435,9 +483,8 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data, stb = buffer[2]; } - rv = copy_to_user(arg, &stb, sizeof(stb)); - if (rv) - rv = -EFAULT; + rv = put_user(stb, (__u8 __user *)arg); + dev_dbg(dev, "stb:0x%02x received %d\n", (unsigned int)stb, rv); exit: /* bump interrupt bTag */ @@ -565,6 +612,7 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t static ssize_t usbtmc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { + struct usbtmc_file_data *file_data; struct usbtmc_device_data *data; struct device *dev; u32 n_characters; @@ -576,7 +624,8 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, size_t this_part; /* Get pointer to private data structure */ - data = filp->private_data; + file_data = filp->private_data; + data = file_data->data; dev = &data->intf->dev; buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); @@ -721,6 +770,7 @@ exit: static ssize_t usbtmc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { + struct usbtmc_file_data *file_data; struct usbtmc_device_data *data; u8 *buffer; int retval; @@ -730,7 +780,8 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf, int done; int this_part; - data = filp->private_data; + file_data = filp->private_data; + data = file_data->data; buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); if (!buffer) @@ -1140,10 +1191,13 @@ exit: static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + struct usbtmc_file_data *file_data; struct usbtmc_device_data *data; int retval = -EBADRQC; - data = file->private_data; + file_data = file->private_data; + data = file_data->data; + mutex_lock(&data->io_mutex); if (data->zombie) { retval = -ENODEV; @@ -1184,7 +1238,8 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case USBTMC488_IOCTL_READ_STB: - retval = usbtmc488_ioctl_read_stb(data, (void __user *)arg); + retval = usbtmc488_ioctl_read_stb(file_data, + (void __user *)arg); break; case USBTMC488_IOCTL_REN_CONTROL: @@ -1210,14 +1265,15 @@ skip_io_on_zombie: static int usbtmc_fasync(int fd, struct file *file, int on) { - struct usbtmc_device_data *data = file->private_data; + struct usbtmc_file_data *file_data = file->private_data; - return fasync_helper(fd, file, on, &data->fasync); + return fasync_helper(fd, file, on, &file_data->data->fasync); } static __poll_t usbtmc_poll(struct file *file, poll_table *wait) { - struct usbtmc_device_data *data = file->private_data; + struct usbtmc_file_data *file_data = file->private_data; + struct usbtmc_device_data *data = file_data->data; __poll_t mask; mutex_lock(&data->io_mutex); @@ -1229,7 +1285,7 @@ static __poll_t usbtmc_poll(struct file *file, poll_table *wait) poll_wait(file, &data->waitq, wait); - mask = (atomic_read(&data->srq_asserted)) ? EPOLLIN | EPOLLRDNORM : 0; + mask = (atomic_read(&file_data->srq_asserted)) ? EPOLLPRI : 0; no_poll: mutex_unlock(&data->io_mutex); @@ -1276,15 +1332,33 @@ static void usbtmc_interrupt(struct urb *urb) } /* check for SRQ notification */ if (data->iin_buffer[0] == 0x81) { + unsigned long flags; + struct list_head *elem; + if (data->fasync) kill_fasync(&data->fasync, - SIGIO, POLL_IN); + SIGIO, POLL_PRI); - atomic_set(&data->srq_asserted, 1); - wake_up_interruptible(&data->waitq); + spin_lock_irqsave(&data->dev_lock, flags); + list_for_each(elem, &data->file_list) { + struct usbtmc_file_data *file_data; + + file_data = list_entry(elem, + struct usbtmc_file_data, + file_elem); + file_data->srq_byte = data->iin_buffer[1]; + atomic_set(&file_data->srq_asserted, 1); + } + spin_unlock_irqrestore(&data->dev_lock, flags); + + dev_dbg(dev, "srq received bTag %x stb %x\n", + (unsigned int)data->iin_buffer[0], + (unsigned int)data->iin_buffer[1]); + wake_up_interruptible_all(&data->waitq); goto exit; } - dev_warn(dev, "invalid notification: %x\n", data->iin_buffer[0]); + dev_warn(dev, "invalid notification: %x\n", + data->iin_buffer[0]); break; case -EOVERFLOW: dev_err(dev, "overflow with length %d, actual length is %d\n", @@ -1295,6 +1369,7 @@ static void usbtmc_interrupt(struct urb *urb) case -ESHUTDOWN: case -EILSEQ: case -ETIME: + case -EPIPE: /* urb terminated, clean up */ dev_dbg(dev, "urb terminated, status: %d\n", status); return; @@ -1339,7 +1414,9 @@ static int usbtmc_probe(struct usb_interface *intf, mutex_init(&data->io_mutex); init_waitqueue_head(&data->waitq); atomic_set(&data->iin_data_valid, 0); - atomic_set(&data->srq_asserted, 0); + INIT_LIST_HEAD(&data->file_list); + spin_lock_init(&data->dev_lock); + data->zombie = 0; /* Initialize USBTMC bTag and other fields */ @@ -1442,17 +1519,14 @@ err_put: static void usbtmc_disconnect(struct usb_interface *intf) { - struct usbtmc_device_data *data; + struct usbtmc_device_data *data = usb_get_intfdata(intf); - dev_dbg(&intf->dev, "usbtmc_disconnect called\n"); - - data = usb_get_intfdata(intf); usb_deregister_dev(intf, &usbtmc_class); sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); mutex_lock(&data->io_mutex); data->zombie = 1; - wake_up_all(&data->waitq); + wake_up_interruptible_all(&data->waitq); mutex_unlock(&data->io_mutex); usbtmc_free_int(data); kref_put(&data->kref, usbtmc_delete); -- cgit v1.2.3 From 19e6c57e96169cb3275fd40b7898454560b9ebba Mon Sep 17 00:00:00 2001 From: Guido Kiener Date: Wed, 18 Jul 2018 10:45:35 +0200 Subject: usb: usbtmc: use consistent timeout error - use consistent error value ETIMEOUT instead of ETIME Tested-by: Dave Penkler Reviewed-by: Steve Bayless Signed-off-by: Guido Kiener Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index db58b84d43ee..243e8446b8dd 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -468,7 +468,7 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data, if (rv == 0) { dev_dbg(dev, "wait timed out\n"); - rv = -ETIME; + rv = -ETIMEDOUT; goto exit; } -- cgit v1.2.3 From 048c6d88a0214757926f264823829e79154fcd4f Mon Sep 17 00:00:00 2001 From: Guido Kiener Date: Wed, 18 Jul 2018 10:45:36 +0200 Subject: usb: usbtmc: Add ioctls to set/get usb timeout Add ioctls USBTMC_IOCTL_GET_TIMEOUT / USBTMC_IOCTL_SET_TIMEOUT to get/set I/O timeout for specific file handle. Different operations on an instrument can take different lengths of time thus it is important to be able to set the timeout slightly longer than the expected duration of each operation to optimise the responsiveness of the application. As the instrument may be shared by multiple applications the timeout should be settable on a per file descriptor basis. Tested-by: Dave Penkler Reviewed-by: Steve Bayless Signed-off-by: Dave Penkler Signed-off-by: Guido Kiener Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 65 ++++++++++++++++++++++++++++++++++++++++---- include/uapi/linux/usb/tmc.h | 4 +++ 2 files changed, 63 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 243e8446b8dd..36d740c4c6fb 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -30,6 +30,8 @@ */ #define USBTMC_SIZE_IOBUFFER 2048 +/* Minimum USB timeout (in milliseconds) */ +#define USBTMC_MIN_TIMEOUT 100 /* Default USB timeout (in milliseconds) */ #define USBTMC_TIMEOUT 5000 @@ -115,6 +117,7 @@ struct usbtmc_file_data { struct usbtmc_device_data *data; struct list_head file_elem; + u32 timeout; u8 srq_byte; atomic_t srq_asserted; }; @@ -153,6 +156,8 @@ static int usbtmc_open(struct inode *inode, struct file *filp) mutex_lock(&data->io_mutex); file_data->data = data; + file_data->timeout = USBTMC_TIMEOUT; + INIT_LIST_HEAD(&file_data->file_elem); spin_lock_irq(&data->dev_lock); list_add_tail(&file_data->file_elem, &data->file_list); @@ -460,7 +465,7 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data, rv = wait_event_interruptible_timeout( data->waitq, atomic_read(&data->iin_data_valid) != 0, - USBTMC_TIMEOUT); + file_data->timeout); if (rv < 0) { dev_dbg(dev, "wait interrupted %d\n", rv); goto exit; @@ -560,8 +565,10 @@ static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data, * * Also updates bTag_last_write. */ -static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t transfer_size) +static int send_request_dev_dep_msg_in(struct usbtmc_file_data *file_data, + size_t transfer_size) { + struct usbtmc_device_data *data = file_data->data; int retval; u8 *buffer; int actual; @@ -590,7 +597,8 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t retval = usb_bulk_msg(data->usb_dev, usb_sndbulkpipe(data->usb_dev, data->bulk_out), - buffer, USBTMC_HEADER_SIZE, &actual, USBTMC_TIMEOUT); + buffer, USBTMC_HEADER_SIZE, + &actual, file_data->timeout); /* Store bTag (in case we need to abort) */ data->bTag_last_write = data->bTag; @@ -640,7 +648,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, dev_dbg(dev, "usb_bulk_msg_in: count(%zu)\n", count); - retval = send_request_dev_dep_msg_in(data, count); + retval = send_request_dev_dep_msg_in(file_data, count); if (retval < 0) { if (data->auto_abort) @@ -659,7 +667,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, usb_rcvbulkpipe(data->usb_dev, data->bulk_in), buffer, USBTMC_SIZE_IOBUFFER, &actual, - USBTMC_TIMEOUT); + file_data->timeout); dev_dbg(dev, "usb_bulk_msg: retval(%u), done(%zu), remaining(%zu), actual(%d)\n", retval, done, remaining, actual); @@ -832,7 +840,7 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf, usb_sndbulkpipe(data->usb_dev, data->bulk_out), buffer, n_bytes, - &actual, USBTMC_TIMEOUT); + &actual, file_data->timeout); if (retval != 0) break; n_bytes -= actual; @@ -1189,6 +1197,41 @@ exit: return rv; } +/* + * Get the usb timeout value + */ +static int usbtmc_ioctl_get_timeout(struct usbtmc_file_data *file_data, + void __user *arg) +{ + u32 timeout; + + timeout = file_data->timeout; + + return put_user(timeout, (__u32 __user *)arg); +} + +/* + * Set the usb timeout value + */ +static int usbtmc_ioctl_set_timeout(struct usbtmc_file_data *file_data, + void __user *arg) +{ + u32 timeout; + + if (get_user(timeout, (__u32 __user *)arg)) + return -EFAULT; + + /* Note that timeout = 0 means + * MAX_SCHEDULE_TIMEOUT in usb_control_msg + */ + if (timeout < USBTMC_MIN_TIMEOUT) + return -EINVAL; + + file_data->timeout = timeout; + + return 0; +} + static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct usbtmc_file_data *file_data; @@ -1229,6 +1272,16 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) retval = usbtmc_ioctl_abort_bulk_in(data); break; + case USBTMC_IOCTL_GET_TIMEOUT: + retval = usbtmc_ioctl_get_timeout(file_data, + (void __user *)arg); + break; + + case USBTMC_IOCTL_SET_TIMEOUT: + retval = usbtmc_ioctl_set_timeout(file_data, + (void __user *)arg); + break; + case USBTMC488_IOCTL_GET_CAPS: retval = copy_to_user((void __user *)arg, &data->usb488_caps, diff --git a/include/uapi/linux/usb/tmc.h b/include/uapi/linux/usb/tmc.h index 03f6adc8f35b..a89ffc33532e 100644 --- a/include/uapi/linux/usb/tmc.h +++ b/include/uapi/linux/usb/tmc.h @@ -16,6 +16,8 @@ #ifndef __LINUX_USB_TMC_H #define __LINUX_USB_TMC_H +#include /* __u8 etc */ + /* USB TMC status values */ #define USBTMC_STATUS_SUCCESS 0x01 #define USBTMC_STATUS_PENDING 0x02 @@ -46,6 +48,8 @@ #define USBTMC_IOCTL_ABORT_BULK_IN _IO(USBTMC_IOC_NR, 4) #define USBTMC_IOCTL_CLEAR_OUT_HALT _IO(USBTMC_IOC_NR, 6) #define USBTMC_IOCTL_CLEAR_IN_HALT _IO(USBTMC_IOC_NR, 7) +#define USBTMC_IOCTL_GET_TIMEOUT _IOR(USBTMC_IOC_NR, 9, __u32) +#define USBTMC_IOCTL_SET_TIMEOUT _IOW(USBTMC_IOC_NR, 10, __u32) #define USBTMC488_IOCTL_GET_CAPS _IOR(USBTMC_IOC_NR, 17, unsigned char) #define USBTMC488_IOCTL_READ_STB _IOR(USBTMC_IOC_NR, 18, unsigned char) #define USBTMC488_IOCTL_REN_CONTROL _IOW(USBTMC_IOC_NR, 19, unsigned char) -- cgit v1.2.3 From fe78a7c637057070f20ac9460608a18d775e6349 Mon Sep 17 00:00:00 2001 From: Guido Kiener Date: Wed, 18 Jul 2018 10:45:37 +0200 Subject: usb: usbtmc: Add ioctl for trigger add USBTMC488_IOCTL_TRIGGER to send TRIGGER Bulk-OUT header according to Subclass USB488 Specification The usbtmc trigger command is equivalent to the IEEE 488 GET (Group Execute Trigger) action. While the "*TRG" command can be sent as data to perform the same operation, in some situations an instrument will be busy and unable to process the data immediately in which case the USBTMC488_IOCTL_TRIGGER can be used to trigger the instrument with lower latency. Reviewed-by: Steve Bayless Tested-by: Dave Penkler Signed-off-by: Dave Penkler Signed-off-by: Guido Kiener Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 49 ++++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/usb/tmc.h | 1 + 2 files changed, 50 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 36d740c4c6fb..38fc7abdc00c 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -557,6 +557,51 @@ static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data, return rv; } +/* + * Sends a TRIGGER Bulk-OUT command message + * See the USBTMC-USB488 specification, Table 2. + * + * Also updates bTag_last_write. + */ +static int usbtmc488_ioctl_trigger(struct usbtmc_file_data *file_data) +{ + struct usbtmc_device_data *data = file_data->data; + int retval; + u8 *buffer; + int actual; + + buffer = kzalloc(USBTMC_HEADER_SIZE, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer[0] = 128; + buffer[1] = data->bTag; + buffer[2] = ~data->bTag; + + retval = usb_bulk_msg(data->usb_dev, + usb_sndbulkpipe(data->usb_dev, + data->bulk_out), + buffer, USBTMC_HEADER_SIZE, + &actual, file_data->timeout); + + /* Store bTag (in case we need to abort) */ + data->bTag_last_write = data->bTag; + + /* Increment bTag -- and increment again if zero */ + data->bTag++; + if (!data->bTag) + data->bTag++; + + kfree(buffer); + if (retval < 0) { + dev_err(&data->intf->dev, "%s returned %d\n", + __func__, retval); + return retval; + } + + return 0; +} + /* * Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-OUT endpoint. * @transfer_size: number of bytes to request from the device. @@ -1309,6 +1354,10 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) retval = usbtmc488_ioctl_simple(data, (void __user *)arg, USBTMC488_REQUEST_LOCAL_LOCKOUT); break; + + case USBTMC488_IOCTL_TRIGGER: + retval = usbtmc488_ioctl_trigger(file_data); + break; } skip_io_on_zombie: diff --git a/include/uapi/linux/usb/tmc.h b/include/uapi/linux/usb/tmc.h index a89ffc33532e..c61bad7150dd 100644 --- a/include/uapi/linux/usb/tmc.h +++ b/include/uapi/linux/usb/tmc.h @@ -55,6 +55,7 @@ #define USBTMC488_IOCTL_REN_CONTROL _IOW(USBTMC_IOC_NR, 19, unsigned char) #define USBTMC488_IOCTL_GOTO_LOCAL _IO(USBTMC_IOC_NR, 20) #define USBTMC488_IOCTL_LOCAL_LOCKOUT _IO(USBTMC_IOC_NR, 21) +#define USBTMC488_IOCTL_TRIGGER _IO(USBTMC_IOC_NR, 22) /* Driver encoded usb488 capabilities */ #define USBTMC488_CAPABILITY_TRIGGER 1 -- cgit v1.2.3 From fbd83971f9429849dd3a105b663822d15b7b992b Mon Sep 17 00:00:00 2001 From: Guido Kiener Date: Wed, 18 Jul 2018 10:45:38 +0200 Subject: usb: usbtmc: Add ioctl for EOM bit add USBTMC_IOCTL_EOM_ENABLE to specify EOM bit for next write() call. Sets Bit 0 of field 'bmTransferAttributes' of DEV_DEP_MSG_OUT Bulk-OUT Header. Allows fine grained control over end of message handling on a per file descriptor basis. Reviewed-by: Steve Bayless Tested-by: Dave Penkler Signed-off-by: Dave Penkler Signed-off-by: Guido Kiener Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 28 +++++++++++++++++++++++++++- include/uapi/linux/usb/tmc.h | 2 ++ 2 files changed, 29 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 38fc7abdc00c..c77e0ac6260b 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -120,6 +120,7 @@ struct usbtmc_file_data { u32 timeout; u8 srq_byte; atomic_t srq_asserted; + u8 eom_val; }; /* Forward declarations */ @@ -157,6 +158,7 @@ static int usbtmc_open(struct inode *inode, struct file *filp) file_data->data = data; file_data->timeout = USBTMC_TIMEOUT; + file_data->eom_val = 1; INIT_LIST_HEAD(&file_data->file_elem); spin_lock_irq(&data->dev_lock); @@ -855,7 +857,7 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf, buffer[8] = 0; } else { this_part = remaining; - buffer[8] = 1; + buffer[8] = file_data->eom_val; } /* Setup IO buffer for DEV_DEP_MSG_OUT message */ @@ -1277,6 +1279,25 @@ static int usbtmc_ioctl_set_timeout(struct usbtmc_file_data *file_data, return 0; } +/* + * enables/disables sending EOM on write + */ +static int usbtmc_ioctl_eom_enable(struct usbtmc_file_data *file_data, + void __user *arg) +{ + u8 eom_enable; + + if (copy_from_user(&eom_enable, arg, sizeof(eom_enable))) + return -EFAULT; + + if (eom_enable > 1) + return -EINVAL; + + file_data->eom_val = eom_enable; + + return 0; +} + static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct usbtmc_file_data *file_data; @@ -1327,6 +1348,11 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) (void __user *)arg); break; + case USBTMC_IOCTL_EOM_ENABLE: + retval = usbtmc_ioctl_eom_enable(file_data, + (void __user *)arg); + break; + case USBTMC488_IOCTL_GET_CAPS: retval = copy_to_user((void __user *)arg, &data->usb488_caps, diff --git a/include/uapi/linux/usb/tmc.h b/include/uapi/linux/usb/tmc.h index c61bad7150dd..e7317dfdd2ae 100644 --- a/include/uapi/linux/usb/tmc.h +++ b/include/uapi/linux/usb/tmc.h @@ -50,6 +50,8 @@ #define USBTMC_IOCTL_CLEAR_IN_HALT _IO(USBTMC_IOC_NR, 7) #define USBTMC_IOCTL_GET_TIMEOUT _IOR(USBTMC_IOC_NR, 9, __u32) #define USBTMC_IOCTL_SET_TIMEOUT _IOW(USBTMC_IOC_NR, 10, __u32) +#define USBTMC_IOCTL_EOM_ENABLE _IOW(USBTMC_IOC_NR, 11, __u8) + #define USBTMC488_IOCTL_GET_CAPS _IOR(USBTMC_IOC_NR, 17, unsigned char) #define USBTMC488_IOCTL_READ_STB _IOR(USBTMC_IOC_NR, 18, unsigned char) #define USBTMC488_IOCTL_REN_CONTROL _IOW(USBTMC_IOC_NR, 19, unsigned char) -- cgit v1.2.3 From 12dcaeb77e67c1162a2604f6b589266baec2d1ef Mon Sep 17 00:00:00 2001 From: Guido Kiener Date: Wed, 18 Jul 2018 10:45:39 +0200 Subject: usb: usbtmc: Add ioctl for termination character add USBTMC_IOCTL_CONFIG_TERMCHAR to control TermChar handling for next read(). Controls field 'TermChar' and Bit 1 of field 'bmTransferAttributes' of REQUEST_DEV_DEP_MSG_IN BULK-OUT header. Allows enabling/disabling of terminating a read on reception of term_char individually for each read request. Reviewed-by: Steve Bayless Tested-by: Dave Penkler Signed-off-by: Dave Penkler Signed-off-by: Guido Kiener Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 36 ++++++++++++++++++++++++++++++++++-- include/uapi/linux/usb/tmc.h | 6 ++++++ 2 files changed, 40 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index c77e0ac6260b..1b7b2e402adb 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -121,6 +121,8 @@ struct usbtmc_file_data { u8 srq_byte; atomic_t srq_asserted; u8 eom_val; + u8 term_char; + bool term_char_enabled; }; /* Forward declarations */ @@ -157,7 +159,10 @@ static int usbtmc_open(struct inode *inode, struct file *filp) mutex_lock(&data->io_mutex); file_data->data = data; + /* copy default values from device settings */ file_data->timeout = USBTMC_TIMEOUT; + file_data->term_char = data->TermChar; + file_data->term_char_enabled = data->TermCharEnabled; file_data->eom_val = 1; INIT_LIST_HEAD(&file_data->file_elem); @@ -634,9 +639,9 @@ static int send_request_dev_dep_msg_in(struct usbtmc_file_data *file_data, buffer[5] = transfer_size >> 8; buffer[6] = transfer_size >> 16; buffer[7] = transfer_size >> 24; - buffer[8] = data->TermCharEnabled * 2; + buffer[8] = file_data->term_char_enabled * 2; /* Use term character? */ - buffer[9] = data->TermChar; + buffer[9] = file_data->term_char; buffer[10] = 0; /* Reserved */ buffer[11] = 0; /* Reserved */ @@ -1298,6 +1303,28 @@ static int usbtmc_ioctl_eom_enable(struct usbtmc_file_data *file_data, return 0; } +/* + * Configure termination character for read() + */ +static int usbtmc_ioctl_config_termc(struct usbtmc_file_data *file_data, + void __user *arg) +{ + struct usbtmc_termchar termc; + + if (copy_from_user(&termc, arg, sizeof(termc))) + return -EFAULT; + + if ((termc.term_char_enabled > 1) || + (termc.term_char_enabled && + !(file_data->data->capabilities.device_capabilities & 1))) + return -EINVAL; + + file_data->term_char = termc.term_char; + file_data->term_char_enabled = termc.term_char_enabled; + + return 0; +} + static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct usbtmc_file_data *file_data; @@ -1353,6 +1380,11 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) (void __user *)arg); break; + case USBTMC_IOCTL_CONFIG_TERMCHAR: + retval = usbtmc_ioctl_config_termc(file_data, + (void __user *)arg); + break; + case USBTMC488_IOCTL_GET_CAPS: retval = copy_to_user((void __user *)arg, &data->usb488_caps, diff --git a/include/uapi/linux/usb/tmc.h b/include/uapi/linux/usb/tmc.h index e7317dfdd2ae..729af2f861a4 100644 --- a/include/uapi/linux/usb/tmc.h +++ b/include/uapi/linux/usb/tmc.h @@ -40,6 +40,11 @@ #define USBTMC488_REQUEST_GOTO_LOCAL 161 #define USBTMC488_REQUEST_LOCAL_LOCKOUT 162 +struct usbtmc_termchar { + __u8 term_char; + __u8 term_char_enabled; +} __attribute__ ((packed)); + /* Request values for USBTMC driver's ioctl entry point */ #define USBTMC_IOC_NR 91 #define USBTMC_IOCTL_INDICATOR_PULSE _IO(USBTMC_IOC_NR, 1) @@ -51,6 +56,7 @@ #define USBTMC_IOCTL_GET_TIMEOUT _IOR(USBTMC_IOC_NR, 9, __u32) #define USBTMC_IOCTL_SET_TIMEOUT _IOW(USBTMC_IOC_NR, 10, __u32) #define USBTMC_IOCTL_EOM_ENABLE _IOW(USBTMC_IOC_NR, 11, __u8) +#define USBTMC_IOCTL_CONFIG_TERMCHAR _IOW(USBTMC_IOC_NR, 12, struct usbtmc_termchar) #define USBTMC488_IOCTL_GET_CAPS _IOR(USBTMC_IOC_NR, 17, unsigned char) #define USBTMC488_IOCTL_READ_STB _IOR(USBTMC_IOC_NR, 18, unsigned char) -- cgit v1.2.3 From 60207c8ef2a3a971567dd1207daa234aaa856faa Mon Sep 17 00:00:00 2001 From: Guido Kiener Date: Tue, 24 Jul 2018 11:05:28 +0200 Subject: usb: usbtmc: Add support for 32 bit compat applications 32 bit applications can only call ioctl functions on 64 bit systems when the field .compat_ioctl is defined for file operations. Tested-by: Dave Penkler Signed-off-by: Guido Kiener Reviewed-by: Steve Bayless Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 1b7b2e402adb..83ffa5a14c3d 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -1459,6 +1460,9 @@ static const struct file_operations fops = { .open = usbtmc_open, .release = usbtmc_release, .unlocked_ioctl = usbtmc_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = usbtmc_ioctl, +#endif .fasync = usbtmc_fasync, .poll = usbtmc_poll, .llseek = default_llseek, -- cgit v1.2.3 From d396e47fb558a819226955ce5db0149fde88da0f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 21 May 2018 11:28:51 +0300 Subject: usb: gadget: uvc: Move userspace API definition to public header The UVC gadget userspace API (V4L2 events and custom ioctls) is defined in a header internal to the kernel. Move it to a new public header to make it accessible to userspace. The UVC_INTF_CONTROL and UVC_INTF_STREAMING macros are not used, so remove them in the process. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- MAINTAINERS | 1 + drivers/usb/gadget/function/uvc.h | 45 +++++---------------------------------- include/uapi/linux/usb/g_uvc.h | 39 +++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 40 deletions(-) create mode 100644 include/uapi/linux/usb/g_uvc.h (limited to 'drivers/usb') diff --git a/MAINTAINERS b/MAINTAINERS index 0fe4228f78cb..37035a0c7522 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14996,6 +14996,7 @@ L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/gadget/function/*uvc* F: drivers/usb/gadget/legacy/webcam.c +F: include/uapi/linux/usb/g_uvc.h USB WIRELESS RNDIS DRIVER (rndis_wlan) M: Jussi Kivilinna diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index a64e07e61f8c..053e4b72039d 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -9,52 +9,20 @@ #ifndef _UVC_GADGET_H_ #define _UVC_GADGET_H_ -#include -#include -#include - -#define UVC_EVENT_FIRST (V4L2_EVENT_PRIVATE_START + 0) -#define UVC_EVENT_CONNECT (V4L2_EVENT_PRIVATE_START + 0) -#define UVC_EVENT_DISCONNECT (V4L2_EVENT_PRIVATE_START + 1) -#define UVC_EVENT_STREAMON (V4L2_EVENT_PRIVATE_START + 2) -#define UVC_EVENT_STREAMOFF (V4L2_EVENT_PRIVATE_START + 3) -#define UVC_EVENT_SETUP (V4L2_EVENT_PRIVATE_START + 4) -#define UVC_EVENT_DATA (V4L2_EVENT_PRIVATE_START + 5) -#define UVC_EVENT_LAST (V4L2_EVENT_PRIVATE_START + 5) - -struct uvc_request_data { - __s32 length; - __u8 data[60]; -}; - -struct uvc_event { - union { - enum usb_device_speed speed; - struct usb_ctrlrequest req; - struct uvc_request_data data; - }; -}; - -#define UVCIOC_SEND_RESPONSE _IOW('U', 1, struct uvc_request_data) - -#define UVC_INTF_CONTROL 0 -#define UVC_INTF_STREAMING 1 - -/* ------------------------------------------------------------------------ - * Debugging, printing and logging - */ - -#ifdef __KERNEL__ - #include /* For usb_endpoint_* */ #include #include +#include #include #include #include #include "uvc_queue.h" +/* ------------------------------------------------------------------------ + * Debugging, printing and logging + */ + #define UVC_TRACE_PROBE (1 << 0) #define UVC_TRACE_DESCR (1 << 1) #define UVC_TRACE_CONTROL (1 << 2) @@ -184,7 +152,4 @@ extern void uvc_endpoint_stream(struct uvc_device *dev); extern void uvc_function_connect(struct uvc_device *uvc); extern void uvc_function_disconnect(struct uvc_device *uvc); -#endif /* __KERNEL__ */ - #endif /* _UVC_GADGET_H_ */ - diff --git a/include/uapi/linux/usb/g_uvc.h b/include/uapi/linux/usb/g_uvc.h new file mode 100644 index 000000000000..3c9ee3020cbb --- /dev/null +++ b/include/uapi/linux/usb/g_uvc.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * g_uvc.h -- USB Video Class Gadget driver API + * + * Copyright (C) 2009-2010 Laurent Pinchart + */ + +#ifndef __LINUX_USB_G_UVC_H +#define __LINUX_USB_G_UVC_H + +#include +#include +#include + +#define UVC_EVENT_FIRST (V4L2_EVENT_PRIVATE_START + 0) +#define UVC_EVENT_CONNECT (V4L2_EVENT_PRIVATE_START + 0) +#define UVC_EVENT_DISCONNECT (V4L2_EVENT_PRIVATE_START + 1) +#define UVC_EVENT_STREAMON (V4L2_EVENT_PRIVATE_START + 2) +#define UVC_EVENT_STREAMOFF (V4L2_EVENT_PRIVATE_START + 3) +#define UVC_EVENT_SETUP (V4L2_EVENT_PRIVATE_START + 4) +#define UVC_EVENT_DATA (V4L2_EVENT_PRIVATE_START + 5) +#define UVC_EVENT_LAST (V4L2_EVENT_PRIVATE_START + 5) + +struct uvc_request_data { + __s32 length; + __u8 data[60]; +}; + +struct uvc_event { + union { + enum usb_device_speed speed; + struct usb_ctrlrequest req; + struct uvc_request_data data; + }; +}; + +#define UVCIOC_SEND_RESPONSE _IOW('U', 1, struct uvc_request_data) + +#endif /* __LINUX_USB_G_UVC_H */ -- cgit v1.2.3 From 284eb1663bed1fe0934d60755443504aeea011f8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 21 May 2018 11:28:52 +0300 Subject: usb: gadget: uvc: Minimize #include in headers In order to speed up compilation, only include the headers that are strictly required within other headers. To that end, use forward structure declaration and move #include statements to .c file as appropriate. While at it, sort headers alphabetically, and remove unneeded __KERNEL__ guards. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 5 +++-- drivers/usb/gadget/function/f_uvc.h | 6 +----- drivers/usb/gadget/function/u_uvc.h | 1 + drivers/usb/gadget/function/uvc.h | 14 ++++++++++---- drivers/usb/gadget/function/uvc_queue.h | 12 ++++++------ drivers/usb/gadget/function/uvc_v4l2.c | 3 ++- drivers/usb/gadget/function/uvc_video.h | 2 ++ 7 files changed, 25 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 439eba660e95..54f04d321829 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -6,16 +6,17 @@ * Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ -#include -#include #include #include #include +#include #include +#include #include #include #include #include +#include #include #include #include diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h index 81defe4557fe..a81a17765558 100644 --- a/drivers/usb/gadget/function/f_uvc.h +++ b/drivers/usb/gadget/function/f_uvc.h @@ -9,10 +9,7 @@ #ifndef _F_UVC_H_ #define _F_UVC_H_ -#include -#include - -#include "uvc.h" +struct uvc_device; void uvc_function_setup_continue(struct uvc_device *uvc); @@ -21,4 +18,3 @@ void uvc_function_connect(struct uvc_device *uvc); void uvc_function_disconnect(struct uvc_device *uvc); #endif /* _F_UVC_H_ */ - diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index d00d3ded71c0..a6fdde6b162b 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -13,6 +13,7 @@ #ifndef U_UVC_H #define U_UVC_H +#include #include #include diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 053e4b72039d..93cf78b420fe 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -9,16 +9,22 @@ #ifndef _UVC_GADGET_H_ #define _UVC_GADGET_H_ -#include /* For usb_endpoint_* */ +#include +#include +#include #include -#include -#include #include -#include + #include +#include +#include #include "uvc_queue.h" +struct usb_ep; +struct usb_request; +struct uvc_descriptor_header; + /* ------------------------------------------------------------------------ * Debugging, printing and logging */ diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h index f9f65b5c1062..2f0fff769843 100644 --- a/drivers/usb/gadget/function/uvc_queue.h +++ b/drivers/usb/gadget/function/uvc_queue.h @@ -2,13 +2,15 @@ #ifndef _UVC_QUEUE_H_ #define _UVC_QUEUE_H_ -#ifdef __KERNEL__ - -#include +#include #include -#include +#include + #include +struct file; +struct mutex; + /* Maximum frame size in bytes, for sanity checking. */ #define UVC_MAX_FRAME_SIZE (16*1024*1024) /* Maximum number of video buffers. */ @@ -91,7 +93,5 @@ struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue); -#endif /* __KERNEL__ */ - #endif /* _UVC_QUEUE_H_ */ diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 9a9019625496..7f1ca3b57823 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -6,10 +6,11 @@ * Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ -#include #include #include +#include #include +#include #include #include #include diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h index 6c20aa75f966..7d77122b0ff9 100644 --- a/drivers/usb/gadget/function/uvc_video.h +++ b/drivers/usb/gadget/function/uvc_video.h @@ -12,6 +12,8 @@ #ifndef __UVC_VIDEO_H__ #define __UVC_VIDEO_H__ +struct uvc_video; + int uvcg_video_pump(struct uvc_video *video); int uvcg_video_enable(struct uvc_video *video, int enable); -- cgit v1.2.3 From 20970d823a17f87aa4105febc181e5ee823dbe25 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 21 May 2018 11:28:53 +0300 Subject: usb: gadget: uvc: Move trace parameter to function module The trace module parameter controls output of debugging messages in the UVC function driver. Move it from the webcam module to the UVC function module where it belongs. This allows ConfigFS-based UVC gadgets to control tracing. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 2 ++ drivers/usb/gadget/function/u_uvc.h | 4 ---- drivers/usb/gadget/legacy/webcam.c | 4 ---- 3 files changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 54f04d321829..1d2feac5c532 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -31,6 +31,8 @@ #include "uvc_video.h" unsigned int uvc_gadget_trace_param; +module_param_named(trace, uvc_gadget_trace_param, uint, 0644); +MODULE_PARM_DESC(trace, "Trace level bitmask"); /* -------------------------------------------------------------------------- * Function descriptors diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index a6fdde6b162b..2ed292e94fbc 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -21,7 +21,6 @@ struct f_uvc_opts { struct usb_function_instance func_inst; - unsigned int uvc_gadget_trace_param; unsigned int streaming_interval; unsigned int streaming_maxpacket; unsigned int streaming_maxburst; @@ -81,7 +80,4 @@ struct f_uvc_opts { int refcnt; }; -void uvc_set_trace_param(unsigned int trace); - #endif /* U_UVC_H */ - diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c index 6b86568c9157..a9f8eb8e1c76 100644 --- a/drivers/usb/gadget/legacy/webcam.c +++ b/drivers/usb/gadget/legacy/webcam.c @@ -30,9 +30,6 @@ static unsigned int streaming_maxburst; module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); -static unsigned int trace; -module_param(trace, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(trace, "Trace level bitmask"); /* -------------------------------------------------------------------------- * Device descriptor */ @@ -379,7 +376,6 @@ webcam_bind(struct usb_composite_dev *cdev) uvc_opts->streaming_interval = streaming_interval; uvc_opts->streaming_maxpacket = streaming_maxpacket; uvc_opts->streaming_maxburst = streaming_maxburst; - uvc_set_trace_param(trace); uvc_opts->fs_control = uvc_fs_control_cls; uvc_opts->ss_control = uvc_ss_control_cls; -- cgit v1.2.3 From 8c45fbcd1f14f472f1da1ae72a5b8354c03f373a Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 22 May 2018 10:05:05 +0100 Subject: usb: gadget: tcm: fix spelling mistake: "Manufactor" -> "Manufacturer" Trivial fix to spelling mistake in usbg_us_strings array Signed-off-by: Colin Ian King Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/tcm_usb_gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c index 682bf99dcf76..40870227999a 100644 --- a/drivers/usb/gadget/legacy/tcm_usb_gadget.c +++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c @@ -41,7 +41,7 @@ static struct usb_device_descriptor usbg_device_desc = { #define USB_G_STR_CONFIG USB_GADGET_FIRST_AVAIL_IDX static struct usb_string usbg_us_strings[] = { - [USB_GADGET_MANUFACTURER_IDX].s = "Target Manufactor", + [USB_GADGET_MANUFACTURER_IDX].s = "Target Manufacturer", [USB_GADGET_PRODUCT_IDX].s = "Target Product", [USB_GADGET_SERIAL_IDX].s = "000000000001", [USB_G_STR_CONFIG].s = "default config", -- cgit v1.2.3 From 0f548098af0f1a7e58e2bb9d8e1e994312c13a0f Mon Sep 17 00:00:00 2001 From: Gevorg Sahakyan Date: Wed, 23 May 2018 11:57:27 +0400 Subject: usb: dwc2: Move dwc2_readl/writel functions after hsotg structure Moved dwc2_readl/writel functions after hsotg declaration for adding hsotg structure to dwc2_readl/writel function prototypes. Acked-by: Minas Harutyunyan Signed-off-by: Gevorg Sahakyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 108 ++++++++++++++++++++++++------------------------ 1 file changed, 54 insertions(+), 54 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 71b3b08ad516..8a9272a2c82c 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -65,60 +65,6 @@ DWC2_TRACE_SCHEDULER_VB(pr_fmt("%s: SCH: " fmt), \ dev_name(hsotg->dev), ##__VA_ARGS__) -#ifdef CONFIG_MIPS -/* - * There are some MIPS machines that can run in either big-endian - * or little-endian mode and that use the dwc2 register without - * a byteswap in both ways. - * Unlike other architectures, MIPS apparently does not require a - * barrier before the __raw_writel() to synchronize with DMA but does - * require the barrier after the __raw_writel() to serialize a set of - * writes. This set of operations was added specifically for MIPS and - * should only be used there. - */ -static inline u32 dwc2_readl(const void __iomem *addr) -{ - u32 value = __raw_readl(addr); - - /* In order to preserve endianness __raw_* operation is used. Therefore - * a barrier is needed to ensure IO access is not re-ordered across - * reads or writes - */ - mb(); - return value; -} - -static inline void dwc2_writel(u32 value, void __iomem *addr) -{ - __raw_writel(value, addr); - - /* - * In order to preserve endianness __raw_* operation is used. Therefore - * a barrier is needed to ensure IO access is not re-ordered across - * reads or writes - */ - mb(); -#ifdef DWC2_LOG_WRITES - pr_info("INFO:: wrote %08x to %p\n", value, addr); -#endif -} -#else -/* Normal architectures just use readl/write */ -static inline u32 dwc2_readl(const void __iomem *addr) -{ - return readl(addr); -} - -static inline void dwc2_writel(u32 value, void __iomem *addr) -{ - writel(value, addr); - -#ifdef DWC2_LOG_WRITES - pr_info("info:: wrote %08x to %p\n", value, addr); -#endif -} -#endif - /* Maximum number of Endpoints/HostChannels */ #define MAX_EPS_CHANNELS 16 @@ -1215,6 +1161,60 @@ struct dwc2_hsotg { #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */ }; +#ifdef CONFIG_MIPS +/* + * There are some MIPS machines that can run in either big-endian + * or little-endian mode and that use the dwc2 register without + * a byteswap in both ways. + * Unlike other architectures, MIPS apparently does not require a + * barrier before the __raw_writel() to synchronize with DMA but does + * require the barrier after the __raw_writel() to serialize a set of + * writes. This set of operations was added specifically for MIPS and + * should only be used there. + */ +static inline u32 dwc2_readl(const void __iomem *addr) +{ + u32 value = __raw_readl(addr); + + /* In order to preserve endianness __raw_* operation is used. Therefore + * a barrier is needed to ensure IO access is not re-ordered across + * reads or writes + */ + mb(); + return value; +} + +static inline void dwc2_writel(u32 value, void __iomem *addr) +{ + __raw_writel(value, addr); + + /* + * In order to preserve endianness __raw_* operation is used. Therefore + * a barrier is needed to ensure IO access is not re-ordered across + * reads or writes + */ + mb(); +#ifdef DWC2_LOG_WRITES + pr_info("INFO:: wrote %08x to %p\n", value, addr); +#endif +} +#else +/* Normal architectures just use readl/write */ +static inline u32 dwc2_readl(const void __iomem *addr) +{ + return readl(addr); +} + +static inline void dwc2_writel(u32 value, void __iomem *addr) +{ + writel(value, addr); + +#ifdef DWC2_LOG_WRITES + pr_info("info:: wrote %08x to %p\n", value, addr); +#endif +} +#endif + /* Reasons for halting a host channel */ enum dwc2_halt_status { DWC2_HC_XFER_NO_HALT_STATUS, -- cgit v1.2.3 From c728effd56d35c12774210f0732b8eb8b7ce7cd9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 23 May 2018 18:49:43 +0300 Subject: usb: gadget: uvc: configfs: Move function to avoid forward declaration The to_f_uvc_opts() function is forward-declared without needing to, as its definition can simply be moved up in the file. Fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_configfs.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index c9b8cc4aae5a..b51f0d278826 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -31,7 +31,11 @@ static struct configfs_attribute prefix##attr_##cname = { \ .show = prefix##cname##_show, \ } -static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item); +static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_uvc_opts, + func_inst.group); +} /* control/header/ */ DECLARE_UVC_HEADER_DESCRIPTOR(1); @@ -2105,12 +2109,6 @@ static const struct config_item_type uvcg_streaming_grp_type = { .ct_owner = THIS_MODULE, }; -static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item) -{ - return container_of(to_config_group(item), struct f_uvc_opts, - func_inst.group); -} - static void uvc_attr_release(struct config_item *item) { struct f_uvc_opts *opts = to_f_uvc_opts(item); -- cgit v1.2.3 From d7af78b9245545908c90bce2206f200a4250b5db Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Thu, 24 May 2018 17:16:12 +0100 Subject: usb: gadget: uvc: Expose configuration name through video node When utilising multiple instantiations of a UVC gadget on a composite device, there is no clear method to link a particular configuration to its respective video node. Provide a means for identifying the correct video node by exposing the name of the function configuration through sysfs. Reviewed-by: Laurent Pinchart Signed-off-by: Kieran Bingham Signed-off-by: Felipe Balbi --- Documentation/ABI/testing/configfs-usb-gadget-uvc | 5 +++++ drivers/usb/gadget/function/f_uvc.c | 24 ++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc index 1ba0d0fda9c0..9281e2aa38df 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uvc +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc @@ -263,3 +263,8 @@ Description: Specific streaming header descriptors is connected bmInfo - capabilities of this video streaming interface + +What: /sys/class/udc/udc.name/device/gadget/video4linux/video.name/function_name +Date: May 2018 +KernelVersion: 4.19 +Description: UVC configfs function instance name diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 1d2feac5c532..d8ce7868fe22 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -413,10 +413,21 @@ uvc_function_disconnect(struct uvc_device *uvc) * USB probe and disconnect */ +static ssize_t function_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uvc_device *uvc = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", uvc->func.fi->group.cg_item.ci_name); +} + +static DEVICE_ATTR_RO(function_name); + static int uvc_register_video(struct uvc_device *uvc) { struct usb_composite_dev *cdev = uvc->func.config->cdev; + int ret; /* TODO reference counting. */ uvc->vdev.v4l2_dev = &uvc->v4l2_dev; @@ -429,7 +440,17 @@ uvc_register_video(struct uvc_device *uvc) video_set_drvdata(&uvc->vdev, uvc); - return video_register_device(&uvc->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&uvc->vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) + return ret; + + ret = device_create_file(&uvc->vdev.dev, &dev_attr_function_name); + if (ret < 0) { + video_unregister_device(&uvc->vdev); + return ret; + } + + return 0; } #define UVC_COPY_DESCRIPTOR(mem, dst, desc) \ @@ -867,6 +888,7 @@ static void uvc_unbind(struct usb_configuration *c, struct usb_function *f) INFO(cdev, "%s\n", __func__); + device_remove_file(&uvc->vdev.dev, &dev_attr_function_name); video_unregister_device(&uvc->vdev); v4l2_device_unregister(&uvc->v4l2_dev); -- cgit v1.2.3 From e610257e462f6104792d22fbb0927724df3bc401 Mon Sep 17 00:00:00 2001 From: Jerry Zhang Date: Mon, 16 Apr 2018 18:17:40 -0700 Subject: usb: gadget: f_fs: Only return delayed status when len is 0 Commit 1b9ba000 ("Allow function drivers to pause control transfers") states that USB_GADGET_DELAYED_STATUS is only supported if data phase is 0 bytes. It seems that when the length is not 0 bytes, there is no need to explicitly delay the data stage since the transfer is not completed until the user responds. However, when the length is 0, there is no data stage and the transfer is finished once setup() returns, hence there is a need to explicitly delay completion. This manifests as the following bugs: Prior to 946ef68ad4e4 ('Let setup() return USB_GADGET_DELAYED_STATUS'), when setup is 0 bytes, ffs would require user to queue a 0 byte request in order to clear setup state. However, that 0 byte request was actually not needed and would hang and cause errors in other setup requests. After the above commit, 0 byte setups work since the gadget now accepts empty queues to ep0 to clear the delay, but all other setups hang. Fixes: 946ef68ad4e4 ("Let setup() return USB_GADGET_DELAYED_STATUS") Signed-off-by: Jerry Zhang Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 33e2030503fa..3ada83d81bda 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -3263,7 +3263,7 @@ static int ffs_func_setup(struct usb_function *f, __ffs_event_add(ffs, FUNCTIONFS_SETUP); spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags); - return USB_GADGET_DELAYED_STATUS; + return creq->wLength == 0 ? USB_GADGET_DELAYED_STATUS : 0; } static bool ffs_func_req_match(struct usb_function *f, -- cgit v1.2.3 From 3fe314ca8c970aefc2d2a96dd93df6de1f4f1a4b Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Tue, 12 Jun 2018 14:26:43 -0700 Subject: usb: dwc3: Add a glue driver for Synopsys HAPS platform This driver is to be used for Synopsys PCIe-base HAPS platform. Move the the HAPS support from dwc3-pci to this driver. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/Kconfig | 13 ++-- drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-haps.c | 137 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/dwc3/dwc3-pci.c | 30 ---------- 4 files changed, 147 insertions(+), 34 deletions(-) create mode 100644 drivers/usb/dwc3/dwc3-haps.c (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 451012ea1294..518ead12458d 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -74,11 +74,16 @@ config USB_DWC3_PCI depends on USB_PCI && ACPI default USB_DWC3 help - If you're using the DesignWare Core IP with a PCIe, please say - 'Y' or 'M' here. + If you're using the DesignWare Core IP with a PCIe (but not HAPS + platform), please say 'Y' or 'M' here. - One such PCIe-based platform is Synopsys' PCIe HAPS model of - this IP. +config USB_DWC3_HAPS + tristate "Synopsys PCIe-based HAPS Platforms" + depends on USB_PCI + default USB_DWC3 + help + If you're using the DesignWare Core IP with a Synopsys PCIe HAPS + platform, please say 'Y' or 'M' here. config USB_DWC3_KEYSTONE tristate "Texas Instruments Keystone2 Platforms" diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 5c07d8f925e0..6e3ef6144e5d 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -45,6 +45,7 @@ endif obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o +obj-$(CONFIG_USB_DWC3_HAPS) += dwc3-haps.o obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o diff --git a/drivers/usb/dwc3/dwc3-haps.c b/drivers/usb/dwc3/dwc3-haps.c new file mode 100644 index 000000000000..c9cc33881bef --- /dev/null +++ b/drivers/usb/dwc3/dwc3-haps.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * dwc3-haps.c - Synopsys HAPS PCI Specific glue layer + * + * Copyright (C) 2018 Synopsys, Inc. + * + * Authors: Thinh Nguyen , + * John Youn + */ + +#include +#include +#include +#include +#include +#include + +#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd +#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI 0xabce +#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31 0xabcf + +/** + * struct dwc3_haps - Driver private structure + * @dwc3: child dwc3 platform_device + * @pci: our link to PCI bus + */ +struct dwc3_haps { + struct platform_device *dwc3; + struct pci_dev *pci; +}; + +static const struct property_entry initial_properties[] = { + PROPERTY_ENTRY_BOOL("snps,usb3_lpm_capable"), + PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"), + PROPERTY_ENTRY_BOOL("snps,dis_enblslpm_quirk"), + PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"), + { }, +}; + +static int dwc3_haps_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + struct dwc3_haps *dwc; + struct device *dev = &pci->dev; + struct resource res[2]; + int ret; + + ret = pcim_enable_device(pci); + if (ret) { + dev_err(dev, "failed to enable pci device\n"); + return -ENODEV; + } + + pci_set_master(pci); + + dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL); + if (!dwc) + return -ENOMEM; + + dwc->dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO); + if (!dwc->dwc3) + return -ENOMEM; + + memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res)); + + res[0].start = pci_resource_start(pci, 0); + res[0].end = pci_resource_end(pci, 0); + res[0].name = "dwc_usb3"; + res[0].flags = IORESOURCE_MEM; + + res[1].start = pci->irq; + res[1].name = "dwc_usb3"; + res[1].flags = IORESOURCE_IRQ; + + ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res)); + if (ret) { + dev_err(dev, "couldn't add resources to dwc3 device\n"); + goto err; + } + + dwc->pci = pci; + dwc->dwc3->dev.parent = dev; + + ret = platform_device_add_properties(dwc->dwc3, initial_properties); + if (ret) + goto err; + + ret = platform_device_add(dwc->dwc3); + if (ret) { + dev_err(dev, "failed to register dwc3 device\n"); + goto err; + } + + pci_set_drvdata(pci, dwc); + + return 0; +err: + platform_device_put(dwc->dwc3); + return ret; +} + +static void dwc3_haps_remove(struct pci_dev *pci) +{ + struct dwc3_haps *dwc = pci_get_drvdata(pci); + + platform_device_unregister(dwc->dwc3); +} + +static const struct pci_device_id dwc3_haps_id_table[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, + PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3), + }, + { + PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, + PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI), + }, + { + PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, + PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31), + }, + { } /* Terminating Entry */ +}; +MODULE_DEVICE_TABLE(pci, dwc3_haps_id_table); + +static struct pci_driver dwc3_haps_driver = { + .name = "dwc3-haps", + .id_table = dwc3_haps_id_table, + .probe = dwc3_haps_probe, + .remove = dwc3_haps_remove, +}; + +MODULE_AUTHOR("Thinh Nguyen "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Synopsys HAPS PCI Glue Layer"); + +module_pci_driver(dwc3_haps_driver); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index f57e7c94b8e5..80f5dc691038 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -19,9 +19,6 @@ #include #include -#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd -#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI 0xabce -#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31 0xabcf #define PCI_DEVICE_ID_INTEL_BYT 0x0f37 #define PCI_DEVICE_ID_INTEL_MRFLD 0x119e #define PCI_DEVICE_ID_INTEL_BSW 0x22b7 @@ -151,21 +148,6 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc) } } - if (pdev->vendor == PCI_VENDOR_ID_SYNOPSYS && - (pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 || - pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI || - pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31)) { - struct property_entry properties[] = { - PROPERTY_ENTRY_BOOL("snps,usb3_lpm_capable"), - PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"), - PROPERTY_ENTRY_BOOL("snps,dis_enblslpm_quirk"), - PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"), - { }, - }; - - return platform_device_add_properties(dwc3, properties); - } - return 0; } @@ -266,18 +248,6 @@ static void dwc3_pci_remove(struct pci_dev *pci) } static const struct pci_device_id dwc3_pci_id_table[] = { - { - PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, - PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3), - }, - { - PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, - PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI), - }, - { - PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, - PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31), - }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), }, -- cgit v1.2.3 From ad22a6663c6312825876fed25fd6d010d4b46db1 Mon Sep 17 00:00:00 2001 From: Parth Y Shah Date: Thu, 14 Jun 2018 12:02:46 +0530 Subject: usb: gadget: configfs: avoid spaces for indentation This fixes the following checkpatch error: ERROR: code indent should use tabs where possible Here, spaces are replaced by a tab in 2 lines. Signed-off-by: Parth Y Shah Signed-off-by: Felipe Balbi --- drivers/usb/gadget/configfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index efba66ca0719..025129942894 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1217,8 +1217,8 @@ static void purge_configs_funcs(struct gadget_info *gi) list_move_tail(&f->list, &cfg->func_list); if (f->unbind) { dev_dbg(&gi->cdev.gadget->dev, - "unbind function '%s'/%p\n", - f->name, f); + "unbind function '%s'/%p\n", + f->name, f); f->unbind(c, f); } } -- cgit v1.2.3 From bf594c1070f5c34a2576a725eef69cba2686b98d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 29 Jun 2018 13:52:20 -0400 Subject: USB: gadget: Document that certain ep operations can be called in interrupt context This documentation patch specifies that certain USB gadget endpoint operations may be called in interrupt context: usb_ep_queue, usb_ep_dequeue, usb_ep_set_halt, usb_ep_clear_halt, usb_ep_set_wedge, usb_ep_fifo_status, and usb_ep_fifo_flush; while others must be called in process context: usb_ep_enable and usb_ep_disable. Signed-off-by: Alan Stern Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/core.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index cab5e4f09924..af88b48c1cea 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -87,6 +87,8 @@ EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit); * configurable, with more generic names like "ep-a". (remember that for * USB, "in" means "towards the USB master".) * + * This routine must be called in process context. + * * returns zero, or a negative error code. */ int usb_ep_enable(struct usb_ep *ep) @@ -119,6 +121,8 @@ EXPORT_SYMBOL_GPL(usb_ep_enable); * gadget drivers must call usb_ep_enable() again before queueing * requests to the endpoint. * + * This routine must be called in process context. + * * returns zero, or a negative error code. */ int usb_ep_disable(struct usb_ep *ep) @@ -241,6 +245,8 @@ EXPORT_SYMBOL_GPL(usb_ep_free_request); * Note that @req's ->complete() callback must never be called from * within usb_ep_queue() as that can create deadlock situations. * + * This routine may be called in interrupt context. + * * Returns zero, or a negative error code. Endpoints that are not enabled * report errors; errors will also be * reported when the usb peripheral is disconnected. @@ -284,6 +290,8 @@ EXPORT_SYMBOL_GPL(usb_ep_queue); * at the head of the queue) except as part of disconnecting from usb. Such * restrictions prevent drivers from supporting configuration changes, * even to configuration zero (a "chapter 9" requirement). + * + * This routine may be called in interrupt context. */ int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req) { @@ -311,6 +319,8 @@ EXPORT_SYMBOL_GPL(usb_ep_dequeue); * current altsetting, see usb_ep_clear_halt(). When switching altsettings, * it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints. * + * This routine may be called in interrupt context. + * * Returns zero, or a negative error code. On success, this call sets * underlying hardware state that blocks data transfers. * Attempts to halt IN endpoints will fail (returning -EAGAIN) if any @@ -336,6 +346,8 @@ EXPORT_SYMBOL_GPL(usb_ep_set_halt); * for endpoints that aren't reconfigured, after clearing any other state * in the endpoint's i/o queue. * + * This routine may be called in interrupt context. + * * Returns zero, or a negative error code. On success, this call clears * the underlying hardware state reflecting endpoint halt and data toggle. * Note that some hardware can't support this request (like pxa2xx_udc), @@ -360,6 +372,8 @@ EXPORT_SYMBOL_GPL(usb_ep_clear_halt); * requests. If the gadget driver clears the halt status, it will * automatically unwedge the endpoint. * + * This routine may be called in interrupt context. + * * Returns zero on success, else negative errno. */ int usb_ep_set_wedge(struct usb_ep *ep) @@ -388,6 +402,8 @@ EXPORT_SYMBOL_GPL(usb_ep_set_wedge); * written OUT to it by the host. Drivers that need precise handling for * fault reporting or recovery may need to use this call. * + * This routine may be called in interrupt context. + * * This returns the number of such bytes in the fifo, or a negative * errno if the endpoint doesn't use a FIFO or doesn't support such * precise handling. @@ -415,6 +431,8 @@ EXPORT_SYMBOL_GPL(usb_ep_fifo_status); * an endpoint fifo after abnormal transaction terminations. The call * must never be used except when endpoint is not being used for any * protocol translation. + * + * This routine may be called in interrupt context. */ void usb_ep_fifo_flush(struct usb_ep *ep) { -- cgit v1.2.3 From ee9a4ae7dcf19bfe13eb7ae5696ea0405de1f089 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sun, 1 Jul 2018 17:35:44 +0200 Subject: usb: usbtest: use irqsave() in USB's complete callback The USB completion callback does not disable interrupts while acquiring the lock. We want to remove the local_irq_disable() invocation from __usb_hcd_giveback_urb() and therefore it is required for the callback handler to disable the interrupts while acquiring the lock. The callback may be invoked either in IRQ or BH context depending on the USB host controller. Use the _irqsave() variant of the locking primitives. Cc: Greg Kroah-Hartman Acked-by: Alan Stern Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/misc/usbtest.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 9e1142b8b91b..c7f82310e73e 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1082,11 +1082,12 @@ static void ctrl_complete(struct urb *urb) struct usb_ctrlrequest *reqp; struct subcase *subcase; int status = urb->status; + unsigned long flags; reqp = (struct usb_ctrlrequest *)urb->setup_packet; subcase = container_of(reqp, struct subcase, setup); - spin_lock(&ctx->lock); + spin_lock_irqsave(&ctx->lock, flags); ctx->count--; ctx->pending--; @@ -1185,7 +1186,7 @@ error: /* signal completion when nothing's queued */ if (ctx->pending == 0) complete(&ctx->complete); - spin_unlock(&ctx->lock); + spin_unlock_irqrestore(&ctx->lock, flags); } static int @@ -1917,8 +1918,9 @@ struct transfer_context { static void complicated_callback(struct urb *urb) { struct transfer_context *ctx = urb->context; + unsigned long flags; - spin_lock(&ctx->lock); + spin_lock_irqsave(&ctx->lock, flags); ctx->count--; ctx->packet_count += urb->number_of_packets; @@ -1958,7 +1960,7 @@ static void complicated_callback(struct urb *urb) complete(&ctx->done); } done: - spin_unlock(&ctx->lock); + spin_unlock_irqrestore(&ctx->lock, flags); } static struct urb *iso_alloc_urb( -- cgit v1.2.3 From 5741022cbdf3616a9def13b15ab5667a36c65706 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Jun 2018 16:01:20 +0200 Subject: usb: dwc3: pci: Add GPIO lookup table on platforms without ACPI GPIO resources Bay Trail / BYT SoCs do not have a builtin device-mode phy, instead they require an external ULPI phy for device-mode. Only some BYT devices have an external phy, but even on those devices device-mode is not working because the dwc3 does not see the phy. The problem is that the ACPI fwnode for the dwc3 does not contain the expected GPIO resources for the GPIOs connected to the chip-select and reset pins of the phy. I've found the workaround which some Android x86 kernels use for this: https://github.com/BORETS24/Kernel-for-Asus-Zenfone-2/blob/master/arch/x86/platform/intel-mid/device_libs/pci/platform_usb_otg.c Which boils down to hardcoding the GPIOs for these devices. The good news it that all boards (*) use the same GPIOs. This commit fixes the ULPI phy not woring by adding a gpiod_lookup_table call which adds a hardcoded mapping for BYT devices. Note that the mapping added by gpiod_add_lookup_table is a fallback mapping, so boards which properly provide GPIO resources in the ACPI firmware-node resources will not use this. *) Except for the first revision of the evalulation-kit, which normal users don't have Signed-off-by: Hans de Goede Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 80f5dc691038..5c175f839efd 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -64,6 +65,15 @@ static const struct acpi_gpio_mapping acpi_dwc3_byt_gpios[] = { { }, }; +static struct gpiod_lookup_table platform_bytcr_gpios = { + .dev_id = "0000:00:16.0", + .table = { + GPIO_LOOKUP("INT33FC:00", 54, "reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 14, "cs", GPIO_ACTIVE_HIGH), + {} + }, +}; + static int dwc3_pci_quirks(struct dwc3_pci *dwc) { struct platform_device *dwc3 = dwc->dwc3; @@ -124,6 +134,13 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc) if (ret) dev_dbg(&pdev->dev, "failed to add mapping table\n"); + /* + * A lot of BYT devices lack ACPI resource entries for + * the GPIOs, add a fallback mapping to the reference + * design GPIOs which all boards seem to use. + */ + gpiod_add_lookup_table(&platform_bytcr_gpios); + /* * These GPIOs will turn on the USB2 PHY. Note that we have to * put the gpio descriptors again here because the phy driver @@ -239,6 +256,7 @@ static void dwc3_pci_remove(struct pci_dev *pci) { struct dwc3_pci *dwc = pci_get_drvdata(pci); + gpiod_remove_lookup_table(&platform_bytcr_gpios); #ifdef CONFIG_PM cancel_work_sync(&dwc->wakeup_work); #endif -- cgit v1.2.3 From 7740d04d901db7fe631b89dc4f695093327e2124 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Jun 2018 16:01:21 +0200 Subject: usb: dwc3: pci: Enable ULPI Refclk on platforms where the firmware does not On some Bay Trail (BYT) systems the firmware does not enable the ULPI Refclk. This commit adds a helper which checks and if necessary enabled the Refclk and calls this helper for BYT machines. Signed-off-by: Hans de Goede Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 5c175f839efd..26829b299482 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -39,6 +39,10 @@ #define PCI_INTEL_BXT_STATE_D0 0 #define PCI_INTEL_BXT_STATE_D3 3 +#define GP_RWBAR 1 +#define GP_RWREG1 0xa0 +#define GP_RWREG1_ULPI_REFCLK_DISABLE (1 << 17) + /** * struct dwc3_pci - Driver private structure * @dwc3: child dwc3 platform_device @@ -74,6 +78,28 @@ static struct gpiod_lookup_table platform_bytcr_gpios = { }, }; +static int dwc3_byt_enable_ulpi_refclock(struct pci_dev *pci) +{ + void __iomem *reg; + u32 value; + + reg = pcim_iomap(pci, GP_RWBAR, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + value = readl(reg + GP_RWREG1); + if (!(value & GP_RWREG1_ULPI_REFCLK_DISABLE)) + goto unmap; /* ULPI refclk already enabled */ + + value &= ~GP_RWREG1_ULPI_REFCLK_DISABLE; + writel(value, reg + GP_RWREG1); + /* This comes from the Intel Android x86 tree w/o any explanation */ + msleep(100); +unmap: + pcim_iounmap(pci, reg); + return 0; +} + static int dwc3_pci_quirks(struct dwc3_pci *dwc) { struct platform_device *dwc3 = dwc->dwc3; @@ -129,6 +155,11 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc) if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) { struct gpio_desc *gpio; + /* On BYT the FW does not always enable the refclock */ + ret = dwc3_byt_enable_ulpi_refclock(pdev); + if (ret) + return ret; + ret = devm_acpi_dev_add_driver_gpios(&pdev->dev, acpi_dwc3_byt_gpios); if (ret) -- cgit v1.2.3 From 211f658b7b40fd3355a0920481132645e24ffb16 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Jun 2018 16:01:22 +0200 Subject: usb: dwc3: pci: Use devm functions to get the phy GPIOs Even though we only use them once, it is better to not put/release the GPIOs immediately after use, so that others cannot claim them. Use devm functions to get the phy GPIOs, so that they will be automatically released when were unbound from the device and remove the gpio_put calls. Signed-off-by: Hans de Goede Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 26829b299482..08713398073d 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -177,20 +177,20 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc) * put the gpio descriptors again here because the phy driver * might want to grab them, too. */ - gpio = gpiod_get_optional(&pdev->dev, "cs", GPIOD_OUT_LOW); + gpio = devm_gpiod_get_optional(&pdev->dev, "cs", + GPIOD_OUT_LOW); if (IS_ERR(gpio)) return PTR_ERR(gpio); gpiod_set_value_cansleep(gpio, 1); - gpiod_put(gpio); - gpio = gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW); + gpio = devm_gpiod_get_optional(&pdev->dev, "reset", + GPIOD_OUT_LOW); if (IS_ERR(gpio)) return PTR_ERR(gpio); if (gpio) { gpiod_set_value_cansleep(gpio, 1); - gpiod_put(gpio); usleep_range(10000, 11000); } } -- cgit v1.2.3 From 7a051e8de3010c2f5f52e0bf305ef96c1f1f19d5 Mon Sep 17 00:00:00 2001 From: Jaejoong Kim Date: Wed, 4 Jul 2018 13:51:27 +0900 Subject: usb: gadget: storage: Add error handling for no memory fsg_common_set_num_buffers() may fail due to ENOMEM. So add error handling for fail case. Acked-by: Alan Stern Acked-by: Michal Nazarewicz Signed-off-by: Jaejoong Kim Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_mass_storage.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index acecd13dcbd9..1b580eb3f78f 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -3308,7 +3308,9 @@ static ssize_t fsg_opts_num_buffers_store(struct config_item *item, if (ret) goto end; - fsg_common_set_num_buffers(opts->common, num); + ret = fsg_common_set_num_buffers(opts->common, num); + if (ret) + goto end; ret = len; end: -- cgit v1.2.3 From 1fcba97e35696b7cc5662fe704ada540b49f5601 Mon Sep 17 00:00:00 2001 From: Jaejoong Kim Date: Wed, 4 Jul 2018 13:51:28 +0900 Subject: usb: gadget: storage: Remove reference counting The kref used to be needed because sharing of fsg_common among multiple USB function instances was handled by fsg. Now this is managed by configfs, we don't need it anymore. So let's eliminate kref from this driver. Acked-by: Alan Stern Acked-by: Michal Nazarewicz Signed-off-by: Jaejoong Kim Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_mass_storage.c | 27 +++++---------------------- drivers/usb/gadget/function/f_mass_storage.h | 4 ---- 2 files changed, 5 insertions(+), 26 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 1b580eb3f78f..ca8a4b53c59f 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -206,7 +206,6 @@ #include #include #include -#include #include #include #include @@ -312,8 +311,6 @@ struct fsg_common { void *private_data; char inquiry_string[INQUIRY_STRING_LEN]; - - struct kref ref; }; struct fsg_dev { @@ -2551,25 +2548,11 @@ static DEVICE_ATTR(file, 0, file_show, file_store); /****************************** FSG COMMON ******************************/ -static void fsg_common_release(struct kref *ref); - static void fsg_lun_release(struct device *dev) { /* Nothing needs to be done */ } -void fsg_common_get(struct fsg_common *common) -{ - kref_get(&common->ref); -} -EXPORT_SYMBOL_GPL(fsg_common_get); - -void fsg_common_put(struct fsg_common *common) -{ - kref_put(&common->ref, fsg_common_release); -} -EXPORT_SYMBOL_GPL(fsg_common_put); - static struct fsg_common *fsg_common_setup(struct fsg_common *common) { if (!common) { @@ -2582,7 +2565,6 @@ static struct fsg_common *fsg_common_setup(struct fsg_common *common) } init_rwsem(&common->filesem); spin_lock_init(&common->lock); - kref_init(&common->ref); init_completion(&common->thread_notifier); init_waitqueue_head(&common->io_wait); init_waitqueue_head(&common->fsg_wait); @@ -2870,9 +2852,8 @@ void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn, } EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string); -static void fsg_common_release(struct kref *ref) +static void fsg_common_release(struct fsg_common *common) { - struct fsg_common *common = container_of(ref, struct fsg_common, ref); int i; /* If the thread isn't already dead, tell it to exit now */ @@ -3346,7 +3327,7 @@ static void fsg_free_inst(struct usb_function_instance *fi) struct fsg_opts *opts; opts = fsg_opts_from_func_inst(fi); - fsg_common_put(opts->common); + fsg_common_release(opts->common); kfree(opts); } @@ -3370,7 +3351,7 @@ static struct usb_function_instance *fsg_alloc_inst(void) rc = fsg_common_set_num_buffers(opts->common, CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS); if (rc) - goto release_opts; + goto release_common; pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); @@ -3393,6 +3374,8 @@ static struct usb_function_instance *fsg_alloc_inst(void) release_buffers: fsg_common_free_buffers(opts->common); +release_common: + kfree(opts->common); release_opts: kfree(opts); return ERR_PTR(rc); diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h index 58857fcf199f..3b8c4ce2a40a 100644 --- a/drivers/usb/gadget/function/f_mass_storage.h +++ b/drivers/usb/gadget/function/f_mass_storage.h @@ -115,10 +115,6 @@ fsg_opts_from_func_inst(const struct usb_function_instance *fi) return container_of(fi, struct fsg_opts, func_inst); } -void fsg_common_get(struct fsg_common *common); - -void fsg_common_put(struct fsg_common *common); - void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs); int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n); -- cgit v1.2.3 From 76251db8656194d27629300c852b53c471e9b586 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Mon, 16 Jul 2018 12:25:47 +0200 Subject: usb: dwc3: of-simple: reset host controller at suspend/resume If we power off the SoC logic rail in S3, we can find that the Type-C PHY can't initialize correctly after system resume. We need to toggle the USB3-OTG reset before trying to initialize the PHY, or else it times out. phy phy-ff800000.phy.9: phy poweron failed --> -110 dwc3 fe900000.dwc3: failed to initialize core dwc3: probe of fe900000.dwc3 failed with error -110 Note that the RK3399 TRM suggests that we should keep the whole usb3 controller in reset for the duration of the Type-C PHY initialization. However, it's hard to assert the reset in the current framework of reset. We're still skeptical about that, and we haven't yet found a case where this seems to have mattered. This approach is much easier, it simply holds the USB3-OTG reset while device is supended. The dwc3 core is going to reinitialize the controller at suspend/resume anyway (including a "soft reset"), so it should be safe to do this. Signed-off-by: Enric Balletbo i Serra Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-of-simple.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index dbeff5e6ad14..40bf9e0bbc59 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -28,6 +28,7 @@ struct dwc3_of_simple { int num_clocks; struct reset_control *resets; bool pulse_resets; + bool need_reset; }; static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count) @@ -93,6 +94,13 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) platform_set_drvdata(pdev, simple); simple->dev = dev; + /* + * Some controllers need to toggle the usb3-otg reset before trying to + * initialize the PHY, otherwise the PHY times out. + */ + if (of_device_is_compatible(np, "rockchip,rk3399-dwc3")) + simple->need_reset = true; + if (of_device_is_compatible(np, "amlogic,meson-axg-dwc3") || of_device_is_compatible(np, "amlogic,meson-gxl-dwc3")) { shared_resets = true; @@ -201,9 +209,30 @@ static int dwc3_of_simple_runtime_resume(struct device *dev) return 0; } + +static int dwc3_of_simple_suspend(struct device *dev) +{ + struct dwc3_of_simple *simple = dev_get_drvdata(dev); + + if (simple->need_reset) + reset_control_assert(simple->resets); + + return 0; +} + +static int dwc3_of_simple_resume(struct device *dev) +{ + struct dwc3_of_simple *simple = dev_get_drvdata(dev); + + if (simple->need_reset) + reset_control_deassert(simple->resets); + + return 0; +} #endif static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dwc3_of_simple_suspend, dwc3_of_simple_resume) SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend, dwc3_of_simple_runtime_resume, NULL) }; -- cgit v1.2.3 From 9a7faac3650216112e034b157289bf1a48a99e2d Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Thu, 19 Jul 2018 17:26:24 -0600 Subject: usb: dwc3: change stream event enable bit back to 13 Commit ff3f0789b3dc ("usb: dwc3: use BIT() macro where possible") changed DWC3_DEPCFG_STREAM_EVENT_EN from bit 13 to bit 12. Spotted this cleanup typo while looking at diffs between 4.9.35 and 4.14.16 for a separate issue. Fixes: ff3f0789b3dc ("usb: dwc3: use BIT() macro where possible") Signed-off-by: Erich E. Hoover Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index db610c56f1d6..2aacd1afd9ff 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -25,7 +25,7 @@ struct dwc3; #define DWC3_DEPCFG_XFER_IN_PROGRESS_EN BIT(9) #define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10) #define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11) -#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(12) +#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(13) #define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16) #define DWC3_DEPCFG_STREAM_CAPABLE BIT(24) #define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25) -- cgit v1.2.3 From 7bab01ecc6c43da882333c6db39741cb43677004 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Tue, 24 Jul 2018 01:34:01 +0200 Subject: USB: option: add support for DW5821e The device exposes AT, NMEA and DIAG ports in both USB configurations. The patch explicitly ignores interfaces 0 and 1, as they're bound to other drivers already; and also interface 6, which is a GNSS interface for which we don't have a driver yet. T: Bus=01 Lev=03 Prnt=04 Port=00 Cnt=01 Dev#= 18 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 2 P: Vendor=413c ProdID=81d7 Rev=03.18 S: Manufacturer=DELL S: Product=DW5821e Snapdragon X20 LTE S: SerialNumber=0123456789ABCDEF C: #Ifs= 7 Cfg#= 2 Atr=a0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option I: If#= 6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) T: Bus=01 Lev=03 Prnt=04 Port=00 Cnt=01 Dev#= 16 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 2 P: Vendor=413c ProdID=81d7 Rev=03.18 S: Manufacturer=DELL S: Product=DW5821e Snapdragon X20 LTE S: SerialNumber=0123456789ABCDEF C: #Ifs= 6 Cfg#= 1 Atr=a0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan I: If#= 1 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=00 Prot=00 Driver=usbhid I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=option I: If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option Signed-off-by: Aleksander Morgado Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 664e61f16b6a..0215b70c4efc 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -196,6 +196,8 @@ static void option_instat_callback(struct urb *urb); #define DELL_PRODUCT_5800_V2_MINICARD_VZW 0x8196 /* Novatel E362 */ #define DELL_PRODUCT_5804_MINICARD_ATT 0x819b /* Novatel E371 */ +#define DELL_PRODUCT_5821E 0x81d7 + #define KYOCERA_VENDOR_ID 0x0c88 #define KYOCERA_PRODUCT_KPC650 0x17da #define KYOCERA_PRODUCT_KPC680 0x180a @@ -1030,6 +1032,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, DELL_PRODUCT_5800_MINICARD_VZW, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, DELL_PRODUCT_5800_V2_MINICARD_VZW, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, DELL_PRODUCT_5804_MINICARD_ATT, 0xff, 0xff, 0xff) }, + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5821E), + .driver_info = RSVD(0) | RSVD(1) | RSVD(6) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) }, /* ADU-E100, ADU-310 */ { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_620UW) }, -- cgit v1.2.3 From 87d852de94d606010faf283bf1df70e3e825e6d0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 20 Jul 2018 19:54:01 +0300 Subject: usb: dwc3: Describe 'wakeup_work' field of struct dwc3_pci Describe 'wakeup_work' field of struct dwc3_pci to avoid a warning: drivers/usb/dwc3/dwc3-pci.c:59: warning: Function parameter or member 'wakeup_work' not described in 'dwc3_pci' Signed-off-by: Andy Shevchenko Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 08713398073d..b29f031dfc23 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -49,6 +49,7 @@ * @pci: our link to PCI bus * @guid: _DSM GUID * @has_dsm_for_pm: true for devices which need to run _DSM on runtime PM + * @wakeup_work: work for asynchronous resume */ struct dwc3_pci { struct platform_device *dwc3; -- cgit v1.2.3 From d635db5508b0b01142d44739717d7122469f59b1 Mon Sep 17 00:00:00 2001 From: Pengbo Mu Date: Mon, 23 Jul 2018 18:32:36 +0800 Subject: usb: dwc3: add global soc bus configuration reg0 Add the macro definition for global soc bus configuration register 0 Signed-off-by: Changming Huang Signed-off-by: Ran Wang Signed-off-by: Pengbo Mu Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 285ce0ef3b91..213b939ad011 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -163,6 +163,17 @@ /* Bit fields */ +/* Global SoC Bus Configuration INCRx Register 0 */ +#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */ +#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */ +#define DWC3_GSBUSCFG0_INCR64BRSTENA (1 << 5) /* INCR64 burst */ +#define DWC3_GSBUSCFG0_INCR32BRSTENA (1 << 4) /* INCR32 burst */ +#define DWC3_GSBUSCFG0_INCR16BRSTENA (1 << 3) /* INCR16 burst */ +#define DWC3_GSBUSCFG0_INCR8BRSTENA (1 << 2) /* INCR8 burst */ +#define DWC3_GSBUSCFG0_INCR4BRSTENA (1 << 1) /* INCR4 burst */ +#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */ +#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff + /* Global Debug Queue/FIFO Space Available Register */ #define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f) #define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0) -- cgit v1.2.3 From d9612c2f0449e24983a8b689603210486a930c90 Mon Sep 17 00:00:00 2001 From: Pengbo Mu Date: Mon, 23 Jul 2018 18:32:37 +0800 Subject: usb: dwc3: Enable undefined length INCR burst type Enable the undefined length INCR burst type and set INCRx. Different platform may has the different burst size type. In order to get best performance, we need to tune the burst size to one special value, instead of the default value. Signed-off-by: Changming Huang Signed-off-by: Ran Wang Signed-off-by: Pengbo Mu Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/dwc3/core.h | 3 ++ 2 files changed, 97 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 103807587dc6..21e4931d0cc0 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -778,6 +778,98 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc) static int dwc3_core_get_phy(struct dwc3 *dwc); static int dwc3_core_ulpi_init(struct dwc3 *dwc); +/* set global incr burst type configuration registers */ +static void dwc3_set_incr_burst_type(struct dwc3 *dwc) +{ + struct device *dev = dwc->dev; + /* incrx_mode : for INCR burst type. */ + bool incrx_mode; + /* incrx_size : for size of INCRX burst. */ + u32 incrx_size; + u32 *vals; + u32 cfg; + int ntype; + int ret; + int i; + + cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0); + + /* + * Handle property "snps,incr-burst-type-adjustment". + * Get the number of value from this property: + * result <= 0, means this property is not supported. + * result = 1, means INCRx burst mode supported. + * result > 1, means undefined length burst mode supported. + */ + ntype = device_property_read_u32_array(dev, + "snps,incr-burst-type-adjustment", NULL, 0); + if (ntype <= 0) + return; + + vals = kcalloc(ntype, sizeof(u32), GFP_KERNEL); + if (!vals) { + dev_err(dev, "Error to get memory\n"); + return; + } + + /* Get INCR burst type, and parse it */ + ret = device_property_read_u32_array(dev, + "snps,incr-burst-type-adjustment", vals, ntype); + if (ret) { + dev_err(dev, "Error to get property\n"); + return; + } + + incrx_size = *vals; + + if (ntype > 1) { + /* INCRX (undefined length) burst mode */ + incrx_mode = INCRX_UNDEF_LENGTH_BURST_MODE; + for (i = 1; i < ntype; i++) { + if (vals[i] > incrx_size) + incrx_size = vals[i]; + } + } else { + /* INCRX burst mode */ + incrx_mode = INCRX_BURST_MODE; + } + + /* Enable Undefined Length INCR Burst and Enable INCRx Burst */ + cfg &= ~DWC3_GSBUSCFG0_INCRBRST_MASK; + if (incrx_mode) + cfg |= DWC3_GSBUSCFG0_INCRBRSTENA; + switch (incrx_size) { + case 256: + cfg |= DWC3_GSBUSCFG0_INCR256BRSTENA; + break; + case 128: + cfg |= DWC3_GSBUSCFG0_INCR128BRSTENA; + break; + case 64: + cfg |= DWC3_GSBUSCFG0_INCR64BRSTENA; + break; + case 32: + cfg |= DWC3_GSBUSCFG0_INCR32BRSTENA; + break; + case 16: + cfg |= DWC3_GSBUSCFG0_INCR16BRSTENA; + break; + case 8: + cfg |= DWC3_GSBUSCFG0_INCR8BRSTENA; + break; + case 4: + cfg |= DWC3_GSBUSCFG0_INCR4BRSTENA; + break; + case 1: + break; + default: + dev_err(dev, "Invalid property\n"); + break; + } + + dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg); +} + /** * dwc3_core_init - Low-level initialization of DWC3 Core * @dwc: Pointer to our controller context structure @@ -840,6 +932,8 @@ static int dwc3_core_init(struct dwc3 *dwc) /* Adjust Frame Length */ dwc3_frame_length_adjustment(dwc); + dwc3_set_incr_burst_type(dwc); + usb_phy_set_suspend(dwc->usb2_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0); ret = phy_power_on(dwc->usb2_generic_phy); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 213b939ad011..5f14fb7121b1 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1168,6 +1168,9 @@ struct dwc3 { u16 imod_interval; }; +#define INCRX_BURST_MODE 0 +#define INCRX_UNDEF_LENGTH_BURST_MODE 1 + #define work_to_dwc(w) (container_of((w), struct dwc3, drd_work)) /* -------------------------------------------------------------------------- */ -- cgit v1.2.3 From 1a7b12f69a9434a766e77c43d113826f0413b032 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 26 Jul 2018 14:48:56 +0300 Subject: usb: dwc3: pci: Supply device properties via driver data For now all PCI enumerated dwc3 devices require some properties to be present. This allows us to unconditionally append them and supply via driver_data. No functional change intended. Signed-off-by: Andy Shevchenko Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 131 +++++++++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 57 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index b29f031dfc23..740ed561f353 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -101,52 +101,37 @@ unmap: return 0; } +static const struct property_entry dwc3_pci_intel_properties[] = { + PROPERTY_ENTRY_STRING("dr_mode", "peripheral"), + PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"), + {} +}; + +static const struct property_entry dwc3_pci_amd_properties[] = { + PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"), + PROPERTY_ENTRY_U8("snps,lpm-nyet-threshold", 0xf), + PROPERTY_ENTRY_BOOL("snps,u2exit_lfps_quirk"), + PROPERTY_ENTRY_BOOL("snps,u2ss_inp3_quirk"), + PROPERTY_ENTRY_BOOL("snps,req_p1p2p3_quirk"), + PROPERTY_ENTRY_BOOL("snps,del_p1p2p3_quirk"), + PROPERTY_ENTRY_BOOL("snps,del_phy_power_chg_quirk"), + PROPERTY_ENTRY_BOOL("snps,lfps_filter_quirk"), + PROPERTY_ENTRY_BOOL("snps,rx_detect_poll_quirk"), + PROPERTY_ENTRY_BOOL("snps,tx_de_emphasis_quirk"), + PROPERTY_ENTRY_U8("snps,tx_de_emphasis", 1), + /* FIXME these quirks should be removed when AMD NL tapes out */ + PROPERTY_ENTRY_BOOL("snps,disable_scramble_quirk"), + PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"), + PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"), + PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"), + {} +}; + static int dwc3_pci_quirks(struct dwc3_pci *dwc) { - struct platform_device *dwc3 = dwc->dwc3; struct pci_dev *pdev = dwc->pci; - if (pdev->vendor == PCI_VENDOR_ID_AMD && - pdev->device == PCI_DEVICE_ID_AMD_NL_USB) { - struct property_entry properties[] = { - PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"), - PROPERTY_ENTRY_U8("snps,lpm-nyet-threshold", 0xf), - PROPERTY_ENTRY_BOOL("snps,u2exit_lfps_quirk"), - PROPERTY_ENTRY_BOOL("snps,u2ss_inp3_quirk"), - PROPERTY_ENTRY_BOOL("snps,req_p1p2p3_quirk"), - PROPERTY_ENTRY_BOOL("snps,del_p1p2p3_quirk"), - PROPERTY_ENTRY_BOOL("snps,del_phy_power_chg_quirk"), - PROPERTY_ENTRY_BOOL("snps,lfps_filter_quirk"), - PROPERTY_ENTRY_BOOL("snps,rx_detect_poll_quirk"), - PROPERTY_ENTRY_BOOL("snps,tx_de_emphasis_quirk"), - PROPERTY_ENTRY_U8("snps,tx_de_emphasis", 1), - /* - * FIXME these quirks should be removed when AMD NL - * tapes out - */ - PROPERTY_ENTRY_BOOL("snps,disable_scramble_quirk"), - PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"), - PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"), - PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"), - { }, - }; - - return platform_device_add_properties(dwc3, properties); - } - if (pdev->vendor == PCI_VENDOR_ID_INTEL) { - int ret; - - struct property_entry properties[] = { - PROPERTY_ENTRY_STRING("dr_mode", "peripheral"), - PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"), - { } - }; - - ret = platform_device_add_properties(dwc3, properties); - if (ret < 0) - return ret; - if (pdev->device == PCI_DEVICE_ID_INTEL_BXT || pdev->device == PCI_DEVICE_ID_INTEL_BXT_M) { guid_parse(PCI_INTEL_BXT_DSM_GUID, &dwc->guid); @@ -155,6 +140,7 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc) if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) { struct gpio_desc *gpio; + int ret; /* On BYT the FW does not always enable the refclock */ ret = dwc3_byt_enable_ulpi_refclock(pdev); @@ -216,9 +202,9 @@ static void dwc3_pci_resume_work(struct work_struct *work) } #endif -static int dwc3_pci_probe(struct pci_dev *pci, - const struct pci_device_id *id) +static int dwc3_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) { + struct property_entry *p = (struct property_entry *)id->driver_data; struct dwc3_pci *dwc; struct resource res[2]; int ret; @@ -261,6 +247,10 @@ static int dwc3_pci_probe(struct pci_dev *pci, dwc->dwc3->dev.parent = dev; ACPI_COMPANION_SET(&dwc->dwc3->dev, ACPI_COMPANION(dev)); + ret = platform_device_add_properties(dwc->dwc3, p); + if (ret < 0) + return ret; + ret = dwc3_pci_quirks(dwc); if (ret) goto err; @@ -298,20 +288,47 @@ static void dwc3_pci_remove(struct pci_dev *pci) } static const struct pci_device_id dwc3_pci_id_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTLP), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTH), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BXT), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BXT_M), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_APL), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KBP), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GLK), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CNPLP), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CNPH), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICLLP), }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BSW), + (kernel_ulong_t) &dwc3_pci_intel_properties }, + + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BYT), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MRFLD), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SPTLP), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SPTH), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BXT), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BXT_M), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_APL), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_KBP), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_GLK), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CNPLP), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CNPH), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICLLP), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_NL_USB), + (kernel_ulong_t) &dwc3_pci_amd_properties, }, { } /* Terminating Entry */ }; MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table); -- cgit v1.2.3 From c31d983beaf04e6754918aa1073f053b12efb700 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 26 Jul 2018 14:48:57 +0300 Subject: usb: dwc3: pci: Intel Merrifield can be host On Intel Edison board the OTG function is enabled, thus, USB can switch to the host mode. Allow that by changing dr_mode property to "otg" for Intel Merrifield. Signed-off-by: Andy Shevchenko Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 740ed561f353..5edd79470368 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -107,6 +107,12 @@ static const struct property_entry dwc3_pci_intel_properties[] = { {} }; +static const struct property_entry dwc3_pci_mrfld_properties[] = { + PROPERTY_ENTRY_STRING("dr_mode", "otg"), + PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"), + {} +}; + static const struct property_entry dwc3_pci_amd_properties[] = { PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"), PROPERTY_ENTRY_U8("snps,lpm-nyet-threshold", 0xf), @@ -295,7 +301,7 @@ static const struct pci_device_id dwc3_pci_id_table[] = { (kernel_ulong_t) &dwc3_pci_intel_properties, }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MRFLD), - (kernel_ulong_t) &dwc3_pci_intel_properties, }, + (kernel_ulong_t) &dwc3_pci_mrfld_properties, }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SPTLP), (kernel_ulong_t) &dwc3_pci_intel_properties, }, -- cgit v1.2.3 From f25c42b8d604fbca6d8d3eff2365a73bbef076d3 Mon Sep 17 00:00:00 2001 From: Gevorg Sahakyan Date: Thu, 26 Jul 2018 18:00:13 +0400 Subject: usb: dwc2: Modify dwc2_readl/writel functions prototype Added hsotg argument to dwc2_readl/writel function prototype, and also instead of address pass offset of register. hsotg will contain flag field for endianness. Also customized dwc2_set_bit and dwc2_clear_bit function for dwc2_readl/writel functions. Signed-off-by: Gevorg Sahakyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.c | 241 ++++++++++---------- drivers/usb/dwc2/core.h | 25 ++- drivers/usb/dwc2/core_intr.c | 118 +++++----- drivers/usb/dwc2/debugfs.c | 55 +++-- drivers/usb/dwc2/gadget.c | 512 +++++++++++++++++++++---------------------- drivers/usb/dwc2/hcd.c | 459 +++++++++++++++++++------------------- drivers/usb/dwc2/hcd.h | 10 +- drivers/usb/dwc2/hcd_ddma.c | 10 +- drivers/usb/dwc2/hcd_intr.c | 96 ++++---- drivers/usb/dwc2/hcd_queue.c | 10 +- drivers/usb/dwc2/params.c | 20 +- 11 files changed, 776 insertions(+), 780 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 1c36a6a9dd63..55d5ae2a7ec7 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -73,17 +73,17 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) /* Backup global regs */ gr = &hsotg->gr_backup; - gr->gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); - gr->gintmsk = dwc2_readl(hsotg->regs + GINTMSK); - gr->gahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); - gr->gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); - gr->grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ); - gr->gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ); - gr->gdfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG); - gr->pcgcctl1 = dwc2_readl(hsotg->regs + PCGCCTL1); - gr->glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); - gr->gi2cctl = dwc2_readl(hsotg->regs + GI2CCTL); - gr->pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + gr->gotgctl = dwc2_readl(hsotg, GOTGCTL); + gr->gintmsk = dwc2_readl(hsotg, GINTMSK); + gr->gahbcfg = dwc2_readl(hsotg, GAHBCFG); + gr->gusbcfg = dwc2_readl(hsotg, GUSBCFG); + gr->grxfsiz = dwc2_readl(hsotg, GRXFSIZ); + gr->gnptxfsiz = dwc2_readl(hsotg, GNPTXFSIZ); + gr->gdfifocfg = dwc2_readl(hsotg, GDFIFOCFG); + gr->pcgcctl1 = dwc2_readl(hsotg, PCGCCTL1); + gr->glpmcfg = dwc2_readl(hsotg, GLPMCFG); + gr->gi2cctl = dwc2_readl(hsotg, GI2CCTL); + gr->pcgcctl = dwc2_readl(hsotg, PCGCTL); gr->valid = true; return 0; @@ -111,18 +111,18 @@ int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) } gr->valid = false; - dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); - dwc2_writel(gr->gotgctl, hsotg->regs + GOTGCTL); - dwc2_writel(gr->gintmsk, hsotg->regs + GINTMSK); - dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG); - dwc2_writel(gr->gahbcfg, hsotg->regs + GAHBCFG); - dwc2_writel(gr->grxfsiz, hsotg->regs + GRXFSIZ); - dwc2_writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ); - dwc2_writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG); - dwc2_writel(gr->pcgcctl1, hsotg->regs + PCGCCTL1); - dwc2_writel(gr->glpmcfg, hsotg->regs + GLPMCFG); - dwc2_writel(gr->pcgcctl, hsotg->regs + PCGCTL); - dwc2_writel(gr->gi2cctl, hsotg->regs + GI2CCTL); + dwc2_writel(hsotg, 0xffffffff, GINTSTS); + dwc2_writel(hsotg, gr->gotgctl, GOTGCTL); + dwc2_writel(hsotg, gr->gintmsk, GINTMSK); + dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG); + dwc2_writel(hsotg, gr->gahbcfg, GAHBCFG); + dwc2_writel(hsotg, gr->grxfsiz, GRXFSIZ); + dwc2_writel(hsotg, gr->gnptxfsiz, GNPTXFSIZ); + dwc2_writel(hsotg, gr->gdfifocfg, GDFIFOCFG); + dwc2_writel(hsotg, gr->pcgcctl1, PCGCCTL1); + dwc2_writel(hsotg, gr->glpmcfg, GLPMCFG); + dwc2_writel(hsotg, gr->pcgcctl, PCGCTL); + dwc2_writel(hsotg, gr->gi2cctl, GI2CCTL); return 0; } @@ -141,17 +141,17 @@ int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) return -ENOTSUPP; - pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl = dwc2_readl(hsotg, PCGCTL); pcgcctl &= ~PCGCTL_STOPPCLK; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); - pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl = dwc2_readl(hsotg, PCGCTL); pcgcctl &= ~PCGCTL_PWRCLMP; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); - pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl = dwc2_readl(hsotg, PCGCTL); pcgcctl &= ~PCGCTL_RSTPDWNMODULE; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); udelay(100); if (restore) { @@ -222,21 +222,21 @@ int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg) * Clear any pending interrupts since dwc2 will not be able to * clear them after entering partial_power_down. */ - dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, 0xffffffff, GINTSTS); /* Put the controller in low power state */ - pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl = dwc2_readl(hsotg, PCGCTL); pcgcctl |= PCGCTL_PWRCLMP; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); ndelay(20); pcgcctl |= PCGCTL_RSTPDWNMODULE; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); ndelay(20); pcgcctl |= PCGCTL_STOPPCLK; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); return ret; } @@ -272,39 +272,39 @@ static void dwc2_restore_essential_regs(struct dwc2_hsotg *hsotg, int rmode, if (!(pcgcctl & PCGCTL_P2HD_DEV_ENUM_SPD_MASK)) pcgcctl |= BIT(17); } - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); /* Umnask global Interrupt in GAHBCFG and restore it */ - dwc2_writel(gr->gahbcfg | GAHBCFG_GLBL_INTR_EN, hsotg->regs + GAHBCFG); + dwc2_writel(hsotg, gr->gahbcfg | GAHBCFG_GLBL_INTR_EN, GAHBCFG); /* Clear all pending interupts */ - dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, 0xffffffff, GINTSTS); /* Unmask restore done interrupt */ - dwc2_writel(GINTSTS_RESTOREDONE, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, GINTSTS_RESTOREDONE, GINTMSK); /* Restore GUSBCFG and HCFG/DCFG */ - dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG); if (is_host) { - dwc2_writel(hr->hcfg, hsotg->regs + HCFG); + dwc2_writel(hsotg, hr->hcfg, HCFG); if (rmode) pcgcctl |= PCGCTL_RESTOREMODE; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); udelay(10); pcgcctl |= PCGCTL_ESS_REG_RESTORED; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); udelay(10); } else { - dwc2_writel(dr->dcfg, hsotg->regs + DCFG); + dwc2_writel(hsotg, dr->dcfg, DCFG); if (!rmode) pcgcctl |= PCGCTL_RESTOREMODE | PCGCTL_RSTPDWNMODULE; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); udelay(10); pcgcctl |= PCGCTL_ESS_REG_RESTORED; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); udelay(10); } } @@ -322,42 +322,42 @@ void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup, u32 gpwrdn; /* Switch-on voltage to the core */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_PWRDNSWTCH; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Reset core */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_PWRDNRSTN; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Enable restore from PMU */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_RESTORE; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Disable Power Down Clamp */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_PWRDNCLMP; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(50); if (!is_host && rem_wakeup) udelay(70); /* Deassert reset core */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_PWRDNRSTN; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Disable PMU interrupt */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_PMUINTSEL; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Set Restore Essential Regs bit in PCGCCTL register */ @@ -431,7 +431,7 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg) return false; /* Check if core configuration includes the IDDIG filter. */ - ghwcfg4 = dwc2_readl(hsotg->regs + GHWCFG4); + ghwcfg4 = dwc2_readl(hsotg, GHWCFG4); if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN)) return false; @@ -439,9 +439,9 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg) * Check if the IDDIG debounce filter is bypassed. Available * in core version >= 3.10a. */ - gsnpsid = dwc2_readl(hsotg->regs + GSNPSID); + gsnpsid = dwc2_readl(hsotg, GSNPSID); if (gsnpsid >= DWC2_CORE_REV_3_10a) { - u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + u32 gotgctl = dwc2_readl(hsotg, GOTGCTL); if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS) return false; @@ -510,8 +510,8 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait) * reset and account for this delay after the reset. */ if (dwc2_iddig_filter_enabled(hsotg)) { - u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); - u32 gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + u32 gotgctl = dwc2_readl(hsotg, GOTGCTL); + u32 gusbcfg = dwc2_readl(hsotg, GUSBCFG); if (!(gotgctl & GOTGCTL_CONID_B) || (gusbcfg & GUSBCFG_FORCEHOSTMODE)) { @@ -520,9 +520,9 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait) } /* Core Soft Reset */ - greset = dwc2_readl(hsotg->regs + GRSTCTL); + greset = dwc2_readl(hsotg, GRSTCTL); greset |= GRSTCTL_CSFTRST; - dwc2_writel(greset, hsotg->regs + GRSTCTL); + dwc2_writel(hsotg, greset, GRSTCTL); if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 50)) { dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n", @@ -594,14 +594,14 @@ void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host) if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST)) return; - gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + gusbcfg = dwc2_readl(hsotg, GUSBCFG); set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE; clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE; gusbcfg &= ~clear; gusbcfg |= set; - dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, gusbcfg, GUSBCFG); dwc2_wait_for_mode(hsotg, host); return; @@ -627,10 +627,10 @@ static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "Clearing force mode bits\n"); - gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + gusbcfg = dwc2_readl(hsotg, GUSBCFG); gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; gusbcfg &= ~GUSBCFG_FORCEDEVMODE; - dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, gusbcfg, GUSBCFG); if (dwc2_iddig_filter_enabled(hsotg)) msleep(100); @@ -670,11 +670,11 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg) void dwc2_enable_acg(struct dwc2_hsotg *hsotg) { if (hsotg->params.acg_enable) { - u32 pcgcctl1 = dwc2_readl(hsotg->regs + PCGCCTL1); + u32 pcgcctl1 = dwc2_readl(hsotg, PCGCCTL1); dev_dbg(hsotg->dev, "Enabling Active Clock Gating\n"); pcgcctl1 |= PCGCCTL1_GATEEN; - dwc2_writel(pcgcctl1, hsotg->regs + PCGCCTL1); + dwc2_writel(hsotg, pcgcctl1, PCGCCTL1); } } @@ -695,56 +695,57 @@ void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "Host Global Registers\n"); addr = hsotg->regs + HCFG; dev_dbg(hsotg->dev, "HCFG @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HCFG)); addr = hsotg->regs + HFIR; dev_dbg(hsotg->dev, "HFIR @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HFIR)); addr = hsotg->regs + HFNUM; dev_dbg(hsotg->dev, "HFNUM @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HFNUM)); addr = hsotg->regs + HPTXSTS; dev_dbg(hsotg->dev, "HPTXSTS @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HPTXSTS)); addr = hsotg->regs + HAINT; dev_dbg(hsotg->dev, "HAINT @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HAINT)); addr = hsotg->regs + HAINTMSK; dev_dbg(hsotg->dev, "HAINTMSK @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HAINTMSK)); if (hsotg->params.dma_desc_enable) { addr = hsotg->regs + HFLBADDR; dev_dbg(hsotg->dev, "HFLBADDR @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HFLBADDR)); } addr = hsotg->regs + HPRT0; dev_dbg(hsotg->dev, "HPRT0 @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HPRT0)); for (i = 0; i < hsotg->params.host_channels; i++) { dev_dbg(hsotg->dev, "Host Channel %d Specific Registers\n", i); addr = hsotg->regs + HCCHAR(i); dev_dbg(hsotg->dev, "HCCHAR @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HCCHAR(i))); addr = hsotg->regs + HCSPLT(i); dev_dbg(hsotg->dev, "HCSPLT @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HCSPLT(i))); addr = hsotg->regs + HCINT(i); dev_dbg(hsotg->dev, "HCINT @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HCINT(i))); addr = hsotg->regs + HCINTMSK(i); dev_dbg(hsotg->dev, "HCINTMSK @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HCINTMSK(i))); addr = hsotg->regs + HCTSIZ(i); dev_dbg(hsotg->dev, "HCTSIZ @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HCTSIZ(i))); addr = hsotg->regs + HCDMA(i); dev_dbg(hsotg->dev, "HCDMA @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HCDMA(i))); if (hsotg->params.dma_desc_enable) { addr = hsotg->regs + HCDMAB(i); dev_dbg(hsotg->dev, "HCDMAB @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, + HCDMAB(i))); } } #endif @@ -766,80 +767,80 @@ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "Core Global Registers\n"); addr = hsotg->regs + GOTGCTL; dev_dbg(hsotg->dev, "GOTGCTL @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GOTGCTL)); addr = hsotg->regs + GOTGINT; dev_dbg(hsotg->dev, "GOTGINT @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GOTGINT)); addr = hsotg->regs + GAHBCFG; dev_dbg(hsotg->dev, "GAHBCFG @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GAHBCFG)); addr = hsotg->regs + GUSBCFG; dev_dbg(hsotg->dev, "GUSBCFG @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GUSBCFG)); addr = hsotg->regs + GRSTCTL; dev_dbg(hsotg->dev, "GRSTCTL @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GRSTCTL)); addr = hsotg->regs + GINTSTS; dev_dbg(hsotg->dev, "GINTSTS @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GINTSTS)); addr = hsotg->regs + GINTMSK; dev_dbg(hsotg->dev, "GINTMSK @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GINTMSK)); addr = hsotg->regs + GRXSTSR; dev_dbg(hsotg->dev, "GRXSTSR @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GRXSTSR)); addr = hsotg->regs + GRXFSIZ; dev_dbg(hsotg->dev, "GRXFSIZ @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GRXFSIZ)); addr = hsotg->regs + GNPTXFSIZ; dev_dbg(hsotg->dev, "GNPTXFSIZ @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GNPTXFSIZ)); addr = hsotg->regs + GNPTXSTS; dev_dbg(hsotg->dev, "GNPTXSTS @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GNPTXSTS)); addr = hsotg->regs + GI2CCTL; dev_dbg(hsotg->dev, "GI2CCTL @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GI2CCTL)); addr = hsotg->regs + GPVNDCTL; dev_dbg(hsotg->dev, "GPVNDCTL @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GPVNDCTL)); addr = hsotg->regs + GGPIO; dev_dbg(hsotg->dev, "GGPIO @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GGPIO)); addr = hsotg->regs + GUID; dev_dbg(hsotg->dev, "GUID @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GUID)); addr = hsotg->regs + GSNPSID; dev_dbg(hsotg->dev, "GSNPSID @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GSNPSID)); addr = hsotg->regs + GHWCFG1; dev_dbg(hsotg->dev, "GHWCFG1 @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GHWCFG1)); addr = hsotg->regs + GHWCFG2; dev_dbg(hsotg->dev, "GHWCFG2 @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GHWCFG2)); addr = hsotg->regs + GHWCFG3; dev_dbg(hsotg->dev, "GHWCFG3 @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GHWCFG3)); addr = hsotg->regs + GHWCFG4; dev_dbg(hsotg->dev, "GHWCFG4 @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GHWCFG4)); addr = hsotg->regs + GLPMCFG; dev_dbg(hsotg->dev, "GLPMCFG @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GLPMCFG)); addr = hsotg->regs + GPWRDN; dev_dbg(hsotg->dev, "GPWRDN @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GPWRDN)); addr = hsotg->regs + GDFIFOCFG; dev_dbg(hsotg->dev, "GDFIFOCFG @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, GDFIFOCFG)); addr = hsotg->regs + HPTXFSIZ; dev_dbg(hsotg->dev, "HPTXFSIZ @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, HPTXFSIZ)); addr = hsotg->regs + PCGCTL; dev_dbg(hsotg->dev, "PCGCTL @0x%08lX : 0x%08X\n", - (unsigned long)addr, dwc2_readl(addr)); + (unsigned long)addr, dwc2_readl(hsotg, PCGCTL)); #endif } @@ -862,7 +863,7 @@ void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num) greset = GRSTCTL_TXFFLSH; greset |= num << GRSTCTL_TXFNUM_SHIFT & GRSTCTL_TXFNUM_MASK; - dwc2_writel(greset, hsotg->regs + GRSTCTL); + dwc2_writel(hsotg, greset, GRSTCTL); if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 10000)) dev_warn(hsotg->dev, "%s: HANG! timeout GRSTCTL GRSTCTL_TXFFLSH\n", @@ -889,7 +890,7 @@ void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg) __func__); greset = GRSTCTL_RXFFLSH; - dwc2_writel(greset, hsotg->regs + GRSTCTL); + dwc2_writel(hsotg, greset, GRSTCTL); /* Wait for RxFIFO flush done */ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_RXFFLSH, 10000)) @@ -902,7 +903,7 @@ void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg) bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg) { - if (dwc2_readl(hsotg->regs + GSNPSID) == 0xffffffff) + if (dwc2_readl(hsotg, GSNPSID) == 0xffffffff) return false; else return true; @@ -916,10 +917,10 @@ bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg) */ void dwc2_enable_global_interrupts(struct dwc2_hsotg *hsotg) { - u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); + u32 ahbcfg = dwc2_readl(hsotg, GAHBCFG); ahbcfg |= GAHBCFG_GLBL_INTR_EN; - dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG); + dwc2_writel(hsotg, ahbcfg, GAHBCFG); } /** @@ -930,16 +931,16 @@ void dwc2_enable_global_interrupts(struct dwc2_hsotg *hsotg) */ void dwc2_disable_global_interrupts(struct dwc2_hsotg *hsotg) { - u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); + u32 ahbcfg = dwc2_readl(hsotg, GAHBCFG); ahbcfg &= ~GAHBCFG_GLBL_INTR_EN; - dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG); + dwc2_writel(hsotg, ahbcfg, GAHBCFG); } /* Returns the controller's GHWCFG2.OTG_MODE. */ unsigned int dwc2_op_mode(struct dwc2_hsotg *hsotg) { - u32 ghwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2); + u32 ghwcfg2 = dwc2_readl(hsotg, GHWCFG2); return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >> GHWCFG2_OP_MODE_SHIFT; @@ -988,7 +989,7 @@ int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hsotg, u32 offset, u32 mask, u32 i; for (i = 0; i < timeout; i++) { - if (dwc2_readl(hsotg->regs + offset) & mask) + if (dwc2_readl(hsotg, offset) & mask) return 0; udelay(1); } @@ -1011,7 +1012,7 @@ int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hsotg, u32 offset, u32 mask, u32 i; for (i = 0; i < timeout; i++) { - if (!(dwc2_readl(hsotg->regs + offset) & mask)) + if (!(dwc2_readl(hsotg, offset) & mask)) return 0; udelay(1); } diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 8a9272a2c82c..bca8463f00b0 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1172,9 +1172,9 @@ struct dwc2_hsotg { * writes. This set of operations was added specifically for MIPS and * should only be used there. */ -static inline u32 dwc2_readl(const void __iomem *addr) +static inline u32 dwc2_readl(struct dwc2_hsotg *hsotg, u32 offset) { - u32 value = __raw_readl(addr); + u32 value = __raw_readl(hsotg->regs + offset); /* In order to preserve endianness __raw_* operation is used. Therefore * a barrier is needed to ensure IO access is not re-ordered across @@ -1184,9 +1184,9 @@ static inline u32 dwc2_readl(const void __iomem *addr) return value; } -static inline void dwc2_writel(u32 value, void __iomem *addr) +static inline void dwc2_writel(struct dwc2_hsotg *hsotg, u32 value, u32 offset) { - __raw_writel(value, addr); + __raw_writel(value, hsotg->regs + offset); /* * In order to preserve endianness __raw_* operation is used. Therefore @@ -1195,22 +1195,23 @@ static inline void dwc2_writel(u32 value, void __iomem *addr) */ mb(); #ifdef DWC2_LOG_WRITES - pr_info("INFO:: wrote %08x to %p\n", value, addr); + pr_info("INFO:: wrote %08x to %p\n", value, hsotg->regs + offset); #endif } #else + /* Normal architectures just use readl/write */ -static inline u32 dwc2_readl(const void __iomem *addr) +static inline u32 dwc2_readl(struct dwc2_hsotg *hsotg, u32 offset) { - return readl(addr); + return readl(hsotg->regs + offset); } -static inline void dwc2_writel(u32 value, void __iomem *addr) +static inline void dwc2_writel(struct dwc2_hsotg *hsotg, u32 value, u32 offset) { - writel(value, addr); + writel(value, hsotg->regs + offset); #ifdef DWC2_LOG_WRITES - pr_info("info:: wrote %08x to %p\n", value, addr); + pr_info("info:: wrote %08x to %p\n", value, hsotg->regs + offset); #endif } #endif @@ -1320,12 +1321,12 @@ bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg); */ static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg) { - return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) != 0; + return (dwc2_readl(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) != 0; } static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg) { - return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) == 0; + return (dwc2_readl(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) == 0; } /* diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index cc90b58b6b3c..19ae2595f1c3 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -81,11 +81,11 @@ static const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg) */ static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg) { - u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0); + u32 hprt0 = dwc2_readl(hsotg, HPRT0); if (hprt0 & HPRT0_ENACHG) { hprt0 &= ~HPRT0_ENA; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); } } @@ -97,7 +97,7 @@ static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg) static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg) { /* Clear interrupt */ - dwc2_writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_MODEMIS, GINTSTS); dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n", dwc2_is_host_mode(hsotg) ? "Host" : "Device"); @@ -115,8 +115,8 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) u32 gotgctl; u32 gintmsk; - gotgint = dwc2_readl(hsotg->regs + GOTGINT); - gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + gotgint = dwc2_readl(hsotg, GOTGINT); + gotgctl = dwc2_readl(hsotg, GOTGCTL); dev_dbg(hsotg->dev, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint, dwc2_op_state_str(hsotg)); @@ -124,7 +124,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, " ++OTG Interrupt: Session End Detected++ (%s)\n", dwc2_op_state_str(hsotg)); - gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg, GOTGCTL); if (dwc2_is_device_mode(hsotg)) dwc2_hsotg_disconnect(hsotg); @@ -150,24 +150,24 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) hsotg->lx_state = DWC2_L0; } - gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg, GOTGCTL); gotgctl &= ~GOTGCTL_DEVHNPEN; - dwc2_writel(gotgctl, hsotg->regs + GOTGCTL); + dwc2_writel(hsotg, gotgctl, GOTGCTL); } if (gotgint & GOTGINT_SES_REQ_SUC_STS_CHNG) { dev_dbg(hsotg->dev, " ++OTG Interrupt: Session Request Success Status Change++\n"); - gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg, GOTGCTL); if (gotgctl & GOTGCTL_SESREQSCS) { if (hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS && hsotg->params.i2c_enable) { hsotg->srp_success = 1; } else { /* Clear Session Request */ - gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg, GOTGCTL); gotgctl &= ~GOTGCTL_SESREQ; - dwc2_writel(gotgctl, hsotg->regs + GOTGCTL); + dwc2_writel(hsotg, gotgctl, GOTGCTL); } } } @@ -177,7 +177,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) * Print statements during the HNP interrupt handling * can cause it to fail */ - gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg, GOTGCTL); /* * WA for 3.00a- HW is not setting cur_mode, even sometimes * this does not help @@ -197,9 +197,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) * interrupt does not get handled and Linux * complains loudly. */ - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg, GINTMSK); gintmsk &= ~GINTSTS_SOF; - dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, gintmsk, GINTMSK); /* * Call callback function with spin lock @@ -213,9 +213,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) hsotg->op_state = OTG_STATE_B_HOST; } } else { - gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg, GOTGCTL); gotgctl &= ~(GOTGCTL_HNPREQ | GOTGCTL_DEVHNPEN); - dwc2_writel(gotgctl, hsotg->regs + GOTGCTL); + dwc2_writel(hsotg, gotgctl, GOTGCTL); dev_dbg(hsotg->dev, "HNP Failed\n"); dev_err(hsotg->dev, "Device Not Connected/Responding\n"); @@ -241,9 +241,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) hsotg->op_state = OTG_STATE_A_PERIPHERAL; } else { /* Need to disable SOF interrupt immediately */ - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg, GINTMSK); gintmsk &= ~GINTSTS_SOF; - dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, gintmsk, GINTMSK); spin_unlock(&hsotg->lock); dwc2_hcd_start(hsotg); spin_lock(&hsotg->lock); @@ -258,7 +258,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, " ++OTG Interrupt: Debounce Done++\n"); /* Clear GOTGINT */ - dwc2_writel(gotgint, hsotg->regs + GOTGINT); + dwc2_writel(hsotg, gotgint, GOTGINT); } /** @@ -276,12 +276,12 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg) u32 gintmsk; /* Clear interrupt */ - dwc2_writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_CONIDSTSCHNG, GINTSTS); /* Need to disable SOF interrupt immediately */ - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg, GINTMSK); gintmsk &= ~GINTSTS_SOF; - dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, gintmsk, GINTMSK); dev_dbg(hsotg->dev, " ++Connector ID Status Change Interrupt++ (%s)\n", dwc2_is_host_mode(hsotg) ? "Host" : "Device"); @@ -314,7 +314,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) int ret; /* Clear interrupt */ - dwc2_writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_SESSREQINT, GINTSTS); dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n", hsotg->lx_state); @@ -351,15 +351,15 @@ static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg) return; } - glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); + glpmcfg = dwc2_readl(hsotg, GLPMCFG); if (dwc2_is_device_mode(hsotg)) { dev_dbg(hsotg->dev, "Exit from L1 state\n"); glpmcfg &= ~GLPMCFG_ENBLSLPM; glpmcfg &= ~GLPMCFG_HIRD_THRES_EN; - dwc2_writel(glpmcfg, hsotg->regs + GLPMCFG); + dwc2_writel(hsotg, glpmcfg, GLPMCFG); do { - glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); + glpmcfg = dwc2_readl(hsotg, GLPMCFG); if (!(glpmcfg & (GLPMCFG_COREL1RES_MASK | GLPMCFG_L1RESUMEOK | GLPMCFG_SLPSTS))) @@ -398,7 +398,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) int ret; /* Clear interrupt */ - dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_WKUPINT, GINTSTS); dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n"); dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state); @@ -410,13 +410,13 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) if (dwc2_is_device_mode(hsotg)) { dev_dbg(hsotg->dev, "DSTS=0x%0x\n", - dwc2_readl(hsotg->regs + DSTS)); + dwc2_readl(hsotg, DSTS)); if (hsotg->lx_state == DWC2_L2) { - u32 dctl = dwc2_readl(hsotg->regs + DCTL); + u32 dctl = dwc2_readl(hsotg, DCTL); /* Clear Remote Wakeup Signaling */ dctl &= ~DCTL_RMTWKUPSIG; - dwc2_writel(dctl, hsotg->regs + DCTL); + dwc2_writel(hsotg, dctl, DCTL); ret = dwc2_exit_partial_power_down(hsotg, true); if (ret && (ret != -ENOTSUPP)) dev_err(hsotg->dev, "exit power_down failed\n"); @@ -430,11 +430,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) return; if (hsotg->lx_state != DWC2_L1) { - u32 pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + u32 pcgcctl = dwc2_readl(hsotg, PCGCTL); /* Restart the Phy Clock */ pcgcctl &= ~PCGCTL_STOPPCLK; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); mod_timer(&hsotg->wkp_timer, jiffies + msecs_to_jiffies(71)); } else { @@ -450,7 +450,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) */ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg) { - dwc2_writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_DISCONNINT, GINTSTS); dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n", dwc2_is_host_mode(hsotg) ? "Host" : "Device", @@ -474,7 +474,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) int ret; /* Clear interrupt */ - dwc2_writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_USBSUSP, GINTSTS); dev_dbg(hsotg->dev, "USB SUSPEND\n"); @@ -483,7 +483,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) * Check the Device status register to determine if the Suspend * state is active */ - dsts = dwc2_readl(hsotg->regs + DSTS); + dsts = dwc2_readl(hsotg, DSTS); dev_dbg(hsotg->dev, "%s: DSTS=0x%0x\n", __func__, dsts); dev_dbg(hsotg->dev, "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d HWCFG4.Hibernation=%d\n", @@ -563,9 +563,9 @@ static void dwc2_handle_lpm_intr(struct dwc2_hsotg *hsotg) u32 enslpm; /* Clear interrupt */ - dwc2_writel(GINTSTS_LPMTRANRCVD, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_LPMTRANRCVD, GINTSTS); - glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); + glpmcfg = dwc2_readl(hsotg, GLPMCFG); if (!(glpmcfg & GLPMCFG_LPMCAP)) { dev_err(hsotg->dev, "Unexpected LPM interrupt\n"); @@ -588,16 +588,16 @@ static void dwc2_handle_lpm_intr(struct dwc2_hsotg *hsotg) } else { dev_dbg(hsotg->dev, "Entering Sleep with L1 Gating\n"); - pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl = dwc2_readl(hsotg, PCGCTL); pcgcctl |= PCGCTL_ENBL_SLEEP_GATING; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); } /** * Examine prt_sleep_sts after TL1TokenTetry period max (10 us) */ udelay(10); - glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); + glpmcfg = dwc2_readl(hsotg, GLPMCFG); if (glpmcfg & GLPMCFG_SLPSTS) { /* Save the current state */ @@ -627,9 +627,9 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg) u32 gahbcfg; u32 gintmsk_common = GINTMSK_COMMON; - gintsts = dwc2_readl(hsotg->regs + GINTSTS); - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); - gahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); + gintsts = dwc2_readl(hsotg, GINTSTS); + gintmsk = dwc2_readl(hsotg, GINTMSK); + gahbcfg = dwc2_readl(hsotg, GAHBCFG); /* If any common interrupts set */ if (gintsts & gintmsk_common) @@ -653,9 +653,9 @@ static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) u32 gpwrdn; int linestate; - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); /* clear all interrupt */ - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); linestate = (gpwrdn & GPWRDN_LINESTATE_MASK) >> GPWRDN_LINESTATE_SHIFT; dev_dbg(hsotg->dev, "%s: dwc2_handle_gpwrdwn_intr called gpwrdn= %08x\n", __func__, @@ -668,38 +668,38 @@ static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "%s: GPWRDN_DISCONN_DET\n", __func__); /* Switch-on voltage to the core */ - gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH; - dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); udelay(10); /* Reset core */ - gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN; - dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); udelay(10); /* Disable Power Down Clamp */ - gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP; - dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); udelay(10); /* Deassert reset core */ - gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); gpwrdn_tmp |= GPWRDN_PWRDNRSTN; - dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); udelay(10); /* Disable PMU interrupt */ - gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); gpwrdn_tmp &= ~GPWRDN_PMUINTSEL; - dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); /* De-assert Wakeup Logic */ - gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); gpwrdn_tmp &= ~GPWRDN_PMUACTV; - dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); hsotg->hibernated = 0; @@ -780,10 +780,10 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) /* Reading current frame number value in device or host modes. */ if (dwc2_is_device_mode(hsotg)) - hsotg->frame_number = (dwc2_readl(hsotg->regs + DSTS) + hsotg->frame_number = (dwc2_readl(hsotg, DSTS) & DSTS_SOFFN_MASK) >> DSTS_SOFFN_SHIFT; else - hsotg->frame_number = (dwc2_readl(hsotg->regs + HFNUM) + hsotg->frame_number = (dwc2_readl(hsotg, HFNUM) & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT; gintsts = dwc2_read_common_intr(hsotg); diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index d0bdb7997557..22d015b0424f 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -69,7 +69,7 @@ static int testmode_show(struct seq_file *s, void *unused) int dctl; spin_lock_irqsave(&hsotg->lock, flags); - dctl = dwc2_readl(hsotg->regs + DCTL); + dctl = dwc2_readl(hsotg, DCTL); dctl &= DCTL_TSTCTL_MASK; dctl >>= DCTL_TSTCTL_SHIFT; spin_unlock_irqrestore(&hsotg->lock, flags); @@ -126,42 +126,41 @@ static const struct file_operations testmode_fops = { static int state_show(struct seq_file *seq, void *v) { struct dwc2_hsotg *hsotg = seq->private; - void __iomem *regs = hsotg->regs; int idx; seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n", - dwc2_readl(regs + DCFG), - dwc2_readl(regs + DCTL), - dwc2_readl(regs + DSTS)); + dwc2_readl(hsotg, DCFG), + dwc2_readl(hsotg, DCTL), + dwc2_readl(hsotg, DSTS)); seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n", - dwc2_readl(regs + DIEPMSK), dwc2_readl(regs + DOEPMSK)); + dwc2_readl(hsotg, DIEPMSK), dwc2_readl(hsotg, DOEPMSK)); seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n", - dwc2_readl(regs + GINTMSK), - dwc2_readl(regs + GINTSTS)); + dwc2_readl(hsotg, GINTMSK), + dwc2_readl(hsotg, GINTSTS)); seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n", - dwc2_readl(regs + DAINTMSK), - dwc2_readl(regs + DAINT)); + dwc2_readl(hsotg, DAINTMSK), + dwc2_readl(hsotg, DAINT)); seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n", - dwc2_readl(regs + GNPTXSTS), - dwc2_readl(regs + GRXSTSR)); + dwc2_readl(hsotg, GNPTXSTS), + dwc2_readl(hsotg, GRXSTSR)); seq_puts(seq, "\nEndpoint status:\n"); for (idx = 0; idx < hsotg->num_of_eps; idx++) { u32 in, out; - in = dwc2_readl(regs + DIEPCTL(idx)); - out = dwc2_readl(regs + DOEPCTL(idx)); + in = dwc2_readl(hsotg, DIEPCTL(idx)); + out = dwc2_readl(hsotg, DOEPCTL(idx)); seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x", idx, in, out); - in = dwc2_readl(regs + DIEPTSIZ(idx)); - out = dwc2_readl(regs + DOEPTSIZ(idx)); + in = dwc2_readl(hsotg, DIEPTSIZ(idx)); + out = dwc2_readl(hsotg, DOEPTSIZ(idx)); seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", in, out); @@ -184,14 +183,13 @@ DEFINE_SHOW_ATTRIBUTE(state); static int fifo_show(struct seq_file *seq, void *v) { struct dwc2_hsotg *hsotg = seq->private; - void __iomem *regs = hsotg->regs; u32 val; int idx; seq_puts(seq, "Non-periodic FIFOs:\n"); - seq_printf(seq, "RXFIFO: Size %d\n", dwc2_readl(regs + GRXFSIZ)); + seq_printf(seq, "RXFIFO: Size %d\n", dwc2_readl(hsotg, GRXFSIZ)); - val = dwc2_readl(regs + GNPTXFSIZ); + val = dwc2_readl(hsotg, GNPTXFSIZ); seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n", val >> FIFOSIZE_DEPTH_SHIFT, val & FIFOSIZE_STARTADDR_MASK); @@ -199,7 +197,7 @@ static int fifo_show(struct seq_file *seq, void *v) seq_puts(seq, "\nPeriodic TXFIFOs:\n"); for (idx = 1; idx < hsotg->num_of_eps; idx++) { - val = dwc2_readl(regs + DPTXFSIZN(idx)); + val = dwc2_readl(hsotg, DPTXFSIZN(idx)); seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, val >> FIFOSIZE_DEPTH_SHIFT, @@ -228,7 +226,6 @@ static int ep_show(struct seq_file *seq, void *v) struct dwc2_hsotg_ep *ep = seq->private; struct dwc2_hsotg *hsotg = ep->parent; struct dwc2_hsotg_req *req; - void __iomem *regs = hsotg->regs; int index = ep->index; int show_limit = 15; unsigned long flags; @@ -239,20 +236,20 @@ static int ep_show(struct seq_file *seq, void *v) /* first show the register state */ seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n", - dwc2_readl(regs + DIEPCTL(index)), - dwc2_readl(regs + DOEPCTL(index))); + dwc2_readl(hsotg, DIEPCTL(index)), + dwc2_readl(hsotg, DOEPCTL(index))); seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n", - dwc2_readl(regs + DIEPDMA(index)), - dwc2_readl(regs + DOEPDMA(index))); + dwc2_readl(hsotg, DIEPDMA(index)), + dwc2_readl(hsotg, DOEPDMA(index))); seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n", - dwc2_readl(regs + DIEPINT(index)), - dwc2_readl(regs + DOEPINT(index))); + dwc2_readl(hsotg, DIEPINT(index)), + dwc2_readl(hsotg, DOEPINT(index))); seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n", - dwc2_readl(regs + DIEPTSIZ(index)), - dwc2_readl(regs + DOEPTSIZ(index))); + dwc2_readl(hsotg, DIEPTSIZ(index)), + dwc2_readl(hsotg, DOEPTSIZ(index))); seq_puts(seq, "\n"); seq_printf(seq, "mps %d\n", ep->ep.maxpacket); diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index a0f82cca2d9a..50f94f277433 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -47,14 +47,14 @@ static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget) return container_of(gadget, struct dwc2_hsotg, gadget); } -static inline void dwc2_set_bit(void __iomem *ptr, u32 val) +static inline void dwc2_set_bit(struct dwc2_hsotg *hsotg, u32 offset, u32 val) { - dwc2_writel(dwc2_readl(ptr) | val, ptr); + dwc2_writel(hsotg, dwc2_readl(hsotg, offset) | val, offset); } -static inline void dwc2_clear_bit(void __iomem *ptr, u32 val) +static inline void dwc2_clear_bit(struct dwc2_hsotg *hsotg, u32 offset, u32 val) { - dwc2_writel(dwc2_readl(ptr) & ~val, ptr); + dwc2_writel(hsotg, dwc2_readl(hsotg, offset) & ~val, offset); } static inline struct dwc2_hsotg_ep *index_to_ep(struct dwc2_hsotg *hsotg, @@ -129,14 +129,14 @@ static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep) */ static void dwc2_hsotg_en_gsint(struct dwc2_hsotg *hsotg, u32 ints) { - u32 gsintmsk = dwc2_readl(hsotg->regs + GINTMSK); + u32 gsintmsk = dwc2_readl(hsotg, GINTMSK); u32 new_gsintmsk; new_gsintmsk = gsintmsk | ints; if (new_gsintmsk != gsintmsk) { dev_dbg(hsotg->dev, "gsintmsk now 0x%08x\n", new_gsintmsk); - dwc2_writel(new_gsintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, new_gsintmsk, GINTMSK); } } @@ -147,13 +147,13 @@ static void dwc2_hsotg_en_gsint(struct dwc2_hsotg *hsotg, u32 ints) */ static void dwc2_hsotg_disable_gsint(struct dwc2_hsotg *hsotg, u32 ints) { - u32 gsintmsk = dwc2_readl(hsotg->regs + GINTMSK); + u32 gsintmsk = dwc2_readl(hsotg, GINTMSK); u32 new_gsintmsk; new_gsintmsk = gsintmsk & ~ints; if (new_gsintmsk != gsintmsk) - dwc2_writel(new_gsintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, new_gsintmsk, GINTMSK); } /** @@ -178,12 +178,12 @@ static void dwc2_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg, bit <<= 16; local_irq_save(flags); - daint = dwc2_readl(hsotg->regs + DAINTMSK); + daint = dwc2_readl(hsotg, DAINTMSK); if (en) daint |= bit; else daint &= ~bit; - dwc2_writel(daint, hsotg->regs + DAINTMSK); + dwc2_writel(hsotg, daint, DAINTMSK); local_irq_restore(flags); } @@ -266,10 +266,11 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg) hsotg->fifo_map = 0; /* set RX/NPTX FIFO sizes */ - dwc2_writel(hsotg->params.g_rx_fifo_size, hsotg->regs + GRXFSIZ); - dwc2_writel((hsotg->params.g_rx_fifo_size << FIFOSIZE_STARTADDR_SHIFT) | + dwc2_writel(hsotg, hsotg->params.g_rx_fifo_size, GRXFSIZ); + dwc2_writel(hsotg, (hsotg->params.g_rx_fifo_size << + FIFOSIZE_STARTADDR_SHIFT) | (hsotg->params.g_np_tx_fifo_size << FIFOSIZE_DEPTH_SHIFT), - hsotg->regs + GNPTXFSIZ); + GNPTXFSIZ); /* * arange all the rest of the TX FIFOs, as some versions of this @@ -295,25 +296,25 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg) "insufficient fifo memory"); addr += txfsz[ep]; - dwc2_writel(val, hsotg->regs + DPTXFSIZN(ep)); - val = dwc2_readl(hsotg->regs + DPTXFSIZN(ep)); + dwc2_writel(hsotg, val, DPTXFSIZN(ep)); + val = dwc2_readl(hsotg, DPTXFSIZN(ep)); } - dwc2_writel(hsotg->hw_params.total_fifo_size | + dwc2_writel(hsotg, hsotg->hw_params.total_fifo_size | addr << GDFIFOCFG_EPINFOBASE_SHIFT, - hsotg->regs + GDFIFOCFG); + GDFIFOCFG); /* * according to p428 of the design guide, we need to ensure that * all fifos are flushed before continuing */ - dwc2_writel(GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH | - GRSTCTL_RXFFLSH, hsotg->regs + GRSTCTL); + dwc2_writel(hsotg, GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH | + GRSTCTL_RXFFLSH, GRSTCTL); /* wait until the fifos are both flushed */ timeout = 100; while (1) { - val = dwc2_readl(hsotg->regs + GRSTCTL); + val = dwc2_readl(hsotg, GRSTCTL); if ((val & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH)) == 0) break; @@ -451,7 +452,7 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg, struct dwc2_hsotg_req *hs_req) { bool periodic = is_ep_periodic(hs_ep); - u32 gnptxsts = dwc2_readl(hsotg->regs + GNPTXSTS); + u32 gnptxsts = dwc2_readl(hsotg, GNPTXSTS); int buf_pos = hs_req->req.actual; int to_write = hs_ep->size_loaded; void *data; @@ -466,7 +467,7 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg, return 0; if (periodic && !hsotg->dedicated_fifos) { - u32 epsize = dwc2_readl(hsotg->regs + DIEPTSIZ(hs_ep->index)); + u32 epsize = dwc2_readl(hsotg, DIEPTSIZ(hs_ep->index)); int size_left; int size_done; @@ -507,8 +508,8 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg, return -ENOSPC; } } else if (hsotg->dedicated_fifos && hs_ep->index != 0) { - can_write = dwc2_readl(hsotg->regs + - DTXFSTS(hs_ep->fifo_index)); + can_write = dwc2_readl(hsotg, + DTXFSTS(hs_ep->fifo_index)); can_write &= 0xffff; can_write *= 4; @@ -652,7 +653,7 @@ static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg) { u32 dsts; - dsts = dwc2_readl(hsotg->regs + DSTS); + dsts = dwc2_readl(hsotg, DSTS); dsts &= DSTS_SOFFN_MASK; dsts >>= DSTS_SOFFN_SHIFT; @@ -915,11 +916,11 @@ static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep) dma_reg = hs_ep->dir_in ? DIEPDMA(index) : DOEPDMA(index); /* write descriptor chain address to control register */ - dwc2_writel(hs_ep->desc_list_dma, hsotg->regs + dma_reg); + dwc2_writel(hsotg, hs_ep->desc_list_dma, dma_reg); - ctrl = dwc2_readl(hsotg->regs + depctl); + ctrl = dwc2_readl(hsotg, depctl); ctrl |= DXEPCTL_EPENA | DXEPCTL_CNAK; - dwc2_writel(ctrl, hsotg->regs + depctl); + dwc2_writel(hsotg, ctrl, depctl); } /** @@ -967,11 +968,11 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, epsize_reg = dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index); dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x, ep %d, dir %s\n", - __func__, dwc2_readl(hsotg->regs + epctrl_reg), index, + __func__, dwc2_readl(hsotg, epctrl_reg), index, hs_ep->dir_in ? "in" : "out"); /* If endpoint is stalled, we will restart request later */ - ctrl = dwc2_readl(hsotg->regs + epctrl_reg); + ctrl = dwc2_readl(hsotg, epctrl_reg); if (index && ctrl & DXEPCTL_STALL) { dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index); @@ -1064,13 +1065,13 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, length); /* write descriptor chain address to control register */ - dwc2_writel(hs_ep->desc_list_dma, hsotg->regs + dma_reg); + dwc2_writel(hsotg, hs_ep->desc_list_dma, dma_reg); dev_dbg(hsotg->dev, "%s: %08x pad => 0x%08x\n", __func__, (u32)hs_ep->desc_list_dma, dma_reg); } else { /* write size / packets */ - dwc2_writel(epsize, hsotg->regs + epsize_reg); + dwc2_writel(hsotg, epsize, epsize_reg); if (using_dma(hsotg) && !continuing && (length != 0)) { /* @@ -1078,7 +1079,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, * already synced by dwc2_hsotg_ep_queue(). */ - dwc2_writel(ureq->dma, hsotg->regs + dma_reg); + dwc2_writel(hsotg, ureq->dma, dma_reg); dev_dbg(hsotg->dev, "%s: %pad => 0x%08x\n", __func__, &ureq->dma, dma_reg); @@ -1104,7 +1105,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); - dwc2_writel(ctrl, hsotg->regs + epctrl_reg); + dwc2_writel(hsotg, ctrl, epctrl_reg); /* * set these, it seems that DMA support increments past the end @@ -1127,13 +1128,13 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, */ /* check ep is enabled */ - if (!(dwc2_readl(hsotg->regs + epctrl_reg) & DXEPCTL_EPENA)) + if (!(dwc2_readl(hsotg, epctrl_reg) & DXEPCTL_EPENA)) dev_dbg(hsotg->dev, "ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n", - index, dwc2_readl(hsotg->regs + epctrl_reg)); + index, dwc2_readl(hsotg, epctrl_reg)); dev_dbg(hsotg->dev, "%s: DXEPCTL=0x%08x\n", - __func__, dwc2_readl(hsotg->regs + epctrl_reg)); + __func__, dwc2_readl(hsotg, epctrl_reg)); /* enable ep interrupts */ dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 1); @@ -1466,7 +1467,7 @@ static struct dwc2_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, */ int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) { - int dctl = dwc2_readl(hsotg->regs + DCTL); + int dctl = dwc2_readl(hsotg, DCTL); dctl &= ~DCTL_TSTCTL_MASK; switch (testmode) { @@ -1480,7 +1481,7 @@ int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) default: return -EINVAL; } - dwc2_writel(dctl, hsotg->regs + DCTL); + dwc2_writel(hsotg, dctl, DCTL); return 0; } @@ -1634,9 +1635,9 @@ static void dwc2_gadget_start_next_request(struct dwc2_hsotg_ep *hs_ep) } else { dev_dbg(hsotg->dev, "%s: No more ISOC-OUT requests\n", __func__); - mask = dwc2_readl(hsotg->regs + epmsk_reg); + mask = dwc2_readl(hsotg, epmsk_reg); mask |= DOEPMSK_OUTTKNEPDISMSK; - dwc2_writel(mask, hsotg->regs + epmsk_reg); + dwc2_writel(hsotg, mask, epmsk_reg); } } @@ -1773,14 +1774,14 @@ static void dwc2_hsotg_stall_ep0(struct dwc2_hsotg *hsotg) * taken effect, so no need to clear later. */ - ctrl = dwc2_readl(hsotg->regs + reg); + ctrl = dwc2_readl(hsotg, reg); ctrl |= DXEPCTL_STALL; ctrl |= DXEPCTL_CNAK; - dwc2_writel(ctrl, hsotg->regs + reg); + dwc2_writel(hsotg, ctrl, reg); dev_dbg(hsotg->dev, "written DXEPCTL=0x%08x to %08x (DXEPCTL=0x%08x)\n", - ctrl, reg, dwc2_readl(hsotg->regs + reg)); + ctrl, reg, dwc2_readl(hsotg, reg)); /* * complete won't be called, so we enqueue @@ -1825,11 +1826,11 @@ static void dwc2_hsotg_process_control(struct dwc2_hsotg *hsotg, switch (ctrl->bRequest) { case USB_REQ_SET_ADDRESS: hsotg->connected = 1; - dcfg = dwc2_readl(hsotg->regs + DCFG); + dcfg = dwc2_readl(hsotg, DCFG); dcfg &= ~DCFG_DEVADDR_MASK; dcfg |= (le16_to_cpu(ctrl->wValue) << DCFG_DEVADDR_SHIFT) & DCFG_DEVADDR_MASK; - dwc2_writel(dcfg, hsotg->regs + DCFG); + dwc2_writel(hsotg, dcfg, DCFG); dev_info(hsotg->dev, "new address %d\n", ctrl->wValue); @@ -1955,16 +1956,16 @@ static void dwc2_hsotg_program_zlp(struct dwc2_hsotg *hsotg, dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, dma, 0); } else { - dwc2_writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | - DXEPTSIZ_XFERSIZE(0), hsotg->regs + + dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | + DXEPTSIZ_XFERSIZE(0), epsiz_reg); } - ctrl = dwc2_readl(hsotg->regs + epctl_reg); + ctrl = dwc2_readl(hsotg, epctl_reg); ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ ctrl |= DXEPCTL_USBACTEP; - dwc2_writel(ctrl, hsotg->regs + epctl_reg); + dwc2_writel(hsotg, ctrl, epctl_reg); } /** @@ -2124,13 +2125,12 @@ static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) { struct dwc2_hsotg_ep *hs_ep = hsotg->eps_out[ep_idx]; struct dwc2_hsotg_req *hs_req = hs_ep->req; - void __iomem *fifo = hsotg->regs + EPFIFO(ep_idx); int to_read; int max_req; int read_ptr; if (!hs_req) { - u32 epctl = dwc2_readl(hsotg->regs + DOEPCTL(ep_idx)); + u32 epctl = dwc2_readl(hsotg, DOEPCTL(ep_idx)); int ptr; dev_dbg(hsotg->dev, @@ -2139,7 +2139,7 @@ static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) /* dump the data from the FIFO, we've nothing we can do */ for (ptr = 0; ptr < size; ptr += 4) - (void)dwc2_readl(fifo); + (void)dwc2_readl(hsotg, EPFIFO(ep_idx)); return; } @@ -2169,7 +2169,8 @@ static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) * note, we might over-write the buffer end by 3 bytes depending on * alignment of the data. */ - ioread32_rep(fifo, hs_req->req.buf + read_ptr, to_read); + ioread32_rep(hsotg->regs + EPFIFO(ep_idx), + hs_req->req.buf + read_ptr, to_read); } /** @@ -2198,12 +2199,12 @@ static void dwc2_hsotg_change_ep_iso_parity(struct dwc2_hsotg *hsotg, { u32 ctrl; - ctrl = dwc2_readl(hsotg->regs + epctl_reg); + ctrl = dwc2_readl(hsotg, epctl_reg); if (ctrl & DXEPCTL_EOFRNUM) ctrl |= DXEPCTL_SETEVENFR; else ctrl |= DXEPCTL_SETODDFR; - dwc2_writel(ctrl, hsotg->regs + epctl_reg); + dwc2_writel(hsotg, ctrl, epctl_reg); } /* @@ -2247,7 +2248,7 @@ static unsigned int dwc2_gadget_get_xfersize_ddma(struct dwc2_hsotg_ep *hs_ep) */ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) { - u32 epsize = dwc2_readl(hsotg->regs + DOEPTSIZ(epnum)); + u32 epsize = dwc2_readl(hsotg, DOEPTSIZ(epnum)); struct dwc2_hsotg_ep *hs_ep = hsotg->eps_out[epnum]; struct dwc2_hsotg_req *hs_req = hs_ep->req; struct usb_request *req = &hs_req->req; @@ -2343,7 +2344,7 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) */ static void dwc2_hsotg_handle_rx(struct dwc2_hsotg *hsotg) { - u32 grxstsr = dwc2_readl(hsotg->regs + GRXSTSP); + u32 grxstsr = dwc2_readl(hsotg, GRXSTSP); u32 epnum, status, size; WARN_ON(using_dma(hsotg)); @@ -2374,7 +2375,7 @@ static void dwc2_hsotg_handle_rx(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n", dwc2_hsotg_read_frameno(hsotg), - dwc2_readl(hsotg->regs + DOEPCTL(0))); + dwc2_readl(hsotg, DOEPCTL(0))); /* * Call dwc2_hsotg_handle_outdone here if it was not called from * GRXSTS_PKTSTS_OUTDONE. That is, if the core didn't @@ -2392,7 +2393,7 @@ static void dwc2_hsotg_handle_rx(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "SetupRX (Frame=0x%08x, DOPEPCTL=0x%08x)\n", dwc2_hsotg_read_frameno(hsotg), - dwc2_readl(hsotg->regs + DOEPCTL(0))); + dwc2_readl(hsotg, DOEPCTL(0))); WARN_ON(hsotg->ep0_state != DWC2_EP0_SETUP); @@ -2446,7 +2447,6 @@ static void dwc2_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg, unsigned int mc, unsigned int dir_in) { struct dwc2_hsotg_ep *hs_ep; - void __iomem *regs = hsotg->regs; u32 reg; hs_ep = index_to_ep(hsotg, ep, dir_in); @@ -2472,15 +2472,15 @@ static void dwc2_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg, } if (dir_in) { - reg = dwc2_readl(regs + DIEPCTL(ep)); + reg = dwc2_readl(hsotg, DIEPCTL(ep)); reg &= ~DXEPCTL_MPS_MASK; reg |= mps; - dwc2_writel(reg, regs + DIEPCTL(ep)); + dwc2_writel(hsotg, reg, DIEPCTL(ep)); } else { - reg = dwc2_readl(regs + DOEPCTL(ep)); + reg = dwc2_readl(hsotg, DOEPCTL(ep)); reg &= ~DXEPCTL_MPS_MASK; reg |= mps; - dwc2_writel(reg, regs + DOEPCTL(ep)); + dwc2_writel(hsotg, reg, DOEPCTL(ep)); } return; @@ -2496,8 +2496,8 @@ bad_mps: */ static void dwc2_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx) { - dwc2_writel(GRSTCTL_TXFNUM(idx) | GRSTCTL_TXFFLSH, - hsotg->regs + GRSTCTL); + dwc2_writel(hsotg, GRSTCTL_TXFNUM(idx) | GRSTCTL_TXFFLSH, + GRSTCTL); /* wait until the fifo is flushed */ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 100)) @@ -2550,7 +2550,7 @@ static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg, struct dwc2_hsotg_ep *hs_ep) { struct dwc2_hsotg_req *hs_req = hs_ep->req; - u32 epsize = dwc2_readl(hsotg->regs + DIEPTSIZ(hs_ep->index)); + u32 epsize = dwc2_readl(hsotg, DIEPTSIZ(hs_ep->index)); int size_left, size_done; if (!hs_req) { @@ -2654,12 +2654,12 @@ static u32 dwc2_gadget_read_ep_interrupts(struct dwc2_hsotg *hsotg, u32 mask; u32 diepempmsk; - mask = dwc2_readl(hsotg->regs + epmsk_reg); - diepempmsk = dwc2_readl(hsotg->regs + DIEPEMPMSK); + mask = dwc2_readl(hsotg, epmsk_reg); + diepempmsk = dwc2_readl(hsotg, DIEPEMPMSK); mask |= ((diepempmsk >> idx) & 0x1) ? DIEPMSK_TXFIFOEMPTY : 0; mask |= DXEPINT_SETUP_RCVD; - ints = dwc2_readl(hsotg->regs + epint_reg); + ints = dwc2_readl(hsotg, epint_reg); ints &= mask; return ints; } @@ -2684,12 +2684,12 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep) unsigned char idx = hs_ep->index; int dir_in = hs_ep->dir_in; u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); - int dctl = dwc2_readl(hsotg->regs + DCTL); + int dctl = dwc2_readl(hsotg, DCTL); dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__); if (dir_in) { - int epctl = dwc2_readl(hsotg->regs + epctl_reg); + int epctl = dwc2_readl(hsotg, epctl_reg); dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index); @@ -2699,17 +2699,17 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep) } if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) { - int dctl = dwc2_readl(hsotg->regs + DCTL); + int dctl = dwc2_readl(hsotg, DCTL); dctl |= DCTL_CGNPINNAK; - dwc2_writel(dctl, hsotg->regs + DCTL); + dwc2_writel(hsotg, dctl, DCTL); } return; } if (dctl & DCTL_GOUTNAKSTS) { dctl |= DCTL_CGOUTNAK; - dwc2_writel(dctl, hsotg->regs + DCTL); + dwc2_writel(hsotg, dctl, DCTL); } if (!hs_ep->isochronous) @@ -2775,23 +2775,23 @@ static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep) u32 dsts; u32 ctrl; - dsts = dwc2_readl(hsotg->regs + DSTS); + dsts = dwc2_readl(hsotg, DSTS); ep->target_frame = dwc2_hsotg_read_frameno(hsotg); dwc2_gadget_incr_frame_num(ep); - ctrl = dwc2_readl(hsotg->regs + DOEPCTL(ep->index)); + ctrl = dwc2_readl(hsotg, DOEPCTL(ep->index)); if (ep->target_frame & 0x1) ctrl |= DXEPCTL_SETODDFR; else ctrl |= DXEPCTL_SETEVENFR; - dwc2_writel(ctrl, hsotg->regs + DOEPCTL(ep->index)); + dwc2_writel(hsotg, ctrl, DOEPCTL(ep->index)); } dwc2_gadget_start_next_request(ep); - doepmsk = dwc2_readl(hsotg->regs + DOEPMSK); + doepmsk = dwc2_readl(hsotg, DOEPMSK); doepmsk &= ~DOEPMSK_OUTTKNEPDISMSK; - dwc2_writel(doepmsk, hsotg->regs + DOEPMSK); + dwc2_writel(hsotg, doepmsk, DOEPMSK); } /** @@ -2829,14 +2829,14 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep) hs_ep->target_frame = tmp; if (hs_ep->interval > 1) { - u32 ctrl = dwc2_readl(hsotg->regs + + u32 ctrl = dwc2_readl(hsotg, DIEPCTL(hs_ep->index)); if (hs_ep->target_frame & 0x1) ctrl |= DXEPCTL_SETODDFR; else ctrl |= DXEPCTL_SETEVENFR; - dwc2_writel(ctrl, hsotg->regs + DIEPCTL(hs_ep->index)); + dwc2_writel(hsotg, ctrl, DIEPCTL(hs_ep->index)); } dwc2_hsotg_complete_request(hsotg, hs_ep, @@ -2866,10 +2866,10 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, u32 ctrl; ints = dwc2_gadget_read_ep_interrupts(hsotg, idx, dir_in); - ctrl = dwc2_readl(hsotg->regs + epctl_reg); + ctrl = dwc2_readl(hsotg, epctl_reg); /* Clear endpoint interrupts */ - dwc2_writel(ints, hsotg->regs + epint_reg); + dwc2_writel(hsotg, ints, epint_reg); if (!hs_ep) { dev_err(hsotg->dev, "%s:Interrupt for unconfigured ep%d(%s)\n", @@ -2897,8 +2897,8 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, if (ints & DXEPINT_XFERCOMPL) { dev_dbg(hsotg->dev, "%s: XferCompl: DxEPCTL=0x%08x, DXEPTSIZ=%08x\n", - __func__, dwc2_readl(hsotg->regs + epctl_reg), - dwc2_readl(hsotg->regs + epsiz_reg)); + __func__, dwc2_readl(hsotg, epctl_reg), + dwc2_readl(hsotg, epsiz_reg)); /* In DDMA handle isochronous requests separately */ if (using_desc_dma(hsotg) && hs_ep->isochronous) { @@ -3016,7 +3016,7 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, */ static void dwc2_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg) { - u32 dsts = dwc2_readl(hsotg->regs + DSTS); + u32 dsts = dwc2_readl(hsotg, DSTS); int ep0_mps = 0, ep_mps = 8; /* @@ -3087,8 +3087,8 @@ static void dwc2_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg) dwc2_hsotg_enqueue_setup(hsotg); dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", - dwc2_readl(hsotg->regs + DIEPCTL0), - dwc2_readl(hsotg->regs + DOEPCTL0)); + dwc2_readl(hsotg, DIEPCTL0), + dwc2_readl(hsotg, DOEPCTL0)); } /** @@ -3115,7 +3115,7 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg, if (!hsotg->dedicated_fifos) return; - size = (dwc2_readl(hsotg->regs + DTXFSTS(ep->fifo_index)) & 0xffff) * 4; + size = (dwc2_readl(hsotg, DTXFSTS(ep->fifo_index)) & 0xffff) * 4; if (size < ep->fifo_size) dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index); } @@ -3216,7 +3216,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, */ /* keep other bits untouched (so e.g. forced modes are not lost) */ - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg, GUSBCFG); usbcfg &= ~(GUSBCFG_TOUTCAL_MASK | GUSBCFG_PHYIF16 | GUSBCFG_SRPCAP | GUSBCFG_HNPCAP | GUSBCFG_USBTRDTIM_MASK); @@ -3231,12 +3231,12 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, usbcfg |= hsotg->phyif | GUSBCFG_TOUTCAL(7) | (val << GUSBCFG_USBTRDTIM_SHIFT); } - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, usbcfg, GUSBCFG); dwc2_hsotg_init_fifo(hsotg); if (!is_usb_reset) - dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON); + dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON); dcfg |= DCFG_EPMISCNT(1); @@ -3257,13 +3257,13 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, if (hsotg->params.ipg_isoc_en) dcfg |= DCFG_IPG_ISOC_SUPPORDED; - dwc2_writel(dcfg, hsotg->regs + DCFG); + dwc2_writel(hsotg, dcfg, DCFG); /* Clear any pending OTG interrupts */ - dwc2_writel(0xffffffff, hsotg->regs + GOTGINT); + dwc2_writel(hsotg, 0xffffffff, GOTGINT); /* Clear any pending interrupts */ - dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, 0xffffffff, GINTSTS); intmsk = GINTSTS_ERLYSUSP | GINTSTS_SESSREQINT | GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF | GINTSTS_USBRST | GINTSTS_RESETDET | @@ -3277,22 +3277,22 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, if (!hsotg->params.external_id_pin_ctl) intmsk |= GINTSTS_CONIDSTSCHNG; - dwc2_writel(intmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, intmsk, GINTMSK); if (using_dma(hsotg)) { - dwc2_writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN | + dwc2_writel(hsotg, GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN | hsotg->params.ahbcfg, - hsotg->regs + GAHBCFG); + GAHBCFG); /* Set DDMA mode support in the core if needed */ if (using_desc_dma(hsotg)) - dwc2_set_bit(hsotg->regs + DCFG, DCFG_DESCDMA_EN); + dwc2_set_bit(hsotg, DCFG, DCFG_DESCDMA_EN); } else { - dwc2_writel(((hsotg->dedicated_fifos) ? + dwc2_writel(hsotg, ((hsotg->dedicated_fifos) ? (GAHBCFG_NP_TXF_EMP_LVL | GAHBCFG_P_TXF_EMP_LVL) : 0) | - GAHBCFG_GLBL_INTR_EN, hsotg->regs + GAHBCFG); + GAHBCFG_GLBL_INTR_EN, GAHBCFG); } /* @@ -3301,33 +3301,33 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, * interrupts. */ - dwc2_writel(((hsotg->dedicated_fifos && !using_dma(hsotg)) ? + dwc2_writel(hsotg, ((hsotg->dedicated_fifos && !using_dma(hsotg)) ? DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK : 0) | DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK | DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK, - hsotg->regs + DIEPMSK); + DIEPMSK); /* * don't need XferCompl, we get that from RXFIFO in slave mode. In * DMA mode we may need this and StsPhseRcvd. */ - dwc2_writel((using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK | + dwc2_writel(hsotg, (using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK | DOEPMSK_STSPHSERCVDMSK) : 0) | DOEPMSK_EPDISBLDMSK | DOEPMSK_AHBERRMSK | DOEPMSK_SETUPMSK, - hsotg->regs + DOEPMSK); + DOEPMSK); /* Enable BNA interrupt for DDMA */ if (using_desc_dma(hsotg)) { - dwc2_set_bit(hsotg->regs + DOEPMSK, DOEPMSK_BNAMSK); - dwc2_set_bit(hsotg->regs + DIEPMSK, DIEPMSK_BNAININTRMSK); + dwc2_set_bit(hsotg, DOEPMSK, DOEPMSK_BNAMSK); + dwc2_set_bit(hsotg, DIEPMSK, DIEPMSK_BNAININTRMSK); } - dwc2_writel(0, hsotg->regs + DAINTMSK); + dwc2_writel(hsotg, 0, DAINTMSK); dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", - dwc2_readl(hsotg->regs + DIEPCTL0), - dwc2_readl(hsotg->regs + DOEPCTL0)); + dwc2_readl(hsotg, DIEPCTL0), + dwc2_readl(hsotg, DOEPCTL0)); /* enable in and out endpoint interrupts */ dwc2_hsotg_en_gsint(hsotg, GINTSTS_OEPINT | GINTSTS_IEPINT); @@ -3345,12 +3345,12 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_hsotg_ctrl_epint(hsotg, 0, 1, 1); if (!is_usb_reset) { - dwc2_set_bit(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); + dwc2_set_bit(hsotg, DCTL, DCTL_PWRONPRGDONE); udelay(10); /* see openiboot */ - dwc2_clear_bit(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); + dwc2_clear_bit(hsotg, DCTL, DCTL_PWRONPRGDONE); } - dev_dbg(hsotg->dev, "DCTL=0x%08x\n", dwc2_readl(hsotg->regs + DCTL)); + dev_dbg(hsotg->dev, "DCTL=0x%08x\n", dwc2_readl(hsotg, DCTL)); /* * DxEPCTL_USBActEp says RO in manual, but seems to be set by @@ -3358,23 +3358,23 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, */ /* set to read 1 8byte packet */ - dwc2_writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | - DXEPTSIZ_XFERSIZE(8), hsotg->regs + DOEPTSIZ0); + dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | + DXEPTSIZ_XFERSIZE(8), DOEPTSIZ0); - dwc2_writel(dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) | + dwc2_writel(hsotg, dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) | DXEPCTL_CNAK | DXEPCTL_EPENA | DXEPCTL_USBACTEP, - hsotg->regs + DOEPCTL0); + DOEPCTL0); /* enable, but don't activate EP0in */ - dwc2_writel(dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) | - DXEPCTL_USBACTEP, hsotg->regs + DIEPCTL0); + dwc2_writel(hsotg, dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) | + DXEPCTL_USBACTEP, DIEPCTL0); /* clear global NAKs */ val = DCTL_CGOUTNAK | DCTL_CGNPINNAK; if (!is_usb_reset) val |= DCTL_SFTDISCON; - dwc2_set_bit(hsotg->regs + DCTL, val); + dwc2_set_bit(hsotg, DCTL, val); /* configure the core to support LPM */ dwc2_gadget_init_lpm(hsotg); @@ -3387,20 +3387,20 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_hsotg_enqueue_setup(hsotg); dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", - dwc2_readl(hsotg->regs + DIEPCTL0), - dwc2_readl(hsotg->regs + DOEPCTL0)); + dwc2_readl(hsotg, DIEPCTL0), + dwc2_readl(hsotg, DOEPCTL0)); } static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) { /* set the soft-disconnect bit */ - dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON); + dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON); } void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) { /* remove the soft-disconnect and let's go */ - dwc2_clear_bit(hsotg->regs + DCTL, DCTL_SFTDISCON); + dwc2_clear_bit(hsotg, DCTL, DCTL_SFTDISCON); } /** @@ -3425,7 +3425,7 @@ static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "Incomplete isoc in interrupt received:\n"); - daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); + daintmsk = dwc2_readl(hsotg, DAINTMSK); for (idx = 1; idx < hsotg->num_of_eps; idx++) { hs_ep = hsotg->eps_in[idx]; @@ -3433,17 +3433,17 @@ static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg) if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk)) continue; - epctrl = dwc2_readl(hsotg->regs + DIEPCTL(idx)); + epctrl = dwc2_readl(hsotg, DIEPCTL(idx)); if ((epctrl & DXEPCTL_EPENA) && dwc2_gadget_target_frame_elapsed(hs_ep)) { epctrl |= DXEPCTL_SNAK; epctrl |= DXEPCTL_EPDIS; - dwc2_writel(epctrl, hsotg->regs + DIEPCTL(idx)); + dwc2_writel(hsotg, epctrl, DIEPCTL(idx)); } } /* Clear interrupt */ - dwc2_writel(GINTSTS_INCOMPL_SOIN, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_INCOMPL_SOIN, GINTSTS); } /** @@ -3470,7 +3470,7 @@ static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__); - daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); + daintmsk = dwc2_readl(hsotg, DAINTMSK); daintmsk >>= DAINT_OUTEP_SHIFT; for (idx = 1; idx < hsotg->num_of_eps; idx++) { @@ -3479,24 +3479,24 @@ static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg) if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk)) continue; - epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx)); + epctrl = dwc2_readl(hsotg, DOEPCTL(idx)); if ((epctrl & DXEPCTL_EPENA) && dwc2_gadget_target_frame_elapsed(hs_ep)) { /* Unmask GOUTNAKEFF interrupt */ - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg, GINTMSK); gintmsk |= GINTSTS_GOUTNAKEFF; - dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, gintmsk, GINTMSK); - gintsts = dwc2_readl(hsotg->regs + GINTSTS); + gintsts = dwc2_readl(hsotg, GINTSTS); if (!(gintsts & GINTSTS_GOUTNAKEFF)) { - dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGOUTNAK); + dwc2_set_bit(hsotg, DCTL, DCTL_SGOUTNAK); break; } } } /* Clear interrupt */ - dwc2_writel(GINTSTS_INCOMPL_SOOUT, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_INCOMPL_SOOUT, GINTSTS); } /** @@ -3516,8 +3516,8 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw) spin_lock(&hsotg->lock); irq_retry: - gintsts = dwc2_readl(hsotg->regs + GINTSTS); - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintsts = dwc2_readl(hsotg, GINTSTS); + gintmsk = dwc2_readl(hsotg, GINTMSK); dev_dbg(hsotg->dev, "%s: %08x %08x (%08x) retry %d\n", __func__, gintsts, gintsts & gintmsk, gintmsk, retry_count); @@ -3527,7 +3527,7 @@ irq_retry: if (gintsts & GINTSTS_RESETDET) { dev_dbg(hsotg->dev, "%s: USBRstDet\n", __func__); - dwc2_writel(GINTSTS_RESETDET, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_RESETDET, GINTSTS); /* This event must be used only if controller is suspended */ if (hsotg->lx_state == DWC2_L2) { @@ -3537,34 +3537,34 @@ irq_retry: } if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) { - u32 usb_status = dwc2_readl(hsotg->regs + GOTGCTL); + u32 usb_status = dwc2_readl(hsotg, GOTGCTL); u32 connected = hsotg->connected; dev_dbg(hsotg->dev, "%s: USBRst\n", __func__); dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n", - dwc2_readl(hsotg->regs + GNPTXSTS)); + dwc2_readl(hsotg, GNPTXSTS)); - dwc2_writel(GINTSTS_USBRST, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_USBRST, GINTSTS); /* Report disconnection if it is not already done. */ dwc2_hsotg_disconnect(hsotg); /* Reset device address to zero */ - dwc2_clear_bit(hsotg->regs + DCFG, DCFG_DEVADDR_MASK); + dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK); if (usb_status & GOTGCTL_BSESVLD && connected) dwc2_hsotg_core_init_disconnected(hsotg, true); } if (gintsts & GINTSTS_ENUMDONE) { - dwc2_writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_ENUMDONE, GINTSTS); dwc2_hsotg_irq_enumdone(hsotg); } if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) { - u32 daint = dwc2_readl(hsotg->regs + DAINT); - u32 daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); + u32 daint = dwc2_readl(hsotg, DAINT); + u32 daintmsk = dwc2_readl(hsotg, DAINTMSK); u32 daint_out, daint_in; int ep; @@ -3623,7 +3623,7 @@ irq_retry: if (gintsts & GINTSTS_ERLYSUSP) { dev_dbg(hsotg->dev, "GINTSTS_ErlySusp\n"); - dwc2_writel(GINTSTS_ERLYSUSP, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_ERLYSUSP, GINTSTS); } /* @@ -3639,12 +3639,12 @@ irq_retry: u32 daintmsk; struct dwc2_hsotg_ep *hs_ep; - daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); + daintmsk = dwc2_readl(hsotg, DAINTMSK); daintmsk >>= DAINT_OUTEP_SHIFT; /* Mask this interrupt */ - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg, GINTMSK); gintmsk &= ~GINTSTS_GOUTNAKEFF; - dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, gintmsk, GINTMSK); dev_dbg(hsotg->dev, "GOUTNakEff triggered\n"); for (idx = 1; idx < hsotg->num_of_eps; idx++) { @@ -3653,12 +3653,12 @@ irq_retry: if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk)) continue; - epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx)); + epctrl = dwc2_readl(hsotg, DOEPCTL(idx)); if (epctrl & DXEPCTL_EPENA) { epctrl |= DXEPCTL_SNAK; epctrl |= DXEPCTL_EPDIS; - dwc2_writel(epctrl, hsotg->regs + DOEPCTL(idx)); + dwc2_writel(hsotg, epctrl, DOEPCTL(idx)); } } @@ -3668,7 +3668,7 @@ irq_retry: if (gintsts & GINTSTS_GINNAKEFF) { dev_info(hsotg->dev, "GINNakEff triggered\n"); - dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGNPINNAK); + dwc2_set_bit(hsotg, DCTL, DCTL_CGNPINNAK); dwc2_hsotg_dump(hsotg); } @@ -3708,7 +3708,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, if (hs_ep->dir_in) { if (hsotg->dedicated_fifos || hs_ep->periodic) { - dwc2_set_bit(hsotg->regs + epctrl_reg, DXEPCTL_SNAK); + dwc2_set_bit(hsotg, epctrl_reg, DXEPCTL_SNAK); /* Wait for Nak effect */ if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_INEPNAKEFF, 100)) @@ -3716,7 +3716,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, "%s: timeout DIEPINT.NAKEFF\n", __func__); } else { - dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGNPINNAK); + dwc2_set_bit(hsotg, DCTL, DCTL_SGNPINNAK); /* Wait for Nak effect */ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_GINNAKEFF, 100)) @@ -3725,8 +3725,8 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, __func__); } } else { - if (!(dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_GOUTNAKEFF)) - dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGOUTNAK); + if (!(dwc2_readl(hsotg, GINTSTS) & GINTSTS_GOUTNAKEFF)) + dwc2_set_bit(hsotg, DCTL, DCTL_SGOUTNAK); /* Wait for global nak to take effect */ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, @@ -3736,7 +3736,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, } /* Disable ep */ - dwc2_set_bit(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK); + dwc2_set_bit(hsotg, epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK); /* Wait for ep to be disabled */ if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_EPDISBLD, 100)) @@ -3744,7 +3744,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, "%s: timeout DOEPCTL.EPDisable\n", __func__); /* Clear EPDISBLD interrupt */ - dwc2_set_bit(hsotg->regs + epint_reg, DXEPINT_EPDISBLD); + dwc2_set_bit(hsotg, epint_reg, DXEPINT_EPDISBLD); if (hs_ep->dir_in) { unsigned short fifo_index; @@ -3759,11 +3759,11 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, /* Clear Global In NP NAK in Shared FIFO for non periodic ep */ if (!hsotg->dedicated_fifos && !hs_ep->periodic) - dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGNPINNAK); + dwc2_set_bit(hsotg, DCTL, DCTL_CGNPINNAK); } else { /* Remove global NAKs */ - dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGOUTNAK); + dwc2_set_bit(hsotg, DCTL, DCTL_CGOUTNAK); } } @@ -3831,7 +3831,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, /* note, we handle this here instead of dwc2_hsotg_set_ep_maxpacket */ epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); - epctrl = dwc2_readl(hsotg->regs + epctrl_reg); + epctrl = dwc2_readl(hsotg, epctrl_reg); dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n", __func__, epctrl, epctrl_reg); @@ -3879,13 +3879,13 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, hs_ep->compl_desc = 0; if (dir_in) { hs_ep->periodic = 1; - mask = dwc2_readl(hsotg->regs + DIEPMSK); + mask = dwc2_readl(hsotg, DIEPMSK); mask |= DIEPMSK_NAKMSK; - dwc2_writel(mask, hsotg->regs + DIEPMSK); + dwc2_writel(hsotg, mask, DIEPMSK); } else { - mask = dwc2_readl(hsotg->regs + DOEPMSK); + mask = dwc2_readl(hsotg, DOEPMSK); mask |= DOEPMSK_OUTTKNEPDISMSK; - dwc2_writel(mask, hsotg->regs + DOEPMSK); + dwc2_writel(hsotg, mask, DOEPMSK); } break; @@ -3920,7 +3920,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, for (i = 1; i < hsotg->num_of_eps; ++i) { if (hsotg->fifo_map & (1 << i)) continue; - val = dwc2_readl(hsotg->regs + DPTXFSIZN(i)); + val = dwc2_readl(hsotg, DPTXFSIZN(i)); val = (val >> FIFOSIZE_DEPTH_SHIFT) * 4; if (val < size) continue; @@ -3958,7 +3958,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, * to 4.00a (including both). Also for FS_IOT_1.00a * and HS_IOT_1.00a. */ - u32 gsnpsid = dwc2_readl(hsotg->regs + GSNPSID); + u32 gsnpsid = dwc2_readl(hsotg, GSNPSID); if ((gsnpsid >= DWC2_CORE_REV_2_72a && gsnpsid <= DWC2_CORE_REV_4_00a) || @@ -3970,9 +3970,9 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, dev_dbg(hsotg->dev, "%s: write DxEPCTL=0x%08x\n", __func__, epctrl); - dwc2_writel(epctrl, hsotg->regs + epctrl_reg); + dwc2_writel(hsotg, epctrl, epctrl_reg); dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x\n", - __func__, dwc2_readl(hsotg->regs + epctrl_reg)); + __func__, dwc2_readl(hsotg, epctrl_reg)); /* enable the endpoint interrupt */ dwc2_hsotg_ctrl_epint(hsotg, index, dir_in, 1); @@ -4021,7 +4021,7 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) spin_lock_irqsave(&hsotg->lock, flags); - ctrl = dwc2_readl(hsotg->regs + epctrl_reg); + ctrl = dwc2_readl(hsotg, epctrl_reg); if (ctrl & DXEPCTL_EPENA) dwc2_hsotg_ep_stop_xfr(hsotg, hs_ep); @@ -4031,7 +4031,7 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) ctrl |= DXEPCTL_SNAK; dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); - dwc2_writel(ctrl, hsotg->regs + epctrl_reg); + dwc2_writel(hsotg, ctrl, epctrl_reg); /* disable endpoint interrupts */ dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0); @@ -4138,7 +4138,7 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now) if (hs_ep->dir_in) { epreg = DIEPCTL(index); - epctl = dwc2_readl(hs->regs + epreg); + epctl = dwc2_readl(hs, epreg); if (value) { epctl |= DXEPCTL_STALL | DXEPCTL_SNAK; @@ -4151,10 +4151,10 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now) xfertype == DXEPCTL_EPTYPE_INTERRUPT) epctl |= DXEPCTL_SETD0PID; } - dwc2_writel(epctl, hs->regs + epreg); + dwc2_writel(hs, epctl, epreg); } else { epreg = DOEPCTL(index); - epctl = dwc2_readl(hs->regs + epreg); + epctl = dwc2_readl(hs, epreg); if (value) { epctl |= DXEPCTL_STALL; @@ -4165,7 +4165,7 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now) xfertype == DXEPCTL_EPTYPE_INTERRUPT) epctl |= DXEPCTL_SETD0PID; } - dwc2_writel(epctl, hs->regs + epreg); + dwc2_writel(hs, epctl, epreg); } hs_ep->halted = value; @@ -4213,29 +4213,29 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg) u32 usbcfg; /* unmask subset of endpoint interrupts */ - dwc2_writel(DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | + dwc2_writel(hsotg, DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK, - hsotg->regs + DIEPMSK); + DIEPMSK); - dwc2_writel(DOEPMSK_SETUPMSK | DOEPMSK_AHBERRMSK | + dwc2_writel(hsotg, DOEPMSK_SETUPMSK | DOEPMSK_AHBERRMSK | DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK, - hsotg->regs + DOEPMSK); + DOEPMSK); - dwc2_writel(0, hsotg->regs + DAINTMSK); + dwc2_writel(hsotg, 0, DAINTMSK); /* Be in disconnected state until gadget is registered */ - dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON); + dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON); /* setup fifos */ dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n", - dwc2_readl(hsotg->regs + GRXFSIZ), - dwc2_readl(hsotg->regs + GNPTXFSIZ)); + dwc2_readl(hsotg, GRXFSIZ), + dwc2_readl(hsotg, GNPTXFSIZ)); dwc2_hsotg_init_fifo(hsotg); /* keep other bits untouched (so e.g. forced modes are not lost) */ - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg, GUSBCFG); usbcfg &= ~(GUSBCFG_TOUTCAL_MASK | GUSBCFG_PHYIF16 | GUSBCFG_SRPCAP | GUSBCFG_HNPCAP | GUSBCFG_USBTRDTIM_MASK); @@ -4243,10 +4243,10 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg) trdtim = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5; usbcfg |= hsotg->phyif | GUSBCFG_TOUTCAL(7) | (trdtim << GUSBCFG_USBTRDTIM_SHIFT); - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, usbcfg, GUSBCFG); if (using_dma(hsotg)) - dwc2_set_bit(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN); + dwc2_set_bit(hsotg, GAHBCFG, GAHBCFG_DMA_EN); } /** @@ -4536,9 +4536,9 @@ static void dwc2_hsotg_initep(struct dwc2_hsotg *hsotg, u32 next = DXEPCTL_NEXTEP((epnum + 1) % 15); if (dir_in) - dwc2_writel(next, hsotg->regs + DIEPCTL(epnum)); + dwc2_writel(hsotg, next, DIEPCTL(epnum)); else - dwc2_writel(next, hsotg->regs + DOEPCTL(epnum)); + dwc2_writel(hsotg, next, DOEPCTL(epnum)); } } @@ -4607,24 +4607,23 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg) { #ifdef DEBUG struct device *dev = hsotg->dev; - void __iomem *regs = hsotg->regs; u32 val; int idx; dev_info(dev, "DCFG=0x%08x, DCTL=0x%08x, DIEPMSK=%08x\n", - dwc2_readl(regs + DCFG), dwc2_readl(regs + DCTL), - dwc2_readl(regs + DIEPMSK)); + dwc2_readl(hsotg, DCFG), dwc2_readl(hsotg, DCTL), + dwc2_readl(hsotg, DIEPMSK)); dev_info(dev, "GAHBCFG=0x%08x, GHWCFG1=0x%08x\n", - dwc2_readl(regs + GAHBCFG), dwc2_readl(regs + GHWCFG1)); + dwc2_readl(hsotg, GAHBCFG), dwc2_readl(hsotg, GHWCFG1)); dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n", - dwc2_readl(regs + GRXFSIZ), dwc2_readl(regs + GNPTXFSIZ)); + dwc2_readl(hsotg, GRXFSIZ), dwc2_readl(hsotg, GNPTXFSIZ)); /* show periodic fifo settings */ for (idx = 1; idx < hsotg->num_of_eps; idx++) { - val = dwc2_readl(regs + DPTXFSIZN(idx)); + val = dwc2_readl(hsotg, DPTXFSIZN(idx)); dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx, val >> FIFOSIZE_DEPTH_SHIFT, val & FIFOSIZE_STARTADDR_MASK); @@ -4633,20 +4632,20 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg) for (idx = 0; idx < hsotg->num_of_eps; idx++) { dev_info(dev, "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx, - dwc2_readl(regs + DIEPCTL(idx)), - dwc2_readl(regs + DIEPTSIZ(idx)), - dwc2_readl(regs + DIEPDMA(idx))); + dwc2_readl(hsotg, DIEPCTL(idx)), + dwc2_readl(hsotg, DIEPTSIZ(idx)), + dwc2_readl(hsotg, DIEPDMA(idx))); - val = dwc2_readl(regs + DOEPCTL(idx)); + val = dwc2_readl(hsotg, DOEPCTL(idx)); dev_info(dev, "ep%d-out: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", - idx, dwc2_readl(regs + DOEPCTL(idx)), - dwc2_readl(regs + DOEPTSIZ(idx)), - dwc2_readl(regs + DOEPDMA(idx))); + idx, dwc2_readl(hsotg, DOEPCTL(idx)), + dwc2_readl(hsotg, DOEPTSIZ(idx)), + dwc2_readl(hsotg, DOEPDMA(idx))); } dev_info(dev, "DVBUSDIS=0x%08x, DVBUSPULSE=%08x\n", - dwc2_readl(regs + DVBUSDIS), dwc2_readl(regs + DVBUSPULSE)); + dwc2_readl(hsotg, DVBUSDIS), dwc2_readl(hsotg, DVBUSPULSE)); #endif } @@ -4835,15 +4834,15 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) /* Backup dev regs */ dr = &hsotg->dr_backup; - dr->dcfg = dwc2_readl(hsotg->regs + DCFG); - dr->dctl = dwc2_readl(hsotg->regs + DCTL); - dr->daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); - dr->diepmsk = dwc2_readl(hsotg->regs + DIEPMSK); - dr->doepmsk = dwc2_readl(hsotg->regs + DOEPMSK); + dr->dcfg = dwc2_readl(hsotg, DCFG); + dr->dctl = dwc2_readl(hsotg, DCTL); + dr->daintmsk = dwc2_readl(hsotg, DAINTMSK); + dr->diepmsk = dwc2_readl(hsotg, DIEPMSK); + dr->doepmsk = dwc2_readl(hsotg, DOEPMSK); for (i = 0; i < hsotg->num_of_eps; i++) { /* Backup IN EPs */ - dr->diepctl[i] = dwc2_readl(hsotg->regs + DIEPCTL(i)); + dr->diepctl[i] = dwc2_readl(hsotg, DIEPCTL(i)); /* Ensure DATA PID is correctly configured */ if (dr->diepctl[i] & DXEPCTL_DPID) @@ -4851,11 +4850,11 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) else dr->diepctl[i] |= DXEPCTL_SETD0PID; - dr->dieptsiz[i] = dwc2_readl(hsotg->regs + DIEPTSIZ(i)); - dr->diepdma[i] = dwc2_readl(hsotg->regs + DIEPDMA(i)); + dr->dieptsiz[i] = dwc2_readl(hsotg, DIEPTSIZ(i)); + dr->diepdma[i] = dwc2_readl(hsotg, DIEPDMA(i)); /* Backup OUT EPs */ - dr->doepctl[i] = dwc2_readl(hsotg->regs + DOEPCTL(i)); + dr->doepctl[i] = dwc2_readl(hsotg, DOEPCTL(i)); /* Ensure DATA PID is correctly configured */ if (dr->doepctl[i] & DXEPCTL_DPID) @@ -4863,9 +4862,9 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) else dr->doepctl[i] |= DXEPCTL_SETD0PID; - dr->doeptsiz[i] = dwc2_readl(hsotg->regs + DOEPTSIZ(i)); - dr->doepdma[i] = dwc2_readl(hsotg->regs + DOEPDMA(i)); - dr->dtxfsiz[i] = dwc2_readl(hsotg->regs + DPTXFSIZN(i)); + dr->doeptsiz[i] = dwc2_readl(hsotg, DOEPTSIZ(i)); + dr->doepdma[i] = dwc2_readl(hsotg, DOEPDMA(i)); + dr->dtxfsiz[i] = dwc2_readl(hsotg, DPTXFSIZN(i)); } dr->valid = true; return 0; @@ -4898,17 +4897,17 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup) dr->valid = false; if (!remote_wakeup) - dwc2_writel(dr->dctl, hsotg->regs + DCTL); + dwc2_writel(hsotg, dr->dctl, DCTL); - dwc2_writel(dr->daintmsk, hsotg->regs + DAINTMSK); - dwc2_writel(dr->diepmsk, hsotg->regs + DIEPMSK); - dwc2_writel(dr->doepmsk, hsotg->regs + DOEPMSK); + dwc2_writel(hsotg, dr->daintmsk, DAINTMSK); + dwc2_writel(hsotg, dr->diepmsk, DIEPMSK); + dwc2_writel(hsotg, dr->doepmsk, DOEPMSK); for (i = 0; i < hsotg->num_of_eps; i++) { /* Restore IN EPs */ - dwc2_writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i)); - dwc2_writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i)); - dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i)); + dwc2_writel(hsotg, dr->dieptsiz[i], DIEPTSIZ(i)); + dwc2_writel(hsotg, dr->diepdma[i], DIEPDMA(i)); + dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i)); /** WA for enabled EPx's IN in DDMA mode. On entering to * hibernation wrong value read and saved from DIEPDMAx, * as result BNA interrupt asserted on hibernation exit @@ -4917,10 +4916,10 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup) if (hsotg->params.g_dma_desc && (dr->diepctl[i] & DXEPCTL_EPENA)) dr->diepdma[i] = hsotg->eps_in[i]->desc_list_dma; - dwc2_writel(dr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i)); - dwc2_writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i)); + dwc2_writel(hsotg, dr->dtxfsiz[i], DPTXFSIZN(i)); + dwc2_writel(hsotg, dr->diepctl[i], DIEPCTL(i)); /* Restore OUT EPs */ - dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i)); + dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i)); /* WA for enabled EPx's OUT in DDMA mode. On entering to * hibernation wrong value read and saved from DOEPDMAx, * as result BNA interrupt asserted on hibernation exit @@ -4929,8 +4928,8 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup) if (hsotg->params.g_dma_desc && (dr->doepctl[i] & DXEPCTL_EPENA)) dr->doepdma[i] = hsotg->eps_out[i]->desc_list_dma; - dwc2_writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i)); - dwc2_writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i)); + dwc2_writel(hsotg, dr->doepdma[i], DOEPDMA(i)); + dwc2_writel(hsotg, dr->doepctl[i], DOEPCTL(i)); } return 0; @@ -4954,9 +4953,8 @@ void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0; val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT; val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0; - dwc2_writel(val, hsotg->regs + GLPMCFG); - dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg->regs - + GLPMCFG)); + dwc2_writel(hsotg, val, GLPMCFG); + dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG)); } /** @@ -4989,40 +4987,40 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) gpwrdn = GPWRDN_PWRDNRSTN; gpwrdn |= GPWRDN_PMUACTV; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Set flag to indicate that we are in hibernation */ hsotg->hibernated = 1; /* Enable interrupts from wake up logic */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_PMUINTSEL; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Unmask device mode interrupts in GPWRDN */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_RST_DET_MSK; gpwrdn |= GPWRDN_LNSTSCHG_MSK; gpwrdn |= GPWRDN_STS_CHGINT_MSK; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Enable Power Down Clamp */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_PWRDNCLMP; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Switch off VDD */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_PWRDNSWTCH; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Save gpwrdn register for further usage if stschng interrupt */ - hsotg->gr_backup.gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + hsotg->gr_backup.gpwrdn = dwc2_readl(hsotg, GPWRDN); dev_dbg(hsotg->dev, "Hibernation completed\n"); return ret; @@ -5064,46 +5062,46 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, if (!reset) { /* Clear all pending interupts */ - dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, 0xffffffff, GINTSTS); } /* De-assert Restore */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_RESTORE; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); if (!rem_wakeup) { - pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl = dwc2_readl(hsotg, PCGCTL); pcgcctl &= ~PCGCTL_RSTPDWNMODULE; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); } /* Restore GUSBCFG, DCFG and DCTL */ - dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG); - dwc2_writel(dr->dcfg, hsotg->regs + DCFG); - dwc2_writel(dr->dctl, hsotg->regs + DCTL); + dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG); + dwc2_writel(hsotg, dr->dcfg, DCFG); + dwc2_writel(hsotg, dr->dctl, DCTL); /* De-assert Wakeup Logic */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_PMUACTV; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); if (rem_wakeup) { udelay(10); /* Start Remote Wakeup Signaling */ - dwc2_writel(dr->dctl | DCTL_RMTWKUPSIG, hsotg->regs + DCTL); + dwc2_writel(hsotg, dr->dctl | DCTL_RMTWKUPSIG, DCTL); } else { udelay(50); /* Set Device programming done bit */ - dctl = dwc2_readl(hsotg->regs + DCTL); + dctl = dwc2_readl(hsotg, DCTL); dctl |= DCTL_PWRONPRGDONE; - dwc2_writel(dctl, hsotg->regs + DCTL); + dwc2_writel(hsotg, dctl, DCTL); } /* Wait for interrupts which must be cleared */ mdelay(2); /* Clear all pending interupts */ - dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, 0xffffffff, GINTSTS); /* Restore global registers */ ret = dwc2_restore_global_registers(hsotg); @@ -5123,9 +5121,9 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, if (rem_wakeup) { mdelay(10); - dctl = dwc2_readl(hsotg->regs + DCTL); + dctl = dwc2_readl(hsotg, DCTL); dctl &= ~DCTL_RMTWKUPSIG; - dwc2_writel(dctl, hsotg->regs + DCTL); + dwc2_writel(hsotg, dctl, DCTL); } hsotg->hibernated = 0; diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index b1104be3429c..72f4f0fb614d 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -75,10 +75,10 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) u32 intmsk; /* Clear any pending OTG Interrupts */ - dwc2_writel(0xffffffff, hsotg->regs + GOTGINT); + dwc2_writel(hsotg, 0xffffffff, GOTGINT); /* Clear any pending interrupts */ - dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, 0xffffffff, GINTSTS); /* Enable the interrupts in the GINTMSK */ intmsk = GINTSTS_MODEMIS | GINTSTS_OTGINT; @@ -94,7 +94,7 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) if (dwc2_is_device_mode(hsotg) && hsotg->params.lpm) intmsk |= GINTSTS_LPMTRANRCVD; - dwc2_writel(intmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, intmsk, GINTMSK); } /* @@ -117,10 +117,10 @@ static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg) } dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val); - hcfg = dwc2_readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg, HCFG); hcfg &= ~HCFG_FSLSPCLKSEL_MASK; hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT; - dwc2_writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hsotg, hcfg, HCFG); } static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) @@ -135,10 +135,10 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) if (select_phy) { dev_dbg(hsotg->dev, "FS PHY selected\n"); - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg, GUSBCFG); if (!(usbcfg & GUSBCFG_PHYSEL)) { usbcfg |= GUSBCFG_PHYSEL; - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, usbcfg, GUSBCFG); /* Reset after a PHY select */ retval = dwc2_core_reset(hsotg, false); @@ -151,7 +151,7 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) } if (hsotg->params.activate_stm_fs_transceiver) { - ggpio = dwc2_readl(hsotg->regs + GGPIO); + ggpio = dwc2_readl(hsotg, GGPIO); if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) { dev_dbg(hsotg->dev, "Activating transceiver\n"); /* @@ -159,7 +159,7 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) * core configuration register. */ ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN; - dwc2_writel(ggpio, hsotg->regs + GGPIO); + dwc2_writel(hsotg, ggpio, GGPIO); } } } @@ -176,18 +176,18 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) dev_dbg(hsotg->dev, "FS PHY enabling I2C\n"); /* Program GUSBCFG.OtgUtmiFsSel to I2C */ - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg, GUSBCFG); usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL; - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, usbcfg, GUSBCFG); /* Program GI2CCTL.I2CEn */ - i2cctl = dwc2_readl(hsotg->regs + GI2CCTL); + i2cctl = dwc2_readl(hsotg, GI2CCTL); i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK; i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT; i2cctl &= ~GI2CCTL_I2CEN; - dwc2_writel(i2cctl, hsotg->regs + GI2CCTL); + dwc2_writel(hsotg, i2cctl, GI2CCTL); i2cctl |= GI2CCTL_I2CEN; - dwc2_writel(i2cctl, hsotg->regs + GI2CCTL); + dwc2_writel(hsotg, i2cctl, GI2CCTL); } return retval; @@ -201,7 +201,7 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) if (!select_phy) return 0; - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg, GUSBCFG); usbcfg_old = usbcfg; /* @@ -236,7 +236,7 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) } if (usbcfg != usbcfg_old) { - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, usbcfg, GUSBCFG); /* Reset after setting the PHY parameters */ retval = dwc2_core_reset(hsotg, false); @@ -273,15 +273,15 @@ static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && hsotg->params.ulpi_fs_ls) { dev_dbg(hsotg->dev, "Setting ULPI FSLS\n"); - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg, GUSBCFG); usbcfg |= GUSBCFG_ULPI_FS_LS; usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M; - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, usbcfg, GUSBCFG); } else { - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg, GUSBCFG); usbcfg &= ~GUSBCFG_ULPI_FS_LS; usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M; - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, usbcfg, GUSBCFG); } return retval; @@ -289,7 +289,7 @@ static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg) { - u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); + u32 ahbcfg = dwc2_readl(hsotg, GAHBCFG); switch (hsotg->hw_params.arch) { case GHWCFG2_EXT_DMA_ARCH: @@ -316,7 +316,7 @@ static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg) else hsotg->params.dma_desc_enable = false; - dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG); + dwc2_writel(hsotg, ahbcfg, GAHBCFG); return 0; } @@ -325,7 +325,7 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg) { u32 usbcfg; - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg, GUSBCFG); usbcfg &= ~(GUSBCFG_HNPCAP | GUSBCFG_SRPCAP); switch (hsotg->hw_params.op_mode) { @@ -353,7 +353,7 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg) break; } - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, usbcfg, GUSBCFG); } static int dwc2_vbus_supply_init(struct dwc2_hsotg *hsotg) @@ -390,16 +390,16 @@ static void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "%s()\n", __func__); /* Disable all interrupts */ - dwc2_writel(0, hsotg->regs + GINTMSK); - dwc2_writel(0, hsotg->regs + HAINTMSK); + dwc2_writel(hsotg, 0, GINTMSK); + dwc2_writel(hsotg, 0, HAINTMSK); /* Enable the common interrupts */ dwc2_enable_common_interrupts(hsotg); /* Enable host mode interrupts without disturbing common interrupts */ - intmsk = dwc2_readl(hsotg->regs + GINTMSK); + intmsk = dwc2_readl(hsotg, GINTMSK); intmsk |= GINTSTS_DISCONNINT | GINTSTS_PRTINT | GINTSTS_HCHINT; - dwc2_writel(intmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, intmsk, GINTMSK); } /** @@ -409,12 +409,12 @@ static void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg) */ static void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg) { - u32 intmsk = dwc2_readl(hsotg->regs + GINTMSK); + u32 intmsk = dwc2_readl(hsotg, GINTMSK); /* Disable host mode interrupts without disturbing common interrupts */ intmsk &= ~(GINTSTS_SOF | GINTSTS_PRTINT | GINTSTS_HCHINT | GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP | GINTSTS_DISCONNINT); - dwc2_writel(intmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, intmsk, GINTMSK); } /* @@ -494,37 +494,37 @@ static void dwc2_config_fifos(struct dwc2_hsotg *hsotg) dwc2_calculate_dynamic_fifo(hsotg); /* Rx FIFO */ - grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ); + grxfsiz = dwc2_readl(hsotg, GRXFSIZ); dev_dbg(hsotg->dev, "initial grxfsiz=%08x\n", grxfsiz); grxfsiz &= ~GRXFSIZ_DEPTH_MASK; grxfsiz |= params->host_rx_fifo_size << GRXFSIZ_DEPTH_SHIFT & GRXFSIZ_DEPTH_MASK; - dwc2_writel(grxfsiz, hsotg->regs + GRXFSIZ); + dwc2_writel(hsotg, grxfsiz, GRXFSIZ); dev_dbg(hsotg->dev, "new grxfsiz=%08x\n", - dwc2_readl(hsotg->regs + GRXFSIZ)); + dwc2_readl(hsotg, GRXFSIZ)); /* Non-periodic Tx FIFO */ dev_dbg(hsotg->dev, "initial gnptxfsiz=%08x\n", - dwc2_readl(hsotg->regs + GNPTXFSIZ)); + dwc2_readl(hsotg, GNPTXFSIZ)); nptxfsiz = params->host_nperio_tx_fifo_size << FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; nptxfsiz |= params->host_rx_fifo_size << FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; - dwc2_writel(nptxfsiz, hsotg->regs + GNPTXFSIZ); + dwc2_writel(hsotg, nptxfsiz, GNPTXFSIZ); dev_dbg(hsotg->dev, "new gnptxfsiz=%08x\n", - dwc2_readl(hsotg->regs + GNPTXFSIZ)); + dwc2_readl(hsotg, GNPTXFSIZ)); /* Periodic Tx FIFO */ dev_dbg(hsotg->dev, "initial hptxfsiz=%08x\n", - dwc2_readl(hsotg->regs + HPTXFSIZ)); + dwc2_readl(hsotg, HPTXFSIZ)); hptxfsiz = params->host_perio_tx_fifo_size << FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; hptxfsiz |= (params->host_rx_fifo_size + params->host_nperio_tx_fifo_size) << FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; - dwc2_writel(hptxfsiz, hsotg->regs + HPTXFSIZ); + dwc2_writel(hsotg, hptxfsiz, HPTXFSIZ); dev_dbg(hsotg->dev, "new hptxfsiz=%08x\n", - dwc2_readl(hsotg->regs + HPTXFSIZ)); + dwc2_readl(hsotg, HPTXFSIZ)); if (hsotg->params.en_multiple_tx_fifo && hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_91a) { @@ -533,14 +533,14 @@ static void dwc2_config_fifos(struct dwc2_hsotg *hsotg) * Global DFIFOCFG calculation for Host mode - * include RxFIFO, NPTXFIFO and HPTXFIFO */ - dfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG); + dfifocfg = dwc2_readl(hsotg, GDFIFOCFG); dfifocfg &= ~GDFIFOCFG_EPINFOBASE_MASK; dfifocfg |= (params->host_rx_fifo_size + params->host_nperio_tx_fifo_size + params->host_perio_tx_fifo_size) << GDFIFOCFG_EPINFOBASE_SHIFT & GDFIFOCFG_EPINFOBASE_MASK; - dwc2_writel(dfifocfg, hsotg->regs + GDFIFOCFG); + dwc2_writel(hsotg, dfifocfg, GDFIFOCFG); } } @@ -560,8 +560,8 @@ u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg) u32 hprt0; int clock = 60; /* default value */ - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); - hprt0 = dwc2_readl(hsotg->regs + HPRT0); + usbcfg = dwc2_readl(hsotg, GUSBCFG); + hprt0 = dwc2_readl(hsotg, HPRT0); if (!(usbcfg & GUSBCFG_PHYSEL) && (usbcfg & GUSBCFG_ULPI_UTMI_SEL) && !(usbcfg & GUSBCFG_PHYIF16)) @@ -603,7 +603,6 @@ u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg) */ void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes) { - u32 __iomem *fifo = hsotg->regs + HCFIFO(0); u32 *data_buf = (u32 *)dest; int word_count = (bytes + 3) / 4; int i; @@ -617,7 +616,7 @@ void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes) dev_vdbg(hsotg->dev, "%s(%p,%p,%d)\n", __func__, hsotg, dest, bytes); for (i = 0; i < word_count; i++, data_buf++) - *data_buf = dwc2_readl(fifo); + *data_buf = dwc2_readl(hsotg, HCFIFO(0)); } /** @@ -646,10 +645,10 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg, if (!chan) return; - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); - hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chan->hc_num)); - hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chan->hc_num)); - hc_dma = dwc2_readl(hsotg->regs + HCDMA(chan->hc_num)); + hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num)); + hcsplt = dwc2_readl(hsotg, HCSPLT(chan->hc_num)); + hctsiz = dwc2_readl(hsotg, HCTSIZ(chan->hc_num)); + hc_dma = dwc2_readl(hsotg, HCDMA(chan->hc_num)); dev_dbg(hsotg->dev, " Assigned to channel %p:\n", chan); dev_dbg(hsotg->dev, " hcchar 0x%08x, hcsplt 0x%08x\n", @@ -797,7 +796,7 @@ static void dwc2_hc_enable_slave_ints(struct dwc2_hsotg *hsotg, break; } - dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); + dwc2_writel(hsotg, hcintmsk, HCINTMSK(chan->hc_num)); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk); } @@ -834,7 +833,7 @@ static void dwc2_hc_enable_dma_ints(struct dwc2_hsotg *hsotg, } } - dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); + dwc2_writel(hsotg, hcintmsk, HCINTMSK(chan->hc_num)); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk); } @@ -855,16 +854,16 @@ static void dwc2_hc_enable_ints(struct dwc2_hsotg *hsotg, } /* Enable the top level host channel interrupt */ - intmsk = dwc2_readl(hsotg->regs + HAINTMSK); + intmsk = dwc2_readl(hsotg, HAINTMSK); intmsk |= 1 << chan->hc_num; - dwc2_writel(intmsk, hsotg->regs + HAINTMSK); + dwc2_writel(hsotg, intmsk, HAINTMSK); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "set HAINTMSK to %08x\n", intmsk); /* Make sure host channel interrupts are enabled */ - intmsk = dwc2_readl(hsotg->regs + GINTMSK); + intmsk = dwc2_readl(hsotg, GINTMSK); intmsk |= GINTSTS_HCHINT; - dwc2_writel(intmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, intmsk, GINTMSK); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "set GINTMSK to %08x\n", intmsk); } @@ -893,7 +892,7 @@ static void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) /* Clear old interrupt conditions for this host channel */ hcintmsk = 0xffffffff; hcintmsk &= ~HCINTMSK_RESERVED14_31; - dwc2_writel(hcintmsk, hsotg->regs + HCINT(hc_num)); + dwc2_writel(hsotg, hcintmsk, HCINT(hc_num)); /* Enable channel interrupts required for this transfer */ dwc2_hc_enable_ints(hsotg, chan); @@ -910,7 +909,7 @@ static void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) hcchar |= HCCHAR_LSPDDEV; hcchar |= chan->ep_type << HCCHAR_EPTYPE_SHIFT & HCCHAR_EPTYPE_MASK; hcchar |= chan->max_packet << HCCHAR_MPS_SHIFT & HCCHAR_MPS_MASK; - dwc2_writel(hcchar, hsotg->regs + HCCHAR(hc_num)); + dwc2_writel(hsotg, hcchar, HCCHAR(hc_num)); if (dbg_hc(chan)) { dev_vdbg(hsotg->dev, "set HCCHAR(%d) to %08x\n", hc_num, hcchar); @@ -964,7 +963,7 @@ static void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) } } - dwc2_writel(hcsplt, hsotg->regs + HCSPLT(hc_num)); + dwc2_writel(hsotg, hcsplt, HCSPLT(hc_num)); } /** @@ -1034,14 +1033,14 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, u32 hcintmsk = HCINTMSK_CHHLTD; dev_vdbg(hsotg->dev, "dequeue/error\n"); - dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); + dwc2_writel(hsotg, hcintmsk, HCINTMSK(chan->hc_num)); /* * Make sure no other interrupts besides halt are currently * pending. Handling another interrupt could cause a crash due * to the QTD and QH state. */ - dwc2_writel(~hcintmsk, hsotg->regs + HCINT(chan->hc_num)); + dwc2_writel(hsotg, ~hcintmsk, HCINT(chan->hc_num)); /* * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR @@ -1050,7 +1049,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, */ chan->halt_status = halt_status; - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num)); if (!(hcchar & HCCHAR_CHENA)) { /* * The channel is either already halted or it hasn't @@ -1078,7 +1077,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, return; } - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num)); /* No need to set the bit in DDMA for disabling the channel */ /* TODO check it everywhere channel is disabled */ @@ -1101,7 +1100,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, if (chan->ep_type == USB_ENDPOINT_XFER_CONTROL || chan->ep_type == USB_ENDPOINT_XFER_BULK) { dev_vdbg(hsotg->dev, "control/bulk\n"); - nptxsts = dwc2_readl(hsotg->regs + GNPTXSTS); + nptxsts = dwc2_readl(hsotg, GNPTXSTS); if ((nptxsts & TXSTS_QSPCAVAIL_MASK) == 0) { dev_vdbg(hsotg->dev, "Disabling channel\n"); hcchar &= ~HCCHAR_CHENA; @@ -1109,7 +1108,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, } else { if (dbg_perio()) dev_vdbg(hsotg->dev, "isoc/intr\n"); - hptxsts = dwc2_readl(hsotg->regs + HPTXSTS); + hptxsts = dwc2_readl(hsotg, HPTXSTS); if ((hptxsts & TXSTS_QSPCAVAIL_MASK) == 0 || hsotg->queuing_high_bandwidth) { if (dbg_perio()) @@ -1122,7 +1121,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, dev_vdbg(hsotg->dev, "DMA enabled\n"); } - dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num)); chan->halt_status = halt_status; if (hcchar & HCCHAR_CHENA) { @@ -1171,10 +1170,10 @@ void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) * Clear channel interrupt enables and any unhandled channel interrupt * conditions */ - dwc2_writel(0, hsotg->regs + HCINTMSK(chan->hc_num)); + dwc2_writel(hsotg, 0, HCINTMSK(chan->hc_num)); hcintmsk = 0xffffffff; hcintmsk &= ~HCINTMSK_RESERVED14_31; - dwc2_writel(hcintmsk, hsotg->regs + HCINT(chan->hc_num)); + dwc2_writel(hsotg, hcintmsk, HCINT(chan->hc_num)); } /** @@ -1228,7 +1227,7 @@ static void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg, !chan->do_split) ? chan->speed : USB_SPEED_HIGH; /* See how many bytes are in the periodic FIFO right now */ - fifo_space = (dwc2_readl(hsotg->regs + HPTXSTS) & + fifo_space = (dwc2_readl(hsotg, HPTXSTS) & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT; bytes_in_fifo = sizeof(u32) * (hsotg->params.host_perio_tx_fifo_size - @@ -1348,13 +1347,13 @@ static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg, if (((unsigned long)data_buf & 0x3) == 0) { /* xfer_buf is DWORD aligned */ for (i = 0; i < dword_count; i++, data_buf++) - dwc2_writel(*data_buf, data_fifo); + dwc2_writel(hsotg, *data_buf, HCFIFO(chan->hc_num)); } else { /* xfer_buf is not DWORD aligned */ for (i = 0; i < dword_count; i++, data_buf++) { u32 data = data_buf[0] | data_buf[1] << 8 | data_buf[2] << 16 | data_buf[3] << 24; - dwc2_writel(data, data_fifo); + dwc2_writel(hsotg, data, HCFIFO(chan->hc_num)); } } @@ -1383,12 +1382,12 @@ static void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg, hctsiz = TSIZ_DOPNG; hctsiz |= 1 << TSIZ_PKTCNT_SHIFT; - dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); + dwc2_writel(hsotg, hctsiz, HCTSIZ(chan->hc_num)); - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num)); hcchar |= HCCHAR_CHENA; hcchar &= ~HCCHAR_CHDIS; - dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num)); } /** @@ -1548,7 +1547,7 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, hctsiz |= num_packets << TSIZ_PKTCNT_SHIFT & TSIZ_PKTCNT_MASK; hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT & TSIZ_SC_MC_PID_MASK; - dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); + dwc2_writel(hsotg, hctsiz, HCTSIZ(chan->hc_num)); if (dbg_hc(chan)) { dev_vdbg(hsotg->dev, "Wrote %08x to HCTSIZ(%d)\n", hctsiz, chan->hc_num); @@ -1576,7 +1575,7 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, } else { dma_addr = chan->xfer_dma; } - dwc2_writel((u32)dma_addr, hsotg->regs + HCDMA(chan->hc_num)); + dwc2_writel(hsotg, (u32)dma_addr, HCDMA(chan->hc_num)); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "Wrote %08lx to HCDMA(%d)\n", @@ -1585,13 +1584,13 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, /* Start the split */ if (chan->do_split) { - u32 hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chan->hc_num)); + u32 hcsplt = dwc2_readl(hsotg, HCSPLT(chan->hc_num)); hcsplt |= HCSPLT_SPLTENA; - dwc2_writel(hcsplt, hsotg->regs + HCSPLT(chan->hc_num)); + dwc2_writel(hsotg, hcsplt, HCSPLT(chan->hc_num)); } - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num)); hcchar &= ~HCCHAR_MULTICNT_MASK; hcchar |= (ec_mc << HCCHAR_MULTICNT_SHIFT) & HCCHAR_MULTICNT_MASK; dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar); @@ -1610,7 +1609,7 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, (hcchar & HCCHAR_MULTICNT_MASK) >> HCCHAR_MULTICNT_SHIFT); - dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num)); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar, chan->hc_num); @@ -1668,18 +1667,18 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg, dev_vdbg(hsotg->dev, " NTD: %d\n", chan->ntd - 1); } - dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); + dwc2_writel(hsotg, hctsiz, HCTSIZ(chan->hc_num)); dma_sync_single_for_device(hsotg->dev, chan->desc_list_addr, chan->desc_list_sz, DMA_TO_DEVICE); - dwc2_writel(chan->desc_list_addr, hsotg->regs + HCDMA(chan->hc_num)); + dwc2_writel(hsotg, chan->desc_list_addr, HCDMA(chan->hc_num)); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "Wrote %pad to HCDMA(%d)\n", &chan->desc_list_addr, chan->hc_num); - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num)); hcchar &= ~HCCHAR_MULTICNT_MASK; hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT & HCCHAR_MULTICNT_MASK; @@ -1698,7 +1697,7 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg, (hcchar & HCCHAR_MULTICNT_MASK) >> HCCHAR_MULTICNT_SHIFT); - dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num)); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar, chan->hc_num); @@ -1755,7 +1754,7 @@ static int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg, * transfer completes, the extra requests for the channel will * be flushed. */ - u32 hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + u32 hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num)); dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar); hcchar |= HCCHAR_CHENA; @@ -1763,7 +1762,7 @@ static int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg, if (dbg_hc(chan)) dev_vdbg(hsotg->dev, " IN xfer: hcchar = 0x%08x\n", hcchar); - dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num)); chan->requests++; return 1; } @@ -1773,7 +1772,7 @@ static int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg, if (chan->xfer_count < chan->xfer_len) { if (chan->ep_type == USB_ENDPOINT_XFER_INT || chan->ep_type == USB_ENDPOINT_XFER_ISOC) { - u32 hcchar = dwc2_readl(hsotg->regs + + u32 hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num)); dwc2_hc_set_even_odd_frame(hsotg, chan, @@ -1887,7 +1886,7 @@ void dwc2_hcd_start(struct dwc2_hsotg *hsotg) */ hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_RST; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); } queue_delayed_work(hsotg->wq_otg, &hsotg->start_work, @@ -1908,11 +1907,11 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg) channel = hsotg->hc_ptr_array[i]; if (!list_empty(&channel->hc_list_entry)) continue; - hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); + hcchar = dwc2_readl(hsotg, HCCHAR(i)); if (hcchar & HCCHAR_CHENA) { hcchar &= ~(HCCHAR_CHENA | HCCHAR_EPDIR); hcchar |= HCCHAR_CHDIS; - dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); + dwc2_writel(hsotg, hcchar, HCCHAR(i)); } } } @@ -1921,11 +1920,11 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg) channel = hsotg->hc_ptr_array[i]; if (!list_empty(&channel->hc_list_entry)) continue; - hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); + hcchar = dwc2_readl(hsotg, HCCHAR(i)); if (hcchar & HCCHAR_CHENA) { /* Halt the channel */ hcchar |= HCCHAR_CHDIS; - dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); + dwc2_writel(hsotg, hcchar, HCCHAR(i)); } dwc2_hc_cleanup(hsotg, channel); @@ -1985,11 +1984,11 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) * interrupt mask and status bits and disabling subsequent host * channel interrupts. */ - intr = dwc2_readl(hsotg->regs + GINTMSK); + intr = dwc2_readl(hsotg, GINTMSK); intr &= ~(GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT); - dwc2_writel(intr, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, intr, GINTMSK); intr = GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT; - dwc2_writel(intr, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, intr, GINTSTS); /* * Turn off the vbus power only if the core has transitioned to device @@ -1999,7 +1998,7 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) if (dwc2_is_device_mode(hsotg)) { if (hsotg->op_state != OTG_STATE_A_SUSPEND) { dev_dbg(hsotg->dev, "Disconnect: PortPower off\n"); - dwc2_writel(0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, 0, HPRT0); } dwc2_disable_host_interrupts(hsotg); @@ -2027,7 +2026,7 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) * and won't get any future interrupts to handle the connect. */ if (!force) { - hprt0 = dwc2_readl(hsotg->regs + HPRT0); + hprt0 = dwc2_readl(hsotg, HPRT0); if (!(hprt0 & HPRT0_CONNDET) && (hprt0 & HPRT0_CONNSTS)) dwc2_hcd_connect(hsotg); } @@ -2071,7 +2070,7 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg) /* Turn off the vbus power */ dev_dbg(hsotg->dev, "PortPower off\n"); - dwc2_writel(0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, 0, HPRT0); } /* Caller must hold driver lock */ @@ -2095,7 +2094,7 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, if ((dev_speed == USB_SPEED_LOW) && (hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED) && (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI)) { - u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0); + u32 hprt0 = dwc2_readl(hsotg, HPRT0); u32 prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; if (prtspd == HPRT0_SPD_FULL_SPEED) @@ -2114,7 +2113,7 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, return retval; } - intr_mask = dwc2_readl(hsotg->regs + GINTMSK); + intr_mask = dwc2_readl(hsotg, GINTMSK); if (!(intr_mask & GINTSTS_SOF)) { enum dwc2_transaction_type tr_type; @@ -2279,7 +2278,7 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg, GUSBCFG); /* Set ULPI External VBUS bit if needed */ usbcfg &= ~GUSBCFG_ULPI_EXT_VBUS_DRV; @@ -2291,7 +2290,7 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) if (hsotg->params.ts_dline) usbcfg |= GUSBCFG_TERMSELDLPULSE; - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, usbcfg, GUSBCFG); /* * Reset the Controller @@ -2325,9 +2324,9 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) dwc2_gusbcfg_init(hsotg); /* Program the GOTGCTL register */ - otgctl = dwc2_readl(hsotg->regs + GOTGCTL); + otgctl = dwc2_readl(hsotg, GOTGCTL); otgctl &= ~GOTGCTL_OTGVER; - dwc2_writel(otgctl, hsotg->regs + GOTGCTL); + dwc2_writel(hsotg, otgctl, GOTGCTL); /* Clear the SRP success bit for FS-I2c */ hsotg->srp_success = 0; @@ -2374,20 +2373,20 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg) * introduced by the PHY in generating the linestate condition * can vary from one PHY to another. */ - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg, GUSBCFG); usbcfg |= GUSBCFG_TOUTCAL(7); - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, usbcfg, GUSBCFG); /* Restart the Phy Clock */ - dwc2_writel(0, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, 0, PCGCTL); /* Initialize Host Configuration Register */ dwc2_init_fs_ls_pclk_sel(hsotg); if (hsotg->params.speed == DWC2_SPEED_PARAM_FULL || hsotg->params.speed == DWC2_SPEED_PARAM_LOW) { - hcfg = dwc2_readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg, HCFG); hcfg |= HCFG_FSLSSUPP; - dwc2_writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hsotg, hcfg, HCFG); } /* @@ -2396,9 +2395,9 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg) * and its value must not be changed during runtime. */ if (hsotg->params.reload_ctl) { - hfir = dwc2_readl(hsotg->regs + HFIR); + hfir = dwc2_readl(hsotg, HFIR); hfir |= HFIR_RLDCTRL; - dwc2_writel(hfir, hsotg->regs + HFIR); + dwc2_writel(hsotg, hfir, HFIR); } if (hsotg->params.dma_desc_enable) { @@ -2415,9 +2414,9 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg) "falling back to buffer DMA mode.\n"); hsotg->params.dma_desc_enable = false; } else { - hcfg = dwc2_readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg, HCFG); hcfg |= HCFG_DESCDMA; - dwc2_writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hsotg, hcfg, HCFG); } } @@ -2426,18 +2425,18 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg) /* TODO - check this */ /* Clear Host Set HNP Enable in the OTG Control Register */ - otgctl = dwc2_readl(hsotg->regs + GOTGCTL); + otgctl = dwc2_readl(hsotg, GOTGCTL); otgctl &= ~GOTGCTL_HSTSETHNPEN; - dwc2_writel(otgctl, hsotg->regs + GOTGCTL); + dwc2_writel(hsotg, otgctl, GOTGCTL); /* Make sure the FIFOs are flushed */ dwc2_flush_tx_fifo(hsotg, 0x10 /* all TX FIFOs */); dwc2_flush_rx_fifo(hsotg); /* Clear Host Set HNP Enable in the OTG Control Register */ - otgctl = dwc2_readl(hsotg->regs + GOTGCTL); + otgctl = dwc2_readl(hsotg, GOTGCTL); otgctl &= ~GOTGCTL_HSTSETHNPEN; - dwc2_writel(otgctl, hsotg->regs + GOTGCTL); + dwc2_writel(hsotg, otgctl, GOTGCTL); if (!hsotg->params.dma_desc_enable) { int num_channels, i; @@ -2446,19 +2445,19 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg) /* Flush out any leftover queued requests */ num_channels = hsotg->params.host_channels; for (i = 0; i < num_channels; i++) { - hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); + hcchar = dwc2_readl(hsotg, HCCHAR(i)); hcchar &= ~HCCHAR_CHENA; hcchar |= HCCHAR_CHDIS; hcchar &= ~HCCHAR_EPDIR; - dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); + dwc2_writel(hsotg, hcchar, HCCHAR(i)); } /* Halt all channels to put them into a known state */ for (i = 0; i < num_channels; i++) { - hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); + hcchar = dwc2_readl(hsotg, HCCHAR(i)); hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS; hcchar &= ~HCCHAR_EPDIR; - dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); + dwc2_writel(hsotg, hcchar, HCCHAR(i)); dev_dbg(hsotg->dev, "%s: Halt channel %d\n", __func__, i); @@ -2482,7 +2481,7 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg) !!(hprt0 & HPRT0_PWR)); if (!(hprt0 & HPRT0_PWR)) { hprt0 |= HPRT0_PWR; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); } } @@ -3076,7 +3075,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) if (dbg_perio()) dev_vdbg(hsotg->dev, "Queue periodic transactions\n"); - tx_status = dwc2_readl(hsotg->regs + HPTXSTS); + tx_status = dwc2_readl(hsotg, HPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> @@ -3091,7 +3090,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) qh_ptr = hsotg->periodic_sched_assigned.next; while (qh_ptr != &hsotg->periodic_sched_assigned) { - tx_status = dwc2_readl(hsotg->regs + HPTXSTS); + tx_status = dwc2_readl(hsotg, HPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; if (qspcavail == 0) { @@ -3161,10 +3160,10 @@ exit: * level to ensure that new requests are loaded as * soon as possible.) */ - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg, GINTMSK); if (!(gintmsk & GINTSTS_PTXFEMP)) { gintmsk |= GINTSTS_PTXFEMP; - dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, gintmsk, GINTMSK); } } else { /* @@ -3174,10 +3173,10 @@ exit: * handlers to queue more transactions as transfer * states change. */ - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg, GINTMSK); if (gintmsk & GINTSTS_PTXFEMP) { gintmsk &= ~GINTSTS_PTXFEMP; - dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, gintmsk, GINTMSK); } } } @@ -3206,7 +3205,7 @@ static void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg) dev_vdbg(hsotg->dev, "Queue non-periodic transactions\n"); - tx_status = dwc2_readl(hsotg->regs + GNPTXSTS); + tx_status = dwc2_readl(hsotg, GNPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> @@ -3229,7 +3228,7 @@ static void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg) * available in the request queue or the Tx FIFO */ do { - tx_status = dwc2_readl(hsotg->regs + GNPTXSTS); + tx_status = dwc2_readl(hsotg, GNPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; if (!hsotg->params.host_dma && qspcavail == 0) { @@ -3266,7 +3265,7 @@ next: } while (hsotg->non_periodic_qh_ptr != orig_qh_ptr); if (!hsotg->params.host_dma) { - tx_status = dwc2_readl(hsotg->regs + GNPTXSTS); + tx_status = dwc2_readl(hsotg, GNPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> @@ -3286,9 +3285,9 @@ next: * level to ensure that new requests are loaded as * soon as possible.) */ - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg, GINTMSK); gintmsk |= GINTSTS_NPTXFEMP; - dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, gintmsk, GINTMSK); } else { /* * Disable the Tx FIFO empty interrupt since there are @@ -3297,9 +3296,9 @@ next: * handlers to queue more transactions as transfer * states change. */ - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg, GINTMSK); gintmsk &= ~GINTSTS_NPTXFEMP; - dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, gintmsk, GINTMSK); } } } @@ -3336,10 +3335,10 @@ void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg, * Ensure NP Tx FIFO empty interrupt is disabled when * there are no non-periodic transfers to process */ - u32 gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + u32 gintmsk = dwc2_readl(hsotg, GINTMSK); gintmsk &= ~GINTSTS_NPTXFEMP; - dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, gintmsk, GINTMSK); } } } @@ -3354,7 +3353,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work) dev_dbg(hsotg->dev, "%s()\n", __func__); - gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg, GOTGCTL); dev_dbg(hsotg->dev, "gotgctl=%0x\n", gotgctl); dev_dbg(hsotg->dev, "gotgctl.b.conidsts=%d\n", !!(gotgctl & GOTGCTL_CONID_B)); @@ -3380,7 +3379,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work) * check it again and jump to host mode if that was * the case. */ - gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg, GOTGCTL); if (!(gotgctl & GOTGCTL_CONID_B)) goto host; if (++count > 250) @@ -3440,9 +3439,9 @@ static void dwc2_wakeup_detected(struct timer_list *t) hprt0 = dwc2_read_hprt0(hsotg); dev_dbg(hsotg->dev, "Resume: HPRT0=%0x\n", hprt0); hprt0 &= ~HPRT0_RES; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); dev_dbg(hsotg->dev, "Clear Resume: HPRT0=%0x\n", - dwc2_readl(hsotg->regs + HPRT0)); + dwc2_readl(hsotg, HPRT0)); dwc2_hcd_rem_wakeup(hsotg); hsotg->bus_suspended = false; @@ -3471,15 +3470,15 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) spin_lock_irqsave(&hsotg->lock, flags); if (windex == hsotg->otg_port && dwc2_host_is_b_hnp_enabled(hsotg)) { - gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg, GOTGCTL); gotgctl |= GOTGCTL_HSTSETHNPEN; - dwc2_writel(gotgctl, hsotg->regs + GOTGCTL); + dwc2_writel(hsotg, gotgctl, GOTGCTL); hsotg->op_state = OTG_STATE_A_SUSPEND; } hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_SUSP; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); hsotg->bus_suspended = true; @@ -3489,17 +3488,17 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) */ if (!hsotg->params.power_down) { /* Suspend the Phy Clock */ - pcgctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgctl = dwc2_readl(hsotg, PCGCTL); pcgctl |= PCGCTL_STOPPCLK; - dwc2_writel(pcgctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgctl, PCGCTL); udelay(10); } /* For HNP the bus must be suspended for at least 200ms */ if (dwc2_host_is_b_hnp_enabled(hsotg)) { - pcgctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgctl = dwc2_readl(hsotg, PCGCTL); pcgctl &= ~PCGCTL_STOPPCLK; - dwc2_writel(pcgctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgctl, PCGCTL); spin_unlock_irqrestore(&hsotg->lock, flags); @@ -3523,9 +3522,9 @@ static void dwc2_port_resume(struct dwc2_hsotg *hsotg) * after registers restore. */ if (!hsotg->params.power_down) { - pcgctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgctl = dwc2_readl(hsotg, PCGCTL); pcgctl &= ~PCGCTL_STOPPCLK; - dwc2_writel(pcgctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgctl, PCGCTL); spin_unlock_irqrestore(&hsotg->lock, flags); msleep(20); spin_lock_irqsave(&hsotg->lock, flags); @@ -3534,7 +3533,7 @@ static void dwc2_port_resume(struct dwc2_hsotg *hsotg) hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_RES; hprt0 &= ~HPRT0_SUSP; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); spin_unlock_irqrestore(&hsotg->lock, flags); msleep(USB_RESUME_TIMEOUT); @@ -3542,7 +3541,7 @@ static void dwc2_port_resume(struct dwc2_hsotg *hsotg) spin_lock_irqsave(&hsotg->lock, flags); hprt0 = dwc2_read_hprt0(hsotg); hprt0 &= ~(HPRT0_RES | HPRT0_SUSP); - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); hsotg->bus_suspended = false; spin_unlock_irqrestore(&hsotg->lock, flags); } @@ -3586,7 +3585,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "ClearPortFeature USB_PORT_FEAT_ENABLE\n"); hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_ENA; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); break; case USB_PORT_FEAT_SUSPEND: @@ -3606,7 +3605,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "ClearPortFeature USB_PORT_FEAT_POWER\n"); hprt0 = dwc2_read_hprt0(hsotg); hprt0 &= ~HPRT0_PWR; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); break; case USB_PORT_FEAT_INDICATOR: @@ -3727,7 +3726,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, break; } - hprt0 = dwc2_readl(hsotg->regs + HPRT0); + hprt0 = dwc2_readl(hsotg, HPRT0); dev_vdbg(hsotg->dev, " HPRT0: 0x%08x\n", hprt0); if (hprt0 & HPRT0_CONNSTS) @@ -3768,9 +3767,9 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, dev_info(hsotg->dev, "Enabling descriptor DMA mode\n"); hsotg->params.dma_desc_enable = true; - hcfg = dwc2_readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg, HCFG); hcfg |= HCFG_DESCDMA; - dwc2_writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hsotg, hcfg, HCFG); hsotg->new_connection = false; } } @@ -3817,7 +3816,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "SetPortFeature - USB_PORT_FEAT_POWER\n"); hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_PWR; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); break; case USB_PORT_FEAT_RESET: @@ -3827,11 +3826,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, hprt0 = dwc2_read_hprt0(hsotg); dev_dbg(hsotg->dev, "SetPortFeature - USB_PORT_FEAT_RESET\n"); - pcgctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgctl = dwc2_readl(hsotg, PCGCTL); pcgctl &= ~(PCGCTL_ENBL_SLEEP_GATING | PCGCTL_STOPPCLK); - dwc2_writel(pcgctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgctl, PCGCTL); /* ??? Original driver does this */ - dwc2_writel(0, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, 0, PCGCTL); hprt0 = dwc2_read_hprt0(hsotg); /* Clear suspend bit if resetting from suspend state */ @@ -3846,13 +3845,13 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, hprt0 |= HPRT0_PWR | HPRT0_RST; dev_dbg(hsotg->dev, "In host mode, hprt0=%08x\n", hprt0); - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); } /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ msleep(50); hprt0 &= ~HPRT0_RST; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); hsotg->lx_state = DWC2_L0; /* Now back to On state */ break; @@ -3868,7 +3867,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "SetPortFeature - USB_PORT_FEAT_TEST\n"); hprt0 &= ~HPRT0_TSTCTL_MASK; hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); break; default: @@ -3925,7 +3924,7 @@ static int dwc2_hcd_is_status_changed(struct dwc2_hsotg *hsotg, int port) int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) { - u32 hfnum = dwc2_readl(hsotg->regs + HFNUM); + u32 hfnum = dwc2_readl(hsotg, HFNUM); #ifdef DWC2_DEBUG_SOF dev_vdbg(hsotg->dev, "DWC OTG HCD GET FRAME NUMBER %d\n", @@ -3936,9 +3935,9 @@ int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us) { - u32 hprt = dwc2_readl(hsotg->regs + HPRT0); - u32 hfir = dwc2_readl(hsotg->regs + HFIR); - u32 hfnum = dwc2_readl(hsotg->regs + HFNUM); + u32 hprt = dwc2_readl(hsotg, HPRT0); + u32 hfir = dwc2_readl(hsotg, HFIR); + u32 hfnum = dwc2_readl(hsotg, HFNUM); unsigned int us_per_frame; unsigned int frame_number; unsigned int remaining; @@ -4057,11 +4056,11 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg) if (chan->xfer_started) { u32 hfnum, hcchar, hctsiz, hcint, hcintmsk; - hfnum = dwc2_readl(hsotg->regs + HFNUM); - hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); - hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(i)); - hcint = dwc2_readl(hsotg->regs + HCINT(i)); - hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(i)); + hfnum = dwc2_readl(hsotg, HFNUM); + hcchar = dwc2_readl(hsotg, HCCHAR(i)); + hctsiz = dwc2_readl(hsotg, HCTSIZ(i)); + hcint = dwc2_readl(hsotg, HCINT(i)); + hcintmsk = dwc2_readl(hsotg, HCINTMSK(i)); dev_dbg(hsotg->dev, " hfnum: 0x%08x\n", hfnum); dev_dbg(hsotg->dev, " hcchar: 0x%08x\n", hcchar); dev_dbg(hsotg->dev, " hctsiz: 0x%08x\n", hctsiz); @@ -4109,12 +4108,12 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, " periodic_channels: %d\n", hsotg->periodic_channels); dev_dbg(hsotg->dev, " periodic_usecs: %d\n", hsotg->periodic_usecs); - np_tx_status = dwc2_readl(hsotg->regs + GNPTXSTS); + np_tx_status = dwc2_readl(hsotg, GNPTXSTS); dev_dbg(hsotg->dev, " NP Tx Req Queue Space Avail: %d\n", (np_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT); dev_dbg(hsotg->dev, " NP Tx FIFO Space Avail: %d\n", (np_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT); - p_tx_status = dwc2_readl(hsotg->regs + HPTXSTS); + p_tx_status = dwc2_readl(hsotg, HPTXSTS); dev_dbg(hsotg->dev, " P Tx Req Queue Space Avail: %d\n", (p_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT); dev_dbg(hsotg->dev, " P Tx FIFO Space Avail: %d\n", @@ -4364,7 +4363,7 @@ static void dwc2_hcd_reset_func(struct work_struct *work) hprt0 = dwc2_read_hprt0(hsotg); hprt0 &= ~HPRT0_RST; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); hsotg->flags.b.port_reset_change = 1; spin_unlock_irqrestore(&hsotg->lock, flags); @@ -4474,7 +4473,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_SUSP; hprt0 &= ~HPRT0_PWR; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); dwc2_vbus_supply_exit(hsotg); } @@ -4565,8 +4564,8 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) * Clear Port Enable and Port Status changes. * Enable Port Power. */ - dwc2_writel(HPRT0_PWR | HPRT0_CONNDET | - HPRT0_ENACHG, hsotg->regs + HPRT0); + dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET | + HPRT0_ENACHG, HPRT0); /* Wait for controller to detect Port Connect */ usleep_range(5000, 7000); } @@ -5086,17 +5085,17 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) hsotg->status_buf = NULL; } - ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); + ahbcfg = dwc2_readl(hsotg, GAHBCFG); /* Disable all interrupts */ ahbcfg &= ~GAHBCFG_GLBL_INTR_EN; - dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG); - dwc2_writel(0, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, ahbcfg, GAHBCFG); + dwc2_writel(hsotg, 0, GINTMSK); if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a) { - dctl = dwc2_readl(hsotg->regs + DCTL); + dctl = dwc2_readl(hsotg, DCTL); dctl |= DCTL_SFTDISCON; - dwc2_writel(dctl, hsotg->regs + DCTL); + dwc2_writel(hsotg, dctl, DCTL); } if (hsotg->wq_otg) { @@ -5139,7 +5138,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg) retval = -ENOMEM; - hcfg = dwc2_readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg, HCFG); dev_dbg(hsotg->dev, "hcfg=%08x\n", hcfg); #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS @@ -5429,14 +5428,14 @@ int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) /* Backup Host regs */ hr = &hsotg->hr_backup; - hr->hcfg = dwc2_readl(hsotg->regs + HCFG); - hr->haintmsk = dwc2_readl(hsotg->regs + HAINTMSK); + hr->hcfg = dwc2_readl(hsotg, HCFG); + hr->haintmsk = dwc2_readl(hsotg, HAINTMSK); for (i = 0; i < hsotg->params.host_channels; ++i) - hr->hcintmsk[i] = dwc2_readl(hsotg->regs + HCINTMSK(i)); + hr->hcintmsk[i] = dwc2_readl(hsotg, HCINTMSK(i)); hr->hprt0 = dwc2_read_hprt0(hsotg); - hr->hfir = dwc2_readl(hsotg->regs + HFIR); - hr->hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ); + hr->hfir = dwc2_readl(hsotg, HFIR); + hr->hptxfsiz = dwc2_readl(hsotg, HPTXFSIZ); hr->valid = true; return 0; @@ -5465,15 +5464,15 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) } hr->valid = false; - dwc2_writel(hr->hcfg, hsotg->regs + HCFG); - dwc2_writel(hr->haintmsk, hsotg->regs + HAINTMSK); + dwc2_writel(hsotg, hr->hcfg, HCFG); + dwc2_writel(hsotg, hr->haintmsk, HAINTMSK); for (i = 0; i < hsotg->params.host_channels; ++i) - dwc2_writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i)); + dwc2_writel(hsotg, hr->hcintmsk[i], HCINTMSK(i)); - dwc2_writel(hr->hprt0, hsotg->regs + HPRT0); - dwc2_writel(hr->hfir, hsotg->regs + HFIR); - dwc2_writel(hr->hptxfsiz, hsotg->regs + HPTXFSIZ); + dwc2_writel(hsotg, hr->hprt0, HPRT0); + dwc2_writel(hsotg, hr->hfir, HFIR); + dwc2_writel(hsotg, hr->hptxfsiz, HPTXFSIZ); hsotg->frame_number = 0; return 0; @@ -5508,10 +5507,10 @@ int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) } /* Enter USB Suspend Mode */ - hprt0 = dwc2_readl(hsotg->regs + HPRT0); + hprt0 = dwc2_readl(hsotg, HPRT0); hprt0 |= HPRT0_SUSP; hprt0 &= ~HPRT0_ENA; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); /* Wait for the HPRT0.PrtSusp register field to be set */ if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 3000)) @@ -5524,56 +5523,56 @@ int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) spin_lock_irqsave(&hsotg->lock, flags); hsotg->lx_state = DWC2_L2; - gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + gusbcfg = dwc2_readl(hsotg, GUSBCFG); if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) { /* ULPI interface */ /* Suspend the Phy Clock */ - pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl = dwc2_readl(hsotg, PCGCTL); pcgcctl |= PCGCTL_STOPPCLK; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); udelay(10); - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_PMUACTV; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); } else { /* UTMI+ Interface */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_PMUACTV; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); - pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl = dwc2_readl(hsotg, PCGCTL); pcgcctl |= PCGCTL_STOPPCLK; - dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(hsotg, pcgcctl, PCGCTL); udelay(10); } /* Enable interrupts from wake up logic */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_PMUINTSEL; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Unmask host mode interrupts in GPWRDN */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_DISCONN_DET_MSK; gpwrdn |= GPWRDN_LNSTSCHG_MSK; gpwrdn |= GPWRDN_STS_CHGINT_MSK; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Enable Power Down Clamp */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_PWRDNCLMP; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Switch off VDD */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_PWRDNSWTCH; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); hsotg->hibernated = 1; hsotg->bus_suspended = 1; @@ -5621,29 +5620,29 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, mdelay(100); /* Clear all pending interupts */ - dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, 0xffffffff, GINTSTS); /* De-assert Restore */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_RESTORE; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); /* Restore GUSBCFG, HCFG */ - dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG); - dwc2_writel(hr->hcfg, hsotg->regs + HCFG); + dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG); + dwc2_writel(hsotg, hr->hcfg, HCFG); /* De-assert Wakeup Logic */ - gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_PMUACTV; - dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); hprt0 = hr->hprt0; hprt0 |= HPRT0_PWR; hprt0 &= ~HPRT0_ENA; hprt0 &= ~HPRT0_SUSP; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); hprt0 = hr->hprt0; hprt0 |= HPRT0_PWR; @@ -5652,32 +5651,32 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, if (reset) { hprt0 |= HPRT0_RST; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); /* Wait for Resume time and then program HPRT again */ mdelay(60); hprt0 &= ~HPRT0_RST; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); } else { hprt0 |= HPRT0_RES; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); /* Wait for Resume time and then program HPRT again */ mdelay(100); hprt0 &= ~HPRT0_RES; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); } /* Clear all interrupt status */ - hprt0 = dwc2_readl(hsotg->regs + HPRT0); + hprt0 = dwc2_readl(hsotg, HPRT0); hprt0 |= HPRT0_CONNDET; hprt0 |= HPRT0_ENACHG; hprt0 &= ~HPRT0_ENA; - dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0, HPRT0); - hprt0 = dwc2_readl(hsotg->regs + HPRT0); + hprt0 = dwc2_readl(hsotg, HPRT0); /* Clear all pending interupts */ - dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, 0xffffffff, GINTSTS); /* Restore global registers */ ret = dwc2_restore_global_registers(hsotg); diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 5502a501f516..3f9bccc95add 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -469,10 +469,10 @@ static inline struct usb_hcd *dwc2_hsotg_to_hcd(struct dwc2_hsotg *hsotg) */ static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr) { - u32 mask = dwc2_readl(hsotg->regs + HCINTMSK(chnum)); + u32 mask = dwc2_readl(hsotg, HCINTMSK(chnum)); mask &= ~intr; - dwc2_writel(mask, hsotg->regs + HCINTMSK(chnum)); + dwc2_writel(hsotg, mask, HCINTMSK(chnum)); } void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan); @@ -487,7 +487,7 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg, */ static inline u32 dwc2_read_hprt0(struct dwc2_hsotg *hsotg) { - u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0); + u32 hprt0 = dwc2_readl(hsotg, HPRT0); hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG | HPRT0_OVRCURRCHG); return hprt0; @@ -690,8 +690,8 @@ static inline u16 dwc2_micro_frame_num(u16 frame) */ static inline u32 dwc2_read_core_intr(struct dwc2_hsotg *hsotg) { - return dwc2_readl(hsotg->regs + GINTSTS) & - dwc2_readl(hsotg->regs + GINTMSK); + return dwc2_readl(hsotg, GINTSTS) & + dwc2_readl(hsotg, GINTMSK); } static inline u32 dwc2_hcd_urb_get_status(struct dwc2_hcd_urb *dwc2_urb) diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c index 74f11c823f79..a858b5f9c1d6 100644 --- a/drivers/usb/dwc2/hcd_ddma.c +++ b/drivers/usb/dwc2/hcd_ddma.c @@ -185,19 +185,19 @@ static void dwc2_per_sched_enable(struct dwc2_hsotg *hsotg, u32 fr_list_en) spin_lock_irqsave(&hsotg->lock, flags); - hcfg = dwc2_readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg, HCFG); if (hcfg & HCFG_PERSCHEDENA) { /* already enabled */ spin_unlock_irqrestore(&hsotg->lock, flags); return; } - dwc2_writel(hsotg->frame_list_dma, hsotg->regs + HFLBADDR); + dwc2_writel(hsotg, hsotg->frame_list_dma, HFLBADDR); hcfg &= ~HCFG_FRLISTEN_MASK; hcfg |= fr_list_en | HCFG_PERSCHEDENA; dev_vdbg(hsotg->dev, "Enabling Periodic schedule\n"); - dwc2_writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hsotg, hcfg, HCFG); spin_unlock_irqrestore(&hsotg->lock, flags); } @@ -209,7 +209,7 @@ static void dwc2_per_sched_disable(struct dwc2_hsotg *hsotg) spin_lock_irqsave(&hsotg->lock, flags); - hcfg = dwc2_readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg, HCFG); if (!(hcfg & HCFG_PERSCHEDENA)) { /* already disabled */ spin_unlock_irqrestore(&hsotg->lock, flags); @@ -218,7 +218,7 @@ static void dwc2_per_sched_disable(struct dwc2_hsotg *hsotg) hcfg &= ~HCFG_PERSCHEDENA; dev_vdbg(hsotg->dev, "Disabling Periodic schedule\n"); - dwc2_writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hsotg, hcfg, HCFG); spin_unlock_irqrestore(&hsotg->lock, flags); } diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index ed7f05cf4906..c98ae3653086 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -144,7 +144,7 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg) enum dwc2_transaction_type tr_type; /* Clear interrupt */ - dwc2_writel(GINTSTS_SOF, hsotg->regs + GINTSTS); + dwc2_writel(hsotg, GINTSTS_SOF, GINTSTS); #ifdef DEBUG_SOF dev_vdbg(hsotg->dev, "--Start of Frame Interrupt--\n"); @@ -191,7 +191,7 @@ static void dwc2_rx_fifo_level_intr(struct dwc2_hsotg *hsotg) if (dbg_perio()) dev_vdbg(hsotg->dev, "--RxFIFO Level Interrupt--\n"); - grxsts = dwc2_readl(hsotg->regs + GRXSTSP); + grxsts = dwc2_readl(hsotg, GRXSTSP); chnum = (grxsts & GRXSTS_HCHNUM_MASK) >> GRXSTS_HCHNUM_SHIFT; chan = hsotg->hc_ptr_array[chnum]; if (!chan) { @@ -274,11 +274,11 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, dev_vdbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); /* Every time when port enables calculate HFIR.FrInterval */ - hfir = dwc2_readl(hsotg->regs + HFIR); + hfir = dwc2_readl(hsotg, HFIR); hfir &= ~HFIR_FRINT_MASK; hfir |= dwc2_calc_frame_interval(hsotg) << HFIR_FRINT_SHIFT & HFIR_FRINT_MASK; - dwc2_writel(hfir, hsotg->regs + HFIR); + dwc2_writel(hsotg, hfir, HFIR); /* Check if we need to adjust the PHY clock speed for low power */ if (!params->host_support_fs_ls_low_power) { @@ -287,7 +287,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, return; } - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg, GUSBCFG); prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; if (prtspd == HPRT0_SPD_LOW_SPEED || prtspd == HPRT0_SPD_FULL_SPEED) { @@ -295,11 +295,11 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL)) { /* Set PHY low power clock select for FS/LS devices */ usbcfg |= GUSBCFG_PHY_LP_CLK_SEL; - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, usbcfg, GUSBCFG); do_reset = 1; } - hcfg = dwc2_readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg, HCFG); fslspclksel = (hcfg & HCFG_FSLSPCLKSEL_MASK) >> HCFG_FSLSPCLKSEL_SHIFT; @@ -312,7 +312,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, fslspclksel = HCFG_FSLSPCLKSEL_6_MHZ; hcfg &= ~HCFG_FSLSPCLKSEL_MASK; hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT; - dwc2_writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hsotg, hcfg, HCFG); do_reset = 1; } } else { @@ -323,7 +323,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, fslspclksel = HCFG_FSLSPCLKSEL_48_MHZ; hcfg &= ~HCFG_FSLSPCLKSEL_MASK; hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT; - dwc2_writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hsotg, hcfg, HCFG); do_reset = 1; } } @@ -331,14 +331,14 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, /* Not low power */ if (usbcfg & GUSBCFG_PHY_LP_CLK_SEL) { usbcfg &= ~GUSBCFG_PHY_LP_CLK_SEL; - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hsotg, usbcfg, GUSBCFG); do_reset = 1; } } if (do_reset) { *hprt0_modify |= HPRT0_RST; - dwc2_writel(*hprt0_modify, hsotg->regs + HPRT0); + dwc2_writel(hsotg, *hprt0_modify, HPRT0); queue_delayed_work(hsotg->wq_otg, &hsotg->reset_work, msecs_to_jiffies(60)); } else { @@ -359,7 +359,7 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg) dev_vdbg(hsotg->dev, "--Port Interrupt--\n"); - hprt0 = dwc2_readl(hsotg->regs + HPRT0); + hprt0 = dwc2_readl(hsotg, HPRT0); hprt0_modify = hprt0; /* @@ -374,7 +374,7 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg) * Set flag and clear if detected */ if (hprt0 & HPRT0_CONNDET) { - dwc2_writel(hprt0_modify | HPRT0_CONNDET, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0_modify | HPRT0_CONNDET, HPRT0); dev_vdbg(hsotg->dev, "--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n", @@ -392,7 +392,7 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg) * Clear if detected - Set internal flag if disabled */ if (hprt0 & HPRT0_ENACHG) { - dwc2_writel(hprt0_modify | HPRT0_ENACHG, hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0_modify | HPRT0_ENACHG, HPRT0); dev_vdbg(hsotg->dev, " --Port Interrupt HPRT0=0x%08x Port Enable Changed (now %d)--\n", hprt0, !!(hprt0 & HPRT0_ENA)); @@ -406,17 +406,17 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg) hsotg->params.dma_desc_enable = false; hsotg->new_connection = false; - hcfg = dwc2_readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg, HCFG); hcfg &= ~HCFG_DESCDMA; - dwc2_writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hsotg, hcfg, HCFG); } } } /* Overcurrent Change Interrupt */ if (hprt0 & HPRT0_OVRCURRCHG) { - dwc2_writel(hprt0_modify | HPRT0_OVRCURRCHG, - hsotg->regs + HPRT0); + dwc2_writel(hsotg, hprt0_modify | HPRT0_OVRCURRCHG, + HPRT0); dev_vdbg(hsotg->dev, " --Port Interrupt HPRT0=0x%08x Port Overcurrent Changed--\n", hprt0); @@ -441,7 +441,7 @@ static u32 dwc2_get_actual_xfer_length(struct dwc2_hsotg *hsotg, { u32 hctsiz, count, length; - hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); + hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum)); if (halt_status == DWC2_HC_XFER_COMPLETE) { if (chan->ep_is_in) { @@ -518,7 +518,7 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg, urb->status = 0; } - hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); + hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum)); dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n", __func__, (chan->ep_is_in ? "IN" : "OUT"), chnum); dev_vdbg(hsotg->dev, " chan->xfer_len %d\n", chan->xfer_len); @@ -541,7 +541,7 @@ void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, int chnum, struct dwc2_qtd *qtd) { - u32 hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); + u32 hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum)); u32 pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT; if (chan->ep_type != USB_ENDPOINT_XFER_CONTROL) { @@ -780,9 +780,9 @@ cleanup: } } - haintmsk = dwc2_readl(hsotg->regs + HAINTMSK); + haintmsk = dwc2_readl(hsotg, HAINTMSK); haintmsk &= ~(1 << chan->hc_num); - dwc2_writel(haintmsk, hsotg->regs + HAINTMSK); + dwc2_writel(hsotg, haintmsk, HAINTMSK); /* Try to queue more transfers now that there's a free channel */ tr_type = dwc2_hcd_select_transactions(hsotg); @@ -829,9 +829,9 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg, * is enabled so that the non-periodic schedule will * be processed */ - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg, GINTMSK); gintmsk |= GINTSTS_NPTXFEMP; - dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, gintmsk, GINTMSK); } else { dev_vdbg(hsotg->dev, "isoc/intr\n"); /* @@ -848,9 +848,9 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg, * enabled so that the periodic schedule will be * processed */ - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg, GINTMSK); gintmsk |= GINTSTS_PTXFEMP; - dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, gintmsk, GINTMSK); } } } @@ -915,7 +915,7 @@ static void dwc2_complete_periodic_xfer(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, enum dwc2_halt_status halt_status) { - u32 hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); + u32 hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum)); qtd->error_count = 0; @@ -959,7 +959,7 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, qtd->isoc_split_offset += len; - hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); + hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum)); pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT; if (frame_desc->actual_length >= frame_desc->length || pid == 0) { @@ -1185,7 +1185,7 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg, urb->actual_length += xfer_length; - hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); + hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum)); dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n", __func__, (chan->ep_is_in ? "IN" : "OUT"), chnum); dev_vdbg(hsotg->dev, " chan->start_pkt_count %d\n", @@ -1561,10 +1561,10 @@ static void dwc2_hc_ahberr_intr(struct dwc2_hsotg *hsotg, dwc2_hc_handle_tt_clear(hsotg, chan, qtd); - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chnum)); - hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chnum)); - hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); - hc_dma = dwc2_readl(hsotg->regs + HCDMA(chnum)); + hcchar = dwc2_readl(hsotg, HCCHAR(chnum)); + hcsplt = dwc2_readl(hsotg, HCSPLT(chnum)); + hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum)); + hc_dma = dwc2_readl(hsotg, HCDMA(chnum)); dev_err(hsotg->dev, "AHB ERROR, Channel %d\n", chnum); dev_err(hsotg->dev, " hcchar 0x%08x, hcsplt 0x%08x\n", hcchar, hcsplt); @@ -1776,10 +1776,10 @@ static bool dwc2_halt_status_ok(struct dwc2_hsotg *hsotg, * This code is here only as a check. This condition should * never happen. Ignore the halt if it does occur. */ - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chnum)); - hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); - hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(chnum)); - hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chnum)); + hcchar = dwc2_readl(hsotg, HCCHAR(chnum)); + hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum)); + hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum)); + hcsplt = dwc2_readl(hsotg, HCSPLT(chnum)); dev_dbg(hsotg->dev, "%s: chan->halt_status DWC2_HC_XFER_NO_HALT_STATUS,\n", __func__); @@ -1803,7 +1803,7 @@ static bool dwc2_halt_status_ok(struct dwc2_hsotg *hsotg, * when the halt interrupt occurs. Halt the channel again if it does * occur. */ - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chnum)); + hcchar = dwc2_readl(hsotg, HCCHAR(chnum)); if (hcchar & HCCHAR_CHDIS) { dev_warn(hsotg->dev, "%s: hcchar.chdis set unexpectedly, hcchar 0x%08x, trying to halt again\n", @@ -1863,7 +1863,7 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg, return; } - hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(chnum)); + hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum)); if (chan->hcint & HCINTMSK_XFERCOMPL) { /* @@ -1958,7 +1958,7 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg, dev_err(hsotg->dev, "hcint 0x%08x, intsts 0x%08x\n", chan->hcint, - dwc2_readl(hsotg->regs + GINTSTS)); + dwc2_readl(hsotg, GINTSTS)); goto error; } } @@ -2031,11 +2031,11 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) chan = hsotg->hc_ptr_array[chnum]; - hcint = dwc2_readl(hsotg->regs + HCINT(chnum)); - hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(chnum)); + hcint = dwc2_readl(hsotg, HCINT(chnum)); + hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum)); if (!chan) { dev_err(hsotg->dev, "## hc_ptr_array for channel is NULL ##\n"); - dwc2_writel(hcint, hsotg->regs + HCINT(chnum)); + dwc2_writel(hsotg, hcint, HCINT(chnum)); return; } @@ -2047,7 +2047,7 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) hcint, hcintmsk, hcint & hcintmsk); } - dwc2_writel(hcint, hsotg->regs + HCINT(chnum)); + dwc2_writel(hsotg, hcint, HCINT(chnum)); /* * If we got an interrupt after someone called @@ -2182,7 +2182,7 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg) int i; struct dwc2_host_chan *chan, *chan_tmp; - haint = dwc2_readl(hsotg->regs + HAINT); + haint = dwc2_readl(hsotg, HAINT); if (dbg_perio()) { dev_vdbg(hsotg->dev, "%s()\n", __func__); @@ -2266,8 +2266,8 @@ irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg) "DWC OTG HCD Finished Servicing Interrupts\n"); dev_vdbg(hsotg->dev, "DWC OTG HCD gintsts=0x%08x gintmsk=0x%08x\n", - dwc2_readl(hsotg->regs + GINTSTS), - dwc2_readl(hsotg->regs + GINTMSK)); + dwc2_readl(hsotg, GINTSTS), + dwc2_readl(hsotg, GINTMSK)); } } diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 301ced1618f8..40839591d2ec 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -1510,7 +1510,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, bool ep_is_in = !!dwc2_hcd_is_pipe_in(&urb->pipe_info); bool ep_is_isoc = (ep_type == USB_ENDPOINT_XFER_ISOC); bool ep_is_int = (ep_type == USB_ENDPOINT_XFER_INT); - u32 hprt = dwc2_readl(hsotg->regs + HPRT0); + u32 hprt = dwc2_readl(hsotg, HPRT0); u32 prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; bool do_split = (prtspd == HPRT0_SPD_HIGH_SPEED && dev_speed != USB_SPEED_HIGH); @@ -1747,9 +1747,9 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) if (status) return status; if (!hsotg->periodic_qh_count) { - intr_mask = dwc2_readl(hsotg->regs + GINTMSK); + intr_mask = dwc2_readl(hsotg, GINTMSK); intr_mask |= GINTSTS_SOF; - dwc2_writel(intr_mask, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, intr_mask, GINTMSK); } hsotg->periodic_qh_count++; @@ -1788,9 +1788,9 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) hsotg->periodic_qh_count--; if (!hsotg->periodic_qh_count && !hsotg->params.dma_desc_enable) { - intr_mask = dwc2_readl(hsotg->regs + GINTMSK); + intr_mask = dwc2_readl(hsotg, GINTMSK); intr_mask &= ~GINTSTS_SOF; - dwc2_writel(intr_mask, hsotg->regs + GINTMSK); + dwc2_writel(hsotg, intr_mask, GINTMSK); } } diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index af075d4da895..f66d0dd5db1c 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -654,8 +654,8 @@ static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg) dwc2_force_mode(hsotg, true); - gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ); - hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ); + gnptxfsiz = dwc2_readl(hsotg, GNPTXFSIZ); + hptxfsiz = dwc2_readl(hsotg, HPTXFSIZ); hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT; @@ -679,13 +679,13 @@ static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg) dwc2_force_mode(hsotg, false); - gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ); + gnptxfsiz = dwc2_readl(hsotg, GNPTXFSIZ); fifo_count = dwc2_hsotg_tx_fifo_count(hsotg); for (fifo = 1; fifo <= fifo_count; fifo++) { hw->g_tx_fifo_size[fifo] = - (dwc2_readl(hsotg->regs + DPTXFSIZN(fifo)) & + (dwc2_readl(hsotg, DPTXFSIZN(fifo)) & FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT; } @@ -713,7 +713,7 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx */ - hw->snpsid = dwc2_readl(hsotg->regs + GSNPSID); + hw->snpsid = dwc2_readl(hsotg, GSNPSID); if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID && (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID && (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) { @@ -726,11 +726,11 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf, hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid); - hwcfg1 = dwc2_readl(hsotg->regs + GHWCFG1); - hwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2); - hwcfg3 = dwc2_readl(hsotg->regs + GHWCFG3); - hwcfg4 = dwc2_readl(hsotg->regs + GHWCFG4); - grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ); + hwcfg1 = dwc2_readl(hsotg, GHWCFG1); + hwcfg2 = dwc2_readl(hsotg, GHWCFG2); + hwcfg3 = dwc2_readl(hsotg, GHWCFG3); + hwcfg4 = dwc2_readl(hsotg, GHWCFG4); + grxfsiz = dwc2_readl(hsotg, GRXFSIZ); /* hwcfg1 */ hw->dev_ep_dirs = hwcfg1; -- cgit v1.2.3 From 342ccce173a5b95541c6ee712e1a7249b10db5cb Mon Sep 17 00:00:00 2001 From: Gevorg Sahakyan Date: Thu, 26 Jul 2018 18:00:41 +0400 Subject: usb: dwc2: replace ioread32/iowrite32_rep with dwc2_readl/writel_rep dwc2_readl_rep/dwc2_writel_rep functions using readl/writel in a loop. Signed-off-by: Gevorg Sahakyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 61 ++++++++++++++++++----------------------------- drivers/usb/dwc2/gadget.c | 6 ++--- 2 files changed, 26 insertions(+), 41 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index bca8463f00b0..ae8534bed884 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1161,60 +1161,45 @@ struct dwc2_hsotg { #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */ }; -#ifdef CONFIG_MIPS -/* - * There are some MIPS machines that can run in either big-endian - * or little-endian mode and that use the dwc2 register without - * a byteswap in both ways. - * Unlike other architectures, MIPS apparently does not require a - * barrier before the __raw_writel() to synchronize with DMA but does - * require the barrier after the __raw_writel() to serialize a set of - * writes. This set of operations was added specifically for MIPS and - * should only be used there. - */ +/* Normal architectures just use readl/write */ static inline u32 dwc2_readl(struct dwc2_hsotg *hsotg, u32 offset) { - u32 value = __raw_readl(hsotg->regs + offset); - - /* In order to preserve endianness __raw_* operation is used. Therefore - * a barrier is needed to ensure IO access is not re-ordered across - * reads or writes - */ - mb(); - return value; + return readl(hsotg->regs + offset); } static inline void dwc2_writel(struct dwc2_hsotg *hsotg, u32 value, u32 offset) { - __raw_writel(value, hsotg->regs + offset); - - /* - * In order to preserve endianness __raw_* operation is used. Therefore - * a barrier is needed to ensure IO access is not re-ordered across - * reads or writes - */ - mb(); + writel(value, hsotg->regs + offset); + #ifdef DWC2_LOG_WRITES - pr_info("INFO:: wrote %08x to %p\n", value, hsotg->regs + offset); + pr_info("info:: wrote %08x to %p\n", value, hsotg->regs + offset); #endif } -#else -/* Normal architectures just use readl/write */ -static inline u32 dwc2_readl(struct dwc2_hsotg *hsotg, u32 offset) +static inline void dwc2_readl_rep(struct dwc2_hsotg *hsotg, u32 offset, + void *buffer, unsigned int count) { - return readl(hsotg->regs + offset); + if (count) { + u32 *buf = buffer; + + do { + u32 x = dwc2_readl(hsotg, offset); + *buf++ = x; + } while (--count); + } } -static inline void dwc2_writel(struct dwc2_hsotg *hsotg, u32 value, u32 offset) +static inline void dwc2_writel_rep(struct dwc2_hsotg *hsotg, u32 offset, + const void *buffer, unsigned int count) { - writel(value, hsotg->regs + offset); + if (count) { + const u32 *buf = buffer; -#ifdef DWC2_LOG_WRITES - pr_info("info:: wrote %08x to %p\n", value, hsotg->regs + offset); -#endif + do { + dwc2_writel(hsotg, *buf++, offset); + } while (--count); + } } -#endif /* Reasons for halting a host channel */ enum dwc2_halt_status { diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 50f94f277433..26fdb4b60ff6 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -599,7 +599,7 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg, to_write = DIV_ROUND_UP(to_write, 4); data = hs_req->req.buf + buf_pos; - iowrite32_rep(hsotg->regs + EPFIFO(hs_ep->index), data, to_write); + dwc2_writel_rep(hsotg, EPFIFO(hs_ep->index), data, to_write); return (to_write >= can_write) ? -ENOSPC : 0; } @@ -2169,8 +2169,8 @@ static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) * note, we might over-write the buffer end by 3 bytes depending on * alignment of the data. */ - ioread32_rep(hsotg->regs + EPFIFO(ep_idx), - hs_req->req.buf + read_ptr, to_read); + dwc2_readl_rep(hsotg, EPFIFO(ep_idx), + hs_req->req.buf + read_ptr, to_read); } /** -- cgit v1.2.3 From 39facfa01c9fc64f90233d1734882f0a0cafe36a Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 27 Jul 2018 10:50:40 +0900 Subject: usb: gadget: udc: renesas_usb3: Add register of usb role switch This patch adds role switch support for R-Car SoCs into the USB 3.0 peripheral driver. Some R-Car SoCs (e.g. R-Car H3) have USB 3.0 dual-role device controller which has the USB 3.0 xHCI host and Renesas USB 3.0 peripheral. Unfortunately, the mode change register (DRD_CON) contains the USB 3.0 peripheral controller side only. So, this renesas_usb3 driver manages the DRD_CON now. However, in peripheral mode, the host should stop. Also the host hardware needs to reinitialize its own registers when the mode changes from peripheral to host mode. Otherwise, the host cannot work correctly (e.g. detect a device as high-speed). To achieve this reinitialization by a driver, this driver also registers a role switch driver to manage the DRD_CON and get a device pointer of usb 3.0 host from "companion" property of OF. Then, when the usb role is changed, renesas_usb3_role_switch_set() will attach/release the xhci-plat driver to reinitialize the host hardware. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Heikki Krogerus Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/Kconfig | 1 + drivers/usb/gadget/udc/renesas_usb3.c | 84 ++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 1df4dedffe86..0a16cbd4e528 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -193,6 +193,7 @@ config USB_RENESAS_USB3 tristate 'Renesas USB3.0 Peripheral controller' depends on ARCH_RENESAS || COMPILE_TEST depends on EXTCON + select USB_ROLE_SWITCH help Renesas USB3.0 Peripheral controller is a USB peripheral controller that supports super, high, and full speed USB 3.0 data transfers. diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 7cf98c793e04..1f879b3f2c96 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include /* register definitions */ #define USB3_AXI_INT_STA 0x008 @@ -335,6 +337,11 @@ struct renesas_usb3 { struct phy *phy; struct dentry *dentry; + struct usb_role_switch *role_sw; + struct device *host_dev; + struct work_struct role_work; + enum usb_role role; + struct renesas_usb3_ep *usb3_ep; int num_usb3_eps; @@ -651,6 +658,14 @@ static void usb3_check_vbus(struct renesas_usb3 *usb3) } } +static void renesas_usb3_role_work(struct work_struct *work) +{ + struct renesas_usb3 *usb3 = + container_of(work, struct renesas_usb3, role_work); + + usb_role_switch_set_role(usb3->role_sw, usb3->role); +} + static void usb3_set_mode(struct renesas_usb3 *usb3, bool host) { if (host) @@ -659,6 +674,16 @@ static void usb3_set_mode(struct renesas_usb3 *usb3, bool host) usb3_set_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON); } +static void usb3_set_mode_by_role_sw(struct renesas_usb3 *usb3, bool host) +{ + if (usb3->role_sw) { + usb3->role = host ? USB_ROLE_HOST : USB_ROLE_DEVICE; + schedule_work(&usb3->role_work); + } else { + usb3_set_mode(usb3, host); + } +} + static void usb3_vbus_out(struct renesas_usb3 *usb3, bool enable) { if (enable) @@ -672,7 +697,7 @@ static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev) unsigned long flags; spin_lock_irqsave(&usb3->lock, flags); - usb3_set_mode(usb3, host); + usb3_set_mode_by_role_sw(usb3, host); usb3_vbus_out(usb3, a_dev); /* for A-Peripheral or forced B-device mode */ if ((!host && a_dev) || @@ -2302,6 +2327,41 @@ static const struct usb_gadget_ops renesas_usb3_gadget_ops = { .set_selfpowered = renesas_usb3_set_selfpowered, }; +static enum usb_role renesas_usb3_role_switch_get(struct device *dev) +{ + struct renesas_usb3 *usb3 = dev_get_drvdata(dev); + enum usb_role cur_role; + + pm_runtime_get_sync(dev); + cur_role = usb3_is_host(usb3) ? USB_ROLE_HOST : USB_ROLE_DEVICE; + pm_runtime_put(dev); + + return cur_role; +} + +static int renesas_usb3_role_switch_set(struct device *dev, + enum usb_role role) +{ + struct renesas_usb3 *usb3 = dev_get_drvdata(dev); + struct device *host = usb3->host_dev; + enum usb_role cur_role = renesas_usb3_role_switch_get(dev); + + pm_runtime_get_sync(dev); + if (cur_role == USB_ROLE_HOST && role == USB_ROLE_DEVICE) { + device_release_driver(host); + usb3_set_mode(usb3, false); + } else if (cur_role == USB_ROLE_DEVICE && role == USB_ROLE_HOST) { + /* Must set the mode before device_attach of the host */ + usb3_set_mode(usb3, true); + /* This device_attach() might sleep */ + if (device_attach(host) < 0) + dev_err(dev, "device_attach(host) failed\n"); + } + pm_runtime_put(dev); + + return 0; +} + static ssize_t role_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -2405,6 +2465,8 @@ static int renesas_usb3_remove(struct platform_device *pdev) debugfs_remove_recursive(usb3->dentry); device_remove_file(&pdev->dev, &dev_attr_role); + usb_role_switch_unregister(usb3->role_sw); + usb_del_gadget_udc(&usb3->gadget); renesas_usb3_dma_free_prd(usb3, &pdev->dev); @@ -2562,6 +2624,12 @@ static const unsigned int renesas_usb3_cable[] = { EXTCON_NONE, }; +static const struct usb_role_switch_desc renesas_usb3_role_switch_desc = { + .set = renesas_usb3_role_switch_set, + .get = renesas_usb3_role_switch_get, + .allow_userspace_control = true, +}; + static int renesas_usb3_probe(struct platform_device *pdev) { struct renesas_usb3 *usb3; @@ -2647,6 +2715,20 @@ static int renesas_usb3_probe(struct platform_device *pdev) if (ret < 0) goto err_dev_create; + INIT_WORK(&usb3->role_work, renesas_usb3_role_work); + usb3->role_sw = usb_role_switch_register(&pdev->dev, + &renesas_usb3_role_switch_desc); + if (!IS_ERR(usb3->role_sw)) { + usb3->host_dev = usb_of_get_companion_dev(&pdev->dev); + if (!usb3->host_dev) { + /* If not found, this driver will not use a role sw */ + usb_role_switch_unregister(usb3->role_sw); + usb3->role_sw = NULL; + } + } else { + usb3->role_sw = NULL; + } + usb3->workaround_for_vbus = priv->workaround_for_vbus; renesas_usb3_debugfs_init(usb3, &pdev->dev); -- cgit v1.2.3 From a77004681148f5773c20370812ca7d14443fb091 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 26 Jul 2018 13:52:11 -0700 Subject: usb: dwc3: Set default mode for dwc_usb31 dwc_usb31 does not support OTG mode. If the controller supports DRD but the dr_mode is not specified or set to OTG, then set the mode to peripheral. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 21e4931d0cc0..64ba664d467c 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -78,6 +78,14 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc) mode = USB_DR_MODE_HOST; else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) mode = USB_DR_MODE_PERIPHERAL; + + /* + * dwc_usb31 does not support OTG mode. If the controller + * supports DRD but the dr_mode is not specified or set to OTG, + * then set the mode to peripheral. + */ + if (mode == USB_DR_MODE_OTG && dwc3_is_usb31(dwc)) + mode = USB_DR_MODE_PERIPHERAL; } if (mode != dwc->dr_mode) { -- cgit v1.2.3 From b138e23d3dff90c0494925b4c1874227b81bddf7 Mon Sep 17 00:00:00 2001 From: Anurag Kumar Vulisha Date: Fri, 27 Jul 2018 13:11:20 +0530 Subject: usb: dwc3: core: Enable AutoRetry feature in the controller By default when core sees any transaction error (CRC or overflow) it replies with terminating retry ACK (Retry=1 and Nump == 0). Enabling this Auto Retry feature in controller will make the core send a non-terminanting ACK upon such transaction errors. That is, ACK TP with Retry=1 and Nump != 0. Doing so will give controller a chance to recover from transient error conditions. Signed-off-by: Anurag Kumar Vulisha Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 16 ++++++++++++++++ drivers/usb/dwc3/core.h | 3 +++ 2 files changed, 19 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 64ba664d467c..88c80fcc39f5 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -985,6 +985,22 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); } + if (dwc->dr_mode == USB_DR_MODE_HOST || + dwc->dr_mode == USB_DR_MODE_OTG) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL); + + /* + * Enable Auto retry Feature to make the controller operating in + * Host mode on seeing transaction errors(CRC errors or internal + * overrun scenerios) on IN transfers to reply to the device + * with a non-terminating retry ACK (i.e, an ACK transcation + * packet with Retry=1 & Nump != 0) + */ + reg |= DWC3_GUCTL_HSTINAUTORETRY; + + dwc3_writel(dwc->regs, DWC3_GUCTL, reg); + } + /* * Must config both number of packets and max burst settings to enable * RX and/or TX threshold. diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 5f14fb7121b1..5bfb62533e0f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -238,6 +238,9 @@ #define DWC3_GCTL_GBLHIBERNATIONEN BIT(1) #define DWC3_GCTL_DSBLCLKGTNG BIT(0) +/* Global User Control Register */ +#define DWC3_GUCTL_HSTINAUTORETRY BIT(14) + /* Global User Control 1 Register */ #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) -- cgit v1.2.3 From fe369e1826b3efae11012ad07d1713223c37ec5d Mon Sep 17 00:00:00 2001 From: Gevorg Sahakyan Date: Fri, 27 Jul 2018 12:26:29 +0400 Subject: usb: dwc2: Make dwc2_readl/writel functions endianness-agnostic. Declared dwc2_check_core_endianness() function for dynamicly check core endianness. Added needs_byte_swap flag to hsotg structure, and depending on flag swap value inside dwc2_readl/writel functions. Signed-off-by: Gevorg Sahakyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 15 +++++++++++++-- drivers/usb/dwc2/platform.c | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index ae8534bed884..cc9c93affa14 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -857,6 +857,7 @@ struct dwc2_hregs_backup { * @gr_backup: Backup of global registers during suspend * @dr_backup: Backup of device registers during suspend * @hr_backup: Backup of host registers during suspend + * @needs_byte_swap: Specifies whether the opposite endianness. * * These are for host mode: * @@ -1046,6 +1047,7 @@ struct dwc2_hsotg { struct dentry *debug_root; struct debugfs_regset32 *regset; + bool needs_byte_swap; /* DWC OTG HW Release versions */ #define DWC2_CORE_REV_2_71a 0x4f54271a @@ -1164,12 +1166,21 @@ struct dwc2_hsotg { /* Normal architectures just use readl/write */ static inline u32 dwc2_readl(struct dwc2_hsotg *hsotg, u32 offset) { - return readl(hsotg->regs + offset); + u32 val; + + val = readl(hsotg->regs + offset); + if (hsotg->needs_byte_swap) + return swab32(val); + else + return val; } static inline void dwc2_writel(struct dwc2_hsotg *hsotg, u32 value, u32 offset) { - writel(value, hsotg->regs + offset); + if (hsotg->needs_byte_swap) + writel(swab32(value), hsotg->regs + offset); + else + writel(value, hsotg->regs + offset); #ifdef DWC2_LOG_WRITES pr_info("info:: wrote %08x to %p\n", value, hsotg->regs + offset); diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 4c0819554bcd..9a53a58e676e 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -352,6 +352,23 @@ static void dwc2_driver_shutdown(struct platform_device *dev) disable_irq(hsotg->irq); } +/** + * dwc2_check_core_endianness() - Returns true if core and AHB have + * opposite endianness. + * @hsotg: Programming view of the DWC_otg controller. + */ +static bool dwc2_check_core_endianness(struct dwc2_hsotg *hsotg) +{ + u32 snpsid; + + snpsid = ioread32(hsotg->regs + GSNPSID); + if ((snpsid & GSNPSID_ID_MASK) == DWC2_OTG_ID || + (snpsid & GSNPSID_ID_MASK) == DWC2_FS_IOT_ID || + (snpsid & GSNPSID_ID_MASK) == DWC2_HS_IOT_ID) + return false; + return true; +} + /** * dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg * driver @@ -395,6 +412,8 @@ static int dwc2_driver_probe(struct platform_device *dev) dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n", (unsigned long)res->start, hsotg->regs); + hsotg->needs_byte_swap = dwc2_check_core_endianness(hsotg); + retval = dwc2_lowlevel_hw_init(hsotg); if (retval) return retval; -- cgit v1.2.3 From 4d4f1e79b800c80313280eec1af25a242a50dce5 Mon Sep 17 00:00:00 2001 From: Minas Harutyunyan Date: Fri, 27 Jul 2018 14:48:45 +0400 Subject: usb: dwc2: gadget: ISOC's starting flow improvement To start ISOC transfers in handlers dwc2_gadget_handle_nak() and dwc2_gadget_handle_out_token_ep_disabled() driver reads current frame number, based on which, set target frame number to start first ISOC transfer. In case if system's high IRQ latency and multiple EP's asserted interrupt in same frame, there are high probability that when reading current frame number in EP's handlers, actual frame number can be increased. As result for bInterval > 1, starting target frame will be set wrongly and all ISOC packets will be dropped. In patch "usb: dwc2: Change reading of current frame number flow" reading of current frame number done ASAP in common interrupt handler. This frame number stored in frame_number variable which used as starting frame number for ISOC EP's in above mentioned handlers. Signed-off-by: Minas Harutyunyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 26fdb4b60ff6..887bea99dce8 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2750,21 +2750,14 @@ static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep) struct dwc2_hsotg *hsotg = ep->parent; int dir_in = ep->dir_in; u32 doepmsk; - u32 tmp; if (dir_in || !ep->isochronous) return; - /* - * Store frame in which irq was asserted here, as - * it can change while completing request below. - */ - tmp = dwc2_hsotg_read_frameno(hsotg); - if (using_desc_dma(hsotg)) { if (ep->target_frame == TARGET_FRAME_INITIAL) { /* Start first ISO Out */ - ep->target_frame = tmp; + ep->target_frame = hsotg->frame_number; dwc2_gadget_start_isoc_ddma(ep); } return; @@ -2772,11 +2765,9 @@ static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep) if (ep->interval > 1 && ep->target_frame == TARGET_FRAME_INITIAL) { - u32 dsts; u32 ctrl; - dsts = dwc2_readl(hsotg, DSTS); - ep->target_frame = dwc2_hsotg_read_frameno(hsotg); + ep->target_frame = hsotg->frame_number; dwc2_gadget_incr_frame_num(ep); ctrl = dwc2_readl(hsotg, DOEPCTL(ep->index)); @@ -2812,22 +2803,20 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep) { struct dwc2_hsotg *hsotg = hs_ep->parent; int dir_in = hs_ep->dir_in; - u32 tmp; if (!dir_in || !hs_ep->isochronous) return; if (hs_ep->target_frame == TARGET_FRAME_INITIAL) { - tmp = dwc2_hsotg_read_frameno(hsotg); if (using_desc_dma(hsotg)) { - hs_ep->target_frame = tmp; + hs_ep->target_frame = hsotg->frame_number; dwc2_gadget_incr_frame_num(hs_ep); dwc2_gadget_start_isoc_ddma(hs_ep); return; } - hs_ep->target_frame = tmp; + hs_ep->target_frame = hsotg->frame_number; if (hs_ep->interval > 1) { u32 ctrl = dwc2_readl(hsotg, DIEPCTL(hs_ep->index)); -- cgit v1.2.3 From b6de7b8c38e99eee5d5ef673f2ff63c982ef608c Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Thu, 26 Jul 2018 13:07:27 -0700 Subject: usb: dwc2: Turn on uframe_sched on "bcm" platforms There's no reason to have the uframe scheduler off on dwc2. Running with uframe_sched = False is equivalent to saying "I don't want to run the correct code, I want to run the old and incorrect code". The uframe scheduler has been off on Broadcom since commit 58b179dcf28c ("staging: dwc2: disable uframe_sched on the bcm2835"). Since then there have been many many improvements, notably the commit 9f9f09b048f5 ("usb: dwc2: host: Totally redo the microframe scheduler") Presumably if everyone is good w/ the uframe_sched turned back on we can kill all the old and crufty non-uframe sched code. Reviewed-by: Minas Harutyunyan Signed-off-by: Douglas Anderson Tested-by: Stefan Wahren Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/params.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index f66d0dd5db1c..f8a74f504285 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -47,7 +47,6 @@ static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg) p->max_transfer_size = 65535; p->max_packet_count = 511; p->ahbcfg = 0x10; - p->uframe_sched = false; } static void dwc2_set_his_params(struct dwc2_hsotg *hsotg) -- cgit v1.2.3 From aea8916702eecc76db3d8c111d61d8c78955fe92 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Thu, 26 Jul 2018 13:07:28 -0700 Subject: usb: dwc2: Turn on uframe_sched on "his" platforms There's no reason to have the uframe scheduler off on dwc2. Running with uframe_sched = False is equivalent to saying "I don't want to run the correct code, I want to run the old and incorrect code". The uframe scheduler has been off on HiSilicon since commit 37dd9d65cc41 ("usb: dwc2: add support of hi6220"). Since then there have been many many improvements, notably the commit 9f9f09b048f5 ("usb: dwc2: host: Totally redo the microframe scheduler") Presumably if everyone is good w/ the uframe_sched turned back on we can kill all the old and crufty non-uframe sched code. Acked-by: Minas Harutyunyan Reviewed-by: Minas Harutyunyan Signed-off-by: Douglas Anderson Tested-by: John Stultz Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/params.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index f8a74f504285..388c9605602a 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -67,7 +67,6 @@ static void dwc2_set_his_params(struct dwc2_hsotg *hsotg) p->reload_ctl = false; p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT; - p->uframe_sched = false; p->change_speed_quirk = true; p->power_down = false; } -- cgit v1.2.3 From 3af0540ad39e0f4935c572ad94f57d5408e95cec Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Thu, 26 Jul 2018 13:07:29 -0700 Subject: usb: dwc2: Turn on uframe_sched on "amlogic" platforms There's no reason to have the uframe scheduler off on dwc2. Running with uframe_sched = False is equivalent to saying "I don't want to run the correct code, I want to run the old and incorrect code". The uframe scheduler has been off on Amlogic since commit f94310ac076e ("usb: dwc2: add support for Meson8b and GXBB SoCs"). While this was after most of the recent improvements, notably the commit 9f9f09b048f5 ("usb: dwc2: host: Totally redo the microframe scheduler"), presumably the parameters were copied from another platform and the uframe scheduler wasn't tried. Presumably if everyone is good w/ the uframe_sched turned back on we can kill all the old and crufty non-uframe sched code. Reviewed-by: Minas Harutyunyan Signed-off-by: Douglas Anderson Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/params.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 388c9605602a..3ac84a3837af 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -110,7 +110,6 @@ static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg) p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI; p->ahbcfg = GAHBCFG_HBSTLEN_INCR8 << GAHBCFG_HBSTLEN_SHIFT; - p->uframe_sched = false; } static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg) -- cgit v1.2.3 From f8781d55099026061f8b03f2636c0b192f5093e2 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Thu, 26 Jul 2018 13:07:30 -0700 Subject: usb: dwc2: Turn on uframe_sched on "stm32f4x9_fsotg" platforms There's no reason to have the uframe scheduler off on dwc2. Running with uframe_sched = False is equivalent to saying "I don't want to run the correct code, I want to run the old and incorrect code". The uframe scheduler has been off on stm32f4x9_fsotg since commit e35b135055e2 ("usb: dwc2: Add support for STM32F429/439/469 USB OTG HS/FS in FS mode (internal PHY)"). That commit is pretty recent, so it's unclear to me why the uframe scheduler was left off. Hopefully it's because someone copied it from other parameters and didn't think to try it? Presumably if everyone is good w/ the uframe_sched turned back on we can kill all the old and crufty non-uframe sched code. Reviewed-by: Minas Harutyunyan Signed-off-by: Douglas Anderson Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/params.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 3ac84a3837af..bf7052e037d6 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -131,7 +131,6 @@ static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg) p->max_packet_count = 256; p->phy_type = DWC2_PHY_TYPE_PARAM_FS; p->i2c_enable = false; - p->uframe_sched = false; p->activate_stm_fs_transceiver = true; } -- cgit v1.2.3 From 4ea438da76f4277627347147f6f7004affae07b9 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Fri, 27 Jul 2018 18:52:41 -0700 Subject: usb: dwc3: gadget: Check MaxPacketSize from descriptor endpoint->maxpacket is not updated after setting the usb_set_maxpacket_limit() on endpoint enable. The MaxPacketSize can be different than the endpoint->maxpacket_limit. DWC3 has been consistently using MaxPacketSize from the endpoint's descriptor, so let's keep it consistent and use the MaxPacketSize from the endpoint's descriptor instead. Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 69bf137aab37..032ea7d709ba 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1121,7 +1121,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, req->request.short_not_ok, req->request.no_interrupt); } else if (req->request.zero && req->request.length && - (IS_ALIGNED(req->request.length,dep->endpoint.maxpacket))) { + (IS_ALIGNED(req->request.length, maxp))) { struct dwc3 *dwc = dep->dwc; struct dwc3_trb *trb; -- cgit v1.2.3 From 479af3216315b239eea87a859ece0d29d27b98f9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 30 Jul 2018 07:58:55 +0000 Subject: usb: renesas_usbhs: Kconfig: convert to SPDX identifiers By default all files without license information are under the default license of the kernel, which is GPL version 2. Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig index b26d7c339c05..7fdbff23ae8b 100644 --- a/drivers/usb/renesas_usbhs/Kconfig +++ b/drivers/usb/renesas_usbhs/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # Renesas USBHS Controller Drivers # -- cgit v1.2.3 From 29c692c96b3a39cd1911fb79cd2505af8d070f07 Mon Sep 17 00:00:00 2001 From: Movie Song Date: Thu, 19 Jul 2018 02:20:48 +0800 Subject: USB: serial: pl2303: add a new device id for ATEN Signed-off-by: Movie Song Cc: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 2 ++ drivers/usb/serial/pl2303.h | 1 + 2 files changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 5d1a1931967e..e41f725ac7aa 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -52,6 +52,8 @@ static const struct usb_device_id id_table[] = { .driver_info = PL2303_QUIRK_ENDPOINT_HACK }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_UC485), .driver_info = PL2303_QUIRK_ENDPOINT_HACK }, + { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_UC232B), + .driver_info = PL2303_QUIRK_ENDPOINT_HACK }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID2) }, { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index fcd72396a7b6..26965cc23c17 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -24,6 +24,7 @@ #define ATEN_VENDOR_ID2 0x0547 #define ATEN_PRODUCT_ID 0x2008 #define ATEN_PRODUCT_UC485 0x2021 +#define ATEN_PRODUCT_UC232B 0x2022 #define ATEN_PRODUCT_ID2 0x2118 #define IODATA_VENDOR_ID 0x04bb -- cgit v1.2.3