diff options
44 files changed, 1918 insertions, 339 deletions
diff --git a/Documentation/devicetree/bindings/usb/maxim,max3421.txt b/Documentation/devicetree/bindings/usb/maxim,max3421.txt new file mode 100644 index 000000000000..7536c3ab3e5a --- /dev/null +++ b/Documentation/devicetree/bindings/usb/maxim,max3421.txt @@ -0,0 +1,24 @@ +Maxim Integrated SPI-based USB 2.0 host controller MAX3421E + +Required properties: + - compatible: "maxim,max3421" + - spi-max-frequency: maximum frequency for this device must not exceed 26 MHz. + - reg: chip select number to which this device is connected. + - maxim,vbus-en-pin: <GPOUTx ACTIVE_LEVEL> + GPOUTx is the number (1-8) of the GPOUT pin of MAX3421E to drive Vbus. + ACTIVE_LEVEL is 0 or 1. + - interrupt-parent: the phandle of the associated interrupt controller. + - interrupts: the interuption line description for the interrupt controller. + The driver configures MAX3421E for active low level triggered interrupts, + configure your interrupt line accordingly. + +Example: + + usb@0 { + compatible = "maxim,max3421"; + reg = <0>; + maxim,vbus-en-pin = <3 1>; + spi-max-frequency = <26000000>; + interrupt-parent = <&PIC>; + interrupts = <42>; + }; diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt index 2d80b60eeabe..ae6e484a8d7c 100644 --- a/Documentation/devicetree/bindings/usb/usb-xhci.txt +++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt @@ -26,6 +26,7 @@ Required properties: Optional properties: - clocks: reference to a clock + - usb2-lpm-disable: indicate if we don't want to enable USB2 HW LPM - usb3-lpm-capable: determines if platform is USB3 LPM capable - quirk-broken-port-ped: set if the controller has broken port disable mechanism diff --git a/drivers/staging/typec/Kconfig b/drivers/staging/typec/Kconfig index 37a0781b0d0c..5359f556d203 100644 --- a/drivers/staging/typec/Kconfig +++ b/drivers/staging/typec/Kconfig @@ -1,13 +1,5 @@ menu "USB Power Delivery and Type-C drivers" -config TYPEC_TCPM - tristate "USB Type-C Port Controller Manager" - depends on USB - select TYPEC - help - The Type-C Port Controller Manager provides a USB PD and USB Type-C - state machine for use with Type-C Port Controllers. - if TYPEC_TCPM config TYPEC_TCPCI @@ -17,8 +9,6 @@ config TYPEC_TCPCI help Type-C Port Controller driver for TCPCI-compliant controller. -source "drivers/staging/typec/fusb302/Kconfig" - endif endmenu diff --git a/drivers/staging/typec/Makefile b/drivers/staging/typec/Makefile index 30a7e29cbc9e..53d649abcb53 100644 --- a/drivers/staging/typec/Makefile +++ b/drivers/staging/typec/Makefile @@ -1,3 +1 @@ -obj-$(CONFIG_TYPEC_TCPM) += tcpm.o obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o -obj-y += fusb302/ diff --git a/drivers/staging/typec/TODO b/drivers/staging/typec/TODO index bc1f97a2d1bf..53fe2f726c88 100644 --- a/drivers/staging/typec/TODO +++ b/drivers/staging/typec/TODO @@ -1,13 +1,3 @@ -tcpm: -- Add documentation (at the very least for the API to low level drivers) -- Split PD code into separate file -- Check if it makes sense to use tracepoints instead of debugfs for debug logs -- Implement Alternate Mode handling -- Address "#if 0" code if not addressed with the above -- Validate all comments marked with "XXX"; either address or remove comments -- Add support for USB PD 3.0. While not mandatory, at least fast role swap - as well as authentication support would be very desirable. - tcpci: - Test with real hardware diff --git a/drivers/staging/typec/fusb302/TODO b/drivers/staging/typec/fusb302/TODO deleted file mode 100644 index 19b466eb585d..000000000000 --- a/drivers/staging/typec/fusb302/TODO +++ /dev/null @@ -1,10 +0,0 @@ -fusb302: -- Find a better logging scheme, at least not having the same debugging/logging - code replicated here and in tcpm -- Find a non-hacky way to coordinate between PM and I2C access -- Documentation? The FUSB302 datasheet provides information on the chip to help - understand the code. But it may still be helpful to have a documentation. -- We may want to replace the "fcs,max-snk-microvolt", "fcs,max-snk-microamp", - "fcs,max-snk-microwatt" and "fcs,operating-snk-microwatt" device(tree) - properties with properties which are part of a generic type-c controller - devicetree binding. diff --git a/drivers/staging/typec/tcpci.c b/drivers/staging/typec/tcpci.c index df72d8b01e73..4636804ea1a4 100644 --- a/drivers/staging/typec/tcpci.c +++ b/drivers/staging/typec/tcpci.c @@ -20,11 +20,11 @@ #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/regmap.h> +#include <linux/usb/pd.h> +#include <linux/usb/tcpm.h> #include <linux/usb/typec.h> -#include "pd.h" #include "tcpci.h" -#include "tcpm.h" #define PD_RETRY_COUNT 3 diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 5160a4a966b3..600a670b4feb 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -570,10 +570,8 @@ static int cxacru_start_wait_urb(struct urb *urb, struct completion *done, { struct timer_list timer; - init_timer(&timer); + setup_timer(&timer, cxacru_timeout_kill, (unsigned long)urb); timer.expires = jiffies + msecs_to_jiffies(CMD_TIMEOUT); - timer.data = (unsigned long) urb; - timer.function = cxacru_timeout_kill; add_timer(&timer); wait_for_completion(done); del_timer_sync(&timer); diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 3676adb40d89..091db9b281f5 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -874,16 +874,13 @@ static int speedtch_bind(struct usbatm_data *usbatm, usbatm->flags |= (use_isoc ? UDSL_USE_ISOC : 0); INIT_WORK(&instance->status_check_work, speedtch_check_status); - init_timer(&instance->status_check_timer); - - instance->status_check_timer.function = speedtch_status_poll; - instance->status_check_timer.data = (unsigned long)instance; + setup_timer(&instance->status_check_timer, speedtch_status_poll, + (unsigned long)instance); instance->last_status = 0xff; instance->poll_delay = MIN_POLL_DELAY; - init_timer(&instance->resubmit_timer); - instance->resubmit_timer.function = speedtch_resubmit_int; - instance->resubmit_timer.data = (unsigned long)instance; + setup_timer(&instance->resubmit_timer, speedtch_resubmit_int, + (unsigned long)instance); instance->int_urb = usb_alloc_urb(0, GFP_KERNEL); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 75ad6718858c..67aa3d039b9b 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2558,9 +2558,7 @@ struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver, hcd->self.bus_name = bus_name; hcd->self.uses_dma = (sysdev->dma_mask != NULL); - init_timer(&hcd->rh_timer); - hcd->rh_timer.function = rh_timer_func; - hcd->rh_timer.data = (unsigned long) hcd; + setup_timer(&hcd->rh_timer, rh_timer_func, (unsigned long)hcd); #ifdef CONFIG_PM INIT_WORK(&hcd->wakeup_work, hcd_resume_work); #endif diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index f04e91ef9e7c..8f0f69783c8b 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2433,9 +2433,7 @@ static DEVICE_ATTR_RO(urbs); static int dummy_start_ss(struct dummy_hcd *dum_hcd) { - init_timer(&dum_hcd->timer); - dum_hcd->timer.function = dummy_timer; - dum_hcd->timer.data = (unsigned long)dum_hcd; + setup_timer(&dum_hcd->timer, dummy_timer, (unsigned long)dum_hcd); dum_hcd->rh_state = DUMMY_RH_RUNNING; dum_hcd->stream_en_ep = 0; INIT_LIST_HEAD(&dum_hcd->urbp_list); @@ -2464,9 +2462,7 @@ static int dummy_start(struct usb_hcd *hcd) return dummy_start_ss(dum_hcd); spin_lock_init(&dum_hcd->dum->lock); - init_timer(&dum_hcd->timer); - dum_hcd->timer.function = dummy_timer; - dum_hcd->timer.data = (unsigned long)dum_hcd; + setup_timer(&dum_hcd->timer, dummy_timer, (unsigned long)dum_hcd); dum_hcd->rh_state = DUMMY_RH_RUNNING; INIT_LIST_HEAD(&dum_hcd->urbp_list); diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index 46ce7bc15f2b..3b8dbed7e2e9 100644 --- a/drivers/usb/gadget/udc/m66592-udc.c +++ b/drivers/usb/gadget/udc/m66592-udc.c @@ -1592,9 +1592,7 @@ static int m66592_probe(struct platform_device *pdev) m66592->gadget.max_speed = USB_SPEED_HIGH; m66592->gadget.name = udc_name; - init_timer(&m66592->timer); - m66592->timer.function = m66592_timer; - m66592->timer.data = (unsigned long)m66592; + setup_timer(&m66592->timer, m66592_timer, (unsigned long)m66592); m66592->reg = reg; ret = request_irq(ires->start, m66592_irq, IRQF_SHARED, diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index a238da906115..974b778e033c 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -2417,9 +2417,7 @@ static int pxa25x_udc_probe(struct platform_device *pdev) gpio_direction_output(dev->mach->gpio_pullup, 0); } - init_timer(&dev->timer); - dev->timer.function = udc_watchdog; - dev->timer.data = (unsigned long) dev; + setup_timer(&dev->timer, udc_watchdog, (unsigned long)dev); the_controller = dev; platform_set_drvdata(pdev, dev); diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index 118ad70f1af0..bb844b94df10 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1877,9 +1877,7 @@ static int r8a66597_probe(struct platform_device *pdev) r8a66597->gadget.max_speed = USB_SPEED_HIGH; r8a66597->gadget.name = udc_name; - init_timer(&r8a66597->timer); - r8a66597->timer.function = r8a66597_timer; - r8a66597->timer.data = (unsigned long)r8a66597; + setup_timer(&r8a66597->timer, r8a66597_timer, (unsigned long)r8a66597); r8a66597->reg = reg; if (r8a66597->pdata->on_chip) { diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c index 38a165dbf924..47df99dbaef4 100644 --- a/drivers/usb/gadget/udc/snps_udc_core.c +++ b/drivers/usb/gadget/udc/snps_udc_core.c @@ -3207,13 +3207,9 @@ int udc_probe(struct udc *dev) goto finished; /* timer init */ - init_timer(&udc_timer); - udc_timer.function = udc_timer_function; - udc_timer.data = 1; + setup_timer(&udc_timer, udc_timer_function, 1); /* timer pollstall init */ - init_timer(&udc_pollstall_timer); - udc_pollstall_timer.function = udc_pollstall_timer_function; - udc_pollstall_timer.data = 1; + setup_timer(&udc_pollstall_timer, udc_pollstall_timer_function, 1); /* set SD */ reg = readl(&dev->regs->ctl); diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index cf2691fffcc0..b2a7f058cccb 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -25,6 +25,10 @@ ifneq ($(CONFIG_USB_XHCI_RCAR), ) xhci-plat-hcd-y += xhci-rcar.o endif +ifneq ($(CONFIG_DEBUG_FS),) + xhci-hcd-y += xhci-debugfs.o +endif + obj-$(CONFIG_USB_WHCI_HCD) += whci/ obj-$(CONFIG_USB_PCI) += pci-quirks.o diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 7440722bfbf0..2a8b9bdc0e57 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -205,7 +205,8 @@ static int __maybe_unused ehci_atmel_drv_resume(struct device *dev) struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd); atmel_start_clock(atmel_ehci); - return ehci_resume(hcd, false); + ehci_resume(hcd, false); + return 0; } #ifdef CONFIG_OF diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 0ece9a9341e5..928a5aabee02 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -60,6 +60,7 @@ #include <linux/spi/spi.h> #include <linux/usb.h> #include <linux/usb/hcd.h> +#include <linux/of.h> #include <linux/platform_data/max3421-hcd.h> @@ -85,6 +86,8 @@ USB_PORT_STAT_C_OVERCURRENT | \ USB_PORT_STAT_C_RESET) << 16) +#define MAX3421_GPOUT_COUNT 8 + enum max3421_rh_state { MAX3421_RH_RESET, MAX3421_RH_SUSPENDED, @@ -1672,7 +1675,7 @@ max3421_gpout_set_value(struct usb_hcd *hcd, u8 pin_number, u8 value) u8 mask, idx; --pin_number; - if (pin_number > 7) + if (pin_number >= MAX3421_GPOUT_COUNT) return; mask = 1u << (pin_number % 4); @@ -1699,6 +1702,10 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, spin_lock_irqsave(&max3421_hcd->lock, flags); pdata = spi->dev.platform_data; + if (!pdata) { + dev_err(&spi->dev, "Device platform data is missing\n"); + return -EFAULT; + } switch (type_req) { case ClearHubFeature: @@ -1832,10 +1839,34 @@ static const struct hc_driver max3421_hcd_desc = { }; static int +max3421_of_vbus_en_pin(struct device *dev, struct max3421_hcd_platform_data *pdata) +{ + int retval; + uint32_t value[2]; + + if (!pdata) + return -EINVAL; + + retval = of_property_read_u32_array(dev->of_node, "maxim,vbus-en-pin", value, 2); + if (retval) { + dev_err(dev, "device tree node property 'maxim,vbus-en-pin' is missing\n"); + return retval; + } + dev_info(dev, "property 'maxim,vbus-en-pin' value is <%d %d>\n", value[0], value[1]); + + pdata->vbus_gpout = value[0]; + pdata->vbus_active_level = value[1]; + + return 0; +} + +static int max3421_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct max3421_hcd *max3421_hcd; struct usb_hcd *hcd = NULL; + struct max3421_hcd_platform_data *pdata = NULL; int retval = -ENOMEM; if (spi_setup(spi) < 0) { @@ -1843,6 +1874,42 @@ max3421_probe(struct spi_device *spi) return -EFAULT; } + if (!spi->irq) { + dev_err(dev, "Failed to get SPI IRQ"); + return -EFAULT; + } + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&spi->dev, "failed to allocate memory for private data\n"); + retval = -ENOMEM; + goto error; + } + retval = max3421_of_vbus_en_pin(dev, pdata); + if (retval) + goto error; + + spi->dev.platform_data = pdata; + } + + pdata = spi->dev.platform_data; + if (!pdata) { + dev_err(&spi->dev, "driver configuration data is not provided\n"); + retval = -EFAULT; + goto error; + } + if (pdata->vbus_active_level > 1) { + dev_err(&spi->dev, "vbus active level value %d is out of range (0/1)\n", pdata->vbus_active_level); + retval = -EINVAL; + goto error; + } + if (pdata->vbus_gpout < 1 || pdata->vbus_gpout > MAX3421_GPOUT_COUNT) { + dev_err(&spi->dev, "vbus gpout value %d is out of range (1..8)\n", pdata->vbus_gpout); + retval = -EINVAL; + goto error; + } + hcd = usb_create_hcd(&max3421_hcd_desc, &spi->dev, dev_name(&spi->dev)); if (!hcd) { @@ -1885,6 +1952,11 @@ max3421_probe(struct spi_device *spi) return 0; error: + if (IS_ENABLED(CONFIG_OF) && dev->of_node && pdata) { + devm_kfree(&spi->dev, pdata); + spi->dev.platform_data = NULL; + } + if (hcd) { kfree(max3421_hcd->tx); kfree(max3421_hcd->rx); @@ -1923,17 +1995,30 @@ max3421_remove(struct spi_device *spi) spin_unlock_irqrestore(&max3421_hcd->lock, flags); + if (IS_ENABLED(CONFIG_OF) && spi->dev.platform_data) { + dev_dbg(&spi->dev, "Freeing platform data structure\n"); + devm_kfree(&spi->dev, spi->dev.platform_data); + spi->dev.platform_data = NULL; + } + free_irq(spi->irq, hcd); usb_put_hcd(hcd); return 0; } +static const struct of_device_id max3421_of_match_table[] = { + { .compatible = "maxim,max3421", }, + {}, +}; +MODULE_DEVICE_TABLE(of, max3421_of_match_table); + static struct spi_driver max3421_driver = { .probe = max3421_probe, .remove = max3421_remove, .driver = { .name = "max3421-hcd", + .of_match_table = of_match_ptr(max3421_of_match_table), }, }; diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index 3a9ea32508df..8758c73215d7 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -42,7 +42,7 @@ #if 0 static void dump_hci_status(struct usb_hcd *hcd, const char *label) { - unsigned long status = sa1111_readl(hcd->regs + USB_STATUS); + unsigned long status = readl_relaxed(hcd->regs + USB_STATUS); printk(KERN_DEBUG "%s USB_STATUS = { %s%s%s%s%s}\n", label, ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""), @@ -134,7 +134,7 @@ static int sa1111_start_hc(struct sa1111_dev *dev) * Configure the power sense and control lines. Place the USB * host controller in reset. */ - sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET, + writel_relaxed(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET, dev->mapbase + USB_RESET); /* @@ -144,7 +144,7 @@ static int sa1111_start_hc(struct sa1111_dev *dev) ret = sa1111_enable_device(dev); if (ret == 0) { udelay(11); - sa1111_writel(usb_rst, dev->mapbase + USB_RESET); + writel_relaxed(usb_rst, dev->mapbase + USB_RESET); } return ret; @@ -159,8 +159,8 @@ static void sa1111_stop_hc(struct sa1111_dev *dev) /* * Put the USB host controller into reset. */ - usb_rst = sa1111_readl(dev->mapbase + USB_RESET); - sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET, + usb_rst = readl_relaxed(dev->mapbase + USB_RESET); + writel_relaxed(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET, dev->mapbase + USB_RESET); /* @@ -178,7 +178,7 @@ static void sa1111_stop_hc(struct sa1111_dev *dev) static int ohci_hcd_sa1111_probe(struct sa1111_dev *dev) { struct usb_hcd *hcd; - int ret; + int ret, irq; if (usb_disabled()) return -ENODEV; @@ -196,6 +196,12 @@ static int ohci_hcd_sa1111_probe(struct sa1111_dev *dev) hcd->rsrc_start = dev->res.start; hcd->rsrc_len = resource_size(&dev->res); + irq = sa1111_get_irq(dev, 1); + if (irq <= 0) { + ret = irq ? : -ENXIO; + goto err1; + } + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { dev_dbg(&dev->dev, "request_mem_region failed\n"); ret = -EBUSY; @@ -208,7 +214,7 @@ static int ohci_hcd_sa1111_probe(struct sa1111_dev *dev) if (ret) goto err2; - ret = usb_add_hcd(hcd, dev->irq[1], 0); + ret = usb_add_hcd(hcd, irq, 0); if (ret == 0) { device_wakeup_enable(hcd->self.controller); return ret; @@ -241,8 +247,9 @@ static int ohci_hcd_sa1111_remove(struct sa1111_dev *dev) return 0; } -static void ohci_hcd_sa1111_shutdown(struct sa1111_dev *dev) +static void ohci_hcd_sa1111_shutdown(struct device *_dev) { + struct sa1111_dev *dev = to_sa1111_device(_dev); struct usb_hcd *hcd = sa1111_get_drvdata(dev); if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { @@ -255,9 +262,9 @@ static struct sa1111_driver ohci_hcd_sa1111_driver = { .drv = { .name = "sa1111-ohci", .owner = THIS_MODULE, + .shutdown = ohci_hcd_sa1111_shutdown, }, .devid = SA1111_DEVID_USB, .probe = ohci_hcd_sa1111_probe, .remove = ohci_hcd_sa1111_remove, - .shutdown = ohci_hcd_sa1111_shutdown, }; diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c new file mode 100644 index 000000000000..6772ee90944b --- /dev/null +++ b/drivers/usb/host/xhci-debugfs.c @@ -0,0 +1,526 @@ +/* + * xhci-debugfs.c - xHCI debugfs interface + * + * Copyright (C) 2017 Intel Corporation + * + * Author: Lu Baolu <baolu.lu@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/slab.h> + +#include "xhci.h" +#include "xhci-debugfs.h" + +static const struct debugfs_reg32 xhci_cap_regs[] = { + dump_register(CAPLENGTH), + dump_register(HCSPARAMS1), + dump_register(HCSPARAMS2), + dump_register(HCSPARAMS3), + dump_register(HCCPARAMS1), + dump_register(DOORBELLOFF), + dump_register(RUNTIMEOFF), + dump_register(HCCPARAMS2), +}; + +static const struct debugfs_reg32 xhci_op_regs[] = { + dump_register(USBCMD), + dump_register(USBSTS), + dump_register(PAGESIZE), + dump_register(DNCTRL), + dump_register(CRCR), + dump_register(DCBAAP_LOW), + dump_register(DCBAAP_HIGH), + dump_register(CONFIG), +}; + +static const struct debugfs_reg32 xhci_runtime_regs[] = { + dump_register(MFINDEX), + dump_register(IR0_IMAN), + dump_register(IR0_IMOD), + dump_register(IR0_ERSTSZ), + dump_register(IR0_ERSTBA_LOW), + dump_register(IR0_ERSTBA_HIGH), + dump_register(IR0_ERDP_LOW), + dump_register(IR0_ERDP_HIGH), +}; + +static const struct debugfs_reg32 xhci_extcap_legsup[] = { + dump_register(EXTCAP_USBLEGSUP), + dump_register(EXTCAP_USBLEGCTLSTS), +}; + +static const struct debugfs_reg32 xhci_extcap_protocol[] = { + dump_register(EXTCAP_REVISION), + dump_register(EXTCAP_NAME), + dump_register(EXTCAP_PORTINFO), + dump_register(EXTCAP_PORTTYPE), + dump_register(EXTCAP_MANTISSA1), + dump_register(EXTCAP_MANTISSA2), + dump_register(EXTCAP_MANTISSA3), + dump_register(EXTCAP_MANTISSA4), + dump_register(EXTCAP_MANTISSA5), + dump_register(EXTCAP_MANTISSA6), +}; + +static const struct debugfs_reg32 xhci_extcap_dbc[] = { + dump_register(EXTCAP_DBC_CAPABILITY), + dump_register(EXTCAP_DBC_DOORBELL), + dump_register(EXTCAP_DBC_ERSTSIZE), + dump_register(EXTCAP_DBC_ERST_LOW), + dump_register(EXTCAP_DBC_ERST_HIGH), + dump_register(EXTCAP_DBC_ERDP_LOW), + dump_register(EXTCAP_DBC_ERDP_HIGH), + dump_register(EXTCAP_DBC_CONTROL), + dump_register(EXTCAP_DBC_STATUS), + dump_register(EXTCAP_DBC_PORTSC), + dump_register(EXTCAP_DBC_CONT_LOW), + dump_register(EXTCAP_DBC_CONT_HIGH), + dump_register(EXTCAP_DBC_DEVINFO1), + dump_register(EXTCAP_DBC_DEVINFO2), +}; + +static struct dentry *xhci_debugfs_root; + +static struct xhci_regset *xhci_debugfs_alloc_regset(struct xhci_hcd *xhci) +{ + struct xhci_regset *regset; + + regset = kzalloc(sizeof(*regset), GFP_KERNEL); + if (!regset) + return NULL; + + /* + * The allocation and free of regset are executed in order. + * We needn't a lock here. + */ + INIT_LIST_HEAD(®set->list); + list_add_tail(®set->list, &xhci->regset_list); + + return regset; +} + +static void xhci_debugfs_free_regset(struct xhci_regset *regset) +{ + if (!regset) + return; + + list_del(®set->list); + kfree(regset); +} + +static void xhci_debugfs_regset(struct xhci_hcd *xhci, u32 base, + const struct debugfs_reg32 *regs, + size_t nregs, struct dentry *parent, + const char *fmt, ...) +{ + struct xhci_regset *rgs; + va_list args; + struct debugfs_regset32 *regset; + struct usb_hcd *hcd = xhci_to_hcd(xhci); + + rgs = xhci_debugfs_alloc_regset(xhci); + if (!rgs) + return; + + va_start(args, fmt); + vsnprintf(rgs->name, sizeof(rgs->name), fmt, args); + va_end(args); + + regset = &rgs->regset; + regset->regs = regs; + regset->nregs = nregs; + regset->base = hcd->regs + base; + + debugfs_create_regset32((const char *)rgs->name, 0444, parent, regset); +} + +static void xhci_debugfs_extcap_regset(struct xhci_hcd *xhci, int cap_id, + const struct debugfs_reg32 *regs, + size_t n, const char *cap_name) +{ + u32 offset; + int index = 0; + size_t psic, nregs = n; + void __iomem *base = &xhci->cap_regs->hc_capbase; + + offset = xhci_find_next_ext_cap(base, 0, cap_id); + while (offset) { + if (cap_id == XHCI_EXT_CAPS_PROTOCOL) { + psic = XHCI_EXT_PORT_PSIC(readl(base + offset + 8)); + nregs = min(4 + psic, n); + } + + xhci_debugfs_regset(xhci, offset, regs, nregs, + xhci->debugfs_root, "%s:%02d", + cap_name, index); + offset = xhci_find_next_ext_cap(base, offset, cap_id); + index++; + } +} + +static int xhci_ring_enqueue_show(struct seq_file *s, void *unused) +{ + dma_addr_t dma; + struct xhci_ring *ring = s->private; + + dma = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue); + seq_printf(s, "%pad\n", &dma); + + return 0; +} + +static int xhci_ring_dequeue_show(struct seq_file *s, void *unused) +{ + dma_addr_t dma; + struct xhci_ring *ring = s->private; + + dma = xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue); + seq_printf(s, "%pad\n", &dma); + + return 0; +} + +static int xhci_ring_cycle_show(struct seq_file *s, void *unused) +{ + struct xhci_ring *ring = s->private; + + seq_printf(s, "%d\n", ring->cycle_state); + + return 0; +} + +static void xhci_ring_dump_segment(struct seq_file *s, + struct xhci_segment *seg) +{ + int i; + dma_addr_t dma; + union xhci_trb *trb; + + for (i = 0; i < TRBS_PER_SEGMENT; i++) { + trb = &seg->trbs[i]; + dma = seg->dma + i * sizeof(*trb); + seq_printf(s, "%pad: %s\n", &dma, + xhci_decode_trb(trb->generic.field[0], + trb->generic.field[1], + trb->generic.field[2], + trb->generic.field[3])); + } +} + +static int xhci_ring_trb_show(struct seq_file *s, void *unused) +{ + int i; + struct xhci_ring *ring = s->private; + struct xhci_segment *seg = ring->first_seg; + + for (i = 0; i < ring->num_segs; i++) { + xhci_ring_dump_segment(s, seg); + seg = seg->next; + } + + return 0; +} + +static struct xhci_file_map ring_files[] = { + {"enqueue", xhci_ring_enqueue_show, }, + {"dequeue", xhci_ring_dequeue_show, }, + {"cycle", xhci_ring_cycle_show, }, + {"trbs", xhci_ring_trb_show, }, +}; + +static int xhci_ring_open(struct inode *inode, struct file *file) +{ + int i; + struct xhci_file_map *f_map; + const char *file_name = file_dentry(file)->d_iname; + + for (i = 0; i < ARRAY_SIZE(ring_files); i++) { + f_map = &ring_files[i]; + + if (strcmp(f_map->name, file_name) == 0) + break; + } + + return single_open(file, f_map->show, inode->i_private); +} + +static const struct file_operations xhci_ring_fops = { + .open = xhci_ring_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int xhci_slot_context_show(struct seq_file *s, void *unused) +{ + struct xhci_hcd *xhci; + struct xhci_slot_ctx *slot_ctx; + struct xhci_slot_priv *priv = s->private; + struct xhci_virt_device *dev = priv->dev; + + xhci = hcd_to_xhci(bus_to_hcd(dev->udev->bus)); + slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx); + seq_printf(s, "%pad: %s\n", &dev->out_ctx->dma, + xhci_decode_slot_context(slot_ctx->dev_info, + slot_ctx->dev_info2, + slot_ctx->tt_info, + slot_ctx->dev_state)); + + return 0; +} + +static int xhci_endpoint_context_show(struct seq_file *s, void *unused) +{ + int dci; + dma_addr_t dma; + struct xhci_hcd *xhci; + struct xhci_ep_ctx *ep_ctx; + struct xhci_slot_priv *priv = s->private; + struct xhci_virt_device *dev = priv->dev; + + xhci = hcd_to_xhci(bus_to_hcd(dev->udev->bus)); + + for (dci = 1; dci < 32; dci++) { + ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, dci); + dma = dev->out_ctx->dma + dci * CTX_SIZE(xhci->hcc_params); + seq_printf(s, "%pad: %s\n", &dma, + xhci_decode_ep_context(ep_ctx->ep_info, + ep_ctx->ep_info2, + ep_ctx->deq, + ep_ctx->tx_info)); + } + + return 0; +} + +static int xhci_device_name_show(struct seq_file *s, void *unused) +{ + struct xhci_slot_priv *priv = s->private; + struct xhci_virt_device *dev = priv->dev; + + seq_printf(s, "%s\n", dev_name(&dev->udev->dev)); + + return 0; +} + +static struct xhci_file_map context_files[] = { + {"name", xhci_device_name_show, }, + {"slot-context", xhci_slot_context_show, }, + {"ep-context", xhci_endpoint_context_show, }, +}; + +static int xhci_context_open(struct inode *inode, struct file *file) +{ + int i; + struct xhci_file_map *f_map; + const char *file_name = file_dentry(file)->d_iname; + + for (i = 0; i < ARRAY_SIZE(context_files); i++) { + f_map = &context_files[i]; + + if (strcmp(f_map->name, file_name) == 0) + break; + } + + return single_open(file, f_map->show, inode->i_private); +} + +static const struct file_operations xhci_context_fops = { + .open = xhci_context_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void xhci_debugfs_create_files(struct xhci_hcd *xhci, + struct xhci_file_map *files, + size_t nentries, void *data, + struct dentry *parent, + const struct file_operations *fops) +{ + int i; + + for (i = 0; i < nentries; i++) + debugfs_create_file(files[i].name, 0444, parent, data, fops); +} + +static struct dentry *xhci_debugfs_create_ring_dir(struct xhci_hcd *xhci, + struct xhci_ring *ring, + const char *name, + struct dentry *parent) +{ + struct dentry *dir; + + dir = debugfs_create_dir(name, parent); + xhci_debugfs_create_files(xhci, ring_files, ARRAY_SIZE(ring_files), + ring, dir, &xhci_ring_fops); + + return dir; +} + +static void xhci_debugfs_create_context_files(struct xhci_hcd *xhci, + struct dentry *parent, + int slot_id) +{ + struct xhci_virt_device *dev = xhci->devs[slot_id]; + + xhci_debugfs_create_files(xhci, context_files, + ARRAY_SIZE(context_files), + dev->debugfs_private, + parent, &xhci_context_fops); +} + +void xhci_debugfs_create_endpoint(struct xhci_hcd *xhci, + struct xhci_virt_device *dev, + int ep_index) +{ + struct xhci_ep_priv *epriv; + struct xhci_slot_priv *spriv = dev->debugfs_private; + + if (spriv->eps[ep_index]) + return; + + epriv = kzalloc(sizeof(*epriv), GFP_KERNEL); + if (!epriv) + return; + + snprintf(epriv->name, sizeof(epriv->name), "ep%02d", ep_index); + epriv->root = xhci_debugfs_create_ring_dir(xhci, + dev->eps[ep_index].new_ring, + epriv->name, + spriv->root); + spriv->eps[ep_index] = epriv; +} + +void xhci_debugfs_remove_endpoint(struct xhci_hcd *xhci, + struct xhci_virt_device *dev, + int ep_index) +{ + struct xhci_ep_priv *epriv; + struct xhci_slot_priv *spriv = dev->debugfs_private; + + if (!spriv || !spriv->eps[ep_index]) + return; + + epriv = spriv->eps[ep_index]; + debugfs_remove_recursive(epriv->root); + spriv->eps[ep_index] = NULL; + kfree(epriv); +} + +void xhci_debugfs_create_slot(struct xhci_hcd *xhci, int slot_id) +{ + struct xhci_slot_priv *priv; + struct xhci_virt_device *dev = xhci->devs[slot_id]; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return; + + snprintf(priv->name, sizeof(priv->name), "%02d", slot_id); + priv->root = debugfs_create_dir(priv->name, xhci->debugfs_slots); + priv->dev = dev; + dev->debugfs_private = priv; + + xhci_debugfs_create_ring_dir(xhci, dev->eps[0].ring, + "ep00", priv->root); + + xhci_debugfs_create_context_files(xhci, priv->root, slot_id); +} + +void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id) +{ + int i; + struct xhci_slot_priv *priv; + struct xhci_virt_device *dev = xhci->devs[slot_id]; + + if (!dev || !dev->debugfs_private) + return; + + priv = dev->debugfs_private; + + debugfs_remove_recursive(priv->root); + + for (i = 0; i < 31; i++) + kfree(priv->eps[i]); + + kfree(priv); + dev->debugfs_private = NULL; +} + +void xhci_debugfs_init(struct xhci_hcd *xhci) +{ + struct device *dev = xhci_to_hcd(xhci)->self.controller; + + xhci->debugfs_root = debugfs_create_dir(dev_name(dev), + xhci_debugfs_root); + + INIT_LIST_HEAD(&xhci->regset_list); + + xhci_debugfs_regset(xhci, + 0, + xhci_cap_regs, ARRAY_SIZE(xhci_cap_regs), + xhci->debugfs_root, "reg-cap"); + + xhci_debugfs_regset(xhci, + HC_LENGTH(readl(&xhci->cap_regs->hc_capbase)), + xhci_op_regs, ARRAY_SIZE(xhci_op_regs), + xhci->debugfs_root, "reg-op"); + + xhci_debugfs_regset(xhci, + readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK, + xhci_runtime_regs, ARRAY_SIZE(xhci_runtime_regs), + xhci->debugfs_root, "reg-runtime"); + + xhci_debugfs_extcap_regset(xhci, XHCI_EXT_CAPS_LEGACY, + xhci_extcap_legsup, + ARRAY_SIZE(xhci_extcap_legsup), + "reg-ext-legsup"); + + xhci_debugfs_extcap_regset(xhci, XHCI_EXT_CAPS_PROTOCOL, + xhci_extcap_protocol, + ARRAY_SIZE(xhci_extcap_protocol), + "reg-ext-protocol"); + + xhci_debugfs_extcap_regset(xhci, XHCI_EXT_CAPS_DEBUG, + xhci_extcap_dbc, + ARRAY_SIZE(xhci_extcap_dbc), + "reg-ext-dbc"); + + xhci_debugfs_create_ring_dir(xhci, xhci->cmd_ring, + "command-ring", + xhci->debugfs_root); + + xhci_debugfs_create_ring_dir(xhci, xhci->event_ring, + "event-ring", + xhci->debugfs_root); + + xhci->debugfs_slots = debugfs_create_dir("devices", xhci->debugfs_root); +} + +void xhci_debugfs_exit(struct xhci_hcd *xhci) +{ + struct xhci_regset *rgs, *tmp; + + debugfs_remove_recursive(xhci->debugfs_root); + xhci->debugfs_root = NULL; + xhci->debugfs_slots = NULL; + + list_for_each_entry_safe(rgs, tmp, &xhci->regset_list, list) + xhci_debugfs_free_regset(rgs); +} + +void __init xhci_debugfs_create_root(void) +{ + xhci_debugfs_root = debugfs_create_dir("xhci", usb_debug_root); +} + +void __exit xhci_debugfs_remove_root(void) +{ + debugfs_remove_recursive(xhci_debugfs_root); + xhci_debugfs_root = NULL; +} diff --git a/drivers/usb/host/xhci-debugfs.h b/drivers/usb/host/xhci-debugfs.h new file mode 100644 index 000000000000..3adc9976f180 --- /dev/null +++ b/drivers/usb/host/xhci-debugfs.h @@ -0,0 +1,137 @@ +/* + * xhci-debugfs.h - xHCI debugfs interface + * + * Copyright (C) 2017 Intel Corporation + * + * Author: Lu Baolu <baolu.lu@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_XHCI_DEBUGFS_H +#define __LINUX_XHCI_DEBUGFS_H + +#include <linux/debugfs.h> + +#define DEBUGFS_NAMELEN 32 + +#define REG_CAPLENGTH 0x00 +#define REG_HCSPARAMS1 0x04 +#define REG_HCSPARAMS2 0x08 +#define REG_HCSPARAMS3 0x0c +#define REG_HCCPARAMS1 0x10 +#define REG_DOORBELLOFF 0x14 +#define REG_RUNTIMEOFF 0x18 +#define REG_HCCPARAMS2 0x1c + +#define REG_USBCMD 0x00 +#define REG_USBSTS 0x04 +#define REG_PAGESIZE 0x08 +#define REG_DNCTRL 0x14 +#define REG_CRCR 0x18 +#define REG_DCBAAP_LOW 0x30 +#define REG_DCBAAP_HIGH 0x34 +#define REG_CONFIG 0x38 + +#define REG_MFINDEX 0x00 +#define REG_IR0_IMAN 0x20 +#define REG_IR0_IMOD 0x24 +#define REG_IR0_ERSTSZ 0x28 +#define REG_IR0_ERSTBA_LOW 0x30 +#define REG_IR0_ERSTBA_HIGH 0x34 +#define REG_IR0_ERDP_LOW 0x38 +#define REG_IR0_ERDP_HIGH 0x3c + +#define REG_EXTCAP_USBLEGSUP 0x00 +#define REG_EXTCAP_USBLEGCTLSTS 0x04 + +#define REG_EXTCAP_REVISION 0x00 +#define REG_EXTCAP_NAME 0x04 +#define REG_EXTCAP_PORTINFO 0x08 +#define REG_EXTCAP_PORTTYPE 0x0c +#define REG_EXTCAP_MANTISSA1 0x10 +#define REG_EXTCAP_MANTISSA2 0x14 +#define REG_EXTCAP_MANTISSA3 0x18 +#define REG_EXTCAP_MANTISSA4 0x1c +#define REG_EXTCAP_MANTISSA5 0x20 +#define REG_EXTCAP_MANTISSA6 0x24 + +#define REG_EXTCAP_DBC_CAPABILITY 0x00 +#define REG_EXTCAP_DBC_DOORBELL 0x04 +#define REG_EXTCAP_DBC_ERSTSIZE 0x08 +#define REG_EXTCAP_DBC_ERST_LOW 0x10 +#define REG_EXTCAP_DBC_ERST_HIGH 0x14 +#define REG_EXTCAP_DBC_ERDP_LOW 0x18 +#define REG_EXTCAP_DBC_ERDP_HIGH 0x1c +#define REG_EXTCAP_DBC_CONTROL 0x20 +#define REG_EXTCAP_DBC_STATUS 0x24 +#define REG_EXTCAP_DBC_PORTSC 0x28 +#define REG_EXTCAP_DBC_CONT_LOW 0x30 +#define REG_EXTCAP_DBC_CONT_HIGH 0x34 +#define REG_EXTCAP_DBC_DEVINFO1 0x38 +#define REG_EXTCAP_DBC_DEVINFO2 0x3c + +#define dump_register(nm) \ +{ \ + .name = __stringify(nm), \ + .offset = REG_ ##nm, \ +} + +struct xhci_regset { + char name[DEBUGFS_NAMELEN]; + struct debugfs_regset32 regset; + size_t nregs; + struct dentry *parent; + struct list_head list; +}; + +struct xhci_file_map { + const char *name; + int (*show)(struct seq_file *s, void *unused); +}; + +struct xhci_ep_priv { + char name[DEBUGFS_NAMELEN]; + struct dentry *root; +}; + +struct xhci_slot_priv { + char name[DEBUGFS_NAMELEN]; + struct dentry *root; + struct xhci_ep_priv *eps[31]; + struct xhci_virt_device *dev; +}; + +#ifdef CONFIG_DEBUG_FS +void xhci_debugfs_init(struct xhci_hcd *xhci); +void xhci_debugfs_exit(struct xhci_hcd *xhci); +void __init xhci_debugfs_create_root(void); +void __exit xhci_debugfs_remove_root(void); +void xhci_debugfs_create_slot(struct xhci_hcd *xhci, int slot_id); +void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id); +void xhci_debugfs_create_endpoint(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int ep_index); +void xhci_debugfs_remove_endpoint(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int ep_index); +#else +static inline void xhci_debugfs_init(struct xhci_hcd *xhci) { } +static inline void xhci_debugfs_exit(struct xhci_hcd *xhci) { } +static inline void __init xhci_debugfs_create_root(void) { } +static inline void __exit xhci_debugfs_remove_root(void) { } +static inline void xhci_debugfs_create_slot(struct xhci_hcd *x, int s) { } +static inline void xhci_debugfs_remove_slot(struct xhci_hcd *x, int s) { } +static inline void +xhci_debugfs_create_endpoint(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int ep_index) { } +static inline void +xhci_debugfs_remove_endpoint(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int ep_index) { } +#endif /* CONFIG_DEBUG_FS */ + +#endif /* __LINUX_XHCI_DEBUGFS_H */ diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index da9158f171cb..f070f94a8022 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -621,7 +621,10 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, xhci_dbg(xhci, "Disable all slots\n"); spin_unlock_irqrestore(&xhci->lock, *flags); for (i = 1; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) { - retval = xhci_disable_slot(xhci, NULL, i); + if (!xhci->devs[i]) + continue; + + retval = xhci_disable_slot(xhci, i); if (retval) xhci_err(xhci, "Failed to disable slot %d, %d. Enter test mode anyway\n", i, retval); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 2a82c927ded2..b81a2317420b 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -28,6 +28,7 @@ #include "xhci.h" #include "xhci-trace.h" +#include "xhci-debugfs.h" /* * Allocates a generic ring segment from the ring pool, sets the dma address, @@ -465,8 +466,6 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, return 0; } -#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32) - static struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, int type, gfp_t flags) { @@ -961,6 +960,7 @@ void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id) } } /* we are now at a leaf device */ + xhci_debugfs_remove_slot(xhci, slot_id); xhci_free_virt_device(xhci, slot_id); } @@ -1496,7 +1496,6 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, ep_ctx->tx_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_LO(max_esit_payload) | EP_AVG_TRB_LENGTH(avg_trb_len)); - /* FIXME Debug endpoint context */ return 0; } diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 1cb6eaef4ae1..1969e56a8d8d 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/usb/phy.h> #include <linux/slab.h> @@ -152,7 +153,7 @@ MODULE_DEVICE_TABLE(of, usb_xhci_of_match); static int xhci_plat_probe(struct platform_device *pdev) { - const struct of_device_id *match; + const struct xhci_plat_priv *priv_match; const struct hc_driver *driver; struct device *sysdev; struct xhci_hcd *xhci; @@ -242,9 +243,8 @@ static int xhci_plat_probe(struct platform_device *pdev) } xhci = hcd_to_xhci(hcd); - match = of_match_node(usb_xhci_of_match, pdev->dev.of_node); - if (match) { - const struct xhci_plat_priv *priv_match = match->data; + priv_match = of_device_get_match_data(&pdev->dev); + if (priv_match) { struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); /* Just copy data for now */ @@ -263,6 +263,9 @@ static int xhci_plat_probe(struct platform_device *pdev) goto disable_clk; } + if (device_property_read_bool(sysdev, "usb2-lpm-disable")) + xhci->quirks |= XHCI_HW_LPM_DISABLE; + if (device_property_read_bool(sysdev, "usb3-lpm-capable")) xhci->quirks |= XHCI_LPM_SUPPORT; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index a9443651ce0f..4764121ba2b1 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -171,13 +171,13 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) if (ring->type == TYPE_EVENT) { if (!last_trb_on_seg(ring->deq_seg, ring->dequeue)) { ring->dequeue++; - return; + goto out; } if (last_trb_on_ring(ring, ring->deq_seg, ring->dequeue)) ring->cycle_state ^= 1; ring->deq_seg = ring->deq_seg->next; ring->dequeue = ring->deq_seg->trbs; - return; + goto out; } /* All other rings have link trbs */ @@ -190,6 +190,7 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) ring->dequeue = ring->deq_seg->trbs; } +out: trace_xhci_inc_deq(ring); return; @@ -1679,9 +1680,14 @@ static void handle_port_status(struct xhci_hcd *xhci, bus_state->resume_done[faked_port_index] = jiffies + msecs_to_jiffies(USB_RESUME_TIMEOUT); set_bit(faked_port_index, &bus_state->resuming_ports); + /* Do the rest in GetPortStatus after resume time delay. + * Avoid polling roothub status before that so that a + * usb device auto-resume latency around ~40ms. + */ + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); mod_timer(&hcd->rh_timer, bus_state->resume_done[faked_port_index]); - /* Do the rest in GetPortStatus */ + bogus_port_status = true; } } diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index f20753b99624..754dfb0e1a02 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -388,6 +388,11 @@ DEFINE_EVENT(xhci_log_slot_ctx, xhci_handle_cmd_set_deq, TP_ARGS(ctx) ); +DEFINE_EVENT(xhci_log_slot_ctx, xhci_configure_endpoint, + TP_PROTO(struct xhci_slot_ctx *ctx), + TP_ARGS(ctx) +); + DECLARE_EVENT_CLASS(xhci_log_ring, TP_PROTO(struct xhci_ring *ring), TP_ARGS(ring), diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index ee198ea47f49..21dd1d98508f 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -32,6 +32,7 @@ #include "xhci.h" #include "xhci-trace.h" #include "xhci-mtk.h" +#include "xhci-debugfs.h" #define DRIVER_AUTHOR "Sarah Sharp" #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver" @@ -632,6 +633,9 @@ int xhci_run(struct usb_hcd *hcd) } xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_run for USB2 roothub"); + + xhci_debugfs_init(xhci); + return 0; } EXPORT_SYMBOL_GPL(xhci_run); @@ -660,6 +664,8 @@ static void xhci_stop(struct usb_hcd *hcd) return; } + xhci_debugfs_exit(xhci); + spin_lock_irq(&xhci->lock); xhci->xhc_state |= XHCI_STATE_HALTED; xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; @@ -1600,6 +1606,8 @@ static int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, ctrl_ctx->add_flags &= cpu_to_le32(~drop_flag); new_add_flags = le32_to_cpu(ctrl_ctx->add_flags); + xhci_debugfs_remove_endpoint(xhci, xhci->devs[udev->slot_id], ep_index); + xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep); if (xhci->quirks & XHCI_MTK_HOST) @@ -1723,6 +1731,8 @@ static int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, /* Store the usb_device pointer for later use */ ep->hcpriv = udev; + xhci_debugfs_create_endpoint(xhci, virt_dev, ep_index); + xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n", (unsigned int) ep->desc.bEndpointAddress, udev->slot_id, @@ -2555,6 +2565,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, unsigned long flags; struct xhci_input_control_ctx *ctrl_ctx; struct xhci_virt_device *virt_dev; + struct xhci_slot_ctx *slot_ctx; if (!command) return -EINVAL; @@ -2593,6 +2604,9 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, return -ENOMEM; } + slot_ctx = xhci_get_slot_ctx(xhci, command->in_ctx); + trace_xhci_configure_endpoint(slot_ctx); + if (!ctx_change) ret = xhci_queue_configure_endpoint(xhci, command, command->in_ctx->dma, @@ -2773,6 +2787,7 @@ static void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) /* Free any rings allocated for added endpoints */ for (i = 0; i < 31; i++) { if (virt_dev->eps[i].new_ring) { + xhci_debugfs_remove_endpoint(xhci, virt_dev, i); xhci_ring_free(xhci, virt_dev->eps[i].new_ring); virt_dev->eps[i].new_ring = NULL; } @@ -3488,6 +3503,7 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, } if (ep->ring) { + xhci_debugfs_remove_endpoint(xhci, virt_dev, i); xhci_free_endpoint_ring(xhci, virt_dev, i); last_freed_endpoint = i; } @@ -3520,11 +3536,8 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_virt_device *virt_dev; struct xhci_slot_ctx *slot_ctx; int i, ret; - struct xhci_command *command; - command = xhci_alloc_command(xhci, false, false, GFP_KERNEL); - if (!command) - return; + xhci_debugfs_remove_slot(xhci, udev->slot_id); #ifndef CONFIG_USB_DEFAULT_PERSIST /* @@ -3540,10 +3553,8 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) /* If the host is halted due to driver unload, we still need to free the * device. */ - if (ret <= 0 && ret != -ENODEV) { - kfree(command); + if (ret <= 0 && ret != -ENODEV) return; - } virt_dev = xhci->devs[udev->slot_id]; slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx); @@ -3555,26 +3566,19 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); } - xhci_disable_slot(xhci, command, udev->slot_id); - /* - * Event command completion handler will free any data structures - * associated with the slot. XXX Can free sleep? - */ + ret = xhci_disable_slot(xhci, udev->slot_id); + if (ret) + xhci_free_virt_device(xhci, udev->slot_id); } -int xhci_disable_slot(struct xhci_hcd *xhci, struct xhci_command *command, - u32 slot_id) +int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id) { + struct xhci_command *command; unsigned long flags; u32 state; int ret = 0; - struct xhci_virt_device *virt_dev; - virt_dev = xhci->devs[slot_id]; - if (!virt_dev) - return -EINVAL; - if (!command) - command = xhci_alloc_command(xhci, false, false, GFP_KERNEL); + command = xhci_alloc_command(xhci, false, false, GFP_KERNEL); if (!command) return -ENOMEM; @@ -3583,17 +3587,16 @@ int xhci_disable_slot(struct xhci_hcd *xhci, struct xhci_command *command, state = readl(&xhci->op_regs->status); if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING) || (xhci->xhc_state & XHCI_STATE_HALTED)) { - xhci_free_virt_device(xhci, slot_id); spin_unlock_irqrestore(&xhci->lock, flags); kfree(command); - return ret; + return -ENODEV; } ret = xhci_queue_slot_control(xhci, command, TRB_DISABLE_SLOT, slot_id); if (ret) { spin_unlock_irqrestore(&xhci->lock, flags); - xhci_dbg(xhci, "FIXME: allocate a command ring segment\n"); + kfree(command); return ret; } xhci_ring_cmd_db(xhci); @@ -3641,13 +3644,10 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) if (!command) return 0; - /* xhci->slot_id and xhci->addr_dev are not thread-safe */ - mutex_lock(&xhci->mutex); spin_lock_irqsave(&xhci->lock, flags); ret = xhci_queue_slot_control(xhci, command, TRB_ENABLE_SLOT, 0); if (ret) { spin_unlock_irqrestore(&xhci->lock, flags); - mutex_unlock(&xhci->mutex); xhci_dbg(xhci, "FIXME: allocate a command ring segment\n"); xhci_free_command(xhci, command); return 0; @@ -3657,7 +3657,6 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) wait_for_completion(command->completion); slot_id = command->slot_id; - mutex_unlock(&xhci->mutex); if (!slot_id || command->status != COMP_SUCCESS) { xhci_err(xhci, "Error while assigning device slot ID\n"); @@ -3668,6 +3667,8 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) return 0; } + xhci_free_command(xhci, command); + if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK)) { spin_lock_irqsave(&xhci->lock, flags); ret = xhci_reserve_host_control_ep_resources(xhci); @@ -3694,6 +3695,8 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) udev->slot_id = slot_id; + xhci_debugfs_create_slot(xhci, slot_id); + #ifndef CONFIG_USB_DEFAULT_PERSIST /* * If resetting upon resume, we can't put the controller into runtime @@ -3703,18 +3706,16 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) pm_runtime_get_noresume(hcd->self.controller); #endif - - xhci_free_command(xhci, command); /* Is this a LS or FS device under a HS hub? */ /* Hub or peripherial? */ return 1; disable_slot: - /* Disable slot, if we can do it without mem alloc */ - kfree(command->completion); - command->completion = NULL; - command->status = 0; - return xhci_disable_slot(xhci, command, udev->slot_id); + ret = xhci_disable_slot(xhci, udev->slot_id); + if (ret) + xhci_free_virt_device(xhci, udev->slot_id); + + return 0; } /* @@ -3838,8 +3839,14 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, break; case COMP_USB_TRANSACTION_ERROR: dev_warn(&udev->dev, "Device not responding to setup %s.\n", act); - ret = -EPROTO; - break; + + mutex_unlock(&xhci->mutex); + ret = xhci_disable_slot(xhci, udev->slot_id); + if (!ret) + xhci_alloc_dev(hcd, udev); + kfree(command->completion); + kfree(command); + return -EPROTO; case COMP_INCOMPATIBLE_DEVICE_ERROR: dev_warn(&udev->dev, "ERROR: Incompatible device for setup %s command\n", act); @@ -4088,7 +4095,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", enable ? "enable" : "disable", port_num + 1); - if (enable) { + if (enable && !(xhci->quirks & XHCI_HW_LPM_DISABLE)) { /* Host supports BESL timeout instead of HIRD */ if (udev->usb2_hw_lpm_besl_capable) { /* if device doesn't have a preferred BESL value use a @@ -5004,6 +5011,8 @@ static int __init xhci_hcd_init(void) if (usb_disabled()) return -ENODEV; + xhci_debugfs_create_root(); + return 0; } @@ -5011,7 +5020,10 @@ static int __init xhci_hcd_init(void) * If an init function is provided, an exit function must also be provided * to allow module unload. */ -static void __exit xhci_hcd_fini(void) { } +static void __exit xhci_hcd_fini(void) +{ + xhci_debugfs_remove_root(); +} module_init(xhci_hcd_init); module_exit(xhci_hcd_fini); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 2b48aa4f6b76..b966cd878351 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -131,6 +131,8 @@ struct xhci_cap_regs { /* Extended Capabilities pointer from PCI base - section 5.3.6 */ #define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p) +#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32) + /* db_off bitmask - bits 0:1 reserved */ #define DBOFF_MASK (~0x3) @@ -1007,6 +1009,8 @@ struct xhci_virt_device { struct xhci_tt_bw_info *tt_info; /* The current max exit latency for the enabled USB3 link states. */ u16 current_mel; + /* Used for the debugfs interfaces. */ + void *debugfs_private; }; /* @@ -1830,6 +1834,7 @@ struct xhci_hcd { #define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26) /* Reserved. It was XHCI_U2_DISABLE_WAKE */ #define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28) +#define XHCI_HW_LPM_DISABLE (1 << 29) unsigned int num_active_eps; unsigned int limit_active_eps; @@ -1859,6 +1864,10 @@ struct xhci_hcd { /* Compliance Mode Timer Triggered every 2 seconds */ #define COMP_MODE_RCVRY_MSECS 2000 + struct dentry *debugfs_root; + struct dentry *debugfs_slots; + struct list_head regset_list; + /* platform-specific data -- must come last */ unsigned long priv[0] __aligned(sizeof(s64)); }; @@ -2012,8 +2021,7 @@ int xhci_run(struct usb_hcd *hcd); int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); void xhci_init_driver(struct hc_driver *drv, const struct xhci_driver_overrides *over); -int xhci_disable_slot(struct xhci_hcd *xhci, - struct xhci_command *command, u32 slot_id); +int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id); int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup); int xhci_resume(struct xhci_hcd *xhci, bool hibernated); @@ -2107,6 +2115,7 @@ struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, struct xhci_container struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id); + static inline struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci, struct urb *urb) { @@ -2442,11 +2451,12 @@ static inline const char *xhci_decode_portsc(u32 portsc) static char str[256]; int ret; - ret = sprintf(str, "%s %s %s Link:%s ", + ret = sprintf(str, "%s %s %s Link:%s PortSpeed:%d ", portsc & PORT_POWER ? "Powered" : "Powered-off", portsc & PORT_CONNECT ? "Connected" : "Not-connected", portsc & PORT_PE ? "Enabled" : "Disabled", - xhci_portsc_link_state_string(portsc)); + xhci_portsc_link_state_string(portsc), + DEV_PORT_SPEED(portsc)); if (portsc & PORT_OC) ret += sprintf(str + ret, "OverCurrent "); diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c index c6052c814bcc..8a07ce63b92d 100644 --- a/drivers/usb/phy/phy-isp1301-omap.c +++ b/drivers/usb/phy/phy-isp1301-omap.c @@ -1222,7 +1222,6 @@ static int isp1301_remove(struct i2c_client *i2c) if (machine_is_omap_h2()) gpio_free(2); - isp->timer.data = 0; set_bit(WORK_STOP, &isp->todo); del_timer_sync(&isp->timer); flush_work(&isp->work); @@ -1507,9 +1506,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) } INIT_WORK(&isp->work, isp1301_work); - init_timer(&isp->timer); - isp->timer.function = isp1301_timer; - isp->timer.data = (unsigned long) isp; + setup_timer(&isp->timer, isp1301_timer, (unsigned long)isp); i2c_set_clientdata(i2c, isp); isp->client = i2c; diff --git a/drivers/usb/storage/option_ms.c b/drivers/usb/storage/option_ms.c index 57282f12317b..4a73cd4783ae 100644 --- a/drivers/usb/storage/option_ms.c +++ b/drivers/usb/storage/option_ms.c @@ -41,7 +41,7 @@ MODULE_PARM_DESC(option_zero_cd, "ZeroCD mode (1=Force Modem (default)," static int option_rezero(struct us_data *us) { - const unsigned char rezero_msg[] = { + static const unsigned char rezero_msg[] = { 0x55, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -87,7 +87,7 @@ out: static int option_inquiry(struct us_data *us) { - const unsigned char inquiry_msg[] = { + static const unsigned char inquiry_msg[] = { 0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78, 0x24, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index bc1b7745f1d4..465d7da849c3 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -4,6 +4,18 @@ menu "USB Power Delivery and Type-C drivers" config TYPEC tristate +config TYPEC_TCPM + tristate "USB Type-C Port Controller Manager" + depends on USB + select TYPEC + help + The Type-C Port Controller Manager provides a USB PD and USB Type-C + state machine for use with Type-C Port Controllers. + +if TYPEC_TCPM + +source "drivers/usb/typec/fusb302/Kconfig" + config TYPEC_WCOVE tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver" depends on ACPI @@ -19,6 +31,19 @@ config TYPEC_WCOVE To compile this driver as module, choose M here: the module will be called typec_wcove +endif + source "drivers/usb/typec/ucsi/Kconfig" +config TYPEC_TPS6598X + tristate "TI TPS6598x USB Power Delivery controller driver" + depends on I2C + select TYPEC + help + Say Y or M here if your system has TI TPS65982 or TPS65983 USB Power + Delivery controller. + + If you choose to build this driver as a dynamically linked module, the + module will be called tps6598x.ko. + endmenu diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index bc214f15f1b5..6be5e161ac2b 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -1,3 +1,6 @@ obj-$(CONFIG_TYPEC) += typec.o +obj-$(CONFIG_TYPEC_TCPM) += tcpm.o +obj-y += fusb302/ obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o obj-$(CONFIG_TYPEC_UCSI) += ucsi/ +obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o diff --git a/drivers/staging/typec/fusb302/Kconfig b/drivers/usb/typec/fusb302/Kconfig index 48a4f2fcee03..48a4f2fcee03 100644 --- a/drivers/staging/typec/fusb302/Kconfig +++ b/drivers/usb/typec/fusb302/Kconfig diff --git a/drivers/staging/typec/fusb302/Makefile b/drivers/usb/typec/fusb302/Makefile index 207efa5fbab8..207efa5fbab8 100644 --- a/drivers/staging/typec/fusb302/Makefile +++ b/drivers/usb/typec/fusb302/Makefile diff --git a/drivers/staging/typec/fusb302/fusb302.c b/drivers/usb/typec/fusb302/fusb302.c index fc6a3cf74eb3..e790b67d4953 100644 --- a/drivers/staging/typec/fusb302/fusb302.c +++ b/drivers/usb/typec/fusb302/fusb302.c @@ -37,11 +37,11 @@ #include <linux/string.h> #include <linux/types.h> #include <linux/usb/typec.h> +#include <linux/usb/tcpm.h> +#include <linux/usb/pd.h> #include <linux/workqueue.h> #include "fusb302_reg.h" -#include "../tcpm.h" -#include "../pd.h" /* * When the device is SNK, BC_LVL interrupt is used to monitor cc pins diff --git a/drivers/staging/typec/fusb302/fusb302_reg.h b/drivers/usb/typec/fusb302/fusb302_reg.h index 0682e63de773..0682e63de773 100644 --- a/drivers/staging/typec/fusb302/fusb302_reg.h +++ b/drivers/usb/typec/fusb302/fusb302_reg.h diff --git a/drivers/staging/typec/tcpm.c b/drivers/usb/typec/tcpm.c index 8af62e74d54c..8483d3e33853 100644 --- a/drivers/staging/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -26,14 +26,13 @@ #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/usb/pd.h> +#include <linux/usb/pd_bdo.h> +#include <linux/usb/pd_vdo.h> +#include <linux/usb/tcpm.h> #include <linux/usb/typec.h> #include <linux/workqueue.h> -#include "pd.h" -#include "pd_vdo.h" -#include "pd_bdo.h" -#include "tcpm.h" - #define FOREACH_STATE(S) \ S(INVALID_STATE), \ S(DRP_TOGGLING), \ @@ -908,27 +907,6 @@ static void svdm_consume_identity(struct tcpm_port *port, const __le32 *payload, memset(&port->mode_data, 0, sizeof(port->mode_data)); -#if 0 /* Not really a match */ - switch (PD_IDH_PTYPE(vdo)) { - case IDH_PTYPE_UNDEF: - port->partner.type = TYPEC_PARTNER_NONE; /* no longer exists */ - break; - case IDH_PTYPE_HUB: - break; - case IDH_PTYPE_PERIPH: - break; - case IDH_PTYPE_PCABLE: - break; - case IDH_PTYPE_ACABLE: - break; - case IDH_PTYPE_AMA: - port->partner.type = TYPEC_PARTNER_ALTMODE; - break; - default: - break; - } -#endif - port->partner_ident.id_header = vdo; port->partner_ident.cert_stat = le32_to_cpu(payload[VDO_INDEX_CSTAT]); port->partner_ident.product = product; @@ -1008,7 +986,7 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload, } port->partner_altmode[pmdata->altmodes] = typec_partner_register_altmode(port->partner, paltmode); - if (port->partner_altmode[pmdata->altmodes] == NULL) { + if (!port->partner_altmode[pmdata->altmodes]) { tcpm_log(port, "Failed to register alternate modes for SVID 0x%04x", paltmode->svid); @@ -1103,11 +1081,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, response[0] = VDO(svid, 1, CMD_DISCOVER_MODES); rlen = 1; } else { -#if 0 - response[0] = pd_dfp_enter_mode(port, 0, 0); - if (response[0]) - rlen = 1; -#endif + /* enter alternate mode if/when implemented */ } break; case CMD_ENTER_MODE: @@ -1145,10 +1119,6 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port, if (PD_VDO_SVDM(p0)) rlen = tcpm_pd_svdm(port, payload, cnt, response); -#if 0 - else - rlen = tcpm_pd_custom_vdm(port, cnt, payload, response); -#endif if (rlen > 0) { tcpm_queue_vdm(port, response[0], &response[1], rlen - 1); @@ -2442,7 +2412,6 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SNK_STARTUP, 0); break; case SNK_STARTUP: - /* XXX: callback into infrastructure */ opmode = tcpm_get_pwr_opmode(port->polarity ? port->cc2 : port->cc1); typec_set_pwr_opmode(port->typec_port, opmode); @@ -3589,11 +3558,6 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->partner_desc.identity = &port->partner_ident; port->port_type = tcpc->config->type; - /* - * TODO: - * - alt_modes, set_alt_mode - * - {debug,audio}_accessory - */ port->typec_port = typec_register_port(port->dev, &port->typec_caps); if (!port->typec_port) { diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c new file mode 100644 index 000000000000..8893f7937560 --- /dev/null +++ b/drivers/usb/typec/tps6598x.c @@ -0,0 +1,475 @@ +/* + * Driver for TI TPS6598x USB Power Delivery controller family + * + * Copyright (C) 2017, Intel Corporation + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/i2c.h> +#include <linux/acpi.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/interrupt.h> +#include <linux/usb/typec.h> + +/* Register offsets */ +#define TPS_REG_CMD1 0x08 +#define TPS_REG_DATA1 0x09 +#define TPS_REG_INT_EVENT1 0x14 +#define TPS_REG_INT_EVENT2 0x15 +#define TPS_REG_INT_MASK1 0x16 +#define TPS_REG_INT_MASK2 0x17 +#define TPS_REG_INT_CLEAR1 0x18 +#define TPS_REG_INT_CLEAR2 0x19 +#define TPS_REG_STATUS 0x1a +#define TPS_REG_SYSTEM_CONF 0x28 +#define TPS_REG_CTRL_CONF 0x29 +#define TPS_REG_POWER_STATUS 0x3f +#define TPS_REG_RX_IDENTITY_SOP 0x48 + +/* TPS_REG_INT_* bits */ +#define TPS_REG_INT_PLUG_EVENT BIT(3) + +/* TPS_REG_STATUS bits */ +#define TPS_STATUS_PLUG_PRESENT BIT(0) +#define TPS_STATUS_ORIENTATION BIT(4) +#define TPS_STATUS_PORTROLE(s) (!!((s) & BIT(5))) +#define TPS_STATUS_DATAROLE(s) (!!((s) & BIT(6))) +#define TPS_STATUS_VCONN(s) (!!((s) & BIT(7))) + +/* TPS_REG_SYSTEM_CONF bits */ +#define TPS_SYSCONF_PORTINFO(c) ((c) & 3) + +enum { + TPS_PORTINFO_SINK, + TPS_PORTINFO_SINK_ACCESSORY, + TPS_PORTINFO_DRP_UFP, + TPS_PORTINFO_DRP_UFP_DRD, + TPS_PORTINFO_DRP_DFP, + TPS_PORTINFO_DRP_DFP_DRD, + TPS_PORTINFO_SOURCE, +}; + +/* TPS_REG_POWER_STATUS bits */ +#define TPS_POWER_STATUS_SOURCESINK BIT(1) +#define TPS_POWER_STATUS_PWROPMODE(p) (((p) & GENMASK(3, 2)) >> 2) + +/* TPS_REG_RX_IDENTITY_SOP */ +struct tps6598x_rx_identity_reg { + u8 status; + struct usb_pd_identity identity; + u32 vdo[3]; +} __packed; + +/* Standard Task return codes */ +#define TPS_TASK_TIMEOUT 1 +#define TPS_TASK_REJECTED 3 + +/* Unrecognized commands will be replaced with "!CMD" */ +#define INVALID_CMD(_cmd_) (_cmd_ == 0x444d4321) + +struct tps6598x { + struct device *dev; + struct regmap *regmap; + struct mutex lock; /* device lock */ + + struct typec_port *port; + struct typec_partner *partner; + struct usb_pd_identity partner_identity; + struct typec_capability typec_cap; +}; + +static inline int tps6598x_read16(struct tps6598x *tps, u8 reg, u16 *val) +{ + return regmap_raw_read(tps->regmap, reg, val, sizeof(u16)); +} + +static inline int tps6598x_read32(struct tps6598x *tps, u8 reg, u32 *val) +{ + return regmap_raw_read(tps->regmap, reg, val, sizeof(u32)); +} + +static inline int tps6598x_read64(struct tps6598x *tps, u8 reg, u64 *val) +{ + return regmap_raw_read(tps->regmap, reg, val, sizeof(u64)); +} + +static inline int tps6598x_write16(struct tps6598x *tps, u8 reg, u16 val) +{ + return regmap_raw_write(tps->regmap, reg, &val, sizeof(u16)); +} + +static inline int tps6598x_write32(struct tps6598x *tps, u8 reg, u32 val) +{ + return regmap_raw_write(tps->regmap, reg, &val, sizeof(u32)); +} + +static inline int tps6598x_write64(struct tps6598x *tps, u8 reg, u64 val) +{ + return regmap_raw_write(tps->regmap, reg, &val, sizeof(u64)); +} + +static inline int +tps6598x_write_4cc(struct tps6598x *tps, u8 reg, const char *val) +{ + return regmap_raw_write(tps->regmap, reg, &val, sizeof(u32)); +} + +static int tps6598x_read_partner_identity(struct tps6598x *tps) +{ + struct tps6598x_rx_identity_reg id; + int ret; + + ret = regmap_raw_read(tps->regmap, TPS_REG_RX_IDENTITY_SOP, + &id, sizeof(id)); + if (ret) + return ret; + + tps->partner_identity = id.identity; + + return 0; +} + +static int tps6598x_connect(struct tps6598x *tps, u32 status) +{ + struct typec_partner_desc desc; + enum typec_pwr_opmode mode; + u16 pwr_status; + int ret; + + if (tps->partner) + return 0; + + ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status); + if (ret < 0) + return ret; + + mode = TPS_POWER_STATUS_PWROPMODE(pwr_status); + + desc.usb_pd = mode == TYPEC_PWR_MODE_PD; + desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */ + desc.identity = NULL; + + if (desc.usb_pd) { + ret = tps6598x_read_partner_identity(tps); + if (ret) + return ret; + desc.identity = &tps->partner_identity; + } + + tps->partner = typec_register_partner(tps->port, &desc); + if (!tps->partner) + return -ENODEV; + + typec_set_pwr_opmode(tps->port, mode); + typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status)); + typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status)); + typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status)); + + if (desc.identity) + typec_partner_set_identity(tps->partner); + + return 0; +} + +static void tps6598x_disconnect(struct tps6598x *tps, u32 status) +{ + typec_unregister_partner(tps->partner); + tps->partner = NULL; + typec_set_pwr_opmode(tps->port, TYPEC_PWR_MODE_USB); + typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status)); + typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status)); + typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status)); +} + +static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd, + size_t in_len, u8 *in_data, + size_t out_len, u8 *out_data) +{ + unsigned long timeout; + u32 val; + int ret; + + ret = tps6598x_read32(tps, TPS_REG_CMD1, &val); + if (ret) + return ret; + if (val && !INVALID_CMD(val)) + return -EBUSY; + + if (in_len) { + ret = regmap_raw_write(tps->regmap, TPS_REG_DATA1, + in_data, in_len); + if (ret) + return ret; + } + + ret = tps6598x_write_4cc(tps, TPS_REG_CMD1, cmd); + if (ret < 0) + return ret; + + /* XXX: Using 1s for now, but it may not be enough for every command. */ + timeout = jiffies + msecs_to_jiffies(1000); + + do { + ret = tps6598x_read32(tps, TPS_REG_CMD1, &val); + if (ret) + return ret; + if (INVALID_CMD(val)) + return -EINVAL; + + if (time_is_before_jiffies(timeout)) + return -ETIMEDOUT; + } while (val); + + if (out_len) { + ret = regmap_raw_read(tps->regmap, TPS_REG_DATA1, + out_data, out_len); + if (ret) + return ret; + val = out_data[0]; + } else { + ret = regmap_read(tps->regmap, TPS_REG_DATA1, &val); + if (ret) + return ret; + } + + switch (val) { + case TPS_TASK_TIMEOUT: + return -ETIMEDOUT; + case TPS_TASK_REJECTED: + return -EPERM; + default: + break; + } + + return 0; +} + +static int +tps6598x_dr_set(const struct typec_capability *cap, enum typec_data_role role) +{ + struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap); + const char *cmd = (role == TYPEC_DEVICE) ? "SWUF" : "SWDF"; + u32 status; + int ret; + + mutex_lock(&tps->lock); + + ret = tps6598x_exec_cmd(tps, cmd, 0, NULL, 0, NULL); + if (ret) + goto out_unlock; + + ret = tps6598x_read32(tps, TPS_REG_STATUS, &status); + if (ret) + goto out_unlock; + + if (role != TPS_STATUS_DATAROLE(status)) { + ret = -EPROTO; + goto out_unlock; + } + + typec_set_data_role(tps->port, role); + +out_unlock: + mutex_unlock(&tps->lock); + + return ret; +} + +static int +tps6598x_pr_set(const struct typec_capability *cap, enum typec_role role) +{ + struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap); + const char *cmd = (role == TYPEC_SINK) ? "SWSk" : "SWSr"; + u32 status; + int ret; + + mutex_lock(&tps->lock); + + ret = tps6598x_exec_cmd(tps, cmd, 0, NULL, 0, NULL); + if (ret) + goto out_unlock; + + ret = tps6598x_read32(tps, TPS_REG_STATUS, &status); + if (ret) + goto out_unlock; + + if (role != TPS_STATUS_PORTROLE(status)) { + ret = -EPROTO; + goto out_unlock; + } + + typec_set_pwr_role(tps->port, role); + +out_unlock: + mutex_unlock(&tps->lock); + + return ret; +} + +static irqreturn_t tps6598x_interrupt(int irq, void *data) +{ + struct tps6598x *tps = data; + u64 event1; + u64 event2; + u32 status; + int ret; + + mutex_lock(&tps->lock); + + ret = tps6598x_read64(tps, TPS_REG_INT_EVENT1, &event1); + ret |= tps6598x_read64(tps, TPS_REG_INT_EVENT2, &event2); + if (ret) { + dev_err(tps->dev, "%s: failed to read events\n", __func__); + goto err_unlock; + } + + ret = tps6598x_read32(tps, TPS_REG_STATUS, &status); + if (ret) { + dev_err(tps->dev, "%s: failed to read status\n", __func__); + goto err_clear_ints; + } + + /* Handle plug insert or removal */ + if ((event1 | event2) & TPS_REG_INT_PLUG_EVENT) { + if (status & TPS_STATUS_PLUG_PRESENT) { + ret = tps6598x_connect(tps, status); + if (ret) + dev_err(tps->dev, + "failed to register partner\n"); + } else { + tps6598x_disconnect(tps, status); + } + } + +err_clear_ints: + tps6598x_write64(tps, TPS_REG_INT_CLEAR1, event1); + tps6598x_write64(tps, TPS_REG_INT_CLEAR2, event2); + +err_unlock: + mutex_unlock(&tps->lock); + + return IRQ_HANDLED; +} + +static const struct regmap_config tps6598x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x7F, +}; + +static int tps6598x_probe(struct i2c_client *client) +{ + struct tps6598x *tps; + u32 status; + u32 conf; + u32 vid; + int ret; + + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + mutex_init(&tps->lock); + tps->dev = &client->dev; + + tps->regmap = devm_regmap_init_i2c(client, &tps6598x_regmap_config); + if (IS_ERR(tps->regmap)) + return PTR_ERR(tps->regmap); + + ret = tps6598x_read32(tps, 0, &vid); + if (ret < 0) + return ret; + if (!vid) + return -ENODEV; + + ret = tps6598x_read32(tps, TPS_REG_STATUS, &status); + if (ret < 0) + return ret; + + ret = tps6598x_read32(tps, TPS_REG_SYSTEM_CONF, &conf); + if (ret < 0) + return ret; + + switch (TPS_SYSCONF_PORTINFO(conf)) { + case TPS_PORTINFO_SINK_ACCESSORY: + case TPS_PORTINFO_SINK: + tps->typec_cap.type = TYPEC_PORT_UFP; + break; + case TPS_PORTINFO_DRP_UFP_DRD: + case TPS_PORTINFO_DRP_DFP_DRD: + tps->typec_cap.dr_set = tps6598x_dr_set; + case TPS_PORTINFO_DRP_UFP: + case TPS_PORTINFO_DRP_DFP: + tps->typec_cap.pr_set = tps6598x_pr_set; + tps->typec_cap.type = TYPEC_PORT_DRP; + break; + case TPS_PORTINFO_SOURCE: + tps->typec_cap.type = TYPEC_PORT_DFP; + break; + default: + return -ENODEV; + } + + tps->typec_cap.revision = USB_TYPEC_REV_1_2; + tps->typec_cap.pd_revision = 0x200; + tps->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; + + tps->port = typec_register_port(&client->dev, &tps->typec_cap); + if (!tps->port) + return -ENODEV; + + if (status & TPS_STATUS_PLUG_PRESENT) { + ret = tps6598x_connect(tps, status); + if (ret) + dev_err(&client->dev, "failed to register partner\n"); + } + + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, + tps6598x_interrupt, + IRQF_SHARED | IRQF_ONESHOT, + dev_name(&client->dev), tps); + if (ret) { + tps6598x_disconnect(tps, 0); + typec_unregister_port(tps->port); + return ret; + } + + i2c_set_clientdata(client, tps); + + return 0; +} + +static int tps6598x_remove(struct i2c_client *client) +{ + struct tps6598x *tps = i2c_get_clientdata(client); + + tps6598x_disconnect(tps, 0); + typec_unregister_port(tps->port); + + return 0; +} + +static const struct acpi_device_id tps6598x_acpi_match[] = { + { "INT3515", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, tps6598x_acpi_match); + +static struct i2c_driver tps6598x_i2c_driver = { + .driver = { + .name = "tps6598x", + .acpi_match_table = tps6598x_acpi_match, + }, + .probe_new = tps6598x_probe, + .remove = tps6598x_remove, +}; +module_i2c_driver(tps6598x_i2c_driver); + +MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI TPS6598x USB Power Delivery Controller Driver"); diff --git a/drivers/usb/typec/typec_wcove.c b/drivers/usb/typec/typec_wcove.c index e9c4e784a9cb..a47a93ad34b7 100644 --- a/drivers/usb/typec/typec_wcove.c +++ b/drivers/usb/typec/typec_wcove.c @@ -11,14 +11,13 @@ #include <linux/acpi.h> #include <linux/module.h> +#include <linux/usb/tcpm.h> #include <linux/interrupt.h> -#include <linux/usb/typec.h> #include <linux/platform_device.h> #include <linux/mfd/intel_soc_pmic.h> /* Register offsets */ #define WCOVE_CHGRIRQ0 0x4e09 -#define WCOVE_PHYCTRL 0x5e07 #define USBC_CONTROL1 0x7001 #define USBC_CONTROL2 0x7002 @@ -28,22 +27,57 @@ #define USBC_STATUS1 0x7007 #define USBC_STATUS2 0x7008 #define USBC_STATUS3 0x7009 +#define USBC_CC1 0x700a +#define USBC_CC2 0x700b +#define USBC_CC1_STATUS 0x700c +#define USBC_CC2_STATUS 0x700d #define USBC_IRQ1 0x7015 #define USBC_IRQ2 0x7016 #define USBC_IRQMASK1 0x7017 #define USBC_IRQMASK2 0x7018 +#define USBC_PDCFG2 0x701a +#define USBC_PDCFG3 0x701b +#define USBC_PDSTATUS 0x701c +#define USBC_RXSTATUS 0x701d +#define USBC_RXINFO 0x701e +#define USBC_TXCMD 0x701f +#define USBC_TXINFO 0x7020 +#define USBC_RX_DATA 0x7028 +#define USBC_TX_DATA 0x7047 /* Register bits */ -#define USBC_CONTROL1_MODE_DRP(r) (((r) & ~0x7) | 4) +#define USBC_CONTROL1_MODE_MASK 0x3 +#define USBC_CONTROL1_MODE_SNK 0 +#define USBC_CONTROL1_MODE_SNKACC 1 +#define USBC_CONTROL1_MODE_SRC 2 +#define USBC_CONTROL1_MODE_SRCACC 3 +#define USBC_CONTROL1_MODE_DRP 4 +#define USBC_CONTROL1_MODE_DRPACC 5 +#define USBC_CONTROL1_MODE_TEST 7 +#define USBC_CONTROL1_CURSRC_MASK 0xc +#define USBC_CONTROL1_CURSRC_UA_0 (0 << 3) +#define USBC_CONTROL1_CURSRC_UA_80 (1 << 3) +#define USBC_CONTROL1_CURSRC_UA_180 (2 << 3) +#define USBC_CONTROL1_CURSRC_UA_330 (3 << 3) +#define USBC_CONTROL1_DRPTOGGLE_RANDOM 0xe0 #define USBC_CONTROL2_UNATT_SNK BIT(0) #define USBC_CONTROL2_UNATT_SRC BIT(1) #define USBC_CONTROL2_DIS_ST BIT(2) +#define USBC_CONTROL3_DET_DIS BIT(0) #define USBC_CONTROL3_PD_DIS BIT(1) +#define USBC_CONTROL3_RESETPHY BIT(2) +#define USBC_CC_CTRL_PU_EN BIT(0) #define USBC_CC_CTRL_VCONN_EN BIT(1) +#define USBC_CC_CTRL_TX_EN BIT(2) +#define USBC_CC_CTRL_PD_EN BIT(3) +#define USBC_CC_CTRL_CDET_EN BIT(4) +#define USBC_CC_CTRL_RDET_EN BIT(5) +#define USBC_CC_CTRL_ADC_EN BIT(6) +#define USBC_CC_CTRL_VBUSOK BIT(7) #define USBC_STATUS1_DET_ONGOING BIT(6) #define USBC_STATUS1_RSLT(r) ((r) & 0xf) @@ -61,6 +95,15 @@ #define USBC_STATUS2_VBUS_REQ BIT(5) +#define UCSC_CC_STATUS_SNK_RP BIT(0) +#define UCSC_CC_STATUS_PWRDEFSNK BIT(1) +#define UCSC_CC_STATUS_PWR_1P5A_SNK BIT(2) +#define UCSC_CC_STATUS_PWR_3A_SNK BIT(3) +#define UCSC_CC_STATUS_SRC_RP BIT(4) +#define UCSC_CC_STATUS_RX(r) (((r) >> 5) & 0x3) +#define USBC_CC_STATUS_RD 1 +#define USBC_CC_STATUS_RA 2 + #define USBC_IRQ1_ADCDONE1 BIT(2) #define USBC_IRQ1_OVERTEMP BIT(1) #define USBC_IRQ1_SHORT BIT(0) @@ -79,15 +122,44 @@ USBC_IRQ2_RX_HR | USBC_IRQ2_RX_CR | \ USBC_IRQ2_TX_SUCCESS | USBC_IRQ2_TX_FAIL) +#define USBC_PDCFG2_SOP BIT(0) +#define USBC_PDCFG2_SOP_P BIT(1) +#define USBC_PDCFG2_SOP_PP BIT(2) +#define USBC_PDCFG2_SOP_P_DEBUG BIT(3) +#define USBC_PDCFG2_SOP_PP_DEBUG BIT(4) + +#define USBC_PDCFG3_DATAROLE_SHIFT 1 +#define USBC_PDCFG3_SOP_SHIFT 2 + +#define USBC_RXSTATUS_RXCLEAR BIT(0) +#define USBC_RXSTATUS_RXDATA BIT(7) + +#define USBC_RXINFO_RXBYTES(i) (((i) >> 3) & 0x1f) + +#define USBC_TXCMD_BUF_RDY BIT(0) +#define USBC_TXCMD_START BIT(1) +#define USBC_TXCMD_NOP (0 << 5) +#define USBC_TXCMD_MSG (1 << 5) +#define USBC_TXCMD_CR (2 << 5) +#define USBC_TXCMD_HR (3 << 5) +#define USBC_TXCMD_BIST (4 << 5) + +#define USBC_TXINFO_RETRIES(d) (d << 3) + struct wcove_typec { struct mutex lock; /* device lock */ struct device *dev; struct regmap *regmap; - struct typec_port *port; - struct typec_capability cap; - struct typec_partner *partner; + guid_t guid; + + bool vbus; + + struct tcpc_dev tcpc; + struct tcpm_port *tcpm; }; +#define tcpc_to_wcove(_tcpc_) container_of(_tcpc_, struct wcove_typec, tcpc) + enum wcove_typec_func { WCOVE_FUNC_DRIVE_VBUS = 1, WCOVE_FUNC_ORIENTATION, @@ -105,8 +177,7 @@ enum wcove_typec_role { WCOVE_ROLE_DEVICE, }; -static guid_t guid = GUID_INIT(0x482383f0, 0x2876, 0x4e49, - 0x86, 0x85, 0xdb, 0x66, 0x21, 0x1a, 0xf0, 0x37); +#define WCOVE_DSM_UUID "482383f0-2876-4e49-8685-db66211af037" static int wcove_typec_func(struct wcove_typec *wcove, enum wcove_typec_func func, int param) @@ -118,7 +189,7 @@ static int wcove_typec_func(struct wcove_typec *wcove, tmp.type = ACPI_TYPE_INTEGER; tmp.integer.value = param; - obj = acpi_evaluate_dsm(ACPI_HANDLE(wcove->dev), &guid, 1, func, + obj = acpi_evaluate_dsm(ACPI_HANDLE(wcove->dev), &wcove->guid, 1, func, &argv4); if (!obj) { dev_err(wcove->dev, "%s: failed to evaluate _DSM\n", __func__); @@ -129,158 +200,349 @@ static int wcove_typec_func(struct wcove_typec *wcove, return 0; } -static irqreturn_t wcove_typec_irq(int irq, void *data) +static int wcove_init(struct tcpc_dev *tcpc) { - enum typec_role role = TYPEC_SINK; - struct typec_partner_desc partner; - struct wcove_typec *wcove = data; - unsigned int cc1_ctrl; - unsigned int cc2_ctrl; - unsigned int cc_irq1; - unsigned int cc_irq2; - unsigned int status1; - unsigned int status2; + struct wcove_typec *wcove = tcpc_to_wcove(tcpc); int ret; - mutex_lock(&wcove->lock); - - ret = regmap_read(wcove->regmap, USBC_IRQ1, &cc_irq1); + /* Unmask everything */ + ret = regmap_write(wcove->regmap, USBC_IRQMASK1, 0); if (ret) - goto err; + return ret; - ret = regmap_read(wcove->regmap, USBC_IRQ2, &cc_irq2); - if (ret) - goto err; + return regmap_write(wcove->regmap, USBC_IRQMASK2, 0); +} - ret = regmap_read(wcove->regmap, USBC_STATUS1, &status1); - if (ret) - goto err; +static int wcove_get_vbus(struct tcpc_dev *tcpc) +{ + struct wcove_typec *wcove = tcpc_to_wcove(tcpc); + unsigned int cc1ctrl; + int ret; - ret = regmap_read(wcove->regmap, USBC_STATUS2, &status2); + ret = regmap_read(wcove->regmap, USBC_CC1_CTRL, &cc1ctrl); if (ret) - goto err; + return ret; - ret = regmap_read(wcove->regmap, USBC_CC1_CTRL, &cc1_ctrl); - if (ret) - goto err; + wcove->vbus = !!(cc1ctrl & USBC_CC_CTRL_VBUSOK); - ret = regmap_read(wcove->regmap, USBC_CC2_CTRL, &cc2_ctrl); - if (ret) - goto err; + return wcove->vbus; +} - if (cc_irq1) { - if (cc_irq1 & USBC_IRQ1_OVERTEMP) - dev_err(wcove->dev, "VCONN Switch Over Temperature!\n"); - if (cc_irq1 & USBC_IRQ1_SHORT) - dev_err(wcove->dev, "VCONN Switch Short Circuit!\n"); - ret = regmap_write(wcove->regmap, USBC_IRQ1, cc_irq1); - if (ret) - goto err; - } +static int wcove_set_vbus(struct tcpc_dev *tcpc, bool on, bool sink) +{ + struct wcove_typec *wcove = tcpc_to_wcove(tcpc); - if (cc_irq2) { - ret = regmap_write(wcove->regmap, USBC_IRQ2, cc_irq2); - if (ret) - goto err; - /* - * Ignoring any PD communication interrupts until the PD support - * is available - */ - if (cc_irq2 & ~USBC_IRQ2_CC_CHANGE) { - dev_WARN(wcove->dev, "USB PD handling missing\n"); - goto err; + return wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VBUS, on); +} + +static int wcove_set_vconn(struct tcpc_dev *tcpc, bool on) +{ + struct wcove_typec *wcove = tcpc_to_wcove(tcpc); + + return wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VCONN, on); +} + +static enum typec_cc_status wcove_to_typec_cc(unsigned int cc) +{ + if (cc & UCSC_CC_STATUS_SNK_RP) { + if (cc & UCSC_CC_STATUS_PWRDEFSNK) + return TYPEC_CC_RP_DEF; + else if (cc & UCSC_CC_STATUS_PWR_1P5A_SNK) + return TYPEC_CC_RP_1_5; + else if (cc & UCSC_CC_STATUS_PWR_3A_SNK) + return TYPEC_CC_RP_3_0; + } else { + switch (UCSC_CC_STATUS_RX(cc)) { + case USBC_CC_STATUS_RD: + return TYPEC_CC_RD; + case USBC_CC_STATUS_RA: + return TYPEC_CC_RA; + default: + break; } } + return TYPEC_CC_OPEN; +} - if (status1 & USBC_STATUS1_DET_ONGOING) - goto out; +static int wcove_get_cc(struct tcpc_dev *tcpc, enum typec_cc_status *cc1, + enum typec_cc_status *cc2) +{ + struct wcove_typec *wcove = tcpc_to_wcove(tcpc); + unsigned int cc1_status; + unsigned int cc2_status; + int ret; - if (USBC_STATUS1_RSLT(status1) == USBC_RSLT_NOTHING) { - if (wcove->partner) { - typec_unregister_partner(wcove->partner); - wcove->partner = NULL; - } + ret = regmap_read(wcove->regmap, USBC_CC1_STATUS, &cc1_status); + if (ret) + return ret; - wcove_typec_func(wcove, WCOVE_FUNC_ORIENTATION, - WCOVE_ORIENTATION_NORMAL); + ret = regmap_read(wcove->regmap, USBC_CC2_STATUS, &cc2_status); + if (ret) + return ret; - /* This makes sure the device controller is disconnected */ - wcove_typec_func(wcove, WCOVE_FUNC_ROLE, WCOVE_ROLE_HOST); + *cc1 = wcove_to_typec_cc(cc1_status); + *cc2 = wcove_to_typec_cc(cc2_status); - /* Port to default role */ - typec_set_data_role(wcove->port, TYPEC_DEVICE); - typec_set_pwr_role(wcove->port, TYPEC_SINK); - typec_set_pwr_opmode(wcove->port, TYPEC_PWR_MODE_USB); + return 0; +} - goto out; - } +static int wcove_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc) +{ + /* XXX: Relying on the HW FSM to configure things correctly for now */ + return 0; +} - if (wcove->partner) - goto out; +static int wcove_set_polarity(struct tcpc_dev *tcpc, enum typec_cc_polarity pol) +{ + struct wcove_typec *wcove = tcpc_to_wcove(tcpc); - switch (USBC_STATUS1_ORIENT(status1)) { - case USBC_ORIENT_NORMAL: - wcove_typec_func(wcove, WCOVE_FUNC_ORIENTATION, - WCOVE_ORIENTATION_NORMAL); - break; - case USBC_ORIENT_REVERSE: - wcove_typec_func(wcove, WCOVE_FUNC_ORIENTATION, - WCOVE_ORIENTATION_REVERSE); - default: - break; + return wcove_typec_func(wcove, WCOVE_FUNC_ORIENTATION, pol); +} + +static int wcove_set_current_limit(struct tcpc_dev *tcpc, u32 max_ma, u32 mv) +{ + return 0; +} + +static int wcove_set_roles(struct tcpc_dev *tcpc, bool attached, + enum typec_role role, enum typec_data_role data) +{ + struct wcove_typec *wcove = tcpc_to_wcove(tcpc); + unsigned int val; + int ret; + + ret = wcove_typec_func(wcove, WCOVE_FUNC_ROLE, data == TYPEC_HOST ? + WCOVE_ROLE_HOST : WCOVE_ROLE_DEVICE); + if (ret) + return ret; + + val = role; + val |= data << USBC_PDCFG3_DATAROLE_SHIFT; + val |= PD_REV20 << USBC_PDCFG3_SOP_SHIFT; + + return regmap_write(wcove->regmap, USBC_PDCFG3, val); +} + +static int wcove_set_pd_rx(struct tcpc_dev *tcpc, bool on) +{ + struct wcove_typec *wcove = tcpc_to_wcove(tcpc); + + return regmap_write(wcove->regmap, USBC_PDCFG2, + on ? USBC_PDCFG2_SOP : 0); +} + +static int wcove_pd_transmit(struct tcpc_dev *tcpc, + enum tcpm_transmit_type type, + const struct pd_message *msg) +{ + struct wcove_typec *wcove = tcpc_to_wcove(tcpc); + unsigned int info = 0; + unsigned int cmd; + int ret; + + ret = regmap_read(wcove->regmap, USBC_TXCMD, &cmd); + if (ret) + return ret; + + if (!(cmd & USBC_TXCMD_BUF_RDY)) { + dev_warn(wcove->dev, "%s: Last transmission still ongoing!", + __func__); + return -EBUSY; } - memset(&partner, 0, sizeof(partner)); + if (msg) { + const u8 *data = (void *)msg; + int i; + + for (i = 0; i < pd_header_cnt(msg->header) * 4 + 2; i++) { + ret = regmap_write(wcove->regmap, USBC_TX_DATA + i, + data[i]); + if (ret) + return ret; + } + } - switch (USBC_STATUS1_RSLT(status1)) { - case USBC_RSLT_SRC_DEFAULT: - typec_set_pwr_opmode(wcove->port, TYPEC_PWR_MODE_USB); + switch (type) { + case TCPC_TX_SOP: + case TCPC_TX_SOP_PRIME: + case TCPC_TX_SOP_PRIME_PRIME: + case TCPC_TX_SOP_DEBUG_PRIME: + case TCPC_TX_SOP_DEBUG_PRIME_PRIME: + info = type + 1; + cmd = USBC_TXCMD_MSG; break; - case USBC_RSLT_SRC_1_5A: - typec_set_pwr_opmode(wcove->port, TYPEC_PWR_MODE_1_5A); + case TCPC_TX_HARD_RESET: + cmd = USBC_TXCMD_HR; break; - case USBC_RSLT_SRC_3_0A: - typec_set_pwr_opmode(wcove->port, TYPEC_PWR_MODE_3_0A); + case TCPC_TX_CABLE_RESET: + cmd = USBC_TXCMD_CR; break; - case USBC_RSLT_SNK: - role = TYPEC_SOURCE; + case TCPC_TX_BIST_MODE_2: + cmd = USBC_TXCMD_BIST; break; - case USBC_RSLT_DEBUG_ACC: - partner.accessory = TYPEC_ACCESSORY_DEBUG; + default: + return -EINVAL; + } + + /* NOTE Setting maximum number of retries (7) */ + ret = regmap_write(wcove->regmap, USBC_TXINFO, + info | USBC_TXINFO_RETRIES(7)); + if (ret) + return ret; + + return regmap_write(wcove->regmap, USBC_TXCMD, cmd | USBC_TXCMD_START); +} + +static int wcove_start_drp_toggling(struct tcpc_dev *tcpc, + enum typec_cc_status cc) +{ + struct wcove_typec *wcove = tcpc_to_wcove(tcpc); + unsigned int usbc_ctrl; + + usbc_ctrl = USBC_CONTROL1_MODE_DRP | USBC_CONTROL1_DRPTOGGLE_RANDOM; + + switch (cc) { + case TYPEC_CC_RP_1_5: + usbc_ctrl |= USBC_CONTROL1_CURSRC_UA_180; break; - case USBC_RSLT_AUDIO_ACC: - partner.accessory = TYPEC_ACCESSORY_AUDIO; + case TYPEC_CC_RP_3_0: + usbc_ctrl |= USBC_CONTROL1_CURSRC_UA_330; break; default: - dev_WARN(wcove->dev, "%s Undefined result\n", __func__); - goto err; + usbc_ctrl |= USBC_CONTROL1_CURSRC_UA_80; + break; } - if (role == TYPEC_SINK) { - wcove_typec_func(wcove, WCOVE_FUNC_ROLE, WCOVE_ROLE_DEVICE); - typec_set_data_role(wcove->port, TYPEC_DEVICE); - typec_set_pwr_role(wcove->port, TYPEC_SINK); - } else { - wcove_typec_func(wcove, WCOVE_FUNC_ROLE, WCOVE_ROLE_HOST); - typec_set_pwr_role(wcove->port, TYPEC_SOURCE); - typec_set_data_role(wcove->port, TYPEC_HOST); + return regmap_write(wcove->regmap, USBC_CONTROL1, usbc_ctrl); +} + +static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg) +{ + unsigned int info; + int ret; + int i; + + ret = regmap_read(wcove->regmap, USBC_RXINFO, &info); + if (ret) + return ret; + + /* FIXME: Check that USBC_RXINFO_RXBYTES(info) matches the header */ + + for (i = 0; i < USBC_RXINFO_RXBYTES(info); i++) { + ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, msg + i); + if (ret) + return ret; } - wcove->partner = typec_register_partner(wcove->port, &partner); - if (!wcove->partner) - dev_err(wcove->dev, "failed register partner\n"); -out: - /* If either CC pins is requesting VCONN, we turn it on */ - if ((cc1_ctrl & USBC_CC_CTRL_VCONN_EN) || - (cc2_ctrl & USBC_CC_CTRL_VCONN_EN)) - wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VCONN, true); - else + return regmap_write(wcove->regmap, USBC_RXSTATUS, + USBC_RXSTATUS_RXCLEAR); +} + +static irqreturn_t wcove_typec_irq(int irq, void *data) +{ + struct wcove_typec *wcove = data; + unsigned int usbc_irq1; + unsigned int usbc_irq2; + unsigned int cc1ctrl; + int ret; + + mutex_lock(&wcove->lock); + + /* Read.. */ + ret = regmap_read(wcove->regmap, USBC_IRQ1, &usbc_irq1); + if (ret) + goto err; + + ret = regmap_read(wcove->regmap, USBC_IRQ2, &usbc_irq2); + if (ret) + goto err; + + ret = regmap_read(wcove->regmap, USBC_CC1_CTRL, &cc1ctrl); + if (ret) + goto err; + + if (!wcove->tcpm) + goto err; + + /* ..check.. */ + if (usbc_irq1 & USBC_IRQ1_OVERTEMP) { + dev_err(wcove->dev, "VCONN Switch Over Temperature!\n"); + wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VCONN, false); + /* REVISIT: Report an error? */ + } + + if (usbc_irq1 & USBC_IRQ1_SHORT) { + dev_err(wcove->dev, "VCONN Switch Short Circuit!\n"); wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VCONN, false); + /* REVISIT: Report an error? */ + } + + if (wcove->vbus != !!(cc1ctrl & USBC_CC_CTRL_VBUSOK)) + tcpm_vbus_change(wcove->tcpm); + + /* REVISIT: See if tcpm code can be made to consider Type-C HW FSMs */ + if (usbc_irq2 & USBC_IRQ2_CC_CHANGE) + tcpm_cc_change(wcove->tcpm); + + if (usbc_irq2 & USBC_IRQ2_RX_PD) { + unsigned int status; + + /* + * FIXME: Need to check if TX is ongoing and report + * TX_DIREGARDED if needed? + */ + + ret = regmap_read(wcove->regmap, USBC_RXSTATUS, &status); + if (ret) + goto err; + + /* Flush all buffers */ + while (status & USBC_RXSTATUS_RXDATA) { + struct pd_message msg; + + ret = wcove_read_rx_buffer(wcove, &msg); + if (ret) { + dev_err(wcove->dev, "%s: RX read failed\n", + __func__); + goto err; + } + + tcpm_pd_receive(wcove->tcpm, &msg); + + ret = regmap_read(wcove->regmap, USBC_RXSTATUS, + &status); + if (ret) + goto err; + } + } + + if (usbc_irq2 & USBC_IRQ2_RX_HR) + tcpm_pd_hard_reset(wcove->tcpm); + + /* REVISIT: if (usbc_irq2 & USBC_IRQ2_RX_CR) */ + + if (usbc_irq2 & USBC_IRQ2_TX_SUCCESS) + tcpm_pd_transmit_complete(wcove->tcpm, TCPC_TX_SUCCESS); + + if (usbc_irq2 & USBC_IRQ2_TX_FAIL) + tcpm_pd_transmit_complete(wcove->tcpm, TCPC_TX_FAILED); - /* Relying on the FSM to know when we need to drive VBUS. */ - wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VBUS, - !!(status2 & USBC_STATUS2_VBUS_REQ)); err: + /* ..and clear. */ + if (usbc_irq1) { + ret = regmap_write(wcove->regmap, USBC_IRQ1, usbc_irq1); + if (ret) + dev_WARN(wcove->dev, "%s failed to clear IRQ1\n", + __func__); + } + + if (usbc_irq2) { + ret = regmap_write(wcove->regmap, USBC_IRQ2, usbc_irq2); + if (ret) + dev_WARN(wcove->dev, "%s failed to clear IRQ2\n", + __func__); + } + /* REVISIT: Clear WhiskeyCove CHGR Type-C interrupt */ regmap_write(wcove->regmap, WCOVE_CHGRIRQ0, BIT(5)); @@ -288,11 +550,41 @@ err: return IRQ_HANDLED; } +/* + * The following power levels should be safe to use with Joule board. + */ +static const u32 src_pdo[] = { + PDO_FIXED(5000, 1500, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | + PDO_FIXED_USB_COMM), +}; + +static const u32 snk_pdo[] = { + PDO_FIXED(12000, 3000, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | + PDO_FIXED_USB_COMM), + PDO_BATT(4750, 12000, 15000), + PDO_VAR(4750, 12000, 3000), +}; + +static struct tcpc_config wcove_typec_config = { + .src_pdo = src_pdo, + .nr_src_pdo = ARRAY_SIZE(src_pdo), + .snk_pdo = snk_pdo, + .nr_snk_pdo = ARRAY_SIZE(snk_pdo), + + .max_snk_mv = 12000, + .max_snk_ma = 3000, + .max_snk_mw = 36000, + .operating_snk_mw = 15000, + + .type = TYPEC_PORT_DRP, + .default_role = TYPEC_SINK, +}; + static int wcove_typec_probe(struct platform_device *pdev) { struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); struct wcove_typec *wcove; - unsigned int val; + int irq; int ret; wcove = devm_kzalloc(&pdev->dev, sizeof(*wcove), GFP_KERNEL); @@ -303,43 +595,47 @@ static int wcove_typec_probe(struct platform_device *pdev) wcove->dev = &pdev->dev; wcove->regmap = pmic->regmap; - ret = regmap_irq_get_virq(pmic->irq_chip_data_chgr, + irq = regmap_irq_get_virq(pmic->irq_chip_data_chgr, platform_get_irq(pdev, 0)); - if (ret < 0) - return ret; + if (irq < 0) + return irq; - ret = devm_request_threaded_irq(&pdev->dev, ret, NULL, - wcove_typec_irq, IRQF_ONESHOT, - "wcove_typec", wcove); + ret = guid_parse(WCOVE_DSM_UUID, &wcove->guid); if (ret) return ret; - if (!acpi_check_dsm(ACPI_HANDLE(&pdev->dev), &guid, 0, 0x1f)) { + if (!acpi_check_dsm(ACPI_HANDLE(&pdev->dev), &wcove->guid, 0, 0x1f)) { dev_err(&pdev->dev, "Missing _DSM functions\n"); return -ENODEV; } - wcove->cap.type = TYPEC_PORT_DRP; - wcove->cap.revision = USB_TYPEC_REV_1_1; - wcove->cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; + wcove->tcpc.init = wcove_init; + wcove->tcpc.get_vbus = wcove_get_vbus; + wcove->tcpc.set_vbus = wcove_set_vbus; + wcove->tcpc.set_cc = wcove_set_cc; + wcove->tcpc.get_cc = wcove_get_cc; + wcove->tcpc.set_polarity = wcove_set_polarity; + wcove->tcpc.set_vconn = wcove_set_vconn; + wcove->tcpc.set_current_limit = wcove_set_current_limit; + wcove->tcpc.start_drp_toggling = wcove_start_drp_toggling; - /* Make sure the PD PHY is disabled until USB PD is available */ - regmap_read(wcove->regmap, USBC_CONTROL3, &val); - regmap_write(wcove->regmap, USBC_CONTROL3, val | USBC_CONTROL3_PD_DIS); + wcove->tcpc.set_pd_rx = wcove_set_pd_rx; + wcove->tcpc.set_roles = wcove_set_roles; + wcove->tcpc.pd_transmit = wcove_pd_transmit; - /* DRP mode without accessory support */ - regmap_read(wcove->regmap, USBC_CONTROL1, &val); - regmap_write(wcove->regmap, USBC_CONTROL1, USBC_CONTROL1_MODE_DRP(val)); + wcove->tcpc.config = &wcove_typec_config; - wcove->port = typec_register_port(&pdev->dev, &wcove->cap); - if (!wcove->port) - return -ENODEV; + wcove->tcpm = tcpm_register_port(wcove->dev, &wcove->tcpc); + if (IS_ERR(wcove->tcpm)) + return PTR_ERR(wcove->tcpm); - /* Unmask everything */ - regmap_read(wcove->regmap, USBC_IRQMASK1, &val); - regmap_write(wcove->regmap, USBC_IRQMASK1, val & ~USBC_IRQMASK1_ALL); - regmap_read(wcove->regmap, USBC_IRQMASK2, &val); - regmap_write(wcove->regmap, USBC_IRQMASK2, val & ~USBC_IRQMASK2_ALL); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + wcove_typec_irq, IRQF_ONESHOT, + "wcove_typec", wcove); + if (ret) { + tcpm_unregister_port(wcove->tcpm); + return ret; + } platform_set_drvdata(pdev, wcove); return 0; @@ -356,8 +652,8 @@ static int wcove_typec_remove(struct platform_device *pdev) regmap_read(wcove->regmap, USBC_IRQMASK2, &val); regmap_write(wcove->regmap, USBC_IRQMASK2, val | USBC_IRQMASK2_ALL); - typec_unregister_partner(wcove->partner); - typec_unregister_port(wcove->port); + tcpm_unregister_port(wcove->tcpm); + return 0; } diff --git a/drivers/staging/typec/pd.h b/include/linux/usb/pd.h index 30b32ad72acd..e00051ced806 100644 --- a/drivers/staging/typec/pd.h +++ b/include/linux/usb/pd.h @@ -104,6 +104,11 @@ static inline unsigned int pd_header_msgid_le(__le16 header) #define PD_MAX_PAYLOAD 7 +/** + * struct pd_message - PD message as seen on wire + * @header: PD message header + * @payload: PD message payload + */ struct pd_message { __le16 header; __le32 payload[PD_MAX_PAYLOAD]; diff --git a/drivers/staging/typec/pd_bdo.h b/include/linux/usb/pd_bdo.h index 90b94d9fea5d..90b94d9fea5d 100644 --- a/drivers/staging/typec/pd_bdo.h +++ b/include/linux/usb/pd_bdo.h diff --git a/drivers/staging/typec/pd_vdo.h b/include/linux/usb/pd_vdo.h index d92259f8de0a..d92259f8de0a 100644 --- a/drivers/staging/typec/pd_vdo.h +++ b/include/linux/usb/pd_vdo.h diff --git a/drivers/staging/typec/tcpm.h b/include/linux/usb/tcpm.h index 7e9a6b7b5cd6..073197f0d2bb 100644 --- a/drivers/staging/typec/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -54,6 +54,27 @@ enum tcpm_transmit_type { TCPC_TX_BIST_MODE_2 = 7 }; +/** + * struct tcpc_config - Port configuration + * @src_pdo: PDO parameters sent to port partner as response to + * PD_CTRL_GET_SOURCE_CAP message + * @nr_src_pdo: Number of entries in @src_pdo + * @snk_pdo: PDO parameters sent to partner as response to + * PD_CTRL_GET_SINK_CAP message + * @nr_snk_pdo: Number of entries in @snk_pdo + * @max_snk_mv: Maximum acceptable sink voltage in mV + * @max_snk_ma: Maximum sink current in mA + * @max_snk_mw: Maximum required sink power in mW + * @operating_snk_mw: + * Required operating sink power in mW + * @type: Port type (TYPEC_PORT_DFP, TYPEC_PORT_UFP, or + * TYPEC_PORT_DRP) + * @default_role: + * Default port role (TYPEC_SINK or TYPEC_SOURCE). + * Set to TYPEC_NO_PREFERRED_ROLE if no default role. + * @try_role_hw:True if try.{Src,Snk} is implemented in hardware + * @alt_modes: List of supported alternate modes + */ struct tcpc_config { const u32 *src_pdo; unsigned int nr_src_pdo; @@ -79,7 +100,6 @@ struct tcpc_config { enum tcpc_usb_switch { TCPC_USB_SWITCH_CONNECT, TCPC_USB_SWITCH_DISCONNECT, - TCPC_USB_SWITCH_RESTORE, /* TODO FIXME */ }; /* Mux state attributes */ @@ -104,17 +124,40 @@ struct tcpc_mux_dev { void *priv_data; }; +/** + * struct tcpc_dev - Port configuration and callback functions + * @config: Pointer to port configuration + * @get_vbus: Called to read current VBUS state + * @get_current_limit: + * Optional; called by the tcpm core when configured as a snk + * and cc=Rp-def. This allows the tcpm to provide a fallback + * current-limit detection method for the cc=Rp-def case. + * For example, some tcpcs may include BC1.2 charger detection + * and use that in this case. + * @set_cc: Called to set value of CC pins + * @get_cc: Called to read current CC pin values + * @set_polarity: + * Called to set polarity + * @set_vconn: Called to enable or disable VCONN + * @set_vbus: Called to enable or disable VBUS + * @set_current_limit: + * Optional; called to set current limit as negotiated + * with partner. + * @set_pd_rx: Called to enable or disable reception of PD messages + * @set_roles: Called to set power and data roles + * @start_drp_toggling: + * Optional; if supported by hardware, called to start DRP + * toggling. DRP toggling is stopped automatically if + * a connection is established. + * @try_role: Optional; called to set a preferred role + * @pd_transmit:Called to transmit PD message + * @mux: Pointer to multiplexer data + */ struct tcpc_dev { const struct tcpc_config *config; int (*init)(struct tcpc_dev *dev); int (*get_vbus)(struct tcpc_dev *dev); - /* - * This optional callback gets called by the tcpm core when configured - * as a snk and cc=Rp-def. This allows the tcpm to provide a fallback - * current-limit detection method for the cc=Rp-def case. E.g. some - * tcpcs may include BC1.2 charger detection and use that in this case. - */ int (*get_current_limit)(struct tcpc_dev *dev); int (*set_cc)(struct tcpc_dev *dev, enum typec_cc_status cc); int (*get_cc)(struct tcpc_dev *dev, enum typec_cc_status *cc1, diff --git a/tools/usb/usbip/Makefile.am b/tools/usb/usbip/Makefile.am index 66f8bf038c9f..45eaa70a71e0 100644 --- a/tools/usb/usbip/Makefile.am +++ b/tools/usb/usbip/Makefile.am @@ -1,6 +1,7 @@ SUBDIRS := libsrc src includedir = @includedir@/usbip include_HEADERS := $(addprefix libsrc/, \ - usbip_common.h vhci_driver.h usbip_host_driver.h) + usbip_common.h vhci_driver.h usbip_host_driver.h \ + list.h sysfs_utils.h usbip_host_common.h) dist_man_MANS := $(addprefix doc/, usbip.8 usbipd.8) |