diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-08-22 13:16:06 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-08-22 13:16:06 -0700 |
commit | 34a0036748257dd97b22b76ef64aa44559f2ef6f (patch) | |
tree | 48384dca01f8fd8bbf566ca0df07e80b18d32fec | |
parent | a85c0f8db3329ab433dab49322616e6985317cd7 (diff) | |
parent | 0852659ef071ccd84e85e37195e7c2f3e7c64d1f (diff) | |
download | op-kernel-dev-34a0036748257dd97b22b76ef64aa44559f2ef6f.zip op-kernel-dev-34a0036748257dd97b22b76ef64aa44559f2ef6f.tar.gz |
Merge tag 'usb-for-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
Felipe writes:
usb: changes for v4.14 merge window
Not a big pull request this time around. Only 49 non-merge
commits. This pull request is, however, all over the place. Most of
the changes are in the bdc driver adding support for USB Phy layer and
PM.
Renesas adds support for R-Car H3 ES2.0 and R-Car M3-W SoCs.
Also here is PM_RUNTIME support for dwc3-keystone.
UDC Core got a DMA unmap fix to make sure we only unmap requests that
were, indeed, mapped.
Other than these, we have a lot of cleanups, many of them adding
'const' to several places.
39 files changed, 950 insertions, 99 deletions
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-rndis b/Documentation/ABI/testing/configfs-usb-gadget-rndis index e32879b..1373990 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-rndis +++ b/Documentation/ABI/testing/configfs-usb-gadget-rndis @@ -12,3 +12,6 @@ Description: Ethernet over USB link dev_addr - MAC address of device's end of this Ethernet over USB link + class - USB interface class, default is 02 (hex) + subclass - USB interface subclass, default is 06 (hex) + protocol - USB interface protocol, default is 00 (hex) diff --git a/Documentation/devicetree/bindings/mfd/wm831x.txt b/Documentation/devicetree/bindings/mfd/wm831x.txt index 9f8b743..5057094 100644 --- a/Documentation/devicetree/bindings/mfd/wm831x.txt +++ b/Documentation/devicetree/bindings/mfd/wm831x.txt @@ -31,6 +31,7 @@ Required properties: ../interrupt-controller/interrupts.txt Optional sub-nodes: + - phys : Contains a phandle to the USB PHY. - regulators : Contains sub-nodes for each of the regulators supplied by the device. The regulators are bound using their names listed below: diff --git a/Documentation/devicetree/bindings/usb/brcm,bdc.txt b/Documentation/devicetree/bindings/usb/brcm,bdc.txt new file mode 100644 index 0000000..63e63af --- /dev/null +++ b/Documentation/devicetree/bindings/usb/brcm,bdc.txt @@ -0,0 +1,29 @@ +Broadcom USB Device Controller (BDC) +==================================== + +Required properties: + +- compatible: must be one of: + "brcm,bdc-v0.16" + "brcm,bdc" +- reg: the base register address and length +- interrupts: the interrupt line for this controller + +Optional properties: + +On Broadcom STB platforms, these properties are required: + +- phys: phandle to one or two USB PHY blocks + NOTE: Some SoC's have a single phy and some have + USB 2.0 and USB 3.0 phys +- clocks: phandle to the functional clock of this block + +Example: + + bdc@f0b02000 { + compatible = "brcm,bdc-v0.16"; + reg = <0xf0b02000 0xfc4>; + interrupts = <0x0 0x60 0x0>; + phys = <&usbphy_0 0x0>; + clocks = <&sw_usbd>; + }; diff --git a/Documentation/devicetree/bindings/usb/keystone-usb.txt b/Documentation/devicetree/bindings/usb/keystone-usb.txt index 60527d3..2d1bef1 100644 --- a/Documentation/devicetree/bindings/usb/keystone-usb.txt +++ b/Documentation/devicetree/bindings/usb/keystone-usb.txt @@ -12,8 +12,21 @@ Required properties: MPU. - ranges: allows valid 1:1 translation between child's address space and parent's address space. - - clocks: Clock IDs array as required by the controller. - - clock-names: names of clocks correseponding to IDs in the clock property. + +SoC-specific Required Properties: +The following are mandatory properties for Keystone 2 66AK2HK, 66AK2L and 66AK2E +SoCs only: + +- clocks: Clock ID for USB functional clock. +- clock-names: Must be "usb". + + +The following are mandatory properties for Keystone 2 66AK2G SoCs only: + +- power-domains: Should contain a phandle to a PM domain provider node + and an args specifier containing the USB device id + value. This property is as per the binding, + Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt Sub-nodes: The dwc3 core should be added as subnode to Keystone DWC3 glue. diff --git a/Documentation/devicetree/bindings/usb/renesas_usb3.txt b/Documentation/devicetree/bindings/usb/renesas_usb3.txt index 8d52766..e280258 100644 --- a/Documentation/devicetree/bindings/usb/renesas_usb3.txt +++ b/Documentation/devicetree/bindings/usb/renesas_usb3.txt @@ -3,20 +3,30 @@ Renesas Electronics USB3.0 Peripheral driver Required properties: - compatible: Must contain one of the following: - "renesas,r8a7795-usb3-peri" + - "renesas,r8a7796-usb3-peri" + - "renesas,rcar-gen3-usb3-peri" for a generic R-Car Gen3 compatible + device + + When compatible with the generic version, nodes must list the + SoC-specific version corresponding to the platform first + followed by the generic version. + - reg: Base address and length of the register for the USB3.0 Peripheral - interrupts: Interrupt specifier for the USB3.0 Peripheral - clocks: clock phandle and specifier pair -Example: +Example of R-Car H3 ES1.x: usb3_peri0: usb@ee020000 { - compatible = "renesas,r8a7795-usb3-peri"; + compatible = "renesas,r8a7795-usb3-peri", + "renesas,rcar-gen3-usb3-peri"; reg = <0 0xee020000 0 0x400>; interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cpg CPG_MOD 328>; }; usb3_peri1: usb@ee060000 { - compatible = "renesas,r8a7795-usb3-peri"; + compatible = "renesas,r8a7795-usb3-peri", + "renesas,rcar-gen3-usb3-peri"; reg = <0 0xee060000 0 0x400>; interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cpg CPG_MOD 327>; diff --git a/drivers/power/supply/wm831x_power.c b/drivers/power/supply/wm831x_power.c index 7082301..927050d44 100644 --- a/drivers/power/supply/wm831x_power.c +++ b/drivers/power/supply/wm831x_power.c @@ -13,6 +13,7 @@ #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/slab.h> +#include <linux/usb/phy.h> #include <linux/mfd/wm831x/core.h> #include <linux/mfd/wm831x/auxadc.h> @@ -31,6 +32,8 @@ struct wm831x_power { char usb_name[20]; char battery_name[20]; bool have_battery; + struct usb_phy *usb_phy; + struct notifier_block usb_notify; }; static int wm831x_power_check_online(struct wm831x *wm831x, int supply, @@ -125,6 +128,43 @@ static enum power_supply_property wm831x_usb_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, }; +/* In milliamps */ +static const unsigned int wm831x_usb_limits[] = { + 0, + 2, + 100, + 500, + 900, + 1500, + 1800, + 550, +}; + +static int wm831x_usb_limit_change(struct notifier_block *nb, + unsigned long limit, void *data) +{ + struct wm831x_power *wm831x_power = container_of(nb, + struct wm831x_power, + usb_notify); + unsigned int i, best; + + /* Find the highest supported limit */ + best = 0; + for (i = 0; i < ARRAY_SIZE(wm831x_usb_limits); i++) { + if (limit >= wm831x_usb_limits[i] && + wm831x_usb_limits[best] < wm831x_usb_limits[i]) + best = i; + } + + dev_dbg(wm831x_power->wm831x->dev, + "Limiting USB current to %umA", wm831x_usb_limits[best]); + + wm831x_set_bits(wm831x_power->wm831x, WM831X_POWER_STATE, + WM831X_USB_ILIM_MASK, best); + + return 0; +} + /********************************************************************* * Battery properties *********************************************************************/ @@ -607,6 +647,33 @@ static int wm831x_power_probe(struct platform_device *pdev) } } + power->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "phys", 0); + ret = PTR_ERR_OR_ZERO(power->usb_phy); + + switch (ret) { + case 0: + power->usb_notify.notifier_call = wm831x_usb_limit_change; + ret = usb_register_notifier(power->usb_phy, &power->usb_notify); + if (ret) { + dev_err(&pdev->dev, "Failed to register notifier: %d\n", + ret); + goto err_bat_irq; + } + break; + case -EINVAL: + case -ENODEV: + /* ignore missing usb-phy, it's optional */ + power->usb_phy = NULL; + ret = 0; + break; + default: + dev_err(&pdev->dev, "Failed to find USB phy: %d\n", ret); + /* fall-through */ + case -EPROBE_DEFER: + goto err_bat_irq; + break; + } + return ret; err_bat_irq: @@ -637,6 +704,11 @@ static int wm831x_power_remove(struct platform_device *pdev) struct wm831x *wm831x = wm831x_power->wm831x; int irq, i; + if (wm831x_power->usb_phy) { + usb_unregister_notifier(wm831x_power->usb_phy, + &wm831x_power->usb_notify); + } + for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) { irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index c4066cd..0d8e09c 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -4179,7 +4179,7 @@ static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value) return ret; } -static struct usb_ep_ops dwc2_hsotg_ep_ops = { +static const struct usb_ep_ops dwc2_hsotg_ep_ops = { .enable = dwc2_hsotg_ep_enable, .disable = dwc2_hsotg_ep_disable, .alloc_request = dwc2_hsotg_ep_alloc_request, diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 740c7e8..c263114 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4388,6 +4388,9 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) spin_lock_irqsave(&hsotg->lock, flags); + if (dwc2_is_device_mode(hsotg)) + goto unlock; + if (hsotg->lx_state != DWC2_L0) goto unlock; @@ -4446,6 +4449,9 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_lock_irqsave(&hsotg->lock, flags); + if (dwc2_is_device_mode(hsotg)) + goto unlock; + if (hsotg->lx_state != DWC2_L2) goto unlock; diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index 12ee23f..d2ed952 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -15,7 +15,6 @@ * GNU General Public License for more details. */ -#include <linux/clk.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/interrupt.h> @@ -23,6 +22,7 @@ #include <linux/dma-mapping.h> #include <linux/io.h> #include <linux/of_platform.h> +#include <linux/pm_runtime.h> /* USBSS register offsets */ #define USBSS_REVISION 0x0000 @@ -41,7 +41,6 @@ struct dwc3_keystone { struct device *dev; - struct clk *clk; void __iomem *usbss; }; @@ -106,17 +105,13 @@ static int kdwc3_probe(struct platform_device *pdev) if (IS_ERR(kdwc->usbss)) return PTR_ERR(kdwc->usbss); - kdwc->clk = devm_clk_get(kdwc->dev, "usb"); - if (IS_ERR(kdwc->clk)) { - dev_err(kdwc->dev, "unable to get usb clock\n"); - return PTR_ERR(kdwc->clk); - } + pm_runtime_enable(kdwc->dev); - error = clk_prepare_enable(kdwc->clk); + error = pm_runtime_get_sync(kdwc->dev); if (error < 0) { - dev_err(kdwc->dev, "unable to enable usb clock, error %d\n", + dev_err(kdwc->dev, "pm_runtime_get_sync failed, error %d\n", error); - return error; + goto err_irq; } irq = platform_get_irq(pdev, 0); @@ -147,7 +142,8 @@ static int kdwc3_probe(struct platform_device *pdev) err_core: kdwc3_disable_irqs(kdwc); err_irq: - clk_disable_unprepare(kdwc->clk); + pm_runtime_put_sync(kdwc->dev); + pm_runtime_disable(kdwc->dev); return error; } @@ -167,7 +163,9 @@ static int kdwc3_remove(struct platform_device *pdev) kdwc3_disable_irqs(kdwc); device_for_each_child(&pdev->dev, NULL, kdwc3_remove_core); - clk_disable_unprepare(kdwc->clk); + pm_runtime_put_sync(kdwc->dev); + pm_runtime_disable(kdwc->dev); + platform_set_drvdata(pdev, NULL); return 0; diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index fe414e7..4cef7d4 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -25,7 +25,6 @@ #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/clk.h> -#include <linux/clk-provider.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> @@ -96,7 +95,8 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) platform_set_drvdata(pdev, simple); simple->dev = dev; - ret = dwc3_of_simple_clk_init(simple, of_clk_get_parent_count(np)); + ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np, + "clocks", "#clock-cells")); if (ret) return ret; diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index f5aaa0c..3530795 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -478,8 +478,8 @@ static int dwc3_omap_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(dev, "missing IRQ resource\n"); - return -EINVAL; + dev_err(dev, "missing IRQ resource: %d\n", irq); + return irq; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 35cc641..31cce78 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -130,7 +130,7 @@ config USB_GADGET_STORAGE_NUM_BUFFERS config U_SERIAL_CONSOLE bool "Serial gadget console support" - depends on USB_G_SERIAL + depends on USB_U_SERIAL help It supports the serial gadget can be used as a console. diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index d21874b..9990944 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -961,10 +961,9 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) /* In the meantime, endpoint got disabled or changed. */ ret = -ESHUTDOWN; } else if (halt) { - /* Halt */ - if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep)) - usb_ep_set_halt(ep->ep); - ret = -EBADMSG; + ret = usb_ep_set_halt(ep->ep); + if (!ret) + ret = -EBADMSG; } else if (unlikely(data_len == -EINVAL)) { /* * Sanity Check: even though data_len can't be used diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 5eea448..d8e359e 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -44,6 +44,7 @@ struct f_hidg { /* configuration */ unsigned char bInterfaceSubClass; unsigned char bInterfaceProtocol; + unsigned char protocol; unsigned short report_desc_length; char *report_desc; unsigned short report_length; @@ -527,7 +528,9 @@ static int hidg_setup(struct usb_function *f, case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | HID_REQ_GET_PROTOCOL): VDBG(cdev, "get_protocol\n"); - goto stall; + length = min_t(unsigned int, length, 1); + ((u8 *) req->buf)[0] = hidg->protocol; + goto respond; break; case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 @@ -539,6 +542,17 @@ static int hidg_setup(struct usb_function *f, case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | HID_REQ_SET_PROTOCOL): VDBG(cdev, "set_protocol\n"); + if (value > HID_REPORT_PROTOCOL) + goto stall; + length = 0; + /* + * We assume that programs implementing the Boot protocol + * are also compatible with the Report Protocol + */ + if (hidg->bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) { + hidg->protocol = value; + goto respond; + } goto stall; break; @@ -768,6 +782,7 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) /* set descriptor dynamic values */ hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass; hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; + hidg->protocol = HID_REPORT_PROTOCOL; hidg_ss_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); hidg_ss_in_comp_desc.wBytesPerInterval = cpu_to_le16(hidg->report_length); diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index a5719f2..5d3d794 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -98,6 +98,7 @@ struct f_midi { DECLARE_KFIFO_PTR(in_req_fifo, struct usb_request *); spinlock_t transmit_lock; unsigned int in_last_port; + unsigned char free_ref; struct gmidi_in_port in_ports_array[/* in_ports */]; }; @@ -108,6 +109,7 @@ static inline struct f_midi *func_to_midi(struct usb_function *f) } static void f_midi_transmit(struct f_midi *midi); +static void f_midi_rmidi_free(struct snd_rawmidi *rmidi); DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); @@ -163,6 +165,13 @@ static struct usb_endpoint_descriptor bulk_out_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; +static struct usb_ss_ep_comp_descriptor bulk_out_ss_comp_desc = { + .bLength = sizeof(bulk_out_ss_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + /* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */ static struct usb_ms_endpoint_descriptor_16 ms_out_desc = { /* .bLength = DYNAMIC */ @@ -180,6 +189,13 @@ static struct usb_endpoint_descriptor bulk_in_desc = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; +static struct usb_ss_ep_comp_descriptor bulk_in_ss_comp_desc = { + .bLength = sizeof(bulk_in_ss_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + /* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */ static struct usb_ms_endpoint_descriptor_16 ms_in_desc = { /* .bLength = DYNAMIC */ @@ -755,13 +771,13 @@ static void f_midi_out_trigger(struct snd_rawmidi_substream *substream, int up) clear_bit(substream->number, &midi->out_triggered); } -static struct snd_rawmidi_ops gmidi_in_ops = { +static const struct snd_rawmidi_ops gmidi_in_ops = { .open = f_midi_in_open, .close = f_midi_in_close, .trigger = f_midi_in_trigger, }; -static struct snd_rawmidi_ops gmidi_out_ops = { +static const struct snd_rawmidi_ops gmidi_out_ops = { .open = f_midi_out_open, .close = f_midi_out_close, .trigger = f_midi_out_trigger @@ -818,6 +834,8 @@ static int f_midi_register_card(struct f_midi *midi) SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; rmidi->private_data = midi; + rmidi->private_free = f_midi_rmidi_free; + midi->free_ref++; /* * Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT. @@ -853,7 +871,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) struct usb_composite_dev *cdev = c->cdev; struct f_midi *midi = func_to_midi(f); struct usb_string *us; - int status, n, jack = 1, i = 0; + int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0; midi->gadget = cdev->gadget; tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi); @@ -895,7 +913,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) goto fail; /* allocate temporary function list */ - midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(*midi_function), + midi_function = kcalloc((MAX_PORTS * 4) + 11, sizeof(*midi_function), GFP_KERNEL); if (!midi_function) { status = -ENOMEM; @@ -985,6 +1003,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) ms_in_desc.bNumEmbMIDIJack = midi->out_ports; /* ... and add them to the list */ + endpoint_descriptor_index = i; midi_function[i++] = (struct usb_descriptor_header *) &bulk_out_desc; midi_function[i++] = (struct usb_descriptor_header *) &ms_out_desc; midi_function[i++] = (struct usb_descriptor_header *) &bulk_in_desc; @@ -1009,13 +1028,34 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) goto fail_f_midi; } + if (gadget_is_superspeed(c->cdev->gadget)) { + bulk_in_desc.wMaxPacketSize = cpu_to_le16(1024); + bulk_out_desc.wMaxPacketSize = cpu_to_le16(1024); + i = endpoint_descriptor_index; + midi_function[i++] = (struct usb_descriptor_header *) + &bulk_out_desc; + midi_function[i++] = (struct usb_descriptor_header *) + &bulk_out_ss_comp_desc; + midi_function[i++] = (struct usb_descriptor_header *) + &ms_out_desc; + midi_function[i++] = (struct usb_descriptor_header *) + &bulk_in_desc; + midi_function[i++] = (struct usb_descriptor_header *) + &bulk_in_ss_comp_desc; + midi_function[i++] = (struct usb_descriptor_header *) + &ms_in_desc; + f->ss_descriptors = usb_copy_descriptors(midi_function); + if (!f->ss_descriptors) + goto fail_f_midi; + } + kfree(midi_function); return 0; fail_f_midi: kfree(midi_function); - usb_free_descriptors(f->hs_descriptors); + usb_free_all_descriptors(f); fail: f_midi_unregister_card(midi); fail_register: @@ -1197,14 +1237,21 @@ static void f_midi_free(struct usb_function *f) midi = func_to_midi(f); opts = container_of(f->fi, struct f_midi_opts, func_inst); - kfree(midi->id); mutex_lock(&opts->lock); - kfifo_free(&midi->in_req_fifo); - kfree(midi); - --opts->refcnt; + if (!--midi->free_ref) { + kfree(midi->id); + kfifo_free(&midi->in_req_fifo); + kfree(midi); + --opts->refcnt; + } mutex_unlock(&opts->lock); } +static void f_midi_rmidi_free(struct snd_rawmidi *rmidi) +{ + f_midi_free(rmidi->private_data); +} + static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = f->config->cdev; @@ -1219,7 +1266,7 @@ static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) card = midi->card; midi->card = NULL; if (card) - snd_card_free(card); + snd_card_free_when_closed(card); usb_free_all_descriptors(f); } @@ -1263,6 +1310,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) midi->buflen = opts->buflen; midi->qlen = opts->qlen; midi->in_last_port = 0; + midi->free_ref = 1; status = kfifo_alloc(&midi->in_req_fifo, midi->qlen, GFP_KERNEL); if (status) diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 24e34cf..45b334c 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -925,8 +925,6 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) */ ncm->port.is_zlp_ok = gadget_is_zlp_supported(cdev->gadget); - ncm->port.no_skb_reserve = - gadget_avoids_skb_reserve(cdev->gadget); ncm->port.cdc_filter = DEFAULT_FILTER; DBG(cdev, "activate ncm\n"); net = gether_connect(&ncm->port); diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 16562e4..e1d5853 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -691,6 +691,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc; } + rndis_iad_descriptor.bFunctionClass = rndis_opts->class; + rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass; + rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol; + /* * in drivers/usb/gadget/configfs.c:configfs_composite_bind() * configurations are bound in sequence with list_for_each_entry, @@ -866,11 +870,23 @@ USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(rndis); /* f_rndis_opts_ifname */ USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(rndis); +/* f_rndis_opts_class */ +USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, class); + +/* f_rndis_opts_subclass */ +USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, subclass); + +/* f_rndis_opts_protocol */ +USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, protocol); + static struct configfs_attribute *rndis_attrs[] = { &rndis_opts_attr_dev_addr, &rndis_opts_attr_host_addr, &rndis_opts_attr_qmult, &rndis_opts_attr_ifname, + &rndis_opts_attr_class, + &rndis_opts_attr_subclass, + &rndis_opts_attr_protocol, NULL, }; @@ -916,6 +932,10 @@ static struct usb_function_instance *rndis_alloc_inst(void) } INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop); + opts->class = rndis_iad_descriptor.bFunctionClass; + opts->subclass = rndis_iad_descriptor.bFunctionSubClass; + opts->protocol = rndis_iad_descriptor.bFunctionProtocol; + descs[0] = &opts->rndis_os_desc; names[0] = "rndis"; config_group_init_type_name(&opts->func_inst.group, "", diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index a8b40d0..bdbc3fd 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -1073,7 +1073,7 @@ struct net_device *gether_connect(struct gether *link) if (result == 0) { dev->zlp = link->is_zlp_ok; - dev->no_skb_reserve = link->no_skb_reserve; + dev->no_skb_reserve = gadget_avoids_skb_reserve(dev->gadget); DBG(dev, "qlen %d\n", qlen(dev->gadget, dev->qmult)); dev->header_len = link->header_len; diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h index 81d94a7..c77145b 100644 --- a/drivers/usb/gadget/function/u_ether.h +++ b/drivers/usb/gadget/function/u_ether.h @@ -64,7 +64,6 @@ struct gether { struct usb_ep *out_ep; bool is_zlp_ok; - bool no_skb_reserve; u16 cdc_filter; diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h index c71133d..e4c3f84a 100644 --- a/drivers/usb/gadget/function/u_ether_configfs.h +++ b/drivers/usb/gadget/function/u_ether_configfs.h @@ -153,4 +153,39 @@ out: \ \ CONFIGFS_ATTR_RO(_f_##_opts_, ifname) +#define USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(_f_, _n_) \ + static ssize_t _f_##_opts_##_n_##_show(struct config_item *item,\ + char *page) \ + { \ + struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ + int ret; \ + \ + mutex_lock(&opts->lock); \ + ret = sprintf(page, "%02x\n", opts->_n_); \ + mutex_unlock(&opts->lock); \ + \ + return ret; \ + } \ + \ + static ssize_t _f_##_opts_##_n_##_store(struct config_item *item,\ + const char *page, \ + size_t len) \ + { \ + struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ + int ret; \ + u8 val; \ + \ + mutex_lock(&opts->lock); \ + ret = sscanf(page, "%02hhx", &val); \ + if (ret > 0) { \ + opts->_n_ = val; \ + ret = len; \ + } \ + mutex_unlock(&opts->lock); \ + \ + return ret; \ + } \ + \ + CONFIGFS_ATTR(_f_##_opts_, _n_) + #endif /* __U_ETHER_CONFIGFS_H */ diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h index 4eafd50..a35ee3c 100644 --- a/drivers/usb/gadget/function/u_rndis.h +++ b/drivers/usb/gadget/function/u_rndis.h @@ -29,6 +29,10 @@ struct f_rndis_opts { struct usb_os_desc rndis_os_desc; char rndis_ext_compat_id[16]; + u8 class; + u8 subclass; + u8 protocol; + /* * Read/write access to configfs attributes is handled by configfs. * diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 9b0805f..4176216 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -537,7 +537,7 @@ static void gs_rx_push(unsigned long _port) } /* push data to (open) tty */ - if (req->actual) { + if (req->actual && tty) { char *packet = req->buf; unsigned size = req->actual; unsigned n; diff --git a/drivers/usb/gadget/udc/bdc/Kconfig b/drivers/usb/gadget/udc/bdc/Kconfig index eb8b553..c74ac25 100644 --- a/drivers/usb/gadget/udc/bdc/Kconfig +++ b/drivers/usb/gadget/udc/bdc/Kconfig @@ -1,6 +1,7 @@ config USB_BDC_UDC tristate "Broadcom USB3.0 device controller IP driver(BDC)" depends on USB_GADGET && HAS_DMA + default ARCH_BRCMSTB help BDC is Broadcom's USB3.0 device controller IP. If your SOC has a BDC IP diff --git a/drivers/usb/gadget/udc/bdc/bdc.h b/drivers/usb/gadget/udc/bdc/bdc.h index 916d471..6df0352 100644 --- a/drivers/usb/gadget/udc/bdc/bdc.h +++ b/drivers/usb/gadget/udc/bdc/bdc.h @@ -27,8 +27,8 @@ #include <linux/usb/gadget.h> #include <asm/unaligned.h> -#define BRCM_BDC_NAME "bdc_usb3" -#define BRCM_BDC_DESC "BDC device controller driver" +#define BRCM_BDC_NAME "bdc" +#define BRCM_BDC_DESC "Broadcom USB Device Controller driver" #define DMA_ADDR_INVALID (~(dma_addr_t)0) @@ -83,14 +83,14 @@ #define BDC_DVCSA 0x50 #define BDC_DVCSB 0x54 -#define BDC_EPSTS0(n) (0x60 + (n * 0x10)) -#define BDC_EPSTS1(n) (0x64 + (n * 0x10)) -#define BDC_EPSTS2(n) (0x68 + (n * 0x10)) -#define BDC_EPSTS3(n) (0x6c + (n * 0x10)) -#define BDC_EPSTS4(n) (0x70 + (n * 0x10)) -#define BDC_EPSTS5(n) (0x74 + (n * 0x10)) -#define BDC_EPSTS6(n) (0x78 + (n * 0x10)) -#define BDC_EPSTS7(n) (0x7c + (n * 0x10)) +#define BDC_EPSTS0 0x60 +#define BDC_EPSTS1 0x64 +#define BDC_EPSTS2 0x68 +#define BDC_EPSTS3 0x6c +#define BDC_EPSTS4 0x70 +#define BDC_EPSTS5 0x74 +#define BDC_EPSTS6 0x78 +#define BDC_EPSTS7 0x7c #define BDC_SRRBAL(n) (0x200 + (n * 0x10)) #define BDC_SRRBAH(n) (0x204 + (n * 0x10)) #define BDC_SRRINT(n) (0x208 + (n * 0x10)) @@ -413,6 +413,9 @@ struct bdc { /* device lock */ spinlock_t lock; + /* generic phy */ + struct phy **phys; + int num_phys; /* num of endpoints for a particular instantiation of IP */ unsigned int num_eps; /* @@ -454,6 +457,7 @@ struct bdc { * Func Wake packet every 2.5 secs. Refer to USB3 spec section 8.5.6.4 */ struct delayed_work func_wake_notify; + struct clk *clk; }; static inline u32 bdc_readl(void __iomem *base, u32 offset) diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index e9bd8d4..7a8af4b 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -24,9 +24,11 @@ #include <linux/dma-mapping.h> #include <linux/dmapool.h> #include <linux/of.h> +#include <linux/phy/phy.h> #include <linux/moduleparam.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/clk.h> #include "bdc.h" #include "bdc_dbg.h" @@ -444,6 +446,43 @@ static int bdc_hw_init(struct bdc *bdc) return 0; } +static int bdc_phy_init(struct bdc *bdc) +{ + int phy_num; + int ret; + + for (phy_num = 0; phy_num < bdc->num_phys; phy_num++) { + ret = phy_init(bdc->phys[phy_num]); + if (ret) + goto err_exit_phy; + ret = phy_power_on(bdc->phys[phy_num]); + if (ret) { + phy_exit(bdc->phys[phy_num]); + goto err_exit_phy; + } + } + + return 0; + +err_exit_phy: + while (--phy_num >= 0) { + phy_power_off(bdc->phys[phy_num]); + phy_exit(bdc->phys[phy_num]); + } + + return ret; +} + +static void bdc_phy_exit(struct bdc *bdc) +{ + int phy_num; + + for (phy_num = 0; phy_num < bdc->num_phys; phy_num++) { + phy_power_off(bdc->phys[phy_num]); + phy_exit(bdc->phys[phy_num]); + } +} + static int bdc_probe(struct platform_device *pdev) { struct bdc *bdc; @@ -452,12 +491,29 @@ static int bdc_probe(struct platform_device *pdev) int irq; u32 temp; struct device *dev = &pdev->dev; + struct clk *clk; + int phy_num; dev_dbg(dev, "%s()\n", __func__); + + clk = devm_clk_get(dev, "sw_usbd"); + if (IS_ERR(clk)) { + dev_info(dev, "Clock not found in Device Tree\n"); + clk = NULL; + } + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "could not enable clock\n"); + return ret; + } + bdc = devm_kzalloc(dev, sizeof(*bdc), GFP_KERNEL); if (!bdc) return -ENOMEM; + bdc->clk = clk; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); bdc->regs = devm_ioremap_resource(dev, res); if (IS_ERR(bdc->regs)) { @@ -473,35 +529,66 @@ static int bdc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bdc); bdc->irq = irq; bdc->dev = dev; - dev_dbg(bdc->dev, "bdc->regs: %p irq=%d\n", bdc->regs, bdc->irq); + dev_dbg(dev, "bdc->regs: %p irq=%d\n", bdc->regs, bdc->irq); + + bdc->num_phys = of_count_phandle_with_args(dev->of_node, + "phys", "#phy-cells"); + if (bdc->num_phys > 0) { + bdc->phys = devm_kcalloc(dev, bdc->num_phys, + sizeof(struct phy *), GFP_KERNEL); + if (!bdc->phys) + return -ENOMEM; + } else { + bdc->num_phys = 0; + } + dev_info(dev, "Using %d phy(s)\n", bdc->num_phys); + + for (phy_num = 0; phy_num < bdc->num_phys; phy_num++) { + bdc->phys[phy_num] = devm_of_phy_get_by_index( + dev, dev->of_node, phy_num); + if (IS_ERR(bdc->phys[phy_num])) { + ret = PTR_ERR(bdc->phys[phy_num]); + dev_err(bdc->dev, + "BDC phy specified but not found:%d\n", ret); + return ret; + } + } + + ret = bdc_phy_init(bdc); + if (ret) { + dev_err(bdc->dev, "BDC phy init failure:%d\n", ret); + return ret; + } temp = bdc_readl(bdc->regs, BDC_BDCCAP1); if ((temp & BDC_P64) && !dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) { - dev_dbg(bdc->dev, "Using 64-bit address\n"); + dev_dbg(dev, "Using 64-bit address\n"); } else { - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (ret) { - dev_err(bdc->dev, "No suitable DMA config available, abort\n"); + dev_err(dev, + "No suitable DMA config available, abort\n"); return -ENOTSUPP; } - dev_dbg(bdc->dev, "Using 32-bit address\n"); + dev_dbg(dev, "Using 32-bit address\n"); } ret = bdc_hw_init(bdc); if (ret) { - dev_err(bdc->dev, "BDC init failure:%d\n", ret); - return ret; + dev_err(dev, "BDC init failure:%d\n", ret); + goto phycleanup; } ret = bdc_udc_init(bdc); if (ret) { - dev_err(bdc->dev, "BDC Gadget init failure:%d\n", ret); + dev_err(dev, "BDC Gadget init failure:%d\n", ret); goto cleanup; } return 0; cleanup: bdc_hw_exit(bdc); - +phycleanup: + bdc_phy_exit(bdc); return ret; } @@ -513,13 +600,56 @@ static int bdc_remove(struct platform_device *pdev) dev_dbg(bdc->dev, "%s ()\n", __func__); bdc_udc_exit(bdc); bdc_hw_exit(bdc); + bdc_phy_exit(bdc); + clk_disable_unprepare(bdc->clk); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int bdc_suspend(struct device *dev) +{ + struct bdc *bdc = dev_get_drvdata(dev); + clk_disable_unprepare(bdc->clk); return 0; } +static int bdc_resume(struct device *dev) +{ + struct bdc *bdc = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(bdc->clk); + if (ret) { + dev_err(bdc->dev, "err enabling the clock\n"); + return ret; + } + ret = bdc_reinit(bdc); + if (ret) { + dev_err(bdc->dev, "err in bdc reinit\n"); + return ret; + } + + return 0; +} + +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(bdc_pm_ops, bdc_suspend, + bdc_resume); + +static const struct of_device_id bdc_of_match[] = { + { .compatible = "brcm,bdc-v0.16" }, + { .compatible = "brcm,bdc" }, + { /* sentinel */ } +}; + static struct platform_driver bdc_driver = { .driver = { .name = BRCM_BDC_NAME, + .owner = THIS_MODULE, + .pm = &bdc_pm_ops, + .of_match_table = bdc_of_match, }, .probe = bdc_probe, .remove = bdc_remove, diff --git a/drivers/usb/gadget/udc/bdc/bdc_dbg.c b/drivers/usb/gadget/udc/bdc/bdc_dbg.c index 5945dbc..ac98f6f 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_dbg.c +++ b/drivers/usb/gadget/udc/bdc/bdc_dbg.c @@ -40,28 +40,28 @@ void bdc_dump_epsts(struct bdc *bdc) { u32 temp; - temp = bdc_readl(bdc->regs, BDC_EPSTS0(0)); + temp = bdc_readl(bdc->regs, BDC_EPSTS0); dev_vdbg(bdc->dev, "BDC_EPSTS0:0x%08x\n", temp); - temp = bdc_readl(bdc->regs, BDC_EPSTS1(0)); + temp = bdc_readl(bdc->regs, BDC_EPSTS1); dev_vdbg(bdc->dev, "BDC_EPSTS1:0x%x\n", temp); - temp = bdc_readl(bdc->regs, BDC_EPSTS2(0)); + temp = bdc_readl(bdc->regs, BDC_EPSTS2); dev_vdbg(bdc->dev, "BDC_EPSTS2:0x%08x\n", temp); - temp = bdc_readl(bdc->regs, BDC_EPSTS3(0)); + temp = bdc_readl(bdc->regs, BDC_EPSTS3); dev_vdbg(bdc->dev, "BDC_EPSTS3:0x%08x\n", temp); - temp = bdc_readl(bdc->regs, BDC_EPSTS4(0)); + temp = bdc_readl(bdc->regs, BDC_EPSTS4); dev_vdbg(bdc->dev, "BDC_EPSTS4:0x%08x\n", temp); - temp = bdc_readl(bdc->regs, BDC_EPSTS5(0)); + temp = bdc_readl(bdc->regs, BDC_EPSTS5); dev_vdbg(bdc->dev, "BDC_EPSTS5:0x%08x\n", temp); - temp = bdc_readl(bdc->regs, BDC_EPSTS6(0)); + temp = bdc_readl(bdc->regs, BDC_EPSTS6); dev_vdbg(bdc->dev, "BDC_EPSTS6:0x%08x\n", temp); - temp = bdc_readl(bdc->regs, BDC_EPSTS7(0)); + temp = bdc_readl(bdc->regs, BDC_EPSTS7); dev_vdbg(bdc->dev, "BDC_EPSTS7:0x%08x\n", temp); } diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index ff1ef24..bfd8f7a 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -777,9 +777,9 @@ static int ep_dequeue(struct bdc_ep *ep, struct bdc_req *req) */ /* The current hw dequeue pointer */ - tmp_32 = bdc_readl(bdc->regs, BDC_EPSTS0(0)); + tmp_32 = bdc_readl(bdc->regs, BDC_EPSTS0); deq_ptr_64 = tmp_32; - tmp_32 = bdc_readl(bdc->regs, BDC_EPSTS1(0)); + tmp_32 = bdc_readl(bdc->regs, BDC_EPSTS1); deq_ptr_64 |= ((u64)tmp_32 << 32); /* we have the dma addr of next bd that will be fetched by hardware */ diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c index aae7458..c843461 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_udc.c +++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c @@ -249,6 +249,7 @@ void bdc_sr_uspc(struct bdc *bdc, struct bdc_sr *sreport) disconn = true; else if ((uspc & BDC_PCS) && !BDC_PST(uspc)) connected = true; + clear_flags |= BDC_PCC; } /* Change in VBus and VBus is present */ @@ -259,16 +260,16 @@ void bdc_sr_uspc(struct bdc *bdc, struct bdc_sr *sreport) bdc_softconn(bdc); usb_gadget_set_state(&bdc->gadget, USB_STATE_POWERED); } - clear_flags = BDC_VBC; + clear_flags |= BDC_VBC; } else if ((uspc & BDC_PRS) || (uspc & BDC_PRC) || disconn) { /* Hot reset, warm reset, 2.0 bus reset or disconn */ dev_dbg(bdc->dev, "Port reset or disconn\n"); bdc_uspc_disconnected(bdc, disconn); - clear_flags = BDC_PCC|BDC_PCS|BDC_PRS|BDC_PRC; + clear_flags |= BDC_PRC; } else if ((uspc & BDC_PSC) && (uspc & BDC_PCS)) { /* Change in Link state */ handle_link_state_change(bdc, uspc); - clear_flags = BDC_PSC|BDC_PCS; + clear_flags |= BDC_PSC; } /* diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index e6f04ee..75c51ca 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -812,6 +812,8 @@ int usb_gadget_map_request_by_dev(struct device *dev, dev_err(dev, "failed to map buffer\n"); return -EFAULT; } + + req->dma_mapped = 1; } return 0; @@ -836,9 +838,10 @@ void usb_gadget_unmap_request_by_dev(struct device *dev, is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); req->num_mapped_sgs = 0; - } else { + } else if (req->dma_mapped) { dma_unmap_single(dev, req->dma, req->length, is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->dma_mapped = 0; } } EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev); @@ -1130,6 +1133,7 @@ static int check_pending_gadget_drivers(struct usb_udc *udc) * @release: a gadget release function. * * Returns zero on success, negative errno otherwise. + * Calls the gadget release function in the latter case. */ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, void (*release)(struct device *dev)) @@ -1137,10 +1141,6 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, struct usb_udc *udc; int ret = -ENOMEM; - udc = kzalloc(sizeof(*udc), GFP_KERNEL); - if (!udc) - goto err1; - dev_set_name(&gadget->dev, "gadget"); INIT_WORK(&gadget->work, usb_gadget_state_work); gadget->dev.parent = parent; @@ -1150,7 +1150,13 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, else gadget->dev.release = usb_udc_nop_release; - ret = device_register(&gadget->dev); + device_initialize(&gadget->dev); + + udc = kzalloc(sizeof(*udc), GFP_KERNEL); + if (!udc) + goto err1; + + ret = device_add(&gadget->dev); if (ret) goto err2; @@ -1197,10 +1203,10 @@ err3: device_del(&gadget->dev); err2: - put_device(&gadget->dev); kfree(udc); err1: + put_device(&gadget->dev); return ret; } EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 3c37603..a030d79 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2776,7 +2776,7 @@ static int __init init(void) if (retval < 0) { i--; while (i >= 0) - platform_device_del(the_udc_pdev[i]); + platform_device_del(the_udc_pdev[i--]); goto err_add_udc; } } diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index e1de8fe..df37c1e 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -8,6 +8,7 @@ * the Free Software Foundation; version 2 of the License. */ +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/err.h> @@ -20,6 +21,8 @@ #include <linux/pm_runtime.h> #include <linux/sizes.h> #include <linux/slab.h> +#include <linux/sys_soc.h> +#include <linux/uaccess.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -347,6 +350,7 @@ struct renesas_usb3 { bool workaround_for_vbus; bool extcon_host; /* check id and set EXTCON_USB_HOST */ bool extcon_usb; /* check vbus and set EXTCON_USB */ + bool forced_b_device; }; #define gadget_to_renesas_usb3(_gadget) \ @@ -663,7 +667,9 @@ static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev) spin_lock_irqsave(&usb3->lock, flags); usb3_set_mode(usb3, host); usb3_vbus_out(usb3, a_dev); - if (!host && a_dev) /* for A-Peripheral */ + /* for A-Peripheral or forced B-device mode */ + if ((!host && a_dev) || + (usb3->workaround_for_vbus && usb3->forced_b_device)) usb3_connect(usb3); spin_unlock_irqrestore(&usb3->lock, flags); } @@ -677,7 +683,7 @@ static void usb3_check_id(struct renesas_usb3 *usb3) { usb3->extcon_host = usb3_is_a_device(usb3); - if (usb3->extcon_host) + if (usb3->extcon_host && !usb3->forced_b_device) usb3_mode_config(usb3, true, true); else usb3_mode_config(usb3, false, false); @@ -2192,7 +2198,7 @@ static void renesas_usb3_ep_fifo_flush(struct usb_ep *_ep) } } -static struct usb_ep_ops renesas_usb3_ep_ops = { +static const struct usb_ep_ops renesas_usb3_ep_ops = { .enable = renesas_usb3_ep_enable, .disable = renesas_usb3_ep_disable, @@ -2283,6 +2289,9 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr, if (!usb3->driver) return -ENODEV; + if (usb3->forced_b_device) + return -EBUSY; + if (!strncmp(buf, "host", strlen("host"))) new_mode_is_host = true; else if (!strncmp(buf, "peripheral", strlen("peripheral"))) @@ -2310,6 +2319,70 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RW(role); +static int renesas_usb3_b_device_show(struct seq_file *s, void *unused) +{ + struct renesas_usb3 *usb3 = s->private; + + seq_printf(s, "%d\n", usb3->forced_b_device); + + return 0; +} + +static int renesas_usb3_b_device_open(struct inode *inode, struct file *file) +{ + return single_open(file, renesas_usb3_b_device_show, inode->i_private); +} + +static ssize_t renesas_usb3_b_device_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct renesas_usb3 *usb3 = s->private; + char buf[32]; + + if (!usb3->driver) + return -ENODEV; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "1", 1)) + usb3->forced_b_device = true; + else + usb3->forced_b_device = false; + + /* Let this driver call usb3_connect() anyway */ + usb3_check_id(usb3); + + return count; +} + +static const struct file_operations renesas_usb3_b_device_fops = { + .open = renesas_usb3_b_device_open, + .write = renesas_usb3_b_device_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void renesas_usb3_debugfs_init(struct renesas_usb3 *usb3, + struct device *dev) +{ + struct dentry *root, *file; + + root = debugfs_create_dir(dev_name(dev), NULL); + if (IS_ERR_OR_NULL(root)) { + dev_info(dev, "%s: Can't create the root\n", __func__); + return; + } + + file = debugfs_create_file("b_device", 0644, root, usb3, + &renesas_usb3_b_device_fops); + if (!file) + dev_info(dev, "%s: Can't create debugfs mode\n", __func__); +} + /*------- platform_driver ------------------------------------------------*/ static int renesas_usb3_remove(struct platform_device *pdev) { @@ -2432,22 +2505,40 @@ static void renesas_usb3_init_ram(struct renesas_usb3 *usb3, struct device *dev, } } -static const struct renesas_usb3_priv renesas_usb3_priv_r8a7795 = { +static const struct renesas_usb3_priv renesas_usb3_priv_r8a7795_es1 = { .ramsize_per_ramif = SZ_16K, .num_ramif = 2, .ramsize_per_pipe = SZ_4K, .workaround_for_vbus = true, }; +static const struct renesas_usb3_priv renesas_usb3_priv_gen3 = { + .ramsize_per_ramif = SZ_16K, + .num_ramif = 4, + .ramsize_per_pipe = SZ_4K, +}; + static const struct of_device_id usb3_of_match[] = { { .compatible = "renesas,r8a7795-usb3-peri", - .data = &renesas_usb3_priv_r8a7795, + .data = &renesas_usb3_priv_gen3, + }, + { + .compatible = "renesas,rcar-gen3-usb3-peri", + .data = &renesas_usb3_priv_gen3, }, { }, }; MODULE_DEVICE_TABLE(of, usb3_of_match); +static const struct soc_device_attribute renesas_usb3_quirks_match[] = { + { + .soc_id = "r8a7795", .revision = "ES1.*", + .data = &renesas_usb3_priv_r8a7795_es1, + }, + { /* sentinel */ }, +}; + static const unsigned int renesas_usb3_cable[] = { EXTCON_USB, EXTCON_USB_HOST, @@ -2461,15 +2552,23 @@ static int renesas_usb3_probe(struct platform_device *pdev) const struct of_device_id *match; int irq, ret; const struct renesas_usb3_priv *priv; + const struct soc_device_attribute *attr; match = of_match_node(usb3_of_match, pdev->dev.of_node); if (!match) return -ENODEV; - priv = match->data; + + attr = soc_device_match(renesas_usb3_quirks_match); + if (attr) + priv = attr->data; + else + priv = match->data; irq = platform_get_irq(pdev, 0); - if (irq < 0) - return -ENODEV; + if (irq < 0) { + dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq); + return irq; + } usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL); if (!usb3) @@ -2527,6 +2626,8 @@ static int renesas_usb3_probe(struct platform_device *pdev) usb3->workaround_for_vbus = priv->workaround_for_vbus; + renesas_usb3_debugfs_init(usb3, &pdev->dev); + dev_info(&pdev->dev, "probed\n"); return 0; diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 0d3ebb3..088e3e6 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -500,6 +500,7 @@ static const struct dev_pm_ops mtu3_pm_ops = { static const struct of_device_id mtu3_of_match[] = { {.compatible = "mediatek,mt8173-mtu3",}, + {.compatible = "mediatek,mtu3",}, {}, }; diff --git a/drivers/usb/phy/phy-qcom-8x16-usb.c b/drivers/usb/phy/phy-qcom-8x16-usb.c index b6a83a5..679afea 100644 --- a/drivers/usb/phy/phy-qcom-8x16-usb.c +++ b/drivers/usb/phy/phy-qcom-8x16-usb.c @@ -270,12 +270,9 @@ static int phy_8x16_probe(struct platform_device *pdev) platform_set_drvdata(pdev, qphy); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - - qphy->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!qphy->regs) - return -ENOMEM; + qphy->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(qphy->regs)) + return PTR_ERR(qphy->regs); phy = &qphy->phy; phy->dev = &pdev->dev; diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 75f3127..89f4ac4 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -18,6 +18,18 @@ #include <linux/usb/phy.h> +/* Default current range by charger type. */ +#define DEFAULT_SDP_CUR_MIN 2 +#define DEFAULT_SDP_CUR_MAX 500 +#define DEFAULT_SDP_CUR_MIN_SS 150 +#define DEFAULT_SDP_CUR_MAX_SS 900 +#define DEFAULT_DCP_CUR_MIN 500 +#define DEFAULT_DCP_CUR_MAX 5000 +#define DEFAULT_CDP_CUR_MIN 1500 +#define DEFAULT_CDP_CUR_MAX 5000 +#define DEFAULT_ACA_CUR_MIN 1500 +#define DEFAULT_ACA_CUR_MAX 5000 + static LIST_HEAD(phy_list); static LIST_HEAD(phy_bind_list); static DEFINE_SPINLOCK(phy_lock); @@ -77,6 +89,221 @@ static struct usb_phy *__of_usb_find_phy(struct device_node *node) return ERR_PTR(-EPROBE_DEFER); } +static void usb_phy_set_default_current(struct usb_phy *usb_phy) +{ + usb_phy->chg_cur.sdp_min = DEFAULT_SDP_CUR_MIN; + usb_phy->chg_cur.sdp_max = DEFAULT_SDP_CUR_MAX; + usb_phy->chg_cur.dcp_min = DEFAULT_DCP_CUR_MIN; + usb_phy->chg_cur.dcp_max = DEFAULT_DCP_CUR_MAX; + usb_phy->chg_cur.cdp_min = DEFAULT_CDP_CUR_MIN; + usb_phy->chg_cur.cdp_max = DEFAULT_CDP_CUR_MAX; + usb_phy->chg_cur.aca_min = DEFAULT_ACA_CUR_MIN; + usb_phy->chg_cur.aca_max = DEFAULT_ACA_CUR_MAX; +} + +/** + * usb_phy_notify_charger_work - notify the USB charger state + * @work - the charger work to notify the USB charger state + * + * This work can be issued when USB charger state has been changed or + * USB charger current has been changed, then we can notify the current + * what can be drawn to power user and the charger state to userspace. + * + * If we get the charger type from extcon subsystem, we can notify the + * charger state to power user automatically by usb_phy_get_charger_type() + * issuing from extcon subsystem. + * + * If we get the charger type from ->charger_detect() instead of extcon + * subsystem, the usb phy driver should issue usb_phy_set_charger_state() + * to set charger state when the charger state has been changed. + */ +static void usb_phy_notify_charger_work(struct work_struct *work) +{ + struct usb_phy *usb_phy = container_of(work, struct usb_phy, chg_work); + char uchger_state[50] = { 0 }; + char *envp[] = { uchger_state, NULL }; + unsigned int min, max; + + switch (usb_phy->chg_state) { + case USB_CHARGER_PRESENT: + usb_phy_get_charger_current(usb_phy, &min, &max); + + atomic_notifier_call_chain(&usb_phy->notifier, max, usb_phy); + snprintf(uchger_state, ARRAY_SIZE(uchger_state), + "USB_CHARGER_STATE=%s", "USB_CHARGER_PRESENT"); + break; + case USB_CHARGER_ABSENT: + usb_phy_set_default_current(usb_phy); + + atomic_notifier_call_chain(&usb_phy->notifier, 0, usb_phy); + snprintf(uchger_state, ARRAY_SIZE(uchger_state), + "USB_CHARGER_STATE=%s", "USB_CHARGER_ABSENT"); + break; + default: + dev_warn(usb_phy->dev, "Unknown USB charger state: %d\n", + usb_phy->chg_state); + return; + } + + kobject_uevent_env(&usb_phy->dev->kobj, KOBJ_CHANGE, envp); +} + +static void __usb_phy_get_charger_type(struct usb_phy *usb_phy) +{ + if (extcon_get_state(usb_phy->edev, EXTCON_CHG_USB_SDP) > 0) { + usb_phy->chg_type = SDP_TYPE; + usb_phy->chg_state = USB_CHARGER_PRESENT; + } else if (extcon_get_state(usb_phy->edev, EXTCON_CHG_USB_CDP) > 0) { + usb_phy->chg_type = CDP_TYPE; + usb_phy->chg_state = USB_CHARGER_PRESENT; + } else if (extcon_get_state(usb_phy->edev, EXTCON_CHG_USB_DCP) > 0) { + usb_phy->chg_type = DCP_TYPE; + usb_phy->chg_state = USB_CHARGER_PRESENT; + } else if (extcon_get_state(usb_phy->edev, EXTCON_CHG_USB_ACA) > 0) { + usb_phy->chg_type = ACA_TYPE; + usb_phy->chg_state = USB_CHARGER_PRESENT; + } else { + usb_phy->chg_type = UNKNOWN_TYPE; + usb_phy->chg_state = USB_CHARGER_ABSENT; + } + + schedule_work(&usb_phy->chg_work); +} + +/** + * usb_phy_get_charger_type - get charger type from extcon subsystem + * @nb -the notifier block to determine charger type + * @state - the cable state + * @data - private data + * + * Determin the charger type from extcon subsystem which also means the + * charger state has been chaned, then we should notify this event. + */ +static int usb_phy_get_charger_type(struct notifier_block *nb, + unsigned long state, void *data) +{ + struct usb_phy *usb_phy = container_of(nb, struct usb_phy, type_nb); + + __usb_phy_get_charger_type(usb_phy); + return NOTIFY_OK; +} + +/** + * usb_phy_set_charger_current - set the USB charger current + * @usb_phy - the USB phy to be used + * @mA - the current need to be set + * + * Usually we only change the charger default current when USB finished the + * enumeration as one SDP charger. As one SDP charger, usb_phy_set_power() + * will issue this function to change charger current when after setting USB + * configuration, or suspend/resume USB. For other type charger, we should + * use the default charger current and we do not suggest to issue this function + * to change the charger current. + * + * When USB charger current has been changed, we need to notify the power users. + */ +void usb_phy_set_charger_current(struct usb_phy *usb_phy, unsigned int mA) +{ + switch (usb_phy->chg_type) { + case SDP_TYPE: + if (usb_phy->chg_cur.sdp_max == mA) + return; + + usb_phy->chg_cur.sdp_max = (mA > DEFAULT_SDP_CUR_MAX_SS) ? + DEFAULT_SDP_CUR_MAX_SS : mA; + break; + case DCP_TYPE: + if (usb_phy->chg_cur.dcp_max == mA) + return; + + usb_phy->chg_cur.dcp_max = (mA > DEFAULT_DCP_CUR_MAX) ? + DEFAULT_DCP_CUR_MAX : mA; + break; + case CDP_TYPE: + if (usb_phy->chg_cur.cdp_max == mA) + return; + + usb_phy->chg_cur.cdp_max = (mA > DEFAULT_CDP_CUR_MAX) ? + DEFAULT_CDP_CUR_MAX : mA; + break; + case ACA_TYPE: + if (usb_phy->chg_cur.aca_max == mA) + return; + + usb_phy->chg_cur.aca_max = (mA > DEFAULT_ACA_CUR_MAX) ? + DEFAULT_ACA_CUR_MAX : mA; + break; + default: + return; + } + + schedule_work(&usb_phy->chg_work); +} +EXPORT_SYMBOL_GPL(usb_phy_set_charger_current); + +/** + * usb_phy_get_charger_current - get the USB charger current + * @usb_phy - the USB phy to be used + * @min - the minimum current + * @max - the maximum current + * + * Usually we will notify the maximum current to power user, but for some + * special case, power user also need the minimum current value. Then the + * power user can issue this function to get the suitable current. + */ +void usb_phy_get_charger_current(struct usb_phy *usb_phy, + unsigned int *min, unsigned int *max) +{ + switch (usb_phy->chg_type) { + case SDP_TYPE: + *min = usb_phy->chg_cur.sdp_min; + *max = usb_phy->chg_cur.sdp_max; + break; + case DCP_TYPE: + *min = usb_phy->chg_cur.dcp_min; + *max = usb_phy->chg_cur.dcp_max; + break; + case CDP_TYPE: + *min = usb_phy->chg_cur.cdp_min; + *max = usb_phy->chg_cur.cdp_max; + break; + case ACA_TYPE: + *min = usb_phy->chg_cur.aca_min; + *max = usb_phy->chg_cur.aca_max; + break; + default: + *min = 0; + *max = 0; + break; + } +} +EXPORT_SYMBOL_GPL(usb_phy_get_charger_current); + +/** + * usb_phy_set_charger_state - set the USB charger state + * @usb_phy - the USB phy to be used + * @state - the new state need to be set for charger + * + * The usb phy driver can issue this function when the usb phy driver + * detected the charger state has been changed, in this case the charger + * type should be get from ->charger_detect(). + */ +void usb_phy_set_charger_state(struct usb_phy *usb_phy, + enum usb_charger_state state) +{ + if (usb_phy->chg_state == state || !usb_phy->charger_detect) + return; + + usb_phy->chg_state = state; + if (usb_phy->chg_state == USB_CHARGER_PRESENT) + usb_phy->chg_type = usb_phy->charger_detect(usb_phy); + else + usb_phy->chg_type = UNKNOWN_TYPE; + + schedule_work(&usb_phy->chg_work); +} +EXPORT_SYMBOL_GPL(usb_phy_set_charger_state); + static void devm_usb_phy_release(struct device *dev, void *res) { struct usb_phy *phy = *(struct usb_phy **)res; @@ -124,6 +351,44 @@ static int usb_add_extcon(struct usb_phy *x) "register VBUS notifier failed\n"); return ret; } + } else { + x->type_nb.notifier_call = usb_phy_get_charger_type; + + ret = devm_extcon_register_notifier(x->dev, x->edev, + EXTCON_CHG_USB_SDP, + &x->type_nb); + if (ret) { + dev_err(x->dev, + "register extcon USB SDP failed.\n"); + return ret; + } + + ret = devm_extcon_register_notifier(x->dev, x->edev, + EXTCON_CHG_USB_CDP, + &x->type_nb); + if (ret) { + dev_err(x->dev, + "register extcon USB CDP failed.\n"); + return ret; + } + + ret = devm_extcon_register_notifier(x->dev, x->edev, + EXTCON_CHG_USB_DCP, + &x->type_nb); + if (ret) { + dev_err(x->dev, + "register extcon USB DCP failed.\n"); + return ret; + } + + ret = devm_extcon_register_notifier(x->dev, x->edev, + EXTCON_CHG_USB_ACA, + &x->type_nb); + if (ret) { + dev_err(x->dev, + "register extcon USB ACA failed.\n"); + return ret; + } } if (x->id_nb.notifier_call) { @@ -145,6 +410,13 @@ static int usb_add_extcon(struct usb_phy *x) } } + usb_phy_set_default_current(x); + INIT_WORK(&x->chg_work, usb_phy_notify_charger_work); + x->chg_type = UNKNOWN_TYPE; + x->chg_state = USB_CHARGER_DEFAULT; + if (x->type_nb.notifier_call) + __usb_phy_get_charger_type(x); + return 0; } diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 2c8161b..c068b67 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -764,7 +764,7 @@ static int usbhsg_ep_set_wedge(struct usb_ep *ep) return __usbhsg_ep_set_halt_wedge(ep, 1, 1); } -static struct usb_ep_ops usbhsg_ep_ops = { +static const struct usb_ep_ops usbhsg_ep_ops = { .enable = usbhsg_ep_enable, .disable = usbhsg_ep_disable, @@ -1082,7 +1082,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) ret = -ENOMEM; goto usbhs_mod_gadget_probe_err_gpriv; } - spin_lock_init(&uep->lock); gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED); dev_info(dev, "%stransceiver found\n", @@ -1132,6 +1131,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) uep->ep.name = uep->ep_name; uep->ep.ops = &usbhsg_ep_ops; INIT_LIST_HEAD(&uep->ep.ep_list); + spin_lock_init(&uep->lock); /* init DCP */ if (usbhsg_is_dcp(uep)) { diff --git a/include/linux/hid.h b/include/linux/hid.h index 5006f9b..6519cdc 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -363,6 +363,12 @@ struct hid_item { #define HID_GROUP_LOGITECH_DJ_DEVICE 0x0102 /* + * HID protocol status + */ +#define HID_REPORT_PROTOCOL 1 +#define HID_BOOT_PROTOCOL 0 + +/* * This is the global environment of the parser. This information is * persistent for main-items. The global environment can be saved and * restored with PUSH/POP statements. diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 1a4a4ba..21468a7 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -48,6 +48,7 @@ struct usb_ep; * by adding a zero length packet as needed; * @short_not_ok: When reading data, makes short packets be * treated as errors (queue stops advancing till cleanup). + * @dma_mapped: Indicates if request has been mapped to DMA (internal) * @complete: Function called when request completes, so this request and * its buffer may be re-used. The function will always be called with * interrupts disabled, and it must not sleep. @@ -103,6 +104,7 @@ struct usb_request { unsigned no_interrupt:1; unsigned zero:1; unsigned short_not_ok:1; + unsigned dma_mapped:1; void (*complete)(struct usb_ep *ep, struct usb_request *req); diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index 2992451..de881b1 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -12,6 +12,7 @@ #include <linux/extcon.h> #include <linux/notifier.h> #include <linux/usb.h> +#include <uapi/linux/usb/charger.h> enum usb_phy_interface { USBPHY_INTERFACE_MODE_UNKNOWN, @@ -72,6 +73,17 @@ struct usb_phy_io_ops { int (*write)(struct usb_phy *x, u32 val, u32 reg); }; +struct usb_charger_current { + unsigned int sdp_min; + unsigned int sdp_max; + unsigned int dcp_min; + unsigned int dcp_max; + unsigned int cdp_min; + unsigned int cdp_max; + unsigned int aca_min; + unsigned int aca_max; +}; + struct usb_phy { struct device *dev; const char *label; @@ -91,6 +103,13 @@ struct usb_phy { struct extcon_dev *id_edev; struct notifier_block vbus_nb; struct notifier_block id_nb; + struct notifier_block type_nb; + + /* Support USB charger */ + enum usb_charger_type chg_type; + enum usb_charger_state chg_state; + struct usb_charger_current chg_cur; + struct work_struct chg_work; /* for notification of usb_phy_events */ struct atomic_notifier_head notifier; @@ -129,6 +148,12 @@ struct usb_phy { enum usb_device_speed speed); int (*notify_disconnect)(struct usb_phy *x, enum usb_device_speed speed); + + /* + * Charger detection method can be implemented if you need to + * manually detect the charger type. + */ + enum usb_charger_type (*charger_detect)(struct usb_phy *x); }; /** @@ -219,6 +244,12 @@ extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); extern int usb_bind_phy(const char *dev_name, u8 index, const char *phy_dev_name); extern void usb_phy_set_event(struct usb_phy *x, unsigned long event); +extern void usb_phy_set_charger_current(struct usb_phy *usb_phy, + unsigned int mA); +extern void usb_phy_get_charger_current(struct usb_phy *usb_phy, + unsigned int *min, unsigned int *max); +extern void usb_phy_set_charger_state(struct usb_phy *usb_phy, + enum usb_charger_state state); #else static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) { @@ -270,11 +301,29 @@ static inline int usb_bind_phy(const char *dev_name, u8 index, static inline void usb_phy_set_event(struct usb_phy *x, unsigned long event) { } + +static inline void usb_phy_set_charger_current(struct usb_phy *usb_phy, + unsigned int mA) +{ +} + +static inline void usb_phy_get_charger_current(struct usb_phy *usb_phy, + unsigned int *min, + unsigned int *max) +{ +} + +static inline void usb_phy_set_charger_state(struct usb_phy *usb_phy, + enum usb_charger_state state) +{ +} #endif static inline int usb_phy_set_power(struct usb_phy *x, unsigned mA) { + usb_phy_set_charger_current(x, mA); + if (x && x->set_power) return x->set_power(x, mA); return 0; diff --git a/include/uapi/linux/usb/charger.h b/include/uapi/linux/usb/charger.h new file mode 100644 index 0000000..5f72af3 --- /dev/null +++ b/include/uapi/linux/usb/charger.h @@ -0,0 +1,31 @@ +/* + * This file defines the USB charger type and state that are needed for + * USB device APIs. + */ + +#ifndef _UAPI__LINUX_USB_CHARGER_H +#define _UAPI__LINUX_USB_CHARGER_H + +/* + * USB charger type: + * SDP (Standard Downstream Port) + * DCP (Dedicated Charging Port) + * CDP (Charging Downstream Port) + * ACA (Accessory Charger Adapters) + */ +enum usb_charger_type { + UNKNOWN_TYPE, + SDP_TYPE, + DCP_TYPE, + CDP_TYPE, + ACA_TYPE, +}; + +/* USB charger state */ +enum usb_charger_state { + USB_CHARGER_DEFAULT, + USB_CHARGER_PRESENT, + USB_CHARGER_ABSENT, +}; + +#endif /* _UAPI__LINUX_USB_CHARGER_H */ |