From 87841c887b7b78742d9f8e5da890ed4af21dd978 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 16 Apr 2015 21:07:05 +0800 Subject: usb: gadget: uvc: remove unused including Remove including that don't need it. Signed-off-by: Wei Yongjun Acked-by: Laurent Pinchart Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index ebe409b..7d3bb62 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -56,7 +56,6 @@ struct uvc_event #include #include #include -#include #include #include -- cgit v0.10.2 From 2d9c7f3ca54625f4d740af78f84ee232da3ca937 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 14 Apr 2015 04:10:04 +0000 Subject: usb: renesas_usbhs: tidyup usbhs_for_each_dfifo macro Current usbhs_for_each_dfifo macro will read out-of-array's memory after last loop operation. It was not good C language operation, and the binary which was compiled by (at least) gcc 4.8.1 is broken. This patch is based on 925403f425a4a9c503f2fc295652647b1eb10d82 (usb: renesas_usbhs: tidyup original usbhsx_for_each_xxx macro) Signed-off-by: Kuninori Morimoto Tested-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 04d3f8a..c7d9b86 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -44,10 +44,11 @@ struct usbhs_fifo_info { struct usbhs_fifo dfifo[USBHS_MAX_NUM_DFIFO]; }; #define usbhsf_get_dnfifo(p, n) (&((p)->fifo_info.dfifo[n])) -#define usbhs_for_each_dfifo(priv, dfifo, i) \ - for ((i) = 0, dfifo = usbhsf_get_dnfifo(priv, (i)); \ - ((i) < USBHS_MAX_NUM_DFIFO); \ - (i)++, dfifo = usbhsf_get_dnfifo(priv, (i))) +#define usbhs_for_each_dfifo(priv, dfifo, i) \ + for ((i) = 0; \ + ((i) < USBHS_MAX_NUM_DFIFO) && \ + ((dfifo) = usbhsf_get_dnfifo(priv, (i))); \ + (i)++) struct usbhs_pkt_handle; struct usbhs_pkt { -- cgit v0.10.2 From d72348fb5ca634583bf3f79996cc4b3ef91d9c3a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 23 Apr 2015 16:06:50 +0200 Subject: usb: musb: fix inefficient copy of unaligned buffers Make sure only to copy any actual data rather than the whole buffer, when releasing the temporary buffer used for unaligned non-isochronous transfers. Signed-off-by: Johan Hovold Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index c3d5fc9..e1fb5d8 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2512,6 +2512,7 @@ static void musb_free_temp_buffer(struct urb *urb) { enum dma_data_direction dir; struct musb_temp_buffer *temp; + size_t length; if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) return; @@ -2522,8 +2523,12 @@ static void musb_free_temp_buffer(struct urb *urb) data); if (dir == DMA_FROM_DEVICE) { - memcpy(temp->old_xfer_buffer, temp->data, - urb->transfer_buffer_length); + if (usb_pipeisoc(urb->pipe)) + length = urb->transfer_buffer_length; + else + length = urb->actual_length; + + memcpy(temp->old_xfer_buffer, temp->data, length); } urb->transfer_buffer = temp->old_xfer_buffer; kfree(temp->kmalloc_ptr); -- cgit v0.10.2 From 205845ef70dd01094415bceafbc86a97eb1899a9 Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Tue, 24 Mar 2015 15:09:25 -0500 Subject: usb: musb: only set test mode once The MUSB test mode register can only be set once, otherwise the result is undefined. This prevents the debugfs testmode entry to set the register more than once which causes test failure. Signed-off-by: Bin Liu Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c index 78a283e..04382ec 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -191,9 +191,16 @@ static ssize_t musb_test_mode_write(struct file *file, { struct seq_file *s = file->private_data; struct musb *musb = s->private; - u8 test = 0; + u8 test; char buf[18]; + test = musb_readb(musb->mregs, MUSB_TESTMODE); + if (test) { + dev_err(musb->controller, "Error: test mode is already set. " + "Please do USB Bus Reset to start a new test.\n"); + return count; + } + memset(buf, 0x00, sizeof(buf)); if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) -- cgit v0.10.2 From 4e39acab03de8db6881dfd981407b6d3ff62c62d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 27 Mar 2015 16:32:18 +0800 Subject: usb: Documentation: gadget-testing: fix parameter for capture channel mask Fix the UAC2 parameter capture channel mask Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index f45b2bf..7769eee 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -629,7 +629,7 @@ Function-specific configfs interface The function name to use when creating the function directory is "uac2". The uac2 function provides these attributes in its function directory: - chmask - capture channel mask + c_chmask - capture channel mask c_srate - capture sampling rate c_ssize - capture sample size (bytes) p_chmask - playback channel mask -- cgit v0.10.2 From 5a294e5469891d0701183049c4a9678887fa7091 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 8 Apr 2015 19:42:24 +0900 Subject: usb: renesas_usbhs: Revise the binding document about the dma-names Since the DT should describe the hardware (not the driver limitation), This patch revises the binding document about the dma-names to change simple numbering as "ch%d" instead of "tx" and "rx". Also this patch fixes the actual code of renesas_usbhs driver to handle the new dma-names. Signed-off-by: Yoshihiro Shimoda Acked-by: Mark Rutland Acked-by: Geert Uytterhoeven Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt index dc2a18f..ddbe304 100644 --- a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt +++ b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt @@ -15,10 +15,8 @@ Optional properties: - phys: phandle + phy specifier pair - phy-names: must be "usb" - dmas: Must contain a list of references to DMA specifiers. - - dma-names : Must contain a list of DMA names: - - tx0 ... tx - - rx0 ... rx - - This means DnFIFO in USBHS module. + - dma-names : named "ch%d", where %d is the channel number ranging from zero + to the number of channels (DnFIFOs) minus one. Example: usbhs: usb@e6590000 { diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 8597cf9..bc23b4a 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -1227,15 +1227,21 @@ static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo, { char name[16]; - snprintf(name, sizeof(name), "tx%d", channel); - fifo->tx_chan = dma_request_slave_channel_reason(dev, name); - if (IS_ERR(fifo->tx_chan)) - fifo->tx_chan = NULL; - - snprintf(name, sizeof(name), "rx%d", channel); - fifo->rx_chan = dma_request_slave_channel_reason(dev, name); - if (IS_ERR(fifo->rx_chan)) - fifo->rx_chan = NULL; + /* + * To avoid complex handing for DnFIFOs, the driver uses each + * DnFIFO as TX or RX direction (not bi-direction). + * So, the driver uses odd channels for TX, even channels for RX. + */ + snprintf(name, sizeof(name), "ch%d", channel); + if (channel & 1) { + fifo->tx_chan = dma_request_slave_channel_reason(dev, name); + if (IS_ERR(fifo->tx_chan)) + fifo->tx_chan = NULL; + } else { + fifo->rx_chan = dma_request_slave_channel_reason(dev, name); + if (IS_ERR(fifo->rx_chan)) + fifo->rx_chan = NULL; + } } static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo, -- cgit v0.10.2 From 591fc116f3302da915bb57d4474a61a5e8884cec Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Thu, 9 Apr 2015 11:34:22 +0300 Subject: usb: phy: msm: Use extcon framework for VBUS and ID detection On recent Qualcomm platforms VBUS and ID lines are not routed to USB PHY LINK controller. Use extcon framework to receive connect and disconnect ID and VBUS notification. Signed-off-by: Ivan T. Ivanov Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt index 2826f2a..f26bcfa 100644 --- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt @@ -69,6 +69,13 @@ Optional properties: (no, min, max) where each value represents either a voltage in microvolts or a value corresponding to voltage corner. +- extcon: phandles to external connector devices. First phandle + should point to external connector, which provide "USB" + cable events, the second should point to external connector + device, which provide "USB-HOST" cable events. If one of + the external connector devices is not required empty <0> + phandle should be specified. + Example HSUSB OTG controller device node: usb@f9a55000 { diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 2175678..811f331 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -141,6 +141,7 @@ config USB_MSM_OTG tristate "Qualcomm on-chip USB OTG controller support" depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST) depends on RESET_CONTROLLER + depends on EXTCON select USB_PHY help Enable this to support the USB OTG transceiver on Qualcomm chips. It diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index c9156be..ad66c67 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1436,9 +1436,42 @@ static const struct of_device_id msm_otg_dt_match[] = { }; MODULE_DEVICE_TABLE(of, msm_otg_dt_match); +static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct msm_usb_cable *vbus = container_of(nb, struct msm_usb_cable, nb); + struct msm_otg *motg = container_of(vbus, struct msm_otg, vbus); + + if (event) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + + schedule_work(&motg->sm_work); + + return NOTIFY_DONE; +} + +static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct msm_usb_cable *id = container_of(nb, struct msm_usb_cable, nb); + struct msm_otg *motg = container_of(id, struct msm_otg, id); + + if (event) + clear_bit(ID, &motg->inputs); + else + set_bit(ID, &motg->inputs); + + schedule_work(&motg->sm_work); + + return NOTIFY_DONE; +} + static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) { struct msm_otg_platform_data *pdata; + struct extcon_dev *ext_id, *ext_vbus; const struct of_device_id *id; struct device_node *node = pdev->dev.of_node; struct property *prop; @@ -1487,6 +1520,52 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX]; } + ext_id = ERR_PTR(-ENODEV); + ext_vbus = ERR_PTR(-ENODEV); + if (of_property_read_bool(node, "extcon")) { + + /* Each one of them is not mandatory */ + ext_vbus = extcon_get_edev_by_phandle(&pdev->dev, 0); + if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) + return PTR_ERR(ext_vbus); + + ext_id = extcon_get_edev_by_phandle(&pdev->dev, 1); + if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) + return PTR_ERR(ext_id); + } + + if (!IS_ERR(ext_vbus)) { + motg->vbus.nb.notifier_call = msm_otg_vbus_notifier; + ret = extcon_register_interest(&motg->vbus.conn, ext_vbus->name, + "USB", &motg->vbus.nb); + if (ret < 0) { + dev_err(&pdev->dev, "register VBUS notifier failed\n"); + return ret; + } + + ret = extcon_get_cable_state(ext_vbus, "USB"); + if (ret) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + } + + if (!IS_ERR(ext_id)) { + motg->id.nb.notifier_call = msm_otg_id_notifier; + ret = extcon_register_interest(&motg->id.conn, ext_id->name, + "USB-HOST", &motg->id.nb); + if (ret < 0) { + dev_err(&pdev->dev, "register ID notifier failed\n"); + return ret; + } + + ret = extcon_get_cable_state(ext_id, "USB-HOST"); + if (ret) + clear_bit(ID, &motg->inputs); + else + set_bit(ID, &motg->inputs); + } + prop = of_find_property(node, "qcom,phy-init-sequence", &len); if (!prop || !len) return 0; @@ -1700,6 +1779,11 @@ static int msm_otg_remove(struct platform_device *pdev) if (phy->otg->host || phy->otg->gadget) return -EBUSY; + if (motg->id.conn.edev) + extcon_unregister_interest(&motg->id.conn); + if (motg->vbus.conn.edev) + extcon_unregister_interest(&motg->vbus.conn); + msm_otg_debugfs_cleanup(); cancel_delayed_work_sync(&motg->chg_work); cancel_work_sync(&motg->sm_work); diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 7dbecf9..c4d956e 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -18,6 +18,7 @@ #ifndef __ASM_ARCH_MSM_HSUSB_H #define __ASM_ARCH_MSM_HSUSB_H +#include #include #include #include @@ -120,6 +121,17 @@ struct msm_otg_platform_data { }; /** + * struct msm_usb_cable - structure for exteternal connector cable + * state tracking + * @nb: hold event notification callback + * @conn: used for notification registration + */ +struct msm_usb_cable { + struct notifier_block nb; + struct extcon_specific_cable_nb conn; +}; + +/** * struct msm_otg: OTG driver data. Shared by HCD and DCD. * @otg: USB OTG Transceiver structure. * @pdata: otg device platform data. @@ -138,6 +150,8 @@ struct msm_otg_platform_data { * @chg_type: The type of charger attached. * @dcd_retires: The retry count used to track Data contact * detection process. + * @vbus: VBUS signal state trakining, using extcon framework + * @id: ID signal state trakining, using extcon framework */ struct msm_otg { struct usb_phy phy; @@ -166,6 +180,9 @@ struct msm_otg { struct reset_control *phy_rst; struct reset_control *link_rst; int vdd_levels[3]; + + struct msm_usb_cable vbus; + struct msm_usb_cable id; }; #endif -- cgit v0.10.2 From 44e42ae3a398b559c768b9b3c324d72b0b0b4479 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Thu, 9 Apr 2015 11:34:33 +0300 Subject: usb: phy: msm: Manual PHY and LINK controller VBUS change notification VBUS is not routed to USB PHY on recent Qualcomm platforms. USB controller must see VBUS in order to pull-up DP when setting RS bit. Henc configure USB PHY and LINK registers sense VBUS and enable manual pullup on D+ line. Cc: Vamsi Krishna Cc: Mayank Rana Signed-off-by: Ivan T. Ivanov Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt index f26bcfa..bd8d9e7 100644 --- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt @@ -69,6 +69,10 @@ Optional properties: (no, min, max) where each value represents either a voltage in microvolts or a value corresponding to voltage corner. +- qcom,manual-pullup: If present, vbus is not routed to USB controller/phy + and controller driver therefore enables pull-up explicitly + before starting controller using usbcmd run/stop bit. + - extcon: phandles to external connector devices. First phandle should point to external connector, which provide "USB" cable events, the second should point to external connector diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index ad66c67..00c49bb 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -240,8 +240,14 @@ static void ulpi_init(struct msm_otg *motg) static int msm_phy_notify_disconnect(struct usb_phy *phy, enum usb_device_speed speed) { + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); int val; + if (motg->manual_pullup) { + val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL; + usb_phy_io_write(phy, val, ULPI_CLR(ULPI_MISC_A)); + } + /* * Put the transceiver in non-driving mode. Otherwise host * may not detect soft-disconnection. @@ -422,6 +428,24 @@ static int msm_phy_init(struct usb_phy *phy) ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); } + if (motg->manual_pullup) { + val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT; + ulpi_write(phy, val, ULPI_SET(ULPI_MISC_A)); + + val = readl(USB_GENCONFIG_2); + val |= GENCONFIG_2_SESS_VLD_CTRL_EN; + writel(val, USB_GENCONFIG_2); + + val = readl(USB_USBCMD); + val |= USBCMD_SESS_VLD_CTRL; + writel(val, USB_USBCMD); + + val = ulpi_read(phy, ULPI_FUNC_CTRL); + val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + val |= ULPI_FUNC_CTRL_OPMODE_NORMAL; + ulpi_write(phy, val, ULPI_FUNC_CTRL); + } + if (motg->phy_number) writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); @@ -1520,6 +1544,8 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX]; } + motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup"); + ext_id = ERR_PTR(-ENODEV); ext_vbus = ERR_PTR(-ENODEV); if (of_property_read_bool(node, "extcon")) { diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index c4d956e..e55a150 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -150,6 +150,9 @@ struct msm_usb_cable { * @chg_type: The type of charger attached. * @dcd_retires: The retry count used to track Data contact * detection process. + * @manual_pullup: true if VBUS is not routed to USB controller/phy + * and controller driver therefore enables pull-up explicitly before + * starting controller using usbcmd run/stop bit. * @vbus: VBUS signal state trakining, using extcon framework * @id: ID signal state trakining, using extcon framework */ @@ -181,6 +184,8 @@ struct msm_otg { struct reset_control *link_rst; int vdd_levels[3]; + bool manual_pullup; + struct msm_usb_cable vbus; struct msm_usb_cable id; }; diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index a29f603..e159b39 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -21,6 +21,8 @@ #define USB_AHBBURST (MSM_USB_BASE + 0x0090) #define USB_AHBMODE (MSM_USB_BASE + 0x0098) +#define USB_GENCONFIG_2 (MSM_USB_BASE + 0x00a0) + #define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */ #define USB_USBCMD (MSM_USB_BASE + 0x0140) @@ -30,6 +32,9 @@ #define USB_PHY_CTRL (MSM_USB_BASE + 0x0240) #define USB_PHY_CTRL2 (MSM_USB_BASE + 0x0278) +#define GENCONFIG_2_SESS_VLD_CTRL_EN BIT(7) +#define USBCMD_SESS_VLD_CTRL BIT(25) + #define USBCMD_RESET 2 #define USB_USBINTR (MSM_USB_BASE + 0x0148) @@ -50,6 +55,10 @@ #define ULPI_PWR_CLK_MNG_REG 0x88 #define OTG_COMP_DISABLE BIT(0) +#define ULPI_MISC_A 0x96 +#define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1) +#define ULPI_MISC_A_VBUSVLDEXT BIT(0) + #define ASYNC_INTR_CTRL (1 << 29) /* Enable async interrupt */ #define ULPI_STP_CTRL (1 << 30) /* Block communication with PHY */ #define PHY_RETEN (1 << 1) /* PHY retention enable/disable */ -- cgit v0.10.2 From b189a2117223edbe40e0a187ae5c606cbdd6447c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 28 Apr 2015 14:04:07 +0200 Subject: usb: phy: Remove the phy-rcar-gen2-usb driver The phy-rcar-gen2-usb driver, which supports legacy platform data only, is no longer used since commit a483dcbfa21f919c ("ARM: shmobile: lager: Remove legacy board support"). This driver was superseded by the DT-only phy-rcar-gen2 driver, which was introduced in commit 1233f59f745b237d ("phy: Renesas R-Car Gen2 PHY driver"). Signed-off-by: Geert Uytterhoeven Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 811f331..173a5b5 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -187,19 +187,6 @@ config USB_RCAR_PHY To compile this driver as a module, choose M here: the module will be called phy-rcar-usb. -config USB_RCAR_GEN2_PHY - tristate "Renesas R-Car Gen2 USB PHY support" - depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST - select USB_PHY - help - Say Y here to add support for the Renesas R-Car Gen2 USB PHY driver. - It is typically used to control internal USB PHY for USBHS, - and to configure shared USB channels 0 and 2. - This driver supports R8A7790 and R8A7791. - - To compile this driver as a module, choose M here: the - module will be called phy-rcar-gen2-usb. - config USB_ULPI bool "Generic ULPI Transceiver Driver" depends on ARM || ARM64 diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 75f2bba..e36ab1d 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -23,7 +23,6 @@ obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o -obj-$(CONFIG_USB_RCAR_GEN2_PHY) += phy-rcar-gen2-usb.o obj-$(CONFIG_USB_ULPI) += phy-ulpi.o obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c deleted file mode 100644 index f81800b..0000000 --- a/drivers/usb/phy/phy-rcar-gen2-usb.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Renesas R-Car Gen2 USB phy driver - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Copyright (C) 2013 Cogent Embedded, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -struct rcar_gen2_usb_phy_priv { - struct usb_phy phy; - void __iomem *base; - struct clk *clk; - spinlock_t lock; - int usecount; - u32 ugctrl2; -}; - -#define usb_phy_to_priv(p) container_of(p, struct rcar_gen2_usb_phy_priv, phy) - -/* Low Power Status register */ -#define USBHS_LPSTS_REG 0x02 -#define USBHS_LPSTS_SUSPM (1 << 14) - -/* USB General control register */ -#define USBHS_UGCTRL_REG 0x80 -#define USBHS_UGCTRL_CONNECT (1 << 2) -#define USBHS_UGCTRL_PLLRESET (1 << 0) - -/* USB General control register 2 */ -#define USBHS_UGCTRL2_REG 0x84 -#define USBHS_UGCTRL2_USB0_PCI (1 << 4) -#define USBHS_UGCTRL2_USB0_HS (3 << 4) -#define USBHS_UGCTRL2_USB2_PCI (0 << 31) -#define USBHS_UGCTRL2_USB2_SS (1 << 31) - -/* USB General status register */ -#define USBHS_UGSTS_REG 0x88 -#define USBHS_UGSTS_LOCK (1 << 8) - -/* Enable USBHS internal phy */ -static int __rcar_gen2_usbhs_phy_enable(void __iomem *base) -{ - u32 val; - int i; - - /* USBHS PHY power on */ - val = ioread32(base + USBHS_UGCTRL_REG); - val &= ~USBHS_UGCTRL_PLLRESET; - iowrite32(val, base + USBHS_UGCTRL_REG); - - val = ioread16(base + USBHS_LPSTS_REG); - val |= USBHS_LPSTS_SUSPM; - iowrite16(val, base + USBHS_LPSTS_REG); - - for (i = 0; i < 20; i++) { - val = ioread32(base + USBHS_UGSTS_REG); - if ((val & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) { - val = ioread32(base + USBHS_UGCTRL_REG); - val |= USBHS_UGCTRL_CONNECT; - iowrite32(val, base + USBHS_UGCTRL_REG); - return 0; - } - udelay(1); - } - - /* Timed out waiting for the PLL lock */ - return -ETIMEDOUT; -} - -/* Disable USBHS internal phy */ -static int __rcar_gen2_usbhs_phy_disable(void __iomem *base) -{ - u32 val; - - /* USBHS PHY power off */ - val = ioread32(base + USBHS_UGCTRL_REG); - val &= ~USBHS_UGCTRL_CONNECT; - iowrite32(val, base + USBHS_UGCTRL_REG); - - val = ioread16(base + USBHS_LPSTS_REG); - val &= ~USBHS_LPSTS_SUSPM; - iowrite16(val, base + USBHS_LPSTS_REG); - - val = ioread32(base + USBHS_UGCTRL_REG); - val |= USBHS_UGCTRL_PLLRESET; - iowrite32(val, base + USBHS_UGCTRL_REG); - return 0; -} - -/* Setup USB channels */ -static void __rcar_gen2_usb_phy_init(struct rcar_gen2_usb_phy_priv *priv) -{ - u32 val; - - clk_prepare_enable(priv->clk); - - /* Set USB channels in the USBHS UGCTRL2 register */ - val = ioread32(priv->base + USBHS_UGCTRL2_REG); - val &= ~(USBHS_UGCTRL2_USB0_HS | USBHS_UGCTRL2_USB2_SS); - val |= priv->ugctrl2; - iowrite32(val, priv->base + USBHS_UGCTRL2_REG); -} - -/* Shutdown USB channels */ -static void __rcar_gen2_usb_phy_shutdown(struct rcar_gen2_usb_phy_priv *priv) -{ - __rcar_gen2_usbhs_phy_disable(priv->base); - clk_disable_unprepare(priv->clk); -} - -static int rcar_gen2_usb_phy_set_suspend(struct usb_phy *phy, int suspend) -{ - struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); - unsigned long flags; - int retval; - - spin_lock_irqsave(&priv->lock, flags); - retval = suspend ? __rcar_gen2_usbhs_phy_disable(priv->base) : - __rcar_gen2_usbhs_phy_enable(priv->base); - spin_unlock_irqrestore(&priv->lock, flags); - return retval; -} - -static int rcar_gen2_usb_phy_init(struct usb_phy *phy) -{ - struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - /* - * Enable the clock and setup USB channels - * if it's the first user - */ - if (!priv->usecount++) - __rcar_gen2_usb_phy_init(priv); - spin_unlock_irqrestore(&priv->lock, flags); - return 0; -} - -static void rcar_gen2_usb_phy_shutdown(struct usb_phy *phy) -{ - struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (!priv->usecount) { - dev_warn(phy->dev, "Trying to disable phy with 0 usecount\n"); - goto out; - } - - /* Disable everything if it's the last user */ - if (!--priv->usecount) - __rcar_gen2_usb_phy_shutdown(priv); -out: - spin_unlock_irqrestore(&priv->lock, flags); -} - -static int rcar_gen2_usb_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rcar_gen2_phy_platform_data *pdata; - struct rcar_gen2_usb_phy_priv *priv; - struct resource *res; - void __iomem *base; - struct clk *clk; - int retval; - - pdata = dev_get_platdata(dev); - if (!pdata) { - dev_err(dev, "No platform data\n"); - return -EINVAL; - } - - clk = devm_clk_get(dev, "usbhs"); - if (IS_ERR(clk)) { - dev_err(dev, "Can't get the clock\n"); - return PTR_ERR(clk); - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - spin_lock_init(&priv->lock); - priv->clk = clk; - priv->base = base; - priv->ugctrl2 = pdata->chan0_pci ? - USBHS_UGCTRL2_USB0_PCI : USBHS_UGCTRL2_USB0_HS; - priv->ugctrl2 |= pdata->chan2_pci ? - USBHS_UGCTRL2_USB2_PCI : USBHS_UGCTRL2_USB2_SS; - priv->phy.dev = dev; - priv->phy.label = dev_name(dev); - priv->phy.init = rcar_gen2_usb_phy_init; - priv->phy.shutdown = rcar_gen2_usb_phy_shutdown; - priv->phy.set_suspend = rcar_gen2_usb_phy_set_suspend; - - retval = usb_add_phy_dev(&priv->phy); - if (retval < 0) { - dev_err(dev, "Failed to add USB phy\n"); - return retval; - } - - platform_set_drvdata(pdev, priv); - - return retval; -} - -static int rcar_gen2_usb_phy_remove(struct platform_device *pdev) -{ - struct rcar_gen2_usb_phy_priv *priv = platform_get_drvdata(pdev); - - usb_remove_phy(&priv->phy); - - return 0; -} - -static struct platform_driver rcar_gen2_usb_phy_driver = { - .driver = { - .name = "usb_phy_rcar_gen2", - }, - .probe = rcar_gen2_usb_phy_probe, - .remove = rcar_gen2_usb_phy_remove, -}; - -module_platform_driver(rcar_gen2_usb_phy_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Renesas R-Car Gen2 USB phy"); -MODULE_AUTHOR("Valentine Barshak "); diff --git a/include/linux/platform_data/usb-rcar-gen2-phy.h b/include/linux/platform_data/usb-rcar-gen2-phy.h deleted file mode 100644 index dd3ba46..0000000 --- a/include/linux/platform_data/usb-rcar-gen2-phy.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2013 Renesas Solutions Corp. - * Copyright (C) 2013 Cogent Embedded, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __USB_RCAR_GEN2_PHY_H -#define __USB_RCAR_GEN2_PHY_H - -#include - -struct rcar_gen2_phy_platform_data { - /* USB channel 0 configuration */ - bool chan0_pci:1; /* true: PCI USB host 0, false: USBHS */ - /* USB channel 2 configuration */ - bool chan2_pci:1; /* true: PCI USB host 2, false: USBSS */ -}; - -#endif -- cgit v0.10.2 From f91eea447ac32ddc24eaf1cafeb3830b44b6ceeb Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Wed, 29 Apr 2015 22:08:59 +0200 Subject: usb: dwc2: move debugfs code to a separate file Prepare to add more debug code. Moreover, don't save dentry * for each file in struct dwc2_hsotg as clean up is done with debugfs_remove_recursive(). s3c_hsotg_delete_debug() is removed altogether for the same reason. Acked-by: John Youn Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile index f07b425..324fbb4 100644 --- a/drivers/usb/dwc2/Makefile +++ b/drivers/usb/dwc2/Makefile @@ -13,6 +13,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC2_PERIPHERAL) $(CONFIG_USB_DWC2_DUAL_ROLE)),) dwc2-y += gadget.o endif +ifneq ($(CONFIG_DEBUG_FS),) + dwc2-y += debugfs.o +endif + # NOTE: The previous s3c-hsotg peripheral mode only driver has been moved to # this location and renamed gadget.c. When building for dynamically linked # modules, dwc2.ko will get built for host mode, peripheral mode, and dual-role diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 836c012..3695c6f 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -615,9 +615,6 @@ struct dwc2_hsotg { enum dwc2_lx_state lx_state; struct dentry *debug_root; - struct dentry *debug_file; - struct dentry *debug_testmode; - struct dentry *debug_fifo; /* DWC OTG HW Release versions */ #define DWC2_CORE_REV_2_71a 0x4f54271a @@ -1005,6 +1002,7 @@ extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset); extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg); extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2); +extern int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode); #else static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } @@ -1018,6 +1016,9 @@ static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset) {} static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {} static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {} +static inline int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, + int testmode) +{ return 0; } #endif #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) diff --git a/drivers/usb/dwc2/debug.h b/drivers/usb/dwc2/debug.h new file mode 100644 index 0000000..12dbd1d --- /dev/null +++ b/drivers/usb/dwc2/debug.h @@ -0,0 +1,27 @@ +/** + * debug.h - Designware USB2 DRD controller debug header + * + * Copyright (C) 2015 Intel Corporation + * Mian Yousaf Kaukab + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "core.h" + +#ifdef CONFIG_DEBUG_FS +extern int dwc2_debugfs_init(struct dwc2_hsotg *); +extern void dwc2_debugfs_exit(struct dwc2_hsotg *); +#else +static inline int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg) +{ } +#endif diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c new file mode 100644 index 0000000..e57e554 --- /dev/null +++ b/drivers/usb/dwc2/debugfs.c @@ -0,0 +1,416 @@ +/** + * debugfs.c - Designware USB2 DRD controller debugfs + * + * Copyright (C) 2015 Intel Corporation + * Mian Yousaf Kaukab + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "core.h" +#include "debug.h" + +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ + IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) +/** + * testmode_write - debugfs: change usb test mode + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry modify the current usb test mode. + */ +static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t + count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct dwc2_hsotg *hsotg = s->private; + unsigned long flags; + u32 testmode = 0; + char buf[32]; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "test_j", 6)) + testmode = TEST_J; + else if (!strncmp(buf, "test_k", 6)) + testmode = TEST_K; + else if (!strncmp(buf, "test_se0_nak", 12)) + testmode = TEST_SE0_NAK; + else if (!strncmp(buf, "test_packet", 11)) + testmode = TEST_PACKET; + else if (!strncmp(buf, "test_force_enable", 17)) + testmode = TEST_FORCE_EN; + else + testmode = 0; + + spin_lock_irqsave(&hsotg->lock, flags); + s3c_hsotg_set_test_mode(hsotg, testmode); + spin_unlock_irqrestore(&hsotg->lock, flags); + return count; +} + +/** + * testmode_show - debugfs: show usb test mode state + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry shows which usb test mode is currently enabled. + */ +static int testmode_show(struct seq_file *s, void *unused) +{ + struct dwc2_hsotg *hsotg = s->private; + unsigned long flags; + int dctl; + + spin_lock_irqsave(&hsotg->lock, flags); + dctl = readl(hsotg->regs + DCTL); + dctl &= DCTL_TSTCTL_MASK; + dctl >>= DCTL_TSTCTL_SHIFT; + spin_unlock_irqrestore(&hsotg->lock, flags); + + switch (dctl) { + case 0: + seq_puts(s, "no test\n"); + break; + case TEST_J: + seq_puts(s, "test_j\n"); + break; + case TEST_K: + seq_puts(s, "test_k\n"); + break; + case TEST_SE0_NAK: + seq_puts(s, "test_se0_nak\n"); + break; + case TEST_PACKET: + seq_puts(s, "test_packet\n"); + break; + case TEST_FORCE_EN: + seq_puts(s, "test_force_enable\n"); + break; + default: + seq_printf(s, "UNKNOWN %d\n", dctl); + } + + return 0; +} + +static int testmode_open(struct inode *inode, struct file *file) +{ + return single_open(file, testmode_show, inode->i_private); +} + +static const struct file_operations testmode_fops = { + .owner = THIS_MODULE, + .open = testmode_open, + .write = testmode_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * state_show - debugfs: show overall driver and device state. + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry shows the overall state of the hardware and + * some general information about each of the endpoints available + * to the system. + */ +static int state_show(struct seq_file *seq, void *v) +{ + struct dwc2_hsotg *hsotg = seq->private; + void __iomem *regs = hsotg->regs; + int idx; + + seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n", + readl(regs + DCFG), + readl(regs + DCTL), + readl(regs + DSTS)); + + seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n", + readl(regs + DIEPMSK), readl(regs + DOEPMSK)); + + seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n", + readl(regs + GINTMSK), + readl(regs + GINTSTS)); + + seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n", + readl(regs + DAINTMSK), + readl(regs + DAINT)); + + seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n", + readl(regs + GNPTXSTS), + readl(regs + GRXSTSR)); + + seq_puts(seq, "\nEndpoint status:\n"); + + for (idx = 0; idx < hsotg->num_of_eps; idx++) { + u32 in, out; + + in = readl(regs + DIEPCTL(idx)); + out = readl(regs + DOEPCTL(idx)); + + seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x", + idx, in, out); + + in = readl(regs + DIEPTSIZ(idx)); + out = readl(regs + DOEPTSIZ(idx)); + + seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", + in, out); + + seq_puts(seq, "\n"); + } + + return 0; +} + +static int state_open(struct inode *inode, struct file *file) +{ + return single_open(file, state_show, inode->i_private); +} + +static const struct file_operations state_fops = { + .owner = THIS_MODULE, + .open = state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * fifo_show - debugfs: show the fifo information + * @seq: The seq_file to write data to. + * @v: Unused parameter. + * + * Show the FIFO information for the overall fifo and all the + * periodic transmission FIFOs. + */ +static int fifo_show(struct seq_file *seq, void *v) +{ + struct dwc2_hsotg *hsotg = seq->private; + void __iomem *regs = hsotg->regs; + u32 val; + int idx; + + seq_puts(seq, "Non-periodic FIFOs:\n"); + seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); + + val = readl(regs + GNPTXFSIZ); + seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n", + val >> FIFOSIZE_DEPTH_SHIFT, + val & FIFOSIZE_DEPTH_MASK); + + seq_puts(seq, "\nPeriodic TXFIFOs:\n"); + + for (idx = 1; idx < hsotg->num_of_eps; idx++) { + val = readl(regs + DPTXFSIZN(idx)); + + seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, + val >> FIFOSIZE_DEPTH_SHIFT, + val & FIFOSIZE_STARTADDR_MASK); + } + + return 0; +} + +static int fifo_open(struct inode *inode, struct file *file) +{ + return single_open(file, fifo_show, inode->i_private); +} + +static const struct file_operations fifo_fops = { + .owner = THIS_MODULE, + .open = fifo_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const char *decode_direction(int is_in) +{ + return is_in ? "in" : "out"; +} + +/** + * ep_show - debugfs: show the state of an endpoint. + * @seq: The seq_file to write data to. + * @v: Unused parameter. + * + * This debugfs entry shows the state of the given endpoint (one is + * registered for each available). + */ +static int ep_show(struct seq_file *seq, void *v) +{ + struct s3c_hsotg_ep *ep = seq->private; + struct dwc2_hsotg *hsotg = ep->parent; + struct s3c_hsotg_req *req; + void __iomem *regs = hsotg->regs; + int index = ep->index; + int show_limit = 15; + unsigned long flags; + + seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n", + ep->index, ep->ep.name, decode_direction(ep->dir_in)); + + /* first show the register state */ + + seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n", + readl(regs + DIEPCTL(index)), + readl(regs + DOEPCTL(index))); + + seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n", + readl(regs + DIEPDMA(index)), + readl(regs + DOEPDMA(index))); + + seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n", + readl(regs + DIEPINT(index)), + readl(regs + DOEPINT(index))); + + seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n", + readl(regs + DIEPTSIZ(index)), + readl(regs + DOEPTSIZ(index))); + + seq_puts(seq, "\n"); + seq_printf(seq, "mps %d\n", ep->ep.maxpacket); + seq_printf(seq, "total_data=%ld\n", ep->total_data); + + seq_printf(seq, "request list (%p,%p):\n", + ep->queue.next, ep->queue.prev); + + spin_lock_irqsave(&hsotg->lock, flags); + + list_for_each_entry(req, &ep->queue, queue) { + if (--show_limit < 0) { + seq_puts(seq, "not showing more requests...\n"); + break; + } + + seq_printf(seq, "%c req %p: %d bytes @%p, ", + req == ep->req ? '*' : ' ', + req, req->req.length, req->req.buf); + seq_printf(seq, "%d done, res %d\n", + req->req.actual, req->req.status); + } + + spin_unlock_irqrestore(&hsotg->lock, flags); + + return 0; +} + +static int ep_open(struct inode *inode, struct file *file) +{ + return single_open(file, ep_show, inode->i_private); +} + +static const struct file_operations ep_fops = { + .owner = THIS_MODULE, + .open = ep_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * s3c_hsotg_create_debug - create debugfs directory and files + * @hsotg: The driver state + * + * Create the debugfs files to allow the user to get information + * about the state of the system. The directory name is created + * with the same name as the device itself, in case we end up + * with multiple blocks in future systems. + */ +static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) +{ + struct dentry *root; + struct dentry *file; + unsigned epidx; + + root = hsotg->debug_root; + + /* create general state file */ + + file = debugfs_create_file("state", S_IRUGO, root, hsotg, &state_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "%s: failed to create state\n", __func__); + + file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, hsotg, + &testmode_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "%s: failed to create testmode\n", + __func__); + + file = debugfs_create_file("fifo", S_IRUGO, root, hsotg, &fifo_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__); + + /* Create one file for each out endpoint */ + for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { + struct s3c_hsotg_ep *ep; + + ep = hsotg->eps_out[epidx]; + if (ep) { + file = debugfs_create_file(ep->name, S_IRUGO, + root, ep, &ep_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "failed to create %s debug file\n", + ep->name); + } + } + /* Create one file for each in endpoint. EP0 is handled with out eps */ + for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) { + struct s3c_hsotg_ep *ep; + + ep = hsotg->eps_in[epidx]; + if (ep) { + file = debugfs_create_file(ep->name, S_IRUGO, + root, ep, &ep_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "failed to create %s debug file\n", + ep->name); + } + } +} +#else +static inline void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) {} +#endif + +/* s3c_hsotg_delete_debug is removed as cleanup in done in dwc2_debugfs_exit */ + +int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) +{ + int ret; + + hsotg->debug_root = debugfs_create_dir(dev_name(hsotg->dev), NULL); + if (!hsotg->debug_root) { + ret = -ENOMEM; + goto err0; + } + + /* Add gadget debugfs nodes */ + s3c_hsotg_create_debug(hsotg); +err0: + return ret; +} +EXPORT_SYMBOL_GPL(dwc2_debugfs_init); + +void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg) +{ + debugfs_remove_recursive(hsotg->debug_root); + hsotg->debug_root = NULL; +} +EXPORT_SYMBOL_GPL(dwc2_debugfs_exit); diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 6a30887..bed56dc 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -35,7 +34,6 @@ #include #include #include -#include #include "core.h" #include "hw.h" @@ -894,7 +892,7 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, * @testmode: requested usb test mode * Enable usb Test Mode requested by the Host. */ -static int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) +int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) { int dctl = readl(hsotg->regs + DCTL); @@ -3391,404 +3389,6 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg) #endif } -/** - * testmode_write - debugfs: change usb test mode - * @seq: The seq file to write to. - * @v: Unused parameter. - * - * This debugfs entry modify the current usb test mode. - */ -static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t - count, loff_t *ppos) -{ - struct seq_file *s = file->private_data; - struct dwc2_hsotg *hsotg = s->private; - unsigned long flags; - u32 testmode = 0; - char buf[32]; - - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) - return -EFAULT; - - if (!strncmp(buf, "test_j", 6)) - testmode = TEST_J; - else if (!strncmp(buf, "test_k", 6)) - testmode = TEST_K; - else if (!strncmp(buf, "test_se0_nak", 12)) - testmode = TEST_SE0_NAK; - else if (!strncmp(buf, "test_packet", 11)) - testmode = TEST_PACKET; - else if (!strncmp(buf, "test_force_enable", 17)) - testmode = TEST_FORCE_EN; - else - testmode = 0; - - spin_lock_irqsave(&hsotg->lock, flags); - s3c_hsotg_set_test_mode(hsotg, testmode); - spin_unlock_irqrestore(&hsotg->lock, flags); - return count; -} - -/** - * testmode_show - debugfs: show usb test mode state - * @seq: The seq file to write to. - * @v: Unused parameter. - * - * This debugfs entry shows which usb test mode is currently enabled. - */ -static int testmode_show(struct seq_file *s, void *unused) -{ - struct dwc2_hsotg *hsotg = s->private; - unsigned long flags; - int dctl; - - spin_lock_irqsave(&hsotg->lock, flags); - dctl = readl(hsotg->regs + DCTL); - dctl &= DCTL_TSTCTL_MASK; - dctl >>= DCTL_TSTCTL_SHIFT; - spin_unlock_irqrestore(&hsotg->lock, flags); - - switch (dctl) { - case 0: - seq_puts(s, "no test\n"); - break; - case TEST_J: - seq_puts(s, "test_j\n"); - break; - case TEST_K: - seq_puts(s, "test_k\n"); - break; - case TEST_SE0_NAK: - seq_puts(s, "test_se0_nak\n"); - break; - case TEST_PACKET: - seq_puts(s, "test_packet\n"); - break; - case TEST_FORCE_EN: - seq_puts(s, "test_force_enable\n"); - break; - default: - seq_printf(s, "UNKNOWN %d\n", dctl); - } - - return 0; -} - -static int testmode_open(struct inode *inode, struct file *file) -{ - return single_open(file, testmode_show, inode->i_private); -} - -static const struct file_operations testmode_fops = { - .owner = THIS_MODULE, - .open = testmode_open, - .write = testmode_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * state_show - debugfs: show overall driver and device state. - * @seq: The seq file to write to. - * @v: Unused parameter. - * - * This debugfs entry shows the overall state of the hardware and - * some general information about each of the endpoints available - * to the system. - */ -static int state_show(struct seq_file *seq, void *v) -{ - struct dwc2_hsotg *hsotg = seq->private; - void __iomem *regs = hsotg->regs; - int idx; - - seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n", - readl(regs + DCFG), - readl(regs + DCTL), - readl(regs + DSTS)); - - seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n", - readl(regs + DIEPMSK), readl(regs + DOEPMSK)); - - seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n", - readl(regs + GINTMSK), - readl(regs + GINTSTS)); - - seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n", - readl(regs + DAINTMSK), - readl(regs + DAINT)); - - seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n", - readl(regs + GNPTXSTS), - readl(regs + GRXSTSR)); - - seq_puts(seq, "\nEndpoint status:\n"); - - for (idx = 0; idx < hsotg->num_of_eps; idx++) { - u32 in, out; - - in = readl(regs + DIEPCTL(idx)); - out = readl(regs + DOEPCTL(idx)); - - seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x", - idx, in, out); - - in = readl(regs + DIEPTSIZ(idx)); - out = readl(regs + DOEPTSIZ(idx)); - - seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", - in, out); - - seq_puts(seq, "\n"); - } - - return 0; -} - -static int state_open(struct inode *inode, struct file *file) -{ - return single_open(file, state_show, inode->i_private); -} - -static const struct file_operations state_fops = { - .owner = THIS_MODULE, - .open = state_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * fifo_show - debugfs: show the fifo information - * @seq: The seq_file to write data to. - * @v: Unused parameter. - * - * Show the FIFO information for the overall fifo and all the - * periodic transmission FIFOs. - */ -static int fifo_show(struct seq_file *seq, void *v) -{ - struct dwc2_hsotg *hsotg = seq->private; - void __iomem *regs = hsotg->regs; - u32 val; - int idx; - - seq_puts(seq, "Non-periodic FIFOs:\n"); - seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); - - val = readl(regs + GNPTXFSIZ); - seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n", - val >> FIFOSIZE_DEPTH_SHIFT, - val & FIFOSIZE_DEPTH_MASK); - - seq_puts(seq, "\nPeriodic TXFIFOs:\n"); - - for (idx = 1; idx < hsotg->num_of_eps; idx++) { - val = readl(regs + DPTXFSIZN(idx)); - - seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, - val >> FIFOSIZE_DEPTH_SHIFT, - val & FIFOSIZE_STARTADDR_MASK); - } - - return 0; -} - -static int fifo_open(struct inode *inode, struct file *file) -{ - return single_open(file, fifo_show, inode->i_private); -} - -static const struct file_operations fifo_fops = { - .owner = THIS_MODULE, - .open = fifo_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - - -static const char *decode_direction(int is_in) -{ - return is_in ? "in" : "out"; -} - -/** - * ep_show - debugfs: show the state of an endpoint. - * @seq: The seq_file to write data to. - * @v: Unused parameter. - * - * This debugfs entry shows the state of the given endpoint (one is - * registered for each available). - */ -static int ep_show(struct seq_file *seq, void *v) -{ - struct s3c_hsotg_ep *ep = seq->private; - struct dwc2_hsotg *hsotg = ep->parent; - struct s3c_hsotg_req *req; - void __iomem *regs = hsotg->regs; - int index = ep->index; - int show_limit = 15; - unsigned long flags; - - seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n", - ep->index, ep->ep.name, decode_direction(ep->dir_in)); - - /* first show the register state */ - - seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n", - readl(regs + DIEPCTL(index)), - readl(regs + DOEPCTL(index))); - - seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n", - readl(regs + DIEPDMA(index)), - readl(regs + DOEPDMA(index))); - - seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n", - readl(regs + DIEPINT(index)), - readl(regs + DOEPINT(index))); - - seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n", - readl(regs + DIEPTSIZ(index)), - readl(regs + DOEPTSIZ(index))); - - seq_puts(seq, "\n"); - seq_printf(seq, "mps %d\n", ep->ep.maxpacket); - seq_printf(seq, "total_data=%ld\n", ep->total_data); - - seq_printf(seq, "request list (%p,%p):\n", - ep->queue.next, ep->queue.prev); - - spin_lock_irqsave(&hsotg->lock, flags); - - list_for_each_entry(req, &ep->queue, queue) { - if (--show_limit < 0) { - seq_puts(seq, "not showing more requests...\n"); - break; - } - - seq_printf(seq, "%c req %p: %d bytes @%p, ", - req == ep->req ? '*' : ' ', - req, req->req.length, req->req.buf); - seq_printf(seq, "%d done, res %d\n", - req->req.actual, req->req.status); - } - - spin_unlock_irqrestore(&hsotg->lock, flags); - - return 0; -} - -static int ep_open(struct inode *inode, struct file *file) -{ - return single_open(file, ep_show, inode->i_private); -} - -static const struct file_operations ep_fops = { - .owner = THIS_MODULE, - .open = ep_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * s3c_hsotg_create_debug - create debugfs directory and files - * @hsotg: The driver state - * - * Create the debugfs files to allow the user to get information - * about the state of the system. The directory name is created - * with the same name as the device itself, in case we end up - * with multiple blocks in future systems. - */ -static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) -{ - struct dentry *root; - unsigned epidx; - - root = debugfs_create_dir(dev_name(hsotg->dev), NULL); - hsotg->debug_root = root; - if (IS_ERR(root)) { - dev_err(hsotg->dev, "cannot create debug root\n"); - return; - } - - /* create general state file */ - - hsotg->debug_file = debugfs_create_file("state", S_IRUGO, root, - hsotg, &state_fops); - - if (IS_ERR(hsotg->debug_file)) - dev_err(hsotg->dev, "%s: failed to create state\n", __func__); - - hsotg->debug_testmode = debugfs_create_file("testmode", - S_IRUGO | S_IWUSR, root, - hsotg, &testmode_fops); - - if (IS_ERR(hsotg->debug_testmode)) - dev_err(hsotg->dev, "%s: failed to create testmode\n", - __func__); - - hsotg->debug_fifo = debugfs_create_file("fifo", S_IRUGO, root, - hsotg, &fifo_fops); - - if (IS_ERR(hsotg->debug_fifo)) - dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__); - - /* Create one file for each out endpoint */ - for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { - struct s3c_hsotg_ep *ep; - - ep = hsotg->eps_out[epidx]; - if (ep) { - ep->debugfs = debugfs_create_file(ep->name, S_IRUGO, - root, ep, &ep_fops); - - if (IS_ERR(ep->debugfs)) - dev_err(hsotg->dev, "failed to create %s debug file\n", - ep->name); - } - } - /* Create one file for each in endpoint. EP0 is handled with out eps */ - for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) { - struct s3c_hsotg_ep *ep; - - ep = hsotg->eps_in[epidx]; - if (ep) { - ep->debugfs = debugfs_create_file(ep->name, S_IRUGO, - root, ep, &ep_fops); - - if (IS_ERR(ep->debugfs)) - dev_err(hsotg->dev, "failed to create %s debug file\n", - ep->name); - } - } -} - -/** - * s3c_hsotg_delete_debug - cleanup debugfs entries - * @hsotg: The driver state - * - * Cleanup (remove) the debugfs files for use on module exit. - */ -static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg) -{ - unsigned epidx; - - for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { - if (hsotg->eps_in[epidx]) - debugfs_remove(hsotg->eps_in[epidx]->debugfs); - if (hsotg->eps_out[epidx]) - debugfs_remove(hsotg->eps_out[epidx]->debugfs); - } - - debugfs_remove(hsotg->debug_file); - debugfs_remove(hsotg->debug_testmode); - debugfs_remove(hsotg->debug_fifo); - debugfs_remove(hsotg->debug_root); -} - #ifdef CONFIG_OF static void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) { @@ -4028,8 +3628,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) if (ret) goto err_supplies; - s3c_hsotg_create_debug(hsotg); - s3c_hsotg_dump(hsotg); return 0; @@ -4050,7 +3648,6 @@ EXPORT_SYMBOL_GPL(dwc2_gadget_init); int s3c_hsotg_remove(struct dwc2_hsotg *hsotg) { usb_del_gadget_udc(&hsotg->gadget); - s3c_hsotg_delete_debug(hsotg); clk_disable_unprepare(hsotg->clk); return 0; diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 185663e..4fb058b 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -47,6 +47,7 @@ #include "core.h" #include "hcd.h" +#include "debug.h" static const char dwc2_driver_name[] = "dwc2"; @@ -121,6 +122,7 @@ static int dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); + dwc2_debugfs_exit(hsotg); if (hsotg->hcd_enabled) dwc2_hcd_remove(hsotg); if (hsotg->gadget_enabled) @@ -256,6 +258,8 @@ static int dwc2_driver_probe(struct platform_device *dev) platform_set_drvdata(dev, hsotg); + dwc2_debugfs_init(hsotg); + return retval; } -- cgit v0.10.2 From 563cf017c443137220428712d29cd5510dae2cb2 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Wed, 29 Apr 2015 22:09:00 +0200 Subject: usb: dwc2: debugfs: add support for complete register dump Dump all registers to take a complete snapshot of dwc2 state. Code is inspired by dwc3/debugfs.c Acked-by: John Youn Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 3695c6f..1fd8d2b 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -615,6 +615,7 @@ struct dwc2_hsotg { enum dwc2_lx_state lx_state; struct dentry *debug_root; + struct debugfs_regset32 *regset; /* DWC OTG HW Release versions */ #define DWC2_CORE_REV_2_71a 0x4f54271a diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index e57e554..af89537 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -391,9 +391,344 @@ static inline void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) {} /* s3c_hsotg_delete_debug is removed as cleanup in done in dwc2_debugfs_exit */ +#define dump_register(nm) \ +{ \ + .name = #nm, \ + .offset = nm, \ +} + +static const struct debugfs_reg32 dwc2_regs[] = { + /* + * Accessing registers like this can trigger mode mismatch interrupt. + * However, according to dwc2 databook, the register access, in this + * case, is completed on the processor bus but is ignored by the core + * and does not affect its operation. + */ + dump_register(GOTGCTL), + dump_register(GOTGINT), + dump_register(GAHBCFG), + dump_register(GUSBCFG), + dump_register(GRSTCTL), + dump_register(GINTSTS), + dump_register(GINTMSK), + dump_register(GRXSTSR), + dump_register(GRXSTSP), + dump_register(GRXFSIZ), + dump_register(GNPTXFSIZ), + dump_register(GNPTXSTS), + dump_register(GI2CCTL), + dump_register(GPVNDCTL), + dump_register(GGPIO), + dump_register(GUID), + dump_register(GSNPSID), + dump_register(GHWCFG1), + dump_register(GHWCFG2), + dump_register(GHWCFG3), + dump_register(GHWCFG4), + dump_register(GLPMCFG), + dump_register(GPWRDN), + dump_register(GDFIFOCFG), + dump_register(ADPCTL), + dump_register(HPTXFSIZ), + dump_register(DPTXFSIZN(1)), + dump_register(DPTXFSIZN(2)), + dump_register(DPTXFSIZN(3)), + dump_register(DPTXFSIZN(4)), + dump_register(DPTXFSIZN(5)), + dump_register(DPTXFSIZN(6)), + dump_register(DPTXFSIZN(7)), + dump_register(DPTXFSIZN(8)), + dump_register(DPTXFSIZN(9)), + dump_register(DPTXFSIZN(10)), + dump_register(DPTXFSIZN(11)), + dump_register(DPTXFSIZN(12)), + dump_register(DPTXFSIZN(13)), + dump_register(DPTXFSIZN(14)), + dump_register(DPTXFSIZN(15)), + dump_register(DCFG), + dump_register(DCTL), + dump_register(DSTS), + dump_register(DIEPMSK), + dump_register(DOEPMSK), + dump_register(DAINT), + dump_register(DAINTMSK), + dump_register(DTKNQR1), + dump_register(DTKNQR2), + dump_register(DTKNQR3), + dump_register(DTKNQR4), + dump_register(DVBUSDIS), + dump_register(DVBUSPULSE), + dump_register(DIEPCTL(0)), + dump_register(DIEPCTL(1)), + dump_register(DIEPCTL(2)), + dump_register(DIEPCTL(3)), + dump_register(DIEPCTL(4)), + dump_register(DIEPCTL(5)), + dump_register(DIEPCTL(6)), + dump_register(DIEPCTL(7)), + dump_register(DIEPCTL(8)), + dump_register(DIEPCTL(9)), + dump_register(DIEPCTL(10)), + dump_register(DIEPCTL(11)), + dump_register(DIEPCTL(12)), + dump_register(DIEPCTL(13)), + dump_register(DIEPCTL(14)), + dump_register(DIEPCTL(15)), + dump_register(DOEPCTL(0)), + dump_register(DOEPCTL(1)), + dump_register(DOEPCTL(2)), + dump_register(DOEPCTL(3)), + dump_register(DOEPCTL(4)), + dump_register(DOEPCTL(5)), + dump_register(DOEPCTL(6)), + dump_register(DOEPCTL(7)), + dump_register(DOEPCTL(8)), + dump_register(DOEPCTL(9)), + dump_register(DOEPCTL(10)), + dump_register(DOEPCTL(11)), + dump_register(DOEPCTL(12)), + dump_register(DOEPCTL(13)), + dump_register(DOEPCTL(14)), + dump_register(DOEPCTL(15)), + dump_register(DIEPINT(0)), + dump_register(DIEPINT(1)), + dump_register(DIEPINT(2)), + dump_register(DIEPINT(3)), + dump_register(DIEPINT(4)), + dump_register(DIEPINT(5)), + dump_register(DIEPINT(6)), + dump_register(DIEPINT(7)), + dump_register(DIEPINT(8)), + dump_register(DIEPINT(9)), + dump_register(DIEPINT(10)), + dump_register(DIEPINT(11)), + dump_register(DIEPINT(12)), + dump_register(DIEPINT(13)), + dump_register(DIEPINT(14)), + dump_register(DIEPINT(15)), + dump_register(DOEPINT(0)), + dump_register(DOEPINT(1)), + dump_register(DOEPINT(2)), + dump_register(DOEPINT(3)), + dump_register(DOEPINT(4)), + dump_register(DOEPINT(5)), + dump_register(DOEPINT(6)), + dump_register(DOEPINT(7)), + dump_register(DOEPINT(8)), + dump_register(DOEPINT(9)), + dump_register(DOEPINT(10)), + dump_register(DOEPINT(11)), + dump_register(DOEPINT(12)), + dump_register(DOEPINT(13)), + dump_register(DOEPINT(14)), + dump_register(DOEPINT(15)), + dump_register(DIEPTSIZ(0)), + dump_register(DIEPTSIZ(1)), + dump_register(DIEPTSIZ(2)), + dump_register(DIEPTSIZ(3)), + dump_register(DIEPTSIZ(4)), + dump_register(DIEPTSIZ(5)), + dump_register(DIEPTSIZ(6)), + dump_register(DIEPTSIZ(7)), + dump_register(DIEPTSIZ(8)), + dump_register(DIEPTSIZ(9)), + dump_register(DIEPTSIZ(10)), + dump_register(DIEPTSIZ(11)), + dump_register(DIEPTSIZ(12)), + dump_register(DIEPTSIZ(13)), + dump_register(DIEPTSIZ(14)), + dump_register(DIEPTSIZ(15)), + dump_register(DOEPTSIZ(0)), + dump_register(DOEPTSIZ(1)), + dump_register(DOEPTSIZ(2)), + dump_register(DOEPTSIZ(3)), + dump_register(DOEPTSIZ(4)), + dump_register(DOEPTSIZ(5)), + dump_register(DOEPTSIZ(6)), + dump_register(DOEPTSIZ(7)), + dump_register(DOEPTSIZ(8)), + dump_register(DOEPTSIZ(9)), + dump_register(DOEPTSIZ(10)), + dump_register(DOEPTSIZ(11)), + dump_register(DOEPTSIZ(12)), + dump_register(DOEPTSIZ(13)), + dump_register(DOEPTSIZ(14)), + dump_register(DOEPTSIZ(15)), + dump_register(DIEPDMA(0)), + dump_register(DIEPDMA(1)), + dump_register(DIEPDMA(2)), + dump_register(DIEPDMA(3)), + dump_register(DIEPDMA(4)), + dump_register(DIEPDMA(5)), + dump_register(DIEPDMA(6)), + dump_register(DIEPDMA(7)), + dump_register(DIEPDMA(8)), + dump_register(DIEPDMA(9)), + dump_register(DIEPDMA(10)), + dump_register(DIEPDMA(11)), + dump_register(DIEPDMA(12)), + dump_register(DIEPDMA(13)), + dump_register(DIEPDMA(14)), + dump_register(DIEPDMA(15)), + dump_register(DOEPDMA(0)), + dump_register(DOEPDMA(1)), + dump_register(DOEPDMA(2)), + dump_register(DOEPDMA(3)), + dump_register(DOEPDMA(4)), + dump_register(DOEPDMA(5)), + dump_register(DOEPDMA(6)), + dump_register(DOEPDMA(7)), + dump_register(DOEPDMA(8)), + dump_register(DOEPDMA(9)), + dump_register(DOEPDMA(10)), + dump_register(DOEPDMA(11)), + dump_register(DOEPDMA(12)), + dump_register(DOEPDMA(13)), + dump_register(DOEPDMA(14)), + dump_register(DOEPDMA(15)), + dump_register(DTXFSTS(0)), + dump_register(DTXFSTS(1)), + dump_register(DTXFSTS(2)), + dump_register(DTXFSTS(3)), + dump_register(DTXFSTS(4)), + dump_register(DTXFSTS(5)), + dump_register(DTXFSTS(6)), + dump_register(DTXFSTS(7)), + dump_register(DTXFSTS(8)), + dump_register(DTXFSTS(9)), + dump_register(DTXFSTS(10)), + dump_register(DTXFSTS(11)), + dump_register(DTXFSTS(12)), + dump_register(DTXFSTS(13)), + dump_register(DTXFSTS(14)), + dump_register(DTXFSTS(15)), + dump_register(PCGCTL), + dump_register(HCFG), + dump_register(HFIR), + dump_register(HFNUM), + dump_register(HPTXSTS), + dump_register(HAINT), + dump_register(HAINTMSK), + dump_register(HFLBADDR), + dump_register(HPRT0), + dump_register(HCCHAR(0)), + dump_register(HCCHAR(1)), + dump_register(HCCHAR(2)), + dump_register(HCCHAR(3)), + dump_register(HCCHAR(4)), + dump_register(HCCHAR(5)), + dump_register(HCCHAR(6)), + dump_register(HCCHAR(7)), + dump_register(HCCHAR(8)), + dump_register(HCCHAR(9)), + dump_register(HCCHAR(10)), + dump_register(HCCHAR(11)), + dump_register(HCCHAR(12)), + dump_register(HCCHAR(13)), + dump_register(HCCHAR(14)), + dump_register(HCCHAR(15)), + dump_register(HCSPLT(0)), + dump_register(HCSPLT(1)), + dump_register(HCSPLT(2)), + dump_register(HCSPLT(3)), + dump_register(HCSPLT(4)), + dump_register(HCSPLT(5)), + dump_register(HCSPLT(6)), + dump_register(HCSPLT(7)), + dump_register(HCSPLT(8)), + dump_register(HCSPLT(9)), + dump_register(HCSPLT(10)), + dump_register(HCSPLT(11)), + dump_register(HCSPLT(12)), + dump_register(HCSPLT(13)), + dump_register(HCSPLT(14)), + dump_register(HCSPLT(15)), + dump_register(HCINT(0)), + dump_register(HCINT(1)), + dump_register(HCINT(2)), + dump_register(HCINT(3)), + dump_register(HCINT(4)), + dump_register(HCINT(5)), + dump_register(HCINT(6)), + dump_register(HCINT(7)), + dump_register(HCINT(8)), + dump_register(HCINT(9)), + dump_register(HCINT(10)), + dump_register(HCINT(11)), + dump_register(HCINT(12)), + dump_register(HCINT(13)), + dump_register(HCINT(14)), + dump_register(HCINT(15)), + dump_register(HCINTMSK(0)), + dump_register(HCINTMSK(1)), + dump_register(HCINTMSK(2)), + dump_register(HCINTMSK(3)), + dump_register(HCINTMSK(4)), + dump_register(HCINTMSK(5)), + dump_register(HCINTMSK(6)), + dump_register(HCINTMSK(7)), + dump_register(HCINTMSK(8)), + dump_register(HCINTMSK(9)), + dump_register(HCINTMSK(10)), + dump_register(HCINTMSK(11)), + dump_register(HCINTMSK(12)), + dump_register(HCINTMSK(13)), + dump_register(HCINTMSK(14)), + dump_register(HCINTMSK(15)), + dump_register(HCTSIZ(0)), + dump_register(HCTSIZ(1)), + dump_register(HCTSIZ(2)), + dump_register(HCTSIZ(3)), + dump_register(HCTSIZ(4)), + dump_register(HCTSIZ(5)), + dump_register(HCTSIZ(6)), + dump_register(HCTSIZ(7)), + dump_register(HCTSIZ(8)), + dump_register(HCTSIZ(9)), + dump_register(HCTSIZ(10)), + dump_register(HCTSIZ(11)), + dump_register(HCTSIZ(12)), + dump_register(HCTSIZ(13)), + dump_register(HCTSIZ(14)), + dump_register(HCTSIZ(15)), + dump_register(HCDMA(0)), + dump_register(HCDMA(1)), + dump_register(HCDMA(2)), + dump_register(HCDMA(3)), + dump_register(HCDMA(4)), + dump_register(HCDMA(5)), + dump_register(HCDMA(6)), + dump_register(HCDMA(7)), + dump_register(HCDMA(8)), + dump_register(HCDMA(9)), + dump_register(HCDMA(10)), + dump_register(HCDMA(11)), + dump_register(HCDMA(12)), + dump_register(HCDMA(13)), + dump_register(HCDMA(14)), + dump_register(HCDMA(15)), + dump_register(HCDMAB(0)), + dump_register(HCDMAB(1)), + dump_register(HCDMAB(2)), + dump_register(HCDMAB(3)), + dump_register(HCDMAB(4)), + dump_register(HCDMAB(5)), + dump_register(HCDMAB(6)), + dump_register(HCDMAB(7)), + dump_register(HCDMAB(8)), + dump_register(HCDMAB(9)), + dump_register(HCDMAB(10)), + dump_register(HCDMAB(11)), + dump_register(HCDMAB(12)), + dump_register(HCDMAB(13)), + dump_register(HCDMAB(14)), + dump_register(HCDMAB(15)), +}; + int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) { int ret; + struct dentry *file; hsotg->debug_root = debugfs_create_dir(dev_name(hsotg->dev), NULL); if (!hsotg->debug_root) { @@ -403,6 +738,28 @@ int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) /* Add gadget debugfs nodes */ s3c_hsotg_create_debug(hsotg); + + hsotg->regset = devm_kzalloc(hsotg->dev, sizeof(*hsotg->regset), + GFP_KERNEL); + if (!hsotg->regset) { + ret = -ENOMEM; + goto err1; + } + + hsotg->regset->regs = dwc2_regs; + hsotg->regset->nregs = ARRAY_SIZE(dwc2_regs); + hsotg->regset->base = hsotg->regs; + + file = debugfs_create_regset32("regdump", S_IRUGO, hsotg->debug_root, + hsotg->regset); + if (!file) { + ret = -ENOMEM; + goto err1; + } + + return 0; +err1: + debugfs_remove_recursive(hsotg->debug_root); err0: return ret; } -- cgit v0.10.2 From d17ee77b3044da8b8f550bfdf3be8fdcc9d09858 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:01 +0200 Subject: usb: dwc2: add controller hibernation support When suspending usb bus, phy driver may disable controller power. In this case, registers need to be saved on suspend and restored on resume. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index d5197d4..889dc5f 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -56,6 +56,383 @@ #include "core.h" #include "hcd.h" +#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) +/** + * dwc2_backup_host_registers() - Backup controller host registers. + * When suspending usb bus, registers needs to be backuped + * if controller power is disabled once suspended. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_hregs_backup *hr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Backup Host regs */ + hr = hsotg->hr_backup; + if (!hr) { + hr = devm_kzalloc(hsotg->dev, sizeof(*hr), GFP_KERNEL); + if (!hr) { + dev_err(hsotg->dev, "%s: can't allocate host regs\n", + __func__); + return -ENOMEM; + } + + hsotg->hr_backup = hr; + } + hr->hcfg = readl(hsotg->regs + HCFG); + hr->haintmsk = readl(hsotg->regs + HAINTMSK); + for (i = 0; i < hsotg->core_params->host_channels; ++i) + hr->hcintmsk[i] = readl(hsotg->regs + HCINTMSK(i)); + + hr->hprt0 = readl(hsotg->regs + HPRT0); + hr->hfir = readl(hsotg->regs + HFIR); + + return 0; +} + +/** + * dwc2_restore_host_registers() - Restore controller host registers. + * When resuming usb bus, device registers needs to be restored + * if controller power were disabled. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_hregs_backup *hr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Restore host regs */ + hr = hsotg->hr_backup; + if (!hr) { + dev_err(hsotg->dev, "%s: no host registers to restore\n", + __func__); + return -EINVAL; + } + + writel(hr->hcfg, hsotg->regs + HCFG); + writel(hr->haintmsk, hsotg->regs + HAINTMSK); + + for (i = 0; i < hsotg->core_params->host_channels; ++i) + writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i)); + + writel(hr->hprt0, hsotg->regs + HPRT0); + writel(hr->hfir, hsotg->regs + HFIR); + + return 0; +} +#else +static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) +{ return 0; } + +static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) +{ return 0; } +#endif + +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ + IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) +/** + * dwc2_backup_device_registers() - Backup controller device registers. + * When suspending usb bus, registers needs to be backuped + * if controller power is disabled once suspended. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_dregs_backup *dr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Backup dev regs */ + dr = hsotg->dr_backup; + if (!dr) { + dr = devm_kzalloc(hsotg->dev, sizeof(*dr), GFP_KERNEL); + if (!dr) { + dev_err(hsotg->dev, "%s: can't allocate device regs\n", + __func__); + return -ENOMEM; + } + + hsotg->dr_backup = dr; + } + + dr->dcfg = readl(hsotg->regs + DCFG); + dr->dctl = readl(hsotg->regs + DCTL); + dr->daintmsk = readl(hsotg->regs + DAINTMSK); + dr->diepmsk = readl(hsotg->regs + DIEPMSK); + dr->doepmsk = readl(hsotg->regs + DOEPMSK); + + for (i = 0; i < hsotg->num_of_eps; i++) { + /* Backup IN EPs */ + dr->diepctl[i] = readl(hsotg->regs + DIEPCTL(i)); + + /* Ensure DATA PID is correctly configured */ + if (dr->diepctl[i] & DXEPCTL_DPID) + dr->diepctl[i] |= DXEPCTL_SETD1PID; + else + dr->diepctl[i] |= DXEPCTL_SETD0PID; + + dr->dieptsiz[i] = readl(hsotg->regs + DIEPTSIZ(i)); + dr->diepdma[i] = readl(hsotg->regs + DIEPDMA(i)); + + /* Backup OUT EPs */ + dr->doepctl[i] = readl(hsotg->regs + DOEPCTL(i)); + + /* Ensure DATA PID is correctly configured */ + if (dr->doepctl[i] & DXEPCTL_DPID) + dr->doepctl[i] |= DXEPCTL_SETD1PID; + else + dr->doepctl[i] |= DXEPCTL_SETD0PID; + + dr->doeptsiz[i] = readl(hsotg->regs + DOEPTSIZ(i)); + dr->doepdma[i] = readl(hsotg->regs + DOEPDMA(i)); + } + + return 0; +} + +/** + * dwc2_restore_device_registers() - Restore controller device registers. + * When resuming usb bus, device registers needs to be restored + * if controller power were disabled. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_dregs_backup *dr; + u32 dctl; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Restore dev regs */ + dr = hsotg->dr_backup; + if (!dr) { + dev_err(hsotg->dev, "%s: no device registers to restore\n", + __func__); + return -EINVAL; + } + + writel(dr->dcfg, hsotg->regs + DCFG); + writel(dr->dctl, hsotg->regs + DCTL); + writel(dr->daintmsk, hsotg->regs + DAINTMSK); + writel(dr->diepmsk, hsotg->regs + DIEPMSK); + writel(dr->doepmsk, hsotg->regs + DOEPMSK); + + for (i = 0; i < hsotg->num_of_eps; i++) { + /* Restore IN EPs */ + writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i)); + writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i)); + writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i)); + + /* Restore OUT EPs */ + writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i)); + writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i)); + writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i)); + } + + /* Set the Power-On Programming done bit */ + dctl = readl(hsotg->regs + DCTL); + dctl |= DCTL_PWRONPRGDONE; + writel(dctl, hsotg->regs + DCTL); + + return 0; +} +#else +static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) +{ return 0; } + +static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) +{ return 0; } +#endif + +/** + * dwc2_backup_global_registers() - Backup global controller registers. + * When suspending usb bus, registers needs to be backuped + * if controller power is disabled once suspended. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_gregs_backup *gr; + int i; + + /* Backup global regs */ + gr = hsotg->gr_backup; + if (!gr) { + gr = devm_kzalloc(hsotg->dev, sizeof(*gr), GFP_KERNEL); + if (!gr) { + dev_err(hsotg->dev, "%s: can't allocate global regs\n", + __func__); + return -ENOMEM; + } + + hsotg->gr_backup = gr; + } + + gr->gotgctl = readl(hsotg->regs + GOTGCTL); + gr->gintmsk = readl(hsotg->regs + GINTMSK); + gr->gahbcfg = readl(hsotg->regs + GAHBCFG); + gr->gusbcfg = readl(hsotg->regs + GUSBCFG); + gr->grxfsiz = readl(hsotg->regs + GRXFSIZ); + gr->gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ); + gr->hptxfsiz = readl(hsotg->regs + HPTXFSIZ); + gr->gdfifocfg = readl(hsotg->regs + GDFIFOCFG); + for (i = 0; i < MAX_EPS_CHANNELS; i++) + gr->dtxfsiz[i] = readl(hsotg->regs + DPTXFSIZN(i)); + + return 0; +} + +/** + * dwc2_restore_global_registers() - Restore controller global registers. + * When resuming usb bus, device registers needs to be restored + * if controller power were disabled. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_gregs_backup *gr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Restore global regs */ + gr = hsotg->gr_backup; + if (!gr) { + dev_err(hsotg->dev, "%s: no global registers to restore\n", + __func__); + return -EINVAL; + } + + writel(0xffffffff, hsotg->regs + GINTSTS); + writel(gr->gotgctl, hsotg->regs + GOTGCTL); + writel(gr->gintmsk, hsotg->regs + GINTMSK); + writel(gr->gusbcfg, hsotg->regs + GUSBCFG); + writel(gr->gahbcfg, hsotg->regs + GAHBCFG); + writel(gr->grxfsiz, hsotg->regs + GRXFSIZ); + writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ); + writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ); + writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG); + for (i = 0; i < MAX_EPS_CHANNELS; i++) + writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i)); + + return 0; +} + +/** + * dwc2_exit_hibernation() - Exit controller from Partial Power Down. + * + * @hsotg: Programming view of the DWC_otg controller + * @restore: Controller registers need to be restored + */ +int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore) +{ + u32 pcgcctl; + int ret = 0; + + pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_STOPPCLK; + writel(pcgcctl, hsotg->regs + PCGCTL); + + pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_PWRCLMP; + writel(pcgcctl, hsotg->regs + PCGCTL); + + pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_RSTPDWNMODULE; + writel(pcgcctl, hsotg->regs + PCGCTL); + + udelay(100); + if (restore) { + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + if (dwc2_is_host_mode(hsotg)) { + ret = dwc2_restore_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore host registers\n", + __func__); + return ret; + } + } else { + ret = dwc2_restore_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore device registers\n", + __func__); + return ret; + } + } + } + + return ret; +} + +/** + * dwc2_enter_hibernation() - Put controller in Partial Power Down. + * + * @hsotg: Programming view of the DWC_otg controller + */ +int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) +{ + u32 pcgcctl; + int ret = 0; + + /* Backup all registers */ + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + + if (dwc2_is_host_mode(hsotg)) { + ret = dwc2_backup_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup host registers\n", + __func__); + return ret; + } + } else { + ret = dwc2_backup_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup device registers\n", + __func__); + return ret; + } + } + + /* Put the controller in low power state */ + pcgcctl = readl(hsotg->regs + PCGCTL); + + pcgcctl |= PCGCTL_PWRCLMP; + writel(pcgcctl, hsotg->regs + PCGCTL); + ndelay(20); + + pcgcctl |= PCGCTL_RSTPDWNMODULE; + writel(pcgcctl, hsotg->regs + PCGCTL); + ndelay(20); + + pcgcctl |= PCGCTL_STOPPCLK; + writel(pcgcctl, hsotg->regs + PCGCTL); + + return ret; +} + /** * dwc2_enable_common_interrupts() - Initializes the commmon interrupts, * used in both device and host modes diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 1fd8d2b..b0ee951 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -452,6 +452,82 @@ struct dwc2_hw_params { #define DWC2_CTRL_BUFF_SIZE 8 /** + * struct dwc2_gregs_backup - Holds global registers state before entering partial + * power down + * @gotgctl: Backup of GOTGCTL register + * @gintmsk: Backup of GINTMSK register + * @gahbcfg: Backup of GAHBCFG register + * @gusbcfg: Backup of GUSBCFG register + * @grxfsiz: Backup of GRXFSIZ register + * @gnptxfsiz: Backup of GNPTXFSIZ register + * @gi2cctl: Backup of GI2CCTL register + * @hptxfsiz: Backup of HPTXFSIZ register + * @gdfifocfg: Backup of GDFIFOCFG register + * @dtxfsiz: Backup of DTXFSIZ registers for each endpoint + * @gpwrdn: Backup of GPWRDN register + */ +struct dwc2_gregs_backup { + u32 gotgctl; + u32 gintmsk; + u32 gahbcfg; + u32 gusbcfg; + u32 grxfsiz; + u32 gnptxfsiz; + u32 gi2cctl; + u32 hptxfsiz; + u32 pcgcctl; + u32 gdfifocfg; + u32 dtxfsiz[MAX_EPS_CHANNELS]; + u32 gpwrdn; +}; + +/** + * struct dwc2_dregs_backup - Holds device registers state before entering partial + * power down + * @dcfg: Backup of DCFG register + * @dctl: Backup of DCTL register + * @daintmsk: Backup of DAINTMSK register + * @diepmsk: Backup of DIEPMSK register + * @doepmsk: Backup of DOEPMSK register + * @diepctl: Backup of DIEPCTL register + * @dieptsiz: Backup of DIEPTSIZ register + * @diepdma: Backup of DIEPDMA register + * @doepctl: Backup of DOEPCTL register + * @doeptsiz: Backup of DOEPTSIZ register + * @doepdma: Backup of DOEPDMA register + */ +struct dwc2_dregs_backup { + u32 dcfg; + u32 dctl; + u32 daintmsk; + u32 diepmsk; + u32 doepmsk; + u32 diepctl[MAX_EPS_CHANNELS]; + u32 dieptsiz[MAX_EPS_CHANNELS]; + u32 diepdma[MAX_EPS_CHANNELS]; + u32 doepctl[MAX_EPS_CHANNELS]; + u32 doeptsiz[MAX_EPS_CHANNELS]; + u32 doepdma[MAX_EPS_CHANNELS]; +}; + +/** + * struct dwc2_hregs_backup - Holds host registers state before entering partial + * power down + * @hcfg: Backup of HCFG register + * @haintmsk: Backup of HAINTMSK register + * @hcintmsk: Backup of HCINTMSK register + * @hptr0: Backup of HPTR0 register + * @hfir: Backup of HFIR register + */ +struct dwc2_hregs_backup { + u32 hcfg; + u32 haintmsk; + u32 hcintmsk[MAX_EPS_CHANNELS]; + u32 hprt0; + u32 hfir; +}; + +/** * struct dwc2_hsotg - Holds the state of the driver, including the non-periodic * and periodic schedules * @@ -481,6 +557,9 @@ struct dwc2_hw_params { * interrupt * @wkp_timer: Timer object for handling Wakeup Detected interrupt * @lx_state: Lx state of connected device + * @gregs_backup: Backup of global registers during suspend + * @dregs_backup: Backup of device registers during suspend + * @hregs_backup: Backup of host registers during suspend * * These are for host mode: * @@ -613,6 +692,9 @@ struct dwc2_hsotg { struct work_struct wf_otg; struct timer_list wkp_timer; enum dwc2_lx_state lx_state; + struct dwc2_gregs_backup *gr_backup; + struct dwc2_dregs_backup *dr_backup; + struct dwc2_hregs_backup *hr_backup; struct dentry *debug_root; struct debugfs_regset32 *regset; @@ -749,6 +831,8 @@ enum dwc2_halt_status { * and the DWC_otg controller */ extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg); +extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg); +extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore); /* * Host core Functions. -- cgit v0.10.2 From f81f46e1f530900323b6e32eba1af7244ca69537 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:02 +0200 Subject: usb: dwc2: implement hibernation during bus suspend/resume Allow controller to enter in hibernation during usb bus suspend and inform both phy and gadget about the suspended state. While in hibernation, the controller can't detect the resume condition. An external mechanism must call usb_phy_set_suspend on resume. Exit hibernation when controller gets the resume interrupt and inform only gadget driver about it. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index b0ee951..e6abc28 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1088,6 +1088,7 @@ extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg); extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2); extern int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode); +#define dwc2_is_device_connected(hsotg) (hsotg->connected) #else static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } @@ -1104,6 +1105,7 @@ static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {} static inline int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) { return 0; } +#define dwc2_is_device_connected(hsotg) (0) #endif #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 6cf0478..6ffb5a9 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -334,6 +334,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) */ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) { + int ret; dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n"); dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state); @@ -345,6 +346,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) /* Clear Remote Wakeup Signaling */ dctl &= ~DCTL_RMTWKUPSIG; writel(dctl, hsotg->regs + DCTL); + ret = dwc2_exit_hibernation(hsotg, true); + if (ret) + dev_err(hsotg->dev, "exit hibernation failed\n"); + + call_gadget(hsotg, resume); } /* Change to L0 state */ hsotg->lx_state = DWC2_L0; @@ -397,6 +403,7 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg) static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) { u32 dsts; + int ret; dev_dbg(hsotg->dev, "USB SUSPEND\n"); @@ -411,6 +418,30 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n", !!(dsts & DSTS_SUSPSTS), hsotg->hw_params.power_optimized); + if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) { + /* Ignore suspend request before enumeration */ + if (!dwc2_is_device_connected(hsotg)) { + dev_dbg(hsotg->dev, + "ignore suspend request before enumeration\n"); + goto clear_int; + } + + ret = dwc2_enter_hibernation(hsotg); + if (ret) { + dev_err(hsotg->dev, + "enter hibernation failed\n"); + goto skip_power_saving; + } + + udelay(100); + + /* Ask phy to be suspended */ + if (!IS_ERR_OR_NULL(hsotg->uphy)) + usb_phy_set_suspend(hsotg->uphy, true); +skip_power_saving: + /* Call gadget suspend callback */ + call_gadget(hsotg, suspend); + } } else { if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) { dev_dbg(hsotg->dev, "a_peripheral->a_host\n"); @@ -426,6 +457,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) /* Change to L2 (suspend) state */ hsotg->lx_state = DWC2_L2; +clear_int: /* Clear interrupt */ writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS); } -- cgit v0.10.2 From 3eb42df3ebfbd8d46b831c26ecb90e128ad474a5 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:03 +0200 Subject: usb: dwc2: controller must update lx_state before releasing lock During suspend, there could a race condition between ep_queue and suspend interrupt if lx_state is updated after releasing spinlock in call_gadget(hsotg, suspend). Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 6ffb5a9..0b7f2b2 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -439,6 +439,12 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) if (!IS_ERR_OR_NULL(hsotg->uphy)) usb_phy_set_suspend(hsotg->uphy, true); skip_power_saving: + /* + * Change to L2 (suspend) state before releasing + * spinlock + */ + hsotg->lx_state = DWC2_L2; + /* Call gadget suspend callback */ call_gadget(hsotg, suspend); } @@ -446,6 +452,8 @@ skip_power_saving: if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) { dev_dbg(hsotg->dev, "a_peripheral->a_host\n"); + /* Change to L2 (suspend) state */ + hsotg->lx_state = DWC2_L2; /* Clear the a_peripheral flag, back to a_host */ spin_unlock(&hsotg->lock); dwc2_hcd_start(hsotg); @@ -454,9 +462,6 @@ skip_power_saving: } } - /* Change to L2 (suspend) state */ - hsotg->lx_state = DWC2_L2; - clear_int: /* Clear interrupt */ writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS); -- cgit v0.10.2 From a6d249d8373343749f9ae55f5581f3b21e178471 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:04 +0200 Subject: usb: dwc2: add external_id_pin_ctl core parameter This is required due to an Intel specific hardware issue. Where id- pin setup causes glitches on the interrupt line when CONIDSTSCHG interrupt is enabled. Specify external_id_pin_ctl when an external driver (for example phy) can handle id change, so that CONIDSTSCHG interrupt can be disabled from the controller. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 889dc5f..8c3bc84 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -454,8 +454,10 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) if (hsotg->core_params->dma_enable <= 0) intmsk |= GINTSTS_RXFLVL; + if (hsotg->core_params->external_id_pin_ctl <= 0) + intmsk |= GINTSTS_CONIDSTSCHNG; - intmsk |= GINTSTS_CONIDSTSCHNG | GINTSTS_WKUPINT | GINTSTS_USBSUSP | + intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP | GINTSTS_SESSREQINT; writel(intmsk, hsotg->regs + GINTMSK); @@ -2979,6 +2981,23 @@ static void dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val) hsotg->core_params->uframe_sched = val; } +static void dwc2_set_param_external_id_pin_ctl(struct dwc2_hsotg *hsotg, + int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "'%d' invalid for parameter external_id_pin_ctl\n", + val); + dev_err(hsotg->dev, "external_id_pin_ctl must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting external_id_pin_ctl to %d\n", val); + } + + hsotg->core_params->external_id_pin_ctl = val; +} + /* * This function is called during module intialization to pass module parameters * for the DWC_otg core. @@ -3023,6 +3042,7 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg, dwc2_set_param_ahbcfg(hsotg, params->ahbcfg); dwc2_set_param_otg_ver(hsotg, params->otg_ver); dwc2_set_param_uframe_sched(hsotg, params->uframe_sched); + dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl); } /** diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index e6abc28..e46304d 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -331,6 +331,11 @@ enum dwc2_ep0_state { * by the driver and are ignored in this * configuration value. * @uframe_sched: True to enable the microframe scheduler + * @external_id_pin_ctl: Specifies whether ID pin is handled externally. + * Disable CONIDSTSCHNG controller interrupt in such + * case. + * 0 - No (default) + * 1 - Yes * * The following parameters may be specified when starting the module. These * parameters define how the DWC_otg controller should be configured. A @@ -368,6 +373,7 @@ struct dwc2_core_params { int reload_ctl; int ahbcfg; int uframe_sched; + int external_id_pin_ctl; }; /** diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 4fb058b..ce39e8a0 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -77,6 +77,7 @@ static const struct dwc2_core_params params_bcm2835 = { .reload_ctl = 0, .ahbcfg = 0x10, .uframe_sched = 0, + .external_id_pin_ctl = -1, }; static const struct dwc2_core_params params_rk3066 = { @@ -105,6 +106,7 @@ static const struct dwc2_core_params params_rk3066 = { .reload_ctl = -1, .ahbcfg = 0x7, /* INCR16 */ .uframe_sched = -1, + .external_id_pin_ctl = -1, }; /** -- cgit v0.10.2 From ecb176c63ac49ddcea83b0171ead1372bb78c165 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Wed, 29 Apr 2015 22:09:05 +0200 Subject: usb: dwc2: set parameter values in probe function So the parameters can be used in both host and gadget modes. Also consolidate param functions in the core.h Acked-by: John Youn Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 8c3bc84..6acbe90 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -3044,6 +3044,7 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg, dwc2_set_param_uframe_sched(hsotg, params->uframe_sched); dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl); } +EXPORT_SYMBOL_GPL(dwc2_set_parameters); /** * During device initialization, read various hardware configuration @@ -3210,6 +3211,24 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) return 0; } +EXPORT_SYMBOL_GPL(dwc2_get_hwparams); + +/* + * Sets all parameters to the given value. + * + * Assumes that the dwc2_core_params struct contains only integers. + */ +void dwc2_set_all_params(struct dwc2_core_params *params, int value) +{ + int *p = (int *)params; + size_t size = sizeof(*params) / sizeof(*p); + int i; + + for (i = 0; i < size; i++) + p[i] = value; +} +EXPORT_SYMBOL_GPL(dwc2_set_all_params); + u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg) { diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index e46304d..d7fb1f7 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1071,6 +1071,15 @@ extern void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val); extern void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val); +extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg, + const struct dwc2_core_params *params); + +extern void dwc2_set_all_params(struct dwc2_core_params *params, int value); + +extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg); + + + /* * Dump core registers and SPRAM */ @@ -1119,14 +1128,12 @@ extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg); extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg); extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg); #else -static inline void dwc2_set_all_params(struct dwc2_core_params *params, int value) {} static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) { return 0; } static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {} -static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, - const struct dwc2_core_params *params) +static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) { return 0; } #endif diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index fbbbac2..8757f62 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -2748,8 +2748,6 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) destroy_workqueue(hsotg->wq_otg); } - kfree(hsotg->core_params); - hsotg->core_params = NULL; del_timer(&hsotg->wkp_timer); } @@ -2762,29 +2760,12 @@ static void dwc2_hcd_release(struct dwc2_hsotg *hsotg) } /* - * Sets all parameters to the given value. - * - * Assumes that the dwc2_core_params struct contains only integers. - */ -void dwc2_set_all_params(struct dwc2_core_params *params, int value) -{ - int *p = (int *)params; - size_t size = sizeof(*params) / sizeof(*p); - int i; - - for (i = 0; i < size; i++) - p[i] = value; -} -EXPORT_SYMBOL_GPL(dwc2_set_all_params); - -/* * Initializes the HCD. This function allocates memory for and initializes the * static parts of the usb_hcd and dwc2_hsotg structures. It also registers the * USB bus with the core and calls the hc_driver->start() function. It returns * a negative error on failure. */ -int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, - const struct dwc2_core_params *params) +int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) { struct usb_hcd *hcd; struct dwc2_host_chan *channel; @@ -2797,12 +2778,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n"); - /* Detect config values from hardware */ - retval = dwc2_get_hwparams(hsotg); - - if (retval) - return retval; - retval = -ENOMEM; hcfg = readl(hsotg->regs + HCFG); @@ -2821,15 +2796,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, hsotg->last_frame_num = HFNUM_MAX_FRNUM; #endif - hsotg->core_params = kzalloc(sizeof(*hsotg->core_params), GFP_KERNEL); - if (!hsotg->core_params) - goto error1; - - dwc2_set_all_params(hsotg->core_params, -1); - - /* Validate parameter values */ - dwc2_set_parameters(hsotg, params); - /* Check if the bus driver or platform code has setup a dma_mask */ if (hsotg->core_params->dma_enable > 0 && hsotg->dev->dma_mask == NULL) { diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index e69a843..7b5841c 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -451,13 +451,8 @@ static inline u8 dwc2_hcd_is_pipe_out(struct dwc2_hcd_pipe_info *pipe) return !dwc2_hcd_is_pipe_in(pipe); } -extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, - const struct dwc2_core_params *params); +extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq); extern void dwc2_hcd_remove(struct dwc2_hsotg *hsotg); -extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg, - const struct dwc2_core_params *params); -extern void dwc2_set_all_params(struct dwc2_core_params *params, int value); -extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg); /* Transaction Execution Functions */ extern enum dwc2_transaction_type dwc2_hcd_select_transactions( diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index ce39e8a0..2562c90 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -241,6 +241,21 @@ static int dwc2_driver_probe(struct platform_device *dev) spin_lock_init(&hsotg->lock); mutex_init(&hsotg->init_mutex); + /* Detect config values from hardware */ + retval = dwc2_get_hwparams(hsotg); + if (retval) + return retval; + + hsotg->core_params = devm_kzalloc(&dev->dev, + sizeof(*hsotg->core_params), GFP_KERNEL); + if (!hsotg->core_params) + return -ENOMEM; + + dwc2_set_all_params(hsotg->core_params, -1); + + /* Validate parameter values */ + dwc2_set_parameters(hsotg, params); + if (hsotg->dr_mode != USB_DR_MODE_HOST) { retval = dwc2_gadget_init(hsotg, irq); if (retval) @@ -249,7 +264,7 @@ static int dwc2_driver_probe(struct platform_device *dev) } if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) { - retval = dwc2_hcd_init(hsotg, irq, params); + retval = dwc2_hcd_init(hsotg, irq); if (retval) { if (hsotg->gadget_enabled) s3c_hsotg_remove(hsotg); -- cgit v0.10.2 From 4876886fb95f93c8b09381ffbdac969d1a1fee0d Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:06 +0200 Subject: usb: dwc2: gadget: use reset detect interrupt ResetDet interrupt is used to detect a reset of the bus while the controller is suspended. This may happens for example when using Command Verifier. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index bed56dc..f867e95 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2308,8 +2308,9 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, writel(GINTSTS_ERLYSUSP | GINTSTS_SESSREQINT | GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF | GINTSTS_CONIDSTSCHNG | GINTSTS_USBRST | - GINTSTS_ENUMDONE | GINTSTS_OTGINT | - GINTSTS_USBSUSP | GINTSTS_WKUPINT, + GINTSTS_RESETDET | GINTSTS_ENUMDONE | + GINTSTS_OTGINT | GINTSTS_USBSUSP | + GINTSTS_WKUPINT, hsotg->regs + GINTMSK); if (using_dma(hsotg)) @@ -2475,7 +2476,19 @@ irq_retry: } } - if (gintsts & GINTSTS_USBRST) { + if (gintsts & GINTSTS_RESETDET) { + dev_dbg(hsotg->dev, "%s: USBRstDet\n", __func__); + + writel(GINTSTS_RESETDET, hsotg->regs + GINTSTS); + + /* This event must be used only if controller is suspended */ + if (hsotg->lx_state == DWC2_L2) { + dwc2_exit_hibernation(hsotg, true); + hsotg->lx_state = DWC2_L0; + } + } + + if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) { u32 usb_status = readl(hsotg->regs + GOTGCTL); -- cgit v0.10.2 From 9e779778ad7e503434aa76bfc96f98d7d7b2d139 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:07 +0200 Subject: usb: dwc2: gadget: ignore pm suspend/resume in L2 Nothing to be done in pm suspend/resume when controller is in L2. Don't disconnect or reset. State is already saved when putting controller in hibernation and will be restored on USB bus resume. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index f867e95..a360f49 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3672,6 +3672,9 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) unsigned long flags; int ret = 0; + if (hsotg->lx_state != DWC2_L0) + return ret; + mutex_lock(&hsotg->init_mutex); if (hsotg->driver) { @@ -3712,6 +3715,9 @@ int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) unsigned long flags; int ret = 0; + if (hsotg->lx_state == DWC2_L2) + return ret; + mutex_lock(&hsotg->init_mutex); if (hsotg->driver) { -- cgit v0.10.2 From 7ababa926c66c5c5a862489b475ff5d96a7dd03a Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:08 +0200 Subject: usb: dwc2: gadget: prevent new request submission during suspend If usb controller is in partial power down, any write to registers may cause unpredictable behavior. Thus, prevent any new request submission once controller is in partial power down. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index a360f49..732761f 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -790,6 +790,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, ep->name, req, req->length, req->buf, req->no_interrupt, req->zero, req->short_not_ok); + /* Prevent new request submission when controller is suspended */ + if (hs->lx_state == DWC2_L2) { + dev_dbg(hs->dev, "%s: don't submit request while suspended\n", + __func__); + return -EAGAIN; + } + /* initialise status of the request */ INIT_LIST_HEAD(&hs_req->queue); req->actual = 0; -- cgit v0.10.2 From 18b2b37c59e1bcd5a61716731ec5549fb5bb0203 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:09 +0200 Subject: usb: dwc2: gadget: powerup controller if needed During vbus session, usb controller needs to exit hibernation if it was previously in suspend state. Since controller will be resetted and configured, there is no need to restore registers. Moreover, set lx_state to L0 on B session. vbus_session callback may not be used by all platforms. Thus, controller software state needs to be set to L0 if the controller detects a valid B session. Otherwise, lx_state will remain L2 and prevent any request submission. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 732761f..56a08ac 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2515,6 +2515,7 @@ irq_retry: kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); + hsotg->lx_state = DWC2_L0; s3c_hsotg_core_init_disconnected(hsotg, true); } } @@ -3205,6 +3206,14 @@ static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) spin_lock_irqsave(&hsotg->lock, flags); if (is_active) { + /* + * If controller is hibernated, it must exit from hibernation + * before being initialized + */ + if (hsotg->lx_state == DWC2_L2) { + dwc2_exit_hibernation(hsotg, false); + hsotg->lx_state = DWC2_L0; + } /* Kill any ep0 requests as controller will be reinitialized */ kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); s3c_hsotg_core_init_disconnected(hsotg, false); -- cgit v0.10.2 From 097ee6627cc8c400d77fc9a42fd787fe0fb04d76 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:10 +0200 Subject: usb: dwc2: gadget: enable otg flag in dual role configuration Inform that device is otg-capable in case of otg configuration. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 56a08ac..eb906bd 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3525,6 +3525,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.ops = &s3c_hsotg_gadget_ops; hsotg->gadget.name = dev_name(dev); + if (hsotg->dr_mode == USB_DR_MODE_OTG) + hsotg->gadget.is_otg = 1; /* reset the system */ -- cgit v0.10.2 From 31bebf4a7f0372b7b1ddc1921c61cfc67aa1e597 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Wed, 29 Apr 2015 22:09:11 +0200 Subject: usb: dwc2: gadget: remove s3c_hsotg_ep_disable_force Force argument is not used anymore. Clean up leftovers from https://lkml.org/lkml/2014/12/9/283 Acked-by: John Youn Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index eb906bd..2b73615 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2764,7 +2764,7 @@ error: * s3c_hsotg_ep_disable - disable given endpoint * @ep: The endpoint to disable. */ -static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force) +static int s3c_hsotg_ep_disable(struct usb_ep *ep) { struct s3c_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hsotg = hs_ep->parent; @@ -2807,10 +2807,6 @@ static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force) return 0; } -static int s3c_hsotg_ep_disable(struct usb_ep *ep) -{ - return s3c_hsotg_ep_disable_force(ep, false); -} /** * on_list - check request is on the given endpoint * @ep: The endpoint to check. -- cgit v0.10.2 From 9df4ceac8b359b6c261c10132fd3a49558bebd16 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Wed, 29 Apr 2015 22:09:12 +0200 Subject: usb: dwc2: host: register handle to the phy If phy driver is present register hcd handle to it. Acked-by: John Youn Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 8757f62..1875f7a 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -2913,6 +2913,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) /* Don't support SG list at this point */ hcd->self.sg_tablesize = 0; + if (!IS_ERR_OR_NULL(hsotg->uphy)) + otg_set_host(hsotg->uphy->otg, &hcd->self); + /* * Finish generic HCD initialization and start the HCD. This function * allocates the DMA buffer pool, registers the USB bus, requests the @@ -2966,6 +2969,9 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) return; } + if (!IS_ERR_OR_NULL(hsotg->uphy)) + otg_set_host(hsotg->uphy->otg, NULL); + usb_remove_hcd(hcd); hsotg->priv = NULL; dwc2_hcd_release(hsotg); -- cgit v0.10.2 From 99a657983a1e03fd3b8495199bb0a4870c1abda4 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:13 +0200 Subject: usb: dwc2: host: add bus_suspend/bus_resume callback Update controller state to indicate suspend entry. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 1875f7a..780f298 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -2313,6 +2313,22 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) usleep_range(1000, 3000); } +static int _dwc2_hcd_suspend(struct usb_hcd *hcd) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + + hsotg->lx_state = DWC2_L2; + return 0; +} + +static int _dwc2_hcd_resume(struct usb_hcd *hcd) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + + hsotg->lx_state = DWC2_L0; + return 0; +} + /* Returns the current frame number */ static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd) { @@ -2683,6 +2699,9 @@ static struct hc_driver dwc2_hc_driver = { .hub_status_data = _dwc2_hcd_hub_status_data, .hub_control = _dwc2_hcd_hub_control, .clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete, + + .bus_suspend = _dwc2_hcd_suspend, + .bus_resume = _dwc2_hcd_resume, }; /* -- cgit v0.10.2 From a7714c1cb11dc3bb97a76d2bb0560415d155b1d5 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:14 +0200 Subject: usb: dwc2: host: resume root hub on port connect Once hub is runtime suspended, dwc2 must resume it on port connect event. Else, roothub will stay in suspended state and will not resume transfers. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 551ba87..6927bba 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -350,6 +350,9 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg) dev_vdbg(hsotg->dev, "--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n", hprt0); + if (hsotg->lx_state != DWC2_L0) + usb_hcd_resume_root_hub(hsotg->priv); + hsotg->flags.b.port_connect_status_change = 1; hsotg->flags.b.port_connect_status = 1; hprt0_modify |= HPRT0_CONNDET; -- cgit v0.10.2 From 33ad261aa62be02f0cedeb4d5735cc726de84a3f Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:15 +0200 Subject: usb: dwc2: host: spinlock urb_enqueue During urb_enqueue, if the urb can't be queued to the endpoint, the urb is freed without any spinlock protection. This leads to memory corruption when concurrent urb_dequeue try to free same urb->hcpriv. Thus, ensure the whole urb_enqueue in spinlocked. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 780f298..d72557c 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -357,12 +357,12 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg) writel(0, hsotg->regs + HPRT0); } +/* Caller must hold driver lock */ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, struct dwc2_hcd_urb *urb, void **ep_handle, gfp_t mem_flags) { struct dwc2_qtd *qtd; - unsigned long flags; u32 intr_mask; int retval; int dev_speed; @@ -413,11 +413,9 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, */ return 0; - spin_lock_irqsave(&hsotg->lock, flags); tr_type = dwc2_hcd_select_transactions(hsotg); if (tr_type != DWC2_TRANSACTION_NONE) dwc2_hcd_queue_transactions(hsotg, tr_type); - spin_unlock_irqrestore(&hsotg->lock, flags); } return 0; @@ -2484,7 +2482,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, "%s: unaligned transfer with no transfer_buffer", __func__); retval = -EINVAL; - goto fail1; + goto fail0; } } @@ -2512,7 +2510,6 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, spin_lock_irqsave(&hsotg->lock, flags); retval = usb_hcd_link_urb_to_ep(hcd, urb); - spin_unlock_irqrestore(&hsotg->lock, flags); if (retval) goto fail1; @@ -2521,22 +2518,22 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, goto fail2; if (alloc_bandwidth) { - spin_lock_irqsave(&hsotg->lock, flags); dwc2_allocate_bus_bandwidth(hcd, dwc2_hcd_get_ep_bandwidth(hsotg, ep), urb); - spin_unlock_irqrestore(&hsotg->lock, flags); } + spin_unlock_irqrestore(&hsotg->lock, flags); + return 0; fail2: - spin_lock_irqsave(&hsotg->lock, flags); dwc2_urb->priv = NULL; usb_hcd_unlink_urb_from_ep(hcd, urb); - spin_unlock_irqrestore(&hsotg->lock, flags); fail1: + spin_unlock_irqrestore(&hsotg->lock, flags); urb->hcpriv = NULL; +fail0: kfree(dwc2_urb); return retval; diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index bb97838..63207dc 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -761,6 +761,7 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb) /** * dwc2_hcd_qtd_add() - Adds a QTD to the QTD-list of a QH + * Caller must hold driver lock. * * @hsotg: The DWC HCD structure * @qtd: The QTD to add @@ -777,7 +778,6 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, struct dwc2_qh **qh, gfp_t mem_flags) { struct dwc2_hcd_urb *urb = qtd->urb; - unsigned long flags; int allocated = 0; int retval; @@ -792,15 +792,12 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, allocated = 1; } - spin_lock_irqsave(&hsotg->lock, flags); - retval = dwc2_hcd_qh_add(hsotg, *qh); if (retval) goto fail; qtd->qh = *qh; list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list); - spin_unlock_irqrestore(&hsotg->lock, flags); return 0; @@ -817,10 +814,7 @@ fail: qtd_list_entry) dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp); - spin_unlock_irqrestore(&hsotg->lock, flags); dwc2_hcd_qh_free(hsotg, qh_tmp); - } else { - spin_unlock_irqrestore(&hsotg->lock, flags); } return retval; -- cgit v0.10.2 From db62b9a804b465f5050438eb06151c99c625ec9a Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:16 +0200 Subject: usb: dwc2: host: don't use dma_alloc_coherent with irqs disabled Align buffer must be allocated using kmalloc since irqs are disabled. Coherency is handled through dma_map_single which can be used with irqs disabled. Reviewed-by: Julius Werner Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index d72557c..745230d 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -719,9 +719,7 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, /* 3072 = 3 max-size Isoc packets */ buf_size = 3072; - qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size, - &qh->dw_align_buf_dma, - GFP_ATOMIC); + qh->dw_align_buf = kmalloc(buf_size, GFP_ATOMIC | GFP_DMA); if (!qh->dw_align_buf) return -ENOMEM; qh->dw_align_buf_size = buf_size; @@ -746,6 +744,15 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, } } + qh->dw_align_buf_dma = dma_map_single(hsotg->dev, + qh->dw_align_buf, qh->dw_align_buf_size, + chan->ep_is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) { + dev_err(hsotg->dev, "can't map align_buf\n"); + chan->align_buf = (dma_addr_t)NULL; + return -EINVAL; + } + chan->align_buf = qh->dw_align_buf_dma; return 0; } diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 6927bba..6ea8eb6 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -466,10 +466,15 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg, } /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && xfer_length && chan->ep_is_in) { + if (chan->align_buf && xfer_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, - xfer_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + urb->actual_length, + chan->qh->dw_align_buf, xfer_length); } dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n", @@ -555,13 +560,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan, chnum, qtd, halt_status, NULL); /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && frame_desc->actual_length && - chan->ep_is_in) { + if (chan->align_buf && frame_desc->actual_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + frame_desc->offset + - qtd->isoc_split_offset, chan->qh->dw_align_buf, - frame_desc->actual_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + frame_desc->offset + + qtd->isoc_split_offset, + chan->qh->dw_align_buf, + frame_desc->actual_length); } break; case DWC2_HC_XFER_FRAME_OVERRUN: @@ -584,13 +594,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan, chnum, qtd, halt_status, NULL); /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && frame_desc->actual_length && - chan->ep_is_in) { + if (chan->align_buf && frame_desc->actual_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + frame_desc->offset + - qtd->isoc_split_offset, chan->qh->dw_align_buf, - frame_desc->actual_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + frame_desc->offset + + qtd->isoc_split_offset, + chan->qh->dw_align_buf, + frame_desc->actual_length); } /* Skip whole frame */ @@ -926,6 +941,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, if (chan->align_buf) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, DMA_FROM_DEVICE); memcpy(qtd->urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, len); } @@ -1155,8 +1172,14 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg, /* Non DWORD-aligned buffer case handling */ if (chan->align_buf && xfer_length && chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, - xfer_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + urb->actual_length, + chan->qh->dw_align_buf, + xfer_length); } urb->actual_length += xfer_length; diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 63207dc..9b5c362 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -229,11 +229,13 @@ static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, */ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { - if (hsotg->core_params->dma_desc_enable > 0) + if (hsotg->core_params->dma_desc_enable > 0) { dwc2_hcd_qh_free_ddma(hsotg, qh); - else if (qh->dw_align_buf) - dma_free_coherent(hsotg->dev, qh->dw_align_buf_size, - qh->dw_align_buf, qh->dw_align_buf_dma); + } else { + /* kfree(NULL) is safe */ + kfree(qh->dw_align_buf); + qh->dw_align_buf_dma = (dma_addr_t)0; + } kfree(qh); } -- cgit v0.10.2 From 96d480e65ea0e4e950f75029b8a1ff4c1269f8b0 Mon Sep 17 00:00:00 2001 From: Jingwu Lin Date: Wed, 29 Apr 2015 22:09:17 +0200 Subject: usb: dwc2: host: implement test mode Add support for SetPortFeature(PORT_TEST) for root port. Acked-by: John Youn Signed-off-by: Jingwu Lin Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 745230d..4773d27 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -1779,6 +1779,15 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, /* Not supported */ break; + case USB_PORT_FEAT_TEST: + hprt0 = dwc2_read_hprt0(hsotg); + dev_dbg(hsotg->dev, + "SetPortFeature - USB_PORT_FEAT_TEST\n"); + hprt0 &= ~HPRT0_TSTCTL_MASK; + hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT; + writel(hprt0, hsotg->regs + HPRT0); + break; + default: retval = -EINVAL; dev_err(hsotg->dev, -- cgit v0.10.2 From 2d1165a4b95e25aed83fed737d53ab0c87b831e6 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Wed, 29 Apr 2015 22:09:18 +0200 Subject: usb: dwc2: remove dwc2_platform.ko As dwc2 pci module is now exporting dwc2 platform device, include platform.o in dwc2-y and remove USB_DWC2_PLATFORM configuration option. Driver will be built as two modules, dwc2.ko and dwc2_pci.ko. dwc2.ko is the new platform driver. Remove all EXPORT_SYMBOL_GPL as they are not needed any more. Acked-by: John Youn Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index 1bcb36a..fd95ba6 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -50,18 +50,10 @@ config USB_DWC2_DUAL_ROLE option requires USB_GADGET to be enabled. endchoice -config USB_DWC2_PLATFORM - tristate "DWC2 Platform" - default USB_DWC2_HOST || USB_DWC2_PERIPHERAL - help - The Designware USB2.0 platform interface module for - controllers directly connected to the CPU. - config USB_DWC2_PCI tristate "DWC2 PCI" depends on PCI default n - select USB_DWC2_PLATFORM select NOP_USB_XCEIV help The Designware USB2.0 PCI interface module for controllers diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile index 324fbb4..50fdaac 100644 --- a/drivers/usb/dwc2/Makefile +++ b/drivers/usb/dwc2/Makefile @@ -2,7 +2,7 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_DWC2) += dwc2.o -dwc2-y := core.o core_intr.o +dwc2-y := core.o core_intr.o platform.o ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),) dwc2-y += hcd.o hcd_intr.o @@ -25,6 +25,3 @@ endif obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o dwc2_pci-y := pci.o - -obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o -dwc2_platform-y := platform.o diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 6acbe90..7f461e3 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -3044,7 +3044,6 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg, dwc2_set_param_uframe_sched(hsotg, params->uframe_sched); dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl); } -EXPORT_SYMBOL_GPL(dwc2_set_parameters); /** * During device initialization, read various hardware configuration @@ -3211,7 +3210,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) return 0; } -EXPORT_SYMBOL_GPL(dwc2_get_hwparams); /* * Sets all parameters to the given value. @@ -3227,7 +3225,6 @@ void dwc2_set_all_params(struct dwc2_core_params *params, int value) for (i = 0; i < size; i++) p[i] = value; } -EXPORT_SYMBOL_GPL(dwc2_set_all_params); u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 0b7f2b2..9e510bb 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -559,4 +559,3 @@ out: spin_unlock(&hsotg->lock); return retval; } -EXPORT_SYMBOL_GPL(dwc2_handle_common_intr); diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index af89537..ef2ee3d 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -763,11 +763,9 @@ err1: err0: return ret; } -EXPORT_SYMBOL_GPL(dwc2_debugfs_init); void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg) { debugfs_remove_recursive(hsotg->debug_root); hsotg->debug_root = NULL; } -EXPORT_SYMBOL_GPL(dwc2_debugfs_exit); diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 2b73615..4d47b7c 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2190,7 +2190,6 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg) call_gadget(hsotg, disconnect); } -EXPORT_SYMBOL_GPL(s3c_hsotg_disconnect); /** * s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler @@ -3666,7 +3665,6 @@ err_clk: return ret; } -EXPORT_SYMBOL_GPL(dwc2_gadget_init); /** * s3c_hsotg_remove - remove function for hsotg driver @@ -3679,7 +3677,6 @@ int s3c_hsotg_remove(struct dwc2_hsotg *hsotg) return 0; } -EXPORT_SYMBOL_GPL(s3c_hsotg_remove); int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) { @@ -3722,7 +3719,6 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) return ret; } -EXPORT_SYMBOL_GPL(s3c_hsotg_suspend); int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) { @@ -3754,4 +3750,3 @@ int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) return ret; } -EXPORT_SYMBOL_GPL(s3c_hsotg_resume); diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 4773d27..d9b8cc3 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -2980,7 +2980,6 @@ error1: dev_err(hsotg->dev, "%s() FAILED, returning %d\n", __func__, retval); return retval; } -EXPORT_SYMBOL_GPL(dwc2_hcd_init); /* * Removes the HCD. @@ -3014,4 +3013,3 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) kfree(hsotg->frame_num_array); #endif } -EXPORT_SYMBOL_GPL(dwc2_hcd_remove); -- cgit v0.10.2 From 285046aa11ad85a4de24891f5458d45f50d1bcc5 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:19 +0200 Subject: usb: dwc2: add hibernation core parameter dwc2 may not be able to exit from hibernation if the hardware does not provide a way to detect resume signalling in this state. Thus, add the possibility to disable hibernation feature. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 7f461e3..e5b546f 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -342,6 +342,9 @@ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore) u32 pcgcctl; int ret = 0; + if (!hsotg->core_params->hibernation) + return -ENOTSUPP; + pcgcctl = readl(hsotg->regs + PCGCTL); pcgcctl &= ~PCGCTL_STOPPCLK; writel(pcgcctl, hsotg->regs + PCGCTL); @@ -392,6 +395,9 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) u32 pcgcctl; int ret = 0; + if (!hsotg->core_params->hibernation) + return -ENOTSUPP; + /* Backup all registers */ ret = dwc2_backup_global_registers(hsotg); if (ret) { @@ -2998,6 +3004,23 @@ static void dwc2_set_param_external_id_pin_ctl(struct dwc2_hsotg *hsotg, hsotg->core_params->external_id_pin_ctl = val; } +static void dwc2_set_param_hibernation(struct dwc2_hsotg *hsotg, + int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "'%d' invalid for parameter hibernation\n", + val); + dev_err(hsotg->dev, "hibernation must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting hibernation to %d\n", val); + } + + hsotg->core_params->hibernation = val; +} + /* * This function is called during module intialization to pass module parameters * for the DWC_otg core. @@ -3043,6 +3066,7 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg, dwc2_set_param_otg_ver(hsotg, params->otg_ver); dwc2_set_param_uframe_sched(hsotg, params->uframe_sched); dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl); + dwc2_set_param_hibernation(hsotg, params->hibernation); } /** diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index d7fb1f7..53b8de0 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -336,6 +336,12 @@ enum dwc2_ep0_state { * case. * 0 - No (default) * 1 - Yes + * @hibernation: Specifies whether the controller support hibernation. + * If hibernation is enabled, the controller will enter + * hibernation in both peripheral and host mode when + * needed. + * 0 - No (default) + * 1 - Yes * * The following parameters may be specified when starting the module. These * parameters define how the DWC_otg controller should be configured. A @@ -374,6 +380,7 @@ struct dwc2_core_params { int ahbcfg; int uframe_sched; int external_id_pin_ctl; + int hibernation; }; /** diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 9e510bb..927be1e 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -347,7 +347,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) dctl &= ~DCTL_RMTWKUPSIG; writel(dctl, hsotg->regs + DCTL); ret = dwc2_exit_hibernation(hsotg, true); - if (ret) + if (ret && (ret != -ENOTSUPP)) dev_err(hsotg->dev, "exit hibernation failed\n"); call_gadget(hsotg, resume); @@ -428,8 +428,9 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) ret = dwc2_enter_hibernation(hsotg); if (ret) { - dev_err(hsotg->dev, - "enter hibernation failed\n"); + if (ret != -ENOTSUPP) + dev_err(hsotg->dev, + "enter hibernation failed\n"); goto skip_power_saving; } diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 2562c90..9093530 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -78,6 +78,7 @@ static const struct dwc2_core_params params_bcm2835 = { .ahbcfg = 0x10, .uframe_sched = 0, .external_id_pin_ctl = -1, + .hibernation = -1, }; static const struct dwc2_core_params params_rk3066 = { @@ -107,6 +108,7 @@ static const struct dwc2_core_params params_rk3066 = { .ahbcfg = 0x7, /* INCR16 */ .uframe_sched = -1, .external_id_pin_ctl = -1, + .hibernation = -1, }; /** -- cgit v0.10.2 From e499123ed780df64a35e6cc0a8c892b282fa71a4 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:20 +0200 Subject: usb: dwc2: host: ensure qtb exists before dereferencing it dwc2_hc_nak_intr could be called with a NULL qtd. Ensure qtd exists before dereferencing it to avoid kernel panic. This happens when using usb to ethernet adapter. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 6ea8eb6..4cc95df 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -1208,6 +1208,16 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, int chnum, struct dwc2_qtd *qtd) { + if (!qtd) { + dev_dbg(hsotg->dev, "%s: qtd is NULL\n", __func__); + return; + } + + if (!qtd->urb) { + dev_dbg(hsotg->dev, "%s: qtd->urb is NULL\n", __func__); + return; + } + if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: NAK Received--\n", chnum); -- cgit v0.10.2 From f8e9f34f80a21540ebf8ba26877568124ca096b0 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 1 May 2015 12:29:27 -0700 Subject: usb: musb: Fix up DMA related macros Pass struct musb to tusb_dma_omap() and is_cppi_enabled(), and add macros for the other DMA controllers. Populate the platform specific quirks with the DMA type and use it during runtime. Note that platform glue layers with no custom DMA code are tagged with MUSB_DMA_INVENTRA which may have a chance of working. Looks like the defconfigs for these use PIO_ONLY, so this should not break existing configs. Signed-off-by: Tony Lindgren Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 220fd4d..72ce2e8 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -438,7 +438,7 @@ static void am35x_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } static const struct musb_platform_ops am35x_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, .init = am35x_musb_init, .exit = am35x_musb_exit, diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 6123b74..2a73a73 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -465,6 +465,7 @@ static int bfin_musb_exit(struct musb *musb) } static const struct musb_platform_ops bfin_ops = { + .quirks = MUSB_DMA_INVENTRA, .init = bfin_musb_init, .exit = bfin_musb_exit, diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 9a9c82a..06c442c 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -458,7 +458,7 @@ static int da8xx_musb_exit(struct musb *musb) } static const struct musb_platform_ops da8xx_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_CPPI | MUSB_INDEXED_EP, .init = da8xx_musb_init, .exit = da8xx_musb_exit, diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 3c1d9b2..26bfdb3 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -284,7 +284,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci) * mask, state, "vector", and EOI registers. */ cppi = container_of(musb->dma_controller, struct cppi, controller); - if (is_cppi_enabled() && musb->dma_controller && !cppi->irq) + if (is_cppi_enabled(musb) && musb->dma_controller && !cppi->irq) retval = cppi_interrupt(irq, __hci); /* ack and handle non-CPPI interrupts */ @@ -491,6 +491,7 @@ static int davinci_musb_exit(struct musb *musb) } static const struct musb_platform_ops davinci_ops = { + .quirks = MUSB_DMA_CPPI, .init = davinci_musb_init, .exit = davinci_musb_exit, diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index bb7b263..b7b5fdc 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -106,7 +106,7 @@ static int jz4740_musb_exit(struct musb *musb) } static const struct musb_platform_ops jz4740_musb_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, .fifo_mode = 2, .init = jz4740_musb_init, .exit = jz4740_musb_exit, diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 3789b08..4ac060a 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1659,7 +1659,7 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) if (!epnum) { #ifndef CONFIG_USB_TUSB_OMAP_DMA - if (!is_cppi_enabled()) { + if (!is_cppi_enabled(musb)) { /* endpoint 0 */ if (is_host_active(musb)) musb_h_ep0_irq(musb); diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 1d44faa..555f2ae 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -68,16 +68,41 @@ struct musb_hw_ep; #define is_dma_capable() (1) #endif -#if defined(CONFIG_USB_TI_CPPI_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA) -#define is_cppi_enabled() 1 +#ifdef CONFIG_USB_UX500_DMA +#define musb_dma_ux500(musb) (musb->io.quirks & MUSB_DMA_UX500) +#else +#define musb_dma_ux500(musb) 0 +#endif + +#ifdef CONFIG_USB_TI_CPPI41_DMA +#define musb_dma_cppi41(musb) (musb->io.quirks & MUSB_DMA_CPPI41) +#else +#define musb_dma_cppi41(musb) 0 +#endif + +#ifdef CONFIG_USB_TI_CPPI_DMA +#define musb_dma_cppi(musb) (musb->io.quirks & MUSB_DMA_CPPI) #else -#define is_cppi_enabled() 0 +#define musb_dma_cppi(musb) 0 #endif #ifdef CONFIG_USB_TUSB_OMAP_DMA -#define tusb_dma_omap() 1 +#define tusb_dma_omap(musb) (musb->io.quirks & MUSB_DMA_TUSB_OMAP) +#else +#define tusb_dma_omap(musb) 0 +#endif + +#ifdef CONFIG_USB_INVENTRA_DMA +#define musb_dma_inventra(musb) (musb->io.quirks & MUSB_DMA_INVENTRA) +#else +#define musb_dma_inventra(musb) 0 +#endif + +#if defined(CONFIG_USB_TI_CPPI_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA) +#define is_cppi_enabled(musb) \ + (musb_dma_cppi(musb) || musb_dma_cppi41(musb)) #else -#define tusb_dma_omap() 0 +#define is_cppi_enabled(musb) 0 #endif /* Anomaly 05000456 - USB Receive Interrupt Is Not Generated in DMA Mode 1 diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 65d931a..63a8d5b 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -634,7 +634,7 @@ static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } static struct musb_platform_ops dsps_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_CPPI41 | MUSB_INDEXED_EP, .init = dsps_musb_init, .exit = dsps_musb_exit, diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 4c481cd..a94db12 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -366,7 +366,7 @@ static void txstate(struct musb *musb, struct musb_request *req) } #endif - if (is_cppi_enabled()) { + if (is_cppi_enabled(musb)) { /* program endpoint CSR first, then setup DMA */ csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE | @@ -402,7 +402,7 @@ static void txstate(struct musb *musb, struct musb_request *req) musb_writew(epio, MUSB_TXCSR, csr); /* invariant: prequest->buf is non-null */ } - } else if (tusb_dma_omap()) + } else if (tusb_dma_omap(musb)) use_dma = use_dma && c->channel_program( musb_ep->dma, musb_ep->packet_sz, request->zero, @@ -595,7 +595,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) return; } - if (is_cppi_enabled() && is_buffer_mapped(req)) { + if (is_cppi_enabled(musb) && is_buffer_mapped(req)) { struct dma_controller *c = musb->dma_controller; struct dma_channel *channel = musb_ep->dma; @@ -772,7 +772,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) fifo_count = min_t(unsigned, len, fifo_count); #ifdef CONFIG_USB_TUSB_OMAP_DMA - if (tusb_dma_omap() && is_buffer_mapped(req)) { + if (tusb_dma_omap(musb) && is_buffer_mapped(req)) { struct dma_controller *c = musb->dma_controller; struct dma_channel *channel = musb_ep->dma; u32 dma_addr = request->dma + request->actual; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index e1fb5d8..696396e 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -181,7 +181,7 @@ static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep) /* NOTE: no locks here; caller should lock and select EP */ txcsr = musb_readw(ep->regs, MUSB_TXCSR); txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS; - if (is_cppi_enabled()) + if (is_cppi_enabled(ep->musb)) txcsr |= MUSB_TXCSR_DMAMODE; musb_writew(ep->regs, MUSB_TXCSR, txcsr); } @@ -294,7 +294,7 @@ start: if (!hw_ep->tx_channel) musb_h_tx_start(hw_ep); - else if (is_cppi_enabled() || tusb_dma_omap()) + else if (is_cppi_enabled(musb) || tusb_dma_omap(musb)) musb_h_tx_dma_start(hw_ep); } } @@ -656,7 +656,7 @@ static bool musb_tx_dma_program(struct dma_controller *dma, channel->desired_mode = mode; musb_writew(epio, MUSB_TXCSR, csr); #else - if (!is_cppi_enabled() && !tusb_dma_omap()) + if (!is_cppi_enabled(hw_ep->musb) && !tusb_dma_omap(hw_ep->musb)) return false; channel->actual_len = 0; @@ -901,7 +901,7 @@ finish: /* kick things off */ - if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) { + if ((is_cppi_enabled(musb) || tusb_dma_omap(musb)) && dma_channel) { /* Candidate for DMA */ dma_channel->actual_len = 0L; qh->segsize = len; @@ -1441,7 +1441,7 @@ done: } else if ((usb_pipeisoc(pipe) || transfer_pending) && dma) { if (musb_tx_dma_program(musb->dma_controller, hw_ep, qh, urb, offset, length)) { - if (is_cppi_enabled() || tusb_dma_omap()) + if (is_cppi_enabled(musb) || tusb_dma_omap(musb)) musb_h_tx_dma_start(hw_ep); return; } diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index cc752d8..5546a22 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -493,6 +493,7 @@ static int omap2430_musb_exit(struct musb *musb) } static const struct musb_platform_ops omap2430_ops = { + .quirks = MUSB_DMA_INVENTRA, .init = omap2430_musb_init, .exit = omap2430_musb_exit, diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 3a5ffd5..af923de 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -890,7 +890,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) dev_dbg(musb->controller, "DMA IRQ %08x\n", dma_src); real_dma_src = ~real_dma_src & dma_src; - if (tusb_dma_omap() && real_dma_src) { + if (tusb_dma_omap(musb) && real_dma_src) { int tx_source = (real_dma_src & 0xffff); int i; @@ -1181,7 +1181,7 @@ static int tusb_musb_exit(struct musb *musb) } static const struct musb_platform_ops tusb_ops = { - .quirks = MUSB_IN_TUSB, + .quirks = MUSB_DMA_TUSB_OMAP | MUSB_IN_TUSB, .init = tusb_musb_init, .exit = tusb_musb_exit, diff --git a/drivers/usb/musb/tusb6010.h b/drivers/usb/musb/tusb6010.h index aec86c8..72cdad2 100644 --- a/drivers/usb/musb/tusb6010.h +++ b/drivers/usb/musb/tusb6010.h @@ -12,12 +12,6 @@ #ifndef __TUSB6010_H__ #define __TUSB6010_H__ -#ifdef CONFIG_USB_TUSB_OMAP_DMA -#define tusb_dma_omap() 1 -#else -#define tusb_dma_omap() 0 -#endif - /* VLYNQ control register. 32-bit at offset 0x000 */ #define TUSB_VLYNQ_CTRL 0x004 diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index abf7272..c6582f1 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -188,7 +188,7 @@ static int ux500_musb_exit(struct musb *musb) } static const struct musb_platform_ops ux500_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_UX500 | MUSB_INDEXED_EP, .init = ux500_musb_init, .exit = ux500_musb_exit, .fifo_mode = 5, -- cgit v0.10.2 From 7f6283ed6fe867ce168ee3eea2ced4f6cdeeb37a Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 1 May 2015 12:29:28 -0700 Subject: usb: musb: Set up function pointers for DMA Set up function pointers for DMA so get closer to being able to build in all the DMA engines. Signed-off-by: Tony Lindgren Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 72ce2e8..6cfd43a 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -443,6 +443,10 @@ static const struct musb_platform_ops am35x_ops = { .exit = am35x_musb_exit, .read_fifo = am35x_read_fifo, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif .enable = am35x_musb_enable, .disable = am35x_musb_disable, diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 2a73a73..310238c 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -478,6 +478,10 @@ static const struct musb_platform_ops bfin_ops = { .fifo_mode = 2, .read_fifo = bfin_read_fifo, .write_fifo = bfin_write_fifo, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif .enable = bfin_musb_enable, .disable = bfin_musb_disable, diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index 904fb85..cc13410 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -1297,7 +1297,8 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id) EXPORT_SYMBOL_GPL(cppi_interrupt); /* Instantiate a software object representing a DMA controller. */ -struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mregs) +struct dma_controller * +cppi_dma_controller_create(struct musb *musb, void __iomem *mregs) { struct cppi *controller; struct device *dev = musb->controller; @@ -1334,7 +1335,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mr if (irq > 0) { if (request_irq(irq, cppi_interrupt, 0, "cppi-dma", musb)) { dev_err(dev, "request_irq %d failed!\n", irq); - dma_controller_destroy(&controller->controller); + musb_dma_controller_destroy(&controller->controller); return NULL; } controller->irq = irq; @@ -1343,11 +1344,12 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mr cppi_controller_start(controller); return &controller->controller; } +EXPORT_SYMBOL_GPL(cppi_dma_controller_create); /* * Destroy a previously-instantiated DMA controller. */ -void dma_controller_destroy(struct dma_controller *c) +void cppi_dma_controller_destroy(struct dma_controller *c) { struct cppi *cppi; @@ -1363,6 +1365,7 @@ void dma_controller_destroy(struct dma_controller *c) kfree(cppi); } +EXPORT_SYMBOL_GPL(cppi_dma_controller_destroy); /* * Context: controller irqlocked, endpoint selected diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 06c442c..b03d3b8 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -463,6 +463,10 @@ static const struct musb_platform_ops da8xx_ops = { .exit = da8xx_musb_exit, .fifo_mode = 2, +#ifdef CONFIG_USB_TI_CPPI_DMA + .dma_init = cppi_dma_controller_create, + .dma_exit = cppi_dma_controller_destroy, +#endif .enable = da8xx_musb_enable, .disable = da8xx_musb_disable, diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 26bfdb3..cee61a5 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -495,6 +495,10 @@ static const struct musb_platform_ops davinci_ops = { .init = davinci_musb_init, .exit = davinci_musb_exit, +#ifdef CONFIG_USB_TI_CPPI_DMA + .dma_init = cppi_dma_controller_create, + .dma_exit = cppi_dma_controller_destroy, +#endif .enable = davinci_musb_enable, .disable = davinci_musb_disable, diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index b7b5fdc..5e5a8fa 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -105,6 +105,10 @@ static int jz4740_musb_exit(struct musb *musb) return 0; } +/* + * DMA has not been confirmed to work with CONFIG_USB_INVENTRA_DMA, + * so let's not set up the dma function pointers yet. + */ static const struct musb_platform_ops jz4740_musb_ops = { .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, .fifo_mode = 2, diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 4ac060a..5f35ecd 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -389,6 +389,15 @@ EXPORT_SYMBOL_GPL(musb_readl); void (*musb_writel)(void __iomem *addr, unsigned offset, u32 data); EXPORT_SYMBOL_GPL(musb_writel); +#ifndef CONFIG_MUSB_PIO_ONLY +struct dma_controller * +(*musb_dma_controller_create)(struct musb *musb, void __iomem *base); +EXPORT_SYMBOL(musb_dma_controller_create); + +void (*musb_dma_controller_destroy)(struct dma_controller *c); +EXPORT_SYMBOL(musb_dma_controller_destroy); +#endif + /* * New style IO functions */ @@ -2059,6 +2068,15 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) if (musb->ops->writel) musb_writel = musb->ops->writel; +#ifndef CONFIG_MUSB_PIO_ONLY + if (!musb->ops->dma_init || !musb->ops->dma_exit) { + dev_err(dev, "DMA controller not set\n"); + goto fail2; + } + musb_dma_controller_create = musb->ops->dma_init; + musb_dma_controller_destroy = musb->ops->dma_exit; +#endif + if (musb->ops->read_fifo) musb->io.read_fifo = musb->ops->read_fifo; else @@ -2078,7 +2096,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) pm_runtime_get_sync(musb->controller); if (use_dma && dev->dma_mask) { - musb->dma_controller = dma_controller_create(musb, musb->mregs); + musb->dma_controller = + musb_dma_controller_create(musb, musb->mregs); if (IS_ERR(musb->dma_controller)) { status = PTR_ERR(musb->dma_controller); goto fail2_5; @@ -2189,7 +2208,7 @@ fail3: cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); if (musb->dma_controller) - dma_controller_destroy(musb->dma_controller); + musb_dma_controller_destroy(musb->dma_controller); fail2_5: pm_runtime_put_sync(musb->controller); @@ -2248,7 +2267,7 @@ static int musb_remove(struct platform_device *pdev) musb_shutdown(pdev); if (musb->dma_controller) - dma_controller_destroy(musb->dma_controller); + musb_dma_controller_destroy(musb->dma_controller); cancel_work_sync(&musb->irq_work); cancel_delayed_work_sync(&musb->finish_resume_work); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 3877249..c7a0d93 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -157,6 +157,8 @@ struct musb_io; * @writel: write 32 bits * @read_fifo: reads the fifo * @write_fifo: writes to fifo + * @dma_init: platform specific dma init function + * @dma_exit: platform specific dma exit function * @init: turns on clocks, sets up platform-specific registers, etc * @exit: undoes @init * @set_mode: forcefully changes operating mode @@ -195,6 +197,9 @@ struct musb_platform_ops { void (*writel)(void __iomem *addr, unsigned offset, u32 data); void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); + struct dma_controller * + (*dma_init) (struct musb *musb, void __iomem *base); + void (*dma_exit)(struct dma_controller *c); int (*set_mode)(struct musb *musb, u8 mode); void (*try_idle)(struct musb *musb, unsigned long timeout); int (*recover)(struct musb *musb); diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 8bd8c5e..4d1b44c 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -678,7 +678,7 @@ err: return ret; } -void dma_controller_destroy(struct dma_controller *c) +void cppi41_dma_controller_destroy(struct dma_controller *c) { struct cppi41_dma_controller *controller = container_of(c, struct cppi41_dma_controller, controller); @@ -687,9 +687,10 @@ void dma_controller_destroy(struct dma_controller *c) cppi41_dma_controller_stop(controller); kfree(controller); } +EXPORT_SYMBOL_GPL(cppi41_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, - void __iomem *base) +struct dma_controller * +cppi41_dma_controller_create(struct musb *musb, void __iomem *base) { struct cppi41_dma_controller *controller; int ret = 0; @@ -726,3 +727,4 @@ kzalloc_fail: return ERR_PTR(ret); return NULL; } +EXPORT_SYMBOL_GPL(cppi41_dma_controller_create); diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 555f2ae..46357e1 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -202,19 +202,41 @@ struct dma_controller { extern void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit); #ifdef CONFIG_MUSB_PIO_ONLY -static inline struct dma_controller *dma_controller_create(struct musb *m, - void __iomem *io) +static inline struct dma_controller * +musb_dma_controller_create(struct musb *m, void __iomem *io) { return NULL; } -static inline void dma_controller_destroy(struct dma_controller *d) { } +static inline void musb_dma_controller_destroy(struct dma_controller *d) { } #else -extern struct dma_controller *dma_controller_create(struct musb *, void __iomem *); +extern struct dma_controller * +(*musb_dma_controller_create)(struct musb *, void __iomem *); -extern void dma_controller_destroy(struct dma_controller *); +extern void (*musb_dma_controller_destroy)(struct dma_controller *); #endif +/* Platform specific DMA functions */ +extern struct dma_controller * +musbhs_dma_controller_create(struct musb *musb, void __iomem *base); +extern void musbhs_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +tusb_dma_controller_create(struct musb *musb, void __iomem *base); +extern void tusb_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +cppi_dma_controller_create(struct musb *musb, void __iomem *base); +extern void cppi_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +cppi41_dma_controller_create(struct musb *musb, void __iomem *base); +extern void cppi41_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +ux500_dma_controller_create(struct musb *musb, void __iomem *base); +extern void ux500_dma_controller_destroy(struct dma_controller *c); + #endif /* __MUSB_DMA_H__ */ diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 63a8d5b..1334a3d 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -638,6 +638,10 @@ static struct musb_platform_ops dsps_ops = { .init = dsps_musb_init, .exit = dsps_musb_exit, +#ifdef CONFIG_USB_TI_CPPI41_DMA + .dma_init = cppi41_dma_controller_create, + .dma_exit = cppi41_dma_controller_destroy, +#endif .enable = dsps_musb_enable, .disable = dsps_musb_disable, diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index ab7ec09..7539c31 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -357,7 +357,7 @@ done: return retval; } -void dma_controller_destroy(struct dma_controller *c) +void musbhs_dma_controller_destroy(struct dma_controller *c) { struct musb_dma_controller *controller = container_of(c, struct musb_dma_controller, controller); @@ -369,8 +369,10 @@ void dma_controller_destroy(struct dma_controller *c) kfree(controller); } +EXPORT_SYMBOL_GPL(musbhs_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *base) +struct dma_controller *musbhs_dma_controller_create(struct musb *musb, + void __iomem *base) { struct musb_dma_controller *controller; struct device *dev = musb->controller; @@ -398,7 +400,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba if (request_irq(irq, dma_controller_irq, 0, dev_name(musb->controller), &controller->controller)) { dev_err(dev, "request_irq %d failed!\n", irq); - dma_controller_destroy(&controller->controller); + musb_dma_controller_destroy(&controller->controller); return NULL; } @@ -407,3 +409,4 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba return &controller->controller; } +EXPORT_SYMBOL_GPL(musbhs_dma_controller_create); diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 5546a22..70f2b8a 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -494,6 +494,10 @@ static int omap2430_musb_exit(struct musb *musb) static const struct musb_platform_ops omap2430_ops = { .quirks = MUSB_DMA_INVENTRA, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif .init = omap2430_musb_init, .exit = omap2430_musb_exit, diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index af923de..df7c9f4 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1192,6 +1192,10 @@ static const struct musb_platform_ops tusb_ops = { .writeb = tusb_writeb, .read_fifo = tusb_read_fifo, .write_fifo = tusb_write_fifo, +#ifdef CONFIG_USB_TUSB_OMAP_DMA + .dma_init = tusb_dma_controller_create, + .dma_exit = tusb_dma_controller_destroy, +#endif .enable = tusb_musb_enable, .disable = tusb_musb_disable, diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 3ce152c..4c82077 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -625,7 +625,7 @@ static void tusb_omap_dma_release(struct dma_channel *channel) channel = NULL; } -void dma_controller_destroy(struct dma_controller *c) +void tusb_dma_controller_destroy(struct dma_controller *c) { struct tusb_omap_dma *tusb_dma; int i; @@ -644,8 +644,10 @@ void dma_controller_destroy(struct dma_controller *c) kfree(tusb_dma); } +EXPORT_SYMBOL_GPL(tusb_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *base) +struct dma_controller * +tusb_dma_controller_create(struct musb *musb, void __iomem *base) { void __iomem *tbase = musb->ctrl_base; struct tusb_omap_dma *tusb_dma; @@ -701,7 +703,8 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba return &tusb_dma->controller; cleanup: - dma_controller_destroy(&tusb_dma->controller); + musb_dma_controller_destroy(&tusb_dma->controller); out: return NULL; } +EXPORT_SYMBOL_GPL(tusb_dma_controller_create); diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index c6582f1..2967b51 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -189,6 +189,10 @@ static int ux500_musb_exit(struct musb *musb) static const struct musb_platform_ops ux500_ops = { .quirks = MUSB_DMA_UX500 | MUSB_INDEXED_EP, +#ifdef CONFIG_USB_UX500_DMA + .dma_init = ux500_dma_controller_create, + .dma_exit = ux500_dma_controller_destroy, +#endif .init = ux500_musb_init, .exit = ux500_musb_exit, .fifo_mode = 5, diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index e93845c..d0b6a1c 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -359,7 +359,7 @@ static int ux500_dma_controller_start(struct ux500_dma_controller *controller) return 0; } -void dma_controller_destroy(struct dma_controller *c) +void ux500_dma_controller_destroy(struct dma_controller *c) { struct ux500_dma_controller *controller = container_of(c, struct ux500_dma_controller, controller); @@ -367,9 +367,10 @@ void dma_controller_destroy(struct dma_controller *c) ux500_dma_controller_stop(controller); kfree(controller); } +EXPORT_SYMBOL_GPL(ux500_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, - void __iomem *base) +struct dma_controller * +ux500_dma_controller_create(struct musb *musb, void __iomem *base) { struct ux500_dma_controller *controller; struct platform_device *pdev = to_platform_device(musb->controller); @@ -407,3 +408,4 @@ plat_get_fail: kzalloc_fail: return NULL; } +EXPORT_SYMBOL_GPL(ux500_dma_controller_create); -- cgit v0.10.2 From 729697a13d7e32b18315df474fbf42498b447a8a Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 1 May 2015 12:29:29 -0700 Subject: usb: musb: Get rid of the DMA ifdefs for musb_core.c For musb_core.c we can now just drop the DMA related ifdef and use the already existing runtime test for !is_cppi_enabled(musb) instead. Signed-off-by: Tony Lindgren Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 5f35ecd..237f7c8 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1667,7 +1667,6 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) /* called with controller lock already held */ if (!epnum) { -#ifndef CONFIG_USB_TUSB_OMAP_DMA if (!is_cppi_enabled(musb)) { /* endpoint 0 */ if (is_host_active(musb)) @@ -1675,7 +1674,6 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) else musb_g_ep0_irq(musb); } -#endif } else { /* endpoints 1..15 */ if (transmit) { -- cgit v0.10.2 From fb91cddc54e71a09b31e0fdf2d45abeaea850113 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 1 May 2015 12:29:30 -0700 Subject: usb: musb: Remove DMA ifdef for musb_gadget.c short_packet Let's get rid of the horrible ifdef in middle of the expression. We can do it by adding a variable for short_packet and testing it separately for DMA related code. Signed-off-by: Tony Lindgren Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index a94db12..625d482f 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -489,6 +489,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) if (request) { u8 is_dma = 0; + bool short_packet = false; if (dma && (csr & MUSB_TXCSR_DMAENAB)) { is_dma = 1; @@ -507,15 +508,18 @@ void musb_g_tx(struct musb *musb, u8 epnum) * First, maybe a terminating short packet. Some DMA * engines might handle this by themselves. */ - if ((request->zero && request->length + if ((request->zero && request->length) && (request->length % musb_ep->packet_sz == 0) && (request->actual == request->length)) -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) - || (is_dma && (!dma->desired_mode || + short_packet = true; + + if ((musb_dma_inventra(musb) || musb_dma_ux500(musb)) && + (is_dma && (!dma->desired_mode || (request->actual & - (musb_ep->packet_sz - 1)))) -#endif - ) { + (musb_ep->packet_sz - 1))))) + short_packet = true; + + if (short_packet) { /* * On DMA completion, FIFO may not be * available yet... -- cgit v0.10.2 From 754fe4a92c072a6e36d89fa328ed789c9ebc1af5 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 1 May 2015 12:29:32 -0700 Subject: usb: musb: Remove ifdefs for TX DMA for musb_host.c We can remove the ifdefs by setting up helper functions for mentor DMA and cppi/tusb DMA. Note that I've kept the existing formatting as otherwise this patch becomes pretty much unreadable. Signed-off-by: Tony Lindgren Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 696396e..a81b446 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -617,23 +617,22 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) ep->rx_reinit = 0; } -static bool musb_tx_dma_program(struct dma_controller *dma, +static int musb_tx_dma_set_mode_mentor(struct dma_controller *dma, struct musb_hw_ep *hw_ep, struct musb_qh *qh, - struct urb *urb, u32 offset, u32 length) + struct urb *urb, u32 offset, + u32 *length, u8 *mode) { struct dma_channel *channel = hw_ep->tx_channel; void __iomem *epio = hw_ep->regs; u16 pkt_size = qh->maxpacket; u16 csr; - u8 mode; -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) - if (length > channel->max_len) - length = channel->max_len; + if (*length > channel->max_len) + *length = channel->max_len; csr = musb_readw(epio, MUSB_TXCSR); - if (length > pkt_size) { - mode = 1; + if (*length > pkt_size) { + *mode = 1; csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB; /* autoset shouldn't be set in high bandwidth */ /* @@ -649,15 +648,28 @@ static bool musb_tx_dma_program(struct dma_controller *dma, can_bulk_split(hw_ep->musb, qh->type))) csr |= MUSB_TXCSR_AUTOSET; } else { - mode = 0; + *mode = 0; csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE); csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */ } channel->desired_mode = mode; musb_writew(epio, MUSB_TXCSR, csr); -#else + + return 0; +} + +static int musb_tx_dma_set_mode_cppi_tusb(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + u32 offset, + u32 *length, + u8 *mode) +{ + struct dma_channel *channel = hw_ep->tx_channel; + if (!is_cppi_enabled(hw_ep->musb) && !tusb_dma_omap(hw_ep->musb)) - return false; + return -ENODEV; channel->actual_len = 0; @@ -665,8 +677,28 @@ static bool musb_tx_dma_program(struct dma_controller *dma, * TX uses "RNDIS" mode automatically but needs help * to identify the zero-length-final-packet case. */ - mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0; -#endif + *mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0; + + return 0; +} + +static bool musb_tx_dma_program(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, struct musb_qh *qh, + struct urb *urb, u32 offset, u32 length) +{ + struct dma_channel *channel = hw_ep->tx_channel; + u16 pkt_size = qh->maxpacket; + u8 mode; + int res; + + if (musb_dma_inventra(hw_ep->musb) || musb_dma_ux500(hw_ep->musb)) + res = musb_tx_dma_set_mode_mentor(dma, hw_ep, qh, urb, + offset, &length, &mode); + else + res = musb_tx_dma_set_mode_cppi_tusb(dma, hw_ep, qh, urb, + offset, &length, &mode); + if (res) + return false; qh->segsize = length; @@ -678,6 +710,9 @@ static bool musb_tx_dma_program(struct dma_controller *dma, if (!dma->channel_program(channel, pkt_size, mode, urb->transfer_dma + offset, length)) { + void __iomem *epio = hw_ep->regs; + u16 csr; + dma->channel_release(channel); hw_ep->tx_channel = NULL; -- cgit v0.10.2 From 069a3fd19aa4108ee24dd8a3c3e4ef393b9f99ef Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 1 May 2015 12:29:33 -0700 Subject: usb: musb: Remove ifdefs for musb_host_rx in musb_host.c part1 Remove ifdefs for musb_host_rx to get closer to building in all the DMA drivers. Signed-off-by: Tony Lindgren Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index a81b446..bf725f0 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1533,6 +1533,44 @@ done: MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY); } +#ifdef CONFIG_USB_TI_CPPI41_DMA +/* Seems to set up ISO for cppi41 and not advance len. See commit c57c41d */ +static int musb_rx_dma_iso_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + struct dma_channel *channel = hw_ep->tx_channel; + void __iomem *epio = hw_ep->regs; + dma_addr_t *buf; + u32 length, res; + u16 val; + + buf = (void *)urb->iso_frame_desc[qh->iso_idx].offset + + (u32)urb->transfer_dma; + + length = urb->iso_frame_desc[qh->iso_idx].length; + + val = musb_readw(epio, MUSB_RXCSR); + val |= MUSB_RXCSR_DMAENAB; + musb_writew(hw_ep->regs, MUSB_RXCSR, val); + + res = dma->channel_program(channel, qh->maxpacket, 0, + (u32)buf, length); + + return res; +} +#else +static inline int musb_rx_dma_iso_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + return false; +} +#endif #ifdef CONFIG_USB_INVENTRA_DMA @@ -1746,25 +1784,14 @@ void musb_host_rx(struct musb *musb, u8 epnum) if (++qh->iso_idx >= urb->number_of_packets) { done = true; } else { -#if defined(CONFIG_USB_TI_CPPI41_DMA) - struct dma_controller *c; - dma_addr_t *buf; - u32 length, ret; + struct dma_controller *c = musb->dma_controller; - c = musb->dma_controller; - buf = (void *) - urb->iso_frame_desc[qh->iso_idx].offset - + (u32)urb->transfer_dma; + /* REVISIT: Why ignore return value here? */ + if (musb_dma_cppi41(musb)) + done = musb_rx_dma_iso_cppi41(c, hw_ep, + qh, urb, + xfer_len); - length = - urb->iso_frame_desc[qh->iso_idx].length; - - val |= MUSB_RXCSR_DMAENAB; - musb_writew(hw_ep->regs, MUSB_RXCSR, val); - - ret = c->channel_program(dma, qh->maxpacket, - 0, (u32) buf, length); -#endif done = false; } -- cgit v0.10.2 From 557d543e3f18acf4aa3b9e1860ef38e106696673 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 1 May 2015 12:29:34 -0700 Subject: usb: musb: Remove ifdefs for musb_host_rx in musb_host.c part2 Remove ifdefs for musb_host_rx to get closer to building in all the DMA drivers. Signed-off-by: Tony Lindgren Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index bf725f0..6f23563 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1734,9 +1734,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) */ /* FIXME this is _way_ too much in-line logic for Mentor DMA */ - -#if !defined(CONFIG_USB_INVENTRA_DMA) && !defined(CONFIG_USB_UX500_DMA) - if (rx_csr & MUSB_RXCSR_H_REQPKT) { + if (!musb_dma_inventra(musb) && !musb_dma_ux500(musb) && + (rx_csr & MUSB_RXCSR_H_REQPKT)) { /* REVISIT this happened for a while on some short reads... * the cleanup still needs investigation... looks bad... * and also duplicates dma cleanup code above ... plus, @@ -1757,7 +1756,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | rx_csr); } -#endif + if (dma && (rx_csr & MUSB_RXCSR_DMAENAB)) { xfer_len = dma->actual_len; -- cgit v0.10.2 From cff84bdb62f15a37ff71e0fb9c39a5e1b7a3f052 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 1 May 2015 12:29:35 -0700 Subject: usb: musb: Remove ifdefs for musb_host_rx in musb_host.c part3 Remove ifdefs for musb_host_rx to get closer to building in all the DMA drivers. Signed-off-by: Tony Lindgren Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 6f23563..0555207 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1572,8 +1572,8 @@ static inline int musb_rx_dma_iso_cppi41(struct dma_controller *dma, } #endif -#ifdef CONFIG_USB_INVENTRA_DMA - +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ + defined(CONFIG_USB_TI_CPPI41_DMA) /* Host side RX (IN) using Mentor DMA works as follows: submit_urb -> - if queue was empty, ProgramEndpoint @@ -1608,7 +1608,68 @@ static inline int musb_rx_dma_iso_cppi41(struct dma_controller *dma, * thus be a great candidate for using mode 1 ... for all but the * last packet of one URB's transfer. */ +static int musb_rx_dma_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + struct dma_channel *channel = hw_ep->rx_channel; + void __iomem *epio = hw_ep->regs; + u16 val; + int pipe; + bool done; + + pipe = urb->pipe; + + if (usb_pipeisoc(pipe)) { + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + d->actual_length = len; + + /* even if there was an error, we did the dma + * for iso_frame_desc->length + */ + if (d->status != -EILSEQ && d->status != -EOVERFLOW) + d->status = 0; + + if (++qh->iso_idx >= urb->number_of_packets) { + done = true; + } else { + /* REVISIT: Why ignore return value here? */ + if (musb_dma_cppi41(hw_ep->musb)) + done = musb_rx_dma_iso_cppi41(dma, hw_ep, qh, + urb, len); + done = false; + } + + } else { + /* done if urb buffer is full or short packet is recd */ + done = (urb->actual_length + len >= + urb->transfer_buffer_length + || channel->actual_len < qh->maxpacket + || channel->rx_packet_done); + } + + /* send IN token for next packet, without AUTOREQ */ + if (!done) { + val = musb_readw(epio, MUSB_RXCSR); + val |= MUSB_RXCSR_H_REQPKT; + musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | val); + } + return done; +} +#else +static inline int musb_rx_dma_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + return false; +} #endif /* @@ -1619,6 +1680,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) { struct urb *urb; struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + struct dma_controller *c = musb->dma_controller; void __iomem *epio = hw_ep->regs; struct musb_qh *qh = hw_ep->in_qh; size_t xfer_len; @@ -1766,56 +1828,18 @@ void musb_host_rx(struct musb *musb, u8 epnum) | MUSB_RXCSR_RXPKTRDY); musb_writew(hw_ep->regs, MUSB_RXCSR, val); -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ - defined(CONFIG_USB_TI_CPPI41_DMA) - if (usb_pipeisoc(pipe)) { - struct usb_iso_packet_descriptor *d; - - d = urb->iso_frame_desc + qh->iso_idx; - d->actual_length = xfer_len; - - /* even if there was an error, we did the dma - * for iso_frame_desc->length - */ - if (d->status != -EILSEQ && d->status != -EOVERFLOW) - d->status = 0; - - if (++qh->iso_idx >= urb->number_of_packets) { - done = true; - } else { - struct dma_controller *c = musb->dma_controller; - - /* REVISIT: Why ignore return value here? */ - if (musb_dma_cppi41(musb)) - done = musb_rx_dma_iso_cppi41(c, hw_ep, - qh, urb, - xfer_len); - - done = false; - } - - } else { - /* done if urb buffer is full or short packet is recd */ - done = (urb->actual_length + xfer_len >= - urb->transfer_buffer_length - || dma->actual_len < qh->maxpacket - || dma->rx_packet_done); - } - - /* send IN token for next packet, without AUTOREQ */ - if (!done) { - val |= MUSB_RXCSR_H_REQPKT; - musb_writew(epio, MUSB_RXCSR, - MUSB_RXCSR_H_WZC_BITS | val); + if (musb_dma_inventra(musb) || musb_dma_ux500(musb) || + musb_dma_cppi41(musb)) { + done = musb_rx_dma_inventra_cppi41(c, hw_ep, qh, urb, xfer_len); + dev_dbg(hw_ep->musb->controller, + "ep %d dma %s, rxcsr %04x, rxcount %d\n", + epnum, done ? "off" : "reset", + musb_readw(epio, MUSB_RXCSR), + musb_readw(epio, MUSB_RXCOUNT)); + } else { + done = true; } - dev_dbg(musb->controller, "ep %d dma %s, rxcsr %04x, rxcount %d\n", epnum, - done ? "off" : "reset", - musb_readw(epio, MUSB_RXCSR), - musb_readw(epio, MUSB_RXCOUNT)); -#else - done = true; -#endif } else if (urb->status == -EINPROGRESS) { /* if no errors, be sure a packet is ready for unloading */ if (unlikely(!(rx_csr & MUSB_RXCSR_RXPKTRDY))) { -- cgit v0.10.2 From e530bb8f7963c2030d54974406d7d4fedc1a98d2 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 1 May 2015 12:29:36 -0700 Subject: usb: musb: Remove ifdefs for musb_host_rx in musb_host.c part4 Remove ifdefs for musb_host_rx to get closer to building in all the DMA drivers. Signed-off-by: Tony Lindgren Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 0555207..79826eb 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1857,9 +1857,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) } /* we are expecting IN packets */ -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ - defined(CONFIG_USB_TI_CPPI41_DMA) - if (dma) { + if ((musb_dma_inventra(musb) || musb_dma_ux500(musb) || + musb_dma_cppi41(musb)) && dma) { struct dma_controller *c; u16 rx_count; int ret, length; @@ -1976,7 +1975,6 @@ void musb_host_rx(struct musb *musb, u8 epnum) musb_writew(epio, MUSB_RXCSR, val); } } -#endif /* Mentor DMA */ if (!dma) { unsigned int received_len; -- cgit v0.10.2 From ac33cdb166811223cc3b47d276aa82ef877f6362 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 1 May 2015 12:29:37 -0700 Subject: usb: musb: Remove ifdefs for musb_host_rx in musb_host.c part5 Remove ifdefs for musb_host_rx to get closer to building in all the DMA drivers. Signed-off-by: Tony Lindgren Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 79826eb..2499515 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1661,6 +1661,122 @@ static int musb_rx_dma_inventra_cppi41(struct dma_controller *dma, return done; } + +/* Disadvantage of using mode 1: + * It's basically usable only for mass storage class; essentially all + * other protocols also terminate transfers on short packets. + * + * Details: + * An extra IN token is sent at the end of the transfer (due to AUTOREQ) + * If you try to use mode 1 for (transfer_buffer_length - 512), and try + * to use the extra IN token to grab the last packet using mode 0, then + * the problem is that you cannot be sure when the device will send the + * last packet and RxPktRdy set. Sometimes the packet is recd too soon + * such that it gets lost when RxCSR is re-set at the end of the mode 1 + * transfer, while sometimes it is recd just a little late so that if you + * try to configure for mode 0 soon after the mode 1 transfer is + * completed, you will find rxcount 0. Okay, so you might think why not + * wait for an interrupt when the pkt is recd. Well, you won't get any! + */ +static int musb_rx_dma_in_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len, + u8 iso_err) +{ + struct musb *musb = hw_ep->musb; + void __iomem *epio = hw_ep->regs; + struct dma_channel *channel = hw_ep->rx_channel; + u16 rx_count, val; + int length, pipe, done; + dma_addr_t buf; + + rx_count = musb_readw(epio, MUSB_RXCOUNT); + pipe = urb->pipe; + + if (usb_pipeisoc(pipe)) { + int d_status = 0; + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + + if (iso_err) { + d_status = -EILSEQ; + urb->error_count++; + } + if (rx_count > d->length) { + if (d_status == 0) { + d_status = -EOVERFLOW; + urb->error_count++; + } + dev_dbg(musb->controller, "** OVERFLOW %d into %d\n", + rx_count, d->length); + + length = d->length; + } else + length = rx_count; + d->status = d_status; + buf = urb->transfer_dma + d->offset; + } else { + length = rx_count; + buf = urb->transfer_dma + urb->actual_length; + } + + channel->desired_mode = 0; +#ifdef USE_MODE1 + /* because of the issue below, mode 1 will + * only rarely behave with correct semantics. + */ + if ((urb->transfer_flags & URB_SHORT_NOT_OK) + && (urb->transfer_buffer_length - urb->actual_length) + > qh->maxpacket) + channel->desired_mode = 1; + if (rx_count < hw_ep->max_packet_sz_rx) { + length = rx_count; + channel->desired_mode = 0; + } else { + length = urb->transfer_buffer_length; + } +#endif + + /* See comments above on disadvantages of using mode 1 */ + val = musb_readw(epio, MUSB_RXCSR); + val &= ~MUSB_RXCSR_H_REQPKT; + + if (channel->desired_mode == 0) + val &= ~MUSB_RXCSR_H_AUTOREQ; + else + val |= MUSB_RXCSR_H_AUTOREQ; + val |= MUSB_RXCSR_DMAENAB; + + /* autoclear shouldn't be set in high bandwidth */ + if (qh->hb_mult == 1) + val |= MUSB_RXCSR_AUTOCLEAR; + + musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | val); + + /* REVISIT if when actual_length != 0, + * transfer_buffer_length needs to be + * adjusted first... + */ + done = dma->channel_program(channel, qh->maxpacket, + channel->desired_mode, + buf, length); + + if (!done) { + dma->channel_release(channel); + hw_ep->rx_channel = NULL; + channel = NULL; + val = musb_readw(epio, MUSB_RXCSR); + val &= ~(MUSB_RXCSR_DMAENAB + | MUSB_RXCSR_H_AUTOREQ + | MUSB_RXCSR_AUTOCLEAR); + musb_writew(epio, MUSB_RXCSR, val); + } + + return done; +} #else static inline int musb_rx_dma_inventra_cppi41(struct dma_controller *dma, struct musb_hw_ep *hw_ep, @@ -1670,6 +1786,16 @@ static inline int musb_rx_dma_inventra_cppi41(struct dma_controller *dma, { return false; } + +static inline int musb_rx_dma_in_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len, + u8 iso_err) +{ + return false; +} #endif /* @@ -1859,121 +1985,21 @@ void musb_host_rx(struct musb *musb, u8 epnum) /* we are expecting IN packets */ if ((musb_dma_inventra(musb) || musb_dma_ux500(musb) || musb_dma_cppi41(musb)) && dma) { - struct dma_controller *c; - u16 rx_count; - int ret, length; - dma_addr_t buf; - - rx_count = musb_readw(epio, MUSB_RXCOUNT); - - dev_dbg(musb->controller, "RX%d count %d, buffer 0x%llx len %d/%d\n", - epnum, rx_count, - (unsigned long long) urb->transfer_dma - + urb->actual_length, - qh->offset, - urb->transfer_buffer_length); - - c = musb->dma_controller; - - if (usb_pipeisoc(pipe)) { - int d_status = 0; - struct usb_iso_packet_descriptor *d; - - d = urb->iso_frame_desc + qh->iso_idx; - - if (iso_err) { - d_status = -EILSEQ; - urb->error_count++; - } - if (rx_count > d->length) { - if (d_status == 0) { - d_status = -EOVERFLOW; - urb->error_count++; - } - dev_dbg(musb->controller, "** OVERFLOW %d into %d\n",\ - rx_count, d->length); - - length = d->length; - } else - length = rx_count; - d->status = d_status; - buf = urb->transfer_dma + d->offset; - } else { - length = rx_count; - buf = urb->transfer_dma + - urb->actual_length; - } - - dma->desired_mode = 0; -#ifdef USE_MODE1 - /* because of the issue below, mode 1 will - * only rarely behave with correct semantics. - */ - if ((urb->transfer_flags & - URB_SHORT_NOT_OK) - && (urb->transfer_buffer_length - - urb->actual_length) - > qh->maxpacket) - dma->desired_mode = 1; - if (rx_count < hw_ep->max_packet_sz_rx) { - length = rx_count; - dma->desired_mode = 0; - } else { - length = urb->transfer_buffer_length; - } -#endif - -/* Disadvantage of using mode 1: - * It's basically usable only for mass storage class; essentially all - * other protocols also terminate transfers on short packets. - * - * Details: - * An extra IN token is sent at the end of the transfer (due to AUTOREQ) - * If you try to use mode 1 for (transfer_buffer_length - 512), and try - * to use the extra IN token to grab the last packet using mode 0, then - * the problem is that you cannot be sure when the device will send the - * last packet and RxPktRdy set. Sometimes the packet is recd too soon - * such that it gets lost when RxCSR is re-set at the end of the mode 1 - * transfer, while sometimes it is recd just a little late so that if you - * try to configure for mode 0 soon after the mode 1 transfer is - * completed, you will find rxcount 0. Okay, so you might think why not - * wait for an interrupt when the pkt is recd. Well, you won't get any! - */ - - val = musb_readw(epio, MUSB_RXCSR); - val &= ~MUSB_RXCSR_H_REQPKT; - - if (dma->desired_mode == 0) - val &= ~MUSB_RXCSR_H_AUTOREQ; + dev_dbg(hw_ep->musb->controller, + "RX%d count %d, buffer 0x%llx len %d/%d\n", + epnum, musb_readw(epio, MUSB_RXCOUNT), + (unsigned long long) urb->transfer_dma + + urb->actual_length, + qh->offset, + urb->transfer_buffer_length); + + done = musb_rx_dma_in_inventra_cppi41(c, hw_ep, qh, + urb, xfer_len, + iso_err); + if (done) + goto finish; else - val |= MUSB_RXCSR_H_AUTOREQ; - val |= MUSB_RXCSR_DMAENAB; - - /* autoclear shouldn't be set in high bandwidth */ - if (qh->hb_mult == 1) - val |= MUSB_RXCSR_AUTOCLEAR; - - musb_writew(epio, MUSB_RXCSR, - MUSB_RXCSR_H_WZC_BITS | val); - - /* REVISIT if when actual_length != 0, - * transfer_buffer_length needs to be - * adjusted first... - */ - ret = c->channel_program( - dma, qh->maxpacket, - dma->desired_mode, buf, length); - - if (!ret) { - c->channel_release(dma); - hw_ep->rx_channel = NULL; - dma = NULL; - val = musb_readw(epio, MUSB_RXCSR); - val &= ~(MUSB_RXCSR_DMAENAB - | MUSB_RXCSR_H_AUTOREQ - | MUSB_RXCSR_AUTOCLEAR); - musb_writew(epio, MUSB_RXCSR, val); - } + dev_err(musb->controller, "error: rx_dma failed\n"); } if (!dma) { -- cgit v0.10.2 From 25e97ab598e6c216d9f56d3e30572df04fa90373 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:39:19 +0900 Subject: usb: phy-ab8500-usb: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 7225d52..f063b2b 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -1504,7 +1504,7 @@ static int ab8500_usb_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id ab8500_usb_devtype[] = { +static const struct platform_device_id ab8500_usb_devtype[] = { { .name = "ab8500-usb", }, { .name = "ab8540-usb", }, { .name = "ab9540-usb", }, -- cgit v0.10.2 From 5665aec631245ff1b6e03fa5efe7383f4ef379cb Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Thu, 7 May 2015 19:16:53 +0100 Subject: usb: dwc3: dwc3-st: Update the incorrect DT dwc3 example. There is a subtle typo phys-names should be phy-names. Using the current example means you don't have working usb (as you fail to obtain the phys). Also update the example to use the generic phy type constants which are now used for miphy28. Additionally also remove the unnecessary new line in the example. Signed-off-by: Peter Griffin Acked-by: Rob Herring Acked-by: Maxime Coquelin Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/dwc3-st.txt b/Documentation/devicetree/bindings/usb/dwc3-st.txt index f9d7025..01c71b1 100644 --- a/Documentation/devicetree/bindings/usb/dwc3-st.txt +++ b/Documentation/devicetree/bindings/usb/dwc3-st.txt @@ -49,8 +49,7 @@ st_dwc3: dwc3@8f94000 { st,syscfg = <&syscfg_core>; resets = <&powerdown STIH407_USB3_POWERDOWN>, <&softreset STIH407_MIPHY2_SOFTRESET>; - reset-names = "powerdown", - "softreset"; + reset-names = "powerdown", "softreset"; #address-cells = <1>; #size-cells = <1>; pinctrl-names = "default"; @@ -62,7 +61,7 @@ st_dwc3: dwc3@8f94000 { reg = <0x09900000 0x100000>; interrupts = ; dr_mode = "host"; - phys-names = "usb2-phy", "usb3-phy"; - phys = <&usb2_picophy2>, <&phy_port2 MIPHY_TYPE_USB>; + phy-names = "usb2-phy", "usb3-phy"; + phys = <&usb2_picophy2>, <&phy_port2 PHY_TYPE_USB3>; }; }; -- cgit v0.10.2 From db978e284781af641a02f9ca9d99a746e1e1f60f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 5 May 2015 18:32:38 +0200 Subject: usb: phy: Allow compile test of GPIO consumers if !GPIOLIB The GPIO subsystem provides dummy GPIO consumer functions if GPIOLIB is not enabled. Hence drivers that depend on GPIOLIB, but use GPIO consumer functionality only, can still be compiled if GPIOLIB is not enabled. Relax the dependency on GPIOLIB if COMPILE_TEST is enabled, where appropriate. Signed-off-by: Geert Uytterhoeven Cc: Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 173a5b5..869c0cfcad 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -91,7 +91,7 @@ config TWL6030_USB config USB_GPIO_VBUS tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST select USB_PHY help Provides simple GPIO VBUS sensing for controllers with an -- cgit v0.10.2 From d74c23d36e7829ca7517a82c725c493d16328a44 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 26 Mar 2015 22:43:39 -0700 Subject: usb: gadget: s3c2410_udc: Remove static char buffer, use vsprintf extension %pV Using unnecessary static char buffers isn't good. Use the %pV extension instead. Miscellanea: o the dprintk return value is unused, make it void o add __printf format and argument verification Signed-off-by: Joe Perches Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index b808951..2979575 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -92,40 +92,38 @@ static struct s3c2410_udc_mach_info *udc_info; static uint32_t s3c2410_ticks = 0; -static int dprintk(int level, const char *fmt, ...) +__printf(2, 3) +static void dprintk(int level, const char *fmt, ...) { - static char printk_buf[1024]; static long prevticks; static int invocation; + struct va_format vaf; va_list args; - int len; if (level > USB_S3C2410_DEBUG_LEVEL) - return 0; + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; if (s3c2410_ticks != prevticks) { prevticks = s3c2410_ticks; invocation = 0; } - len = scnprintf(printk_buf, - sizeof(printk_buf), "%1lu.%02d USB: ", - prevticks, invocation++); + pr_debug("%1lu.%02d USB: %pV", prevticks, invocation++, &vaf); - va_start(args, fmt); - len = vscnprintf(printk_buf+len, - sizeof(printk_buf)-len, fmt, args); va_end(args); - - pr_debug("%s", printk_buf); - return len; } #else -static int dprintk(int level, const char *fmt, ...) +__printf(2, 3) +static void dprintk(int level, const char *fmt, ...) { - return 0; } #endif + static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p) { u32 addr_reg, pwr_reg, ep_int_reg, usb_int_reg; -- cgit v0.10.2 From 83210e59ee1527f229af6aef78c95b747bdcf9c4 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Fri, 20 Mar 2015 08:18:47 +0100 Subject: usb: gadget: rndis: use rndis_params instead of configNr RNDIS function has a limitation on the number of allowed instances. So far it has been RNDIS_MAX_CONFIGS, which happens to be one. In order to eliminate this kind of arbitrary limitation we should not preallocate a predefined (RNDIS_MAX_CONFIGS) array of struct rndis_params instances but instead allow allocating them on demand. This patch prepares the elimination of the said limit by converting all the functions which accept rndis config number to accept a pointer to the actual struct rndis_params. Consequently, rndis_register() returns a pointer to a corresponding struct rndis_params instance. The pointer is then always used by f_rndis.c instead of config number when it talks to rndis.c API. A nice side-effect of the changes is that many lines of code in rndis.c become shorter and fit in 80 columns. If a function prototype changes in rndis.h a style cleanup is made at the same time, otherwise checkpatch complains that the patch has style problems. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 829edf8..2dafe72 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -76,7 +76,7 @@ struct f_rndis { u8 ethaddr[ETH_ALEN]; u32 vendorID; const char *manufacturer; - int config; + struct rndis_params *params; struct usb_ep *notify; struct usb_request *notify_req; @@ -453,7 +453,7 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ // spin_lock(&dev->lock); - status = rndis_msg_parser(rndis->config, (u8 *) req->buf); + status = rndis_msg_parser(rndis->params, (u8 *) req->buf); if (status < 0) pr_err("RNDIS command error %d, %d/%d\n", status, req->actual, req->length); @@ -499,12 +499,12 @@ rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) u32 n; /* return the result */ - buf = rndis_get_next_response(rndis->config, &n); + buf = rndis_get_next_response(rndis->params, &n); if (buf) { memcpy(req->buf, buf, n); req->complete = rndis_response_complete; req->context = rndis; - rndis_free_response(rndis->config, buf); + rndis_free_response(rndis->params, buf); value = n; } /* else stalls ... spec says to avoid that */ @@ -597,7 +597,7 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (IS_ERR(net)) return PTR_ERR(net); - rndis_set_param_dev(rndis->config, net, + rndis_set_param_dev(rndis->params, net, &rndis->port.cdc_filter); } else goto fail; @@ -617,7 +617,7 @@ static void rndis_disable(struct usb_function *f) DBG(cdev, "rndis deactivated\n"); - rndis_uninit(rndis->config); + rndis_uninit(rndis->params); gether_disconnect(&rndis->port); usb_ep_disable(rndis->notify); @@ -640,9 +640,9 @@ static void rndis_open(struct gether *geth) DBG(cdev, "%s\n", __func__); - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, bitrate(cdev->gadget) / 100); - rndis_signal_connect(rndis->config); + rndis_signal_connect(rndis->params); } static void rndis_close(struct gether *geth) @@ -651,8 +651,8 @@ static void rndis_close(struct gether *geth) DBG(geth->func.config->cdev, "%s\n", __func__); - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); - rndis_signal_disconnect(rndis->config); + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); + rndis_signal_disconnect(rndis->params); } /*-------------------------------------------------------------------------*/ @@ -796,11 +796,11 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis->port.open = rndis_open; rndis->port.close = rndis_close; - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); - rndis_set_host_mac(rndis->config, rndis->ethaddr); + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); + rndis_set_host_mac(rndis->params, rndis->ethaddr); if (rndis->manufacturer && rndis->vendorID && - rndis_set_param_vendor(rndis->config, rndis->vendorID, + rndis_set_param_vendor(rndis->params, rndis->vendorID, rndis->manufacturer)) { status = -EINVAL; goto fail_free_descs; @@ -944,7 +944,7 @@ static void rndis_free(struct usb_function *f) struct f_rndis_opts *opts; rndis = func_to_rndis(f); - rndis_deregister(rndis->config); + rndis_deregister(rndis->params); opts = container_of(f->fi, struct f_rndis_opts, func_inst); kfree(rndis); mutex_lock(&opts->lock); @@ -968,7 +968,7 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) { struct f_rndis *rndis; struct f_rndis_opts *opts; - int status; + struct rndis_params *params; /* allocate and initialize one new instance */ rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); @@ -1002,12 +1002,12 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) rndis->port.func.disable = rndis_disable; rndis->port.func.free_func = rndis_free; - status = rndis_register(rndis_response_available, rndis); - if (status < 0) { + params = rndis_register(rndis_response_available, rndis); + if (IS_ERR(params)) { kfree(rndis); - return ERR_PTR(status); + return ERR_CAST(params); } - rndis->config = status; + rndis->params = params; return &rndis->port.func; } diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 95d2324..01a3b58 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -66,7 +66,8 @@ static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS]; static const __le32 rndis_driver_version = cpu_to_le32(1); /* Function Prototypes */ -static rndis_resp_t *rndis_add_response(int configNr, u32 length); +static rndis_resp_t *rndis_add_response(struct rndis_params *params, + u32 length); /* supported OIDs */ @@ -160,7 +161,7 @@ static const u32 oid_supported_list[] = /* NDIS Functions */ -static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, +static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf, unsigned buf_len, rndis_resp_t *r) { int retval = -ENOTSUPP; @@ -192,7 +193,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, outbuf = (__le32 *)&resp[1]; resp->InformationBufferOffset = cpu_to_le32(16); - net = rndis_per_dev_params[configNr].dev; + net = params->dev; stats = dev_get_stats(net, &temp); switch (OID) { @@ -225,7 +226,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_MEDIA_SUPPORTED: pr_debug("%s: RNDIS_OID_GEN_MEDIA_SUPPORTED\n", __func__); - *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium); + *outbuf = cpu_to_le32(params->medium); retval = 0; break; @@ -233,16 +234,15 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, case RNDIS_OID_GEN_MEDIA_IN_USE: pr_debug("%s: RNDIS_OID_GEN_MEDIA_IN_USE\n", __func__); /* one medium, one transport... (maybe you do it better) */ - *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium); + *outbuf = cpu_to_le32(params->medium); retval = 0; break; /* mandatory */ case RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE: pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__); - if (rndis_per_dev_params[configNr].dev) { - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].dev->mtu); + if (params->dev) { + *outbuf = cpu_to_le32(params->dev->mtu); retval = 0; } break; @@ -251,21 +251,18 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, case RNDIS_OID_GEN_LINK_SPEED: if (rndis_debug > 1) pr_debug("%s: RNDIS_OID_GEN_LINK_SPEED\n", __func__); - if (rndis_per_dev_params[configNr].media_state - == RNDIS_MEDIA_STATE_DISCONNECTED) + if (params->media_state == RNDIS_MEDIA_STATE_DISCONNECTED) *outbuf = cpu_to_le32(0); else - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].speed); + *outbuf = cpu_to_le32(params->speed); retval = 0; break; /* mandatory */ case RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE: pr_debug("%s: RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__); - if (rndis_per_dev_params[configNr].dev) { - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].dev->mtu); + if (params->dev) { + *outbuf = cpu_to_le32(params->dev->mtu); retval = 0; } break; @@ -273,9 +270,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE: pr_debug("%s: RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__); - if (rndis_per_dev_params[configNr].dev) { - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].dev->mtu); + if (params->dev) { + *outbuf = cpu_to_le32(params->dev->mtu); retval = 0; } break; @@ -283,20 +279,16 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_VENDOR_ID: pr_debug("%s: RNDIS_OID_GEN_VENDOR_ID\n", __func__); - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].vendorID); + *outbuf = cpu_to_le32(params->vendorID); retval = 0; break; /* mandatory */ case RNDIS_OID_GEN_VENDOR_DESCRIPTION: pr_debug("%s: RNDIS_OID_GEN_VENDOR_DESCRIPTION\n", __func__); - if (rndis_per_dev_params[configNr].vendorDescr) { - length = strlen(rndis_per_dev_params[configNr]. - vendorDescr); - memcpy(outbuf, - rndis_per_dev_params[configNr].vendorDescr, - length); + if (params->vendorDescr) { + length = strlen(params->vendorDescr); + memcpy(outbuf, params->vendorDescr, length); } else { outbuf[0] = 0; } @@ -313,7 +305,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_CURRENT_PACKET_FILTER: pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER\n", __func__); - *outbuf = cpu_to_le32(*rndis_per_dev_params[configNr].filter); + *outbuf = cpu_to_le32(*params->filter); retval = 0; break; @@ -328,8 +320,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS: if (rndis_debug > 1) pr_debug("%s: RNDIS_OID_GEN_MEDIA_CONNECT_STATUS\n", __func__); - *outbuf = cpu_to_le32(rndis_per_dev_params[configNr] - .media_state); + *outbuf = cpu_to_le32(params->media_state); retval = 0; break; @@ -409,11 +400,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_802_3_PERMANENT_ADDRESS: pr_debug("%s: RNDIS_OID_802_3_PERMANENT_ADDRESS\n", __func__); - if (rndis_per_dev_params[configNr].dev) { + if (params->dev) { length = ETH_ALEN; - memcpy(outbuf, - rndis_per_dev_params[configNr].host_mac, - length); + memcpy(outbuf, params->host_mac, length); retval = 0; } break; @@ -421,11 +410,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_802_3_CURRENT_ADDRESS: pr_debug("%s: RNDIS_OID_802_3_CURRENT_ADDRESS\n", __func__); - if (rndis_per_dev_params[configNr].dev) { + if (params->dev) { length = ETH_ALEN; - memcpy(outbuf, - rndis_per_dev_params [configNr].host_mac, - length); + memcpy(outbuf, params->host_mac, length); retval = 0; } break; @@ -490,12 +477,11 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, return retval; } -static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, - rndis_resp_t *r) +static int gen_ndis_set_resp(struct rndis_params *params, u32 OID, + u8 *buf, u32 buf_len, rndis_resp_t *r) { rndis_set_cmplt_type *resp; int i, retval = -ENOTSUPP; - struct rndis_params *params; if (!r) return -ENOMEM; @@ -514,7 +500,6 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, } } - params = &rndis_per_dev_params[configNr]; switch (OID) { case RNDIS_OID_GEN_CURRENT_PACKET_FILTER: @@ -563,16 +548,16 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, * Response Functions */ -static int rndis_init_response(int configNr, rndis_init_msg_type *buf) +static int rndis_init_response(struct rndis_params *params, + rndis_init_msg_type *buf) { rndis_init_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; if (!params->dev) return -ENOTSUPP; - r = rndis_add_response(configNr, sizeof(rndis_init_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_init_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_init_cmplt_type *)r->buf; @@ -599,11 +584,11 @@ static int rndis_init_response(int configNr, rndis_init_msg_type *buf) return 0; } -static int rndis_query_response(int configNr, rndis_query_msg_type *buf) +static int rndis_query_response(struct rndis_params *params, + rndis_query_msg_type *buf) { rndis_query_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; /* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */ if (!params->dev) @@ -615,7 +600,7 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf) * rndis_query_cmplt_type followed by data. * oid_supported_list is the largest data reply */ - r = rndis_add_response(configNr, + r = rndis_add_response(params, sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type)); if (!r) return -ENOMEM; @@ -624,7 +609,7 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf) resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - if (gen_ndis_query_resp(configNr, le32_to_cpu(buf->OID), + if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID), le32_to_cpu(buf->InformationBufferOffset) + 8 + (u8 *)buf, le32_to_cpu(buf->InformationBufferLength), @@ -641,14 +626,14 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf) return 0; } -static int rndis_set_response(int configNr, rndis_set_msg_type *buf) +static int rndis_set_response(struct rndis_params *params, + rndis_set_msg_type *buf) { u32 BufLength, BufOffset; rndis_set_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; - r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_set_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_set_cmplt_type *)r->buf; @@ -671,7 +656,7 @@ static int rndis_set_response(int configNr, rndis_set_msg_type *buf) resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C); resp->MessageLength = cpu_to_le32(16); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - if (gen_ndis_set_resp(configNr, le32_to_cpu(buf->OID), + if (gen_ndis_set_resp(params, le32_to_cpu(buf->OID), ((u8 *)buf) + 8 + BufOffset, BufLength, r)) resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); else @@ -681,13 +666,13 @@ static int rndis_set_response(int configNr, rndis_set_msg_type *buf) return 0; } -static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf) +static int rndis_reset_response(struct rndis_params *params, + rndis_reset_msg_type *buf) { rndis_reset_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; - r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_reset_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_reset_cmplt_type *)r->buf; @@ -702,16 +687,15 @@ static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf) return 0; } -static int rndis_keepalive_response(int configNr, +static int rndis_keepalive_response(struct rndis_params *params, rndis_keepalive_msg_type *buf) { rndis_keepalive_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; /* host "should" check only in RNDIS_DATA_INITIALIZED state */ - r = rndis_add_response(configNr, sizeof(rndis_keepalive_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_keepalive_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_keepalive_cmplt_type *)r->buf; @@ -729,17 +713,15 @@ static int rndis_keepalive_response(int configNr, /* * Device to Host Comunication */ -static int rndis_indicate_status_msg(int configNr, u32 status) +static int rndis_indicate_status_msg(struct rndis_params *params, u32 status) { rndis_indicate_status_msg_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; if (params->state == RNDIS_UNINITIALIZED) return -ENOTSUPP; - r = rndis_add_response(configNr, - sizeof(rndis_indicate_status_msg_type)); + r = rndis_add_response(params, sizeof(rndis_indicate_status_msg_type)); if (!r) return -ENOMEM; resp = (rndis_indicate_status_msg_type *)r->buf; @@ -754,53 +736,48 @@ static int rndis_indicate_status_msg(int configNr, u32 status) return 0; } -int rndis_signal_connect(int configNr) +int rndis_signal_connect(struct rndis_params *params) { - rndis_per_dev_params[configNr].media_state - = RNDIS_MEDIA_STATE_CONNECTED; - return rndis_indicate_status_msg(configNr, - RNDIS_STATUS_MEDIA_CONNECT); + params->media_state = RNDIS_MEDIA_STATE_CONNECTED; + return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_CONNECT); } EXPORT_SYMBOL_GPL(rndis_signal_connect); -int rndis_signal_disconnect(int configNr) +int rndis_signal_disconnect(struct rndis_params *params) { - rndis_per_dev_params[configNr].media_state - = RNDIS_MEDIA_STATE_DISCONNECTED; - return rndis_indicate_status_msg(configNr, - RNDIS_STATUS_MEDIA_DISCONNECT); + params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED; + return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_DISCONNECT); } EXPORT_SYMBOL_GPL(rndis_signal_disconnect); -void rndis_uninit(int configNr) +void rndis_uninit(struct rndis_params *params) { u8 *buf; u32 length; - if (configNr >= RNDIS_MAX_CONFIGS) + if (!params) return; - rndis_per_dev_params[configNr].state = RNDIS_UNINITIALIZED; + params->state = RNDIS_UNINITIALIZED; /* drain the response queue */ - while ((buf = rndis_get_next_response(configNr, &length))) - rndis_free_response(configNr, buf); + while ((buf = rndis_get_next_response(params, &length))) + rndis_free_response(params, buf); } EXPORT_SYMBOL_GPL(rndis_uninit); -void rndis_set_host_mac(int configNr, const u8 *addr) +void rndis_set_host_mac(struct rndis_params *params, const u8 *addr) { - rndis_per_dev_params[configNr].host_mac = addr; + params->host_mac = addr; } EXPORT_SYMBOL_GPL(rndis_set_host_mac); /* * Message Parser */ -int rndis_msg_parser(u8 configNr, u8 *buf) +int rndis_msg_parser(struct rndis_params *params, u8 *buf) { u32 MsgType, MsgLength; __le32 *tmp; - struct rndis_params *params; if (!buf) return -ENOMEM; @@ -809,9 +786,8 @@ int rndis_msg_parser(u8 configNr, u8 *buf) MsgType = get_unaligned_le32(tmp++); MsgLength = get_unaligned_le32(tmp++); - if (configNr >= RNDIS_MAX_CONFIGS) + if (!params) return -ENOTSUPP; - params = &rndis_per_dev_params[configNr]; /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for * rx/tx statistics and link status, in addition to KEEPALIVE traffic @@ -824,8 +800,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf) pr_debug("%s: RNDIS_MSG_INIT\n", __func__); params->state = RNDIS_INITIALIZED; - return rndis_init_response(configNr, - (rndis_init_msg_type *)buf); + return rndis_init_response(params, (rndis_init_msg_type *)buf); case RNDIS_MSG_HALT: pr_debug("%s: RNDIS_MSG_HALT\n", @@ -838,17 +813,16 @@ int rndis_msg_parser(u8 configNr, u8 *buf) return 0; case RNDIS_MSG_QUERY: - return rndis_query_response(configNr, + return rndis_query_response(params, (rndis_query_msg_type *)buf); case RNDIS_MSG_SET: - return rndis_set_response(configNr, - (rndis_set_msg_type *)buf); + return rndis_set_response(params, (rndis_set_msg_type *)buf); case RNDIS_MSG_RESET: pr_debug("%s: RNDIS_MSG_RESET\n", __func__); - return rndis_reset_response(configNr, + return rndis_reset_response(params, (rndis_reset_msg_type *)buf); case RNDIS_MSG_KEEPALIVE: @@ -856,7 +830,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf) if (rndis_debug > 1) pr_debug("%s: RNDIS_MSG_KEEPALIVE\n", __func__); - return rndis_keepalive_response(configNr, + return rndis_keepalive_response(params, (rndis_keepalive_msg_type *) buf); @@ -876,12 +850,12 @@ int rndis_msg_parser(u8 configNr, u8 *buf) } EXPORT_SYMBOL_GPL(rndis_msg_parser); -int rndis_register(void (*resp_avail)(void *v), void *v) +struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v) { u8 i; if (!resp_avail) - return -EINVAL; + return ERR_PTR(-EINVAL); for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { if (!rndis_per_dev_params[i].used) { @@ -889,58 +863,64 @@ int rndis_register(void (*resp_avail)(void *v), void *v) rndis_per_dev_params[i].resp_avail = resp_avail; rndis_per_dev_params[i].v = v; pr_debug("%s: configNr = %d\n", __func__, i); - return i; + return &rndis_per_dev_params[i]; } } pr_debug("failed\n"); - return -ENODEV; + return ERR_PTR(-ENODEV); } EXPORT_SYMBOL_GPL(rndis_register); -void rndis_deregister(int configNr) +void rndis_deregister(struct rndis_params *params) { pr_debug("%s:\n", __func__); - if (configNr >= RNDIS_MAX_CONFIGS) return; - rndis_per_dev_params[configNr].used = 0; + if (!params) + return; + params->used = 0; } EXPORT_SYMBOL_GPL(rndis_deregister); -int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter) +int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev, + u16 *cdc_filter) { pr_debug("%s:\n", __func__); if (!dev) return -EINVAL; - if (configNr >= RNDIS_MAX_CONFIGS) return -1; + if (!params) + return -1; - rndis_per_dev_params[configNr].dev = dev; - rndis_per_dev_params[configNr].filter = cdc_filter; + params->dev = dev; + params->filter = cdc_filter; return 0; } EXPORT_SYMBOL_GPL(rndis_set_param_dev); -int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr) +int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID, + const char *vendorDescr) { pr_debug("%s:\n", __func__); if (!vendorDescr) return -1; - if (configNr >= RNDIS_MAX_CONFIGS) return -1; + if (!params) + return -1; - rndis_per_dev_params[configNr].vendorID = vendorID; - rndis_per_dev_params[configNr].vendorDescr = vendorDescr; + params->vendorID = vendorID; + params->vendorDescr = vendorDescr; return 0; } EXPORT_SYMBOL_GPL(rndis_set_param_vendor); -int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed) +int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed) { pr_debug("%s: %u %u\n", __func__, medium, speed); - if (configNr >= RNDIS_MAX_CONFIGS) return -1; + if (!params) + return -1; - rndis_per_dev_params[configNr].medium = medium; - rndis_per_dev_params[configNr].speed = speed; + params->medium = medium; + params->speed = speed; return 0; } @@ -961,13 +941,12 @@ void rndis_add_hdr(struct sk_buff *skb) } EXPORT_SYMBOL_GPL(rndis_add_hdr); -void rndis_free_response(int configNr, u8 *buf) +void rndis_free_response(struct rndis_params *params, u8 *buf) { rndis_resp_t *r; struct list_head *act, *tmp; - list_for_each_safe(act, tmp, - &(rndis_per_dev_params[configNr].resp_queue)) + list_for_each_safe(act, tmp, &(params->resp_queue)) { r = list_entry(act, rndis_resp_t, list); if (r && r->buf == buf) { @@ -978,15 +957,14 @@ void rndis_free_response(int configNr, u8 *buf) } EXPORT_SYMBOL_GPL(rndis_free_response); -u8 *rndis_get_next_response(int configNr, u32 *length) +u8 *rndis_get_next_response(struct rndis_params *params, u32 *length) { rndis_resp_t *r; struct list_head *act, *tmp; if (!length) return NULL; - list_for_each_safe(act, tmp, - &(rndis_per_dev_params[configNr].resp_queue)) + list_for_each_safe(act, tmp, &(params->resp_queue)) { r = list_entry(act, rndis_resp_t, list); if (!r->send) { @@ -1000,7 +978,7 @@ u8 *rndis_get_next_response(int configNr, u32 *length) } EXPORT_SYMBOL_GPL(rndis_get_next_response); -static rndis_resp_t *rndis_add_response(int configNr, u32 length) +static rndis_resp_t *rndis_add_response(struct rndis_params *params, u32 length) { rndis_resp_t *r; @@ -1012,8 +990,7 @@ static rndis_resp_t *rndis_add_response(int configNr, u32 length) r->length = length; r->send = 0; - list_add_tail(&r->list, - &(rndis_per_dev_params[configNr].resp_queue)); + list_add_tail(&r->list, &(params->resp_queue)); return r; } diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h index 0f4abb4..5ab940b 100644 --- a/drivers/usb/gadget/function/rndis.h +++ b/drivers/usb/gadget/function/rndis.h @@ -197,24 +197,25 @@ typedef struct rndis_params } rndis_params; /* RNDIS Message parser and other useless functions */ -int rndis_msg_parser (u8 configNr, u8 *buf); -int rndis_register(void (*resp_avail)(void *v), void *v); -void rndis_deregister (int configNr); -int rndis_set_param_dev (u8 configNr, struct net_device *dev, +int rndis_msg_parser(struct rndis_params *params, u8 *buf); +struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v); +void rndis_deregister(struct rndis_params *params); +int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev, u16 *cdc_filter); -int rndis_set_param_vendor (u8 configNr, u32 vendorID, +int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID, const char *vendorDescr); -int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed); +int rndis_set_param_medium(struct rndis_params *params, u32 medium, + u32 speed); void rndis_add_hdr (struct sk_buff *skb); int rndis_rm_hdr(struct gether *port, struct sk_buff *skb, struct sk_buff_head *list); -u8 *rndis_get_next_response (int configNr, u32 *length); -void rndis_free_response (int configNr, u8 *buf); - -void rndis_uninit (int configNr); -int rndis_signal_connect (int configNr); -int rndis_signal_disconnect (int configNr); -int rndis_state (int configNr); -extern void rndis_set_host_mac (int configNr, const u8 *addr); +u8 *rndis_get_next_response(struct rndis_params *params, u32 *length); +void rndis_free_response(struct rndis_params *params, u8 *buf); + +void rndis_uninit(struct rndis_params *params); +int rndis_signal_connect(struct rndis_params *params); +int rndis_signal_disconnect(struct rndis_params *params); +int rndis_state(struct rndis_params *params); +extern void rndis_set_host_mac(struct rndis_params *params, const u8 *addr); #endif /* _LINUX_RNDIS_H */ -- cgit v0.10.2 From 6122b151c7799673794927031a884df0f2355922 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Fri, 6 Feb 2015 13:43:29 +0100 Subject: usb: gadget: rndis: style correction Don't use a space between function name and parameter list opening bracket. All other functions in this file comply wich checkpatch rules. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h index 5ab940b..194abb1 100644 --- a/drivers/usb/gadget/function/rndis.h +++ b/drivers/usb/gadget/function/rndis.h @@ -206,7 +206,7 @@ int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID, const char *vendorDescr); int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed); -void rndis_add_hdr (struct sk_buff *skb); +void rndis_add_hdr(struct sk_buff *skb); int rndis_rm_hdr(struct gether *port, struct sk_buff *skb, struct sk_buff_head *list); u8 *rndis_get_next_response(struct rndis_params *params, u32 *length); -- cgit v0.10.2 From d6d22922d9070b660e3dce0a87a94f0b581e803e Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Fri, 6 Feb 2015 13:43:30 +0100 Subject: usb: gadget: rndis: remove the limit of available rndis connections RNDIS function has a limitation on the number of allowed instances. So far it has been RNDIS_MAX_CONFIGS, which happens to be one. In order to eliminate this kind of arbitrary limitation we should not preallocate a predefined (RNDIS_MAX_CONFIGS) array of struct rndis_params instances but instead allow allocating them on demand. This patch allocates struct rndis_params on demand in rndis_register(). Coversly, the structure is free()'d in rndis_deregister(). If CONFIG_USB_GADGET_DEBUG_FILES is set, the proc files are created which is the same behaviour as before, but the moment of creation is delayed until struct rndis_params is actually allocated. rnids_init() and rndis_exit() have nothing to do, so they are eliminated. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 7769eee..5926780 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -526,8 +526,6 @@ Except for ifname they can be written to until the function is linked to a configuration. The ifname is read-only and contains the name of the interface which was assigned by the net core, e. g. usb0. -By default there can be only 1 RNDIS interface in the system. - Testing the RNDIS function -------------------------- diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 2dafe72..32985da 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -1012,26 +1012,6 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) return &rndis->port.func; } -DECLARE_USB_FUNCTION(rndis, rndis_alloc_inst, rndis_alloc); - -static int __init rndis_mod_init(void) -{ - int ret; - - ret = rndis_init(); - if (ret) - return ret; - - return usb_function_register(&rndisusb_func); -} -module_init(rndis_mod_init); - -static void __exit rndis_mod_exit(void) -{ - usb_function_unregister(&rndisusb_func); - rndis_exit(); -} -module_exit(rndis_mod_exit); - +DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 01a3b58..dd68000 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -57,10 +58,13 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging"); #define rndis_debug 0 #endif -#define RNDIS_MAX_CONFIGS 1 +#ifdef CONFIG_USB_GADGET_DEBUG_FILES +#define NAME_TEMPLATE "driver/rndis-%03d" -static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS]; +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + +static DEFINE_IDA(rndis_ida); /* Driver Version */ static const __le32 rndis_driver_version = cpu_to_le32(1); @@ -69,6 +73,11 @@ static const __le32 rndis_driver_version = cpu_to_le32(1); static rndis_resp_t *rndis_add_response(struct rndis_params *params, u32 length); +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +static const struct file_operations rndis_proc_fops; + +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ /* supported OIDs */ static const u32 oid_supported_list[] = @@ -850,38 +859,93 @@ int rndis_msg_parser(struct rndis_params *params, u8 *buf) } EXPORT_SYMBOL_GPL(rndis_msg_parser); +static inline int rndis_get_nr(void) +{ + return ida_simple_get(&rndis_ida, 0, 0, GFP_KERNEL); +} + +static inline void rndis_put_nr(int nr) +{ + ida_simple_remove(&rndis_ida, nr); +} + struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v) { + struct rndis_params *params; u8 i; if (!resp_avail) return ERR_PTR(-EINVAL); - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - if (!rndis_per_dev_params[i].used) { - rndis_per_dev_params[i].used = 1; - rndis_per_dev_params[i].resp_avail = resp_avail; - rndis_per_dev_params[i].v = v; - pr_debug("%s: configNr = %d\n", __func__, i); - return &rndis_per_dev_params[i]; + i = rndis_get_nr(); + if (i < 0) { + pr_debug("failed\n"); + + return ERR_PTR(-ENODEV); + } + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + rndis_put_nr(i); + + return ERR_PTR(-ENOMEM); + } + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + { + struct proc_dir_entry *proc_entry; + char name[20]; + + sprintf(name, NAME_TEMPLATE, i); + proc_entry = proc_create_data(name, 0660, NULL, + &rndis_proc_fops, params); + if (!proc_entry) { + kfree(params); + rndis_put_nr(i); + + return ERR_PTR(-EIO); } } - pr_debug("failed\n"); +#endif - return ERR_PTR(-ENODEV); + params->confignr = i; + params->used = 1; + params->state = RNDIS_UNINITIALIZED; + params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED; + params->resp_avail = resp_avail; + params->v = v; + INIT_LIST_HEAD(&(params->resp_queue)); + pr_debug("%s: configNr = %d\n", __func__, i); + + return params; } EXPORT_SYMBOL_GPL(rndis_register); void rndis_deregister(struct rndis_params *params) { + u8 i; + pr_debug("%s:\n", __func__); if (!params) return; - params->used = 0; + + i = params->confignr; + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + { + u8 i; + char name[20]; + + sprintf(name, NAME_TEMPLATE, i); + remove_proc_entry(name, NULL); + } +#endif + + kfree(params); + rndis_put_nr(i); } EXPORT_SYMBOL_GPL(rndis_deregister); - int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev, u16 *cdc_filter) { @@ -1114,54 +1178,4 @@ static const struct file_operations rndis_proc_fops = { #define NAME_TEMPLATE "driver/rndis-%03d" -static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; - #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ - - -int rndis_init(void) -{ - u8 i; - - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - char name [20]; - - sprintf(name, NAME_TEMPLATE, i); - rndis_connect_state[i] = proc_create_data(name, 0660, NULL, - &rndis_proc_fops, - (void *)(rndis_per_dev_params + i)); - if (!rndis_connect_state[i]) { - pr_debug("%s: remove entries", __func__); - while (i) { - sprintf(name, NAME_TEMPLATE, --i); - remove_proc_entry(name, NULL); - } - pr_debug("\n"); - return -EIO; - } -#endif - rndis_per_dev_params[i].confignr = i; - rndis_per_dev_params[i].used = 0; - rndis_per_dev_params[i].state = RNDIS_UNINITIALIZED; - rndis_per_dev_params[i].media_state - = RNDIS_MEDIA_STATE_DISCONNECTED; - INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue)); - } - - return 0; -} - -void rndis_exit(void) -{ -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - u8 i; - char name[20]; - - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - sprintf(name, NAME_TEMPLATE, i); - remove_proc_entry(name, NULL); - } -#endif -} - diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h index e902aa4..4eafd50 100644 --- a/drivers/usb/gadget/function/u_rndis.h +++ b/drivers/usb/gadget/function/u_rndis.h @@ -39,8 +39,6 @@ struct f_rndis_opts { int refcnt; }; -int rndis_init(void); -void rndis_exit(void); void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net); #endif /* U_RNDIS_H */ -- cgit v0.10.2 From ffc1d299aa836f540a20302e9f22120639ea2944 Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Tue, 24 Mar 2015 15:19:56 -0500 Subject: usb: musb: add softconnect for host mode Add a debugfs interface - softconnect - for host mode to connect/disconnect the devices without physically remove the them. This adds the capability to re-enumerate the devices which are permanently mounted on the board with the MUSB controller together. Signed-off-by: Bin Liu Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c index 04382ec..9b22d94 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -245,6 +245,90 @@ static const struct file_operations musb_test_mode_fops = { .release = single_release, }; +static int musb_softconnect_show(struct seq_file *s, void *unused) +{ + struct musb *musb = s->private; + u8 reg; + int connect; + + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_HOST: + case OTG_STATE_A_WAIT_BCON: + reg = musb_readb(musb->mregs, MUSB_DEVCTL); + connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0; + break; + default: + connect = -1; + } + + seq_printf(s, "%d\n", connect); + + return 0; +} + +static int musb_softconnect_open(struct inode *inode, struct file *file) +{ + return single_open(file, musb_softconnect_show, inode->i_private); +} + +static ssize_t musb_softconnect_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct musb *musb = s->private; + char buf[2]; + u8 reg; + + memset(buf, 0x00, sizeof(buf)); + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "0", 1)) { + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_HOST: + musb_root_disconnect(musb); + reg = musb_readb(musb->mregs, MUSB_DEVCTL); + reg &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, reg); + break; + default: + break; + } + } else if (!strncmp(buf, "1", 1)) { + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_WAIT_BCON: + /* + * musb_save_context() called in musb_runtime_suspend() + * might cache devctl with SESSION bit cleared during + * soft-disconnect, so specifically set SESSION bit + * here to preserve it for musb_runtime_resume(). + */ + musb->context.devctl |= MUSB_DEVCTL_SESSION; + reg = musb_readb(musb->mregs, MUSB_DEVCTL); + reg |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, reg); + break; + default: + break; + } + } + + return count; +} + +/* + * In host mode, connect/disconnect the bus without physically + * remove the devices. + */ +static const struct file_operations musb_softconnect_fops = { + .open = musb_softconnect_open, + .write = musb_softconnect_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + int musb_init_debugfs(struct musb *musb) { struct dentry *root; @@ -271,6 +355,13 @@ int musb_init_debugfs(struct musb *musb) goto err1; } + file = debugfs_create_file("softconnect", S_IRUGO | S_IWUSR, + root, musb, &musb_softconnect_fops); + if (!file) { + ret = -ENOMEM; + goto err1; + } + musb->debugfs_root = root; return 0; -- cgit v0.10.2 From 92bfbf71350b320f718a35446f017fbefd51d850 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 8 Apr 2015 16:36:00 -0700 Subject: usb: Prefer firmware values when determining whether a port is removable Windows appears to pay more attention to the ACPI values than any hub configuration, so prefer the firmware's opinion on whether a port is fixed or removable before falling back to the hub values. Signed-off-by: Matthew Garrett Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3b71516..2df229c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2350,6 +2350,23 @@ static void set_usb_port_removable(struct usb_device *udev) hub = usb_hub_to_struct_hub(udev->parent); + /* + * If the platform firmware has provided information about a port, + * use that to determine whether it's removable. + */ + switch (hub->ports[udev->portnum - 1]->connect_type) { + case USB_PORT_CONNECT_TYPE_HOT_PLUG: + udev->removable = USB_DEVICE_REMOVABLE; + return; + case USB_PORT_CONNECT_TYPE_HARD_WIRED: + udev->removable = USB_DEVICE_FIXED; + return; + } + + /* + * Otherwise, check whether the hub knows whether a port is removable + * or not + */ wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); if (!(wHubCharacteristics & HUB_CHAR_COMPOUND)) @@ -2369,21 +2386,6 @@ static void set_usb_port_removable(struct usb_device *udev) else udev->removable = USB_DEVICE_FIXED; - /* - * Platform firmware may have populated an alternative value for - * removable. If the parent port has a known connect_type use - * that instead. - */ - switch (hub->ports[udev->portnum - 1]->connect_type) { - case USB_PORT_CONNECT_TYPE_HOT_PLUG: - udev->removable = USB_DEVICE_REMOVABLE; - break; - case USB_PORT_CONNECT_TYPE_HARD_WIRED: - udev->removable = USB_DEVICE_FIXED; - break; - default: /* use what was set above */ - break; - } } /** -- cgit v0.10.2 From 58339c2e8b6181e6ffb2ef0dccc4d010b8acd013 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 8 Apr 2015 16:36:01 -0700 Subject: usb: Set unused ports to "fixed" rather than "unknown" The Microsoft document "Using ACPI to Configure USB Ports on a Computer" makes it clear that the removable flag will be cleared on ports that are marked as unused by the firmware. Handle this case to match. Signed-off-by: Matthew Garrett Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 2df229c..95041b6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2359,8 +2359,11 @@ static void set_usb_port_removable(struct usb_device *udev) udev->removable = USB_DEVICE_REMOVABLE; return; case USB_PORT_CONNECT_TYPE_HARD_WIRED: + case USB_PORT_NOT_USED: udev->removable = USB_DEVICE_FIXED; return; + default: + break; } /* -- cgit v0.10.2 From 3091fa77ffb1b91f4ac3ae005efe6213508f7ec1 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 22 Apr 2015 12:11:59 -0400 Subject: USB: don't build PCI quirks if USB support isn't configured MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The USB PCI quirks code gets built into the kernel whenever CONFIG_PCI is enabled, even if CONFIG_USB is not set. This can cause unnecessary messages to show up in the kernel log, such as "CONFIG_USB_XHCI_HCD is turned off, defaulting to EHCI" (which makes no sense when the kernel has been configured without host-side USB support). This patch addresses the problem by building pci-quirks.o only when CONFIG_PCI and CONFIG_USB are both enabled. Signed-off-by: Alan Stern Reported-by: Toralf Förster Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 65b0b6a..972a74a 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -24,7 +24,9 @@ endif obj-$(CONFIG_USB_WHCI_HCD) += whci/ -obj-$(CONFIG_PCI) += pci-quirks.o +ifneq ($(CONFIG_USB), ) + obj-$(CONFIG_PCI) += pci-quirks.o +endif obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o -- cgit v0.10.2 From 7d203a9e11fa179306a9f49ad06eb3dd1b9d8614 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 18 Apr 2015 19:59:48 -0700 Subject: usb: storage: scsiglue: Remove SPRINTF macro use Single transform macros with hidden arguments are not particularly useful. Just use seq_printf directly instead. Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 0e400f3..a8cbbff 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -456,17 +456,13 @@ static int write_info(struct Scsi_Host *host, char *buffer, int length) return length; } -/* we use this macro to help us write into the buffer */ -#undef SPRINTF -#define SPRINTF(args...) seq_printf(m, ## args) - static int show_info (struct seq_file *m, struct Scsi_Host *host) { struct us_data *us = host_to_us(host); const char *string; /* print the controller name */ - SPRINTF(" Host scsi%d: usb-storage\n", host->host_no); + seq_printf(m, " Host scsi%d: usb-storage\n", host->host_no); /* print product, vendor, and serial number strings */ if (us->pusb_dev->manufacturer) @@ -475,26 +471,26 @@ static int show_info (struct seq_file *m, struct Scsi_Host *host) string = us->unusual_dev->vendorName; else string = "Unknown"; - SPRINTF(" Vendor: %s\n", string); + seq_printf(m, " Vendor: %s\n", string); if (us->pusb_dev->product) string = us->pusb_dev->product; else if (us->unusual_dev->productName) string = us->unusual_dev->productName; else string = "Unknown"; - SPRINTF(" Product: %s\n", string); + seq_printf(m, " Product: %s\n", string); if (us->pusb_dev->serial) string = us->pusb_dev->serial; else string = "None"; - SPRINTF("Serial Number: %s\n", string); + seq_printf(m, "Serial Number: %s\n", string); /* show the protocol and transport */ - SPRINTF(" Protocol: %s\n", us->protocol_name); - SPRINTF(" Transport: %s\n", us->transport_name); + seq_printf(m, " Protocol: %s\n", us->protocol_name); + seq_printf(m, " Transport: %s\n", us->transport_name); /* show the device flags */ - SPRINTF(" Quirks:"); + seq_printf(m, " Quirks:"); #define US_FLAG(name, value) \ if (us->fflags & value) seq_printf(m, " " #name); -- cgit v0.10.2 From a8425292816ceaa8c49e29d2114e85d85a73e080 Mon Sep 17 00:00:00 2001 From: Rupesh Tatiya Date: Tue, 14 Apr 2015 16:36:55 +0530 Subject: usb: Enable LPM for USB 2.01+ full-speed devices USB 2.01+ full-speed devices can have extended descriptor as well and can support LPM. Signed-off-by: Rupesh Tatiya Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 95041b6..52178bc 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -127,7 +127,7 @@ static int usb_device_supports_lpm(struct usb_device *udev) /* USB 2.1 (and greater) devices indicate LPM support through * their USB 2.0 Extended Capabilities BOS descriptor. */ - if (udev->speed == USB_SPEED_HIGH) { + if (udev->speed == USB_SPEED_HIGH || udev->speed == USB_SPEED_FULL) { if (udev->bos->ext_cap && (USB_LPM_SUPPORT & le32_to_cpu(udev->bos->ext_cap->bmAttributes))) -- cgit v0.10.2 From 8ca9a3217f0ac35e8595bf203685a2205213ddc8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 23 Apr 2015 16:06:50 +0200 Subject: USB: musb: fix inefficient copy of unaligned buffers Make sure only to copy any actual data rather than the whole buffer, when releasing the temporary buffer used for unaligned non-isochronous transfers. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index c3d5fc9..e1fb5d8 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2512,6 +2512,7 @@ static void musb_free_temp_buffer(struct urb *urb) { enum dma_data_direction dir; struct musb_temp_buffer *temp; + size_t length; if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) return; @@ -2522,8 +2523,12 @@ static void musb_free_temp_buffer(struct urb *urb) data); if (dir == DMA_FROM_DEVICE) { - memcpy(temp->old_xfer_buffer, temp->data, - urb->transfer_buffer_length); + if (usb_pipeisoc(urb->pipe)) + length = urb->transfer_buffer_length; + else + length = urb->actual_length; + + memcpy(temp->old_xfer_buffer, temp->data, length); } urb->transfer_buffer = temp->old_xfer_buffer; kfree(temp->kmalloc_ptr); -- cgit v0.10.2 From 0efd937e27d5e169031c2bc45d65fe1b0d289454 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 23 Apr 2015 16:06:51 +0200 Subject: USB: ehci-tegra: fix inefficient copy of unaligned buffers Make sure only to copy any actual data rather than the whole buffer, when releasing the temporary buffer used for unaligned non-isochronous transfers. Compile-tested only. Signed-off-by: Johan Hovold Tested-by: Stephen Warren Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index ff9af29..4031b37 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -304,6 +304,7 @@ struct dma_aligned_buffer { static void free_dma_aligned_buffer(struct urb *urb) { struct dma_aligned_buffer *temp; + size_t length; if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) return; @@ -311,9 +312,14 @@ static void free_dma_aligned_buffer(struct urb *urb) temp = container_of(urb->transfer_buffer, struct dma_aligned_buffer, data); - if (usb_urb_dir_in(urb)) - memcpy(temp->old_xfer_buffer, temp->data, - urb->transfer_buffer_length); + if (usb_urb_dir_in(urb)) { + if (usb_pipeisoc(urb->pipe)) + length = urb->transfer_buffer_length; + else + length = urb->actual_length; + + memcpy(temp->old_xfer_buffer, temp->data, length); + } urb->transfer_buffer = temp->old_xfer_buffer; kfree(temp->kmalloc_ptr); -- cgit v0.10.2 From 34ef33f7da6b00900d3a896d33522a035a930245 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 28 Apr 2015 14:04:07 +0200 Subject: usb: phy: Remove the phy-rcar-gen2-usb driver The phy-rcar-gen2-usb driver, which supports legacy platform data only, is no longer used since commit a483dcbfa21f919c ("ARM: shmobile: lager: Remove legacy board support"). This driver was superseded by the DT-only phy-rcar-gen2 driver, which was introduced in commit 1233f59f745b237d ("phy: Renesas R-Car Gen2 PHY driver"). Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 2175678..3cd3bee 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -186,19 +186,6 @@ config USB_RCAR_PHY To compile this driver as a module, choose M here: the module will be called phy-rcar-usb. -config USB_RCAR_GEN2_PHY - tristate "Renesas R-Car Gen2 USB PHY support" - depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST - select USB_PHY - help - Say Y here to add support for the Renesas R-Car Gen2 USB PHY driver. - It is typically used to control internal USB PHY for USBHS, - and to configure shared USB channels 0 and 2. - This driver supports R8A7790 and R8A7791. - - To compile this driver as a module, choose M here: the - module will be called phy-rcar-gen2-usb. - config USB_ULPI bool "Generic ULPI Transceiver Driver" depends on ARM || ARM64 diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 75f2bba..e36ab1d 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -23,7 +23,6 @@ obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o -obj-$(CONFIG_USB_RCAR_GEN2_PHY) += phy-rcar-gen2-usb.o obj-$(CONFIG_USB_ULPI) += phy-ulpi.o obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c deleted file mode 100644 index f81800b..0000000 --- a/drivers/usb/phy/phy-rcar-gen2-usb.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Renesas R-Car Gen2 USB phy driver - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Copyright (C) 2013 Cogent Embedded, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -struct rcar_gen2_usb_phy_priv { - struct usb_phy phy; - void __iomem *base; - struct clk *clk; - spinlock_t lock; - int usecount; - u32 ugctrl2; -}; - -#define usb_phy_to_priv(p) container_of(p, struct rcar_gen2_usb_phy_priv, phy) - -/* Low Power Status register */ -#define USBHS_LPSTS_REG 0x02 -#define USBHS_LPSTS_SUSPM (1 << 14) - -/* USB General control register */ -#define USBHS_UGCTRL_REG 0x80 -#define USBHS_UGCTRL_CONNECT (1 << 2) -#define USBHS_UGCTRL_PLLRESET (1 << 0) - -/* USB General control register 2 */ -#define USBHS_UGCTRL2_REG 0x84 -#define USBHS_UGCTRL2_USB0_PCI (1 << 4) -#define USBHS_UGCTRL2_USB0_HS (3 << 4) -#define USBHS_UGCTRL2_USB2_PCI (0 << 31) -#define USBHS_UGCTRL2_USB2_SS (1 << 31) - -/* USB General status register */ -#define USBHS_UGSTS_REG 0x88 -#define USBHS_UGSTS_LOCK (1 << 8) - -/* Enable USBHS internal phy */ -static int __rcar_gen2_usbhs_phy_enable(void __iomem *base) -{ - u32 val; - int i; - - /* USBHS PHY power on */ - val = ioread32(base + USBHS_UGCTRL_REG); - val &= ~USBHS_UGCTRL_PLLRESET; - iowrite32(val, base + USBHS_UGCTRL_REG); - - val = ioread16(base + USBHS_LPSTS_REG); - val |= USBHS_LPSTS_SUSPM; - iowrite16(val, base + USBHS_LPSTS_REG); - - for (i = 0; i < 20; i++) { - val = ioread32(base + USBHS_UGSTS_REG); - if ((val & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) { - val = ioread32(base + USBHS_UGCTRL_REG); - val |= USBHS_UGCTRL_CONNECT; - iowrite32(val, base + USBHS_UGCTRL_REG); - return 0; - } - udelay(1); - } - - /* Timed out waiting for the PLL lock */ - return -ETIMEDOUT; -} - -/* Disable USBHS internal phy */ -static int __rcar_gen2_usbhs_phy_disable(void __iomem *base) -{ - u32 val; - - /* USBHS PHY power off */ - val = ioread32(base + USBHS_UGCTRL_REG); - val &= ~USBHS_UGCTRL_CONNECT; - iowrite32(val, base + USBHS_UGCTRL_REG); - - val = ioread16(base + USBHS_LPSTS_REG); - val &= ~USBHS_LPSTS_SUSPM; - iowrite16(val, base + USBHS_LPSTS_REG); - - val = ioread32(base + USBHS_UGCTRL_REG); - val |= USBHS_UGCTRL_PLLRESET; - iowrite32(val, base + USBHS_UGCTRL_REG); - return 0; -} - -/* Setup USB channels */ -static void __rcar_gen2_usb_phy_init(struct rcar_gen2_usb_phy_priv *priv) -{ - u32 val; - - clk_prepare_enable(priv->clk); - - /* Set USB channels in the USBHS UGCTRL2 register */ - val = ioread32(priv->base + USBHS_UGCTRL2_REG); - val &= ~(USBHS_UGCTRL2_USB0_HS | USBHS_UGCTRL2_USB2_SS); - val |= priv->ugctrl2; - iowrite32(val, priv->base + USBHS_UGCTRL2_REG); -} - -/* Shutdown USB channels */ -static void __rcar_gen2_usb_phy_shutdown(struct rcar_gen2_usb_phy_priv *priv) -{ - __rcar_gen2_usbhs_phy_disable(priv->base); - clk_disable_unprepare(priv->clk); -} - -static int rcar_gen2_usb_phy_set_suspend(struct usb_phy *phy, int suspend) -{ - struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); - unsigned long flags; - int retval; - - spin_lock_irqsave(&priv->lock, flags); - retval = suspend ? __rcar_gen2_usbhs_phy_disable(priv->base) : - __rcar_gen2_usbhs_phy_enable(priv->base); - spin_unlock_irqrestore(&priv->lock, flags); - return retval; -} - -static int rcar_gen2_usb_phy_init(struct usb_phy *phy) -{ - struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - /* - * Enable the clock and setup USB channels - * if it's the first user - */ - if (!priv->usecount++) - __rcar_gen2_usb_phy_init(priv); - spin_unlock_irqrestore(&priv->lock, flags); - return 0; -} - -static void rcar_gen2_usb_phy_shutdown(struct usb_phy *phy) -{ - struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (!priv->usecount) { - dev_warn(phy->dev, "Trying to disable phy with 0 usecount\n"); - goto out; - } - - /* Disable everything if it's the last user */ - if (!--priv->usecount) - __rcar_gen2_usb_phy_shutdown(priv); -out: - spin_unlock_irqrestore(&priv->lock, flags); -} - -static int rcar_gen2_usb_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rcar_gen2_phy_platform_data *pdata; - struct rcar_gen2_usb_phy_priv *priv; - struct resource *res; - void __iomem *base; - struct clk *clk; - int retval; - - pdata = dev_get_platdata(dev); - if (!pdata) { - dev_err(dev, "No platform data\n"); - return -EINVAL; - } - - clk = devm_clk_get(dev, "usbhs"); - if (IS_ERR(clk)) { - dev_err(dev, "Can't get the clock\n"); - return PTR_ERR(clk); - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - spin_lock_init(&priv->lock); - priv->clk = clk; - priv->base = base; - priv->ugctrl2 = pdata->chan0_pci ? - USBHS_UGCTRL2_USB0_PCI : USBHS_UGCTRL2_USB0_HS; - priv->ugctrl2 |= pdata->chan2_pci ? - USBHS_UGCTRL2_USB2_PCI : USBHS_UGCTRL2_USB2_SS; - priv->phy.dev = dev; - priv->phy.label = dev_name(dev); - priv->phy.init = rcar_gen2_usb_phy_init; - priv->phy.shutdown = rcar_gen2_usb_phy_shutdown; - priv->phy.set_suspend = rcar_gen2_usb_phy_set_suspend; - - retval = usb_add_phy_dev(&priv->phy); - if (retval < 0) { - dev_err(dev, "Failed to add USB phy\n"); - return retval; - } - - platform_set_drvdata(pdev, priv); - - return retval; -} - -static int rcar_gen2_usb_phy_remove(struct platform_device *pdev) -{ - struct rcar_gen2_usb_phy_priv *priv = platform_get_drvdata(pdev); - - usb_remove_phy(&priv->phy); - - return 0; -} - -static struct platform_driver rcar_gen2_usb_phy_driver = { - .driver = { - .name = "usb_phy_rcar_gen2", - }, - .probe = rcar_gen2_usb_phy_probe, - .remove = rcar_gen2_usb_phy_remove, -}; - -module_platform_driver(rcar_gen2_usb_phy_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Renesas R-Car Gen2 USB phy"); -MODULE_AUTHOR("Valentine Barshak "); diff --git a/include/linux/platform_data/usb-rcar-gen2-phy.h b/include/linux/platform_data/usb-rcar-gen2-phy.h deleted file mode 100644 index dd3ba46..0000000 --- a/include/linux/platform_data/usb-rcar-gen2-phy.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2013 Renesas Solutions Corp. - * Copyright (C) 2013 Cogent Embedded, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __USB_RCAR_GEN2_PHY_H -#define __USB_RCAR_GEN2_PHY_H - -#include - -struct rcar_gen2_phy_platform_data { - /* USB channel 0 configuration */ - bool chan0_pci:1; /* true: PCI USB host 0, false: USBHS */ - /* USB channel 2 configuration */ - bool chan2_pci:1; /* true: PCI USB host 2, false: USBSS */ -}; - -#endif -- cgit v0.10.2 From 424dd7dd9b48691c1bb57440493590c012199a95 Mon Sep 17 00:00:00 2001 From: Nizam Haider Date: Wed, 29 Apr 2015 16:19:33 +0530 Subject: Usb: core: buffer: fixed the checkpatch warning Fixed two warnings sizeof name and Blank line after declaration Signed-off-by: Nizam Haider Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 506b969..89f2e77 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -70,7 +70,7 @@ int hcd_buffer_create(struct usb_hcd *hcd) size = pool_max[i]; if (!size) continue; - snprintf(name, sizeof name, "buffer-%d", size); + snprintf(name, sizeof(name), "buffer-%d", size); hcd->pool[i] = dma_pool_create(name, hcd->self.controller, size, size, 0); if (!hcd->pool[i]) { @@ -95,6 +95,7 @@ void hcd_buffer_destroy(struct usb_hcd *hcd) for (i = 0; i < HCD_BUFFER_POOLS; i++) { struct dma_pool *pool = hcd->pool[i]; + if (pool) { dma_pool_destroy(pool); hcd->pool[i] = NULL; -- cgit v0.10.2 From 911fdb6ecad37cf64cbc46d839afb7973fc36a12 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:32:50 +0200 Subject: USB: ehci-dbg.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 524cbf2..b26b96e 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -628,7 +628,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) unsigned i; __hc32 tag; - if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC))) + seen = kmalloc(DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC); + if (!seen) return 0; seen_count = 0; -- cgit v0.10.2 From 0a9abb1e15aa64fd79beb73441361e772daf234d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:32:51 +0200 Subject: USB: fusbh200-hcd.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Sergei Shtylyov Acked-by: Alan Stern CC: Petr Mladek CC: Randy Dunlap CC: Masanari Iida Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/host/fusbh200-hcd.c b/drivers/usb/host/fusbh200-hcd.c index 00e492e..1fd8718 100644 --- a/drivers/usb/host/fusbh200-hcd.c +++ b/drivers/usb/host/fusbh200-hcd.c @@ -499,7 +499,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) unsigned i; __hc32 tag; - if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC))) + seen = kmalloc(DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC); + if (!seen) return 0; seen_count = 0; -- cgit v0.10.2 From c688d6211f57e1f6f8aaae5522a9223247febbd6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:32:52 +0200 Subject: USB: hcd.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. Acked-by: Alan Stern CC: Dan Williams CC: Antoine Tenart CC: Petr Mladek CC: Michal Sojka CC: "Rafael J. Wysocki" CC: Sergei Shtylyov Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 45a915c..be5b207 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2691,7 +2691,8 @@ int usb_add_hcd(struct usb_hcd *hcd, if ((retval = usb_register_bus(&hcd->self)) < 0) goto err_register_bus; - if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) { + rhdev = usb_alloc_dev(NULL, &hcd->self, 0); + if (rhdev == NULL) { dev_err(hcd->self.controller, "unable to allocate root hub\n"); retval = -ENOMEM; goto err_allocate_root_hub; -- cgit v0.10.2 From d544d273f398a40f71012cfd165dba6ef73690c9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:32:53 +0200 Subject: USB: hub.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. Acked-by: Alan Stern CC: Dan Williams CC: Petr Mladek CC: Peter Chen Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 52178bc..50da096 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -795,7 +795,8 @@ int usb_hub_clear_tt_buffer(struct urb *urb) * since each TT has "at least two" buffers that can need it (and * there can be many TTs per hub). even if they're uncommon. */ - if ((clear = kmalloc (sizeof *clear, GFP_ATOMIC)) == NULL) { + clear = kmalloc(sizeof *clear, GFP_ATOMIC); + if (clear == NULL) { dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n"); /* FIXME recover somehow ... RESET_TT? */ return -ENOMEM; -- cgit v0.10.2 From 1ee7eead7071efc71f3dde214f215dfb820069e1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:32:54 +0200 Subject: USB: inode.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Al Viro CC: Peter Chen Acked-by: Alan Stern CC: Andrzej Pietrasiewicz CC: Paul Bolle Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 2030565..f454c7a 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -769,9 +769,12 @@ ep_config (struct ep_data *data, const char *buf, size_t len) if (data->dev->state == STATE_DEV_UNBOUND) { value = -ENOENT; goto gone; - } else if ((ep = data->ep) == NULL) { - value = -ENODEV; - goto gone; + } else { + ep = data->ep; + if (ep == NULL) { + value = -ENODEV; + goto gone; + } } switch (data->dev->gadget->speed) { case USB_SPEED_LOW: -- cgit v0.10.2 From 925f0042cf2058cd3929c9c49593fba7750ffe8d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:32:55 +0200 Subject: USB: isp116x-hcd.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Olav Kongas Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 13181dc..d089b3f 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -500,7 +500,8 @@ static void start_atl_transfers(struct isp116x *isp116x) if (isp116x->periodic_count) { isp116x->fmindex = index = (isp116x->fmindex + 1) & (PERIODIC_SIZE - 1); - if ((load = isp116x->load[index])) { + load = isp116x->load[index]; + if (load) { /* Bring all int transfers for this frame into the active queue */ isp116x->atl_active = last_ep = -- cgit v0.10.2 From 0296951a62bb863008821b33343cff446fce4b84 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:32:56 +0200 Subject: USB: mon_bin.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 9a62e89..3598f1a 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -675,7 +675,8 @@ static int mon_bin_open(struct inode *inode, struct file *file) int rc; mutex_lock(&mon_lock); - if ((mbus = mon_bus_lookup(iminor(inode))) == NULL) { + mbus = mon_bus_lookup(iminor(inode)); + if (mbus == NULL) { mutex_unlock(&mon_lock); return -ENODEV; } @@ -1018,8 +1019,8 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg return -EINVAL; size = CHUNK_ALIGN(arg); - if ((vec = kzalloc(sizeof(struct mon_pgmap) * (size/CHUNK_SIZE), - GFP_KERNEL)) == NULL) { + vec = kzalloc(sizeof(struct mon_pgmap) * (size / CHUNK_SIZE), GFP_KERNEL); + if (vec == NULL) { ret = -ENOMEM; break; } -- cgit v0.10.2 From d4950d5dab78a78c90c0c5cd6113af28cbac516f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:32:57 +0200 Subject: USB: mon_main.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index 1040511..f7c292f 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -96,7 +96,8 @@ static void mon_submit(struct usb_bus *ubus, struct urb *urb) { struct mon_bus *mbus; - if ((mbus = ubus->mon_bus) != NULL) + mbus = ubus->mon_bus; + if (mbus != NULL) mon_bus_submit(mbus, urb); mon_bus_submit(&mon_bus0, urb); } @@ -122,7 +123,8 @@ static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error) { struct mon_bus *mbus; - if ((mbus = ubus->mon_bus) != NULL) + mbus = ubus->mon_bus; + if (mbus != NULL) mon_bus_submit_error(mbus, urb, error); mon_bus_submit_error(&mon_bus0, urb, error); } @@ -148,7 +150,8 @@ static void mon_complete(struct usb_bus *ubus, struct urb *urb, int status) { struct mon_bus *mbus; - if ((mbus = ubus->mon_bus) != NULL) + mbus = ubus->mon_bus; + if (mbus != NULL) mon_bus_complete(mbus, urb, status); mon_bus_complete(&mon_bus0, urb, status); } @@ -280,7 +283,8 @@ static void mon_bus_init(struct usb_bus *ubus) { struct mon_bus *mbus; - if ((mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL) + mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL); + if (mbus == NULL) goto err_alloc; kref_init(&mbus->ref); spin_lock_init(&mbus->lock); -- cgit v0.10.2 From 4c08ccf0dc50a7a4e8d1ddc41e9e6d5d25aacdc1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:32:58 +0200 Subject: USB: mon_stat.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c index ebd6189..5388a33 100644 --- a/drivers/usb/mon/mon_stat.c +++ b/drivers/usb/mon/mon_stat.c @@ -28,7 +28,8 @@ static int mon_stat_open(struct inode *inode, struct file *file) struct mon_bus *mbus; struct snap *sp; - if ((sp = kmalloc(sizeof(struct snap), GFP_KERNEL)) == NULL) + sp = kmalloc(sizeof(struct snap), GFP_KERNEL); + if (sp == NULL) return -ENOMEM; mbus = inode->i_private; -- cgit v0.10.2 From d0d6bc89b6da035610ee5087593c1fdb35349249 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:32:59 +0200 Subject: USB: ohci-dbg.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 04f2186..c3eded3 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -491,7 +491,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) char *next; unsigned i; - if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC))) + seen = kmalloc(DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC); + if (!seen) return 0; seen_count = 0; @@ -506,7 +507,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) /* dump a snapshot of the periodic schedule (and load) */ spin_lock_irqsave (&ohci->lock, flags); for (i = 0; i < NUM_INTS; i++) { - if (!(ed = ohci->periodic [i])) + ed = ohci->periodic[i]; + if (!ed) continue; temp = scnprintf (next, size, "%2d [%3d]:", i, ohci->load [i]); -- cgit v0.10.2 From 71f46340738322246285a40e25c1c3457a7d986b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:33:00 +0200 Subject: USB: ohci-hcd.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 1dab9df..760cb57 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -155,7 +155,8 @@ static int ohci_urb_enqueue ( int retval = 0; /* every endpoint has a ed, locate and maybe (re)initialize it */ - if (! (ed = ed_get (ohci, urb->ep, urb->dev, pipe, urb->interval))) + ed = ed_get(ohci, urb->ep, urb->dev, pipe, urb->interval); + if (! ed) return -ENOMEM; /* for the private part of the URB we need the number of TDs (size) */ -- cgit v0.10.2 From 8af9319198f04f10aa63f751fb0bfbf4f97be6eb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:33:01 +0200 Subject: USB: ohci-q.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 1463c39..f7d561e 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -407,7 +407,8 @@ static struct ed *ed_get ( spin_lock_irqsave (&ohci->lock, flags); - if (!(ed = ep->hcpriv)) { + ed = ep->hcpriv; + if (!ed) { struct td *td; int is_out; u32 info; -- cgit v0.10.2 From 187859f9ce217a56ba7873d1c32562c02ac516fa Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:33:02 +0200 Subject: USB: sisusb.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Thomas Winischhofer Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 022dc00..306d685 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -2316,10 +2316,12 @@ sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) /* Set mode 0x03 */ SiSUSBSetMode(sisusb->SiS_Pr, 0x03); - if (!(myfont = find_font("VGA8x16"))) + myfont = find_font("VGA8x16"); + if (!myfont) return 1; - if (!(tempbuf = vmalloc(8192))) + tempbuf = vmalloc(8192); + if (!tempbuf) return 1; for (i = 0; i < 256; i++) @@ -2342,7 +2344,8 @@ sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) if (init && !sisusb->scrbuf) { - if ((tempbuf = vmalloc(8192))) { + tempbuf = vmalloc(8192); + if (tempbuf) { i = 4096; tempbufb = (u16 *)tempbuf; @@ -2417,11 +2420,13 @@ sisusb_open(struct inode *inode, struct file *file) struct usb_interface *interface; int subminor = iminor(inode); - if (!(interface = usb_find_interface(&sisusb_driver, subminor))) { + interface = usb_find_interface(&sisusb_driver, subminor); + if (!interface) { return -ENODEV; } - if (!(sisusb = usb_get_intfdata(interface))) { + sisusb = usb_get_intfdata(interface); + if (!sisusb) { return -ENODEV; } @@ -2488,7 +2493,8 @@ sisusb_release(struct inode *inode, struct file *file) { struct sisusb_usb_data *sisusb; - if (!(sisusb = file->private_data)) + sisusb = file->private_data; + if (!sisusb) return -ENODEV; mutex_lock(&sisusb->lock); @@ -2520,7 +2526,8 @@ sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) u16 buf16; u32 buf32, address; - if (!(sisusb = file->private_data)) + sisusb = file->private_data; + if (!sisusb) return -ENODEV; mutex_lock(&sisusb->lock); @@ -2662,7 +2669,8 @@ sisusb_write(struct file *file, const char __user *buffer, size_t count, u16 buf16; u32 buf32, address; - if (!(sisusb = file->private_data)) + sisusb = file->private_data; + if (!sisusb) return -ENODEV; mutex_lock(&sisusb->lock); @@ -2805,7 +2813,8 @@ sisusb_lseek(struct file *file, loff_t offset, int orig) struct sisusb_usb_data *sisusb; loff_t ret; - if (!(sisusb = file->private_data)) + sisusb = file->private_data; + if (!sisusb) return -ENODEV; mutex_lock(&sisusb->lock); @@ -2970,7 +2979,8 @@ sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) long retval = 0; u32 __user *argp = (u32 __user *)arg; - if (!(sisusb = file->private_data)) + sisusb = file->private_data; + if (!sisusb) return -ENODEV; mutex_lock(&sisusb->lock); @@ -3084,7 +3094,8 @@ static int sisusb_probe(struct usb_interface *intf, dev->devnum); /* Allocate memory for our private */ - if (!(sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL))) { + sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL); + if (!sisusb) { dev_err(&dev->dev, "Failed to allocate memory for private data\n"); return -ENOMEM; } @@ -3093,7 +3104,8 @@ static int sisusb_probe(struct usb_interface *intf, mutex_init(&(sisusb->lock)); /* Register device */ - if ((retval = usb_register_dev(intf, &usb_sisusb_class))) { + retval = usb_register_dev(intf, &usb_sisusb_class); + if (retval) { dev_err(&sisusb->sisusb_dev->dev, "Failed to get a minor for device %d\n", dev->devnum); retval = -ENODEV; @@ -3214,7 +3226,8 @@ static void sisusb_disconnect(struct usb_interface *intf) struct sisusb_usb_data *sisusb; /* This should *not* happen */ - if (!(sisusb = usb_get_intfdata(intf))) + sisusb = usb_get_intfdata(intf); + if (!sisusb) return; #ifdef INCL_SISUSB_CON -- cgit v0.10.2 From b87f5671cc76a216465d4e8ba6551991c67e1429 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:33:03 +0200 Subject: USB: sisusb_con.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Thomas Winischhofer Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c index a638c4e..ace3430 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_con.c +++ b/drivers/usb/misc/sisusbvga/sisusb_con.c @@ -169,7 +169,8 @@ sisusb_get_sisusb_lock_and_check(unsigned short console) if (in_atomic()) return NULL; - if (!(sisusb = sisusb_get_sisusb(console))) + sisusb = sisusb_get_sisusb(console); + if (!sisusb) return NULL; mutex_lock(&sisusb->lock); @@ -214,7 +215,8 @@ sisusbcon_init(struct vc_data *c, int init) * are set up/restored. */ - if (!(sisusb = sisusb_get_sisusb(c->vc_num))) + sisusb = sisusb_get_sisusb(c->vc_num); + if (!sisusb) return; mutex_lock(&sisusb->lock); @@ -277,7 +279,8 @@ sisusbcon_deinit(struct vc_data *c) * and others, ie not under our control. */ - if (!(sisusb = sisusb_get_sisusb(c->vc_num))) + sisusb = sisusb_get_sisusb(c->vc_num); + if (!sisusb) return; mutex_lock(&sisusb->lock); @@ -369,7 +372,8 @@ sisusbcon_putc(struct vc_data *c, int ch, int y, int x) struct sisusb_usb_data *sisusb; ssize_t written; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return; /* sisusb->lock is down */ @@ -395,7 +399,8 @@ sisusbcon_putcs(struct vc_data *c, const unsigned short *s, u16 *dest; int i; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return; /* sisusb->lock is down */ @@ -433,7 +438,8 @@ sisusbcon_clear(struct vc_data *c, int y, int x, int height, int width) if (width <= 0 || height <= 0) return; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return; /* sisusb->lock is down */ @@ -486,7 +492,8 @@ sisusbcon_bmove(struct vc_data *c, int sy, int sx, if (width <= 0 || height <= 0) return; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return; /* sisusb->lock is down */ @@ -520,7 +527,8 @@ sisusbcon_switch(struct vc_data *c) * Returnvalue != 0 naturally means the opposite. */ - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return 0; /* sisusb->lock is down */ @@ -569,7 +577,8 @@ sisusbcon_save_screen(struct vc_data *c) * buffer. */ - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return; /* sisusb->lock is down */ @@ -602,7 +611,8 @@ sisusbcon_set_palette(struct vc_data *c, unsigned char *table) if (!CON_IS_VISIBLE(c)) return -EINVAL; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return -EINVAL; /* sisusb->lock is down */ @@ -637,7 +647,8 @@ sisusbcon_blank(struct vc_data *c, int blank, int mode_switch) ssize_t written; int ret = 0; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return 0; /* sisusb->lock is down */ @@ -721,7 +732,8 @@ sisusbcon_scrolldelta(struct vc_data *c, int lines) /* The return value does not seem to be used */ - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return 0; /* sisusb->lock is down */ @@ -779,7 +791,8 @@ sisusbcon_cursor(struct vc_data *c, int mode) struct sisusb_usb_data *sisusb; int from, to, baseline; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return; /* sisusb->lock is down */ @@ -906,7 +919,8 @@ sisusbcon_scroll(struct vc_data *c, int t, int b, int dir, int lines) if (!lines) return 1; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return 0; /* sisusb->lock is down */ @@ -1018,7 +1032,8 @@ sisusbcon_set_origin(struct vc_data *c) * screenbuffer as the origin. */ - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return 0; /* sisusb->lock is down */ @@ -1047,7 +1062,8 @@ sisusbcon_resize(struct vc_data *c, unsigned int newcols, unsigned int newrows, struct sisusb_usb_data *sisusb; int fh; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return -ENODEV; fh = sisusb->current_font_height; @@ -1286,7 +1302,8 @@ sisusbcon_font_set(struct vc_data *c, struct console_font *font, if (font->width != 8 || (charcount != 256 && charcount != 512)) return -EINVAL; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return -ENODEV; /* sisusb->lock is down */ @@ -1326,7 +1343,8 @@ sisusbcon_font_get(struct vc_data *c, struct console_font *font) { struct sisusb_usb_data *sisusb; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return -ENODEV; /* sisusb->lock is down */ -- cgit v0.10.2 From 3383ee4c3abf2efa419ac9c78f097ea1087a4e8e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:33:04 +0200 Subject: USB: speedtch.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Duncan Sands Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 0dc8c06..0270d13 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -255,7 +255,8 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance, usb_dbg(usbatm, "%s entered\n", __func__); - if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) { + buffer = (unsigned char *)__get_free_page(GFP_KERNEL); + if (!buffer) { ret = -ENOMEM; usb_dbg(usbatm, "%s: no memory for buffer!\n", __func__); goto out; @@ -638,7 +639,8 @@ static void speedtch_handle_int(struct urb *int_urb) goto fail; } - if ((int_urb = instance->int_urb)) { + int_urb = instance->int_urb; + if (int_urb) { ret = usb_submit_urb(int_urb, GFP_ATOMIC); schedule_work(&instance->status_check_work); if (ret < 0) { @@ -650,7 +652,8 @@ static void speedtch_handle_int(struct urb *int_urb) return; fail: - if ((int_urb = instance->int_urb)) + int_urb = instance->int_urb; + if (int_urb) mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY)); } @@ -759,11 +762,13 @@ static void speedtch_release_interfaces(struct usb_device *usb_dev, struct usb_interface *cur_intf; int i; - for (i = 0; i < num_interfaces; i++) - if ((cur_intf = usb_ifnum_to_if(usb_dev, i))) { + for (i = 0; i < num_interfaces; i++) { + cur_intf = usb_ifnum_to_if(usb_dev, i); + if (cur_intf) { usb_set_intfdata(cur_intf, NULL); usb_driver_release_interface(&speedtch_usb_driver, cur_intf); } + } } static int speedtch_bind(struct usbatm_data *usbatm, @@ -787,7 +792,8 @@ static int speedtch_bind(struct usbatm_data *usbatm, return -ENODEV; } - if (!(data_intf = usb_ifnum_to_if(usb_dev, INTERFACE_DATA))) { + data_intf = usb_ifnum_to_if(usb_dev, INTERFACE_DATA); + if (!data_intf) { usb_err(usbatm, "%s: data interface not found!\n", __func__); return -ENODEV; } -- cgit v0.10.2 From f354c8456507e552d86acce84a7abd9bf480c382 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:33:05 +0200 Subject: USB: usbatm.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Duncan Sands Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index dada014..db322d9 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -382,7 +382,8 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char "%s: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", __func__, length, pdu_length, vcc); - if (!(skb = dev_alloc_skb(length))) { + skb = dev_alloc_skb(length); + if (!skb) { if (printk_ratelimit()) atm_err(instance, "%s: no memory for skb (length: %u)!\n", __func__, length); @@ -816,7 +817,8 @@ static int usbatm_atm_open(struct atm_vcc *vcc) goto fail; } - if (!(new = kzalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL))) { + new = kzalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL); + if (!new) { atm_err(instance, "%s: no memory for vcc_data!\n", __func__); ret = -ENOMEM; goto fail; -- cgit v0.10.2 From 2fcdbdfd77d80a6de6c625b84574b063cad29cc8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:33:06 +0200 Subject: USB: usblp.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. Acked-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 0924ee4..f38e875 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -660,7 +660,8 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case LPGETSTATUS: - if ((retval = usblp_read_status(usblp, usblp->statusbuf))) { + retval = usblp_read_status(usblp, usblp->statusbuf); + if (retval) { printk_ratelimited(KERN_ERR "usblp%d:" "failed reading printer status (%d)\n", usblp->minor, retval); @@ -693,9 +694,11 @@ static struct urb *usblp_new_writeurb(struct usblp *usblp, int transfer_length) struct urb *urb; char *writebuf; - if ((writebuf = kmalloc(transfer_length, GFP_KERNEL)) == NULL) + writebuf = kmalloc(transfer_length, GFP_KERNEL); + if (writebuf == NULL) return NULL; - if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (urb == NULL) { kfree(writebuf); return NULL; } @@ -732,7 +735,8 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t transfer_length = USBLP_BUF_SIZE; rv = -ENOMEM; - if ((writeurb = usblp_new_writeurb(usblp, transfer_length)) == NULL) + writeurb = usblp_new_writeurb(usblp, transfer_length); + if (writeurb == NULL) goto raise_urb; usb_anchor_urb(writeurb, &usblp->urbs); @@ -980,7 +984,8 @@ static int usblp_submit_read(struct usblp *usblp) int rc; rc = -ENOMEM; - if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) + urb = usb_alloc_urb(0, GFP_KERNEL); + if (urb == NULL) goto raise_urb; usb_fill_bulk_urb(urb, usblp->dev, -- cgit v0.10.2 From adde04c62886c668d2380186f5e60dc75113b5d0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:33:07 +0200 Subject: USB: uss720.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index 588d62a..bbd029c 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -714,7 +714,8 @@ static int uss720_probe(struct usb_interface *intf, /* * Allocate parport interface */ - if (!(priv = kzalloc(sizeof(struct parport_uss720_private), GFP_KERNEL))) { + priv = kzalloc(sizeof(struct parport_uss720_private), GFP_KERNEL); + if (!priv) { usb_put_dev(usbdev); return -ENOMEM; } @@ -723,7 +724,8 @@ static int uss720_probe(struct usb_interface *intf, kref_init(&priv->ref_count); spin_lock_init(&priv->asynclock); INIT_LIST_HEAD(&priv->asynclist); - if (!(pp = parport_register_port(0, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &parport_uss720_ops))) { + pp = parport_register_port(0, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &parport_uss720_ops); + if (!pp) { printk(KERN_WARNING "uss720: could not register parport\n"); goto probe_abort; } -- cgit v0.10.2 From de5198815100a9bcb7b79b4baf2716c37d3fbeeb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:33:08 +0200 Subject: USB: xusbatm.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Felipe Balbi diff --git a/drivers/usb/atm/xusbatm.c b/drivers/usb/atm/xusbatm.c index b3b1bb7..a87597f 100644 --- a/drivers/usb/atm/xusbatm.c +++ b/drivers/usb/atm/xusbatm.c @@ -73,7 +73,8 @@ static int xusbatm_capture_intf(struct usbatm_data *usbatm, struct usb_device *u usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, ifnum, ret); return ret; } - if ((ret = usb_set_interface(usb_dev, ifnum, altsetting))) { + ret = usb_set_interface(usb_dev, ifnum, altsetting); + if (ret) { usb_err(usbatm, "%s: altsetting %2d for interface %2d failed (%d)!\n", __func__, altsetting, ifnum, ret); return ret; } @@ -128,7 +129,8 @@ static int xusbatm_bind(struct usbatm_data *usbatm, rx_intf->altsetting->desc.bInterfaceNumber, tx_intf->altsetting->desc.bInterfaceNumber); - if ((ret = xusbatm_capture_intf(usbatm, usb_dev, rx_intf, rx_alt, rx_intf != intf))) + ret = xusbatm_capture_intf(usbatm, usb_dev, rx_intf, rx_alt, rx_intf != intf); + if (ret) return ret; if ((tx_intf != rx_intf) && (ret = xusbatm_capture_intf(usbatm, usb_dev, tx_intf, tx_alt, tx_intf != intf))) { -- cgit v0.10.2 From 1cb39e256410830833aaae9c5cec8b10a43cf022 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:39:19 +0900 Subject: usb: phy-ab8500-usb: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 7225d52..f063b2b 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -1504,7 +1504,7 @@ static int ab8500_usb_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id ab8500_usb_devtype[] = { +static const struct platform_device_id ab8500_usb_devtype[] = { { .name = "ab8500-usb", }, { .name = "ab8540-usb", }, { .name = "ab9540-usb", }, -- cgit v0.10.2 From aa519be34f45954f33a6c20430deac8e544a180f Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 6 May 2015 18:24:21 +0900 Subject: usb: storage: fix module reference for scsi host While accessing a unusual usb storage (ums-alauda, ums-cypress, ...), the module reference count is not incremented. Because these drivers allocate scsi hosts with usb_stor_host_template defined in usb-storage module. So these drivers always can be unloaded. This fixes it by preparing scsi host template which is initialized at module_init() for each ums-* driver. In order to minimize the difference in ums-* drivers, introduce module_usb_stor_driver() helper macro which is same as module_usb_driver() except that it also initializes scsi host template. Signed-off-by: Akinobu Mita Cc: Vinayak Holikatti Cc: Dolev Raviv Cc: Sujit Reddy Thumma Cc: Subhash Jadavani Cc: Christoph Hellwig Cc: "James E.J. Bottomley" Cc: Matthew Dharm Cc: Greg Kroah-Hartman Cc: "David S. Miller" Cc: Hannes Reinecke Cc: linux-usb@vger.kernel.org Cc: usb-storage@lists.one-eyed-alien.net Cc: linux-scsi@vger.kernel.org Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c index 4b55ab6..171fa7d 100644 --- a/drivers/usb/storage/alauda.c +++ b/drivers/usb/storage/alauda.c @@ -42,6 +42,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-alauda" MODULE_DESCRIPTION("Driver for Alauda-based card readers"); MODULE_AUTHOR("Daniel Drake "); @@ -1232,6 +1235,8 @@ static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_FAILED; } +static struct scsi_host_template alauda_host_template; + static int alauda_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1239,7 +1244,8 @@ static int alauda_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - alauda_usb_ids) + alauda_unusual_dev_list); + (id - alauda_usb_ids) + alauda_unusual_dev_list, + &alauda_host_template); if (result) return result; @@ -1253,7 +1259,7 @@ static int alauda_probe(struct usb_interface *intf, } static struct usb_driver alauda_driver = { - .name = "ums-alauda", + .name = DRV_NAME, .probe = alauda_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -1266,4 +1272,4 @@ static struct usb_driver alauda_driver = { .no_dynamic_id = 1, }; -module_usb_driver(alauda_driver); +module_usb_stor_driver(alauda_driver, alauda_host_template, DRV_NAME); diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c index b3466d1..c80d3de 100644 --- a/drivers/usb/storage/cypress_atacb.c +++ b/drivers/usb/storage/cypress_atacb.c @@ -30,6 +30,8 @@ #include "scsiglue.h" #include "debug.h" +#define DRV_NAME "ums-cypress" + MODULE_DESCRIPTION("SAT support for Cypress USB/ATA bridges with ATACB"); MODULE_AUTHOR("Matthieu Castet "); MODULE_LICENSE("GPL"); @@ -241,6 +243,7 @@ end: srb->cmd_len = 12; } +static struct scsi_host_template cypress_host_template; static int cypress_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -250,7 +253,8 @@ static int cypress_probe(struct usb_interface *intf, struct usb_device *device; result = usb_stor_probe1(&us, intf, id, - (id - cypress_usb_ids) + cypress_unusual_dev_list); + (id - cypress_usb_ids) + cypress_unusual_dev_list, + &cypress_host_template); if (result) return result; @@ -273,7 +277,7 @@ static int cypress_probe(struct usb_interface *intf, } static struct usb_driver cypress_driver = { - .name = "ums-cypress", + .name = DRV_NAME, .probe = cypress_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -286,4 +290,4 @@ static struct usb_driver cypress_driver = { .no_dynamic_id = 1, }; -module_usb_driver(cypress_driver); +module_usb_stor_driver(cypress_driver, cypress_host_template, DRV_NAME); diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 7b17c21..aa4f519 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -59,6 +59,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-datafab" MODULE_DESCRIPTION("Driver for Datafab USB Compact Flash reader"); MODULE_AUTHOR("Jimmie Mayfield "); @@ -721,6 +724,8 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_FAILED; } +static struct scsi_host_template datafab_host_template; + static int datafab_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -728,7 +733,8 @@ static int datafab_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - datafab_usb_ids) + datafab_unusual_dev_list); + (id - datafab_usb_ids) + datafab_unusual_dev_list, + &datafab_host_template); if (result) return result; @@ -742,7 +748,7 @@ static int datafab_probe(struct usb_interface *intf, } static struct usb_driver datafab_driver = { - .name = "ums-datafab", + .name = DRV_NAME, .probe = datafab_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -755,4 +761,4 @@ static struct usb_driver datafab_driver = { .no_dynamic_id = 1, }; -module_usb_driver(datafab_driver); +module_usb_stor_driver(datafab_driver, datafab_host_template, DRV_NAME); diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 56f782b..f3cf4ce 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -28,6 +28,7 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" #define SD_INIT1_FIRMWARE "ene-ub6250/sd_init1.bin" #define SD_INIT2_FIRMWARE "ene-ub6250/sd_init2.bin" @@ -36,6 +37,8 @@ #define MSP_RW_FIRMWARE "ene-ub6250/msp_rdwr.bin" #define MS_RW_FIRMWARE "ene-ub6250/ms_rdwr.bin" +#define DRV_NAME "ums_eneub6250" + MODULE_DESCRIPTION("Driver for ENE UB6250 reader"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(SD_INIT1_FIRMWARE); @@ -2307,6 +2310,7 @@ static int ene_transport(struct scsi_cmnd *srb, struct us_data *us) return 0; } +static struct scsi_host_template ene_ub6250_host_template; static int ene_ub6250_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -2316,7 +2320,8 @@ static int ene_ub6250_probe(struct usb_interface *intf, struct us_data *us; result = usb_stor_probe1(&us, intf, id, - (id - ene_ub6250_usb_ids) + ene_ub6250_unusual_dev_list); + (id - ene_ub6250_usb_ids) + ene_ub6250_unusual_dev_list, + &ene_ub6250_host_template); if (result) return result; @@ -2404,7 +2409,7 @@ static int ene_ub6250_reset_resume(struct usb_interface *iface) #endif static struct usb_driver ene_ub6250_driver = { - .name = "ums_eneub6250", + .name = DRV_NAME, .probe = ene_ub6250_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -2417,4 +2422,4 @@ static struct usb_driver ene_ub6250_driver = { .no_dynamic_id = 1, }; -module_usb_driver(ene_ub6250_driver); +module_usb_stor_driver(ene_ub6250_driver, ene_ub6250_host_template, DRV_NAME); diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index ef16068..3f2b089 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -34,6 +34,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-freecom" MODULE_DESCRIPTION("Driver for Freecom USB/IDE adaptor"); MODULE_AUTHOR("David Brown "); @@ -523,6 +526,8 @@ static void pdump(struct us_data *us, void *ibuffer, int length) } #endif +static struct scsi_host_template freecom_host_template; + static int freecom_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -530,7 +535,8 @@ static int freecom_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - freecom_usb_ids) + freecom_unusual_dev_list); + (id - freecom_usb_ids) + freecom_unusual_dev_list, + &freecom_host_template); if (result) return result; @@ -544,7 +550,7 @@ static int freecom_probe(struct usb_interface *intf, } static struct usb_driver freecom_driver = { - .name = "ums-freecom", + .name = DRV_NAME, .probe = freecom_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -557,4 +563,4 @@ static struct usb_driver freecom_driver = { .no_dynamic_id = 1, }; -module_usb_driver(freecom_driver); +module_usb_stor_driver(freecom_driver, freecom_host_template, DRV_NAME); diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 0761786..1bac215 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -60,6 +60,8 @@ #include "debug.h" #include "scsiglue.h" +#define DRV_NAME "ums-isd200" + MODULE_DESCRIPTION("Driver for In-System Design, Inc. ISD200 ASIC"); MODULE_AUTHOR("Björn Stenberg "); MODULE_LICENSE("GPL"); @@ -1537,6 +1539,8 @@ static void isd200_ata_command(struct scsi_cmnd *srb, struct us_data *us) isd200_srb_set_bufflen(srb, orig_bufflen); } +static struct scsi_host_template isd200_host_template; + static int isd200_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1544,7 +1548,8 @@ static int isd200_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - isd200_usb_ids) + isd200_unusual_dev_list); + (id - isd200_usb_ids) + isd200_unusual_dev_list, + &isd200_host_template); if (result) return result; @@ -1556,7 +1561,7 @@ static int isd200_probe(struct usb_interface *intf, } static struct usb_driver isd200_driver = { - .name = "ums-isd200", + .name = DRV_NAME, .probe = isd200_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -1569,4 +1574,4 @@ static struct usb_driver isd200_driver = { .no_dynamic_id = 1, }; -module_usb_driver(isd200_driver); +module_usb_stor_driver(isd200_driver, isd200_host_template, DRV_NAME); diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index 563078b..ee613e2 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -56,7 +56,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" +#define DRV_NAME "ums-jumpshot" MODULE_DESCRIPTION("Driver for Lexar \"Jumpshot\" Compact Flash reader"); MODULE_AUTHOR("Jimmie Mayfield "); @@ -647,6 +649,8 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_FAILED; } +static struct scsi_host_template jumpshot_host_template; + static int jumpshot_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -654,7 +658,8 @@ static int jumpshot_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - jumpshot_usb_ids) + jumpshot_unusual_dev_list); + (id - jumpshot_usb_ids) + jumpshot_unusual_dev_list, + &jumpshot_host_template); if (result) return result; @@ -668,7 +673,7 @@ static int jumpshot_probe(struct usb_interface *intf, } static struct usb_driver jumpshot_driver = { - .name = "ums-jumpshot", + .name = DRV_NAME, .probe = jumpshot_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -681,4 +686,4 @@ static struct usb_driver jumpshot_driver = { .no_dynamic_id = 1, }; -module_usb_driver(jumpshot_driver); +module_usb_stor_driver(jumpshot_driver, jumpshot_host_template, DRV_NAME); diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c index 94d16ee..ae201e6 100644 --- a/drivers/usb/storage/karma.c +++ b/drivers/usb/storage/karma.c @@ -28,6 +28,9 @@ #include "usb.h" #include "transport.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-karma" MODULE_DESCRIPTION("Driver for Rio Karma"); MODULE_AUTHOR("Bob Copeland , Keith Bennett "); @@ -200,6 +203,8 @@ out: return ret; } +static struct scsi_host_template karma_host_template; + static int karma_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -207,7 +212,8 @@ static int karma_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - karma_usb_ids) + karma_unusual_dev_list); + (id - karma_usb_ids) + karma_unusual_dev_list, + &karma_host_template); if (result) return result; @@ -220,7 +226,7 @@ static int karma_probe(struct usb_interface *intf, } static struct usb_driver karma_driver = { - .name = "ums-karma", + .name = DRV_NAME, .probe = karma_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -233,4 +239,4 @@ static struct usb_driver karma_driver = { .no_dynamic_id = 1, }; -module_usb_driver(karma_driver); +module_usb_stor_driver(karma_driver, karma_host_template, DRV_NAME); diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index 74e2aa2..acc3d03 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -35,6 +35,9 @@ #include #include "usb.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-onetouch" MODULE_DESCRIPTION("Maxtor USB OneTouch hard drive button driver"); MODULE_AUTHOR("Nick Sillik "); @@ -283,6 +286,8 @@ static void onetouch_release_input(void *onetouch_) } } +static struct scsi_host_template onetouch_host_template; + static int onetouch_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -290,7 +295,8 @@ static int onetouch_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - onetouch_usb_ids) + onetouch_unusual_dev_list); + (id - onetouch_usb_ids) + onetouch_unusual_dev_list, + &onetouch_host_template); if (result) return result; @@ -301,7 +307,7 @@ static int onetouch_probe(struct usb_interface *intf, } static struct usb_driver onetouch_driver = { - .name = "ums-onetouch", + .name = DRV_NAME, .probe = onetouch_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -314,4 +320,4 @@ static struct usb_driver onetouch_driver = { .no_dynamic_id = 1, }; -module_usb_driver(onetouch_driver); +module_usb_stor_driver(onetouch_driver, onetouch_host_template, DRV_NAME); diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 27e4a58..2043356 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -39,6 +39,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-realtek" MODULE_DESCRIPTION("Driver for Realtek USB Card Reader"); MODULE_AUTHOR("wwang "); @@ -1034,6 +1037,8 @@ INIT_FAIL: return -EIO; } +static struct scsi_host_template realtek_cr_host_template; + static int realtek_cr_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1044,7 +1049,8 @@ static int realtek_cr_probe(struct usb_interface *intf, result = usb_stor_probe1(&us, intf, id, (id - realtek_cr_ids) + - realtek_cr_unusual_dev_list); + realtek_cr_unusual_dev_list, + &realtek_cr_host_template); if (result) return result; @@ -1054,7 +1060,7 @@ static int realtek_cr_probe(struct usb_interface *intf, } static struct usb_driver realtek_cr_driver = { - .name = "ums-realtek", + .name = DRV_NAME, .probe = realtek_cr_probe, .disconnect = usb_stor_disconnect, /* .suspend = usb_stor_suspend, */ @@ -1070,4 +1076,4 @@ static struct usb_driver realtek_cr_driver = { .no_dynamic_id = 1, }; -module_usb_driver(realtek_cr_driver); +module_usb_stor_driver(realtek_cr_driver, realtek_cr_host_template, DRV_NAME); diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index a8cbbff..c922d8b 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -536,7 +536,7 @@ static struct device_attribute *sysfs_device_attr_list[] = { * this defines our host template, with which we'll allocate hosts */ -struct scsi_host_template usb_stor_host_template = { +static const struct scsi_host_template usb_stor_host_template = { /* basic userland interface stuff */ .name = "usb-storage", .proc_name = "usb-storage", @@ -588,6 +588,16 @@ struct scsi_host_template usb_stor_host_template = { .module = THIS_MODULE }; +void usb_stor_host_template_init(struct scsi_host_template *sht, + const char *name, struct module *owner) +{ + *sht = usb_stor_host_template; + sht->name = name; + sht->proc_name = name; + sht->module = owner; +} +EXPORT_SYMBOL_GPL(usb_stor_host_template_init); + /* To Report "Illegal Request: Invalid Field in CDB */ unsigned char usb_stor_sense_invalidCDB[18] = { [0] = 0x70, /* current error */ diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h index ffa1cca..5494d87 100644 --- a/drivers/usb/storage/scsiglue.h +++ b/drivers/usb/storage/scsiglue.h @@ -41,8 +41,9 @@ extern void usb_stor_report_device_reset(struct us_data *us); extern void usb_stor_report_bus_reset(struct us_data *us); +extern void usb_stor_host_template_init(struct scsi_host_template *sht, + const char *name, struct module *owner); extern unsigned char usb_stor_sense_invalidCDB[18]; -extern struct scsi_host_template usb_stor_host_template; #endif diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 3847053..b746036 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -52,6 +52,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-sddr09" MODULE_DESCRIPTION("Driver for SanDisk SDDR-09 SmartMedia reader"); MODULE_AUTHOR("Andries Brouwer , Robert Baruch "); @@ -1738,6 +1741,8 @@ usb_stor_sddr09_init(struct us_data *us) { return sddr09_common_init(us); } +static struct scsi_host_template sddr09_host_template; + static int sddr09_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1745,7 +1750,8 @@ static int sddr09_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - sddr09_usb_ids) + sddr09_unusual_dev_list); + (id - sddr09_usb_ids) + sddr09_unusual_dev_list, + &sddr09_host_template); if (result) return result; @@ -1766,7 +1772,7 @@ static int sddr09_probe(struct usb_interface *intf, } static struct usb_driver sddr09_driver = { - .name = "ums-sddr09", + .name = DRV_NAME, .probe = sddr09_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -1779,4 +1785,4 @@ static struct usb_driver sddr09_driver = { .no_dynamic_id = 1, }; -module_usb_driver(sddr09_driver); +module_usb_stor_driver(sddr09_driver, sddr09_host_template, DRV_NAME); diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index aacedef..e5e0a25 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -34,6 +34,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-sddr55" MODULE_DESCRIPTION("Driver for SanDisk SDDR-55 SmartMedia reader"); MODULE_AUTHOR("Simon Munton"); @@ -968,6 +971,7 @@ static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_FAILED; // FIXME: sense buffer? } +static struct scsi_host_template sddr55_host_template; static int sddr55_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -976,7 +980,8 @@ static int sddr55_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - sddr55_usb_ids) + sddr55_unusual_dev_list); + (id - sddr55_usb_ids) + sddr55_unusual_dev_list, + &sddr55_host_template); if (result) return result; @@ -990,7 +995,7 @@ static int sddr55_probe(struct usb_interface *intf, } static struct usb_driver sddr55_driver = { - .name = "ums-sddr55", + .name = DRV_NAME, .probe = sddr55_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -1003,4 +1008,4 @@ static struct usb_driver sddr55_driver = { .no_dynamic_id = 1, }; -module_usb_driver(sddr55_driver); +module_usb_stor_driver(sddr55_driver, sddr55_host_template, DRV_NAME); diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index 008d805..a3ec86b 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -53,6 +53,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-usbat" MODULE_DESCRIPTION("Driver for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable"); MODULE_AUTHOR("Daniel Drake , Robert Baruch "); @@ -1834,6 +1837,8 @@ static int init_usbat_flash(struct us_data *us) return init_usbat(us, USBAT_DEV_FLASH); } +static struct scsi_host_template usbat_host_template; + static int usbat_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1841,7 +1846,8 @@ static int usbat_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - usbat_usb_ids) + usbat_unusual_dev_list); + (id - usbat_usb_ids) + usbat_unusual_dev_list, + &usbat_host_template); if (result) return result; @@ -1858,7 +1864,7 @@ static int usbat_probe(struct usb_interface *intf, } static struct usb_driver usbat_driver = { - .name = "ums-usbat", + .name = DRV_NAME, .probe = usbat_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -1871,4 +1877,4 @@ static struct usb_driver usbat_driver = { .no_dynamic_id = 1, }; -module_usb_driver(usbat_driver); +module_usb_stor_driver(usbat_driver, usbat_host_template, DRV_NAME); diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 6c10c88..43576ed 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -76,6 +76,8 @@ #include "uas-detect.h" #endif +#define DRV_NAME "usb-storage" + /* Some informational data */ MODULE_AUTHOR("Matthew Dharm "); MODULE_DESCRIPTION("USB Mass Storage driver for Linux"); @@ -924,7 +926,8 @@ static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf) int usb_stor_probe1(struct us_data **pus, struct usb_interface *intf, const struct usb_device_id *id, - struct us_unusual_dev *unusual_dev) + struct us_unusual_dev *unusual_dev, + struct scsi_host_template *sht) { struct Scsi_Host *host; struct us_data *us; @@ -936,7 +939,7 @@ int usb_stor_probe1(struct us_data **pus, * Ask the SCSI layer to allocate a host structure, with extra * space at the end for our private us_data structure. */ - host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us)); + host = scsi_host_alloc(sht, sizeof(*us)); if (!host) { dev_warn(&intf->dev, "Unable to allocate the scsi host\n"); return -ENOMEM; @@ -1073,6 +1076,8 @@ void usb_stor_disconnect(struct usb_interface *intf) } EXPORT_SYMBOL_GPL(usb_stor_disconnect); +static struct scsi_host_template usb_stor_host_template; + /* The main probe routine for standard devices */ static int storage_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -1113,7 +1118,8 @@ static int storage_probe(struct usb_interface *intf, id->idVendor, id->idProduct); } - result = usb_stor_probe1(&us, intf, id, unusual_dev); + result = usb_stor_probe1(&us, intf, id, unusual_dev, + &usb_stor_host_template); if (result) return result; @@ -1124,7 +1130,7 @@ static int storage_probe(struct usb_interface *intf, } static struct usb_driver usb_storage_driver = { - .name = "usb-storage", + .name = DRV_NAME, .probe = storage_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -1137,4 +1143,4 @@ static struct usb_driver usb_storage_driver = { .soft_unbind = 1, }; -module_usb_driver(usb_storage_driver); +module_usb_stor_driver(usb_storage_driver, usb_stor_host_template, DRV_NAME); diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 307e339..da0ad32 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -197,11 +197,25 @@ extern int usb_stor_post_reset(struct usb_interface *iface); extern int usb_stor_probe1(struct us_data **pus, struct usb_interface *intf, const struct usb_device_id *id, - struct us_unusual_dev *unusual_dev); + struct us_unusual_dev *unusual_dev, + struct scsi_host_template *sht); extern int usb_stor_probe2(struct us_data *us); extern void usb_stor_disconnect(struct usb_interface *intf); extern void usb_stor_adjust_quirks(struct usb_device *dev, unsigned long *fflags); +#define module_usb_stor_driver(__driver, __sht, __name) \ +static int __init __driver##_init(void) \ +{ \ + usb_stor_host_template_init(&(__sht), __name, THIS_MODULE); \ + return usb_register(&(__driver)); \ +} \ +module_init(__driver##_init); \ +static void __exit __driver##_exit(void) \ +{ \ + usb_deregister(&(__driver)); \ +} \ +module_exit(__driver##_exit) + #endif -- cgit v0.10.2 From 6be109b31ccdb9c98e7be12687171f6602527a5d Mon Sep 17 00:00:00 2001 From: Arun Ramamurthy Date: Wed, 22 Apr 2015 16:04:11 -0700 Subject: phy: core: Add devm_of_phy_get_by_index to phy-core Some generic drivers, such as ehci, may use multiple phys and for such drivers referencing phy(s) by name(s) does not make sense. Instead of inventing new naming schemes and using custom code to iterate through them, such drivers are better of using nameless phy bindings and using this newly introduced API to iterate through them. Signed-off-by: Arun Ramamurthy Reviewed-by: Ray Jui Reviewed-by: Scott Branden [kishon@ti.com: fix compilation errors] Signed-off-by: Kishon Vijay Abraham I diff --git a/Documentation/phy.txt b/Documentation/phy.txt index 371361c..b388c5a 100644 --- a/Documentation/phy.txt +++ b/Documentation/phy.txt @@ -76,6 +76,8 @@ struct phy *phy_get(struct device *dev, const char *string); struct phy *phy_optional_get(struct device *dev, const char *string); struct phy *devm_phy_get(struct device *dev, const char *string); struct phy *devm_phy_optional_get(struct device *dev, const char *string); +struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np, + int index); phy_get, phy_optional_get, devm_phy_get and devm_phy_optional_get can be used to get the PHY. In the case of dt boot, the string arguments @@ -86,7 +88,10 @@ successful PHY get. On driver detach, release function is invoked on the the devres data and devres data is freed. phy_optional_get and devm_phy_optional_get should be used when the phy is optional. These two functions will never return -ENODEV, but instead returns NULL when -the phy cannot be found. +the phy cannot be found.Some generic drivers, such as ehci, may use multiple +phys and for such drivers referencing phy(s) by name(s) does not make sense. In +this case, devm_of_phy_get_by_index can be used to get a phy reference based on +the index. It should be noted that NULL is a valid phy reference. All phy consumer calls on the NULL phy become NOPs. That is the release calls, diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 3791838..964a84d 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -623,6 +623,38 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np, EXPORT_SYMBOL_GPL(devm_of_phy_get); /** + * devm_of_phy_get_by_index() - lookup and obtain a reference to a phy by index. + * @dev: device that requests this phy + * @np: node containing the phy + * @index: index of the phy + * + * Gets the phy using _of_phy_get(), and associates a device with it using + * devres. On driver detach, release function is invoked on the devres data, + * then, devres data is freed. + * + */ +struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np, + int index) +{ + struct phy **ptr, *phy; + + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + phy = _of_phy_get(np, index); + if (!IS_ERR(phy)) { + *ptr = phy; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return phy; +} +EXPORT_SYMBOL_GPL(devm_of_phy_get_by_index); + +/** * phy_create() - create a new phy * @dev: device that is creating the new phy * @node: device node of the phy diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index a0197fa..8cf05e3 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -133,6 +133,8 @@ struct phy *devm_phy_get(struct device *dev, const char *string); struct phy *devm_phy_optional_get(struct device *dev, const char *string); struct phy *devm_of_phy_get(struct device *dev, struct device_node *np, const char *con_id); +struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np, + int index); void phy_put(struct phy *phy); void devm_phy_put(struct device *dev, struct phy *phy); struct phy *of_phy_get(struct device_node *np, const char *con_id); @@ -261,6 +263,13 @@ static inline struct phy *devm_of_phy_get(struct device *dev, return ERR_PTR(-ENOSYS); } +static inline struct phy *devm_of_phy_get_by_index(struct device *dev, + struct device_node *np, + int index) +{ + return ERR_PTR(-ENOSYS); +} + static inline void phy_put(struct phy *phy) { } -- cgit v0.10.2 From 216e299269c1ec16a241eaba3620b9c6e65385eb Mon Sep 17 00:00:00 2001 From: Arun Ramamurthy Date: Wed, 22 Apr 2015 16:04:12 -0700 Subject: usb: ehci-platform: Use devm_of_phy_get_by_index Getting phys by index instead of phy names so that we do not have to create a naming scheme when multiple phys are present Signed-off-by: Arun Ramamurthy Reviewed-by: Ray Jui Reviewed-by: Scott Branden Acked-by: Alan Stern Signed-off-by: Kishon Vijay Abraham I diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index d8a75a5..145bf19 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -88,15 +88,13 @@ static int ehci_platform_power_on(struct platform_device *dev) } for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - if (priv->phys[phy_num]) { - ret = phy_init(priv->phys[phy_num]); - if (ret) - goto err_exit_phy; - ret = phy_power_on(priv->phys[phy_num]); - if (ret) { - phy_exit(priv->phys[phy_num]); - goto err_exit_phy; - } + ret = phy_init(priv->phys[phy_num]); + if (ret) + goto err_exit_phy; + ret = phy_power_on(priv->phys[phy_num]); + if (ret) { + phy_exit(priv->phys[phy_num]); + goto err_exit_phy; } } @@ -104,10 +102,8 @@ static int ehci_platform_power_on(struct platform_device *dev) err_exit_phy: while (--phy_num >= 0) { - if (priv->phys[phy_num]) { - phy_power_off(priv->phys[phy_num]); - phy_exit(priv->phys[phy_num]); - } + phy_power_off(priv->phys[phy_num]); + phy_exit(priv->phys[phy_num]); } err_disable_clks: while (--clk >= 0) @@ -123,10 +119,8 @@ static void ehci_platform_power_off(struct platform_device *dev) int clk, phy_num; for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - if (priv->phys[phy_num]) { - phy_power_off(priv->phys[phy_num]); - phy_exit(priv->phys[phy_num]); - } + phy_power_off(priv->phys[phy_num]); + phy_exit(priv->phys[phy_num]); } for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) @@ -154,7 +148,6 @@ static int ehci_platform_probe(struct platform_device *dev) struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); struct ehci_platform_priv *priv; struct ehci_hcd *ehci; - const char *phy_name; int err, irq, phy_num, clk = 0; if (usb_disabled()) @@ -204,36 +197,22 @@ static int ehci_platform_probe(struct platform_device *dev) priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, "phys", "#phy-cells"); - priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1; - priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, - sizeof(struct phy *), GFP_KERNEL); - if (!priv->phys) - return -ENOMEM; + if (priv->num_phys > 0) { + priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, + sizeof(struct phy *), GFP_KERNEL); + if (!priv->phys) + return -ENOMEM; + } else + priv->num_phys = 0; for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - err = of_property_read_string_index( - dev->dev.of_node, - "phy-names", phy_num, - &phy_name); - - if (err < 0) { - if (priv->num_phys > 1) { - dev_err(&dev->dev, "phy-names not provided"); - goto err_put_hcd; - } else - phy_name = "usb"; - } - - priv->phys[phy_num] = devm_phy_get(&dev->dev, - phy_name); - if (IS_ERR(priv->phys[phy_num])) { - err = PTR_ERR(priv->phys[phy_num]); - if ((priv->num_phys > 1) || - (err == -EPROBE_DEFER)) - goto err_put_hcd; - priv->phys[phy_num] = NULL; - } + priv->phys[phy_num] = devm_of_phy_get_by_index( + &dev->dev, dev->dev.of_node, phy_num); + if (IS_ERR(priv->phys[phy_num])) { + err = PTR_ERR(priv->phys[phy_num]); + goto err_put_hcd; + } } for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { -- cgit v0.10.2 From a666f7d097486bb74f9b02d6d049c1e5a765fa61 Mon Sep 17 00:00:00 2001 From: Arun Ramamurthy Date: Wed, 22 Apr 2015 16:04:13 -0700 Subject: usb: ohci-platform: Use devm_of_phy_get_by_index Getting phys by index instead of phy names so that we do not have to create a naming scheme when multiple phys are present Signed-off-by: Arun Ramamurthy Reviewed-by: Ray Jui Reviewed-by: Scott Branden Acked-by: Alan Stern Signed-off-by: Kishon Vijay Abraham I diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 185ceee..c2669f18 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -57,15 +57,13 @@ static int ohci_platform_power_on(struct platform_device *dev) } for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - if (priv->phys[phy_num]) { - ret = phy_init(priv->phys[phy_num]); - if (ret) - goto err_exit_phy; - ret = phy_power_on(priv->phys[phy_num]); - if (ret) { - phy_exit(priv->phys[phy_num]); - goto err_exit_phy; - } + ret = phy_init(priv->phys[phy_num]); + if (ret) + goto err_exit_phy; + ret = phy_power_on(priv->phys[phy_num]); + if (ret) { + phy_exit(priv->phys[phy_num]); + goto err_exit_phy; } } @@ -73,10 +71,8 @@ static int ohci_platform_power_on(struct platform_device *dev) err_exit_phy: while (--phy_num >= 0) { - if (priv->phys[phy_num]) { - phy_power_off(priv->phys[phy_num]); - phy_exit(priv->phys[phy_num]); - } + phy_power_off(priv->phys[phy_num]); + phy_exit(priv->phys[phy_num]); } err_disable_clks: while (--clk >= 0) @@ -92,10 +88,8 @@ static void ohci_platform_power_off(struct platform_device *dev) int clk, phy_num; for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - if (priv->phys[phy_num]) { - phy_power_off(priv->phys[phy_num]); - phy_exit(priv->phys[phy_num]); - } + phy_power_off(priv->phys[phy_num]); + phy_exit(priv->phys[phy_num]); } for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) @@ -123,7 +117,6 @@ static int ohci_platform_probe(struct platform_device *dev) struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); struct ohci_platform_priv *priv; struct ohci_hcd *ohci; - const char *phy_name; int err, irq, phy_num, clk = 0; if (usb_disabled()) @@ -174,36 +167,22 @@ static int ohci_platform_probe(struct platform_device *dev) priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, "phys", "#phy-cells"); - priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1; - priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, - sizeof(struct phy *), GFP_KERNEL); - if (!priv->phys) - return -ENOMEM; + if (priv->num_phys > 0) { + priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, + sizeof(struct phy *), GFP_KERNEL); + if (!priv->phys) + return -ENOMEM; + } else + priv->num_phys = 0; for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - err = of_property_read_string_index( - dev->dev.of_node, - "phy-names", phy_num, - &phy_name); - - if (err < 0) { - if (priv->num_phys > 1) { - dev_err(&dev->dev, "phy-names not provided"); - goto err_put_hcd; - } else - phy_name = "usb"; - } - - priv->phys[phy_num] = devm_phy_get(&dev->dev, - phy_name); - if (IS_ERR(priv->phys[phy_num])) { - err = PTR_ERR(priv->phys[phy_num]); - if ((priv->num_phys > 1) || - (err == -EPROBE_DEFER)) - goto err_put_hcd; - priv->phys[phy_num] = NULL; - } + priv->phys[phy_num] = devm_of_phy_get_by_index( + &dev->dev, dev->dev.of_node, phy_num); + if (IS_ERR(priv->phys[phy_num])) { + err = PTR_ERR(priv->phys[phy_num]); + goto err_put_hcd; + } } for (clk = 0; clk < OHCI_MAX_CLKS; clk++) { -- cgit v0.10.2 From 87006dd600e36e05a2bd978c112a0aa883713c00 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 22 Apr 2015 16:14:37 -0700 Subject: phy: phy-core: allow specifying supply at port level Multi-port phys may have per port power supplies. Let's change phy core to look for supply at the port level when multiple ports are specified. To keep compatibility with the existing device tree board descriptions for single-port phys we will continue looking up the power supply at the parent node level Signed-off-by: Dmitry Torokhov Signed-off-by: Arun Ramamurthy Reviewed-by: Ray Jui Reviewed-by: Scott Branden Signed-off-by: Kishon Vijay Abraham I diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 964a84d..5f729bb 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -683,16 +683,6 @@ struct phy *phy_create(struct device *dev, struct device_node *node, goto free_phy; } - /* phy-supply */ - phy->pwr = regulator_get_optional(dev, "phy"); - if (IS_ERR(phy->pwr)) { - if (PTR_ERR(phy->pwr) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto free_ida; - } - phy->pwr = NULL; - } - device_initialize(&phy->dev); mutex_init(&phy->mutex); @@ -706,6 +696,16 @@ struct phy *phy_create(struct device *dev, struct device_node *node, if (ret) goto put_dev; + /* phy-supply */ + phy->pwr = regulator_get_optional(&phy->dev, "phy"); + if (IS_ERR(phy->pwr)) { + ret = PTR_ERR(phy->pwr); + if (ret == -EPROBE_DEFER) + goto put_dev; + + phy->pwr = NULL; + } + ret = device_add(&phy->dev); if (ret) goto put_dev; @@ -721,9 +721,6 @@ put_dev: put_device(&phy->dev); /* calls phy_release() which frees resources */ return ERR_PTR(ret); -free_ida: - ida_simple_remove(&phy_ida, phy->id); - free_phy: kfree(phy); return ERR_PTR(ret); -- cgit v0.10.2 From 99db11c6e8a89b236b283df0ec592aecdd00d8ef Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 8 May 2015 11:30:48 -0500 Subject: phy: miphy28lp: fix sparse warnings Add missing __iomem annotation to the base address so Sparse doesn't complain. Signed-off-by: Felipe Balbi Acked-by: Maxime Coquelin Signed-off-by: Kishon Vijay Abraham I diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c index c4cc11d..58d5339 100644 --- a/drivers/phy/phy-miphy28lp.c +++ b/drivers/phy/phy-miphy28lp.c @@ -367,7 +367,7 @@ static struct miphy28lp_pll_gen pcie_pll_gen[] = { static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy) { - void *base = miphy_phy->base; + void __iomem *base = miphy_phy->base; u8 val; /* Putting Macro in reset */ @@ -391,7 +391,7 @@ static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy) static inline void miphy28lp_pll_calibration(struct miphy28lp_phy *miphy_phy, struct pll_ratio *pll_ratio) { - void *base = miphy_phy->base; + void __iomem *base = miphy_phy->base; u8 val; /* Applying PLL Settings */ -- cgit v0.10.2 From 5bea496820cdcb0f6711741ba663ccfe78ac4dbf Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 8 May 2015 11:30:49 -0500 Subject: phy: miphy365x: fix sparse warnings Add missing 'static' modifier so sparse won't complain anymore. Signed-off-by: Felipe Balbi Acked-by: Maxime Coquelin Signed-off-by: Kishon Vijay Abraham I diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c index 019c2d7..ba6993d 100644 --- a/drivers/phy/phy-miphy365x.c +++ b/drivers/phy/phy-miphy365x.c @@ -441,8 +441,8 @@ static int miphy365x_init(struct phy *phy) return ret; } -int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy, - int index) +static int miphy365x_get_addr(struct device *dev, + struct miphy365x_phy *miphy_phy, int index) { struct device_node *phynode = miphy_phy->phy->dev.of_node; const char *name; -- cgit v0.10.2 From 33f434d283a27116fb358ae5bc3b42967c12f85a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 7 Apr 2015 12:23:38 +0800 Subject: phy: core: Check requested PHY status in _of_phy_get() This is a common checking in various drivers, so move the checking to _of_phy_get(). Signed-off-by: Axel Lin Signed-off-by: Kishon Vijay Abraham I diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 5f729bb..7d535db 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -367,13 +367,21 @@ static struct phy *_of_phy_get(struct device_node *np, int index) phy_provider = of_phy_provider_lookup(args.np); if (IS_ERR(phy_provider) || !try_module_get(phy_provider->owner)) { phy = ERR_PTR(-EPROBE_DEFER); - goto err0; + goto out_unlock; + } + + if (!of_device_is_available(args.np)) { + dev_warn(phy_provider->dev, "Requested PHY is disabled\n"); + phy = ERR_PTR(-ENODEV); + goto out_put_module; } phy = phy_provider->of_xlate(phy_provider->dev, &args); + +out_put_module: module_put(phy_provider->owner); -err0: +out_unlock: mutex_unlock(&phy_provider_mutex); of_node_put(args.np); diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c index 58d5339..5e257ef 100644 --- a/drivers/phy/phy-miphy28lp.c +++ b/drivers/phy/phy-miphy28lp.c @@ -1107,11 +1107,6 @@ static struct phy *miphy28lp_xlate(struct device *dev, struct device_node *phynode = args->np; int ret, index = 0; - if (!of_device_is_available(phynode)) { - dev_warn(dev, "Requested PHY is disabled\n"); - return ERR_PTR(-ENODEV); - } - if (args->args_count != 1) { dev_err(dev, "Invalid number of cells in 'phy' property\n"); return ERR_PTR(-EINVAL); diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c index ba6993d..0ff354d 100644 --- a/drivers/phy/phy-miphy365x.c +++ b/drivers/phy/phy-miphy365x.c @@ -476,11 +476,6 @@ static struct phy *miphy365x_xlate(struct device *dev, struct device_node *phynode = args->np; int ret, index; - if (!of_device_is_available(phynode)) { - dev_warn(dev, "Requested PHY is disabled\n"); - return ERR_PTR(-ENODEV); - } - if (args->args_count != 1) { dev_err(dev, "Invalid number of cells in 'phy' property\n"); return ERR_PTR(-EINVAL); diff --git a/drivers/phy/phy-rcar-gen2.c b/drivers/phy/phy-rcar-gen2.c index 778276a..f47bfd8 100644 --- a/drivers/phy/phy-rcar-gen2.c +++ b/drivers/phy/phy-rcar-gen2.c @@ -206,11 +206,6 @@ static struct phy *rcar_gen2_phy_xlate(struct device *dev, struct device_node *np = args->np; int i; - if (!of_device_is_available(np)) { - dev_warn(dev, "Requested PHY is disabled\n"); - return ERR_PTR(-ENODEV); - } - drv = dev_get_drvdata(dev); if (!drv) return ERR_PTR(-EINVAL); -- cgit v0.10.2 From 3521a399dae8d66fc784cef70a78e65ce73e364f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 8 May 2015 13:30:12 -0500 Subject: usb: dwc2: hcd: fix build warning commit db62b9a804b4 (usb: dwc2: host: don't use dma_alloc_coherent with irqs disabled) introduced a build warning by using NULL as an integer. Fix that by just using 0 instead of NULL. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index d9b8cc3..b10377c 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -749,7 +749,7 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, chan->ep_is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) { dev_err(hsotg->dev, "can't map align_buf\n"); - chan->align_buf = (dma_addr_t)NULL; + chan->align_buf = 0; return -EINVAL; } -- cgit v0.10.2 From 289fcff4bcdb1dcc0ce8788b7ea0f58a9e4a495f Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 13 May 2015 15:26:42 +0300 Subject: usb: add bus type for USB ULPI UTMI+ Low Pin Interface (ULPI) is a commonly used PHY interface for USB 2.0. The ULPI specification describes a standard set of registers which the vendors can extend for their specific needs. ULPI PHYs provide often functions such as charger detection and ADP sensing and probing. There are two major issues that the bus type is meant to tackle: Firstly, ULPI registers are accessed from the controller. The bus provides convenient method for the controller drivers to share that access with the actual PHY drivers. Secondly, there are already platforms that assume ULPI PHYs are runtime detected, such as many Intel Baytrail based platforms. They do not provide any kind of hardware description for the ULPI PHYs like separate ACPI device object that could be used to enumerate a device from. Signed-off-by: Heikki Krogerus Acked-by: David Cohen Signed-off-by: Felipe Balbi diff --git a/MAINTAINERS b/MAINTAINERS index 2e5bbc0..2202e91 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10453,6 +10453,13 @@ S: Maintained F: Documentation/video4linux/zr364xx.txt F: drivers/media/usb/zr364xx/ +ULPI BUS +M: Heikki Krogerus +L: linux-usb@vger.kernel.org +S: Maintained +F: drivers/usb/common/ulpi.c +F: include/linux/ulpi/ + USER-MODE LINUX (UML) M: Jeff Dike M: Richard Weinberger diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile index ca2f8bd..6bbb3ec 100644 --- a/drivers/usb/common/Makefile +++ b/drivers/usb/common/Makefile @@ -7,3 +7,4 @@ usb-common-y += common.o usb-common-$(CONFIG_USB_LED_TRIG) += led.o obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o +obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c new file mode 100644 index 0000000..0e6f968 --- /dev/null +++ b/drivers/usb/common/ulpi.c @@ -0,0 +1,255 @@ +/** + * ulpi.c - USB ULPI PHY bus + * + * Copyright (C) 2015 Intel Corporation + * + * Author: Heikki Krogerus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +/* -------------------------------------------------------------------------- */ + +int ulpi_read(struct ulpi *ulpi, u8 addr) +{ + return ulpi->ops->read(ulpi->ops, addr); +} +EXPORT_SYMBOL_GPL(ulpi_read); + +int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val) +{ + return ulpi->ops->write(ulpi->ops, addr, val); +} +EXPORT_SYMBOL_GPL(ulpi_write); + +/* -------------------------------------------------------------------------- */ + +static int ulpi_match(struct device *dev, struct device_driver *driver) +{ + struct ulpi_driver *drv = to_ulpi_driver(driver); + struct ulpi *ulpi = to_ulpi_dev(dev); + const struct ulpi_device_id *id; + + for (id = drv->id_table; id->vendor; id++) + if (id->vendor == ulpi->id.vendor && + id->product == ulpi->id.product) + return 1; + + return 0; +} + +static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct ulpi *ulpi = to_ulpi_dev(dev); + + if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x", + ulpi->id.vendor, ulpi->id.product)) + return -ENOMEM; + return 0; +} + +static int ulpi_probe(struct device *dev) +{ + struct ulpi_driver *drv = to_ulpi_driver(dev->driver); + + return drv->probe(to_ulpi_dev(dev)); +} + +static int ulpi_remove(struct device *dev) +{ + struct ulpi_driver *drv = to_ulpi_driver(dev->driver); + + if (drv->remove) + drv->remove(to_ulpi_dev(dev)); + + return 0; +} + +static struct bus_type ulpi_bus = { + .name = "ulpi", + .match = ulpi_match, + .uevent = ulpi_uevent, + .probe = ulpi_probe, + .remove = ulpi_remove, +}; + +/* -------------------------------------------------------------------------- */ + +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ulpi *ulpi = to_ulpi_dev(dev); + + return sprintf(buf, "ulpi:v%04xp%04x\n", + ulpi->id.vendor, ulpi->id.product); +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *ulpi_dev_attrs[] = { + &dev_attr_modalias.attr, + NULL +}; + +static struct attribute_group ulpi_dev_attr_group = { + .attrs = ulpi_dev_attrs, +}; + +static const struct attribute_group *ulpi_dev_attr_groups[] = { + &ulpi_dev_attr_group, + NULL +}; + +static void ulpi_dev_release(struct device *dev) +{ + kfree(to_ulpi_dev(dev)); +} + +static struct device_type ulpi_dev_type = { + .name = "ulpi_device", + .groups = ulpi_dev_attr_groups, + .release = ulpi_dev_release, +}; + +/* -------------------------------------------------------------------------- */ + +/** + * ulpi_register_driver - register a driver with the ULPI bus + * @drv: driver being registered + * + * Registers a driver with the ULPI bus. + */ +int ulpi_register_driver(struct ulpi_driver *drv) +{ + if (!drv->probe) + return -EINVAL; + + drv->driver.bus = &ulpi_bus; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(ulpi_register_driver); + +/** + * ulpi_unregister_driver - unregister a driver with the ULPI bus + * @drv: driver to unregister + * + * Unregisters a driver with the ULPI bus. + */ +void ulpi_unregister_driver(struct ulpi_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(ulpi_unregister_driver); + +/* -------------------------------------------------------------------------- */ + +static int ulpi_register(struct device *dev, struct ulpi *ulpi) +{ + int ret; + + /* Test the interface */ + ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa); + if (ret < 0) + return ret; + + ret = ulpi_read(ulpi, ULPI_SCRATCH); + if (ret < 0) + return ret; + + if (ret != 0xaa) + return -ENODEV; + + ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW); + ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8; + + ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW); + ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8; + + ulpi->dev.parent = dev; + ulpi->dev.bus = &ulpi_bus; + ulpi->dev.type = &ulpi_dev_type; + dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev)); + + ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev)); + + request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product); + + ret = device_register(&ulpi->dev); + if (ret) + return ret; + + dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n", + ulpi->id.vendor, ulpi->id.product); + + return 0; +} + +/** + * ulpi_register_interface - instantiate new ULPI device + * @dev: USB controller's device interface + * @ops: ULPI register access + * + * Allocates and registers a ULPI device and an interface for it. Called from + * the USB controller that provides the ULPI interface. + */ +struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi_ops *ops) +{ + struct ulpi *ulpi; + int ret; + + ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL); + if (!ulpi) + return ERR_PTR(-ENOMEM); + + ulpi->ops = ops; + ops->dev = dev; + + ret = ulpi_register(dev, ulpi); + if (ret) { + kfree(ulpi); + return ERR_PTR(ret); + } + + return ulpi; +} +EXPORT_SYMBOL_GPL(ulpi_register_interface); + +/** + * ulpi_unregister_interface - unregister ULPI interface + * @intrf: struct ulpi_interface + * + * Unregisters a ULPI device and it's interface that was created with + * ulpi_create_interface(). + */ +void ulpi_unregister_interface(struct ulpi *ulpi) +{ + device_unregister(&ulpi->dev); +} +EXPORT_SYMBOL_GPL(ulpi_unregister_interface); + +/* -------------------------------------------------------------------------- */ + +static int __init ulpi_init(void) +{ + return bus_register(&ulpi_bus); +} +module_init(ulpi_init); + +static void __exit ulpi_exit(void) +{ + bus_unregister(&ulpi_bus); +} +module_exit(ulpi_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("USB ULPI PHY bus"); diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index cc0ced0..a99c89e 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -84,3 +84,23 @@ config USB_OTG_FSM Implements OTG Finite State Machine as specified in On-The-Go and Embedded Host Supplement to the USB Revision 2.0 Specification. +config USB_ULPI_BUS + tristate "USB ULPI PHY interface support" + depends on USB_SUPPORT + help + UTMI+ Low Pin Interface (ULPI) is specification for a commonly used + USB 2.0 PHY interface. The ULPI specification defines a standard set + of registers that can be used to detect the vendor and product which + allows ULPI to be handled as a bus. This module is the driver for that + bus. + + The ULPI interfaces (the buses) are registered by the drivers for USB + controllers which support ULPI register access and have ULPI PHY + attached to them. The ULPI PHY drivers themselves are normal PHY + drivers. + + ULPI PHYs provide often functions such as ADP sensing/probing (OTG + protocol) and USB charger detection. + + To compile this driver as a module, choose M here: the module will + be called ulpi. diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 3bfd567..7ab00d6 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -629,4 +629,10 @@ struct mcb_device_id { kernel_ulong_t driver_data; }; +struct ulpi_device_id { + __u16 vendor; + __u16 product; + kernel_ulong_t driver_data; +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/ulpi/driver.h b/include/linux/ulpi/driver.h new file mode 100644 index 0000000..388f6e0 --- /dev/null +++ b/include/linux/ulpi/driver.h @@ -0,0 +1,60 @@ +#ifndef __LINUX_ULPI_DRIVER_H +#define __LINUX_ULPI_DRIVER_H + +#include + +#include + +struct ulpi_ops; + +/** + * struct ulpi - describes ULPI PHY device + * @id: vendor and product ids for ULPI device + * @ops: I/O access + * @dev: device interface + */ +struct ulpi { + struct ulpi_device_id id; + struct ulpi_ops *ops; + struct device dev; +}; + +#define to_ulpi_dev(d) container_of(d, struct ulpi, dev) + +static inline void ulpi_set_drvdata(struct ulpi *ulpi, void *data) +{ + dev_set_drvdata(&ulpi->dev, data); +} + +static inline void *ulpi_get_drvdata(struct ulpi *ulpi) +{ + return dev_get_drvdata(&ulpi->dev); +} + +/** + * struct ulpi_driver - describes a ULPI PHY driver + * @id_table: array of device identifiers supported by this driver + * @probe: binds this driver to ULPI device + * @remove: unbinds this driver from ULPI device + * @driver: the name and owner members must be initialized by the drivers + */ +struct ulpi_driver { + const struct ulpi_device_id *id_table; + int (*probe)(struct ulpi *ulpi); + void (*remove)(struct ulpi *ulpi); + struct device_driver driver; +}; + +#define to_ulpi_driver(d) container_of(d, struct ulpi_driver, driver) + +int ulpi_register_driver(struct ulpi_driver *drv); +void ulpi_unregister_driver(struct ulpi_driver *drv); + +#define module_ulpi_driver(__ulpi_driver) \ + module_driver(__ulpi_driver, ulpi_register_driver, \ + ulpi_unregister_driver) + +int ulpi_read(struct ulpi *ulpi, u8 addr); +int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val); + +#endif /* __LINUX_ULPI_DRIVER_H */ diff --git a/include/linux/ulpi/interface.h b/include/linux/ulpi/interface.h new file mode 100644 index 0000000..4de8ab4 --- /dev/null +++ b/include/linux/ulpi/interface.h @@ -0,0 +1,23 @@ +#ifndef __LINUX_ULPI_INTERFACE_H +#define __LINUX_ULPI_INTERFACE_H + +#include + +struct ulpi; + +/** + * struct ulpi_ops - ULPI register access + * @dev: the interface provider + * @read: read operation for ULPI register access + * @write: write operation for ULPI register access + */ +struct ulpi_ops { + struct device *dev; + int (*read)(struct ulpi_ops *ops, u8 addr); + int (*write)(struct ulpi_ops *ops, u8 addr, u8 val); +}; + +struct ulpi *ulpi_register_interface(struct device *, struct ulpi_ops *); +void ulpi_unregister_interface(struct ulpi *); + +#endif /* __LINUX_ULPI_INTERFACE_H */ diff --git a/include/linux/ulpi/regs.h b/include/linux/ulpi/regs.h new file mode 100644 index 0000000..b5b8b88 --- /dev/null +++ b/include/linux/ulpi/regs.h @@ -0,0 +1,130 @@ +#ifndef __LINUX_ULPI_REGS_H +#define __LINUX_ULPI_REGS_H + +/* + * Macros for Set and Clear + * See ULPI 1.1 specification to find the registers with Set and Clear offsets + */ +#define ULPI_SET(a) (a + 1) +#define ULPI_CLR(a) (a + 2) + +/* + * Register Map + */ +#define ULPI_VENDOR_ID_LOW 0x00 +#define ULPI_VENDOR_ID_HIGH 0x01 +#define ULPI_PRODUCT_ID_LOW 0x02 +#define ULPI_PRODUCT_ID_HIGH 0x03 +#define ULPI_FUNC_CTRL 0x04 +#define ULPI_IFC_CTRL 0x07 +#define ULPI_OTG_CTRL 0x0a +#define ULPI_USB_INT_EN_RISE 0x0d +#define ULPI_USB_INT_EN_FALL 0x10 +#define ULPI_USB_INT_STS 0x13 +#define ULPI_USB_INT_LATCH 0x14 +#define ULPI_DEBUG 0x15 +#define ULPI_SCRATCH 0x16 +/* Optional Carkit Registers */ +#define ULPI_CARKIT_CTRL 0x19 +#define ULPI_CARKIT_INT_DELAY 0x1c +#define ULPI_CARKIT_INT_EN 0x1d +#define ULPI_CARKIT_INT_STS 0x20 +#define ULPI_CARKIT_INT_LATCH 0x21 +#define ULPI_CARKIT_PLS_CTRL 0x22 +/* Other Optional Registers */ +#define ULPI_TX_POS_WIDTH 0x25 +#define ULPI_TX_NEG_WIDTH 0x26 +#define ULPI_POLARITY_RECOVERY 0x27 +/* Access Extended Register Set */ +#define ULPI_ACCESS_EXTENDED 0x2f +/* Vendor Specific */ +#define ULPI_VENDOR_SPECIFIC 0x30 +/* Extended Registers */ +#define ULPI_EXT_VENDOR_SPECIFIC 0x80 + +/* + * Register Bits + */ + +/* Function Control */ +#define ULPI_FUNC_CTRL_XCVRSEL BIT(0) +#define ULPI_FUNC_CTRL_XCVRSEL_MASK 0x3 +#define ULPI_FUNC_CTRL_HIGH_SPEED 0x0 +#define ULPI_FUNC_CTRL_FULL_SPEED 0x1 +#define ULPI_FUNC_CTRL_LOW_SPEED 0x2 +#define ULPI_FUNC_CTRL_FS4LS 0x3 +#define ULPI_FUNC_CTRL_TERMSELECT BIT(2) +#define ULPI_FUNC_CTRL_OPMODE BIT(3) +#define ULPI_FUNC_CTRL_OPMODE_MASK (0x3 << 3) +#define ULPI_FUNC_CTRL_OPMODE_NORMAL (0x0 << 3) +#define ULPI_FUNC_CTRL_OPMODE_NONDRIVING (0x1 << 3) +#define ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI (0x2 << 3) +#define ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP (0x3 << 3) +#define ULPI_FUNC_CTRL_RESET BIT(5) +#define ULPI_FUNC_CTRL_SUSPENDM BIT(6) + +/* Interface Control */ +#define ULPI_IFC_CTRL_6_PIN_SERIAL_MODE BIT(0) +#define ULPI_IFC_CTRL_3_PIN_SERIAL_MODE BIT(1) +#define ULPI_IFC_CTRL_CARKITMODE BIT(2) +#define ULPI_IFC_CTRL_CLOCKSUSPENDM BIT(3) +#define ULPI_IFC_CTRL_AUTORESUME BIT(4) +#define ULPI_IFC_CTRL_EXTERNAL_VBUS BIT(5) +#define ULPI_IFC_CTRL_PASSTHRU BIT(6) +#define ULPI_IFC_CTRL_PROTECT_IFC_DISABLE BIT(7) + +/* OTG Control */ +#define ULPI_OTG_CTRL_ID_PULLUP BIT(0) +#define ULPI_OTG_CTRL_DP_PULLDOWN BIT(1) +#define ULPI_OTG_CTRL_DM_PULLDOWN BIT(2) +#define ULPI_OTG_CTRL_DISCHRGVBUS BIT(3) +#define ULPI_OTG_CTRL_CHRGVBUS BIT(4) +#define ULPI_OTG_CTRL_DRVVBUS BIT(5) +#define ULPI_OTG_CTRL_DRVVBUS_EXT BIT(6) +#define ULPI_OTG_CTRL_EXTVBUSIND BIT(7) + +/* USB Interrupt Enable Rising, + * USB Interrupt Enable Falling, + * USB Interrupt Status and + * USB Interrupt Latch + */ +#define ULPI_INT_HOST_DISCONNECT BIT(0) +#define ULPI_INT_VBUS_VALID BIT(1) +#define ULPI_INT_SESS_VALID BIT(2) +#define ULPI_INT_SESS_END BIT(3) +#define ULPI_INT_IDGRD BIT(4) + +/* Debug */ +#define ULPI_DEBUG_LINESTATE0 BIT(0) +#define ULPI_DEBUG_LINESTATE1 BIT(1) + +/* Carkit Control */ +#define ULPI_CARKIT_CTRL_CARKITPWR BIT(0) +#define ULPI_CARKIT_CTRL_IDGNDDRV BIT(1) +#define ULPI_CARKIT_CTRL_TXDEN BIT(2) +#define ULPI_CARKIT_CTRL_RXDEN BIT(3) +#define ULPI_CARKIT_CTRL_SPKLEFTEN BIT(4) +#define ULPI_CARKIT_CTRL_SPKRIGHTEN BIT(5) +#define ULPI_CARKIT_CTRL_MICEN BIT(6) + +/* Carkit Interrupt Enable */ +#define ULPI_CARKIT_INT_EN_IDFLOAT_RISE BIT(0) +#define ULPI_CARKIT_INT_EN_IDFLOAT_FALL BIT(1) +#define ULPI_CARKIT_INT_EN_CARINTDET BIT(2) +#define ULPI_CARKIT_INT_EN_DP_RISE BIT(3) +#define ULPI_CARKIT_INT_EN_DP_FALL BIT(4) + +/* Carkit Interrupt Status and + * Carkit Interrupt Latch + */ +#define ULPI_CARKIT_INT_IDFLOAT BIT(0) +#define ULPI_CARKIT_INT_CARINTDET BIT(1) +#define ULPI_CARKIT_INT_DP BIT(2) + +/* Carkit Pulse Control*/ +#define ULPI_CARKIT_PLS_CTRL_TXPLSEN BIT(0) +#define ULPI_CARKIT_PLS_CTRL_RXPLSEN BIT(1) +#define ULPI_CARKIT_PLS_CTRL_SPKRLEFT_BIASEN BIT(2) +#define ULPI_CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN BIT(3) + +#endif /* __LINUX_ULPI_REGS_H */ diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h index 5c295c2..5f07407 100644 --- a/include/linux/usb/ulpi.h +++ b/include/linux/usb/ulpi.h @@ -12,6 +12,8 @@ #define __LINUX_USB_ULPI_H #include +#include + /*-------------------------------------------------------------------------*/ /* @@ -49,138 +51,6 @@ /*-------------------------------------------------------------------------*/ -/* - * Macros for Set and Clear - * See ULPI 1.1 specification to find the registers with Set and Clear offsets - */ -#define ULPI_SET(a) (a + 1) -#define ULPI_CLR(a) (a + 2) - -/*-------------------------------------------------------------------------*/ - -/* - * Register Map - */ -#define ULPI_VENDOR_ID_LOW 0x00 -#define ULPI_VENDOR_ID_HIGH 0x01 -#define ULPI_PRODUCT_ID_LOW 0x02 -#define ULPI_PRODUCT_ID_HIGH 0x03 -#define ULPI_FUNC_CTRL 0x04 -#define ULPI_IFC_CTRL 0x07 -#define ULPI_OTG_CTRL 0x0a -#define ULPI_USB_INT_EN_RISE 0x0d -#define ULPI_USB_INT_EN_FALL 0x10 -#define ULPI_USB_INT_STS 0x13 -#define ULPI_USB_INT_LATCH 0x14 -#define ULPI_DEBUG 0x15 -#define ULPI_SCRATCH 0x16 -/* Optional Carkit Registers */ -#define ULPI_CARCIT_CTRL 0x19 -#define ULPI_CARCIT_INT_DELAY 0x1c -#define ULPI_CARCIT_INT_EN 0x1d -#define ULPI_CARCIT_INT_STS 0x20 -#define ULPI_CARCIT_INT_LATCH 0x21 -#define ULPI_CARCIT_PLS_CTRL 0x22 -/* Other Optional Registers */ -#define ULPI_TX_POS_WIDTH 0x25 -#define ULPI_TX_NEG_WIDTH 0x26 -#define ULPI_POLARITY_RECOVERY 0x27 -/* Access Extended Register Set */ -#define ULPI_ACCESS_EXTENDED 0x2f -/* Vendor Specific */ -#define ULPI_VENDOR_SPECIFIC 0x30 -/* Extended Registers */ -#define ULPI_EXT_VENDOR_SPECIFIC 0x80 - -/*-------------------------------------------------------------------------*/ - -/* - * Register Bits - */ - -/* Function Control */ -#define ULPI_FUNC_CTRL_XCVRSEL (1 << 0) -#define ULPI_FUNC_CTRL_XCVRSEL_MASK (3 << 0) -#define ULPI_FUNC_CTRL_HIGH_SPEED (0 << 0) -#define ULPI_FUNC_CTRL_FULL_SPEED (1 << 0) -#define ULPI_FUNC_CTRL_LOW_SPEED (2 << 0) -#define ULPI_FUNC_CTRL_FS4LS (3 << 0) -#define ULPI_FUNC_CTRL_TERMSELECT (1 << 2) -#define ULPI_FUNC_CTRL_OPMODE (1 << 3) -#define ULPI_FUNC_CTRL_OPMODE_MASK (3 << 3) -#define ULPI_FUNC_CTRL_OPMODE_NORMAL (0 << 3) -#define ULPI_FUNC_CTRL_OPMODE_NONDRIVING (1 << 3) -#define ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI (2 << 3) -#define ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP (3 << 3) -#define ULPI_FUNC_CTRL_RESET (1 << 5) -#define ULPI_FUNC_CTRL_SUSPENDM (1 << 6) - -/* Interface Control */ -#define ULPI_IFC_CTRL_6_PIN_SERIAL_MODE (1 << 0) -#define ULPI_IFC_CTRL_3_PIN_SERIAL_MODE (1 << 1) -#define ULPI_IFC_CTRL_CARKITMODE (1 << 2) -#define ULPI_IFC_CTRL_CLOCKSUSPENDM (1 << 3) -#define ULPI_IFC_CTRL_AUTORESUME (1 << 4) -#define ULPI_IFC_CTRL_EXTERNAL_VBUS (1 << 5) -#define ULPI_IFC_CTRL_PASSTHRU (1 << 6) -#define ULPI_IFC_CTRL_PROTECT_IFC_DISABLE (1 << 7) - -/* OTG Control */ -#define ULPI_OTG_CTRL_ID_PULLUP (1 << 0) -#define ULPI_OTG_CTRL_DP_PULLDOWN (1 << 1) -#define ULPI_OTG_CTRL_DM_PULLDOWN (1 << 2) -#define ULPI_OTG_CTRL_DISCHRGVBUS (1 << 3) -#define ULPI_OTG_CTRL_CHRGVBUS (1 << 4) -#define ULPI_OTG_CTRL_DRVVBUS (1 << 5) -#define ULPI_OTG_CTRL_DRVVBUS_EXT (1 << 6) -#define ULPI_OTG_CTRL_EXTVBUSIND (1 << 7) - -/* USB Interrupt Enable Rising, - * USB Interrupt Enable Falling, - * USB Interrupt Status and - * USB Interrupt Latch - */ -#define ULPI_INT_HOST_DISCONNECT (1 << 0) -#define ULPI_INT_VBUS_VALID (1 << 1) -#define ULPI_INT_SESS_VALID (1 << 2) -#define ULPI_INT_SESS_END (1 << 3) -#define ULPI_INT_IDGRD (1 << 4) - -/* Debug */ -#define ULPI_DEBUG_LINESTATE0 (1 << 0) -#define ULPI_DEBUG_LINESTATE1 (1 << 1) - -/* Carkit Control */ -#define ULPI_CARKIT_CTRL_CARKITPWR (1 << 0) -#define ULPI_CARKIT_CTRL_IDGNDDRV (1 << 1) -#define ULPI_CARKIT_CTRL_TXDEN (1 << 2) -#define ULPI_CARKIT_CTRL_RXDEN (1 << 3) -#define ULPI_CARKIT_CTRL_SPKLEFTEN (1 << 4) -#define ULPI_CARKIT_CTRL_SPKRIGHTEN (1 << 5) -#define ULPI_CARKIT_CTRL_MICEN (1 << 6) - -/* Carkit Interrupt Enable */ -#define ULPI_CARKIT_INT_EN_IDFLOAT_RISE (1 << 0) -#define ULPI_CARKIT_INT_EN_IDFLOAT_FALL (1 << 1) -#define ULPI_CARKIT_INT_EN_CARINTDET (1 << 2) -#define ULPI_CARKIT_INT_EN_DP_RISE (1 << 3) -#define ULPI_CARKIT_INT_EN_DP_FALL (1 << 4) - -/* Carkit Interrupt Status and - * Carkit Interrupt Latch - */ -#define ULPI_CARKIT_INT_IDFLOAT (1 << 0) -#define ULPI_CARKIT_INT_CARINTDET (1 << 1) -#define ULPI_CARKIT_INT_DP (1 << 2) - -/* Carkit Pulse Control*/ -#define ULPI_CARKIT_PLS_CTRL_TXPLSEN (1 << 0) -#define ULPI_CARKIT_PLS_CTRL_RXPLSEN (1 << 1) -#define ULPI_CARKIT_PLS_CTRL_SPKRLEFT_BIASEN (1 << 2) -#define ULPI_CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN (1 << 3) - -/*-------------------------------------------------------------------------*/ - #if IS_ENABLED(CONFIG_USB_ULPI) struct usb_phy *otg_ulpi_create(struct usb_phy_io_ops *ops, unsigned int flags); diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index fce36d0..ada8417 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -189,5 +189,9 @@ int main(void) DEVID_FIELD(rio_device_id, asm_did); DEVID_FIELD(rio_device_id, asm_vid); + DEVID(ulpi_device_id); + DEVID_FIELD(ulpi_device_id, vendor); + DEVID_FIELD(ulpi_device_id, product); + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 78691d5..a7a8560 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1192,6 +1192,19 @@ static int do_rio_entry(const char *filename, } ADD_TO_DEVTABLE("rapidio", rio_device_id, do_rio_entry); +/* Looks like: ulpi:vNpN */ +static int do_ulpi_entry(const char *filename, void *symval, + char *alias) +{ + DEF_FIELD(symval, ulpi_device_id, vendor); + DEF_FIELD(symval, ulpi_device_id, product); + + sprintf(alias, "ulpi:v%04xp%04x", vendor, product); + + return 1; +} +ADD_TO_DEVTABLE("ulpi", ulpi_device_id, do_ulpi_entry); + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { -- cgit v0.10.2 From b5699eeee68f7667f200b05f57bcf8124ddba9fc Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 13 May 2015 15:26:43 +0300 Subject: usb: dwc3: USB2 PHY register access bits Definitions for Global USB2 PHY Vendor Control Register bits. We will need them to access ULPI PHY registers later. Signed-off-by: Heikki Krogerus Acked-by: David Cohen Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index fdab715..747805d 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -174,6 +174,14 @@ #define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) #define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) +/* Global USB2 PHY Vendor Control Register */ +#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25) +#define DWC3_GUSB2PHYACC_BUSY (1 << 23) +#define DWC3_GUSB2PHYACC_WRITE (1 << 22) +#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16) +#define DWC3_GUSB2PHYACC_EXTEND_ADDR(n) (n << 8) +#define DWC3_GUSB2PHYACC_DATA(n) (n & 0xff) + /* Global USB3 PIPE Control Register */ #define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) #define DWC3_GUSB3PIPECTL_U2SSINP3OK (1 << 29) -- cgit v0.10.2 From f699b94789a64bec66e45c2dc39f06ae1208d852 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 13 May 2015 15:26:44 +0300 Subject: usb: dwc3: ULPI or UTMI+ select Make selection between ULPI and UTMI+ interfaces possible by providing definition for the bit in Global USB2 PHY Configuration Register that controls it. Signed-off-by: Heikki Krogerus Acked-by: David Cohen Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 747805d..c6eafaa 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -173,6 +173,7 @@ /* Global USB2 PHY Configuration Register */ #define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) #define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) +#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4) /* Global USB2 PHY Vendor Control Register */ #define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25) -- cgit v0.10.2 From 6c89cce0476f309689549c74717445e624e0de04 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 13 May 2015 15:26:45 +0300 Subject: usb: dwc3: store driver data earlier We need to store it before phys are handled, so we can later use it in ULPI interface support code. Signed-off-by: Heikki Krogerus Acked-by: David Cohen Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 2bbab3d..c7734ed 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -875,12 +875,13 @@ static int dwc3_probe(struct platform_device *pdev) dwc->hird_threshold = hird_threshold | (dwc->is_utmi_l1_suspend << 4); + platform_set_drvdata(pdev, dwc); + ret = dwc3_core_get_phy(dwc); if (ret) goto err0; spin_lock_init(&dwc->lock); - platform_set_drvdata(pdev, dwc); if (!dev->dma_mask) { dev->dma_mask = dev->parent->dma_mask; -- cgit v0.10.2 From 2917e7181589f859ac1e00ba1b3decbbfc2549bb Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 13 May 2015 15:26:46 +0300 Subject: usb: dwc3: cache hwparams earlier So they are available when ULPI interface support is added. Signed-off-by: Heikki Krogerus Acked-by: David Cohen Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index c7734ed..104b236 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -876,6 +876,7 @@ static int dwc3_probe(struct platform_device *pdev) | (dwc->is_utmi_l1_suspend << 4); platform_set_drvdata(pdev, dwc); + dwc3_cache_hwparams(dwc); ret = dwc3_core_get_phy(dwc); if (ret) @@ -893,8 +894,6 @@ static int dwc3_probe(struct platform_device *pdev) pm_runtime_get_sync(dev); pm_runtime_forbid(dev); - dwc3_cache_hwparams(dwc); - ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); -- cgit v0.10.2 From c5cc74e8c12be67ef7f09f77c2b9df6faf7904f0 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 13 May 2015 15:26:47 +0300 Subject: usb: dwc3: soft reset to it's own function So it can be called from other places later. Signed-off-by: Heikki Krogerus Acked-by: David Cohen Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 104b236..921f181 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -117,6 +117,33 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) } /** + * dwc3_soft_reset - Issue soft reset + * @dwc: Pointer to our controller context structure + */ +static int dwc3_soft_reset(struct dwc3 *dwc) +{ + unsigned long timeout; + u32 reg; + + timeout = jiffies + msecs_to_jiffies(500); + dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + break; + + if (time_after(jiffies, timeout)) { + dev_err(dwc->dev, "Reset Timed Out\n"); + return -ETIMEDOUT; + } + + cpu_relax(); + } while (true); + + return 0; +} + +/** * dwc3_free_one_event_buffer - Frees one event buffer * @dwc: Pointer to our controller context structure * @evt: Pointer to event buffer to be freed @@ -438,7 +465,6 @@ static void dwc3_phy_setup(struct dwc3 *dwc) */ static int dwc3_core_init(struct dwc3 *dwc) { - unsigned long timeout; u32 hwparams4 = dwc->hwparams.hwparams4; u32 reg; int ret; @@ -466,21 +492,9 @@ static int dwc3_core_init(struct dwc3 *dwc) } /* issue device SoftReset too */ - timeout = jiffies + msecs_to_jiffies(500); - dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); - do { - reg = dwc3_readl(dwc->regs, DWC3_DCTL); - if (!(reg & DWC3_DCTL_CSFTRST)) - break; - - if (time_after(jiffies, timeout)) { - dev_err(dwc->dev, "Reset Timed Out\n"); - ret = -ETIMEDOUT; - goto err0; - } - - cpu_relax(); - } while (true); + ret = dwc3_soft_reset(dwc); + if (ret) + goto err0; ret = dwc3_core_soft_reset(dwc); if (ret) -- cgit v0.10.2 From 45bb7de213d86d491840e9c3ae139475ab3fa493 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 13 May 2015 15:26:48 +0300 Subject: usb: dwc3: setup phys earlier This allows dwc3_phy_setup() to be more useful later. There is nothing preventing the PHY configuration registers from being programmed early. They do not loose their context in soft reset. There are however other PHY related operations that should be executed before the driver request handles to the PHYs, such as registering DWC3's ULPI interface, which can now be done in dwc3_phy_setup(). Also, if there ever was need for the two 100ms delays in dwc3_phy_setup() there isn't anymore. The PHYs are now reset after the PHY interfaces are setup. Signed-off-by: Heikki Krogerus Acked-by: David Cohen Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 921f181..6b02e12 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -436,8 +436,6 @@ static void dwc3_phy_setup(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); - mdelay(100); - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); /* @@ -453,8 +451,6 @@ static void dwc3_phy_setup(struct dwc3 *dwc) reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); - - mdelay(100); } /** @@ -569,8 +565,6 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GCTL, reg); - dwc3_phy_setup(dwc); - ret = dwc3_alloc_scratch_buffers(dwc); if (ret) goto err1; @@ -892,6 +886,8 @@ static int dwc3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dwc); dwc3_cache_hwparams(dwc); + dwc3_phy_setup(dwc); + ret = dwc3_core_get_phy(dwc); if (ret) goto err0; -- cgit v0.10.2 From 3e10a2ce98d1a57992a44ed40325af60ab7b0f5d Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 13 May 2015 15:26:49 +0300 Subject: usb: dwc3: add hsphy_interface property Platforms that have configured DWC_USB3_HSPHY_INTERFACE with value 3, i.e. UTMI+ and ULPI, need to inform the driver of the actual HSPHY interface type with the property. "utmi" if the interface is UTMI+ or "ulpi" if the interface is ULPI. Signed-off-by: Heikki Krogerus Acked-by: David Cohen Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index 5cc3643..0815eac 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -38,6 +38,8 @@ Optional properties: - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal utmi_l1_suspend_n, false when asserts utmi_sleep_n - snps,hird-threshold: HIRD threshold + - snps,hsphy_interface: High-Speed PHY interface selection between "utmi" for + UTMI+ and "ulpi" for ULPI when the DWC_USB3_HSPHY_INTERFACE has value 3. This is usually a subnode to DWC3 glue to which it is connected. diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 6b02e12..0de8968 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -438,6 +438,22 @@ static void dwc3_phy_setup(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + /* Select the HS PHY interface */ + switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { + case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI: + if (!strncmp(dwc->hsphy_interface, "utmi", 4)) { + reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI; + } else if (!strncmp(dwc->hsphy_interface, "ulpi", 4)) { + reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI; + } else { + dev_warn(dwc->dev, "HSPHY Interface not defined\n"); + break; + } + /* FALLTHROUGH */ + default: + break; + } + /* * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to * '0' during coreConsultant configuration. So default value will @@ -844,6 +860,8 @@ static int dwc3_probe(struct platform_device *pdev) "snps,tx_de_emphasis_quirk"); of_property_read_u8(node, "snps,tx_de_emphasis", &tx_de_emphasis); + of_property_read_string(node, "snps,hsphy_interface", + &dwc->hsphy_interface); } else if (pdata) { dwc->maximum_speed = pdata->maximum_speed; dwc->has_lpm_erratum = pdata->has_lpm_erratum; @@ -871,6 +889,8 @@ static int dwc3_probe(struct platform_device *pdev) dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk; if (pdata->tx_de_emphasis) tx_de_emphasis = pdata->tx_de_emphasis; + + dwc->hsphy_interface = pdata->hsphy_interface; } /* default to superspeed if no maximum_speed passed */ diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index c6eafaa..7b3ab64 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -682,6 +682,7 @@ struct dwc3_scratchpad_array { * @test_mode_nr: test feature selector * @lpm_nyet_threshold: LPM NYET response threshold * @hird_threshold: HIRD threshold + * @hsphy_interface: "utmi" or "ulpi" * @delayed_status: true when gadget driver asks for delayed status * @ep0_bounced: true when we used bounce buffer * @ep0_expect_in: true when we expect a DATA IN transfer @@ -809,6 +810,8 @@ struct dwc3 { u8 lpm_nyet_threshold; u8 hird_threshold; + const char *hsphy_interface; + unsigned delayed_status:1; unsigned ep0_bounced:1; unsigned ep0_expect_in:1; diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h index a2bd464..d3614ec 100644 --- a/drivers/usb/dwc3/platform_data.h +++ b/drivers/usb/dwc3/platform_data.h @@ -45,4 +45,6 @@ struct dwc3_platform_data { unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; + + const char *hsphy_interface; }; -- cgit v0.10.2 From a89d977cc04c77d9aa45d426dbf8de9dd1326c77 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 13 May 2015 15:26:50 +0300 Subject: usb: dwc3: pci: add quirk for Baytrails On some BYT platforms the USB2 PHY needs to be put into operational mode by the controller driver with GPIOs controlling the PHYs reset and cs signals. Signed-off-by: Heikki Krogerus Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index b773fb5..27e4fc8 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include "platform_data.h" @@ -31,6 +33,15 @@ #define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30 #define PCI_DEVICE_ID_INTEL_SPTH 0xa130 +static const struct acpi_gpio_params reset_gpios = { 0, 0, false }; +static const struct acpi_gpio_params cs_gpios = { 1, 0, false }; + +static const struct acpi_gpio_mapping acpi_dwc3_byt_gpios[] = { + { "reset-gpios", &reset_gpios, 1 }, + { "cs-gpios", &cs_gpios, 1 }, + { }, +}; + static int dwc3_pci_quirks(struct pci_dev *pdev) { if (pdev->vendor == PCI_VENDOR_ID_AMD && @@ -65,6 +76,30 @@ static int dwc3_pci_quirks(struct pci_dev *pdev) sizeof(pdata)); } + if (pdev->vendor == PCI_VENDOR_ID_INTEL && + pdev->device == PCI_DEVICE_ID_INTEL_BYT) { + struct gpio_desc *gpio; + + acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev), + acpi_dwc3_byt_gpios); + + /* These GPIOs will turn on the USB2 PHY */ + gpio = gpiod_get(&pdev->dev, "cs"); + if (!IS_ERR(gpio)) { + gpiod_direction_output(gpio, 0); + gpiod_set_value_cansleep(gpio, 1); + gpiod_put(gpio); + } + + gpio = gpiod_get(&pdev->dev, "reset"); + if (!IS_ERR(gpio)) { + gpiod_direction_output(gpio, 0); + gpiod_set_value_cansleep(gpio, 1); + gpiod_put(gpio); + usleep_range(10000, 11000); + } + } + return 0; } @@ -128,6 +163,7 @@ err: static void dwc3_pci_remove(struct pci_dev *pci) { + acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pci->dev)); platform_device_unregister(pci_get_drvdata(pci)); } -- cgit v0.10.2 From 88bc9d194ff69875a4d3c958d969ed2a053c8308 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 13 May 2015 15:26:51 +0300 Subject: usb: dwc3: add ULPI interface support Registers DWC3's ULPI interface with the ULPI bus when it's available. Signed-off-by: Heikki Krogerus Acked-by: David Cohen Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 827c4f8..473bfaa 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -11,6 +11,13 @@ config USB_DWC3 if USB_DWC3 +config USB_DWC3_ULPI + bool "Register ULPI PHY Interface" + depends on USB_ULPI_BUS=y || USB_ULPI_BUS=USB_DWC3 + help + Select this if you have ULPI type PHY attached to your DWC3 + controller. + choice bool "DWC3 Mode Selection" default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET) diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 46172f4..c7076e3 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -15,6 +15,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),) dwc3-y += gadget.o ep0.o endif +ifneq ($(CONFIG_USB_DWC3_ULPI),) + dwc3-y += ulpi.o +endif + ifneq ($(CONFIG_DEBUG_FS),) dwc3-y += debugfs.o endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 0de8968..5c110d8 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -394,10 +394,15 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) /** * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core * @dwc: Pointer to our controller context structure + * + * Returns 0 on success. The USB PHY interfaces are configured but not + * initialized. The PHY interfaces and the PHYs get initialized together with + * the core in dwc3_core_init. */ -static void dwc3_phy_setup(struct dwc3 *dwc) +static int dwc3_phy_setup(struct dwc3 *dwc) { u32 reg; + int ret; reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); @@ -443,13 +448,30 @@ static void dwc3_phy_setup(struct dwc3 *dwc) case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI: if (!strncmp(dwc->hsphy_interface, "utmi", 4)) { reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI; + break; } else if (!strncmp(dwc->hsphy_interface, "ulpi", 4)) { reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); } else { dev_warn(dwc->dev, "HSPHY Interface not defined\n"); - break; + + /* Relying on default value. */ + if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI)) + break; } /* FALLTHROUGH */ + case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI: + /* Making sure the interface and PHY are operational */ + ret = dwc3_soft_reset(dwc); + if (ret) + return ret; + + udelay(1); + + ret = dwc3_ulpi_init(dwc); + if (ret) + return ret; + /* FALLTHROUGH */ default: break; } @@ -467,6 +489,8 @@ static void dwc3_phy_setup(struct dwc3 *dwc) reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + return 0; } /** @@ -906,7 +930,9 @@ static int dwc3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dwc); dwc3_cache_hwparams(dwc); - dwc3_phy_setup(dwc); + ret = dwc3_phy_setup(dwc); + if (ret) + goto err0; ret = dwc3_core_get_phy(dwc); if (ret) @@ -994,6 +1020,7 @@ err2: err1: dwc3_free_event_buffers(dwc); + dwc3_ulpi_exit(dwc); err0: /* @@ -1029,6 +1056,7 @@ static int dwc3_remove(struct platform_device *pdev) phy_power_off(dwc->usb3_generic_phy); dwc3_core_exit(dwc); + dwc3_ulpi_exit(dwc); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 7b3ab64..d70da20 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -661,6 +662,7 @@ struct dwc3_scratchpad_array { * @usb3_phy: pointer to USB3 PHY * @usb2_generic_phy: pointer to USB2 PHY * @usb3_generic_phy: pointer to USB3 PHY + * @ulpi: pointer to ulpi interface * @dcfg: saved contents of DCFG register * @gctl: saved contents of GCTL register * @isoch_delay: wValue from Set Isochronous Delay request; @@ -749,6 +751,8 @@ struct dwc3 { struct phy *usb2_generic_phy; struct phy *usb3_generic_phy; + struct ulpi *ulpi; + void __iomem *regs; size_t regs_size; @@ -1047,4 +1051,14 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc) } #endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */ +#if IS_ENABLED(CONFIG_USB_DWC3_ULPI) +int dwc3_ulpi_init(struct dwc3 *dwc); +void dwc3_ulpi_exit(struct dwc3 *dwc); +#else +static inline int dwc3_ulpi_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_ulpi_exit(struct dwc3 *dwc) +{ } +#endif + #endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c new file mode 100644 index 0000000..ec004c6 --- /dev/null +++ b/drivers/usb/dwc3/ulpi.c @@ -0,0 +1,91 @@ +/** + * ulpi.c - DesignWare USB3 Controller's ULPI PHY interface + * + * Copyright (C) 2015 Intel Corporation + * + * Author: Heikki Krogerus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#include "core.h" +#include "io.h" + +#define DWC3_ULPI_ADDR(a) \ + ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \ + DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \ + DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a)) + +static int dwc3_ulpi_busyloop(struct dwc3 *dwc) +{ + unsigned count = 1000; + u32 reg; + + while (count--) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); + if (!(reg & DWC3_GUSB2PHYACC_BUSY)) + return 0; + cpu_relax(); + } + + return -ETIMEDOUT; +} + +static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr) +{ + struct dwc3 *dwc = dev_get_drvdata(ops->dev); + u32 reg; + int ret; + + reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); + dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); + + ret = dwc3_ulpi_busyloop(dwc); + if (ret) + return ret; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); + + return DWC3_GUSB2PHYACC_DATA(reg); +} + +static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val) +{ + struct dwc3 *dwc = dev_get_drvdata(ops->dev); + u32 reg; + + reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); + reg |= DWC3_GUSB2PHYACC_WRITE | val; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); + + return dwc3_ulpi_busyloop(dwc); +} + +static struct ulpi_ops dwc3_ulpi_ops = { + .read = dwc3_ulpi_read, + .write = dwc3_ulpi_write, +}; + +int dwc3_ulpi_init(struct dwc3 *dwc) +{ + /* Register the interface */ + dwc->ulpi = ulpi_register_interface(dwc->dev, &dwc3_ulpi_ops); + if (IS_ERR(dwc->ulpi)) { + dev_err(dwc->dev, "failed to register ULPI interface"); + return PTR_ERR(dwc->ulpi); + } + + return 0; +} + +void dwc3_ulpi_exit(struct dwc3 *dwc) +{ + if (dwc->ulpi) { + ulpi_unregister_interface(dwc->ulpi); + dwc->ulpi = NULL; + } +} -- cgit v0.10.2 From 723487a6ff50bb88c628a859aeac3fe721f0d1fa Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 13 May 2015 15:26:52 +0300 Subject: phy: helpers for USB ULPI PHY registering ULPI PHYs need to be bound to their controllers with a lookup. This adds helpers that the ULPI drivers can use to do both, the registration of the PHY and the lookup, at the same time. Signed-off-by: Heikki Krogerus Acked-by: David Cohen Acked-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/phy/ulpi_phy.h b/drivers/phy/ulpi_phy.h new file mode 100644 index 0000000..ac49fb6 --- /dev/null +++ b/drivers/phy/ulpi_phy.h @@ -0,0 +1,31 @@ +#include + +/** + * Helper that registers PHY for a ULPI device and adds a lookup for binding it + * and it's controller, which is always the parent. + */ +static inline struct phy +*ulpi_phy_create(struct ulpi *ulpi, struct phy_ops *ops) +{ + struct phy *phy; + int ret; + + phy = phy_create(&ulpi->dev, NULL, ops); + if (IS_ERR(phy)) + return phy; + + ret = phy_create_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent)); + if (ret) { + phy_destroy(phy); + return ERR_PTR(ret); + } + + return phy; +} + +/* Remove a PHY that was created with ulpi_phy_create() and it's lookup. */ +static inline void ulpi_phy_destroy(struct ulpi *ulpi, struct phy *phy) +{ + phy_remove_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent)); + phy_destroy(phy); +} -- cgit v0.10.2 From 1c14905ef951fb968c8da90e4e64be02c309a2ae Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 13 May 2015 15:26:53 +0300 Subject: phy: add driver for TI TUSB1210 ULPI PHY TUSB1210 ULPI PHY has vendor specific register for eye diagram tuning. On some platforms the system firmware has set optimized value to it. In order to not loose the optimized value, the driver stores it during probe and restores it every time the PHY is powered back on. Signed-off-by: Heikki Krogerus Acked-by: David Cohen Acked-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index a53bd5b..fceac96 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -309,4 +309,11 @@ config PHY_QCOM_UFS help Support for UFS PHY on QCOM chipsets. +config PHY_TUSB1210 + tristate "TI TUSB1210 ULPI PHY module" + depends on USB_ULPI_BUS + select GENERIC_PHY + help + Support for TI TUSB1210 USB ULPI PHY. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f126251..0a20418 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -40,3 +40,4 @@ obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o +obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o diff --git a/drivers/phy/phy-tusb1210.c b/drivers/phy/phy-tusb1210.c new file mode 100644 index 0000000..07efdd3 --- /dev/null +++ b/drivers/phy/phy-tusb1210.c @@ -0,0 +1,153 @@ +/** + * tusb1210.c - TUSB1210 USB ULPI PHY driver + * + * Copyright (C) 2015 Intel Corporation + * + * Author: Heikki Krogerus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#include "ulpi_phy.h" + +#define TUSB1210_VENDOR_SPECIFIC2 0x80 +#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0 +#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4 +#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6 + +struct tusb1210 { + struct ulpi *ulpi; + struct phy *phy; + struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_cs; + u8 vendor_specific2; +}; + +static int tusb1210_power_on(struct phy *phy) +{ + struct tusb1210 *tusb = phy_get_drvdata(phy); + + gpiod_set_value_cansleep(tusb->gpio_reset, 1); + gpiod_set_value_cansleep(tusb->gpio_cs, 1); + + /* Restore the optional eye diagram optimization value */ + if (tusb->vendor_specific2) + ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2, + tusb->vendor_specific2); + + return 0; +} + +static int tusb1210_power_off(struct phy *phy) +{ + struct tusb1210 *tusb = phy_get_drvdata(phy); + + gpiod_set_value_cansleep(tusb->gpio_reset, 0); + gpiod_set_value_cansleep(tusb->gpio_cs, 0); + + return 0; +} + +static struct phy_ops phy_ops = { + .power_on = tusb1210_power_on, + .power_off = tusb1210_power_off, + .owner = THIS_MODULE, +}; + +static int tusb1210_probe(struct ulpi *ulpi) +{ + struct gpio_desc *gpio; + struct tusb1210 *tusb; + u8 val, reg; + int ret; + + tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL); + if (!tusb) + return -ENOMEM; + + gpio = devm_gpiod_get(&ulpi->dev, "reset"); + if (!IS_ERR(gpio)) { + ret = gpiod_direction_output(gpio, 0); + if (ret) + return ret; + gpiod_set_value_cansleep(gpio, 1); + tusb->gpio_reset = gpio; + } + + gpio = devm_gpiod_get(&ulpi->dev, "cs"); + if (!IS_ERR(gpio)) { + ret = gpiod_direction_output(gpio, 0); + if (ret) + return ret; + gpiod_set_value_cansleep(gpio, 1); + tusb->gpio_cs = gpio; + } + + /* + * VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye + * diagram optimization and DP/DM swap. + */ + + /* High speed output drive strength configuration */ + device_property_read_u8(&ulpi->dev, "ihstx", &val); + reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT; + + /* High speed output impedance configuration */ + device_property_read_u8(&ulpi->dev, "zhsdrv", &val); + reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT; + + /* DP/DM swap control */ + device_property_read_u8(&ulpi->dev, "datapolarity", &val); + reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT; + + if (reg) { + ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg); + tusb->vendor_specific2 = reg; + } + + tusb->phy = ulpi_phy_create(ulpi, &phy_ops); + if (IS_ERR(tusb->phy)) + return PTR_ERR(tusb->phy); + + tusb->ulpi = ulpi; + + phy_set_drvdata(tusb->phy, tusb); + ulpi_set_drvdata(ulpi, tusb); + return 0; +} + +static void tusb1210_remove(struct ulpi *ulpi) +{ + struct tusb1210 *tusb = ulpi_get_drvdata(ulpi); + + ulpi_phy_destroy(ulpi, tusb->phy); +} + +#define TI_VENDOR_ID 0x0451 + +static const struct ulpi_device_id tusb1210_ulpi_id[] = { + { TI_VENDOR_ID, 0x1507, }, + { }, +}; +MODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id); + +static struct ulpi_driver tusb1210_driver = { + .id_table = tusb1210_ulpi_id, + .probe = tusb1210_probe, + .remove = tusb1210_remove, + .driver = { + .name = "tusb1210", + .owner = THIS_MODULE, + }, +}; + +module_ulpi_driver(tusb1210_driver); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TUSB1210 ULPI PHY driver"); -- cgit v0.10.2 From 88a25e02f35e56a6686d94fa334b7f00e9b72623 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Fri, 9 Jan 2015 09:28:41 +0900 Subject: usb: renesas_usbhs: Add access control for INTSTS1 and INTENB1 register INTSTS1 and INTENB1 register of renesas_usbhs can access only Host mode. This adds process of accessing INTSTS1 and INTENB1 only when renesas_usbhs is Host mode. Signed-off-by: Nobuhiro Iwamatsu Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 9a705b1..e5ce6e6 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -218,10 +218,12 @@ static int usbhs_status_get_each_irq(struct usbhs_priv *priv, /******************** spin lock ********************/ usbhs_lock(priv, flags); state->intsts0 = usbhs_read(priv, INTSTS0); - state->intsts1 = usbhs_read(priv, INTSTS1); - intenb0 = usbhs_read(priv, INTENB0); - intenb1 = usbhs_read(priv, INTENB1); + + if (usbhs_mod_is_host(priv)) { + state->intsts1 = usbhs_read(priv, INTSTS1); + intenb1 = usbhs_read(priv, INTENB1); + } /* mask */ if (mod) { @@ -275,7 +277,8 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) * - Function :: VALID bit should 0 */ usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC); - usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC); + if (usbhs_mod_is_host(priv)) + usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC); usbhs_write(priv, BRDYSTS, ~irq_state.brdysts); usbhs_write(priv, NRDYSTS, ~irq_state.nrdysts); @@ -303,19 +306,20 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) if (irq_state.intsts0 & BRDY) usbhs_mod_call(priv, irq_ready, priv, &irq_state); - /* INTSTS1 */ - if (irq_state.intsts1 & ATTCH) - usbhs_mod_call(priv, irq_attch, priv, &irq_state); - - if (irq_state.intsts1 & DTCH) - usbhs_mod_call(priv, irq_dtch, priv, &irq_state); + if (usbhs_mod_is_host(priv)) { + /* INTSTS1 */ + if (irq_state.intsts1 & ATTCH) + usbhs_mod_call(priv, irq_attch, priv, &irq_state); - if (irq_state.intsts1 & SIGN) - usbhs_mod_call(priv, irq_sign, priv, &irq_state); + if (irq_state.intsts1 & DTCH) + usbhs_mod_call(priv, irq_dtch, priv, &irq_state); - if (irq_state.intsts1 & SACK) - usbhs_mod_call(priv, irq_sack, priv, &irq_state); + if (irq_state.intsts1 & SIGN) + usbhs_mod_call(priv, irq_sign, priv, &irq_state); + if (irq_state.intsts1 & SACK) + usbhs_mod_call(priv, irq_sack, priv, &irq_state); + } return IRQ_HANDLED; } @@ -334,7 +338,8 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) * - update INTSTS0 */ usbhs_write(priv, INTENB0, 0); - usbhs_write(priv, INTENB1, 0); + if (usbhs_mod_is_host(priv)) + usbhs_write(priv, INTENB1, 0); usbhs_write(priv, BEMPENB, 0); usbhs_write(priv, BRDYENB, 0); @@ -368,25 +373,27 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) intenb0 |= BRDYE; } - /* - * INTSTS1 - */ - if (mod->irq_attch) - intenb1 |= ATTCHE; + if (usbhs_mod_is_host(priv)) { + /* + * INTSTS1 + */ + if (mod->irq_attch) + intenb1 |= ATTCHE; - if (mod->irq_dtch) - intenb1 |= DTCHE; + if (mod->irq_dtch) + intenb1 |= DTCHE; - if (mod->irq_sign) - intenb1 |= SIGNE; + if (mod->irq_sign) + intenb1 |= SIGNE; - if (mod->irq_sack) - intenb1 |= SACKE; + if (mod->irq_sack) + intenb1 |= SACKE; + } } if (intenb0) usbhs_write(priv, INTENB0, intenb0); - if (intenb1) + if (usbhs_mod_is_host(priv) && intenb1) usbhs_write(priv, INTENB1, intenb1); } -- cgit v0.10.2 From 9d699bf4dbe29d8f26769ea81dec4a8c91edcd5a Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 18 May 2015 18:33:47 +0900 Subject: phy: rcar-gen2: Add support for R-Car E2 This patch adds a compatible string to support for R-Car E2. Signed-off-by: Yoshihiro Shimoda Acked-by: Geert Uytterhoeven Acked-by: Simon Horman Signed-off-by: Kishon Vijay Abraham I diff --git a/Documentation/devicetree/bindings/phy/rcar-gen2-phy.txt b/Documentation/devicetree/bindings/phy/rcar-gen2-phy.txt index 00fc52a..d564ba4 100644 --- a/Documentation/devicetree/bindings/phy/rcar-gen2-phy.txt +++ b/Documentation/devicetree/bindings/phy/rcar-gen2-phy.txt @@ -6,6 +6,7 @@ This file provides information on what the device node for the R-Car generation Required properties: - compatible: "renesas,usb-phy-r8a7790" if the device is a part of R8A7790 SoC. "renesas,usb-phy-r8a7791" if the device is a part of R8A7791 SoC. + "renesas,usb-phy-r8a7794" if the device is a part of R8A7794 SoC. - reg: offset and length of the register block. - #address-cells: number of address cells for the USB channel subnodes, must be <1>. diff --git a/drivers/phy/phy-rcar-gen2.c b/drivers/phy/phy-rcar-gen2.c index f47bfd8..0050301 100644 --- a/drivers/phy/phy-rcar-gen2.c +++ b/drivers/phy/phy-rcar-gen2.c @@ -195,6 +195,7 @@ static struct phy_ops rcar_gen2_phy_ops = { static const struct of_device_id rcar_gen2_phy_match_table[] = { { .compatible = "renesas,usb-phy-r8a7790" }, { .compatible = "renesas,usb-phy-r8a7791" }, + { .compatible = "renesas,usb-phy-r8a7794" }, { } }; MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table); -- cgit v0.10.2 From 868055fdd2399cbb0cb3fe0cd7b5047a3a77b8b9 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 18 May 2015 17:40:02 +0200 Subject: usb: gadget: rndis: change the value passed to rndis_signal_(dis)connect() The patch: 83210e59ee1527f229af6aef78c95b747bdcf9c4 usb: gadget: rndis: use rndis_params instead of configNr should change all invocations of rndis_signal_(dis)connect(). This patch fixes that. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index dd68000..f626a63 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -1144,11 +1144,11 @@ static ssize_t rndis_proc_write(struct file *file, const char __user *buffer, break; case 'C': case 'c': - rndis_signal_connect(p->confignr); + rndis_signal_connect(p); break; case 'D': case 'd': - rndis_signal_disconnect(p->confignr); + rndis_signal_disconnect(p); break; default: if (fl_speed) p->speed = speed; -- cgit v0.10.2 From c0d96af2e0427cc90b1f0d3c6a5294d90f93fcf4 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 18 May 2015 17:40:03 +0200 Subject: usb: gadget: rndis: don't duplicate the "i" variable If CONFIG_USB_GADGET_DEBUG_FILES is set then a block is opened and inside it there is a local variable "i" which hides the "i" local to the rndis_deregister(). Consequently, a random value is formatted into the "name" buffer. This patch removes the block-local i. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index f626a63..aac59c0 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -934,7 +934,6 @@ void rndis_deregister(struct rndis_params *params) #ifdef CONFIG_USB_GADGET_DEBUG_FILES { - u8 i; char name[20]; sprintf(name, NAME_TEMPLATE, i); -- cgit v0.10.2 From 81dff8692865292aa70ec3fd93489ae9f33f709e Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 18 May 2015 17:40:04 +0200 Subject: usb: gadget: rndis: use signed type for a signed value rndis_get_nr() returns either a non-negative value on success or a negative value on failure. In case of failure an error code is returned to the caller of rndis_register(). If the "i" is unsigned, the information about error from rndis_get_nr() is lost. If there is no error but rndis_get_nr() returns a value greater than 256 the least significant bits of i are zero effectively limiting the number of configs to 256. This patch fixes that. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index aac59c0..70d3917 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -872,7 +872,7 @@ static inline void rndis_put_nr(int nr) struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v) { struct rndis_params *params; - u8 i; + int i; if (!resp_avail) return ERR_PTR(-EINVAL); @@ -923,7 +923,7 @@ EXPORT_SYMBOL_GPL(rndis_register); void rndis_deregister(struct rndis_params *params) { - u8 i; + int i; pr_debug("%s:\n", __func__); diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h index 194abb1..ef92eb6 100644 --- a/drivers/usb/gadget/function/rndis.h +++ b/drivers/usb/gadget/function/rndis.h @@ -177,7 +177,7 @@ typedef struct rndis_resp_t typedef struct rndis_params { - u8 confignr; + int confignr; u8 used; u16 saved_filter; enum rndis_state state; -- cgit v0.10.2 From 5972edbd8386020347656a37dc014ae838a0adff Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 12 May 2015 16:28:20 -0700 Subject: Documentation: devicetree: add Broadcom SATA PHY binding For 28nm STB chips, based on BCM7445. Signed-off-by: Brian Norris Signed-off-by: Kishon Vijay Abraham I diff --git a/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt b/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt new file mode 100644 index 0000000..7f81ef9 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt @@ -0,0 +1,40 @@ +* Broadcom SATA3 PHY for STB + +Required properties: +- compatible: should be one or more of + "brcm,bcm7445-sata-phy" + "brcm,phy-sata3" +- address-cells: should be 1 +- size-cells: should be 0 +- reg: register range for the PHY PCB interface +- reg-names: should be "phy" + +Sub-nodes: + Each port's PHY should be represented as a sub-node. + +Sub-nodes required properties: +- reg: the PHY number +- phy-cells: generic PHY binding; must be 0 +Optional: +- brcm,enable-ssc: use spread spectrum clocking (SSC) on this port + + +Example: + + sata-phy@f0458100 { + compatible = "brcm,bcm7445-sata-phy", "brcm,phy-sata3"; + reg = <0xf0458100 0x1e00>, <0xf045804c 0x10>; + reg-names = "phy"; + #address-cells = <1>; + #size-cells = <0>; + + sata-phy@0 { + reg = <0>; + #phy-cells = <0>; + }; + + sata-phy@1 { + reg = <1>; + #phy-cells = <0>; + }; + }; -- cgit v0.10.2 From 0d486806ffd0de672f151763b38c19c0a357bf56 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 20 May 2015 17:18:40 -0700 Subject: phy: add Broadcom SATA3 PHY driver for Broadcom STB SoCs Supports up to two ports which can each be powered on/off and configured independently. Signed-off-by: Brian Norris Signed-off-by: Kishon Vijay Abraham I diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index a53bd5b..36788b6 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -309,4 +309,13 @@ config PHY_QCOM_UFS help Support for UFS PHY on QCOM chipsets. +config PHY_BRCMSTB_SATA + tristate "Broadcom STB SATA PHY driver" + depends on ARCH_BRCMSTB + depends on OF + select GENERIC_PHY + help + Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs. + Likely useful only with CONFIG_SATA_BRCMSTB enabled. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f126251..c61f3fd 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -40,3 +40,4 @@ obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o +obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o diff --git a/drivers/phy/phy-brcmstb-sata.c b/drivers/phy/phy-brcmstb-sata.c new file mode 100644 index 0000000..b7e303d --- /dev/null +++ b/drivers/phy/phy-brcmstb-sata.c @@ -0,0 +1,216 @@ +/* + * Broadcom SATA3 AHCI Controller PHY Driver + * + * Copyright © 2009-2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SATA_MDIO_BANK_OFFSET 0x23c +#define SATA_MDIO_REG_OFFSET(ofs) ((ofs) * 4) +#define SATA_MDIO_REG_SPACE_SIZE 0x1000 +#define SATA_MDIO_REG_LENGTH 0x1f00 + +#define MAX_PORTS 2 + +/* Register offset between PHYs in PCB space */ +#define SATA_MDIO_REG_SPACE_SIZE 0x1000 + +struct brcm_sata_port { + int portnum; + struct phy *phy; + struct brcm_sata_phy *phy_priv; + bool ssc_en; +}; + +struct brcm_sata_phy { + struct device *dev; + void __iomem *phy_base; + + struct brcm_sata_port phys[MAX_PORTS]; +}; + +enum sata_mdio_phy_regs_28nm { + PLL_REG_BANK_0 = 0x50, + PLL_REG_BANK_0_PLLCONTROL_0 = 0x81, + + TXPMD_REG_BANK = 0x1a0, + TXPMD_CONTROL1 = 0x81, + TXPMD_CONTROL1_TX_SSC_EN_FRC = BIT(0), + TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL = BIT(1), + TXPMD_TX_FREQ_CTRL_CONTROL1 = 0x82, + TXPMD_TX_FREQ_CTRL_CONTROL2 = 0x83, + TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK = 0x3ff, + TXPMD_TX_FREQ_CTRL_CONTROL3 = 0x84, + TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff, +}; + +static inline void __iomem *brcm_sata_phy_base(struct brcm_sata_port *port) +{ + struct brcm_sata_phy *priv = port->phy_priv; + + return priv->phy_base + (port->portnum * SATA_MDIO_REG_SPACE_SIZE); +} + +static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs, + u32 msk, u32 value) +{ + u32 tmp; + + writel(bank, addr + SATA_MDIO_BANK_OFFSET); + tmp = readl(addr + SATA_MDIO_REG_OFFSET(ofs)); + tmp = (tmp & msk) | value; + writel(tmp, addr + SATA_MDIO_REG_OFFSET(ofs)); +} + +/* These defaults were characterized by H/W group */ +#define FMIN_VAL_DEFAULT 0x3df +#define FMAX_VAL_DEFAULT 0x3df +#define FMAX_VAL_SSC 0x83 + +static void brcm_sata_cfg_ssc_28nm(struct brcm_sata_port *port) +{ + void __iomem *base = brcm_sata_phy_base(port); + struct brcm_sata_phy *priv = port->phy_priv; + u32 tmp; + + /* override the TX spread spectrum setting */ + tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC; + brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp); + + /* set fixed min freq */ + brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2, + ~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK, + FMIN_VAL_DEFAULT); + + /* set fixed max freq depending on SSC config */ + if (port->ssc_en) { + dev_info(priv->dev, "enabling SSC on port %d\n", port->portnum); + tmp = FMAX_VAL_SSC; + } else { + tmp = FMAX_VAL_DEFAULT; + } + + brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3, + ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp); +} + +static int brcm_sata_phy_init(struct phy *phy) +{ + struct brcm_sata_port *port = phy_get_drvdata(phy); + + brcm_sata_cfg_ssc_28nm(port); + + return 0; +} + +static struct phy_ops phy_ops_28nm = { + .init = brcm_sata_phy_init, + .owner = THIS_MODULE, +}; + +static const struct of_device_id brcm_sata_phy_of_match[] = { + { .compatible = "brcm,bcm7445-sata-phy" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); + +static int brcm_sata_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node, *child; + struct brcm_sata_phy *priv; + struct resource *res; + struct phy_provider *provider; + int count = 0; + + if (of_get_child_count(dn) == 0) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + dev_set_drvdata(dev, priv); + priv->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); + priv->phy_base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->phy_base)) + return PTR_ERR(priv->phy_base); + + for_each_available_child_of_node(dn, child) { + unsigned int id; + struct brcm_sata_port *port; + + if (of_property_read_u32(child, "reg", &id)) { + dev_err(dev, "missing reg property in node %s\n", + child->name); + return -EINVAL; + } + + if (id >= MAX_PORTS) { + dev_err(dev, "invalid reg: %u\n", id); + return -EINVAL; + } + if (priv->phys[id].phy) { + dev_err(dev, "already registered port %u\n", id); + return -EINVAL; + } + + port = &priv->phys[id]; + port->portnum = id; + port->phy_priv = priv; + port->phy = devm_phy_create(dev, child, &phy_ops_28nm); + port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc"); + if (IS_ERR(port->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(port->phy); + } + + phy_set_drvdata(port->phy, port); + count++; + } + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "could not register PHY provider\n"); + return PTR_ERR(provider); + } + + dev_info(dev, "registered %d port(s)\n", count); + + return 0; +} + +static struct platform_driver brcm_sata_phy_driver = { + .probe = brcm_sata_phy_probe, + .driver = { + .of_match_table = brcm_sata_phy_of_match, + .name = "brcmstb-sata-phy", + } +}; +module_platform_driver(brcm_sata_phy_driver); + +MODULE_DESCRIPTION("Broadcom STB SATA PHY driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marc Carino"); +MODULE_AUTHOR("Brian Norris"); +MODULE_ALIAS("platform:phy-brcmstb-sata"); -- cgit v0.10.2 From 672bfdaa310004368a0d493478e2a40f2f2f914f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 22 May 2015 13:06:35 +0200 Subject: usb: renesas_usbhs: avoid uninitialized variable use After the renesas_usbhs driver is enabled in ARM multi_v7_defconfig, we now get a new warning: renesas_usbhs/mod.c: In function 'usbhs_interrupt': renesas_usbhs/mod.c:246:7: warning: 'intenb1' may be used uninitialized in this function [-Wmaybe-uninitialized] gcc correctly points to a problem here, for the case that the device is in host mode, we use the intenb1 variable without having assigned it first. The state->intsts1 has a similar problem, but gcc cannot know that. This avoids the problem by initializing both sides of the comparison to zero when we don't read them from the respective registers. Signed-off-by: Arnd Bergmann Fixes: 88a25e02f3 ("usb: renesas_usbhs: Add access control for INTSTS1 and INTENB1 register") Acked-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index e5ce6e6..d4be5d5 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -223,6 +223,8 @@ static int usbhs_status_get_each_irq(struct usbhs_priv *priv, if (usbhs_mod_is_host(priv)) { state->intsts1 = usbhs_read(priv, INTSTS1); intenb1 = usbhs_read(priv, INTENB1); + } else { + state->intsts1 = intenb1 = 0; } /* mask */ -- cgit v0.10.2 From ca07e1c1e4a6aed2b2576256b1fdc53a9f87bb40 Mon Sep 17 00:00:00 2001 From: Ramneek Mehresh Date: Thu, 14 May 2015 19:04:46 +0530 Subject: drivers:usb:fsl:Make fsl ehci drv an independent driver module Make Freescale EHCI driver an independent entity from ehci-hcd.c. This involves - using module_init/module_exit functions - using overrides structure - some necessary code cleanup Signed-off-by: Ramneek Mehresh Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 197a6a3..547cee8 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -137,7 +137,7 @@ config XPS_USB_HCD_XILINX devices only. config USB_EHCI_FSL - bool "Support for Freescale PPC on-chip EHCI USB controller" + tristate "Support for Freescale PPC on-chip EHCI USB controller" depends on FSL_SOC select USB_EHCI_ROOT_HUB_TT select USB_FSL_MPH_DR_OF if OF diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 972a74a..754efaa 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o +obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o obj-$(CONFIG_USB_FUSBH200_HCD) += fusbh200-hcd.o diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index ab4eee3..5352e74 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -1,6 +1,6 @@ /* * Copyright 2005-2009 MontaVista Software, Inc. - * Copyright 2008,2012 Freescale Semiconductor, Inc. + * Copyright 2008,2012,2015 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -24,29 +24,38 @@ */ #include +#include #include #include #include #include +#include +#include +#include +#include #include #include +#include "ehci.h" #include "ehci-fsl.h" +#define DRIVER_DESC "Freescale EHCI Host controller driver" +#define DRV_NAME "ehci-fsl" + +static struct hc_driver __read_mostly fsl_ehci_hc_driver; + /* configure so an HC device and id are always provided */ /* always called with process context; sleeping is OK */ -/** - * usb_hcd_fsl_probe - initialize FSL-based HCDs - * @drvier: Driver to be used for this HCD +/* + * fsl_ehci_drv_probe - initialize FSL-based HCDs * @pdev: USB Host Controller being probed * Context: !in_interrupt() * * Allocates basic resources for this USB host controller. * */ -static int usb_hcd_fsl_probe(const struct hc_driver *driver, - struct platform_device *pdev) +static int fsl_ehci_drv_probe(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata; struct usb_hcd *hcd; @@ -86,7 +95,8 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, } irq = res->start; - hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + hcd = usb_create_hcd(&fsl_ehci_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); if (!hcd) { retval = -ENOMEM; goto err1; @@ -159,38 +169,6 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, return retval; } -/* may be called without controller electrically present */ -/* may be called with controller, bus, and devices active */ - -/** - * usb_hcd_fsl_remove - shutdown processing for FSL-based HCDs - * @dev: USB Host Controller being removed - * Context: !in_interrupt() - * - * Reverses the effect of usb_hcd_fsl_probe(). - * - */ -static void usb_hcd_fsl_remove(struct usb_hcd *hcd, - struct platform_device *pdev) -{ - struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); - - if (!IS_ERR_OR_NULL(hcd->usb_phy)) { - otg_set_host(hcd->usb_phy->otg, NULL); - usb_put_phy(hcd->usb_phy); - } - - usb_remove_hcd(hcd); - - /* - * do platform specific un-initialization: - * release iomux pins, disable clock, etc. - */ - if (pdata->exit) - pdata->exit(pdev); - usb_put_hcd(hcd); -} - static int ehci_fsl_setup_phy(struct usb_hcd *hcd, enum fsl_usb2_phy_modes phy_mode, unsigned int port_offset) @@ -636,79 +614,77 @@ static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port) #define ehci_start_port_reset NULL #endif /* CONFIG_USB_OTG */ +static struct ehci_driver_overrides ehci_fsl_overrides __initdata = { + .extra_priv_size = sizeof(struct ehci_fsl), + .reset = ehci_fsl_setup, +}; -static const struct hc_driver ehci_fsl_hc_driver = { - .description = hcd_name, - .product_desc = "Freescale On-Chip EHCI Host Controller", - .hcd_priv_size = sizeof(struct ehci_fsl), +/** + * fsl_ehci_drv_remove - shutdown processing for FSL-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_fsl_probe(). + * + */ - /* - * generic hardware linkage - */ - .irq = ehci_irq, - .flags = HCD_USB2 | HCD_MEMORY | HCD_BH, +static int fsl_ehci_drv_remove(struct platform_device *pdev) +{ + struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct usb_hcd *hcd = platform_get_drvdata(pdev); - /* - * basic lifecycle operations - */ - .reset = ehci_fsl_setup, - .start = ehci_run, - .stop = ehci_stop, - .shutdown = ehci_shutdown, + if (!IS_ERR_OR_NULL(hcd->usb_phy)) { + otg_set_host(hcd->usb_phy->otg, NULL); + usb_put_phy(hcd->usb_phy); + } - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, + usb_remove_hcd(hcd); /* - * scheduling support + * do platform specific un-initialization: + * release iomux pins, disable clock, etc. */ - .get_frame_number = ehci_get_frame, + if (pdata->exit) + pdata->exit(pdev); + usb_put_hcd(hcd); - /* - * root hub support - */ - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, - .start_port_reset = ehci_start_port_reset, - .relinquish_port = ehci_relinquish_port, - .port_handed_over = ehci_port_handed_over, - - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, + return 0; +} + +static struct platform_driver ehci_fsl_driver = { + .probe = fsl_ehci_drv_probe, + .remove = fsl_ehci_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "fsl-ehci", + .pm = EHCI_FSL_PM_OPS, + }, }; -static int ehci_fsl_drv_probe(struct platform_device *pdev) +static int __init ehci_fsl_init(void) { if (usb_disabled()) return -ENODEV; - /* FIXME we only want one one probe() not two */ - return usb_hcd_fsl_probe(&ehci_fsl_hc_driver, pdev); -} + pr_info(DRV_NAME ": " DRIVER_DESC "\n"); -static int ehci_fsl_drv_remove(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); + ehci_init_driver(&fsl_ehci_hc_driver, &ehci_fsl_overrides); - /* FIXME we only want one one remove() not two */ - usb_hcd_fsl_remove(hcd, pdev); - return 0; + fsl_ehci_hc_driver.product_desc = + "Freescale On-Chip EHCI Host Controller"; + fsl_ehci_hc_driver.start_port_reset = ehci_start_port_reset; + + + return platform_driver_register(&ehci_fsl_driver); } +module_init(ehci_fsl_init); -MODULE_ALIAS("platform:fsl-ehci"); +static void __exit ehci_fsl_cleanup(void) +{ + platform_driver_unregister(&ehci_fsl_driver); +} +module_exit(ehci_fsl_cleanup); -static struct platform_driver ehci_fsl_driver = { - .probe = ehci_fsl_drv_probe, - .remove = ehci_fsl_drv_remove, - .shutdown = usb_hcd_platform_shutdown, - .driver = { - .name = "fsl-ehci", - .pm = EHCI_FSL_PM_OPS, - }, -}; +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f4d88df..9dd161c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1250,11 +1250,6 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR (DRIVER_AUTHOR); MODULE_LICENSE ("GPL"); -#ifdef CONFIG_USB_EHCI_FSL -#include "ehci-fsl.c" -#define PLATFORM_DRIVER ehci_fsl_driver -#endif - #ifdef CONFIG_USB_EHCI_SH #include "ehci-sh.c" #define PLATFORM_DRIVER ehci_hcd_sh_driver -- cgit v0.10.2 From 40f2f2a3255314fc826325f49add1a2fbfc811bd Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 16 May 2015 17:17:40 +0200 Subject: USB: ehci-platform: support EHCIs with transaction translator Some EHCI controllers have a Transaction Translator built into the root hub. Support this feature in device tree when using the ehci-platform driver by adding a feature flag for it. This is needed to get USB working on NXP LPC18xx/43xx platforms. Signed-off-by: Joachim Eastwood Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/usb/usb-ehci.txt b/Documentation/devicetree/bindings/usb/usb-ehci.txt index 0b04fdf..a12d601 100644 --- a/Documentation/devicetree/bindings/usb/usb-ehci.txt +++ b/Documentation/devicetree/bindings/usb/usb-ehci.txt @@ -13,6 +13,8 @@ Optional properties: - big-endian-desc : boolean, set this for hcds with big-endian descriptors - big-endian : boolean, for hcds with big-endian-regs + big-endian-desc - needs-reset-on-resume : boolean, set this to force EHCI reset after resume + - has-transaction-translator : boolean, set this if EHCI have a Transaction + Translator built into the root hub. - clocks : a list of phandle + clock specifier pairs - phys : phandle + phy specifier pair - phy-names : "usb" diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index d8a75a5..ba07f16 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -202,6 +202,10 @@ static int ehci_platform_probe(struct platform_device *dev) "needs-reset-on-resume")) pdata->reset_on_resume = 1; + if (of_property_read_bool(dev->dev.of_node, + "has-transaction-translator")) + pdata->has_tt = 1; + priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, "phys", "#phy-cells"); priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1; -- cgit v0.10.2 From 83ed07c5db71bc02bd646d6eb60b48908235cdf9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 18 May 2015 15:29:51 +0300 Subject: USB: devio: fix a condition in async_completed() Static checkers complain that the current condition is never true. It seems pretty likely that it's a typo and "URB" was intended instead of "USB". Fixes: 3d97ff63f899 ('usbdevfs: Use scatter-gather lists for large bulk transfers') Signed-off-by: Dan Carpenter Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 4b0448c..986abde 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -513,7 +513,7 @@ static void async_completed(struct urb *urb) snoop(&urb->dev->dev, "urb complete\n"); snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, as->status, COMPLETE, NULL, 0); - if ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_IN) + if ((urb->transfer_flags & URB_DIR_MASK) == URB_DIR_IN) snoop_urb_data(urb, urb->actual_length); if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET && -- cgit v0.10.2 From 6cb4f4df686e6515f43d9fec3f43226d408999b3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 18 May 2015 17:34:11 +0200 Subject: USB: cdc-acm: use idr to manage minor numbers Use the idr-interface rather than a static table to manage minor-number allocations. This allows us to easily switch over to fully dynamic minor allocations when the TTY-layer can handle that. Signed-off-by: Johan Hovold Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 5c8f581..877b637 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include "cdc-acm.h" @@ -56,27 +57,27 @@ static struct usb_driver acm_driver; static struct tty_driver *acm_tty_driver; -static struct acm *acm_table[ACM_TTY_MINORS]; -static DEFINE_MUTEX(acm_table_lock); +static DEFINE_IDR(acm_minors); +static DEFINE_MUTEX(acm_minors_lock); static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old); /* - * acm_table accessors + * acm_minors accessors */ /* - * Look up an ACM structure by index. If found and not disconnected, increment + * Look up an ACM structure by minor. If found and not disconnected, increment * its refcount and return it with its mutex held. */ -static struct acm *acm_get_by_index(unsigned index) +static struct acm *acm_get_by_minor(unsigned int minor) { struct acm *acm; - mutex_lock(&acm_table_lock); - acm = acm_table[index]; + mutex_lock(&acm_minors_lock); + acm = idr_find(&acm_minors, minor); if (acm) { mutex_lock(&acm->mutex); if (acm->disconnected) { @@ -87,7 +88,7 @@ static struct acm *acm_get_by_index(unsigned index) mutex_unlock(&acm->mutex); } } - mutex_unlock(&acm_table_lock); + mutex_unlock(&acm_minors_lock); return acm; } @@ -98,14 +99,9 @@ static int acm_alloc_minor(struct acm *acm) { int minor; - mutex_lock(&acm_table_lock); - for (minor = 0; minor < ACM_TTY_MINORS; minor++) { - if (!acm_table[minor]) { - acm_table[minor] = acm; - break; - } - } - mutex_unlock(&acm_table_lock); + mutex_lock(&acm_minors_lock); + minor = idr_alloc(&acm_minors, acm, 0, ACM_TTY_MINORS, GFP_KERNEL); + mutex_unlock(&acm_minors_lock); return minor; } @@ -113,9 +109,9 @@ static int acm_alloc_minor(struct acm *acm) /* Release the minor number associated with 'acm'. */ static void acm_release_minor(struct acm *acm) { - mutex_lock(&acm_table_lock); - acm_table[acm->minor] = NULL; - mutex_unlock(&acm_table_lock); + mutex_lock(&acm_minors_lock); + idr_remove(&acm_minors, acm->minor); + mutex_unlock(&acm_minors_lock); } /* @@ -497,7 +493,7 @@ static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty) dev_dbg(tty->dev, "%s\n", __func__); - acm = acm_get_by_index(tty->index); + acm = acm_get_by_minor(tty->index); if (!acm) return -ENODEV; @@ -1316,7 +1312,7 @@ made_compressed_probe: goto alloc_fail; minor = acm_alloc_minor(acm); - if (minor == ACM_TTY_MINORS) { + if (minor < 0) { dev_err(&intf->dev, "no more free acm devices\n"); kfree(acm); return -ENODEV; -- cgit v0.10.2 From 65c35dd59002798cc87212d23ff3c87fd3a5d721 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 18 May 2015 17:34:12 +0200 Subject: USB: cdc-acm: add support for up to 256 devices Increase the minor range to enable support for up to 256 devices. Some people are hitting the current 32 device limit. Hopefully 256 minors will be enough for while still. Signed-off-by: Johan Hovold Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index ffeb3c8..a9e68ce 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -19,7 +19,7 @@ */ #define ACM_TTY_MAJOR 166 -#define ACM_TTY_MINORS 32 +#define ACM_TTY_MINORS 256 /* * Requests. -- cgit v0.10.2 From 2cfef79ddce42e9604293424381b2e59913f600c Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 18 May 2015 19:59:37 +0200 Subject: cdc-acm: use swap() in acm_probe() Use kernel.h macro definition. Signed-off-by: Fabian Frederick Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 877b637..eb22a06 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1263,12 +1263,9 @@ skip_normal_probe: != CDC_DATA_INTERFACE_TYPE) { if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) { - struct usb_interface *t; dev_dbg(&intf->dev, "Your device has switched interfaces.\n"); - t = control_interface; - control_interface = data_interface; - data_interface = t; + swap(control_interface, data_interface); } else { return -EINVAL; } @@ -1297,12 +1294,9 @@ skip_normal_probe: /* workaround for switched endpoints */ if (!usb_endpoint_dir_in(epread)) { /* descriptors are swapped */ - struct usb_endpoint_descriptor *t; dev_dbg(&intf->dev, "The data interface has switched endpoints\n"); - t = epread; - epread = epwrite; - epwrite = t; + swap(epread, epwrite); } made_compressed_probe: dev_dbg(&intf->dev, "interfaces are valid\n"); -- cgit v0.10.2 From 591fa9dd3fee3de8c729febca395beb75c8ee819 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 29 Mar 2015 12:50:47 +0200 Subject: usb: musb: Add pre and post root port reset end callbacks The sunxi otg phy has a bug where it wrongly detects a high speed squelch when reset on the root port gets de-asserted with a lo-speed device. The workaround for this is to disable squelch detect before de-asserting reset, and re-enabling it after the reset de-assert is done. Signed-off-by: Hans de Goede Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index c7a0d93..7117226 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -167,6 +167,8 @@ struct musb_io; * @vbus_status: returns vbus status if possible * @set_vbus: forces vbus status * @adjust_channel_params: pre check for standard dma channel_program func + * @pre_root_reset_end: called before the root usb port reset flag gets cleared + * @post_root_reset_end: called after the root usb port reset flag gets cleared */ struct musb_platform_ops { @@ -210,6 +212,8 @@ struct musb_platform_ops { int (*adjust_channel_params)(struct dma_channel *channel, u16 packet_sz, u8 *mode, dma_addr_t *dma_addr, u32 *len); + void (*pre_root_reset_end)(struct musb *musb); + void (*post_root_reset_end)(struct musb *musb); }; /* @@ -595,4 +599,16 @@ static inline int musb_platform_exit(struct musb *musb) return musb->ops->exit(musb); } +static inline void musb_platform_pre_root_reset_end(struct musb *musb) +{ + if (musb->ops->pre_root_reset_end) + musb->ops->pre_root_reset_end(musb); +} + +static inline void musb_platform_post_root_reset_end(struct musb *musb) +{ + if (musb->ops->post_root_reset_end) + musb->ops->post_root_reset_end(musb); +} + #endif /* __MUSB_CORE_H__ */ diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 86c4b53..30842bc 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -195,8 +195,10 @@ void musb_port_reset(struct musb *musb, bool do_reset) msecs_to_jiffies(50)); } else { dev_dbg(musb->controller, "root port reset stopped\n"); + musb_platform_pre_root_reset_end(musb); musb_writeb(mbase, MUSB_POWER, power & ~MUSB_POWER_RESET); + musb_platform_post_root_reset_end(musb); power = musb_readb(mbase, MUSB_POWER); if (power & MUSB_POWER_HSMODE) { -- cgit v0.10.2 From 891b1dc022955d36cf4c0f42d383226a930db7ed Mon Sep 17 00:00:00 2001 From: Subbaraya Sundeep Bhatta Date: Thu, 21 May 2015 15:46:47 +0530 Subject: usb: dwc3: gadget: return error if command sent to DGCMD register fails We need to return error to caller if command is not sent to controller succesfully. Signed-off-by: Subbaraya Sundeep Bhatta Fixes: b09bb64239c8 (usb: dwc3: gadget: implement Global Command support) Cc: #v3.5+ Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8946c32..fcbe120 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -291,6 +291,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) dwc3_trace(trace_dwc3_gadget, "Command Complete --> %d", DWC3_DGCMD_STATUS(reg)); + if (DWC3_DGCMD_STATUS(reg)) + return -EINVAL; return 0; } -- cgit v0.10.2 From 76e838c9f7765f9a6205b4d558d75a66104bc60d Mon Sep 17 00:00:00 2001 From: Subbaraya Sundeep Bhatta Date: Thu, 21 May 2015 15:46:48 +0530 Subject: usb: dwc3: gadget: return error if command sent to DEPCMD register fails We need to return error to caller if command is not sent to controller succesfully. Signed-off-by: Subbaraya Sundeep Bhatta Fixes: 72246da40f37 (usb: Introduce DesignWare USB3 DRD Driver) Cc: Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index fcbe120..55b5edc 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -330,6 +330,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, dwc3_trace(trace_dwc3_gadget, "Command Complete --> %d", DWC3_DEPCMD_STATUS(reg)); + if (DWC3_DEPCMD_STATUS(reg)) + return -EINVAL; return 0; } -- cgit v0.10.2 From f14e9ad17f46051b02bffffac2036486097de19e Mon Sep 17 00:00:00 2001 From: Rui Miguel Silva Date: Wed, 20 May 2015 14:52:40 +0100 Subject: usb: gadget: f_fs: add extra check before unregister_gadget_item ffs_closed can race with configfs_rmdir which will call config_item_release, so add an extra check to avoid calling the unregister_gadget_item with an null gadget item. Signed-off-by: Rui Miguel Silva Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 6bdb570..e455eee 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -3433,6 +3433,7 @@ done: static void ffs_closed(struct ffs_data *ffs) { struct ffs_dev *ffs_obj; + struct f_fs_opts *opts; ENTER(); ffs_dev_lock(); @@ -3446,8 +3447,13 @@ static void ffs_closed(struct ffs_data *ffs) if (ffs_obj->ffs_closed_callback) ffs_obj->ffs_closed_callback(ffs); - if (!ffs_obj->opts || ffs_obj->opts->no_configfs - || !ffs_obj->opts->func_inst.group.cg_item.ci_parent) + if (ffs_obj->opts) + opts = ffs_obj->opts; + else + goto done; + + if (opts->no_configfs || !opts->func_inst.group.cg_item.ci_parent + || !atomic_read(&opts->func_inst.group.cg_item.ci_kref.refcount)) goto done; unregister_gadget_item(ffs_obj->opts-> -- cgit v0.10.2 From e0213bc5467ca5fe44ab04527f0e47998f30c046 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 18 May 2015 20:04:14 +0900 Subject: usb: renesas_usbhs: Change USBHS_TYPE_R8A779x to USBHS_TYPE_RCAR_GEN2 Since the HSUSB controllers of R-Car Gen2 are the same specification (they have 16 pipes and usb-dmac), this patch changes USBHS_TYPE_R8A7790 and USBHS_TYPE_R8A7791 to USBHS_TYPE_RCAR_GEN2. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 0f7e850..b56bb9d 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -466,11 +466,11 @@ static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) static const struct of_device_id usbhs_of_match[] = { { .compatible = "renesas,usbhs-r8a7790", - .data = (void *)USBHS_TYPE_R8A7790, + .data = (void *)USBHS_TYPE_RCAR_GEN2, }, { .compatible = "renesas,usbhs-r8a7791", - .data = (void *)USBHS_TYPE_R8A7791, + .data = (void *)USBHS_TYPE_RCAR_GEN2, }, { }, }; @@ -497,14 +497,8 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) if (gpio > 0) dparam->enable_gpio = gpio; - switch (dparam->type) { - case USBHS_TYPE_R8A7790: - case USBHS_TYPE_R8A7791: + if (dparam->type == USBHS_TYPE_RCAR_GEN2) dparam->has_usb_dmac = 1; - break; - default: - break; - } return info; } @@ -559,8 +553,7 @@ static int usbhs_probe(struct platform_device *pdev) sizeof(struct renesas_usbhs_driver_param)); switch (priv->dparam.type) { - case USBHS_TYPE_R8A7790: - case USBHS_TYPE_R8A7791: + case USBHS_TYPE_RCAR_GEN2: priv->pfunc = usbhs_rcar2_ops; if (!priv->dparam.pipe_type) { priv->dparam.pipe_type = usbhsc_new_pipe_type; diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index f06529c..3dd5a78 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -169,8 +169,7 @@ struct renesas_usbhs_driver_param { #define USBHS_USB_DMAC_XFER_SIZE 32 /* hardcode the xfer size */ }; -#define USBHS_TYPE_R8A7790 1 -#define USBHS_TYPE_R8A7791 2 +#define USBHS_TYPE_RCAR_GEN2 1 /* * option: -- cgit v0.10.2 From af6e613bb1b60fcbfe48c893b76c104c8952b599 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 18 May 2015 20:04:15 +0900 Subject: usb: renesas_usbhs: Add support for R-Car E2 This patch adds a compatible string to support for R-Car E2. Signed-off-by: Yoshihiro Shimoda Acked-by: Geert Uytterhoeven " in patch 2 Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt index ddbe304..64a4ca6 100644 --- a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt +++ b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt @@ -4,6 +4,7 @@ Required properties: - compatible: Must contain one of the following: - "renesas,usbhs-r8a7790" - "renesas,usbhs-r8a7791" + - "renesas,usbhs-r8a7794" - reg: Base address and length of the register for the USBHS - interrupts: Interrupt specifier for the USBHS - clocks: A list of phandle + clock specifier pairs diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index b56bb9d..e8bf408 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -472,6 +472,10 @@ static const struct of_device_id usbhs_of_match[] = { .compatible = "renesas,usbhs-r8a7791", .data = (void *)USBHS_TYPE_RCAR_GEN2, }, + { + .compatible = "renesas,usbhs-r8a7794", + .data = (void *)USBHS_TYPE_RCAR_GEN2, + }, { }, }; MODULE_DEVICE_TABLE(of, usbhs_of_match); -- cgit v0.10.2 From 25d40ee8189531d9df3ff10e25ddb92b5f075343 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Sat, 16 May 2015 22:33:30 +0200 Subject: usb: gadget: net2280: fix ep_cfg for defect7374 ep_cfg.IN_EP_ENABLE is only valid in advance mode. Tested-by: Ricardo Ribalda Delgado Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 9871b90..62bc157 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -1860,8 +1860,8 @@ static void defect7374_enable_data_eps_zero(struct net2280 *dev) tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) | (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) | ((dev->enhanced_mode) ? - BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) | - BIT(IN_ENDPOINT_ENABLE)); + BIT(OUT_ENDPOINT_ENABLE) | BIT(IN_ENDPOINT_ENABLE) : + BIT(ENDPOINT_ENABLE))); for (i = 1; i < 5; i++) writel(tmp, &dev->ep[i].cfg->ep_cfg); -- cgit v0.10.2 From e6ac4bb0590d6482f48a86a10884a9f2eb66c111 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Sat, 16 May 2015 22:33:31 +0200 Subject: usb: gadget: net2280: reset sequence number on ep enable Sequence number can be out of sync if endpoint is disabled after some data transfers and enabled again. Reset it to stay in sync with host. Tested-by: Ricardo Ribalda Delgado Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 62bc157..a91da36 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -123,6 +123,8 @@ static char *type_string(u8 bmAttributes) #define valid_bit cpu_to_le32(BIT(VALID_BIT)) #define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE)) +static void ep_clear_seqnum(struct net2280_ep *ep); + /*-------------------------------------------------------------------------*/ static inline void enable_pciirqenb(struct net2280_ep *ep) { @@ -256,6 +258,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); } + if (dev->quirks & PLX_SUPERSPEED) + ep_clear_seqnum(ep); writel(tmp, &ep->cfg->ep_cfg); /* enable irqs */ -- cgit v0.10.2 From 3fc0a7c3d3539a4e57b249d1fb5b2ab80c075174 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Sat, 16 May 2015 22:33:32 +0200 Subject: usb: gadget: net2280: unconditionally reset dma in usb_reset If ep->dma is set, abort_dma() takes care of dma clean-up. If ep->dma is not set, unconditionally reset dma channel. Tested-by: Ricardo Ribalda Delgado Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index a91da36..07e0dba 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -1979,9 +1979,15 @@ static void usb_reset_338x(struct net2280 *dev) /* clear old dma and irq state */ for (tmp = 0; tmp < 4; tmp++) { struct net2280_ep *ep = &dev->ep[tmp + 1]; + struct net2280_dma_regs __iomem *dma; - if (ep->dma) + if (ep->dma) { abort_dma(ep); + } else { + dma = &dev->dma[tmp]; + writel(BIT(DMA_ABORT), &dma->dmastat); + writel(0, &dma->dmactl); + } } writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1); -- cgit v0.10.2 From ea86507fd2ebcf8bfbaf92db279331c9c600e0d2 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Sat, 16 May 2015 22:33:33 +0200 Subject: usb: gadget: net2280: don't set ep_cfg.direction bit USB3380 ep_cfg.direction bit is reserved in enhanced mode. Don't set it. Tested-by: Ricardo Ribalda Delgado Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 07e0dba..4a90ae6 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -232,8 +232,6 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) if (dev->enhanced_mode && ep->is_in) { tmp <<= IN_ENDPOINT_TYPE; tmp |= BIT(IN_ENDPOINT_ENABLE); - /* Not applicable to Legacy */ - tmp |= BIT(ENDPOINT_DIRECTION); } else { tmp <<= OUT_ENDPOINT_TYPE; tmp |= BIT(OUT_ENDPOINT_ENABLE); -- cgit v0.10.2 From 463e104fb0ff1374c52bb0a8e0029537799192ac Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Sat, 16 May 2015 22:33:34 +0200 Subject: usb: gadget: net2280: set all byte enables on start Default 0 value can result in unintentional zlp for IN endpoints. Tested-by: Ricardo Ribalda Delgado Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 4a90ae6..878a98e 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -238,6 +238,7 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) tmp |= (ep->is_in << ENDPOINT_DIRECTION); } + tmp |= (4 << ENDPOINT_BYTE_COUNT); tmp |= usb_endpoint_num(desc); tmp |= (ep->ep.maxburst << MAX_BURST_SIZE); } -- cgit v0.10.2 From a09e23f53e2c14a65a3b14a00060fea163081e1f Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Sat, 16 May 2015 22:33:35 +0200 Subject: usb: gadget: net2280: check interrupts for all endpoints USB3380 in enhanced mode has 4 IN and 4 OUT endpoints. Check interrupts for all of them. Tested-by: Ricardo Ribalda Delgado Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 878a98e..a78a9c0 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -2883,6 +2883,26 @@ next_endpoints3: return; } +static void usb338x_handle_ep_intr(struct net2280 *dev, u32 stat0) +{ + u32 index; + u32 bit; + + for (index = 0; index < ARRAY_SIZE(ep_bit); index++) { + bit = BIT(ep_bit[index]); + + if (!stat0) + break; + + if (!(stat0 & bit)) + continue; + + stat0 &= ~bit; + + handle_ep_small(&dev->ep[index]); + } +} + static void handle_stat0_irqs(struct net2280 *dev, u32 stat) { struct net2280_ep *ep; @@ -3107,20 +3127,31 @@ do_stall: #undef w_length next_endpoints: - /* endpoint data irq ? */ - scratch = stat & 0x7f; - stat &= ~0x7f; - for (num = 0; scratch; num++) { - u32 t; - - /* do this endpoint's FIFO and queue need tending? */ - t = BIT(num); - if ((scratch & t) == 0) - continue; - scratch ^= t; + if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) { + u32 mask = (BIT(ENDPOINT_0_INTERRUPT) | + USB3380_IRQSTAT0_EP_INTR_MASK_IN | + USB3380_IRQSTAT0_EP_INTR_MASK_OUT); + + if (stat & mask) { + usb338x_handle_ep_intr(dev, stat & mask); + stat &= ~mask; + } + } else { + /* endpoint data irq ? */ + scratch = stat & 0x7f; + stat &= ~0x7f; + for (num = 0; scratch; num++) { + u32 t; + + /* do this endpoint's FIFO and queue need tending? */ + t = BIT(num); + if ((scratch & t) == 0) + continue; + scratch ^= t; - ep = &dev->ep[num]; - handle_ep_small(ep); + ep = &dev->ep[num]; + handle_ep_small(ep); + } } if (stat) diff --git a/include/linux/usb/net2280.h b/include/linux/usb/net2280.h index 148b8fa..7251202 100644 --- a/include/linux/usb/net2280.h +++ b/include/linux/usb/net2280.h @@ -168,6 +168,9 @@ struct net2280_regs { #define ENDPOINT_B_INTERRUPT 2 #define ENDPOINT_A_INTERRUPT 1 #define ENDPOINT_0_INTERRUPT 0 +#define USB3380_IRQSTAT0_EP_INTR_MASK_IN (0xF << 17) +#define USB3380_IRQSTAT0_EP_INTR_MASK_OUT (0xF << 1) + u32 irqstat1; #define POWER_STATE_CHANGE_INTERRUPT 27 #define PCI_ARBITER_TIMEOUT_INTERRUPT 26 -- cgit v0.10.2 From c65c4f052bc3b67989bf54914798513685c54988 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Sat, 16 May 2015 22:33:36 +0200 Subject: usb: gadget: net2280: fix use of GPEP in both directions USB3380 enhanced mode allows GPEP to be used in both IN and OUT directions. However, IN and OUT endpoints must use same USB endpoint address (bEndpointAddress). Fix this by setting the ep_cfg.ep_number during initialization and keep it in net2280_enable() Tested-by: Ricardo Ribalda Delgado Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index a78a9c0..779e6fe 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -144,7 +144,9 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { struct net2280 *dev; struct net2280_ep *ep; - u32 max, tmp; + u32 max; + u32 tmp = 0; + u32 type; unsigned long flags; static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 }; int ret = 0; @@ -200,15 +202,29 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) /* set type, direction, address; reset fifo counters */ writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); - tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); - if (tmp == USB_ENDPOINT_XFER_INT) { + + if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) { + tmp = readl(&ep->cfg->ep_cfg); + /* If USB ep number doesn't match hardware ep number */ + if ((tmp & 0xf) != usb_endpoint_num(desc)) { + ret = -EINVAL; + spin_unlock_irqrestore(&dev->lock, flags); + goto print_err; + } + if (ep->is_in) + tmp &= ~USB3380_EP_CFG_MASK_IN; + else + tmp &= ~USB3380_EP_CFG_MASK_OUT; + } + type = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); + if (type == USB_ENDPOINT_XFER_INT) { /* erratum 0105 workaround prevents hs NYET */ if (dev->chiprev == 0100 && dev->gadget.speed == USB_SPEED_HIGH && !(desc->bEndpointAddress & USB_DIR_IN)) writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); - } else if (tmp == USB_ENDPOINT_XFER_BULK) { + } else if (type == USB_ENDPOINT_XFER_BULK) { /* catch some particularly blatant driver bugs */ if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) || (dev->gadget.speed == USB_SPEED_HIGH && max != 512) || @@ -218,10 +234,10 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) goto print_err; } } - ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); + ep->is_iso = (type == USB_ENDPOINT_XFER_ISOC); /* Enable this endpoint */ if (dev->quirks & PLX_LEGACY) { - tmp <<= ENDPOINT_TYPE; + tmp |= type << ENDPOINT_TYPE; tmp |= desc->bEndpointAddress; /* default full fifo lines */ tmp |= (4 << ENDPOINT_BYTE_COUNT); @@ -230,16 +246,17 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) } else { /* In Legacy mode, only OUT endpoints are used */ if (dev->enhanced_mode && ep->is_in) { - tmp <<= IN_ENDPOINT_TYPE; + tmp |= type << IN_ENDPOINT_TYPE; tmp |= BIT(IN_ENDPOINT_ENABLE); } else { - tmp <<= OUT_ENDPOINT_TYPE; + tmp |= type << OUT_ENDPOINT_TYPE; tmp |= BIT(OUT_ENDPOINT_ENABLE); tmp |= (ep->is_in << ENDPOINT_DIRECTION); } tmp |= (4 << ENDPOINT_BYTE_COUNT); - tmp |= usb_endpoint_num(desc); + if (!dev->enhanced_mode) + tmp |= usb_endpoint_num(desc); tmp |= (ep->ep.maxburst << MAX_BURST_SIZE); } @@ -2074,6 +2091,12 @@ static void usb_reinit_338x(struct net2280 *dev) if (dev->enhanced_mode) { ep->cfg = &dev->epregs[ne[i]]; + /* + * Set USB endpoint number, hardware allows same number + * in both directions. + */ + if (i > 0 && i < 5) + writel(ne[i], &ep->cfg->ep_cfg); ep->regs = (struct net2280_ep_regs __iomem *) (((void __iomem *)&dev->epregs[ne[i]]) + ep_reg_addr[i]); diff --git a/include/linux/usb/usb338x.h b/include/linux/usb/usb338x.h index f92eb63..11525d8 100644 --- a/include/linux/usb/usb338x.h +++ b/include/linux/usb/usb338x.h @@ -43,6 +43,10 @@ #define IN_ENDPOINT_TYPE 12 #define OUT_ENDPOINT_ENABLE 10 #define OUT_ENDPOINT_TYPE 8 +#define USB3380_EP_CFG_MASK_IN ((0x3 << IN_ENDPOINT_TYPE) | \ + BIT(IN_ENDPOINT_ENABLE)) +#define USB3380_EP_CFG_MASK_OUT ((0x3 << OUT_ENDPOINT_TYPE) | \ + BIT(OUT_ENDPOINT_ENABLE)) struct usb338x_usb_ext_regs { u32 usbclass; -- cgit v0.10.2 From e9ab4d0ab8f5b1159558b9ab236e408d50962a00 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Sat, 16 May 2015 22:33:37 +0200 Subject: usb: gadget: autoconf: net2280: match hardware and usb ep address USB3380 GPEP can be used in IN and OUT directions however, both directions should use same endpoint address. Fulfil this requirement by mapping usb endpoint to hardware endpoint with the same address. Tested-by: Ricardo Ribalda Delgado Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 0567cca..919cdfd 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -258,15 +258,25 @@ struct usb_ep *usb_ep_autoconfig_ss( /* First, apply chip-specific "best usage" knowledge. * This might make a good usb_gadget_ops hook ... */ - if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) { - /* ep-e, ep-f are PIO with only 64 byte fifos */ - ep = find_ep (gadget, "ep-e"); - if (ep && ep_matches(gadget, ep, desc, ep_comp)) - goto found_ep; - ep = find_ep (gadget, "ep-f"); + if (gadget_is_net2280(gadget)) { + char name[8]; + + if (type == USB_ENDPOINT_XFER_INT) { + /* ep-e, ep-f are PIO with only 64 byte fifos */ + ep = find_ep(gadget, "ep-e"); + if (ep && ep_matches(gadget, ep, desc, ep_comp)) + goto found_ep; + ep = find_ep(gadget, "ep-f"); + if (ep && ep_matches(gadget, ep, desc, ep_comp)) + goto found_ep; + } + + /* USB3380: use same address for usb and hardware endpoints */ + snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc), + usb_endpoint_dir_in(desc) ? "in" : "out"); + ep = find_ep(gadget, name); if (ep && ep_matches(gadget, ep, desc, ep_comp)) goto found_ep; - } else if (gadget_is_goku (gadget)) { if (USB_ENDPOINT_XFER_INT == type) { /* single buffering is enough */ -- cgit v0.10.2 From 971fe65670400f17f9ba05239ff0e796cffee696 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Sat, 16 May 2015 22:33:38 +0200 Subject: usb: gadget: net2280: physically disable endpoint on disable operation Reset configuration in ep_cfg on disable to physically disable the endpoint. Tested-by: Ricardo Ribalda Delgado Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 779e6fe..5740e0d 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -461,6 +461,13 @@ static void ep_reset_338x(struct net2280_regs __iomem *regs, BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat); + + tmp = readl(&ep->cfg->ep_cfg); + if (ep->is_in) + tmp &= ~USB3380_EP_CFG_MASK_IN; + else + tmp &= ~USB3380_EP_CFG_MASK_OUT; + writel(tmp, &ep->cfg->ep_cfg); } static void nuke(struct net2280_ep *); -- cgit v0.10.2 From 11bece5e063ca567e631c6ea3b1611c10dbc3282 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Sat, 16 May 2015 22:33:39 +0200 Subject: usb: gadget: net2280: fix pullup handling Gadget must be informed about disconnection when pullup is removed. Tested-by: Ricardo Ribalda Delgado Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 5740e0d..2bee912 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -124,6 +124,9 @@ static char *type_string(u8 bmAttributes) #define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE)) static void ep_clear_seqnum(struct net2280_ep *ep); +static void stop_activity(struct net2280 *dev, + struct usb_gadget_driver *driver); +static void ep0_start(struct net2280 *dev); /*-------------------------------------------------------------------------*/ static inline void enable_pciirqenb(struct net2280_ep *ep) @@ -1495,11 +1498,14 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on) spin_lock_irqsave(&dev->lock, flags); tmp = readl(&dev->usb->usbctl); dev->softconnect = (is_on != 0); - if (is_on) - tmp |= BIT(USB_DETECT_ENABLE); - else - tmp &= ~BIT(USB_DETECT_ENABLE); - writel(tmp, &dev->usb->usbctl); + if (is_on) { + ep0_start(dev); + writel(tmp | BIT(USB_DETECT_ENABLE), &dev->usb->usbctl); + } else { + writel(tmp & ~BIT(USB_DETECT_ENABLE), &dev->usb->usbctl); + stop_activity(dev, dev->driver); + } + spin_unlock_irqrestore(&dev->lock, flags); return 0; -- cgit v0.10.2 From e842b84c8e7221c45c8dbd7de09185c6149e1cf9 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 23 Mar 2015 09:52:48 +1100 Subject: usb: phy: Add interface to get phy give of device_node. Split the "get phy from device_node" functionality out of "get phy by phandle" so it can be used directly. This is useful when a battery-charger is intimately associated with a particular phy but handled by a separate driver. The charger can find the device_node based on sibling relationships without the need for a redundant declaration in the devicetree description. As a peripheral that gets a phy will often want to register a notifier block, and de-register it later, that functionality is included so the de-registration is automatic. Acked-by: Pavel Machek Signed-off-by: NeilBrown Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index d1cd6b5..98f75d2 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -22,6 +22,11 @@ static LIST_HEAD(phy_list); static LIST_HEAD(phy_bind_list); static DEFINE_SPINLOCK(phy_lock); +struct phy_devm { + struct usb_phy *phy; + struct notifier_block *nb; +}; + static struct usb_phy *__usb_find_phy(struct list_head *list, enum usb_phy_type type) { @@ -79,6 +84,15 @@ static void devm_usb_phy_release(struct device *dev, void *res) usb_put_phy(phy); } +static void devm_usb_phy_release2(struct device *dev, void *_res) +{ + struct phy_devm *res = _res; + + if (res->nb) + usb_unregister_notifier(res->phy, res->nb); + usb_put_phy(res->phy); +} + static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) { struct usb_phy **phy = res; @@ -153,40 +167,30 @@ err0: EXPORT_SYMBOL_GPL(usb_get_phy); /** - * devm_usb_get_phy_by_phandle - find the USB PHY by phandle + * devm_usb_get_phy_by_node - find the USB PHY by device_node * @dev - device that requests this phy - * @phandle - name of the property holding the phy phandle value - * @index - the index of the phy + * @node - the device_node for the phy device. + * @nb - a notifier_block to register with the phy. * - * Returns the phy driver associated with the given phandle value, + * Returns the phy driver associated with the given device_node, * after getting a refcount to it, -ENODEV if there is no such phy or - * -EPROBE_DEFER if there is a phandle to the phy, but the device is - * not yet loaded. While at that, it also associates the device with + * -EPROBE_DEFER if the device is not yet loaded. While at that, it + * also associates the device with * the phy using devres. On driver detach, release function is invoked * on the devres data, then, devres data is freed. * - * For use by USB host and peripheral drivers. + * For use by peripheral drivers for devices related to a phy, + * such as a charger. */ -struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, - const char *phandle, u8 index) +struct usb_phy *devm_usb_get_phy_by_node(struct device *dev, + struct device_node *node, + struct notifier_block *nb) { - struct usb_phy *phy = ERR_PTR(-ENOMEM), **ptr; + struct usb_phy *phy = ERR_PTR(-ENOMEM); + struct phy_devm *ptr; unsigned long flags; - struct device_node *node; - if (!dev->of_node) { - dev_dbg(dev, "device does not have a device node entry\n"); - return ERR_PTR(-EINVAL); - } - - node = of_parse_phandle(dev->of_node, phandle, index); - if (!node) { - dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, - dev->of_node->full_name); - return ERR_PTR(-ENODEV); - } - - ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); + ptr = devres_alloc(devm_usb_phy_release2, sizeof(*ptr), GFP_KERNEL); if (!ptr) { dev_dbg(dev, "failed to allocate memory for devres\n"); goto err0; @@ -205,8 +209,10 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, devres_free(ptr); goto err1; } - - *ptr = phy; + if (nb) + usb_register_notifier(phy, nb); + ptr->phy = phy; + ptr->nb = nb; devres_add(dev, ptr); get_device(phy->dev); @@ -215,10 +221,47 @@ err1: spin_unlock_irqrestore(&phy_lock, flags); err0: - of_node_put(node); return phy; } +EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_node); + +/** + * devm_usb_get_phy_by_phandle - find the USB PHY by phandle + * @dev - device that requests this phy + * @phandle - name of the property holding the phy phandle value + * @index - the index of the phy + * + * Returns the phy driver associated with the given phandle value, + * after getting a refcount to it, -ENODEV if there is no such phy or + * -EPROBE_DEFER if there is a phandle to the phy, but the device is + * not yet loaded. While at that, it also associates the device with + * the phy using devres. On driver detach, release function is invoked + * on the devres data, then, devres data is freed. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, + const char *phandle, u8 index) +{ + struct device_node *node; + struct usb_phy *phy; + + if (!dev->of_node) { + dev_dbg(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + node = of_parse_phandle(dev->of_node, phandle, index); + if (!node) { + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + phy = devm_usb_get_phy_by_node(dev, node, NULL); + of_node_put(node); + return phy; +} EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle); /** diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index bc91b5d..8ed1e29 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -205,6 +205,8 @@ extern struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index); extern struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index); extern struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, const char *phandle, u8 index); +extern struct usb_phy *devm_usb_get_phy_by_node(struct device *dev, + struct device_node *node, struct notifier_block *nb); extern void usb_put_phy(struct usb_phy *); extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); extern int usb_bind_phy(const char *dev_name, u8 index, -- cgit v0.10.2 From f5e4edb8c888958a970b2d42c47d2871a1a4fcdf Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 23 Mar 2015 09:52:48 +1100 Subject: power: twl4030_charger: find associated phy by more reliable means. twl4030_charger currently finds the associated phy using usb_get_phy() which will return the first USB2 phy. If your platform has multiple such phys (as mine does), this is not reliable (and reliably fails on the GTA04). Change to use devm_usb_get_phy_by_node(), having found the node by looking for an appropriately named sibling in device-tree. This makes usb-charging dependent on correct device-tree configuration. Acked-By: Sebastian Reichel Acked-by: Pavel Machek Signed-off-by: NeilBrown Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/power/twl-charger.txt b/Documentation/devicetree/bindings/power/twl-charger.txt index d5c7062..3b4ea1b 100644 --- a/Documentation/devicetree/bindings/power/twl-charger.txt +++ b/Documentation/devicetree/bindings/power/twl-charger.txt @@ -1,5 +1,15 @@ TWL BCI (Battery Charger Interface) +The battery charger needs to interact with the USB phy in order +to know when charging is permissible, and when there is a connection +or disconnection. + +The choice of phy cannot be configured at a hardware level, so there +is no value in explicit configuration in device-tree. Rather +if there is a sibling of the BCI node which is compatible with +"ti,twl4030-usb", then that is used to determine when and how +use USB power for charging. + Required properties: - compatible: - "ti,twl4030-bci" diff --git a/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt b/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt index 0aee0ad..17327a2 100644 --- a/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt +++ b/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt @@ -30,6 +30,9 @@ TWL4030 USB PHY AND COMPARATOR - usb_mode : The mode used by the phy to connect to the controller. "1" specifies "ULPI" mode and "2" specifies "CEA2011_3PIN" mode. +If a sibling node is compatible "ti,twl4030-bci", then it will find +this device and query it for USB power status. + twl4030-usb { compatible = "ti,twl4030-usb"; interrupts = < 10 4 >; diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 02a522c..022b891 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -638,10 +638,15 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); - bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (!IS_ERR_OR_NULL(bci->transceiver)) { - bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; - usb_register_notifier(bci->transceiver, &bci->usb_nb); + bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; + if (bci->dev->of_node) { + struct device_node *phynode; + + phynode = of_find_compatible_node(bci->dev->of_node->parent, + NULL, "ti,twl4030-usb"); + if (phynode) + bci->transceiver = devm_usb_get_phy_by_node( + bci->dev, phynode, &bci->usb_nb); } /* Enable interrupts now. */ @@ -671,10 +676,6 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) return 0; fail_unmask_interrupts: - if (!IS_ERR_OR_NULL(bci->transceiver)) { - usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_phy(bci->transceiver); - } free_irq(bci->irq_bci, bci); fail_bci_irq: free_irq(bci->irq_chg, bci); @@ -703,10 +704,6 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, TWL4030_INTERRUPTS_BCIIMR2A); - if (!IS_ERR_OR_NULL(bci->transceiver)) { - usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_phy(bci->transceiver); - } free_irq(bci->irq_bci, bci); free_irq(bci->irq_chg, bci); power_supply_unregister(bci->usb); -- cgit v0.10.2 From 0cb74b3dc45a5448161eb481d4709cdda2a889fd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 20 Mar 2015 20:11:11 +0100 Subject: usb: musb: Make musb_write_rxfun* and musb_write_rxhub* work like their tx versions For some reason the musb_write_rxfun* and musb_write_rxhub* functions had a different function prototype and some extra magic needed on the caller side compared to their tx counterparts, this commit makes them work the same as their tx counterparts. Signed-off-by: Hans de Goede Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 237f7c8..a3bc06d 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1544,7 +1544,6 @@ static int musb_core_init(u16 musb_type, struct musb *musb) #endif hw_ep->regs = musb->io.ep_offset(i, 0) + mbase; - hw_ep->target_regs = musb_read_target_reg_base(i, mbase); hw_ep->rx_reinit = 1; hw_ep->tx_reinit = 1; @@ -2352,7 +2351,6 @@ static void musb_restore_context(struct musb *musb) { int i; void __iomem *musb_base = musb->mregs; - void __iomem *ep_target_regs; void __iomem *epio; u8 power; @@ -2420,14 +2418,11 @@ static void musb_restore_context(struct musb *musb) musb_write_txhubport(musb_base, i, musb->context.index_regs[i].txhubport); - ep_target_regs = - musb_read_target_reg_base(i, musb_base); - - musb_write_rxfunaddr(ep_target_regs, + musb_write_rxfunaddr(musb_base, i, musb->context.index_regs[i].rxfunaddr); - musb_write_rxhubaddr(ep_target_regs, + musb_write_rxhubaddr(musb_base, i, musb->context.index_regs[i].rxhubaddr); - musb_write_rxhubport(ep_target_regs, + musb_write_rxhubport(musb_base, i, musb->context.index_regs[i].rxhubport); } musb_writeb(musb_base, MUSB_INDEX, musb->context.index); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 7117226..b8372f6 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -250,8 +250,6 @@ struct musb_hw_ep { void __iomem *fifo_sync_va; #endif - void __iomem *target_regs; - /* currently scheduled peripheral endpoint */ struct musb_qh *in_qh; struct musb_qh *out_qh; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 2499515..4a19c81 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -555,8 +555,9 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err) * the busy/not-empty tests are basically paranoia. */ static void -musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) +musb_rx_reinit(struct musb *musb, struct musb_qh *qh, u8 epnum) { + struct musb_hw_ep *ep = musb->endpoints + epnum; u16 csr; /* NOTE: we know the "rx" fifo reinit never triggers for ep0. @@ -594,10 +595,9 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) /* target addr and (for multipoint) hub addr/port */ if (musb->is_multipoint) { - musb_write_rxfunaddr(ep->target_regs, qh->addr_reg); - musb_write_rxhubaddr(ep->target_regs, qh->h_addr_reg); - musb_write_rxhubport(ep->target_regs, qh->h_port_reg); - + musb_write_rxfunaddr(musb->mregs, epnum, qh->addr_reg); + musb_write_rxhubaddr(musb->mregs, epnum, qh->h_addr_reg); + musb_write_rxhubport(musb->mregs, epnum, qh->h_port_reg); } else musb_writeb(musb->mregs, MUSB_FADDR, qh->addr_reg); @@ -910,7 +910,7 @@ finish: u16 csr; if (hw_ep->rx_reinit) { - musb_rx_reinit(musb, qh, hw_ep); + musb_rx_reinit(musb, qh, epnum); /* init new state: toggle and NYET, maybe DMA later */ if (usb_gettoggle(urb->dev, qh->epnum, 0)) diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index 11f0be0..edfc730 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -364,27 +364,25 @@ static inline u16 musb_read_hwvers(void __iomem *mbase) return musb_readw(mbase, MUSB_HWVERS); } -static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) -{ - return (MUSB_BUSCTL_OFFSET(i, 0) + mbase); -} - -static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxfunaddr(void __iomem *mbase, u8 epnum, u8 qh_addr_reg) { - musb_writeb(ep_target_regs, MUSB_RXFUNCADDR, qh_addr_reg); + musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXFUNCADDR), + qh_addr_reg); } -static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxhubaddr(void __iomem *mbase, u8 epnum, u8 qh_h_addr_reg) { - musb_writeb(ep_target_regs, MUSB_RXHUBADDR, qh_h_addr_reg); + musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBADDR), + qh_h_addr_reg); } -static inline void musb_write_rxhubport(void __iomem *ep_target_regs, +static inline void musb_write_rxhubport(void __iomem *mbase, u8 epnum, u8 qh_h_port_reg) { - musb_writeb(ep_target_regs, MUSB_RXHUBPORT, qh_h_port_reg); + musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBPORT), + qh_h_port_reg); } static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum, @@ -556,22 +554,17 @@ static inline u16 musb_read_hwvers(void __iomem *mbase) return MUSB_HWVERS_1900; } -static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) -{ - return NULL; -} - -static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxfunaddr(void __iomem *mbase, u8 epnum, u8 qh_addr_req) { } -static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxhubaddr(void __iomem *mbase, u8 epnum, u8 qh_h_addr_reg) { } -static inline void musb_write_rxhubport(void __iomem *ep_target_regs, +static inline void musb_write_rxhubport(void __iomem *mbase, u8 epnum, u8 qh_h_port_reg) { } -- cgit v0.10.2 From 6cc2af6d50204e8a1034ecd162378ceea22b09e8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 20 Mar 2015 20:11:12 +0100 Subject: usb: musb: Make busctl_offset an io-op rather then a define The Allwinner (sunxi) implementation of the musb has its busctl registers indexed by the MUSB_INDEX register rather then in a flat address space. This commit turns MUSB_BUSCTL_OFFSET from a macro into an io-op which can be overridden from the platform ops. Signed-off-by: Hans de Goede Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index a3bc06d..fea3402f 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -251,6 +251,11 @@ static u32 musb_indexed_ep_offset(u8 epnum, u16 offset) return 0x10 + offset; } +static u32 musb_default_busctl_offset(u8 epnum, u16 offset) +{ + return 0x80 + (0x08 * epnum) + offset; +} + static u8 musb_default_readb(const void __iomem *addr, unsigned offset) { return __raw_readb(addr + offset); @@ -2052,6 +2057,11 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) else musb->io.fifo_offset = musb_default_fifo_offset; + if (musb->ops->busctl_offset) + musb->io.busctl_offset = musb->ops->busctl_offset; + else + musb->io.busctl_offset = musb_default_busctl_offset; + if (musb->ops->readb) musb_readb = musb->ops->readb; if (musb->ops->writeb) @@ -2332,18 +2342,18 @@ static void musb_save_context(struct musb *musb) musb_readb(epio, MUSB_RXINTERVAL); musb->context.index_regs[i].txfunaddr = - musb_read_txfunaddr(musb_base, i); + musb_read_txfunaddr(musb, i); musb->context.index_regs[i].txhubaddr = - musb_read_txhubaddr(musb_base, i); + musb_read_txhubaddr(musb, i); musb->context.index_regs[i].txhubport = - musb_read_txhubport(musb_base, i); + musb_read_txhubport(musb, i); musb->context.index_regs[i].rxfunaddr = - musb_read_rxfunaddr(musb_base, i); + musb_read_rxfunaddr(musb, i); musb->context.index_regs[i].rxhubaddr = - musb_read_rxhubaddr(musb_base, i); + musb_read_rxhubaddr(musb, i); musb->context.index_regs[i].rxhubport = - musb_read_rxhubport(musb_base, i); + musb_read_rxhubport(musb, i); } } @@ -2411,18 +2421,18 @@ static void musb_restore_context(struct musb *musb) musb_writeb(epio, MUSB_RXINTERVAL, musb->context.index_regs[i].rxinterval); - musb_write_txfunaddr(musb_base, i, + musb_write_txfunaddr(musb, i, musb->context.index_regs[i].txfunaddr); - musb_write_txhubaddr(musb_base, i, + musb_write_txhubaddr(musb, i, musb->context.index_regs[i].txhubaddr); - musb_write_txhubport(musb_base, i, + musb_write_txhubport(musb, i, musb->context.index_regs[i].txhubport); - musb_write_rxfunaddr(musb_base, i, + musb_write_rxfunaddr(musb, i, musb->context.index_regs[i].rxfunaddr); - musb_write_rxhubaddr(musb_base, i, + musb_write_rxhubaddr(musb, i, musb->context.index_regs[i].rxhubaddr); - musb_write_rxhubport(musb_base, i, + musb_write_rxhubport(musb, i, musb->context.index_regs[i].rxhubport); } musb_writeb(musb_base, MUSB_INDEX, musb->context.index); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index b8372f6..4b886d0 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -67,7 +67,6 @@ struct musb_ep; #include "musb_dma.h" #include "musb_io.h" -#include "musb_regs.h" #include "musb_gadget.h" #include @@ -191,6 +190,7 @@ struct musb_platform_ops { void (*ep_select)(void __iomem *mbase, u8 epnum); u16 fifo_mode; u32 (*fifo_offset)(u8 epnum); + u32 (*busctl_offset)(u8 epnum, u16 offset); u8 (*readb)(const void __iomem *addr, unsigned offset); void (*writeb)(void __iomem *addr, unsigned offset, u8 data); u16 (*readw)(const void __iomem *addr, unsigned offset); @@ -444,6 +444,9 @@ struct musb { #endif }; +/* This must be included after struct musb is defined */ +#include "musb_regs.h" + static inline struct musb *gadget_to_musb(struct usb_gadget *g) { return container_of(g, struct musb, g); diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 4a19c81..26c65e6 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -595,9 +595,9 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, u8 epnum) /* target addr and (for multipoint) hub addr/port */ if (musb->is_multipoint) { - musb_write_rxfunaddr(musb->mregs, epnum, qh->addr_reg); - musb_write_rxhubaddr(musb->mregs, epnum, qh->h_addr_reg); - musb_write_rxhubport(musb->mregs, epnum, qh->h_port_reg); + musb_write_rxfunaddr(musb, epnum, qh->addr_reg); + musb_write_rxhubaddr(musb, epnum, qh->h_addr_reg); + musb_write_rxhubport(musb, epnum, qh->h_port_reg); } else musb_writeb(musb->mregs, MUSB_FADDR, qh->addr_reg); @@ -836,9 +836,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum, /* target addr and (for multipoint) hub addr/port */ if (musb->is_multipoint) { - musb_write_txfunaddr(mbase, epnum, qh->addr_reg); - musb_write_txhubaddr(mbase, epnum, qh->h_addr_reg); - musb_write_txhubport(mbase, epnum, qh->h_port_reg); + musb_write_txfunaddr(musb, epnum, qh->addr_reg); + musb_write_txhubaddr(musb, epnum, qh->h_addr_reg); + musb_write_txhubport(musb, epnum, qh->h_port_reg); /* FIXME if !epnum, do the same for RX ... */ } else musb_writeb(mbase, MUSB_FADDR, qh->addr_reg); diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index 8a57a6f..17a80ae 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -47,6 +47,7 @@ * @fifo_offset: platform specific function to get fifo offset * @read_fifo: platform specific function to read fifo * @write_fifo: platform specific function to write fifo + * @busctl_offset: platform specific function to get busctl offset */ struct musb_io { u32 quirks; @@ -55,6 +56,7 @@ struct musb_io { u32 (*fifo_offset)(u8 epnum); void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); + u32 (*busctl_offset)(u8 epnum, u16 offset); }; /* Do not add new entries here, add them the struct musb_io instead */ diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index edfc730..cff5bcf 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -300,9 +300,6 @@ #define MUSB_RXHUBADDR 0x06 #define MUSB_RXHUBPORT 0x07 -#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \ - (0x80 + (8*(_epnum)) + (_offset)) - static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) { musb_writeb(mbase, MUSB_TXFIFOSZ, c_size); @@ -364,76 +361,84 @@ static inline u16 musb_read_hwvers(void __iomem *mbase) return musb_readw(mbase, MUSB_HWVERS); } -static inline void musb_write_rxfunaddr(void __iomem *mbase, u8 epnum, +static inline void musb_write_rxfunaddr(struct musb *musb, u8 epnum, u8 qh_addr_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXFUNCADDR), - qh_addr_reg); + musb_writeb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXFUNCADDR), + qh_addr_reg); } -static inline void musb_write_rxhubaddr(void __iomem *mbase, u8 epnum, +static inline void musb_write_rxhubaddr(struct musb *musb, u8 epnum, u8 qh_h_addr_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBADDR), + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_RXHUBADDR), qh_h_addr_reg); } -static inline void musb_write_rxhubport(void __iomem *mbase, u8 epnum, +static inline void musb_write_rxhubport(struct musb *musb, u8 epnum, u8 qh_h_port_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBPORT), + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_RXHUBPORT), qh_h_port_reg); } -static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum, +static inline void musb_write_txfunaddr(struct musb *musb, u8 epnum, u8 qh_addr_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR), - qh_addr_reg); + musb_writeb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXFUNCADDR), + qh_addr_reg); } -static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum, +static inline void musb_write_txhubaddr(struct musb *musb, u8 epnum, u8 qh_addr_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR), + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_TXHUBADDR), qh_addr_reg); } -static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum, +static inline void musb_write_txhubport(struct musb *musb, u8 epnum, u8 qh_h_port_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT), + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_TXHUBPORT), qh_h_port_reg); } -static inline u8 musb_read_rxfunaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_rxfunaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXFUNCADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXFUNCADDR)); } -static inline u8 musb_read_rxhubaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_rxhubaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXHUBADDR)); } -static inline u8 musb_read_rxhubport(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_rxhubport(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBPORT)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXHUBPORT)); } -static inline u8 musb_read_txfunaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_txfunaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXFUNCADDR)); } -static inline u8 musb_read_txhubaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_txhubaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXHUBADDR)); } -static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_txhubport(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXHUBPORT)); } #else /* CONFIG_BLACKFIN */ -- cgit v0.10.2 From be780381772909ba4a89805945995b9f10c59ca8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 20 Mar 2015 20:11:13 +0100 Subject: usb: musb: Do not use musb_read[b|w] / _write[b|w] wrappers in generic fifo functions The generic fifo functions already use non wrapped accesses in various cases through the iowrite#_rep functions, and all platforms which override the default musb_read[b|w] / _write[b|w] functions also provide their own fifo access functions, so we can safely drop the unnecessary indirection from the fifo access functions. Signed-off-by: Hans de Goede Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index fea3402f..575a1f8 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -314,7 +314,7 @@ static void musb_default_write_fifo(struct musb_hw_ep *hw_ep, u16 len, index += len & ~0x03; } if (len & 0x02) { - musb_writew(fifo, 0, *(u16 *)&src[index]); + __raw_writew(*(u16 *)&src[index], fifo); index += 2; } } else { @@ -324,7 +324,7 @@ static void musb_default_write_fifo(struct musb_hw_ep *hw_ep, u16 len, } } if (len & 0x01) - musb_writeb(fifo, 0, src[index]); + __raw_writeb(src[index], fifo); } else { /* byte aligned */ iowrite8_rep(fifo, src, len); @@ -356,7 +356,7 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) index = len & ~0x03; } if (len & 0x02) { - *(u16 *)&dst[index] = musb_readw(fifo, 0); + *(u16 *)&dst[index] = __raw_readw(fifo); index += 2; } } else { @@ -366,7 +366,7 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } } if (len & 0x01) - dst[index] = musb_readb(fifo, 0); + dst[index] = __raw_readb(fifo); } else { /* byte aligned */ ioread8_rep(fifo, dst, len); -- cgit v0.10.2 From 47a82730b54c2757ca5c89a82a9727ca0129af9d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 20 Mar 2015 20:11:14 +0100 Subject: usb: musb: Fix platform code being unable to override ep access ops musb-core was setting the ops to the default indexed or flat handlers after checking for platform overrides. Reverse the order of this so that platform overrides actually work. Signed-off-by: Hans de Goede Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 575a1f8..d878aee 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2032,13 +2032,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) if (musb->ops->quirks) musb->io.quirks = musb->ops->quirks; - /* At least tusb6010 has it's own offsets.. */ - if (musb->ops->ep_offset) - musb->io.ep_offset = musb->ops->ep_offset; - if (musb->ops->ep_select) - musb->io.ep_select = musb->ops->ep_select; - - /* ..and some devices use indexed offset or flat offset */ + /* Set default ep access to indexed offset or flat offset ops */ if (musb->io.quirks & MUSB_INDEXED_EP) { musb->io.ep_offset = musb_indexed_ep_offset; musb->io.ep_select = musb_indexed_ep_select; @@ -2046,6 +2040,11 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb->io.ep_offset = musb_flat_ep_offset; musb->io.ep_select = musb_flat_ep_select; } + /* And override them with platform specific ops if specified. */ + if (musb->ops->ep_offset) + musb->io.ep_offset = musb->ops->ep_offset; + if (musb->ops->ep_select) + musb->io.ep_select = musb->ops->ep_select; if (musb->ops->fifo_mode) fifo_mode = musb->ops->fifo_mode; -- cgit v0.10.2 From 7a64c7283ef83c82cb2125339c5d12092256614e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 May 2015 15:34:45 -0500 Subject: usb: gadget: atmel: fix build warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the following build warning: drivers/usb/gadget/udc/atmel_usba_udc.c:707:2: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 4 has type ‘dma_addr_t’ [-Wformat=] Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 4c01953..54373469 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -704,8 +704,8 @@ static int queue_dma(struct usba_udc *udc, struct usba_ep *ep, unsigned long flags; int ret; - DBG(DBG_DMA, "%s: req l/%u d/%08x %c%c%c\n", - ep->ep.name, req->req.length, req->req.dma, + DBG(DBG_DMA, "%s: req l/%u d/%pad %c%c%c\n", + ep->ep.name, req->req.length, &req->req.dma, req->req.zero ? 'Z' : 'z', req->req.short_not_ok ? 'S' : 's', req->req.no_interrupt ? 'I' : 'i'); -- cgit v0.10.2 From 24fe86a617c550fb9bdc6c8bd7cf647d3955f8ba Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 29 Mar 2015 12:50:46 +0200 Subject: phy: sun4i-usb: Add a sunxi specific function for setting squelch-detect The sunxi otg phy has a bug where it wrongly detects a high speed squelch when reset on the root port gets de-asserted with a lo-speed device. The workaround for this is to disable squelch detect before de-asserting reset, and re-enabling it after the reset de-assert is done. Add a sunxi specific phy function to allow the sunxi-musb glue to do this. Acked-by: Kishon Vijay Abraham I Signed-off-by: Hans de Goede Signed-off-by: Felipe Balbi diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index a2b08f3c..e17c539 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,7 @@ #define PHY_OTG_FUNC_EN 0x28 #define PHY_VBUS_DET_EN 0x29 #define PHY_DISCON_TH_SEL 0x2a +#define PHY_SQUELCH_DETECT 0x3c #define MAX_PHYS 3 @@ -204,6 +206,13 @@ static int sun4i_usb_phy_power_off(struct phy *_phy) return 0; } +void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled) +{ + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + + sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2); +} + static struct phy_ops sun4i_usb_phy_ops = { .init = sun4i_usb_phy_init, .exit = sun4i_usb_phy_exit, diff --git a/include/linux/phy/phy-sun4i-usb.h b/include/linux/phy/phy-sun4i-usb.h new file mode 100644 index 0000000..50aed92 --- /dev/null +++ b/include/linux/phy/phy-sun4i-usb.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015 Hans de Goede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef PHY_SUN4I_USB_H_ +#define PHY_SUN4I_USB_H_ + +#include "phy.h" + +/** + * sun4i_usb_phy_set_squelch_detect() - Enable/disable squelch detect + * @phy: reference to a sun4i usb phy + * @enabled: wether to enable or disable squelch detect + */ +void sun4i_usb_phy_set_squelch_detect(struct phy *phy, bool enabled); + +#endif -- cgit v0.10.2 From fea2fc6e21967fc674d218f20710680d814079d2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 27 May 2015 12:24:23 -0500 Subject: usb: musb: am35x: fix build warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the following build warnings: drivers/usb/musb/am35x.c:573:12: warning: ‘am35x_suspend’ defined but not used [-Wunused-function] drivers/usb/musb/am35x.c:589:12: warning: ‘am35x_resume’ defined but not used [-Wunused-function] drivers/usb/musb/am35x.c:573:12: warning: ‘am35x_suspend’ defined but not used [-Wunused-function] drivers/usb/musb/am35x.c:589:12: warning: ‘am35x_resume’ defined but not used [-Wunused-function] Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 6cfd43a..c41fe58 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -569,7 +569,7 @@ static int am35x_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int am35x_suspend(struct device *dev) { struct am35x_glue *glue = dev_get_drvdata(dev); -- cgit v0.10.2 From 30d092231d3b0579cb2fecc1ed8b25b329d47f98 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 27 May 2015 12:25:40 -0500 Subject: usb: musb: ux500: fix build warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the following build warnings: drivers/usb/musb/ux500.c:346:12: warning: ‘ux500_suspend’ defined but not used [-Wunused-function] drivers/usb/musb/ux500.c:357:12: warning: ‘ux500_resume’ defined but not used [-Wunused-function] Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 2967b51..39168fe 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -342,7 +342,7 @@ static int ux500_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ux500_suspend(struct device *dev) { struct ux500_glue *glue = dev_get_drvdata(dev); -- cgit v0.10.2 From 94a715ed5c57e43f216a4fe85a3604a574c08515 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 27 May 2015 12:29:18 -0500 Subject: usb: gadget: atmel: fix build warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the following build warnings: drivers/usb/gadget/udc/atmel_usba_udc.c:2207:12: warning: ‘usba_udc_suspend’ defined but not used [-Wunused-function] static int usba_udc_suspend(struct device *dev) drivers/usb/gadget/udc/atmel_usba_udc.c:2236:12: warning: ‘usba_udc_resume’ defined but not used [-Wunused-function] static int usba_udc_resume(struct device *dev) Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 54373469..a01483f 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -2203,7 +2203,7 @@ static int __exit usba_udc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int usba_udc_suspend(struct device *dev) { struct usba_udc *udc = dev_get_drvdata(dev); -- cgit v0.10.2 From 307c858bc245da5229b30186546590e1d472fc8f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 28 May 2015 16:08:02 +0200 Subject: usb: phy: add static inline wrapper for devm_usb_get_phy_by_node The newly introduced devm_usb_get_phy_by_node function only has an extern declaration, but no alternative for the case that CONFIG_USB_PHY is disabled, which leads to a build error when it is used anyway: drivers/power/twl4030_charger.c: In function 'twl4030_bci_probe': drivers/power/twl4030_charger.c:648:23: error: implicit declaration of function 'devm_usb_get_phy_by_node' [-Werror=implicit-function-declaration] bci->transceiver = devm_usb_get_phy_by_node( This adds the wrapper in the same way that we have one for all other usb-phy API functions. Signed-off-by: Arnd Bergmann Fixes: e842b84c8e7 ("usb: phy: Add interface to get phy give of device_node.") Signed-off-by: Felipe Balbi diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index 8ed1e29..e39f251 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -240,6 +240,12 @@ static inline struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, return ERR_PTR(-ENXIO); } +static inline struct usb_phy *devm_usb_get_phy_by_node(struct device *dev, + struct device_node *node, struct notifier_block *nb) +{ + return ERR_PTR(-ENXIO); +} + static inline void usb_put_phy(struct usb_phy *x) { } -- cgit v0.10.2 From e18b7975c885bc3a938b9a76daf32957ea0235fa Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 29 May 2015 10:06:38 -0500 Subject: usb: dwc3: gadget: don't clear EP_BUSY too early In case of non-Isochronous transfers, we don't want to clear DWC3_EP_BUSY flag until XferComplete event. That's because XferInProgress was only enabled so we can recycle TRBs and usb_requests quicker, but there are still other pending requests being transferred. In order to make sure we don't allow for another StartTransfer command while the HW is still processing other transfers, we must keep DWC3_EP_BUSY flag set and this what this patch does. Fixes: f3af36511e60 (usb: dwc3: gadget: always enable IOC on bulk/interrupt transfers) Cc: # v3.15+ Reported-by: sundeep subbaraya Tested-by: sundeep subbaraya Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 55b5edc..333a7c0 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1906,12 +1906,16 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, { unsigned status = 0; int clean_busy; + u32 is_xfer_complete; + + is_xfer_complete = (event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE); if (event->status & DEPEVT_STATUS_BUSERR) status = -ECONNRESET; clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status); - if (clean_busy) + if (clean_busy && (is_xfer_complete || + usb_endpoint_xfer_isoc(dep->endpoint.desc))) dep->flags &= ~DWC3_EP_BUSY; /* -- cgit v0.10.2 From cd33a32157e42483ffea31e32b1cee4f11ff9592 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 29 May 2015 17:01:46 +0300 Subject: usb: xhci: cleanup xhci_hcd allocation HCD core allocates memory for HCD private data in usb_create_[shared_]hcd() so make use of that mechanism to allocate the struct xhci_hcd. Introduce struct xhci_driver_overrides to provide the size of HCD private data and hc_driver operation overrides. As of now we only need to override the reset and start methods. Signed-off-by: Roger Quadros Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 2af32e2..4a4cb1d 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -45,6 +45,13 @@ static const char hcd_name[] = "xhci_hcd"; static struct hc_driver __read_mostly xhci_pci_hc_driver; +static int xhci_pci_setup(struct usb_hcd *hcd); + +static const struct xhci_driver_overrides xhci_pci_overrides __initconst = { + .extra_priv_size = sizeof(struct xhci_hcd), + .reset = xhci_pci_setup, +}; + /* called after powerup, by probe or system-pm "wakeup" */ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev) { @@ -206,7 +213,6 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (!retval) return retval; - kfree(xhci); return retval; } @@ -247,11 +253,6 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) goto dealloc_usb2_hcd; } - /* Set the xHCI pointer before xhci_pci_setup() (aka hcd_driver.reset) - * is called by usb_add_hcd(). - */ - *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci; - retval = usb_add_hcd(xhci->shared_hcd, dev->irq, IRQF_SHARED); if (retval) @@ -290,8 +291,6 @@ static void xhci_pci_remove(struct pci_dev *dev) /* Workaround for spurious wakeups at shutdown with HSW */ if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) pci_set_power_state(dev, PCI_D3hot); - - kfree(xhci); } #ifdef CONFIG_PM @@ -379,7 +378,7 @@ static struct pci_driver xhci_pci_driver = { static int __init xhci_pci_init(void) { - xhci_init_driver(&xhci_pci_hc_driver, xhci_pci_setup); + xhci_init_driver(&xhci_pci_hc_driver, &xhci_pci_overrides); #ifdef CONFIG_PM xhci_pci_hc_driver.pci_suspend = xhci_pci_suspend; xhci_pci_hc_driver.pci_resume = xhci_pci_resume; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 783e819..0bc4309 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -26,6 +26,15 @@ static struct hc_driver __read_mostly xhci_plat_hc_driver; +static int xhci_plat_setup(struct usb_hcd *hcd); +static int xhci_plat_start(struct usb_hcd *hcd); + +static const struct xhci_driver_overrides xhci_plat_overrides __initconst = { + .extra_priv_size = sizeof(struct xhci_hcd), + .reset = xhci_plat_setup, + .start = xhci_plat_start, +}; + static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) { /* @@ -147,11 +156,6 @@ static int xhci_plat_probe(struct platform_device *pdev) if ((node && of_property_read_bool(node, "usb3-lpm-capable")) || (pdata && pdata->usb3_lpm_capable)) xhci->quirks |= XHCI_LPM_SUPPORT; - /* - * Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset) - * is called by usb_add_hcd(). - */ - *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci; if (HCC_MAX_PSA(xhci->hcc_params) >= 4) xhci->shared_hcd->can_do_streams = 1; @@ -207,7 +211,6 @@ static int xhci_plat_remove(struct platform_device *dev) if (!IS_ERR(clk)) clk_disable_unprepare(clk); usb_put_hcd(hcd); - kfree(xhci); return 0; } @@ -271,8 +274,7 @@ MODULE_ALIAS("platform:xhci-hcd"); static int __init xhci_plat_init(void) { - xhci_init_driver(&xhci_plat_hc_driver, xhci_plat_setup); - xhci_plat_hc_driver.start = xhci_plat_start; + xhci_init_driver(&xhci_plat_hc_driver, &xhci_plat_overrides); return platform_driver_register(&usb_xhci_driver); } module_init(xhci_plat_init); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index ec8ac16..01118f7 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4832,10 +4832,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) hcd->self.no_stop_on_short = 1; if (usb_hcd_is_primary_hcd(hcd)) { - xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); - if (!xhci) - return -ENOMEM; - *((struct xhci_hcd **) hcd->hcd_priv) = xhci; + xhci = hcd_to_xhci(hcd); xhci->main_hcd = hcd; /* Mark the first roothub as being USB 2.0. * The xHCI driver will register the USB 3.0 roothub. @@ -4883,13 +4880,13 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Make sure the HC is halted. */ retval = xhci_halt(xhci); if (retval) - goto error; + return retval; xhci_dbg(xhci, "Resetting HCD\n"); /* Reset the internal HC memory state and registers. */ retval = xhci_reset(xhci); if (retval) - goto error; + return retval; xhci_dbg(xhci, "Reset complete\n"); /* Set dma_mask and coherent_dma_mask to 64-bits, @@ -4904,16 +4901,13 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Initialize HCD and host controller data structures. */ retval = xhci_init(hcd); if (retval) - goto error; + return retval; xhci_dbg(xhci, "Called HCD init\n"); xhci_info(xhci, "hcc params 0x%08x hci version 0x%x quirks 0x%08x\n", xhci->hcc_params, xhci->hci_version, xhci->quirks); return 0; -error: - kfree(xhci); - return retval; } EXPORT_SYMBOL_GPL(xhci_gen_setup); @@ -4978,11 +4972,21 @@ static const struct hc_driver xhci_hc_driver = { .find_raw_port_number = xhci_find_raw_port_number, }; -void xhci_init_driver(struct hc_driver *drv, int (*setup_fn)(struct usb_hcd *)) +void xhci_init_driver(struct hc_driver *drv, + const struct xhci_driver_overrides *over) { - BUG_ON(!setup_fn); + BUG_ON(!over); + + /* Copy the generic table to drv then apply the overrides */ *drv = xhci_hc_driver; - drv->reset = setup_fn; + + if (over) { + drv->hcd_priv_size += over->extra_priv_size; + if (over->reset) + drv->reset = over->reset; + if (over->start) + drv->start = over->start; + } } EXPORT_SYMBOL_GPL(xhci_init_driver); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ea75e8c..11a231d 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1591,10 +1591,24 @@ struct xhci_hcd { #define COMP_MODE_RCVRY_MSECS 2000 }; +/* Platform specific overrides to generic XHCI hc_driver ops */ +struct xhci_driver_overrides { + size_t extra_priv_size; + int (*reset)(struct usb_hcd *hcd); + int (*start)(struct usb_hcd *hcd); +}; + /* convert between an HCD pointer and the corresponding EHCI_HCD */ static inline struct xhci_hcd *hcd_to_xhci(struct usb_hcd *hcd) { - return *((struct xhci_hcd **) (hcd->hcd_priv)); + struct usb_hcd *primary_hcd; + + if (usb_hcd_is_primary_hcd(hcd)) + primary_hcd = hcd; + else + primary_hcd = hcd->primary_hcd; + + return (struct xhci_hcd *) (primary_hcd->hcd_priv); } static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci) @@ -1748,7 +1762,8 @@ int xhci_run(struct usb_hcd *hcd); void xhci_stop(struct usb_hcd *hcd); void xhci_shutdown(struct usb_hcd *hcd); int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); -void xhci_init_driver(struct hc_driver *drv, int (*setup_fn)(struct usb_hcd *)); +void xhci_init_driver(struct hc_driver *drv, + const struct xhci_driver_overrides *over); #ifdef CONFIG_PM int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup); -- cgit v0.10.2 From 4ac53087d6d48e46e1cf4d0ca9ed9accdf9c928a Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 29 May 2015 17:01:47 +0300 Subject: usb: xhci: plat: Create both HCDs before adding them As xhci_hcd is now allocated by usb_create_hcd(), we don't need to add the primary HCD before creating the shared HCD. Creating the shared HCD before adding the primary HCD is particularly useful for the OTG use case so that we know at the OTG core if the HCD is in single configuration or dual (primary + shared) configuration. Signed-off-by: Roger Quadros [Mathias: rearranged to fit on top of the Marvell Armada 385 phy changes] Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 0bc4309..267787a 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -136,21 +136,15 @@ static int xhci_plat_probe(struct platform_device *pdev) goto disable_clk; } - ret = usb_add_hcd(hcd, irq, IRQF_SHARED); - if (ret) - goto disable_clk; - device_wakeup_enable(hcd->self.controller); - /* USB 2.0 roothub is stored in the platform_device now. */ - hcd = platform_get_drvdata(pdev); xhci = hcd_to_xhci(hcd); xhci->clk = clk; xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev, dev_name(&pdev->dev), hcd); if (!xhci->shared_hcd) { ret = -ENOMEM; - goto dealloc_usb2_hcd; + goto disable_clk; } if ((node && of_property_read_bool(node, "usb3-lpm-capable")) || @@ -172,21 +166,26 @@ static int xhci_plat_probe(struct platform_device *pdev) goto put_usb3_hcd; } - ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto disable_usb_phy; + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); + if (ret) + goto dealloc_usb2_hcd; + return 0; + +dealloc_usb2_hcd: + usb_remove_hcd(hcd); + disable_usb_phy: usb_phy_shutdown(hcd->usb_phy); put_usb3_hcd: usb_put_hcd(xhci->shared_hcd); -dealloc_usb2_hcd: - usb_remove_hcd(hcd); - disable_clk: if (!IS_ERR(clk)) clk_disable_unprepare(clk); @@ -205,9 +204,10 @@ static int xhci_plat_remove(struct platform_device *dev) usb_remove_hcd(xhci->shared_hcd); usb_phy_shutdown(hcd->usb_phy); - usb_put_hcd(xhci->shared_hcd); usb_remove_hcd(hcd); + usb_put_hcd(xhci->shared_hcd); + if (!IS_ERR(clk)) clk_disable_unprepare(clk); usb_put_hcd(hcd); -- cgit v0.10.2 From 8a853759c262fd52ff0f5e89f89c65928077fe4a Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 29 May 2015 17:01:48 +0300 Subject: usb: xhci: Allow usb_add/remove_hcd() to be called repeatedly Don't set xhci->shared_hcd to NULL in xhci_stop() as we have still not de-allocated it. It was resulting in a NULL pointer de-reference if usb_add/remove_hcd() is called repeatedly. We want repeated add/remove to work for the OTG use case. Signed-off-by: Roger Quadros Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 01118f7..8a7e319 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -660,12 +660,6 @@ static void xhci_only_stop_hcd(struct usb_hcd *hcd) spin_lock_irq(&xhci->lock); xhci_halt(xhci); - - /* The shared_hcd is going to be deallocated shortly (the USB core only - * calls this function when allocation fails in usb_add_hcd(), or - * usb_remove_hcd() is called). So we need to unset xHCI's pointer. - */ - xhci->shared_hcd = NULL; spin_unlock_irq(&xhci->lock); } -- cgit v0.10.2 From ad6b1d914a9e07f3b9a9ae3396f3c840d0070539 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 29 May 2015 17:01:49 +0300 Subject: usb: xhci: fix xhci locking up during hcd remove The problem seems to be that if a new device is detected while we have already removed the shared HCD, then many of the xhci operations (e.g. xhci_alloc_dev(), xhci_setup_device()) hang as command never completes. I don't think XHCI can operate without the shared HCD as we've already called xhci_halt() in xhci_only_stop_hcd() when shared HCD goes away. We need to prevent new commands from being queued not only when HCD is dying but also when HCD is halted. The following lockup was detected while testing the otg state machine. [ 178.199951] xhci-hcd xhci-hcd.0.auto: xHCI Host Controller [ 178.205799] xhci-hcd xhci-hcd.0.auto: new USB bus registered, assigned bus number 1 [ 178.214458] xhci-hcd xhci-hcd.0.auto: hcc params 0x0220f04c hci version 0x100 quirks 0x00010010 [ 178.223619] xhci-hcd xhci-hcd.0.auto: irq 400, io mem 0x48890000 [ 178.230677] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002 [ 178.237796] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1 [ 178.245358] usb usb1: Product: xHCI Host Controller [ 178.250483] usb usb1: Manufacturer: Linux 4.0.0-rc1-00024-g6111320 xhci-hcd [ 178.257783] usb usb1: SerialNumber: xhci-hcd.0.auto [ 178.267014] hub 1-0:1.0: USB hub found [ 178.272108] hub 1-0:1.0: 1 port detected [ 178.278371] xhci-hcd xhci-hcd.0.auto: xHCI Host Controller [ 178.284171] xhci-hcd xhci-hcd.0.auto: new USB bus registered, assigned bus number 2 [ 178.294038] usb usb2: New USB device found, idVendor=1d6b, idProduct=0003 [ 178.301183] usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1 [ 178.308776] usb usb2: Product: xHCI Host Controller [ 178.313902] usb usb2: Manufacturer: Linux 4.0.0-rc1-00024-g6111320 xhci-hcd [ 178.321222] usb usb2: SerialNumber: xhci-hcd.0.auto [ 178.329061] hub 2-0:1.0: USB hub found [ 178.333126] hub 2-0:1.0: 1 port detected [ 178.567585] dwc3 48890000.usb: usb_otg_start_host 0 [ 178.572707] xhci-hcd xhci-hcd.0.auto: remove, state 4 [ 178.578064] usb usb2: USB disconnect, device number 1 [ 178.586565] xhci-hcd xhci-hcd.0.auto: USB bus 2 deregistered [ 178.592585] xhci-hcd xhci-hcd.0.auto: remove, state 1 [ 178.597924] usb usb1: USB disconnect, device number 1 [ 178.603248] usb 1-1: new high-speed USB device number 2 using xhci-hcd [ 190.597337] INFO: task kworker/u4:0:6 blocked for more than 10 seconds. [ 190.604273] Not tainted 4.0.0-rc1-00024-g6111320 #1058 [ 190.610228] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 190.618443] kworker/u4:0 D c05c0ac0 0 6 2 0x00000000 [ 190.625120] Workqueue: usb_otg usb_otg_work [ 190.629533] [] (__schedule) from [] (schedule+0x34/0x98) [ 190.636915] [] (schedule) from [] (schedule_preempt_disabled+0xc/0x10) [ 190.645591] [] (schedule_preempt_disabled) from [] (mutex_lock_nested+0x1ac/0x3fc) [ 190.655353] [] (mutex_lock_nested) from [] (usb_disconnect+0x3c/0x208) [ 190.664043] [] (usb_disconnect) from [] (_usb_remove_hcd+0x98/0x1d8) [ 190.672535] [] (_usb_remove_hcd) from [] (usb_otg_start_host+0x50/0xf4) [ 190.681299] [] (usb_otg_start_host) from [] (otg_set_protocol+0x5c/0xd0) [ 190.690153] [] (otg_set_protocol) from [] (otg_set_state+0x170/0xbfc) [ 190.698735] [] (otg_set_state) from [] (otg_statemachine+0x12c/0x470) [ 190.707326] [] (otg_statemachine) from [] (process_one_work+0x1b4/0x4a0) [ 190.716162] [] (process_one_work) from [] (worker_thread+0x154/0x44c) [ 190.724742] [] (worker_thread) from [] (kthread+0xd4/0xf0) [ 190.732328] [] (kthread) from [] (ret_from_fork+0x14/0x24) [ 190.739898] 5 locks held by kworker/u4:0/6: [ 190.744274] #0: ("%s""usb_otg"){.+.+.+}, at: [] process_one_work+0x124/0x4a0 [ 190.752799] #1: ((&otgd->work)){+.+.+.}, at: [] process_one_work+0x124/0x4a0 [ 190.761326] #2: (&otgd->fsm.lock){+.+.+.}, at: [] otg_statemachine+0x18/0x470 [ 190.769934] #3: (usb_bus_list_lock){+.+.+.}, at: [] _usb_remove_hcd+0x90/0x1d8 [ 190.778635] #4: (&dev->mutex){......}, at: [] usb_disconnect+0x3c/0x208 [ 190.786700] INFO: task kworker/1:0:14 blocked for more than 10 seconds. [ 190.793633] Not tainted 4.0.0-rc1-00024-g6111320 #1058 [ 190.799567] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 190.807783] kworker/1:0 D c05c0ac0 0 14 2 0x00000000 [ 190.814457] Workqueue: usb_hub_wq hub_event [ 190.818866] [] (__schedule) from [] (schedule+0x34/0x98) [ 190.826252] [] (schedule) from [] (schedule_timeout+0x13c/0x1ec) [ 190.834377] [] (schedule_timeout) from [] (wait_for_common+0xbc/0x150) [ 190.843062] [] (wait_for_common) from [] (xhci_setup_device+0x164/0x5cc [xhci_hcd]) [ 190.852986] [] (xhci_setup_device [xhci_hcd]) from [] (hub_port_init+0x3f4/0xb10) [ 190.862667] [] (hub_port_init) from [] (hub_event+0x704/0x1018) [ 190.870704] [] (hub_event) from [] (process_one_work+0x1b4/0x4a0) [ 190.878919] [] (process_one_work) from [] (worker_thread+0x154/0x44c) [ 190.887503] [] (worker_thread) from [] (kthread+0xd4/0xf0) [ 190.895076] [] (kthread) from [] (ret_from_fork+0x14/0x24) [ 190.902650] 5 locks held by kworker/1:0/14: [ 190.907023] #0: ("usb_hub_wq"){.+.+.+}, at: [] process_one_work+0x124/0x4a0 [ 190.915454] #1: ((&hub->events)){+.+.+.}, at: [] process_one_work+0x124/0x4a0 [ 190.924070] #2: (&dev->mutex){......}, at: [] hub_event+0x30/0x1018 [ 190.931768] #3: (&port_dev->status_lock){+.+.+.}, at: [] hub_event+0x6f0/0x1018 [ 190.940558] #4: (&bus->usb_address0_mutex){+.+.+.}, at: [] hub_port_init+0x58/0xb10 Signed-off-by: Roger Quadros Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 7d34cbf..d7fd5efd 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3781,8 +3781,11 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, { int reserved_trbs = xhci->cmd_ring_reserved_trbs; int ret; - if (xhci->xhc_state & XHCI_STATE_DYING) + + if (xhci->xhc_state) { + xhci_dbg(xhci, "xHCI dying or halted, can't queue_command\n"); return -ESHUTDOWN; + } if (!command_must_succeed) reserved_trbs++; -- cgit v0.10.2 From 9fa733f24bb54dd532b5f5d7abc275c4a547c69f Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 29 May 2015 17:01:50 +0300 Subject: usb: xhci: Fix suspend/resume when used with OTG core In the OTG case, the controller might not yet have been added or is removed before the system suspends. Assign xhci->main_hcd during probe to prevent NULL pointer de-reference in xhci_suspend/resume(). Use the hcd->state flag to check if HCD is halted and if that is so do nothing for xhci_suspend/resume(). [Only for xhci-plat devices, pci devices need it in gen_setup -Mathias] Signed-off-by: Roger Quadros Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 267787a..890ad9d 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -140,6 +140,7 @@ static int xhci_plat_probe(struct platform_device *pdev) xhci = hcd_to_xhci(hcd); xhci->clk = clk; + xhci->main_hcd = hcd; xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev, dev_name(&pdev->dev), hcd); if (!xhci->shared_hcd) { diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8a7e319..e5568ba 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -891,6 +891,9 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) struct usb_hcd *hcd = xhci_to_hcd(xhci); u32 command; + if (!hcd->state) + return 0; + if (hcd->state != HC_STATE_SUSPENDED || xhci->shared_hcd->state != HC_STATE_SUSPENDED) return -EINVAL; @@ -977,6 +980,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) int retval = 0; bool comp_timer_running = false; + if (!hcd->state) + return 0; + /* Wait a bit if either of the roothubs need to settle from the * transition into bus suspend. */ -- cgit v0.10.2 From 41485a90d573764738a2d096c01133fe30c2ebca Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 29 May 2015 17:01:51 +0300 Subject: xhci: optimize xhci bus resume time We used to write the root port state changes in turn for every port, sleeping 20ms after every port state change. Suspended usb2 ports need two state changes, taking minimun 40ms per port. Now instead poll the Port Link State Change (PLC) bit as the state change to U0 will set this bit. Suspended usb2 ports still need the extra 20ms delay, but we now change all the port states at once so we only need to sleep 20ms once all together Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 0827d7c..e75c565 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1184,6 +1184,10 @@ int xhci_bus_resume(struct usb_hcd *hcd) struct xhci_bus_state *bus_state; u32 temp; unsigned long flags; + unsigned long port_was_suspended = 0; + bool need_usb2_u3_exit = false; + int slot_id; + int sret; max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; @@ -1207,7 +1211,6 @@ int xhci_bus_resume(struct usb_hcd *hcd) /* Check whether need resume ports. If needed resume port and disable remote wakeup */ u32 temp; - int slot_id; temp = readl(port_array[port_index]); if (DEV_SUPERSPEED(temp)) @@ -1216,39 +1219,47 @@ int xhci_bus_resume(struct usb_hcd *hcd) temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); if (test_bit(port_index, &bus_state->bus_suspended) && (temp & PORT_PLS_MASK)) { - if (DEV_SUPERSPEED(temp)) { - xhci_set_link_state(xhci, port_array, - port_index, XDEV_U0); - } else { + set_bit(port_index, &port_was_suspended); + if (!DEV_SUPERSPEED(temp)) { xhci_set_link_state(xhci, port_array, port_index, XDEV_RESUME); - - spin_unlock_irqrestore(&xhci->lock, flags); - msleep(20); - spin_lock_irqsave(&xhci->lock, flags); - - xhci_set_link_state(xhci, port_array, - port_index, XDEV_U0); + need_usb2_u3_exit = true; } - /* wait for the port to enter U0 and report port link - * state change. - */ - spin_unlock_irqrestore(&xhci->lock, flags); - msleep(20); - spin_lock_irqsave(&xhci->lock, flags); - - /* Clear PLC */ - xhci_test_and_clear_bit(xhci, port_array, port_index, - PORT_PLC); - - slot_id = xhci_find_slot_id_by_port(hcd, - xhci, port_index + 1); - if (slot_id) - xhci_ring_device(xhci, slot_id); } else writel(temp, port_array[port_index]); } + if (need_usb2_u3_exit) { + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(20); + spin_lock_irqsave(&xhci->lock, flags); + } + + port_index = max_ports; + while (port_index--) { + if (!(port_was_suspended & BIT(port_index))) + continue; + /* Clear PLC to poll it later after XDEV_U0 */ + xhci_test_and_clear_bit(xhci, port_array, port_index, PORT_PLC); + xhci_set_link_state(xhci, port_array, port_index, XDEV_U0); + } + + port_index = max_ports; + while (port_index--) { + if (!(port_was_suspended & BIT(port_index))) + continue; + /* Poll and Clear PLC */ + sret = xhci_handshake(port_array[port_index], PORT_PLC, + PORT_PLC, 10 * 1000); + if (sret) + xhci_warn(xhci, "port %d resume PLC timeout\n", + port_index); + xhci_test_and_clear_bit(xhci, port_array, port_index, PORT_PLC); + slot_id = xhci_find_slot_id_by_port(hcd, xhci, port_index + 1); + if (slot_id) + xhci_ring_device(xhci, slot_id); + } + (void) readl(&xhci->op_regs->command); bus_state->next_statechange = jiffies + msecs_to_jiffies(5); -- cgit v0.10.2 From 22ae47e65e7c1819e3598b16fcedc469ff97ec58 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 29 May 2015 17:01:53 +0300 Subject: xhci: Return correct number of tranferred bytes for stalled control endpoints Fix the xhci driver from bluntly setting the transferred length to 0 if we get a STALL on anything else than the data stage of a control transfer. Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index d7fd5efd..94416ff 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1934,7 +1934,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, td->urb->actual_length = td->urb->transfer_buffer_length - EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); - else + else if (!td->urb_length_set) td->urb->actual_length = 0; return finish_td(xhci, td, event_trb, event, ep, status, false); -- cgit v0.10.2 From 02c018af7db9d4e701e78b333dc951dca380633d Mon Sep 17 00:00:00 2001 From: Chris Bainbridge Date: Fri, 29 May 2015 17:01:54 +0300 Subject: usb: host: xhci: remove incorrect comment about mutex The comment stating that xhci_setup_device() is protected by the address mutex is not true since commit 6fecd4f2a58c ("USB: separate usb_address0 mutexes for each bus") as xhci handles two buses. Signed-off-by: Chris Bainbridge Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index e5568ba..afccda7 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3764,8 +3764,6 @@ disable_slot: /* * Issue an Address Device command and optionally send a corresponding * SetAddress request to the device. - * We should be protected by the usb_address0_mutex in hub_wq's hub_port_init, - * so we should only issue and wait on one address command at the same time. */ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, enum xhci_setup_dev setup) -- cgit v0.10.2 From f0e6a326deec8b51ee12f82a34057efd3d0979b8 Mon Sep 17 00:00:00 2001 From: Abhishek Bist Date: Wed, 27 May 2015 23:54:19 +0530 Subject: USB: hcd.h : Removed an unnecessary function prototype usb_find_interface_driver() This function is used to call in early version of linux kernel in order to find out the interface used by a usb device. But now it's use is completely abolished. So,it would be relevant to remove this obselete function from kernel mainline. Signed-off-by: Abhishek Bist Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 68b1e83..c9aa779 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -622,8 +622,6 @@ extern struct list_head usb_bus_list; extern struct mutex usb_bus_list_lock; extern wait_queue_head_t usb_kill_urb_queue; -extern int usb_find_interface_driver(struct usb_device *dev, - struct usb_interface *interface); #define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN)) -- cgit v0.10.2 From fb6d1f7df5d25299fd7b3e84b72b8851d3634764 Mon Sep 17 00:00:00 2001 From: Robert Schlabbach Date: Tue, 26 May 2015 00:27:30 +0200 Subject: usb: core: Fix USB 3.0 devices lost in NOTATTACHED state after a hub port reset Fix USB 3.0 devices lost in NOTATTACHED state after a hub port reset. Dissolve the function hub_port_finish_reset() completely and divide the actions to be taken into those which need to be done after each reset attempt and those which need to be done after the full procedure is complete, and place them in the appropriate places in hub_port_reset(). Also, remove an unneeded forward declaration of hub_port_reset(). Verbose Problem Description: USB 3.0 devices may be "lost for good" during a hub port reset. This makes Linux unable to boot from USB 3.0 devices in certain constellations of host controllers and devices, because the USB device is lost during initialization, preventing the rootfs from being mounted. The underlying problem is that in the affected constellations, during the processing inside hub_port_reset(), the hub link state goes from 0 to SS.inactive after the initial reset, and back to 0 again only after the following "warm" reset. However, hub_port_finish_reset() is called after each reset attempt and sets the state the connected USB device based on the "preliminary" status of the hot reset to USB_STATE_NOTATTACHED due to SS.inactive, yet when the following warm reset is complete and hub_port_finish_reset() is called again, its call to set the device to USB_STATE_DEFAULT is blocked by usb_set_device_state() which does not allow taking USB devices out of USB_STATE_NOTATTACHED state. Thanks to Alan Stern for guiding me to the proper solution and how to submit it. Link: http://lkml.kernel.org/r/trinity-25981484-72a9-4d46-bf17-9c1cf9301a31-1432073240136%20()%203capp-gmx-bs27 Signed-off-by: Robert Schlabbach Cc: stable Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 50da096..43cb2f2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2622,9 +2622,6 @@ static bool use_new_scheme(struct usb_device *udev, int retry) return USE_NEW_SCHEME(retry); } -static int hub_port_reset(struct usb_hub *hub, int port1, - struct usb_device *udev, unsigned int delay, bool warm); - /* Is a USB 3.0 port in the Inactive or Compliance Mode state? * Port worm reset is required to recover */ @@ -2712,44 +2709,6 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, return 0; } -static void hub_port_finish_reset(struct usb_hub *hub, int port1, - struct usb_device *udev, int *status) -{ - switch (*status) { - case 0: - /* TRSTRCY = 10 ms; plus some extra */ - msleep(10 + 40); - if (udev) { - struct usb_hcd *hcd = bus_to_hcd(udev->bus); - - update_devnum(udev, 0); - /* The xHC may think the device is already reset, - * so ignore the status. - */ - if (hcd->driver->reset_device) - hcd->driver->reset_device(hcd, udev); - } - /* FALL THROUGH */ - case -ENOTCONN: - case -ENODEV: - usb_clear_port_feature(hub->hdev, - port1, USB_PORT_FEAT_C_RESET); - if (hub_is_superspeed(hub->hdev)) { - usb_clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_BH_PORT_RESET); - usb_clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_PORT_LINK_STATE); - usb_clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_CONNECTION); - } - if (udev) - usb_set_device_state(udev, *status - ? USB_STATE_NOTATTACHED - : USB_STATE_DEFAULT); - break; - } -} - /* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */ static int hub_port_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay, bool warm) @@ -2773,13 +2732,10 @@ static int hub_port_reset(struct usb_hub *hub, int port1, * If the caller hasn't explicitly requested a warm reset, * double check and see if one is needed. */ - status = hub_port_status(hub, port1, - &portstatus, &portchange); - if (status < 0) - goto done; - - if (hub_port_warm_reset_required(hub, port1, portstatus)) - warm = true; + if (hub_port_status(hub, port1, &portstatus, &portchange) == 0) + if (hub_port_warm_reset_required(hub, port1, + portstatus)) + warm = true; } clear_bit(port1, hub->warm_reset_bits); @@ -2805,11 +2761,19 @@ static int hub_port_reset(struct usb_hub *hub, int port1, /* Check for disconnect or reset */ if (status == 0 || status == -ENOTCONN || status == -ENODEV) { - hub_port_finish_reset(hub, port1, udev, &status); + usb_clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_RESET); if (!hub_is_superspeed(hub->hdev)) goto done; + usb_clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_BH_PORT_RESET); + usb_clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_PORT_LINK_STATE); + usb_clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + /* * If a USB 3.0 device migrates from reset to an error * state, re-issue the warm reset. @@ -2842,6 +2806,26 @@ static int hub_port_reset(struct usb_hub *hub, int port1, dev_err(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n"); done: + if (status == 0) { + /* TRSTRCY = 10 ms; plus some extra */ + msleep(10 + 40); + if (udev) { + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + update_devnum(udev, 0); + /* The xHC may think the device is already reset, + * so ignore the status. + */ + if (hcd->driver->reset_device) + hcd->driver->reset_device(hcd, udev); + + usb_set_device_state(udev, USB_STATE_DEFAULT); + } + } else { + if (udev) + usb_set_device_state(udev, USB_STATE_NOTATTACHED); + } + if (!hub_is_superspeed(hub->hdev)) up_read(&ehci_cf_port_reset_rwsem); -- cgit v0.10.2 From d7c444e5467bab8d703c6c9b41ee615b081ec46c Mon Sep 17 00:00:00 2001 From: Nikhil Badola Date: Tue, 26 May 2015 17:14:42 +0530 Subject: drivers:usb:fsl: Check IP version 2.4 for mph USB controller Check IP version 2.4 for multi port host USB controller and return FSL_USB_VER_2_4 macro Signed-off-by: Nikhil Badola Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 7e325e9..e588ccd 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -126,6 +126,7 @@ static int usb_get_ver_info(struct device_node *np) /* * returns 1 for usb controller version 1.6 * returns 2 for usb controller version 2.2 + * returns 3 for usb controller version 2.4 * returns 0 otherwise */ if (of_device_is_compatible(np, "fsl-usb2-dr")) { @@ -150,6 +151,8 @@ static int usb_get_ver_info(struct device_node *np) ver = FSL_USB_VER_1_6; else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.2")) ver = FSL_USB_VER_2_2; + else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.4")) + ver = FSL_USB_VER_2_4; else /* for previous controller versions */ ver = FSL_USB_VER_OLD; } -- cgit v0.10.2 From 138c3f03b017e261316a4f1ec793e1ff74516def Mon Sep 17 00:00:00 2001 From: Nikhil Badola Date: Tue, 26 May 2015 17:15:29 +0530 Subject: drivers:usb:fsl: Add support for USB controller version-2.5 Add support for USB controller version-2.5 used in T4240 rev2.0, T1024, T1040, T2080, LS1021A Signed-off-by: Nikhil Badola Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index e588ccd..5e0d600 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -127,6 +127,7 @@ static int usb_get_ver_info(struct device_node *np) * returns 1 for usb controller version 1.6 * returns 2 for usb controller version 2.2 * returns 3 for usb controller version 2.4 + * returns 4 for usb controller version 2.5 * returns 0 otherwise */ if (of_device_is_compatible(np, "fsl-usb2-dr")) { @@ -136,6 +137,8 @@ static int usb_get_ver_info(struct device_node *np) ver = FSL_USB_VER_2_2; else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.4")) ver = FSL_USB_VER_2_4; + else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.5")) + ver = FSL_USB_VER_2_5; else /* for previous controller versions */ ver = FSL_USB_VER_OLD; @@ -153,6 +156,8 @@ static int usb_get_ver_info(struct device_node *np) ver = FSL_USB_VER_2_2; else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.4")) ver = FSL_USB_VER_2_4; + else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.5")) + ver = FSL_USB_VER_2_5; else /* for previous controller versions */ ver = FSL_USB_VER_OLD; } diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index a82296a..2a2f56b 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -24,6 +24,7 @@ #define FSL_USB_VER_1_6 1 #define FSL_USB_VER_2_2 2 #define FSL_USB_VER_2_4 3 +#define FSL_USB_VER_2_5 4 #include -- cgit v0.10.2 From 5e582ff309288898be3744f093ce2d726f4747fe Mon Sep 17 00:00:00 2001 From: Kazuya Mizuguchi Date: Tue, 26 May 2015 20:13:42 +0900 Subject: usb: renesas_usbhs: Fix fifo unclear in usbhsf_prepare_pop This patch fixes an issue for control write. When usbhsf_prepare_pop() is called after this driver called a gadget setup function, this controller doesn't receive the control write data. So, this patch adds a code to clear the fifo for control write in usbhsf_prepare_pop(). Signed-off-by: Kazuya Mizuguchi Signed-off-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 8597cf9..50f4e3a 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -611,6 +611,8 @@ struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = { static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done) { struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); if (usbhs_pipe_is_busy(pipe)) return 0; @@ -624,6 +626,9 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done) usbhs_pipe_data_sequence(pipe, pkt->sequence); pkt->sequence = -1; /* -1 sequence will be ignored */ + if (usbhs_pipe_is_dcp(pipe)) + usbhsf_fifo_clear(pipe, fifo); + usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length); usbhs_pipe_enable(pipe); usbhs_pipe_running(pipe, 1); -- cgit v0.10.2 From c5d496ad98c212e7d074040504b7737afb4d8bd7 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 26 May 2015 20:13:43 +0900 Subject: usb: renesas_usbhs: Don't disable the pipe if Control write status stage This patch fixes an issue that sometimes this controller is not able to complete the Control write status stage. This driver should enable DCPCTR.CCPL and PID_BUF to complete the status stage. However, if this driver detects the ctrl_stage interruption first before the control write data is received, this driver will clear the PID_BUF wrongly in the usbhsf_pio_try_pop(). To avoid this issue, this patch doesn't clear the PID_BUF in the usbhsf_pio_try_pop(). (Since also the privious code doesn't disable the PID_BUF after a control transfer was finished, this patch doesn't have any side efforts.) Signed-off-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 50f4e3a..fa126a2 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -678,7 +678,14 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done) *is_done = 1; usbhsf_rx_irq_ctrl(pipe, 0); usbhs_pipe_running(pipe, 0); - usbhs_pipe_disable(pipe); /* disable pipe first */ + /* + * If function mode, since this controller is possible to enter + * Control Write status stage at this timing, this driver + * should not disable the pipe. If such a case happens, this + * controller is not able to complete the status stage. + */ + if (!usbhs_mod_is_host(priv) && !usbhs_pipe_is_dcp(pipe)) + usbhs_pipe_disable(pipe); /* disable pipe first */ } /* -- cgit v0.10.2 From 74db22cb3a16dcd782a31236eb139f5865804ae6 Mon Sep 17 00:00:00 2001 From: Ramneek Mehresh Date: Fri, 29 May 2015 11:28:30 +0530 Subject: drivers:usb:fsl: Fix compilation error for fsl ehci drv Fix compilation error in fsl ehci drv because ehci_reset() and ehci_adjust_port_wakeup_flags() were not exported, and are used when PM is enabled Signed-off-by: Ramneek Mehresh Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 9dd161c..c63d82c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -239,7 +239,7 @@ static void tdi_reset (struct ehci_hcd *ehci) * Reset a non-running (STS_HALT == 1) controller. * Must be called with interrupts enabled and the lock not held. */ -static int ehci_reset (struct ehci_hcd *ehci) +int ehci_reset(struct ehci_hcd *ehci) { int retval; u32 command = ehci_readl(ehci, &ehci->regs->command); @@ -275,6 +275,7 @@ static int ehci_reset (struct ehci_hcd *ehci) ehci->resuming_ports = 0; return retval; } +EXPORT_SYMBOL_GPL(ehci_reset); /* * Idle the controller (turn off the schedules). diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 6920844..22abb68 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -155,7 +155,7 @@ static int ehci_port_change(struct ehci_hcd *ehci) return 0; } -static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, +void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, bool suspending, bool do_wakeup) { int port; @@ -220,6 +220,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, spin_unlock_irq(&ehci->lock); } +EXPORT_SYMBOL_GPL(ehci_adjust_port_wakeup_flags); static int ehci_bus_suspend (struct usb_hcd *hcd) { diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 52ef084..f700157 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -868,10 +868,13 @@ extern void ehci_init_driver(struct hc_driver *drv, extern int ehci_setup(struct usb_hcd *hcd); extern int ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr, u32 mask, u32 done, int usec); +extern int ehci_reset(struct ehci_hcd *ehci); #ifdef CONFIG_PM extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup); extern int ehci_resume(struct usb_hcd *hcd, bool force_reset); +extern void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, + bool suspending, bool do_wakeup); #endif /* CONFIG_PM */ extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, -- cgit v0.10.2 From 56301df6bcaaed31e77b8c500ca1b437f46a3158 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 16 Apr 2015 18:03:04 +1000 Subject: phy: twl4030-usb: make runtime pm more reliable. A construct like: if (pm_runtime_suspended(twl->dev)) pm_runtime_get_sync(twl->dev); is against the spirit of the runtime_pm interface as it makes the internal refcounting useless. In this case it is also racy, particularly as 'put_autosuspend' is used to drop a reference. When that happens a timer is started and the device is runtime-suspended after the timeout. If the above code runs in this window, the device will not be found to be suspended so no pm_runtime reference is taken. When the timer expires the device will be suspended, which is against the intention of the code. So be more direct is taking and dropping references. If twl->linkstat is VBUS_VALID or ID_GROUND, then hold a pm_runtime reference, otherwise don't. Define "cable_present()" to test for this condition. Tested-by: Tony Lindgren Signed-off-by: NeilBrown Signed-off-by: Kishon Vijay Abraham I diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index bc42d6a..3078f80 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -144,6 +144,16 @@ #define PMBR1 0x0D #define GPIO_USB_4PIN_ULPI_2430C (3 << 0) +/* + * If VBUS is valid or ID is ground, then we know a + * cable is present and we need to be runtime-enabled + */ +static inline bool cable_present(enum omap_musb_vbus_id_status stat) +{ + return stat == OMAP_MUSB_VBUS_VALID || + stat == OMAP_MUSB_ID_GROUND; +} + struct twl4030_usb { struct usb_phy phy; struct device *dev; @@ -536,8 +546,10 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) mutex_lock(&twl->lock); if (status >= 0 && status != twl->linkstat) { + status_changed = + cable_present(twl->linkstat) != + cable_present(status); twl->linkstat = status; - status_changed = true; } mutex_unlock(&twl->lock); @@ -553,15 +565,11 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) * USB_LINK_VBUS state. musb_hdrc won't care until it * starts to handle softconnect right. */ - if ((status == OMAP_MUSB_VBUS_VALID) || - (status == OMAP_MUSB_ID_GROUND)) { - if (pm_runtime_suspended(twl->dev)) - pm_runtime_get_sync(twl->dev); + if (cable_present(status)) { + pm_runtime_get_sync(twl->dev); } else { - if (pm_runtime_active(twl->dev)) { - pm_runtime_mark_last_busy(twl->dev); - pm_runtime_put_autosuspend(twl->dev); - } + pm_runtime_mark_last_busy(twl->dev); + pm_runtime_put_autosuspend(twl->dev); } omap_musb_mailbox(status); } @@ -767,6 +775,9 @@ static int twl4030_usb_remove(struct platform_device *pdev) /* disable complete OTG block */ twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); + + if (cable_present(twl->linkstat)) + pm_runtime_put_noidle(twl->dev); pm_runtime_mark_last_busy(twl->dev); pm_runtime_put(twl->dev); -- cgit v0.10.2 From 186ecebeb60d5271c519450d71026901d9741714 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 16 Apr 2015 18:03:04 +1000 Subject: phy: twl4030-usb: remove pointless 'suspended' test in 'suspend' callback. When the runtime_suspend callback is running, 'runtime_status' is always RPM_SUSPENDING, so pm_runtime_suspended() will always fail. Similarly while the runtime_resume callback is running 'runtime_status' is RPM_RESUMING, so pm_runtime_active() will always fail. So remove these two pointless tests. Signed-off-by: NeilBrown Signed-off-by: Kishon Vijay Abraham I diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 3078f80..590c2b1 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -396,8 +396,6 @@ static int twl4030_usb_runtime_suspend(struct device *dev) struct twl4030_usb *twl = dev_get_drvdata(dev); dev_dbg(twl->dev, "%s\n", __func__); - if (pm_runtime_suspended(dev)) - return 0; __twl4030_phy_power(twl, 0); regulator_disable(twl->usb1v5); @@ -413,8 +411,6 @@ static int twl4030_usb_runtime_resume(struct device *dev) int res; dev_dbg(twl->dev, "%s\n", __func__); - if (pm_runtime_active(dev)) - return 0; res = regulator_enable(twl->usb3v1); if (res) -- cgit v0.10.2 From 4724e27114c4a7eceeee07db227a17fcab6f165c Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 16 Apr 2015 18:03:04 +1000 Subject: phy: twl4030-usb: remove incorrect pm_runtime_get_sync() in probe function. The USB phy should initialize with power-off, and will be powered on by the USB system when a cable connection is detected. Having this pm_runtime_get_sync() during probe causes the phy to *always* be powered on. Removing it returns to sensible power management. Fixes: 96be39ab34b77c6f6f5cd6ae03aac6c6449ee5c4 Signed-off-by: NeilBrown Signed-off-by: Kishon Vijay Abraham I diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 590c2b1..3a707dd 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -715,7 +715,6 @@ static int twl4030_usb_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); /* Our job is to use irqs and status from the power module * to keep the transceiver disabled when nothing's connected. -- cgit v0.10.2 From 692fbb89fa0b8028f402e33eb912474e11b5f435 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 16 Apr 2015 18:03:04 +1000 Subject: phy: twl4030-usb: add ABI documentation This driver device one local attribute: vbus. Describe that in Documentation/ABI/testing/sysfs-platform/twl4030-usb. Signed-off-by: NeilBrown Signed-off-by: Kishon Vijay Abraham I diff --git a/Documentation/ABI/testing/sysfs-platform-twl4030-usb b/Documentation/ABI/testing/sysfs-platform-twl4030-usb new file mode 100644 index 0000000..512c51b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-twl4030-usb @@ -0,0 +1,8 @@ +What: /sys/bus/platform/devices/*twl4030-usb/vbus +Description: + Read-only status reporting if VBUS (approx 5V) + is being supplied by the USB bus. + + Possible values: "on", "off". + + Changes are notified via select/poll. -- cgit v0.10.2 From f05b7cb6f2beed0869669cc4748341560eba2aef Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Mon, 1 Jun 2015 06:41:57 +0530 Subject: USB: serial: mos7840: Use setup_timer Use the timer API function setup_timer instead of structure field assignments to initialize a timer. A simplified version of the Coccinelle semantic patch that performs this transformation is as follows: @change@ expression e1, e2, e3, e4, a, b; @@ -init_timer(&e1); +setup_timer(&e1, a, b); ... when != a = e2 when != b = e3 -e1.function = a; ... when != b = e4 -e1.data = b; Signed-off-by: Vaishali Thakkar Signed-off-by: Johan Hovold diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index e4473a9..8ac9b55 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -2301,17 +2301,14 @@ static int mos7840_port_probe(struct usb_serial_port *port) goto error; } - init_timer(&mos7840_port->led_timer1); - mos7840_port->led_timer1.function = mos7840_led_off; + setup_timer(&mos7840_port->led_timer1, mos7840_led_off, + (unsigned long)mos7840_port); mos7840_port->led_timer1.expires = jiffies + msecs_to_jiffies(LED_ON_MS); - mos7840_port->led_timer1.data = (unsigned long)mos7840_port; - - init_timer(&mos7840_port->led_timer2); - mos7840_port->led_timer2.function = mos7840_led_flag_off; + setup_timer(&mos7840_port->led_timer2, mos7840_led_flag_off, + (unsigned long)mos7840_port); mos7840_port->led_timer2.expires = jiffies + msecs_to_jiffies(LED_OFF_MS); - mos7840_port->led_timer2.data = (unsigned long)mos7840_port; /* Turn off LED */ mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300); -- cgit v0.10.2 From f50420223071b6ff4b586308f5c27eec54694a81 Mon Sep 17 00:00:00 2001 From: Teunis van Beelen Date: Sun, 31 May 2015 09:36:22 +0200 Subject: USB: usbtmc: add device quirk for Rigol DS6104 Recently we purchased the Rigol DS6104 and when I try to operate it from my Linux pc, everything works well with the default usbtmc driver, except when I want to download a big datachunk like a screenshot. This bitmapfile has a size of 1152054 bytes but I receive a smaller file and no new packets can be read. When I took a look at the driver source, I found this "Rigol quirk" and I added the id of the new DS series oscilloscopes to this list. I compiled it and loaded the new driver and now everything seems to work fine. Signed-off-by: Teunis van Beelen Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 960bc08..7a11a82 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -109,6 +109,7 @@ struct usbtmc_ID_rigol_quirk { static const struct usbtmc_ID_rigol_quirk usbtmc_id_quirk[] = { { 0x1ab1, 0x0588 }, + { 0x1ab1, 0x04b0 }, { 0, 0 } }; -- cgit v0.10.2 From aa90e9904dbc9d2ae4d333f1ff58018374d545d5 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 9 May 2015 12:15:24 -0300 Subject: usb: chipidea: usbmisc_imx: Remove unneeded semicolon Remove unneeded semicolon. The semantic patch that makes this change is available in scripts/coccinelle/misc/semicolon.cocci. More information about semantic patching is available at http://coccinelle.lip6.fr/ Signed-off-by: Fabio Estevam Signed-off-by: Peter Chen diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 140945c..3cefd49 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -160,7 +160,7 @@ static int usbmisc_imx27_init(struct imx_usbmisc_data *data) break; default: return -EINVAL; - }; + } spin_lock_irqsave(&usbmisc->lock, flags); if (data->disable_oc) -- cgit v0.10.2 From 15bf722e6f6c0b884521a0363204532e849deb7f Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Tue, 2 Jun 2015 11:49:30 +0300 Subject: cdc-acm: Add support of ATOL FPrint fiscal printers ATOL FPrint fiscal printers require usb_clear_halt to be executed to work properly. Add quirk to fix the issue. Signed-off-by: Alexey Sokolov Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index eb22a06..519a77b 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1467,6 +1467,11 @@ skip_countries: goto alloc_fail8; } + if (quirks & CLEAR_HALT_CONDITIONS) { + usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress)); + usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress)); + } + return 0; alloc_fail8: if (acm->country_codes) { @@ -1746,6 +1751,10 @@ static const struct usb_device_id acm_ids[] = { .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, + { USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */ + .driver_info = CLEAR_HALT_CONDITIONS, + }, + /* Nokia S60 phones expose two ACM channels. The first is * a modem and is picked up by the standard AT-command * information below. The second is 'vendor-specific' but diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index a9e68ce..dd9af38 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -133,3 +133,4 @@ struct acm { #define NO_DATA_INTERFACE BIT(4) #define IGNORE_DEVICE BIT(5) #define QUIRK_CONTROL_LINE_STATE BIT(6) +#define CLEAR_HALT_CONDITIONS BIT(7) -- cgit v0.10.2 From e3e64f3f99d9e70973fb1ae7d02825fe2bb9eef2 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 2 Jun 2015 19:05:13 +0100 Subject: usb: isp1760: check for null return from kzalloc isp1760_ep_alloc_request allocates a structure with kzalloc without checking for NULL and then returns a pointer to one of the structure fields. As the field happens to be the first in the structure the caller can properly check for NULL, but this is risky if the structure layout is changed later. Add an explicit NULL check for the kzalloc return value Detected with smatch static analysis: drivers/usb/isp1760/isp1760-udc.c:816 isp1760_ep_alloc_request() error: potential null dereference 'req'. (kzalloc returns null) [ thanks to Laurent Pinchart for improved commit message ] Signed-off-by: Colin Ian King Acked-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c index 3fc4fe7..18ebf5b 100644 --- a/drivers/usb/isp1760/isp1760-udc.c +++ b/drivers/usb/isp1760/isp1760-udc.c @@ -812,6 +812,8 @@ static struct usb_request *isp1760_ep_alloc_request(struct usb_ep *ep, struct isp1760_request *req; req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; return &req->req; } -- cgit v0.10.2 From 87997195e341f2ca175e793460a66f5170f76921 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 6 Jun 2015 21:53:03 +0200 Subject: USB: ssb: fix error handling in ssb_hcd_create_pdev() This patch makes bcma_hcd_create_pdev() not return NULL, but a prober error code in case of an error. Signed-off-by: Hauke Mehrtens Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ssb-hcd.c b/drivers/usb/host/ssb-hcd.c index ffc32f4..623cb0b 100644 --- a/drivers/usb/host/ssb-hcd.c +++ b/drivers/usb/host/ssb-hcd.c @@ -105,7 +105,7 @@ static struct platform_device *ssb_hcd_create_pdev(struct ssb_device *dev, bool { struct platform_device *hci_dev; struct resource hci_res[2]; - int ret = -ENOMEM; + int ret; memset(hci_res, 0, sizeof(hci_res)); @@ -119,7 +119,7 @@ static struct platform_device *ssb_hcd_create_pdev(struct ssb_device *dev, bool hci_dev = platform_device_alloc(ohci ? "ohci-platform" : "ehci-platform" , 0); if (!hci_dev) - return NULL; + return ERR_PTR(-ENOMEM); hci_dev->dev.parent = dev->dev; hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask; -- cgit v0.10.2 From b0a252ed983b8e6339e01749029749adbaa890de Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 6 Jun 2015 21:53:04 +0200 Subject: USB: ssb: use devm_kzalloc Instead of manually handling the frees use devm. There was also a free missing in the unregister call which is not needed with devm. Signed-off-by: Hauke Mehrtens Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ssb-hcd.c b/drivers/usb/host/ssb-hcd.c index 623cb0b..62b6b78 100644 --- a/drivers/usb/host/ssb-hcd.c +++ b/drivers/usb/host/ssb-hcd.c @@ -166,7 +166,8 @@ static int ssb_hcd_probe(struct ssb_device *dev, if (dma_set_mask_and_coherent(dev->dma_dev, DMA_BIT_MASK(32))) return -EOPNOTSUPP; - usb_dev = kzalloc(sizeof(struct ssb_hcd_device), GFP_KERNEL); + usb_dev = devm_kzalloc(dev->dev, sizeof(struct ssb_hcd_device), + GFP_KERNEL); if (!usb_dev) return -ENOMEM; @@ -181,10 +182,8 @@ static int ssb_hcd_probe(struct ssb_device *dev, start = ssb_admatch_base(tmp); len = (coreid == SSB_DEV_USB20_HOST) ? 0x800 : ssb_admatch_size(tmp); usb_dev->ohci_dev = ssb_hcd_create_pdev(dev, true, start, len); - if (IS_ERR(usb_dev->ohci_dev)) { - err = PTR_ERR(usb_dev->ohci_dev); - goto err_free_usb_dev; - } + if (IS_ERR(usb_dev->ohci_dev)) + return PTR_ERR(usb_dev->ohci_dev); if (coreid == SSB_DEV_USB20_HOST) { start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */ @@ -200,8 +199,6 @@ static int ssb_hcd_probe(struct ssb_device *dev, err_unregister_ohci_dev: platform_device_unregister(usb_dev->ohci_dev); -err_free_usb_dev: - kfree(usb_dev); return err; } -- cgit v0.10.2 From 6c95ff9c1c4842b1b818fe7c6ce6753c646baf8a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 29 May 2015 11:38:41 -0500 Subject: dt-bindings: Add Marvell PXA1928 USB and HSIC PHY bindings Add PHY binding for Marvell PXA1928 SOC's USB and HSIC PHYs. Signed-off-by: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Cc: devicetree@vger.kernel.org Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/phy/pxa1928-usb-phy.txt b/Documentation/devicetree/bindings/phy/pxa1928-usb-phy.txt new file mode 100644 index 0000000..660a13c --- /dev/null +++ b/Documentation/devicetree/bindings/phy/pxa1928-usb-phy.txt @@ -0,0 +1,18 @@ +* Marvell PXA1928 USB and HSIC PHYs + +Required properties: +- compatible: "marvell,pxa1928-usb-phy" or "marvell,pxa1928-hsic-phy" +- reg: base address and length of the registers +- clocks - A single clock. From common clock binding. +- #phys-cells: should be 0. From commmon phy binding. +- resets: reference to the reset controller + +Example: + + usbphy: phy@7000 { + compatible = "marvell,pxa1928-usb-phy"; + reg = <0x7000 0xe0>; + clocks = <&apmu_clocks PXA1928_CLK_USB>; + #phy-cells = <0>; + }; + -- cgit v0.10.2 From 603c5f9d9c52ff5744b7f35ead1891478fb6a569 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 29 May 2015 11:38:42 -0500 Subject: phy: Add Marvell USB 2.0 OTG 28nm PHY Add driver for USB 28nm PHY found in Marvell PXA1928 SOC. Signed-off-by: Rob Herring Cc: Kishon Vijay Abraham I Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 15cab07..e3785e1 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -54,6 +54,16 @@ config PHY_EXYNOS_MIPI_VIDEO Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P and EXYNOS SoCs. +config PHY_PXA_28NM_USB2 + tristate "Marvell USB 2.0 28nm PHY Driver" + select GENERIC_PHY + help + Enable this to support Marvell USB 2.0 PHY driver for Marvell + SoC. This driver will do the PHY initialization and shutdown. + The PHY driver will be used by Marvell udc/ehci/otg driver. + + To compile this driver as a module, choose M here. + config PHY_MVEBU_SATA def_bool y depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index bc92b42..f2670df 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o +obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o diff --git a/drivers/phy/phy-pxa-28nm-usb2.c b/drivers/phy/phy-pxa-28nm-usb2.c new file mode 100644 index 0000000..37e9c8c --- /dev/null +++ b/drivers/phy/phy-pxa-28nm-usb2.c @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2015 Linaro, Ltd. + * Rob Herring + * + * Based on vendor driver: + * Copyright (C) 2013 Marvell Inc. + * Author: Chao Xie + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* USB PXA1928 PHY mapping */ +#define PHY_28NM_PLL_REG0 0x0 +#define PHY_28NM_PLL_REG1 0x4 +#define PHY_28NM_CAL_REG 0x8 +#define PHY_28NM_TX_REG0 0x0c +#define PHY_28NM_TX_REG1 0x10 +#define PHY_28NM_RX_REG0 0x14 +#define PHY_28NM_RX_REG1 0x18 +#define PHY_28NM_DIG_REG0 0x1c +#define PHY_28NM_DIG_REG1 0x20 +#define PHY_28NM_TEST_REG0 0x24 +#define PHY_28NM_TEST_REG1 0x28 +#define PHY_28NM_MOC_REG 0x2c +#define PHY_28NM_PHY_RESERVE 0x30 +#define PHY_28NM_OTG_REG 0x34 +#define PHY_28NM_CHRG_DET 0x38 +#define PHY_28NM_CTRL_REG0 0xc4 +#define PHY_28NM_CTRL_REG1 0xc8 +#define PHY_28NM_CTRL_REG2 0xd4 +#define PHY_28NM_CTRL_REG3 0xdc + +/* PHY_28NM_PLL_REG0 */ +#define PHY_28NM_PLL_READY BIT(31) + +#define PHY_28NM_PLL_SELLPFR_SHIFT 28 +#define PHY_28NM_PLL_SELLPFR_MASK (0x3 << 28) + +#define PHY_28NM_PLL_FBDIV_SHIFT 16 +#define PHY_28NM_PLL_FBDIV_MASK (0x1ff << 16) + +#define PHY_28NM_PLL_ICP_SHIFT 8 +#define PHY_28NM_PLL_ICP_MASK (0x7 << 8) + +#define PHY_28NM_PLL_REFDIV_SHIFT 0 +#define PHY_28NM_PLL_REFDIV_MASK 0x7f + +/* PHY_28NM_PLL_REG1 */ +#define PHY_28NM_PLL_PU_BY_REG BIT(1) + +#define PHY_28NM_PLL_PU_PLL BIT(0) + +/* PHY_28NM_CAL_REG */ +#define PHY_28NM_PLL_PLLCAL_DONE BIT(31) + +#define PHY_28NM_PLL_IMPCAL_DONE BIT(23) + +#define PHY_28NM_PLL_KVCO_SHIFT 16 +#define PHY_28NM_PLL_KVCO_MASK (0x7 << 16) + +#define PHY_28NM_PLL_CAL12_SHIFT 20 +#define PHY_28NM_PLL_CAL12_MASK (0x3 << 20) + +#define PHY_28NM_IMPCAL_VTH_SHIFT 8 +#define PHY_28NM_IMPCAL_VTH_MASK (0x7 << 8) + +#define PHY_28NM_PLLCAL_START_SHIFT 22 +#define PHY_28NM_IMPCAL_START_SHIFT 13 + +/* PHY_28NM_TX_REG0 */ +#define PHY_28NM_TX_PU_BY_REG BIT(25) + +#define PHY_28NM_TX_PU_ANA BIT(24) + +#define PHY_28NM_TX_AMP_SHIFT 20 +#define PHY_28NM_TX_AMP_MASK (0x7 << 20) + +/* PHY_28NM_RX_REG0 */ +#define PHY_28NM_RX_SQ_THRESH_SHIFT 0 +#define PHY_28NM_RX_SQ_THRESH_MASK (0xf << 0) + +/* PHY_28NM_RX_REG1 */ +#define PHY_28NM_RX_SQCAL_DONE BIT(31) + +/* PHY_28NM_DIG_REG0 */ +#define PHY_28NM_DIG_BITSTAFFING_ERR BIT(31) +#define PHY_28NM_DIG_SYNC_ERR BIT(30) + +#define PHY_28NM_DIG_SQ_FILT_SHIFT 16 +#define PHY_28NM_DIG_SQ_FILT_MASK (0x7 << 16) + +#define PHY_28NM_DIG_SQ_BLK_SHIFT 12 +#define PHY_28NM_DIG_SQ_BLK_MASK (0x7 << 12) + +#define PHY_28NM_DIG_SYNC_NUM_SHIFT 0 +#define PHY_28NM_DIG_SYNC_NUM_MASK (0x3 << 0) + +#define PHY_28NM_PLL_LOCK_BYPASS BIT(7) + +/* PHY_28NM_OTG_REG */ +#define PHY_28NM_OTG_CONTROL_BY_PIN BIT(5) +#define PHY_28NM_OTG_PU_OTG BIT(4) + +#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 13 +#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28 12 +#define PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28 10 +#define PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28 8 +#define PHY_28NM_CHGDTC_CDP_DM_AUTO_SWITCH_SHIFT_28 7 +#define PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28 6 +#define PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28 5 +#define PHY_28NM_CHGDTC_PD_EN_SHIFT_28 4 +#define PHY_28NM_CHGDTC_DCP_EN_SHIFT_28 3 +#define PHY_28NM_CHGDTC_CDP_EN_SHIFT_28 2 +#define PHY_28NM_CHGDTC_TESTMON_CHRGDTC_SHIFT_28 0 + +#define PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28 4 +#define PHY_28NM_CTRL1_VBUSDTC_OUT_SHIFT_28 2 + +#define PHY_28NM_CTRL3_OVERWRITE BIT(0) +#define PHY_28NM_CTRL3_VBUS_VALID BIT(4) +#define PHY_28NM_CTRL3_AVALID BIT(5) +#define PHY_28NM_CTRL3_BVALID BIT(6) + +struct mv_usb2_phy { + struct phy *phy; + struct platform_device *pdev; + void __iomem *base; + struct clk *clk; +}; + +static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) +{ + timeout += jiffies; + while (time_is_after_eq_jiffies(timeout)) { + if ((readl(reg) & mask) == mask) + return true; + msleep(1); + } + return false; +} + +static int mv_usb2_phy_28nm_init(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + struct platform_device *pdev = mv_phy->pdev; + void __iomem *base = mv_phy->base; + u32 reg; + int ret; + + clk_prepare_enable(mv_phy->clk); + + /* PHY_28NM_PLL_REG0 */ + reg = readl(base + PHY_28NM_PLL_REG0) & + ~(PHY_28NM_PLL_SELLPFR_MASK | PHY_28NM_PLL_FBDIV_MASK + | PHY_28NM_PLL_ICP_MASK | PHY_28NM_PLL_REFDIV_MASK); + writel(reg | (0x1 << PHY_28NM_PLL_SELLPFR_SHIFT + | 0xf0 << PHY_28NM_PLL_FBDIV_SHIFT + | 0x3 << PHY_28NM_PLL_ICP_SHIFT + | 0xd << PHY_28NM_PLL_REFDIV_SHIFT), + base + PHY_28NM_PLL_REG0); + + /* PHY_28NM_PLL_REG1 */ + reg = readl(base + PHY_28NM_PLL_REG1); + writel(reg | PHY_28NM_PLL_PU_PLL | PHY_28NM_PLL_PU_BY_REG, + base + PHY_28NM_PLL_REG1); + + /* PHY_28NM_TX_REG0 */ + reg = readl(base + PHY_28NM_TX_REG0) & ~PHY_28NM_TX_AMP_MASK; + writel(reg | PHY_28NM_TX_PU_BY_REG | 0x3 << PHY_28NM_TX_AMP_SHIFT | + PHY_28NM_TX_PU_ANA, + base + PHY_28NM_TX_REG0); + + /* PHY_28NM_RX_REG0 */ + reg = readl(base + PHY_28NM_RX_REG0) & ~PHY_28NM_RX_SQ_THRESH_MASK; + writel(reg | 0xa << PHY_28NM_RX_SQ_THRESH_SHIFT, + base + PHY_28NM_RX_REG0); + + /* PHY_28NM_DIG_REG0 */ + reg = readl(base + PHY_28NM_DIG_REG0) & + ~(PHY_28NM_DIG_BITSTAFFING_ERR | PHY_28NM_DIG_SYNC_ERR | + PHY_28NM_DIG_SQ_FILT_MASK | PHY_28NM_DIG_SQ_BLK_MASK | + PHY_28NM_DIG_SYNC_NUM_MASK); + writel(reg | (0x1 << PHY_28NM_DIG_SYNC_NUM_SHIFT | + PHY_28NM_PLL_LOCK_BYPASS), + base + PHY_28NM_DIG_REG0); + + /* PHY_28NM_OTG_REG */ + reg = readl(base + PHY_28NM_OTG_REG) | PHY_28NM_OTG_PU_OTG; + writel(reg & ~PHY_28NM_OTG_CONTROL_BY_PIN, base + PHY_28NM_OTG_REG); + + /* + * Calibration Timing + * ____________________________ + * CAL START ___| + * ____________________ + * CAL_DONE ___________| + * | 400us | + */ + + /* Make sure PHY Calibration is ready */ + if (!wait_for_reg(base + PHY_28NM_CAL_REG, + PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE, + HZ / 10)) { + dev_warn(&pdev->dev, "USB PHY PLL calibrate not done after 100mS."); + ret = -ETIMEDOUT; + goto err_clk; + } + if (!wait_for_reg(base + PHY_28NM_RX_REG1, + PHY_28NM_RX_SQCAL_DONE, HZ / 10)) { + dev_warn(&pdev->dev, "USB PHY RX SQ calibrate not done after 100mS."); + ret = -ETIMEDOUT; + goto err_clk; + } + /* Make sure PHY PLL is ready */ + if (!wait_for_reg(base + PHY_28NM_PLL_REG0, + PHY_28NM_PLL_READY, HZ / 10)) { + dev_warn(&pdev->dev, "PLL_READY not set after 100mS."); + ret = -ETIMEDOUT; + goto err_clk; + } + + return 0; +err_clk: + clk_disable_unprepare(mv_phy->clk); + return ret; +} + +static int mv_usb2_phy_28nm_power_on(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + writel(readl(base + PHY_28NM_CTRL_REG3) | + (PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID | + PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), + base + PHY_28NM_CTRL_REG3); + + return 0; +} + +static int mv_usb2_phy_28nm_power_off(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + writel(readl(base + PHY_28NM_CTRL_REG3) | + ~(PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID + | PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), + base + PHY_28NM_CTRL_REG3); + + return 0; +} + +static int mv_usb2_phy_28nm_exit(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + unsigned int val; + + val = readw(base + PHY_28NM_PLL_REG1); + val &= ~PHY_28NM_PLL_PU_PLL; + writew(val, base + PHY_28NM_PLL_REG1); + + /* power down PHY Analog part */ + val = readw(base + PHY_28NM_TX_REG0); + val &= ~PHY_28NM_TX_PU_ANA; + writew(val, base + PHY_28NM_TX_REG0); + + /* power down PHY OTG part */ + val = readw(base + PHY_28NM_OTG_REG); + val &= ~PHY_28NM_OTG_PU_OTG; + writew(val, base + PHY_28NM_OTG_REG); + + clk_disable_unprepare(mv_phy->clk); + return 0; +} + +static const struct phy_ops usb_ops = { + .init = mv_usb2_phy_28nm_init, + .power_on = mv_usb2_phy_28nm_power_on, + .power_off = mv_usb2_phy_28nm_power_off, + .exit = mv_usb2_phy_28nm_exit, + .owner = THIS_MODULE, +}; + +static int mv_usb2_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct mv_usb2_phy *mv_phy; + struct resource *r; + + mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); + if (!mv_phy) + return -ENOMEM; + + mv_phy->pdev = pdev; + + mv_phy->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mv_phy->clk)) { + dev_err(&pdev->dev, "failed to get clock.\n"); + return PTR_ERR(mv_phy->clk); + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mv_phy->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(mv_phy->base)) + return PTR_ERR(mv_phy->base); + + mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &usb_ops); + if (IS_ERR(mv_phy->phy)) + return PTR_ERR(mv_phy->phy); + + phy_set_drvdata(mv_phy->phy, mv_phy); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id mv_usbphy_dt_match[] = { + { .compatible = "marvell,pxa1928-usb-phy", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mv_usbphy_dt_match); + +static struct platform_driver mv_usb2_phy_driver = { + .probe = mv_usb2_phy_probe, + .driver = { + .name = "mv-usb2-phy", + .of_match_table = of_match_ptr(mv_usbphy_dt_match), + }, +}; +module_platform_driver(mv_usb2_phy_driver); + +MODULE_AUTHOR("Rob Herring "); +MODULE_DESCRIPTION("Marvell USB2 phy driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 10d9029bcb2e27a812da81d3e9b598e307be4fe1 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 29 May 2015 11:38:43 -0500 Subject: phy: add Marvell HSIC 28nm PHY Add PHY driver for the Marvell HSIC 28nm PHY. This PHY is found in PXA1928 SOC. Signed-off-by: Rob Herring Cc: Kishon Vijay Abraham I Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index e3785e1..487d057 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -54,6 +54,16 @@ config PHY_EXYNOS_MIPI_VIDEO Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P and EXYNOS SoCs. +config PHY_PXA_28NM_HSIC + tristate "Marvell USB HSIC 28nm PHY Driver" + select GENERIC_PHY + help + Enable this to support Marvell USB HSIC PHY driver for Marvell + SoC. This driver will do the PHY initialization and shutdown. + The PHY driver will be used by Marvell ehci driver. + + To compile this driver as a module, choose M here. + config PHY_PXA_28NM_USB2 tristate "Marvell USB 2.0 28nm PHY Driver" select GENERIC_PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f2670df..42f58e9 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o +obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o diff --git a/drivers/phy/phy-pxa-28nm-hsic.c b/drivers/phy/phy-pxa-28nm-hsic.c new file mode 100644 index 0000000..234aacf --- /dev/null +++ b/drivers/phy/phy-pxa-28nm-hsic.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2015 Linaro, Ltd. + * Rob Herring + * + * Based on vendor driver: + * Copyright (C) 2013 Marvell Inc. + * Author: Chao Xie + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHY_28NM_HSIC_CTRL 0x08 +#define PHY_28NM_HSIC_IMPCAL_CAL 0x18 +#define PHY_28NM_HSIC_PLL_CTRL01 0x1c +#define PHY_28NM_HSIC_PLL_CTRL2 0x20 +#define PHY_28NM_HSIC_INT 0x28 + +#define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT 26 +#define PHY_28NM_HSIC_PLL_FBDIV_SHIFT 0 +#define PHY_28NM_HSIC_PLL_REFDIV_SHIFT 9 + +#define PHY_28NM_HSIC_S2H_PU_PLL BIT(10) +#define PHY_28NM_HSIC_H2S_PLL_LOCK BIT(15) +#define PHY_28NM_HSIC_S2H_HSIC_EN BIT(7) +#define S2H_DRV_SE0_4RESUME BIT(14) +#define PHY_28NM_HSIC_H2S_IMPCAL_DONE BIT(27) + +#define PHY_28NM_HSIC_CONNECT_INT BIT(1) +#define PHY_28NM_HSIC_HS_READY_INT BIT(2) + +struct mv_hsic_phy { + struct phy *phy; + struct platform_device *pdev; + void __iomem *base; + struct clk *clk; +}; + +static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) +{ + timeout += jiffies; + while (time_is_after_eq_jiffies(timeout)) { + if ((readl(reg) & mask) == mask) + return true; + msleep(1); + } + return false; +} + +static int mv_hsic_phy_init(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + struct platform_device *pdev = mv_phy->pdev; + void __iomem *base = mv_phy->base; + + clk_prepare_enable(mv_phy->clk); + + /* Set reference clock */ + writel(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT | + 0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT | + 0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT, + base + PHY_28NM_HSIC_PLL_CTRL01); + + /* Turn on PLL */ + writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) | + PHY_28NM_HSIC_S2H_PU_PLL, + base + PHY_28NM_HSIC_PLL_CTRL2); + + /* Make sure PHY PLL is locked */ + if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2, + PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) { + dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS."); + clk_disable_unprepare(mv_phy->clk); + return -ETIMEDOUT; + } + + return 0; +} + +static int mv_hsic_phy_power_on(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + struct platform_device *pdev = mv_phy->pdev; + void __iomem *base = mv_phy->base; + u32 reg; + + reg = readl(base + PHY_28NM_HSIC_CTRL); + /* Avoid SE0 state when resume for some device will take it as reset */ + reg &= ~S2H_DRV_SE0_4RESUME; + reg |= PHY_28NM_HSIC_S2H_HSIC_EN; /* Enable HSIC PHY */ + writel(reg, base + PHY_28NM_HSIC_CTRL); + + /* + * Calibration Timing + * ____________________________ + * CAL START ___| + * ____________________ + * CAL_DONE ___________| + * | 400us | + */ + + /* Make sure PHY Calibration is ready */ + if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL, + PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) { + dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS."); + return -ETIMEDOUT; + } + + /* Waiting for HSIC connect int*/ + if (!wait_for_reg(base + PHY_28NM_HSIC_INT, + PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) { + dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout."); + return -ETIMEDOUT; + } + + return 0; +} + +static int mv_hsic_phy_power_off(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + writel(readl(base + PHY_28NM_HSIC_CTRL) & ~PHY_28NM_HSIC_S2H_HSIC_EN, + base + PHY_28NM_HSIC_CTRL); + + return 0; +} + +static int mv_hsic_phy_exit(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + /* Turn off PLL */ + writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) & + ~PHY_28NM_HSIC_S2H_PU_PLL, + base + PHY_28NM_HSIC_PLL_CTRL2); + + clk_disable_unprepare(mv_phy->clk); + return 0; +} + + +static const struct phy_ops hsic_ops = { + .init = mv_hsic_phy_init, + .power_on = mv_hsic_phy_power_on, + .power_off = mv_hsic_phy_power_off, + .exit = mv_hsic_phy_exit, + .owner = THIS_MODULE, +}; + +static int mv_hsic_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct mv_hsic_phy *mv_phy; + struct resource *r; + + mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); + if (!mv_phy) + return -ENOMEM; + + mv_phy->pdev = pdev; + + mv_phy->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mv_phy->clk)) { + dev_err(&pdev->dev, "failed to get clock.\n"); + return PTR_ERR(mv_phy->clk); + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mv_phy->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(mv_phy->base)) + return PTR_ERR(mv_phy->base); + + mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &hsic_ops); + if (IS_ERR(mv_phy->phy)) + return PTR_ERR(mv_phy->phy); + + phy_set_drvdata(mv_phy->phy, mv_phy); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id mv_hsic_phy_dt_match[] = { + { .compatible = "marvell,pxa1928-hsic-phy", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mv_hsic_phy_dt_match); + +static struct platform_driver mv_hsic_phy_driver = { + .probe = mv_hsic_phy_probe, + .driver = { + .name = "mv-hsic-phy", + .of_match_table = of_match_ptr(mv_hsic_phy_dt_match), + }, +}; +module_platform_driver(mv_hsic_phy_driver); + +MODULE_AUTHOR("Rob Herring "); +MODULE_DESCRIPTION("Marvell HSIC phy driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 9d062b9b416857873bb149e18fbb4230d202d0e8 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 29 May 2015 11:38:44 -0500 Subject: dt-bindings: Consolidate ChipIdea USB ci13xxx bindings Combine the ChipIdea USB binding into a single document to reduce duplication and fragmentation. This marks use of the old PHY bindings as deprecated. Future compatible bindings should use generic PHY binding. Signed-off-by: Rob Herring Cc: Ivan T. Ivanov Cc: Peter Chen Cc: Daniel Tang Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Cc: devicetree@vger.kernel.org Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt deleted file mode 100644 index 38a5480..0000000 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt +++ /dev/null @@ -1,35 +0,0 @@ -* Freescale i.MX ci13xxx usb controllers - -Required properties: -- compatible: Should be "fsl,imx27-usb" -- reg: Should contain registers location and length -- interrupts: Should contain controller interrupt -- fsl,usbphy: phandle of usb phy that connects to the port - -Recommended properies: -- phy_type: the type of the phy connected to the core. Should be one - of "utmi", "utmi_wide", "ulpi", "serial" or "hsic". Without this - property the PORTSC register won't be touched -- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg" - -Optional properties: -- fsl,usbmisc: phandler of non-core register device, with one argument - that indicate usb controller index -- vbus-supply: regulator for vbus -- disable-over-current: disable over current detect -- external-vbus-divider: enables off-chip resistor divider for Vbus -- maximum-speed: limit the maximum connection speed to "full-speed". -- tpl-support: TPL (Targeted Peripheral List) feature for targeted hosts - -Examples: -usb@02184000 { /* USB OTG */ - compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; - reg = <0x02184000 0x200>; - interrupts = <0 43 0x04>; - fsl,usbphy = <&usbphy1>; - fsl,usbmisc = <&usbmisc 0>; - disable-over-current; - external-vbus-divider; - maximum-speed = "full-speed"; - tpl-support; -}; diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-qcom.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-qcom.txt deleted file mode 100644 index f2899b5..0000000 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-qcom.txt +++ /dev/null @@ -1,17 +0,0 @@ -Qualcomm CI13xxx (Chipidea) USB controllers - -Required properties: -- compatible: should contain "qcom,ci-hdrc" -- reg: offset and length of the register set in the memory map -- interrupts: interrupt-specifier for the controller interrupt. -- usb-phy: phandle for the PHY device -- dr_mode: Should be "peripheral" - -Examples: - gadget@f9a55000 { - compatible = "qcom,ci-hdrc"; - reg = <0xf9a55000 0x400>; - dr_mode = "peripheral"; - interrupts = <0 134 0>; - usb-phy = <&usbphy0>; - }; diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt index 27f8b1e..553e2fa 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt @@ -1,15 +1,35 @@ * USB2 ChipIdea USB controller for ci13xxx Required properties: -- compatible: should be "chipidea,usb2" +- compatible: should be one of: + "fsl,imx27-usb" + "lsi,zevio-usb" + "qcom,ci-hdrc" + "chipidea,usb2" - reg: base address and length of the registers - interrupts: interrupt for the USB controller +Recommended properies: +- phy_type: the type of the phy connected to the core. Should be one + of "utmi", "utmi_wide", "ulpi", "serial" or "hsic". Without this + property the PORTSC register won't be touched. +- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg" + +Deprecated properties: +- usb-phy: phandle for the PHY device. Use "phys" instead. +- fsl,usbphy: phandle of usb phy that connects to the port. Use "phys" instead. + Optional properties: - clocks: reference to the USB clock - phys: reference to the USB PHY - phy-names: should be "usb-phy" - vbus-supply: reference to the VBUS regulator +- maximum-speed: limit the maximum connection speed to "full-speed". +- tpl-support: TPL (Targeted Peripheral List) feature for targeted hosts +- fsl,usbmisc: (FSL only) phandler of non-core register device, with one + argument that indicate usb controller index +- disable-over-current: (FSL only) disable over current detect +- external-vbus-divider: (FSL only) enables off-chip resistor divider for Vbus Example: diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-zevio.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-zevio.txt deleted file mode 100644 index abbcb2a..0000000 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-zevio.txt +++ /dev/null @@ -1,17 +0,0 @@ -* LSI Zevio USB OTG Controller - -Required properties: -- compatible: Should be "lsi,zevio-usb" -- reg: Should contain registers location and length -- interrupts: Should contain controller interrupt - -Optional properties: -- vbus-supply: regulator for vbus - -Examples: - usb0: usb@b0000000 { - reg = <0xb0000000 0x1000>; - compatible = "lsi,zevio-usb"; - interrupts = <8>; - vbus-supply = <&vbus_reg>; - }; -- cgit v0.10.2 From d95699be183c03bb804c1dfdbbeaba7ee1ed8a0d Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 29 May 2015 11:38:45 -0500 Subject: usb: chipidea: allow multiple instances to use default ci_default_pdata Currently, ci_default_pdata is common to all instances of the driver and gets modified by the core driver code. This is bad if there are multiple instances of the device with different settings such as the phy type. Fix this by making a copy of the default platform_data. Signed-off-by: Rob Herring Cc: Peter Chen Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c index 45844c9..9eae1a1 100644 --- a/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -25,7 +25,7 @@ struct ci_hdrc_usb2_priv { struct clk *clk; }; -static struct ci_hdrc_platform_data ci_default_pdata = { +static const struct ci_hdrc_platform_data ci_default_pdata = { .capoffset = DEF_CAPOFFSET, .flags = CI_HDRC_DISABLE_STREAMING, }; @@ -37,8 +37,10 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev) struct ci_hdrc_platform_data *ci_pdata = dev_get_platdata(dev); int ret; - if (!ci_pdata) - ci_pdata = &ci_default_pdata; + if (!ci_pdata) { + ci_pdata = devm_kmalloc(dev, sizeof(*ci_pdata), GFP_KERNEL); + *ci_pdata = ci_default_pdata; /* struct copy */ + } priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) -- cgit v0.10.2 From fc6b68ba4990b4fb2625b150599c77d04d85b1eb Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 29 May 2015 11:38:46 -0500 Subject: usb: chipidea: add work-around for Marvell HSIC PHY startup The Marvell 28nm HSIC PHY requires the port to be forced to HS mode after the port power is applied. This is done using the test mode in the PORTSC register. As HSIC is always HS, this work-around should be safe to do with all HSIC PHYs and has been tested on i.MX6S. Signed-off-by: Rob Herring Tested-by: Peter Chen Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 21fe1a3..6cf87b8 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -37,12 +37,14 @@ static int (*orig_bus_suspend)(struct usb_hcd *hcd); struct ehci_ci_priv { struct regulator *reg_vbus; + struct ci_hdrc *ci; }; static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv; + struct ci_hdrc *ci = priv->ci; struct device *dev = hcd->self.controller; int ret = 0; int port = HCS_N_PORTS(ehci->hcs_params); @@ -64,6 +66,15 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) return ret; } } + + if (enable && (ci->platdata->phy_mode == USBPHY_INTERFACE_MODE_HSIC)) { + /* + * Marvell 28nm HSIC PHY requires forcing the port to HS mode. + * As HSIC is always HS, this should be safe for others. + */ + hw_port_test_set(ci, 5); + hw_port_test_set(ci, 0); + } return 0; }; @@ -112,6 +123,7 @@ static int host_start(struct ci_hdrc *ci) priv = (struct ehci_ci_priv *)ehci->priv; priv->reg_vbus = NULL; + priv->ci = ci; if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) { if (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON) { -- cgit v0.10.2 From 50641056d833813b71b0ad51823f7ded8dd62e7f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 12 Jun 2015 15:16:21 +0200 Subject: usb: dwc3: Use ASCII space in Kconfig The USB_DWC3_ULPI Kconfig entry uses a UTF-8 non-breaking space (0xca20) instead of a regular ASCII space (0x20). Commit 2e0d737fc76f ("kconfig: don't silently ignore unhandled characters") exposes this by warning about unhandled characters. Signed-off-by: Thierry Reding Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 473bfaa..dede32e 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -13,7 +13,7 @@ if USB_DWC3 config USB_DWC3_ULPI bool "Register ULPI PHY Interface" - depends on USB_ULPI_BUS=y || USB_ULPI_BUS=USB_DWC3 + depends on USB_ULPI_BUS=y || USB_ULPI_BUS=USB_DWC3 help Select this if you have ULPI type PHY attached to your DWC3 controller. -- cgit v0.10.2