Commit 34a00367 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

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.
parents a85c0f8d 0852659e
...@@ -12,3 +12,6 @@ Description: ...@@ -12,3 +12,6 @@ Description:
Ethernet over USB link Ethernet over USB link
dev_addr - MAC address of device's end of this dev_addr - MAC address of device's end of this
Ethernet over USB link 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)
...@@ -31,6 +31,7 @@ Required properties: ...@@ -31,6 +31,7 @@ Required properties:
../interrupt-controller/interrupts.txt ../interrupt-controller/interrupts.txt
Optional sub-nodes: Optional sub-nodes:
- phys : Contains a phandle to the USB PHY.
- regulators : Contains sub-nodes for each of the regulators supplied by - regulators : Contains sub-nodes for each of the regulators supplied by
the device. The regulators are bound using their names listed below: the device. The regulators are bound using their names listed below:
......
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>;
};
...@@ -12,8 +12,21 @@ Required properties: ...@@ -12,8 +12,21 @@ Required properties:
MPU. MPU.
- ranges: allows valid 1:1 translation between child's address space and - ranges: allows valid 1:1 translation between child's address space and
parent's address space. 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: Sub-nodes:
The dwc3 core should be added as subnode to Keystone DWC3 glue. The dwc3 core should be added as subnode to Keystone DWC3 glue.
......
...@@ -3,20 +3,30 @@ Renesas Electronics USB3.0 Peripheral driver ...@@ -3,20 +3,30 @@ Renesas Electronics USB3.0 Peripheral driver
Required properties: Required properties:
- compatible: Must contain one of the following: - compatible: Must contain one of the following:
- "renesas,r8a7795-usb3-peri" - "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 - reg: Base address and length of the register for the USB3.0 Peripheral
- interrupts: Interrupt specifier for the USB3.0 Peripheral - interrupts: Interrupt specifier for the USB3.0 Peripheral
- clocks: clock phandle and specifier pair - clocks: clock phandle and specifier pair
Example: Example of R-Car H3 ES1.x:
usb3_peri0: usb@ee020000 { usb3_peri0: usb@ee020000 {
compatible = "renesas,r8a7795-usb3-peri"; compatible = "renesas,r8a7795-usb3-peri",
"renesas,rcar-gen3-usb3-peri";
reg = <0 0xee020000 0 0x400>; reg = <0 0xee020000 0 0x400>;
interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 328>; clocks = <&cpg CPG_MOD 328>;
}; };
usb3_peri1: usb@ee060000 { usb3_peri1: usb@ee060000 {
compatible = "renesas,r8a7795-usb3-peri"; compatible = "renesas,r8a7795-usb3-peri",
"renesas,rcar-gen3-usb3-peri";
reg = <0 0xee060000 0 0x400>; reg = <0 0xee060000 0 0x400>;
interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 327>; clocks = <&cpg CPG_MOD 327>;
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/usb/phy.h>
#include <linux/mfd/wm831x/core.h> #include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/auxadc.h> #include <linux/mfd/wm831x/auxadc.h>
...@@ -31,6 +32,8 @@ struct wm831x_power { ...@@ -31,6 +32,8 @@ struct wm831x_power {
char usb_name[20]; char usb_name[20];
char battery_name[20]; char battery_name[20];
bool have_battery; bool have_battery;
struct usb_phy *usb_phy;
struct notifier_block usb_notify;
}; };
static int wm831x_power_check_online(struct wm831x *wm831x, int supply, static int wm831x_power_check_online(struct wm831x *wm831x, int supply,
...@@ -125,6 +128,43 @@ static enum power_supply_property wm831x_usb_props[] = { ...@@ -125,6 +128,43 @@ static enum power_supply_property wm831x_usb_props[] = {
POWER_SUPPLY_PROP_VOLTAGE_NOW, 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 * Battery properties
*********************************************************************/ *********************************************************************/
...@@ -607,6 +647,33 @@ static int wm831x_power_probe(struct platform_device *pdev) ...@@ -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; return ret;
err_bat_irq: err_bat_irq:
...@@ -637,6 +704,11 @@ static int wm831x_power_remove(struct platform_device *pdev) ...@@ -637,6 +704,11 @@ static int wm831x_power_remove(struct platform_device *pdev)
struct wm831x *wm831x = wm831x_power->wm831x; struct wm831x *wm831x = wm831x_power->wm831x;
int irq, i; 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++) { for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
irq = wm831x_irq(wm831x, irq = wm831x_irq(wm831x,
platform_get_irq_byname(pdev, platform_get_irq_byname(pdev,
......
...@@ -4179,7 +4179,7 @@ static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value) ...@@ -4179,7 +4179,7 @@ static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value)
return ret; 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, .enable = dwc2_hsotg_ep_enable,
.disable = dwc2_hsotg_ep_disable, .disable = dwc2_hsotg_ep_disable,
.alloc_request = dwc2_hsotg_ep_alloc_request, .alloc_request = dwc2_hsotg_ep_alloc_request,
......
...@@ -4388,6 +4388,9 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) ...@@ -4388,6 +4388,9 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
spin_lock_irqsave(&hsotg->lock, flags); spin_lock_irqsave(&hsotg->lock, flags);
if (dwc2_is_device_mode(hsotg))
goto unlock;
if (hsotg->lx_state != DWC2_L0) if (hsotg->lx_state != DWC2_L0)
goto unlock; goto unlock;
...@@ -4446,6 +4449,9 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) ...@@ -4446,6 +4449,9 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
spin_lock_irqsave(&hsotg->lock, flags); spin_lock_irqsave(&hsotg->lock, flags);
if (dwc2_is_device_mode(hsotg))
goto unlock;
if (hsotg->lx_state != DWC2_L2) if (hsotg->lx_state != DWC2_L2)
goto unlock; goto unlock;
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include <linux/clk.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -23,6 +22,7 @@ ...@@ -23,6 +22,7 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/pm_runtime.h>
/* USBSS register offsets */ /* USBSS register offsets */
#define USBSS_REVISION 0x0000 #define USBSS_REVISION 0x0000
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
struct dwc3_keystone { struct dwc3_keystone {
struct device *dev; struct device *dev;
struct clk *clk;
void __iomem *usbss; void __iomem *usbss;
}; };
...@@ -106,17 +105,13 @@ static int kdwc3_probe(struct platform_device *pdev) ...@@ -106,17 +105,13 @@ static int kdwc3_probe(struct platform_device *pdev)
if (IS_ERR(kdwc->usbss)) if (IS_ERR(kdwc->usbss))
return PTR_ERR(kdwc->usbss); return PTR_ERR(kdwc->usbss);
kdwc->clk = devm_clk_get(kdwc->dev, "usb"); pm_runtime_enable(kdwc->dev);
if (IS_ERR(kdwc->clk)) {
dev_err(kdwc->dev, "unable to get usb clock\n");
return PTR_ERR(kdwc->clk);
}
error = clk_prepare_enable(kdwc->clk); error = pm_runtime_get_sync(kdwc->dev);
if (error < 0) { 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); error);
return error; goto err_irq;
} }
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
...@@ -147,7 +142,8 @@ static int kdwc3_probe(struct platform_device *pdev) ...@@ -147,7 +142,8 @@ static int kdwc3_probe(struct platform_device *pdev)
err_core: err_core:
kdwc3_disable_irqs(kdwc); kdwc3_disable_irqs(kdwc);
err_irq: err_irq:
clk_disable_unprepare(kdwc->clk); pm_runtime_put_sync(kdwc->dev);
pm_runtime_disable(kdwc->dev);
return error; return error;
} }
...@@ -167,7 +163,9 @@ static int kdwc3_remove(struct platform_device *pdev) ...@@ -167,7 +163,9 @@ static int kdwc3_remove(struct platform_device *pdev)
kdwc3_disable_irqs(kdwc); kdwc3_disable_irqs(kdwc);
device_for_each_child(&pdev->dev, NULL, kdwc3_remove_core); 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); platform_set_drvdata(pdev, NULL);
return 0; return 0;
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
...@@ -96,7 +95,8 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) ...@@ -96,7 +95,8 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, simple); platform_set_drvdata(pdev, simple);
simple->dev = dev; 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) if (ret)
return ret; return ret;
......
...@@ -478,8 +478,8 @@ static int dwc3_omap_probe(struct platform_device *pdev) ...@@ -478,8 +478,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) { if (irq < 0) {
dev_err(dev, "missing IRQ resource\n"); dev_err(dev, "missing IRQ resource: %d\n", irq);
return -EINVAL; return irq;
} }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
......
...@@ -130,7 +130,7 @@ config USB_GADGET_STORAGE_NUM_BUFFERS ...@@ -130,7 +130,7 @@ config USB_GADGET_STORAGE_NUM_BUFFERS
config U_SERIAL_CONSOLE config U_SERIAL_CONSOLE
bool "Serial gadget console support" bool "Serial gadget console support"
depends on USB_G_SERIAL depends on USB_U_SERIAL
help help
It supports the serial gadget can be used as a console. It supports the serial gadget can be used as a console.
......
...@@ -961,10 +961,9 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) ...@@ -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. */ /* In the meantime, endpoint got disabled or changed. */
ret = -ESHUTDOWN; ret = -ESHUTDOWN;
} else if (halt) { } else if (halt) {
/* Halt */ ret = usb_ep_set_halt(ep->ep);
if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep)) if (!ret)
usb_ep_set_halt(ep->ep); ret = -EBADMSG;
ret = -EBADMSG;
} else if (unlikely(data_len == -EINVAL)) { } else if (unlikely(data_len == -EINVAL)) {
/* /*
* Sanity Check: even though data_len can't be used * Sanity Check: even though data_len can't be used
......
...@@ -44,6 +44,7 @@ struct f_hidg { ...@@ -44,6 +44,7 @@ struct f_hidg {
/* configuration */ /* configuration */
unsigned char bInterfaceSubClass; unsigned char bInterfaceSubClass;
unsigned char bInterfaceProtocol; unsigned char bInterfaceProtocol;
unsigned char protocol;
unsigned short report_desc_length; unsigned short report_desc_length;
char *report_desc; char *report_desc;
unsigned short report_length; unsigned short report_length;
...@@ -527,7 +528,9 @@ static int hidg_setup(struct usb_function *f, ...@@ -527,7 +528,9 @@ static int hidg_setup(struct usb_function *f,
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_GET_PROTOCOL): | HID_REQ_GET_PROTOCOL):
VDBG(cdev, "get_protocol\n"); VDBG(cdev, "get_protocol\n");
goto stall; length = min_t(unsigned int, length, 1);
((u8 *) req->buf)[0] = hidg->protocol;
goto respond;
break; break;
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
...@@ -539,6 +542,17 @@ static int hidg_setup(struct usb_function *f, ...@@ -539,6 +542,17 @@ static int hidg_setup(struct usb_function *f,
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_SET_PROTOCOL): | HID_REQ_SET_PROTOCOL):
VDBG(cdev, "set_protocol\n"); 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; goto stall;
break; break;
...@@ -768,6 +782,7 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -768,6 +782,7 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
/* set descriptor dynamic values */ /* set descriptor dynamic values */
hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass; hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; 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_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
hidg_ss_in_comp_desc.wBytesPerInterval = hidg_ss_in_comp_desc.wBytesPerInterval =
cpu_to_le16(hidg->report_length); cpu_to_le16(hidg->report_length);
......
...@@ -98,6 +98,7 @@ struct f_midi { ...@@ -98,6 +98,7 @@ struct f_midi {
DECLARE_KFIFO_PTR(in_req_fifo, struct usb_request *); DECLARE_KFIFO_PTR(in_req_fifo, struct usb_request *);
spinlock_t transmit_lock; spinlock_t transmit_lock;
unsigned int in_last_port; unsigned int in_last_port;
unsigned char free_ref;
struct gmidi_in_port in_ports_array[/* in_ports */]; 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) ...@@ -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_transmit(struct f_midi *midi);
static void f_midi_rmidi_free(struct snd_rawmidi *rmidi);
DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
...@@ -163,6 +165,13 @@ static struct usb_endpoint_descriptor bulk_out_desc = { ...@@ -163,6 +165,13 @@ static struct usb_endpoint_descriptor bulk_out_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK, .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 */ /* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */
static struct usb_ms_endpoint_descriptor_16 ms_out_desc = { static struct usb_ms_endpoint_descriptor_16 ms_out_desc = {
/* .bLength = DYNAMIC */ /* .bLength = DYNAMIC */
...@@ -180,6 +189,13 @@ static struct usb_endpoint_descriptor bulk_in_desc = { ...@@ -180,6 +189,13 @@ static struct usb_endpoint_descriptor bulk_in_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK, .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 */ /* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */
static struct usb_ms_endpoint_descriptor_16 ms_in_desc = { static struct usb_ms_endpoint_descriptor_16 ms_in_desc = {
/* .bLength = DYNAMIC */ /* .bLength = DYNAMIC */
...@@ -755,13 +771,13 @@ static void f_midi_out_trigger(struct snd_rawmidi_substream *substream, int up) ...@@ -755,13 +771,13 @@ static void f_midi_out_trigger(struct snd_rawmidi_substream *substream, int up)
clear_bit(substream->number, &midi->out_triggered); 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, .open = f_midi_in_open,
.close = f_midi_in_close, .close = f_midi_in_close,
.trigger = f_midi_in_trigger, .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, .open = f_midi_out_open,
.close = f_midi_out_close, .close = f_midi_out_close,
.trigger = f_midi_out_trigger .trigger = f_midi_out_trigger
...@@ -818,6 +834,8 @@ static int f_midi_register_card(struct f_midi *midi) ...@@ -818,6 +834,8 @@ static int f_midi_register_card(struct f_midi *midi)
SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX; SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = midi; rmidi->private_data = midi;
rmidi->private_free = f_midi_rmidi_free;
midi->free_ref++;
/* /*
* Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT. * 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) ...@@ -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 usb_composite_dev *cdev = c->cdev;
struct f_midi *midi = func_to_midi(f); struct f_midi *midi = func_to_midi(f);
struct usb_string *us; 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; midi->gadget = cdev->gadget;
tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi); 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) ...@@ -895,7 +913,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
goto fail; goto fail;
/* allocate temporary function list */ /* 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); GFP_KERNEL);
if (!midi_function) { if (!midi_function) {
status = -ENOMEM; status = -ENOMEM;
...@@ -985,6 +1003,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -985,6 +1003,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
ms_in_desc.bNumEmbMIDIJack = midi->out_ports; ms_in_desc.bNumEmbMIDIJack = midi->out_ports;
/* ... and add them to the list */ /* ... 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 *) &bulk_out_desc;
midi_function[i++] = (struct usb_descriptor_header *) &ms_out_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_desc;
...@@ -1009,13 +1028,34 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -1009,13 +1028,34 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
goto fail_f_midi; 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); kfree(midi_function);
return 0; return 0;
fail_f_midi: fail_f_midi:
kfree(midi_function); kfree(midi_function);
usb_free_descriptors(f->hs_descriptors); usb_free_all_descriptors(f);
fail: fail:
f_midi_unregister_card(midi); f_midi_unregister_card(midi);
fail_register: fail_register:
...@@ -1197,14 +1237,21 @@ static void f_midi_free(struct usb_function *f) ...@@ -1197,14 +1237,21 @@ static void f_midi_free(struct usb_function *f)
midi = func_to_midi(f); midi = func_to_midi(f);
opts = container_of(f->fi, struct f_midi_opts, func_inst); opts = container_of(f->fi, struct f_midi_opts, func_inst);
kfree(midi->id);
mutex_lock(&opts->lock); mutex_lock(&opts->lock);
kfifo_free(&midi->in_req_fifo); if (!--midi->free_ref) {
kfree(midi); kfree(midi->id);
--opts->refcnt; kfifo_free(&midi->in_req_fifo);
kfree(midi);
--opts->refcnt;
}
mutex_unlock(&opts->lock); 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) static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
{ {
struct usb_composite_dev *cdev = f->config->cdev; 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) ...@@ -1219,7 +1266,7 @@ static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
card = midi->card; card = midi->card;
midi->card = NULL; midi->card = NULL;
if (card) if (card)
snd_card_free(card); snd_card_free_when_closed(card);
usb_free_all_descriptors(f); usb_free_all_descriptors(f);
} }
...@@ -1263,6 +1310,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) ...@@ -1263,6 +1310,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
midi->buflen = opts->buflen; midi->buflen = opts->buflen;
midi->qlen = opts->qlen; midi->qlen = opts->qlen;
midi->in_last_port = 0; midi->in_last_port = 0;
midi->free_ref = 1;
status = kfifo_alloc(&midi->in_req_fifo, midi->qlen, GFP_KERNEL); status = kfifo_alloc(&midi->in_req_fifo, midi->qlen, GFP_KERNEL);
if (status) if (status)
......
...@@ -925,8 +925,6 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ...@@ -925,8 +925,6 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
*/ */
ncm->port.is_zlp_ok = ncm->port.is_zlp_ok =
gadget_is_zlp_supported(cdev->gadget); gadget_is_zlp_supported(cdev->gadget);
ncm->port.no_skb_reserve =
gadget_avoids_skb_reserve(cdev->gadget);
ncm->port.cdc_filter = DEFAULT_FILTER; ncm->port.cdc_filter = DEFAULT_FILTER;
DBG(cdev, "activate ncm\n"); DBG(cdev, "activate ncm\n");
net = gether_connect(&ncm->port); net = gether_connect(&ncm->port);
......
...@@ -691,6 +691,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) ...@@ -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; 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() * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
* configurations are bound in sequence with list_for_each_entry, * configurations are bound in sequence with list_for_each_entry,
...@@ -866,11 +870,23 @@ USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(rndis); ...@@ -866,11 +870,23 @@ USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(rndis);
/* f_rndis_opts_ifname */ /* f_rndis_opts_ifname */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(rndis); 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[] = { static struct configfs_attribute *rndis_attrs[] = {
&rndis_opts_attr_dev_addr, &rndis_opts_attr_dev_addr,
&rndis_opts_attr_host_addr, &rndis_opts_attr_host_addr,
&rndis_opts_attr_qmult, &rndis_opts_attr_qmult,
&rndis_opts_attr_ifname, &rndis_opts_attr_ifname,
&rndis_opts_attr_class,
&rndis_opts_attr_subclass,
&rndis_opts_attr_protocol,
NULL, NULL,
}; };
...@@ -916,6 +932,10 @@ static struct usb_function_instance *rndis_alloc_inst(void) ...@@ -916,6 +932,10 @@ static struct usb_function_instance *rndis_alloc_inst(void)
} }
INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop); 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; descs[0] = &opts->rndis_os_desc;
names[0] = "rndis"; names[0] = "rndis";
config_group_init_type_name(&opts->func_inst.group, "", config_group_init_type_name(&opts->func_inst.group, "",
......
...@@ -1073,7 +1073,7 @@ struct net_device *gether_connect(struct gether *link) ...@@ -1073,7 +1073,7 @@ struct net_device *gether_connect(struct gether *link)
if (result == 0) { if (result == 0) {
dev->zlp = link->is_zlp_ok; 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)); DBG(dev, "qlen %d\n", qlen(dev->gadget, dev->qmult));
dev->header_len = link->header_len; dev->header_len = link->header_len;
......
...@@ -64,7 +64,6 @@ struct gether { ...@@ -64,7 +64,6 @@ struct gether {
struct usb_ep *out_ep; struct usb_ep *out_ep;
bool is_zlp_ok; bool is_zlp_ok;
bool no_skb_reserve;
u16 cdc_filter; u16 cdc_filter;
......
...@@ -153,4 +153,39 @@ out: \ ...@@ -153,4 +153,39 @@ out: \
\ \
CONFIGFS_ATTR_RO(_f_##_opts_, ifname) 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 */ #endif /* __U_ETHER_CONFIGFS_H */
...@@ -29,6 +29,10 @@ struct f_rndis_opts { ...@@ -29,6 +29,10 @@ struct f_rndis_opts {
struct usb_os_desc rndis_os_desc; struct usb_os_desc rndis_os_desc;
char rndis_ext_compat_id[16]; char rndis_ext_compat_id[16];
u8 class;
u8 subclass;
u8 protocol;
/* /*
* Read/write access to configfs attributes is handled by configfs. * Read/write access to configfs attributes is handled by configfs.
* *
......
...@@ -537,7 +537,7 @@ static void gs_rx_push(unsigned long _port) ...@@ -537,7 +537,7 @@ static void gs_rx_push(unsigned long _port)
} }
/* push data to (open) tty */ /* push data to (open) tty */
if (req->actual) { if (req->actual && tty) {
char *packet = req->buf; char *packet = req->buf;
unsigned size = req->actual; unsigned size = req->actual;
unsigned n; unsigned n;
......
config USB_BDC_UDC config USB_BDC_UDC
tristate "Broadcom USB3.0 device controller IP driver(BDC)" tristate "Broadcom USB3.0 device controller IP driver(BDC)"
depends on USB_GADGET && HAS_DMA depends on USB_GADGET && HAS_DMA
default ARCH_BRCMSTB
help help
BDC is Broadcom's USB3.0 device controller IP. If your SOC has a BDC IP BDC is Broadcom's USB3.0 device controller IP. If your SOC has a BDC IP
......
...@@ -27,8 +27,8 @@ ...@@ -27,8 +27,8 @@
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#define BRCM_BDC_NAME "bdc_usb3" #define BRCM_BDC_NAME "bdc"
#define BRCM_BDC_DESC "BDC device controller driver" #define BRCM_BDC_DESC "Broadcom USB Device Controller driver"
#define DMA_ADDR_INVALID (~(dma_addr_t)0) #define DMA_ADDR_INVALID (~(dma_addr_t)0)
...@@ -83,14 +83,14 @@ ...@@ -83,14 +83,14 @@
#define BDC_DVCSA 0x50 #define BDC_DVCSA 0x50
#define BDC_DVCSB 0x54 #define BDC_DVCSB 0x54
#define BDC_EPSTS0(n) (0x60 + (n * 0x10)) #define BDC_EPSTS0 0x60
#define BDC_EPSTS1(n) (0x64 + (n * 0x10)) #define BDC_EPSTS1 0x64
#define BDC_EPSTS2(n) (0x68 + (n * 0x10)) #define BDC_EPSTS2 0x68
#define BDC_EPSTS3(n) (0x6c + (n * 0x10)) #define BDC_EPSTS3 0x6c
#define BDC_EPSTS4(n) (0x70 + (n * 0x10)) #define BDC_EPSTS4 0x70
#define BDC_EPSTS5(n) (0x74 + (n * 0x10)) #define BDC_EPSTS5 0x74
#define BDC_EPSTS6(n) (0x78 + (n * 0x10)) #define BDC_EPSTS6 0x78
#define BDC_EPSTS7(n) (0x7c + (n * 0x10)) #define BDC_EPSTS7 0x7c
#define BDC_SRRBAL(n) (0x200 + (n * 0x10)) #define BDC_SRRBAL(n) (0x200 + (n * 0x10))
#define BDC_SRRBAH(n) (0x204 + (n * 0x10)) #define BDC_SRRBAH(n) (0x204 + (n * 0x10))
#define BDC_SRRINT(n) (0x208 + (n * 0x10)) #define BDC_SRRINT(n) (0x208 + (n * 0x10))
...@@ -413,6 +413,9 @@ struct bdc { ...@@ -413,6 +413,9 @@ struct bdc {
/* device lock */ /* device lock */
spinlock_t lock; spinlock_t lock;
/* generic phy */
struct phy **phys;
int num_phys;
/* num of endpoints for a particular instantiation of IP */ /* num of endpoints for a particular instantiation of IP */
unsigned int num_eps; unsigned int num_eps;
/* /*
...@@ -454,6 +457,7 @@ struct bdc { ...@@ -454,6 +457,7 @@ struct bdc {
* Func Wake packet every 2.5 secs. Refer to USB3 spec section 8.5.6.4 * Func Wake packet every 2.5 secs. Refer to USB3 spec section 8.5.6.4
*/ */
struct delayed_work func_wake_notify; struct delayed_work func_wake_notify;
struct clk *clk;
}; };
static inline u32 bdc_readl(void __iomem *base, u32 offset) static inline u32 bdc_readl(void __iomem *base, u32 offset)
......
...@@ -24,9 +24,11 @@ ...@@ -24,9 +24,11 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/dmapool.h> #include <linux/dmapool.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/clk.h>
#include "bdc.h" #include "bdc.h"
#include "bdc_dbg.h" #include "bdc_dbg.h"
...@@ -444,6 +446,43 @@ static int bdc_hw_init(struct bdc *bdc) ...@@ -444,6 +446,43 @@ static int bdc_hw_init(struct bdc *bdc)
return 0; 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) static int bdc_probe(struct platform_device *pdev)
{ {
struct bdc *bdc; struct bdc *bdc;
...@@ -452,12 +491,29 @@ static int bdc_probe(struct platform_device *pdev) ...@@ -452,12 +491,29 @@ static int bdc_probe(struct platform_device *pdev)
int irq; int irq;
u32 temp; u32 temp;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct clk *clk;
int phy_num;
dev_dbg(dev, "%s()\n", __func__); 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); bdc = devm_kzalloc(dev, sizeof(*bdc), GFP_KERNEL);
if (!bdc) if (!bdc)
return -ENOMEM; return -ENOMEM;
bdc->clk = clk;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
bdc->regs = devm_ioremap_resource(dev, res); bdc->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(bdc->regs)) { if (IS_ERR(bdc->regs)) {
...@@ -473,35 +529,66 @@ static int bdc_probe(struct platform_device *pdev) ...@@ -473,35 +529,66 @@ static int bdc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bdc); platform_set_drvdata(pdev, bdc);
bdc->irq = irq; bdc->irq = irq;
bdc->dev = dev; 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); temp = bdc_readl(bdc->regs, BDC_BDCCAP1);
if ((temp & BDC_P64) && if ((temp & BDC_P64) &&
!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) { !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 { } 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) { 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; return -ENOTSUPP;
} }
dev_dbg(bdc->dev, "Using 32-bit address\n"); dev_dbg(dev, "Using 32-bit address\n");
} }
ret = bdc_hw_init(bdc); ret = bdc_hw_init(bdc);
if (ret) { if (ret) {
dev_err(bdc->dev, "BDC init failure:%d\n", ret); dev_err(dev, "BDC init failure:%d\n", ret);
return ret; goto phycleanup;
} }
ret = bdc_udc_init(bdc); ret = bdc_udc_init(bdc);
if (ret) { 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; goto cleanup;
} }
return 0; return 0;
cleanup: cleanup:
bdc_hw_exit(bdc); bdc_hw_exit(bdc);
phycleanup:
bdc_phy_exit(bdc);
return ret; return ret;
} }
...@@ -513,13 +600,56 @@ static int bdc_remove(struct platform_device *pdev) ...@@ -513,13 +600,56 @@ static int bdc_remove(struct platform_device *pdev)
dev_dbg(bdc->dev, "%s ()\n", __func__); dev_dbg(bdc->dev, "%s ()\n", __func__);
bdc_udc_exit(bdc); bdc_udc_exit(bdc);
bdc_hw_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; 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 = { static struct platform_driver bdc_driver = {
.driver = { .driver = {
.name = BRCM_BDC_NAME, .name = BRCM_BDC_NAME,
.owner = THIS_MODULE,
.pm = &bdc_pm_ops,
.of_match_table = bdc_of_match,
}, },
.probe = bdc_probe, .probe = bdc_probe,
.remove = bdc_remove, .remove = bdc_remove,
......
...@@ -40,28 +40,28 @@ void bdc_dump_epsts(struct bdc *bdc) ...@@ -40,28 +40,28 @@ void bdc_dump_epsts(struct bdc *bdc)
{ {
u32 temp; 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); 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); 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); 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); 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); 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); 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); 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); dev_vdbg(bdc->dev, "BDC_EPSTS7:0x%08x\n", temp);
} }
......
...@@ -777,9 +777,9 @@ static int ep_dequeue(struct bdc_ep *ep, struct bdc_req *req) ...@@ -777,9 +777,9 @@ static int ep_dequeue(struct bdc_ep *ep, struct bdc_req *req)
*/ */
/* The current hw dequeue pointer */ /* 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; 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); deq_ptr_64 |= ((u64)tmp_32 << 32);
/* we have the dma addr of next bd that will be fetched by hardware */ /* we have the dma addr of next bd that will be fetched by hardware */
......
...@@ -249,6 +249,7 @@ void bdc_sr_uspc(struct bdc *bdc, struct bdc_sr *sreport) ...@@ -249,6 +249,7 @@ void bdc_sr_uspc(struct bdc *bdc, struct bdc_sr *sreport)
disconn = true; disconn = true;
else if ((uspc & BDC_PCS) && !BDC_PST(uspc)) else if ((uspc & BDC_PCS) && !BDC_PST(uspc))
connected = true; connected = true;
clear_flags |= BDC_PCC;
} }
/* Change in VBus and VBus is present */ /* Change in VBus and VBus is present */
...@@ -259,16 +260,16 @@ void bdc_sr_uspc(struct bdc *bdc, struct bdc_sr *sreport) ...@@ -259,16 +260,16 @@ void bdc_sr_uspc(struct bdc *bdc, struct bdc_sr *sreport)
bdc_softconn(bdc); bdc_softconn(bdc);
usb_gadget_set_state(&bdc->gadget, USB_STATE_POWERED); 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) { } else if ((uspc & BDC_PRS) || (uspc & BDC_PRC) || disconn) {
/* Hot reset, warm reset, 2.0 bus reset or disconn */ /* Hot reset, warm reset, 2.0 bus reset or disconn */
dev_dbg(bdc->dev, "Port reset or disconn\n"); dev_dbg(bdc->dev, "Port reset or disconn\n");
bdc_uspc_disconnected(bdc, disconn); 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)) { } else if ((uspc & BDC_PSC) && (uspc & BDC_PCS)) {
/* Change in Link state */ /* Change in Link state */
handle_link_state_change(bdc, uspc); handle_link_state_change(bdc, uspc);
clear_flags = BDC_PSC|BDC_PCS; clear_flags |= BDC_PSC;
} }
/* /*
......
...@@ -812,6 +812,8 @@ int usb_gadget_map_request_by_dev(struct device *dev, ...@@ -812,6 +812,8 @@ int usb_gadget_map_request_by_dev(struct device *dev,
dev_err(dev, "failed to map buffer\n"); dev_err(dev, "failed to map buffer\n");
return -EFAULT; return -EFAULT;
} }
req->dma_mapped = 1;
} }
return 0; return 0;
...@@ -836,9 +838,10 @@ void usb_gadget_unmap_request_by_dev(struct device *dev, ...@@ -836,9 +838,10 @@ void usb_gadget_unmap_request_by_dev(struct device *dev,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
req->num_mapped_sgs = 0; req->num_mapped_sgs = 0;
} else { } else if (req->dma_mapped) {
dma_unmap_single(dev, req->dma, req->length, dma_unmap_single(dev, req->dma, req->length,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
req->dma_mapped = 0;
} }
} }
EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev); EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev);
...@@ -1130,6 +1133,7 @@ static int check_pending_gadget_drivers(struct usb_udc *udc) ...@@ -1130,6 +1133,7 @@ static int check_pending_gadget_drivers(struct usb_udc *udc)
* @release: a gadget release function. * @release: a gadget release function.
* *
* Returns zero on success, negative errno otherwise. * 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, int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
void (*release)(struct device *dev)) void (*release)(struct device *dev))
...@@ -1137,10 +1141,6 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, ...@@ -1137,10 +1141,6 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
struct usb_udc *udc; struct usb_udc *udc;
int ret = -ENOMEM; int ret = -ENOMEM;
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
if (!udc)
goto err1;
dev_set_name(&gadget->dev, "gadget"); dev_set_name(&gadget->dev, "gadget");
INIT_WORK(&gadget->work, usb_gadget_state_work); INIT_WORK(&gadget->work, usb_gadget_state_work);
gadget->dev.parent = parent; gadget->dev.parent = parent;
...@@ -1150,7 +1150,13 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, ...@@ -1150,7 +1150,13 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
else else
gadget->dev.release = usb_udc_nop_release; 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) if (ret)
goto err2; goto err2;
...@@ -1197,10 +1203,10 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, ...@@ -1197,10 +1203,10 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
device_del(&gadget->dev); device_del(&gadget->dev);
err2: err2:
put_device(&gadget->dev);
kfree(udc); kfree(udc);
err1: err1:
put_device(&gadget->dev);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
......
...@@ -2776,7 +2776,7 @@ static int __init init(void) ...@@ -2776,7 +2776,7 @@ static int __init init(void)
if (retval < 0) { if (retval < 0) {
i--; i--;
while (i >= 0) while (i >= 0)
platform_device_del(the_udc_pdev[i]); platform_device_del(the_udc_pdev[i--]);
goto err_add_udc; goto err_add_udc;
} }
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* the Free Software Foundation; version 2 of the License. * the Free Software Foundation; version 2 of the License.
*/ */
#include <linux/debugfs.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/err.h> #include <linux/err.h>
...@@ -20,6 +21,8 @@ ...@@ -20,6 +21,8 @@
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/sizes.h> #include <linux/sizes.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/uaccess.h>
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
...@@ -347,6 +350,7 @@ struct renesas_usb3 { ...@@ -347,6 +350,7 @@ struct renesas_usb3 {
bool workaround_for_vbus; bool workaround_for_vbus;
bool extcon_host; /* check id and set EXTCON_USB_HOST */ bool extcon_host; /* check id and set EXTCON_USB_HOST */
bool extcon_usb; /* check vbus and set EXTCON_USB */ bool extcon_usb; /* check vbus and set EXTCON_USB */
bool forced_b_device;
}; };
#define gadget_to_renesas_usb3(_gadget) \ #define gadget_to_renesas_usb3(_gadget) \
...@@ -663,7 +667,9 @@ static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev) ...@@ -663,7 +667,9 @@ static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev)
spin_lock_irqsave(&usb3->lock, flags); spin_lock_irqsave(&usb3->lock, flags);
usb3_set_mode(usb3, host); usb3_set_mode(usb3, host);
usb3_vbus_out(usb3, a_dev); 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); usb3_connect(usb3);
spin_unlock_irqrestore(&usb3->lock, flags); spin_unlock_irqrestore(&usb3->lock, flags);
} }
...@@ -677,7 +683,7 @@ static void usb3_check_id(struct renesas_usb3 *usb3) ...@@ -677,7 +683,7 @@ static void usb3_check_id(struct renesas_usb3 *usb3)
{ {
usb3->extcon_host = usb3_is_a_device(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); usb3_mode_config(usb3, true, true);
else else
usb3_mode_config(usb3, false, false); usb3_mode_config(usb3, false, false);
...@@ -2192,7 +2198,7 @@ static void renesas_usb3_ep_fifo_flush(struct usb_ep *_ep) ...@@ -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, .enable = renesas_usb3_ep_enable,
.disable = renesas_usb3_ep_disable, .disable = renesas_usb3_ep_disable,
...@@ -2283,6 +2289,9 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr, ...@@ -2283,6 +2289,9 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr,
if (!usb3->driver) if (!usb3->driver)
return -ENODEV; return -ENODEV;
if (usb3->forced_b_device)
return -EBUSY;
if (!strncmp(buf, "host", strlen("host"))) if (!strncmp(buf, "host", strlen("host")))
new_mode_is_host = true; new_mode_is_host = true;
else if (!strncmp(buf, "peripheral", strlen("peripheral"))) else if (!strncmp(buf, "peripheral", strlen("peripheral")))
...@@ -2310,6 +2319,70 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr, ...@@ -2310,6 +2319,70 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr,
} }
static DEVICE_ATTR_RW(role); 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 ------------------------------------------------*/ /*------- platform_driver ------------------------------------------------*/
static int renesas_usb3_remove(struct platform_device *pdev) 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, ...@@ -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, .ramsize_per_ramif = SZ_16K,
.num_ramif = 2, .num_ramif = 2,
.ramsize_per_pipe = SZ_4K, .ramsize_per_pipe = SZ_4K,
.workaround_for_vbus = true, .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[] = { static const struct of_device_id usb3_of_match[] = {
{ {
.compatible = "renesas,r8a7795-usb3-peri", .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); 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[] = { static const unsigned int renesas_usb3_cable[] = {
EXTCON_USB, EXTCON_USB,
EXTCON_USB_HOST, EXTCON_USB_HOST,
...@@ -2461,15 +2552,23 @@ static int renesas_usb3_probe(struct platform_device *pdev) ...@@ -2461,15 +2552,23 @@ static int renesas_usb3_probe(struct platform_device *pdev)
const struct of_device_id *match; const struct of_device_id *match;
int irq, ret; int irq, ret;
const struct renesas_usb3_priv *priv; const struct renesas_usb3_priv *priv;
const struct soc_device_attribute *attr;
match = of_match_node(usb3_of_match, pdev->dev.of_node); match = of_match_node(usb3_of_match, pdev->dev.of_node);
if (!match) if (!match)
return -ENODEV; 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); irq = platform_get_irq(pdev, 0);
if (irq < 0) if (irq < 0) {
return -ENODEV; dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
return irq;
}
usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL); usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL);
if (!usb3) if (!usb3)
...@@ -2527,6 +2626,8 @@ static int renesas_usb3_probe(struct platform_device *pdev) ...@@ -2527,6 +2626,8 @@ static int renesas_usb3_probe(struct platform_device *pdev)
usb3->workaround_for_vbus = priv->workaround_for_vbus; usb3->workaround_for_vbus = priv->workaround_for_vbus;
renesas_usb3_debugfs_init(usb3, &pdev->dev);
dev_info(&pdev->dev, "probed\n"); dev_info(&pdev->dev, "probed\n");
return 0; return 0;
......
...@@ -500,6 +500,7 @@ static const struct dev_pm_ops mtu3_pm_ops = { ...@@ -500,6 +500,7 @@ static const struct dev_pm_ops mtu3_pm_ops = {
static const struct of_device_id mtu3_of_match[] = { static const struct of_device_id mtu3_of_match[] = {
{.compatible = "mediatek,mt8173-mtu3",}, {.compatible = "mediatek,mt8173-mtu3",},
{.compatible = "mediatek,mtu3",},
{}, {},
}; };
......
...@@ -270,12 +270,9 @@ static int phy_8x16_probe(struct platform_device *pdev) ...@@ -270,12 +270,9 @@ static int phy_8x16_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, qphy); platform_set_drvdata(pdev, qphy);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) qphy->regs = devm_ioremap_resource(&pdev->dev, res);
return -EINVAL; if (IS_ERR(qphy->regs))
return PTR_ERR(qphy->regs);
qphy->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!qphy->regs)
return -ENOMEM;
phy = &qphy->phy; phy = &qphy->phy;
phy->dev = &pdev->dev; phy->dev = &pdev->dev;
......
...@@ -18,6 +18,18 @@ ...@@ -18,6 +18,18 @@
#include <linux/usb/phy.h> #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_list);
static LIST_HEAD(phy_bind_list); static LIST_HEAD(phy_bind_list);
static DEFINE_SPINLOCK(phy_lock); static DEFINE_SPINLOCK(phy_lock);
...@@ -77,6 +89,221 @@ static struct usb_phy *__of_usb_find_phy(struct device_node *node) ...@@ -77,6 +89,221 @@ static struct usb_phy *__of_usb_find_phy(struct device_node *node)
return ERR_PTR(-EPROBE_DEFER); 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) static void devm_usb_phy_release(struct device *dev, void *res)
{ {
struct usb_phy *phy = *(struct usb_phy **)res; struct usb_phy *phy = *(struct usb_phy **)res;
...@@ -124,6 +351,44 @@ static int usb_add_extcon(struct usb_phy *x) ...@@ -124,6 +351,44 @@ static int usb_add_extcon(struct usb_phy *x)
"register VBUS notifier failed\n"); "register VBUS notifier failed\n");
return ret; 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) { if (x->id_nb.notifier_call) {
...@@ -145,6 +410,13 @@ static int usb_add_extcon(struct usb_phy *x) ...@@ -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; return 0;
} }
......
...@@ -764,7 +764,7 @@ static int usbhsg_ep_set_wedge(struct usb_ep *ep) ...@@ -764,7 +764,7 @@ static int usbhsg_ep_set_wedge(struct usb_ep *ep)
return __usbhsg_ep_set_halt_wedge(ep, 1, 1); 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, .enable = usbhsg_ep_enable,
.disable = usbhsg_ep_disable, .disable = usbhsg_ep_disable,
...@@ -1082,7 +1082,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) ...@@ -1082,7 +1082,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
ret = -ENOMEM; ret = -ENOMEM;
goto usbhs_mod_gadget_probe_err_gpriv; goto usbhs_mod_gadget_probe_err_gpriv;
} }
spin_lock_init(&uep->lock);
gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED); gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED);
dev_info(dev, "%stransceiver found\n", dev_info(dev, "%stransceiver found\n",
...@@ -1132,6 +1131,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) ...@@ -1132,6 +1131,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
uep->ep.name = uep->ep_name; uep->ep.name = uep->ep_name;
uep->ep.ops = &usbhsg_ep_ops; uep->ep.ops = &usbhsg_ep_ops;
INIT_LIST_HEAD(&uep->ep.ep_list); INIT_LIST_HEAD(&uep->ep.ep_list);
spin_lock_init(&uep->lock);
/* init DCP */ /* init DCP */
if (usbhsg_is_dcp(uep)) { if (usbhsg_is_dcp(uep)) {
......
...@@ -362,6 +362,12 @@ struct hid_item { ...@@ -362,6 +362,12 @@ struct hid_item {
#define HID_GROUP_WACOM 0x0101 #define HID_GROUP_WACOM 0x0101
#define HID_GROUP_LOGITECH_DJ_DEVICE 0x0102 #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 * This is the global environment of the parser. This information is
* persistent for main-items. The global environment can be saved and * persistent for main-items. The global environment can be saved and
......
...@@ -48,6 +48,7 @@ struct usb_ep; ...@@ -48,6 +48,7 @@ struct usb_ep;
* by adding a zero length packet as needed; * by adding a zero length packet as needed;
* @short_not_ok: When reading data, makes short packets be * @short_not_ok: When reading data, makes short packets be
* treated as errors (queue stops advancing till cleanup). * 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 * @complete: Function called when request completes, so this request and
* its buffer may be re-used. The function will always be called with * its buffer may be re-used. The function will always be called with
* interrupts disabled, and it must not sleep. * interrupts disabled, and it must not sleep.
...@@ -103,6 +104,7 @@ struct usb_request { ...@@ -103,6 +104,7 @@ struct usb_request {
unsigned no_interrupt:1; unsigned no_interrupt:1;
unsigned zero:1; unsigned zero:1;
unsigned short_not_ok:1; unsigned short_not_ok:1;
unsigned dma_mapped:1;
void (*complete)(struct usb_ep *ep, void (*complete)(struct usb_ep *ep,
struct usb_request *req); struct usb_request *req);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/extcon.h> #include <linux/extcon.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <uapi/linux/usb/charger.h>
enum usb_phy_interface { enum usb_phy_interface {
USBPHY_INTERFACE_MODE_UNKNOWN, USBPHY_INTERFACE_MODE_UNKNOWN,
...@@ -72,6 +73,17 @@ struct usb_phy_io_ops { ...@@ -72,6 +73,17 @@ struct usb_phy_io_ops {
int (*write)(struct usb_phy *x, u32 val, u32 reg); 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 usb_phy {
struct device *dev; struct device *dev;
const char *label; const char *label;
...@@ -91,6 +103,13 @@ struct usb_phy { ...@@ -91,6 +103,13 @@ struct usb_phy {
struct extcon_dev *id_edev; struct extcon_dev *id_edev;
struct notifier_block vbus_nb; struct notifier_block vbus_nb;
struct notifier_block id_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 */ /* for notification of usb_phy_events */
struct atomic_notifier_head notifier; struct atomic_notifier_head notifier;
...@@ -129,6 +148,12 @@ struct usb_phy { ...@@ -129,6 +148,12 @@ struct usb_phy {
enum usb_device_speed speed); enum usb_device_speed speed);
int (*notify_disconnect)(struct usb_phy *x, int (*notify_disconnect)(struct usb_phy *x,
enum usb_device_speed speed); 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); ...@@ -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, extern int usb_bind_phy(const char *dev_name, u8 index,
const char *phy_dev_name); const char *phy_dev_name);
extern void usb_phy_set_event(struct usb_phy *x, unsigned long event); 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 #else
static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) 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, ...@@ -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_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 #endif
static inline int static inline int
usb_phy_set_power(struct usb_phy *x, unsigned mA) usb_phy_set_power(struct usb_phy *x, unsigned mA)
{ {
usb_phy_set_charger_current(x, mA);
if (x && x->set_power) if (x && x->set_power)
return x->set_power(x, mA); return x->set_power(x, mA);
return 0; return 0;
......
/*
* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment