From 73103c7f958b99561555c3bd1bc1a0809e0b7d61 Mon Sep 17 00:00:00 2001 From: Fei Yang Date: Tue, 19 Mar 2019 22:32:20 -0700 Subject: usb: gadget: f_fs: don't free buffer prematurely The following kernel panic happens due to the io_data buffer gets deallocated before the async io is completed. Add a check for the case where io_data buffer should be deallocated by ffs_user_copy_worker. [ 41.663334] BUG: unable to handle kernel NULL pointer dereference at 0000000000000048 [ 41.672099] #PF error: [normal kernel read fault] [ 41.677356] PGD 20c974067 P4D 20c974067 PUD 20c973067 PMD 0 [ 41.683687] Oops: 0000 [#1] PREEMPT SMP [ 41.687976] CPU: 1 PID: 7 Comm: kworker/u8:0 Tainted: G U 5.0.0-quilt-2e5dc0ac-00790-gd8c79f2-dirty #2 [ 41.705309] Workqueue: adb ffs_user_copy_worker [ 41.705316] RIP: 0010:__vunmap+0x2a/0xc0 [ 41.705318] Code: 0f 1f 44 00 00 48 85 ff 0f 84 87 00 00 00 55 f7 c7 ff 0f 00 00 48 89 e5 41 55 41 89 f5 41 54 53 48 89 fb 75 71 e8 56 d7 ff ff <4c> 8b 60 48 4d 85 e4 74 76 48 89 df e8 25 ff ff ff 45 85 ed 74 46 [ 41.705320] RSP: 0018:ffffbc3a40053df0 EFLAGS: 00010286 [ 41.705322] RAX: 0000000000000000 RBX: ffffbc3a406f1000 RCX: 0000000000000000 [ 41.705323] RDX: 0000000000000001 RSI: 0000000000000001 RDI: 00000000ffffffff [ 41.705324] RBP: ffffbc3a40053e08 R08: 000000000001fb79 R09: 0000000000000037 [ 41.705325] R10: ffffbc3a40053b68 R11: ffffbc3a40053cad R12: fffffffffffffff2 [ 41.705326] R13: 0000000000000001 R14: 0000000000000000 R15: ffffffffffffffff [ 41.705328] FS: 0000000000000000(0000) GS:ffff9e2977a80000(0000) knlGS:0000000000000000 [ 41.705329] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 41.705330] CR2: 0000000000000048 CR3: 000000020c994000 CR4: 00000000003406e0 [ 41.705331] Call Trace: [ 41.705338] vfree+0x50/0xb0 [ 41.705341] ffs_user_copy_worker+0xe9/0x1c0 [ 41.705344] process_one_work+0x19f/0x3e0 [ 41.705348] worker_thread+0x3f/0x3b0 [ 41.829766] kthread+0x12b/0x150 [ 41.833371] ? process_one_work+0x3e0/0x3e0 [ 41.838045] ? kthread_create_worker_on_cpu+0x70/0x70 [ 41.843695] ret_from_fork+0x3a/0x50 [ 41.847689] Modules linked in: hci_uart bluetooth ecdh_generic rfkill_gpio dwc3_pci dwc3 snd_usb_audio mei_me tpm_crb snd_usbmidi_lib xhci_pci xhci_hcd mei tpm snd_hwdep cfg80211 snd_soc_skl snd_soc_skl_ipc snd_soc_sst_ipc snd_soc_sst_dsp snd_hda_ext_core snd_hda_core videobuf2_dma_sg crlmodule [ 41.876880] CR2: 0000000000000048 [ 41.880584] ---[ end trace 2bc4addff0f2e673 ]--- [ 41.891346] RIP: 0010:__vunmap+0x2a/0xc0 [ 41.895734] Code: 0f 1f 44 00 00 48 85 ff 0f 84 87 00 00 00 55 f7 c7 ff 0f 00 00 48 89 e5 41 55 41 89 f5 41 54 53 48 89 fb 75 71 e8 56 d7 ff ff <4c> 8b 60 48 4d 85 e4 74 76 48 89 df e8 25 ff ff ff 45 85 ed 74 46 [ 41.916740] RSP: 0018:ffffbc3a40053df0 EFLAGS: 00010286 [ 41.922583] RAX: 0000000000000000 RBX: ffffbc3a406f1000 RCX: 0000000000000000 [ 41.930563] RDX: 0000000000000001 RSI: 0000000000000001 RDI: 00000000ffffffff [ 41.938540] RBP: ffffbc3a40053e08 R08: 000000000001fb79 R09: 0000000000000037 [ 41.946520] R10: ffffbc3a40053b68 R11: ffffbc3a40053cad R12: fffffffffffffff2 [ 41.954502] R13: 0000000000000001 R14: 0000000000000000 R15: ffffffffffffffff [ 41.962482] FS: 0000000000000000(0000) GS:ffff9e2977a80000(0000) knlGS:0000000000000000 [ 41.971536] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 41.977960] CR2: 0000000000000048 CR3: 000000020c994000 CR4: 00000000003406e0 [ 41.985930] Kernel panic - not syncing: Fatal exception [ 41.991817] Kernel Offset: 0x16000000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff) [ 42.009525] Rebooting in 10 seconds.. [ 52.014376] ACPI MEMORY or I/O RESET_REG. Fixes: 772a7a724f69 ("usb: gadget: f_fs: Allow scatter-gather buffers") Signed-off-by: Fei Yang Reviewed-by: Manu Gautam Tested-by: John Stultz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 20413c276c61..47be961f1bf3 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1133,7 +1133,8 @@ error_lock: error_mutex: mutex_unlock(&epfile->mutex); error: - ffs_free_buffer(io_data); + if (ret != -EIOCBQUEUED) /* don't free if there is iocb queued */ + ffs_free_buffer(io_data); return ret; } -- cgit v1.2.3 From 67130830ce420b54490ec69a775528afdaef6a54 Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Wed, 24 Apr 2019 17:00:57 +0200 Subject: usb: dwc3: Allow building USB_DWC3_QCOM without EXTCON Keep EXTCON support optional, as some platforms do not need it. Do the same for USB_DWC3_OMAP while we're at it. Fixes: 3def4031b3e3f ("usb: dwc3: add EXTCON dependency for qcom") Signed-off-by: Marc Gonzalez Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/Kconfig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 2b1494460d0c..784309435916 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -54,7 +54,8 @@ comment "Platform Glue Driver Support" config USB_DWC3_OMAP tristate "Texas Instruments OMAP5 and similar Platforms" - depends on EXTCON && (ARCH_OMAP2PLUS || COMPILE_TEST) + depends on ARCH_OMAP2PLUS || COMPILE_TEST + depends on EXTCON || !EXTCON depends on OF default USB_DWC3 help @@ -115,7 +116,8 @@ config USB_DWC3_ST config USB_DWC3_QCOM tristate "Qualcomm Platform" - depends on EXTCON && (ARCH_QCOM || COMPILE_TEST) + depends on ARCH_QCOM || COMPILE_TEST + depends on EXTCON || !EXTCON depends on OF default USB_DWC3 help -- cgit v1.2.3 From 50896c410354432e8e7baf97fcdd7df265e683ae Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 18 Apr 2019 13:12:07 -0400 Subject: USB: dummy-hcd: Fix failure to give back unlinked URBs The syzkaller USB fuzzer identified a failure mode in which dummy-hcd would never give back an unlinked URB. This causes usb_kill_urb() to hang, leading to WARNINGs and unkillable threads. In dummy-hcd, all URBs are given back by the dummy_timer() routine as it scans through the list of pending URBS. Failure to give back URBs can be caused by failure to start or early exit from the scanning loop. The code currently has two such pathways: One is triggered when an unsupported bus transfer speed is encountered, and the other by exhausting the simulated bandwidth for USB transfers during a frame. This patch removes those two paths, thereby allowing all unlinked URBs to be given back in a timely manner. It adds a check for the bus speed when the gadget first starts running, so that dummy_timer() will never thereafter encounter an unsupported speed. And it prevents the loop from exiting as soon as the total bandwidth has been used up (the scanning loop continues, giving back unlinked URBs as they are found, but not transferring any more data). Thanks to Andrey Konovalov for manually running the syzkaller fuzzer to help track down the source of the bug. Signed-off-by: Alan Stern Reported-and-tested-by: syzbot+d919b0f29d7b5a4994b9@syzkaller.appspotmail.com CC: Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/dummy_hcd.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index baf72f95f0f1..213b52508621 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -979,8 +979,18 @@ static int dummy_udc_start(struct usb_gadget *g, struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g); struct dummy *dum = dum_hcd->dum; - if (driver->max_speed == USB_SPEED_UNKNOWN) + switch (g->speed) { + /* All the speeds we support */ + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + case USB_SPEED_SUPER: + break; + default: + dev_err(dummy_dev(dum_hcd), "Unsupported driver max speed %d\n", + driver->max_speed); return -EINVAL; + } /* * SLAVE side init ... the layer above hardware, which @@ -1784,9 +1794,10 @@ static void dummy_timer(struct timer_list *t) /* Bus speed is 500000 bytes/ms, so use a little less */ total = 490000; break; - default: + default: /* Can't happen */ dev_err(dummy_dev(dum_hcd), "bogus device speed\n"); - return; + total = 0; + break; } /* FIXME if HZ != 1000 this will probably misbehave ... */ @@ -1828,7 +1839,7 @@ restart: /* Used up this frame's bandwidth? */ if (total <= 0) - break; + continue; /* find the gadget's ep for this request (if configured) */ address = usb_pipeendpoint (urb->pipe); -- cgit v1.2.3 From fc4e326ee72cc36c942333c65d851247b31c567b Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 23 Apr 2019 10:51:26 +0200 Subject: usb: dwc2: Add Amlogic G12A DWC2 Params This patchs sets the params for the DWC2 Controller found in the Amlogic G12A SoC family. It mainly sets the settings reported incorrect by the driver, leaving the remaining detected automatically by the driver and provided by the DT node. Signed-off-by: Neil Armstrong Acked-by: Minas Harutyunyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/params.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 24ff5f21cb25..442113246cba 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -121,6 +121,16 @@ static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg) p->power_down = DWC2_POWER_DOWN_PARAM_NONE; } +static void dwc2_set_amlogic_g12a_params(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + + p->lpm = false; + p->lpm_clock_gating = false; + p->besl = false; + p->hird_threshold_en = false; +} + static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg) { struct dwc2_core_params *p = &hsotg->params; @@ -167,6 +177,8 @@ const struct of_device_id dwc2_of_match_table[] = { .data = dwc2_set_amlogic_params }, { .compatible = "amlogic,meson-gxbb-usb", .data = dwc2_set_amlogic_params }, + { .compatible = "amlogic,meson-g12a-usb", + .data = dwc2_set_amlogic_g12a_params }, { .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params }, { .compatible = "st,stm32f4x9-fsotg", .data = dwc2_set_stm32f4x9_fsotg_params }, -- cgit v1.2.3 From c99993376f72ca3dcc989813512607c6435cbed8 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 23 Apr 2019 10:51:27 +0200 Subject: usb: dwc3: Add Amlogic G12A DWC3 glue Adds support for Amlogic G12A USB Control Glue HW. The Amlogic G12A SoC Family embeds 2 USB Controllers : - a DWC3 IP configured as Host for USB2 and USB3 - a DWC2 IP configured as Peripheral USB2 Only A glue connects these both controllers to 2 USB2 PHYs, and optionnally to an USB3+PCIE Combo PHY shared with the PCIE controller. The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including routing of the OTG PHY between the DWC3 and DWC2 controllers, and setups the on-chip OTG mode selection for this PHY. This drivers supports the on-probe setup of the OTG mode, and manually via a debugfs interface. The IRQ mode change detect is yet to be added in a future patchset, mainly due to lack of hardware to validate on. Signed-off-by: Neil Armstrong Reviewed-by: Martin Blumenstingl Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/Kconfig | 10 + drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-meson-g12a.c | 604 +++++++++++++++++++++++++++++++++++++ 3 files changed, 615 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-meson-g12a.c (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 784309435916..4a62045cc812 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -96,6 +96,16 @@ config USB_DWC3_KEYSTONE Support of USB2/3 functionality in TI Keystone2 and AM654 platforms. Say 'Y' or 'M' here if you have one such device +config USB_DWC3_MESON_G12A + tristate "Amlogic Meson G12A Platforms" + depends on OF && COMMON_CLK + depends on ARCH_MESON || COMPILE_TEST + default USB_DWC3 + select USB_ROLE_SWITCH + help + Support USB2/3 functionality in Amlogic G12A platforms. + Say 'Y' or 'M' if you have one such device. + config USB_DWC3_OF_SIMPLE tristate "Generic OF Simple Glue Layer" depends on OF && COMMON_CLK diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 6e3ef6144e5d..ae86da0dc5bd 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o obj-$(CONFIG_USB_DWC3_HAPS) += dwc3-haps.o obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o +obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c new file mode 100644 index 000000000000..2aec31a2eacb --- /dev/null +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -0,0 +1,604 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Glue for Amlogic G12A SoCs + * + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong + */ + +/* + * The USB is organized with a glue around the DWC3 Controller IP as : + * - Control registers for each USB2 Ports + * - Control registers for the USB PHY layer + * - SuperSpeed PHY can be enabled only if port is used + * + * TOFIX: + * - Add dynamic OTG switching with ID change interrupt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* USB2 Ports Control Registers */ + +#define U2P_REG_SIZE 0x20 + +#define U2P_R0 0x0 + #define U2P_R0_HOST_DEVICE BIT(0) + #define U2P_R0_POWER_OK BIT(1) + #define U2P_R0_HAST_MODE BIT(2) + #define U2P_R0_POWER_ON_RESET BIT(3) + #define U2P_R0_ID_PULLUP BIT(4) + #define U2P_R0_DRV_VBUS BIT(5) + +#define U2P_R1 0x4 + #define U2P_R1_PHY_READY BIT(0) + #define U2P_R1_ID_DIG BIT(1) + #define U2P_R1_OTG_SESSION_VALID BIT(2) + #define U2P_R1_VBUS_VALID BIT(3) + +/* USB Glue Control Registers */ + +#define USB_R0 0x80 + #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) + #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) + #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) + #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) + #define USB_R0_U2D_ACT BIT(31) + +#define USB_R1 0x84 + #define USB_R1_U3H_BIGENDIAN_GS BIT(0) + #define USB_R1_U3H_PME_ENABLE BIT(1) + #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2) + #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(9, 7) + #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(13, 12) + #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) + #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) + #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) + #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) + #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) + +#define USB_R2 0x88 + #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) + #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) + +#define USB_R3 0x8c + #define USB_R3_P30_SSC_ENABLE BIT(0) + #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) + #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) + #define USB_R3_P30_REF_SSP_EN BIT(13) + +#define USB_R4 0x90 + #define USB_R4_P21_PORT_RESET_0 BIT(0) + #define USB_R4_P21_SLEEP_M0 BIT(1) + #define USB_R4_MEM_PD_MASK GENMASK(3, 2) + #define USB_R4_P21_ONLY BIT(4) + +#define USB_R5 0x94 + #define USB_R5_ID_DIG_SYNC BIT(0) + #define USB_R5_ID_DIG_REG BIT(1) + #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) + #define USB_R5_ID_DIG_EN_0 BIT(4) + #define USB_R5_ID_DIG_EN_1 BIT(5) + #define USB_R5_ID_DIG_CURR BIT(6) + #define USB_R5_ID_DIG_IRQ BIT(7) + #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) + #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) + +enum { + USB2_HOST_PHY = 0, + USB2_OTG_PHY, + USB3_HOST_PHY, + PHY_COUNT, +}; + +static const char *phy_names[PHY_COUNT] = { + "usb2-phy0", "usb2-phy1", "usb3-phy0", +}; + +struct dwc3_meson_g12a { + struct device *dev; + struct regmap *regmap; + struct clk *clk; + struct reset_control *reset; + struct phy *phys[PHY_COUNT]; + enum usb_dr_mode otg_mode; + enum phy_mode otg_phy_mode; + unsigned int usb2_ports; + unsigned int usb3_ports; + struct regulator *vbus; + struct usb_role_switch_desc switch_desc; + struct usb_role_switch *role_switch; +}; + +static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, + int i, enum phy_mode mode) +{ + if (mode == PHY_MODE_USB_HOST) + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, + U2P_R0_HOST_DEVICE); + else + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, 0); +} + +static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv) +{ + int i; + + if (priv->otg_mode == USB_DR_MODE_PERIPHERAL) + priv->otg_phy_mode = PHY_MODE_USB_DEVICE; + else + priv->otg_phy_mode = PHY_MODE_USB_HOST; + + for (i = 0 ; i < USB3_HOST_PHY ; ++i) { + if (!priv->phys[i]) + continue; + + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_POWER_ON_RESET, + U2P_R0_POWER_ON_RESET); + + if (i == USB2_OTG_PHY) { + regmap_update_bits(priv->regmap, + U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, + U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS); + + dwc3_meson_g12a_usb2_set_mode(priv, i, + priv->otg_phy_mode); + } else + dwc3_meson_g12a_usb2_set_mode(priv, i, + PHY_MODE_USB_HOST); + + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_POWER_ON_RESET, 0); + } + + return 0; +} + +static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv) +{ + regmap_update_bits(priv->regmap, USB_R3, + USB_R3_P30_SSC_RANGE_MASK | + USB_R3_P30_REF_SSP_EN, + USB_R3_P30_SSC_ENABLE | + FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) | + USB_R3_P30_REF_SSP_EN); + udelay(2); + + regmap_update_bits(priv->regmap, USB_R2, + USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, + FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15)); + + regmap_update_bits(priv->regmap, USB_R2, + USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, + FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20)); + + udelay(2); + + regmap_update_bits(priv->regmap, USB_R1, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT); + + regmap_update_bits(priv->regmap, USB_R1, + USB_R1_P30_PCS_TX_SWING_FULL_MASK, + FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127)); +} + +static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv) +{ + if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) { + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, USB_R0_U2D_ACT); + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0); + regmap_update_bits(priv->regmap, USB_R4, + USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); + } else { + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, 0); + regmap_update_bits(priv->regmap, USB_R4, + USB_R4_P21_SLEEP_M0, 0); + } +} + +static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv) +{ + int ret; + + ret = dwc3_meson_g12a_usb2_init(priv); + if (ret) + return ret; + + regmap_update_bits(priv->regmap, USB_R1, + USB_R1_U3H_FLADJ_30MHZ_REG_MASK, + FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); + + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_0, + USB_R5_ID_DIG_EN_0); + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_1, + USB_R5_ID_DIG_EN_1); + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_TH_MASK, + FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff)); + + /* If we have an actual SuperSpeed port, initialize it */ + if (priv->usb3_ports) + dwc3_meson_g12a_usb3_init(priv); + + dwc3_meson_g12a_usb_otg_apply_mode(priv); + + return 0; +} + +static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = USB_R5, +}; + +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv) +{ + int i; + + for (i = 0 ; i < PHY_COUNT ; ++i) { + priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]); + if (!priv->phys[i]) + continue; + + if (IS_ERR(priv->phys[i])) + return PTR_ERR(priv->phys[i]); + + if (i == USB3_HOST_PHY) + priv->usb3_ports++; + else + priv->usb2_ports++; + } + + dev_info(priv->dev, "USB2 ports: %d\n", priv->usb2_ports); + dev_info(priv->dev, "USB3 ports: %d\n", priv->usb3_ports); + + return 0; +} + +static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv) +{ + u32 reg; + + regmap_read(priv->regmap, USB_R5, ®); + + if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG)) + return PHY_MODE_USB_DEVICE; + + return PHY_MODE_USB_HOST; +} + +static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, + enum phy_mode mode) +{ + int ret; + + if (!priv->phys[USB2_OTG_PHY]) + return -EINVAL; + + if (mode == PHY_MODE_USB_HOST) + dev_info(priv->dev, "switching to Host Mode\n"); + else + dev_info(priv->dev, "switching to Device Mode\n"); + + if (priv->vbus) { + if (mode == PHY_MODE_USB_DEVICE) + ret = regulator_disable(priv->vbus); + else + ret = regulator_enable(priv->vbus); + if (ret) + return ret; + } + + priv->otg_phy_mode = mode; + + dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode); + + dwc3_meson_g12a_usb_otg_apply_mode(priv); + + return 0; +} + +static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + enum phy_mode mode; + + if (role == USB_ROLE_NONE) + return 0; + + mode = (role == USB_ROLE_HOST) ? PHY_MODE_USB_HOST + : PHY_MODE_USB_DEVICE; + + if (mode == priv->otg_phy_mode) + return 0; + + return dwc3_meson_g12a_otg_mode_set(priv, mode); +} + +static enum usb_role dwc3_meson_g12a_role_get(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + return priv->otg_phy_mode == PHY_MODE_USB_HOST ? + USB_ROLE_HOST : USB_ROLE_DEVICE; +} + +static struct device *dwc3_meson_g12_find_child(struct device *dev, + const char *compatible) +{ + struct platform_device *pdev; + struct device_node *np; + + np = of_get_compatible_child(dev->of_node, compatible); + if (!np) + return NULL; + + pdev = of_find_device_by_node(np); + of_node_put(np); + if (!pdev) + return NULL; + + return &pdev->dev; +} + +static int dwc3_meson_g12a_probe(struct platform_device *pdev) +{ + struct dwc3_meson_g12a *priv; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + void __iomem *base; + struct resource *res; + enum phy_mode otg_id; + int ret, i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, + &phy_meson_g12a_usb3_regmap_conf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->vbus = devm_regulator_get_optional(dev, "vbus"); + if (IS_ERR(priv->vbus)) { + if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) + return PTR_ERR(priv->vbus); + priv->vbus = NULL; + } + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + devm_add_action_or_reset(dev, + (void(*)(void *))clk_disable_unprepare, + priv->clk); + + platform_set_drvdata(pdev, priv); + priv->dev = dev; + + priv->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(priv->reset)) { + ret = PTR_ERR(priv->reset); + dev_err(dev, "failed to get device reset, err=%d\n", ret); + return ret; + } + + ret = reset_control_reset(priv->reset); + if (ret) + return ret; + + ret = dwc3_meson_g12a_get_phys(priv); + if (ret) + return ret; + + if (priv->vbus) { + ret = regulator_enable(priv->vbus); + if (ret) + return ret; + } + + /* Get dr_mode */ + priv->otg_mode = usb_get_dr_mode(dev); + + dwc3_meson_g12a_usb_init(priv); + + /* Init PHYs */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_init(priv->phys[i]); + if (ret) + return ret; + } + + /* Set PHY Power */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_power_on(priv->phys[i]); + if (ret) + goto err_phys_exit; + } + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + clk_disable_unprepare(priv->clk); + goto err_phys_power; + } + + /* Setup OTG mode corresponding to the ID pin */ + if (priv->otg_mode == USB_DR_MODE_OTG) { + /* TOFIX Handle ID mode toggling via IRQ */ + otg_id = dwc3_meson_g12a_get_id(priv); + if (otg_id != priv->otg_phy_mode) { + if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) + dev_warn(dev, "Failed to switch OTG mode\n"); + } + } + + /* Setup role switcher */ + priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev, + "snps,dwc3"); + priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2"); + priv->switch_desc.allow_userspace_control = true; + priv->switch_desc.set = dwc3_meson_g12a_role_set; + priv->switch_desc.get = dwc3_meson_g12a_role_get; + + priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc); + if (IS_ERR(priv->role_switch)) + dev_warn(dev, "Unable to register Role Switch\n"); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + return 0; + +err_phys_power: + for (i = 0 ; i < PHY_COUNT ; ++i) + phy_power_off(priv->phys[i]); + +err_phys_exit: + for (i = 0 ; i < PHY_COUNT ; ++i) + phy_exit(priv->phys[i]); + + return ret; +} + +static int dwc3_meson_g12a_remove(struct platform_device *pdev) +{ + struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int i; + + usb_role_switch_unregister(priv->role_switch); + + of_platform_depopulate(dev); + + for (i = 0 ; i < PHY_COUNT ; ++i) { + phy_power_off(priv->phys[i]); + phy_exit(priv->phys[i]); + } + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + clk_disable(priv->clk); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + return clk_enable(priv->clk); +} + +static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + int i; + + for (i = 0 ; i < PHY_COUNT ; ++i) { + phy_power_off(priv->phys[i]); + phy_exit(priv->phys[i]); + } + + reset_control_assert(priv->reset); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + int i, ret; + + reset_control_deassert(priv->reset); + + dwc3_meson_g12a_usb_init(priv); + + /* Init PHYs */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_init(priv->phys[i]); + if (ret) + return ret; + } + + /* Set PHY Power */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_power_on(priv->phys[i]); + if (ret) + return ret; + } + + return 0; +} + +static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume) + SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend, + dwc3_meson_g12a_runtime_resume, NULL) +}; + +static const struct of_device_id dwc3_meson_g12a_match[] = { + { .compatible = "amlogic,meson-g12a-usb-ctrl" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); + +static struct platform_driver dwc3_meson_g12a_driver = { + .probe = dwc3_meson_g12a_probe, + .remove = dwc3_meson_g12a_remove, + .driver = { + .name = "dwc3-meson-g12a", + .of_match_table = dwc3_meson_g12a_match, + .pm = &dwc3_meson_g12a_dev_pm_ops, + }, +}; + +module_platform_driver(dwc3_meson_g12a_driver); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer"); +MODULE_AUTHOR("Neil Armstrong "); -- cgit v1.2.3 From 6f6d70597c15b2a406afa541517e6ad35f56a8a3 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 17 Apr 2019 17:13:52 -0700 Subject: usb: dwc2: bus suspend/resume for hosts with DWC2_POWER_DOWN_PARAM_NONE This is an attempt to rehash commit 0cf884e819e0 ("usb: dwc2: add bus suspend/resume for dwc2") on ToT. That commit was reverted in commit b0bb9bb6ce01 ("Revert "usb: dwc2: add bus suspend/resume for dwc2"") because apparently it broke the Altera SOCFPGA. With all the changes that have happened to dwc2 in the meantime, it's possible that the Altera SOCFPGA will just magically work with this change now. ...and it would be good to get bus suspend/resume implemented. This change is a forward port of one that's been living in the Chrome OS 3.14 kernel tree. Signed-off-by: Douglas Anderson Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd.c | 84 +++++++++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 31 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 3f087962f498..8667ddf3ca74 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4471,6 +4471,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) unsigned long flags; int ret = 0; u32 hprt0; + u32 pcgctl; spin_lock_irqsave(&hsotg->lock, flags); @@ -4486,7 +4487,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) if (hsotg->op_state == OTG_STATE_B_PERIPHERAL) goto unlock; - if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) + if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL) goto skip_power_saving; /* @@ -4495,21 +4496,35 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) */ if (!hsotg->bus_suspended) { hprt0 = dwc2_read_hprt0(hsotg); - hprt0 |= HPRT0_SUSP; - hprt0 &= ~HPRT0_PWR; - dwc2_writel(hsotg, hprt0, HPRT0); - spin_unlock_irqrestore(&hsotg->lock, flags); - dwc2_vbus_supply_exit(hsotg); - spin_lock_irqsave(&hsotg->lock, flags); + if (hprt0 & HPRT0_CONNSTS) { + hprt0 |= HPRT0_SUSP; + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) + hprt0 &= ~HPRT0_PWR; + dwc2_writel(hsotg, hprt0, HPRT0); + } + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { + spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_vbus_supply_exit(hsotg); + spin_lock_irqsave(&hsotg->lock, flags); + } else { + pcgctl = readl(hsotg->regs + PCGCTL); + pcgctl |= PCGCTL_STOPPCLK; + writel(pcgctl, hsotg->regs + PCGCTL); + } } - /* Enter partial_power_down */ - ret = dwc2_enter_partial_power_down(hsotg); - if (ret) { - if (ret != -ENOTSUPP) - dev_err(hsotg->dev, - "enter partial_power_down failed\n"); - goto skip_power_saving; + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { + /* Enter partial_power_down */ + ret = dwc2_enter_partial_power_down(hsotg); + if (ret) { + if (ret != -ENOTSUPP) + dev_err(hsotg->dev, + "enter partial_power_down failed\n"); + goto skip_power_saving; + } + + /* After entering partial_power_down, hardware is no more accessible */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); } /* Ask phy to be suspended */ @@ -4519,9 +4534,6 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) spin_lock_irqsave(&hsotg->lock, flags); } - /* After entering partial_power_down, hardware is no more accessible */ - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - skip_power_saving: hsotg->lx_state = DWC2_L2; unlock: @@ -4534,6 +4546,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) { struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); unsigned long flags; + u32 pcgctl; int ret = 0; spin_lock_irqsave(&hsotg->lock, flags); @@ -4544,17 +4557,11 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) if (hsotg->lx_state != DWC2_L2) goto unlock; - if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) { + if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL) { hsotg->lx_state = DWC2_L0; goto unlock; } - /* - * Set HW accessible bit before powering on the controller - * since an interrupt may rise. - */ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - /* * Enable power if not already done. * This must not be spinlocked since duration @@ -4566,10 +4573,23 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_lock_irqsave(&hsotg->lock, flags); } - /* Exit partial_power_down */ - ret = dwc2_exit_partial_power_down(hsotg, true); - if (ret && (ret != -ENOTSUPP)) - dev_err(hsotg->dev, "exit partial_power_down failed\n"); + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { + /* + * Set HW accessible bit before powering on the controller + * since an interrupt may rise. + */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + + /* Exit partial_power_down */ + ret = dwc2_exit_partial_power_down(hsotg, true); + if (ret && (ret != -ENOTSUPP)) + dev_err(hsotg->dev, "exit partial_power_down failed\n"); + } else { + pcgctl = readl(hsotg->regs + PCGCTL); + pcgctl &= ~PCGCTL_STOPPCLK; + writel(pcgctl, hsotg->regs + PCGCTL); + } hsotg->lx_state = DWC2_L0; @@ -4581,10 +4601,12 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_unlock_irqrestore(&hsotg->lock, flags); dwc2_port_resume(hsotg); } else { - dwc2_vbus_supply_init(hsotg); + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { + dwc2_vbus_supply_init(hsotg); - /* Wait for controller to correctly update D+/D- level */ - usleep_range(3000, 5000); + /* Wait for controller to correctly update D+/D- level */ + usleep_range(3000, 5000); + } /* * Clear Port Enable and Port Status changes. -- cgit v1.2.3 From 7a6127e39a16c97b505f13352238567fdc3f79a2 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 17 Apr 2019 17:13:53 -0700 Subject: USB: Export usb_wakeup_enabled_descendants() In (e583d9d USB: global suspend and remote wakeup don't mix) we introduced wakeup_enabled_descendants() as a static function. We'd like to use this function in USB controller drivers to know if we should keep the controller on during suspend time, since doing so has a power impact. Signed-off-by: Douglas Anderson Signed-off-by: Felipe Balbi --- drivers/usb/core/hub.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 8d4631c81b9f..5e8f3fa7ae5a 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3174,13 +3174,14 @@ static int usb_disable_remote_wakeup(struct usb_device *udev) } /* Count of wakeup-enabled devices at or below udev */ -static unsigned wakeup_enabled_descendants(struct usb_device *udev) +unsigned usb_wakeup_enabled_descendants(struct usb_device *udev) { struct usb_hub *hub = usb_hub_to_struct_hub(udev); return udev->do_remote_wakeup + (hub ? hub->wakeup_enabled_descendants : 0); } +EXPORT_SYMBOL_GPL(usb_wakeup_enabled_descendants); /* * usb_port_suspend - suspend a usb device's upstream port @@ -3282,7 +3283,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) * Therefore we will turn on the suspend feature if udev or any of its * descendants is enabled for remote wakeup. */ - else if (PMSG_IS_AUTO(msg) || wakeup_enabled_descendants(udev) > 0) + else if (PMSG_IS_AUTO(msg) || usb_wakeup_enabled_descendants(udev) > 0) status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); else { @@ -3687,7 +3688,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) } if (udev) hub->wakeup_enabled_descendants += - wakeup_enabled_descendants(udev); + usb_wakeup_enabled_descendants(udev); } if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) { -- cgit v1.2.3 From 466375657d6c5987f2f2404c75b7081ede14cff4 Mon Sep 17 00:00:00 2001 From: Minas Harutyunyan Date: Thu, 18 Apr 2019 15:40:43 +0400 Subject: usb: dwc2: gadget: Reject LPM token during Control transfers Avoiding switch to L1 state in any stage of control transfers. Send NYET handshake to LPM token. Renamed GLPMCFG_LPM_ACCEPT_CTRL_ISOC to GLPMCFG_LPM_REJECT_CTRL_CONTROL because by setting this bit core reject LPM token. Signed-off-by: Minas Harutyunyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 1 + drivers/usb/dwc2/hw.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 6812a8a3a98b..6ac850d6ad44 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -5073,6 +5073,7 @@ void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0; val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT; val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0; + val |= GLPMCFG_LPM_REJECT_CTRL_CONTROL; val |= GLPMCFG_LPM_ACCEPT_CTRL_ISOC; dwc2_writel(hsotg, val, GLPMCFG); dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG)); diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 98af924a9a5c..1bc394dcfa9d 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -333,7 +333,7 @@ #define GLPMCFG_SNDLPM BIT(24) #define GLPMCFG_RETRY_CNT_MASK (0x7 << 21) #define GLPMCFG_RETRY_CNT_SHIFT 21 -#define GLPMCFG_LPM_ACCEPT_CTRL_CONTROL BIT(21) +#define GLPMCFG_LPM_REJECT_CTRL_CONTROL BIT(21) #define GLPMCFG_LPM_ACCEPT_CTRL_ISOC BIT(22) #define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17) #define GLPMCFG_LPM_CHNL_INDX_SHIFT 17 -- cgit v1.2.3 From 60722c4eefbc9acddaf5e641f6dfb24bce930f9a Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Wed, 17 Apr 2019 16:28:19 +0800 Subject: usb: dwc2: get optional clock by devm_clk_get_optional() When the driver tries to get optional clock, it ignores all errors, but if only ignores -ENOENT, it will cover some real errors, such as -EPROBE_DEFER, so use devm_clk_get_optional() to get optional clock. Cc: Minas Harutyunyan Signed-off-by: Chunfeng Yun Acked-by: Minas Harutyunyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/platform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index c0b64d483552..9aa9682a5cd2 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -284,10 +284,10 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) } /* Clock */ - hsotg->clk = devm_clk_get(hsotg->dev, "otg"); + hsotg->clk = devm_clk_get_optional(hsotg->dev, "otg"); if (IS_ERR(hsotg->clk)) { - hsotg->clk = NULL; - dev_dbg(hsotg->dev, "cannot get otg clock\n"); + dev_err(hsotg->dev, "cannot get otg clock\n"); + return PTR_ERR(hsotg->clk); } /* Regulators */ -- cgit v1.2.3 From 550eef0c353030ac4223b9c9479bdf77a05445d6 Mon Sep 17 00:00:00 2001 From: Romain Izard Date: Tue, 16 Apr 2019 16:07:31 +0200 Subject: usb: gadget: f_ncm: Fix NTP-32 support When connecting a CDC-NCM gadget to an host that uses the NTP-32 mode, or that relies on the default CRC setting, the current implementation gets confused, and does not expect the correct signature for its packets. Fix this, by ensuring that the ndp_sign member in the f_ncm structure always contain a valid value. Signed-off-by: Romain Izard Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_ncm.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 5780fba620ab..d5c47e7a7f61 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -35,9 +35,7 @@ /* to trigger crc/non-crc ndp signature */ -#define NCM_NDP_HDR_CRC_MASK 0x01000000 #define NCM_NDP_HDR_CRC 0x01000000 -#define NCM_NDP_HDR_NOCRC 0x00000000 enum ncm_notify_state { NCM_NOTIFY_NONE, /* don't notify */ @@ -526,6 +524,7 @@ static inline void ncm_reset_values(struct f_ncm *ncm) { ncm->parser_opts = &ndp16_opts; ncm->is_crc = false; + ncm->ndp_sign = ncm->parser_opts->ndp_sign; ncm->port.cdc_filter = DEFAULT_FILTER; /* doesn't make sense for ncm, fixed size used */ @@ -805,25 +804,20 @@ static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | USB_CDC_SET_CRC_MODE: { - int ndp_hdr_crc = 0; - if (w_length != 0 || w_index != ncm->ctrl_id) goto invalid; switch (w_value) { case 0x0000: ncm->is_crc = false; - ndp_hdr_crc = NCM_NDP_HDR_NOCRC; DBG(cdev, "non-CRC mode selected\n"); break; case 0x0001: ncm->is_crc = true; - ndp_hdr_crc = NCM_NDP_HDR_CRC; DBG(cdev, "CRC mode selected\n"); break; default: goto invalid; } - ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc; value = 0; break; } @@ -840,6 +834,8 @@ invalid: ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length); } + ncm->ndp_sign = ncm->parser_opts->ndp_sign | + (ncm->is_crc ? NCM_NDP_HDR_CRC : 0); /* respond with data transfer or status phase? */ if (value >= 0) { -- cgit v1.2.3 From 793409292382027226769d0299987f06cbd97a6e Mon Sep 17 00:00:00 2001 From: Romain Izard Date: Tue, 16 Apr 2019 16:07:32 +0200 Subject: usb: gadget: f_ncm: Add OS descriptor support To be able to use the default USB class drivers available in Microsoft Windows, we need to add OS descriptors to the exported USB gadget to tell the OS that we are compatible with the built-in drivers. Copy the OS descriptor support from f_rndis into f_ncm. As a result, using the WINNCM compatible ID, the UsbNcm driver is loaded on enumeration without the need for a custom driver or inf file. Signed-off-by: Romain Izard Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_ncm.c | 47 ++++++++++++++++++++++++++++++++++--- drivers/usb/gadget/function/u_ncm.h | 3 +++ 2 files changed, 47 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index d5c47e7a7f61..2d6e76e4cffa 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -23,6 +23,7 @@ #include "u_ether.h" #include "u_ether_configfs.h" #include "u_ncm.h" +#include "configfs.h" /* * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link. @@ -1391,6 +1392,16 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) return -EINVAL; ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst); + + if (cdev->use_os_string) { + f->os_desc_table = kzalloc(sizeof(*f->os_desc_table), + GFP_KERNEL); + if (!f->os_desc_table) + return -ENOMEM; + f->os_desc_n = 1; + f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc; + } + /* * in drivers/usb/gadget/configfs.c:configfs_composite_bind() * configurations are bound in sequence with list_for_each_entry, @@ -1404,13 +1415,15 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) status = gether_register_netdev(ncm_opts->net); mutex_unlock(&ncm_opts->lock); if (status) - return status; + goto fail; ncm_opts->bound = true; } us = usb_gstrings_attach(cdev, ncm_strings, ARRAY_SIZE(ncm_string_defs)); - if (IS_ERR(us)) - return PTR_ERR(us); + if (IS_ERR(us)) { + status = PTR_ERR(us); + goto fail; + } ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id; ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id; ncm_data_intf.iInterface = us[STRING_DATA_IDX].id; @@ -1427,6 +1440,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ncm_control_intf.bInterfaceNumber = status; ncm_union_desc.bMasterInterface0 = status; + if (cdev->use_os_string) + f->os_desc_table[0].if_id = + ncm_iad_desc.bFirstInterface; + status = usb_interface_id(c, f); if (status < 0) goto fail; @@ -1506,6 +1523,9 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: + kfree(f->os_desc_table); + f->os_desc_n = 0; + if (ncm->notify_req) { kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); @@ -1560,16 +1580,22 @@ static void ncm_free_inst(struct usb_function_instance *f) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); + kfree(opts->ncm_interf_group); kfree(opts); } static struct usb_function_instance *ncm_alloc_inst(void) { struct f_ncm_opts *opts; + struct usb_os_desc *descs[1]; + char *names[1]; + struct config_group *ncm_interf_group; opts = kzalloc(sizeof(*opts), GFP_KERNEL); if (!opts) return ERR_PTR(-ENOMEM); + opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id; + mutex_init(&opts->lock); opts->func_inst.free_func_inst = ncm_free_inst; opts->net = gether_setup_default(); @@ -1578,8 +1604,20 @@ static struct usb_function_instance *ncm_alloc_inst(void) kfree(opts); return ERR_CAST(net); } + INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop); + + descs[0] = &opts->ncm_os_desc; + names[0] = "ncm"; config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type); + ncm_interf_group = + usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs, + names, THIS_MODULE); + if (IS_ERR(ncm_interf_group)) { + ncm_free_inst(&opts->func_inst); + return ERR_CAST(ncm_interf_group); + } + opts->ncm_interf_group = ncm_interf_group; return &opts->func_inst; } @@ -1605,6 +1643,9 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) hrtimer_cancel(&ncm->task_timer); + kfree(f->os_desc_table); + f->os_desc_n = 0; + ncm_string_defs[0].id = 0; usb_free_all_descriptors(f); diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h index d483e45c0f77..70da3201a1d0 100644 --- a/drivers/usb/gadget/function/u_ncm.h +++ b/drivers/usb/gadget/function/u_ncm.h @@ -20,6 +20,9 @@ struct f_ncm_opts { struct net_device *net; bool bound; + struct config_group *ncm_interf_group; + struct usb_os_desc ncm_os_desc; + char ncm_ext_compat_id[16]; /* * Read/write access to configfs attributes is handled by configfs. * -- cgit v1.2.3 From 2100e3ca3676e894fa48b8f6f01d01733387fe81 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 Apr 2019 14:25:32 +0200 Subject: usb: gadget: fsl: fix link error against usb-gadget module The dependency to ensure this driver links correctly fails since it can not be a loadable module: drivers/usb/phy/phy-fsl-usb.o: In function `fsl_otg_set_peripheral': phy-fsl-usb.c:(.text+0x2224): undefined reference to `usb_gadget_vbus_disconnect' Make the option 'tristate' so it can work correctly. Fixes: 5a8d651a2bde ("usb: gadget: move gadget API functions to udc-core") Signed-off-by: Arnd Bergmann Signed-off-by: Felipe Balbi --- drivers/usb/phy/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 8c509b060c09..24b4f091acb8 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -21,7 +21,7 @@ config AB8500_USB in host mode, low speed. config FSL_USB2_OTG - bool "Freescale USB OTG Transceiver Driver" + tristate "Freescale USB OTG Transceiver Driver" depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM=y && PM depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' select USB_PHY -- cgit v1.2.3 From 6574abe69946589bf0f69cf9b32f6a2c71ae764f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 15 Apr 2019 13:36:58 -0400 Subject: USB: UDC: net2280: Remove redundant "if" condition The net2280 driver includes an unnecessary test for an endpoint's queue being empty. The test is redundant; it sits inside a conditional block of an "if" statement which already tests the endpoint's queue. This patch removes the redundant test. Signed-off-by: Alan Stern Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 898339e5df10..b17473a00b43 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -1058,7 +1058,7 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* PIO ... stuff the fifo, or unblock it. */ if (ep->is_in) write_fifo(ep, _req); - else if (list_empty(&ep->queue)) { + else { u32 s; /* OUT FIFO might have packet(s) buffered */ -- cgit v1.2.3 From de497f634609aa1710fef4b6d833a037120369cc Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 15 Apr 2019 13:35:46 -0400 Subject: USB: UDC: net22{80,72}: remove mistaken test of req->zero The net2280 UDC driver (and also net2272, probably via copy-and-paste) incorrectly checks the req->zero flag during OUT transfers, after copying data from the UDC's FIFO into memory. This makes no sense at all; the "zero" flag indicates that an extra zero-length packet should be appended to an IN transfer if the length is an even multiple of the maxpacket size. It has nothing to do with OUT transfers. In practice this doesn't cause any problems because gadget drivers never set req->zero for OUT transfers anyway. Still, it is an error and unnecessary code, so this patch removes the check. Signed-off-by: Alan Stern Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2272.c | 3 +-- drivers/usb/gadget/udc/net2280.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index c2011cd7df8c..564aeee1a1fe 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -573,8 +573,7 @@ net2272_read_fifo(struct net2272_ep *ep, struct net2272_request *req) /* completion */ if (unlikely(cleanup || is_short || - ((req->req.actual == req->req.length) - && !req->req.zero))) { + req->req.actual == req->req.length)) { if (cleanup) { net2272_out_flush(ep); diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index b17473a00b43..b6bbe2e448ba 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -789,8 +789,7 @@ static int read_fifo(struct net2280_ep *ep, struct net2280_request *req) (void) readl(&ep->regs->ep_rsp); } - return is_short || ((req->req.actual == req->req.length) && - !req->req.zero); + return is_short || req->req.actual == req->req.length; } /* fill out dma descriptor to match a given request */ -- cgit v1.2.3 From c40cf7705e13d288d900e044c0a2f756e9e4909a Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Tue, 16 Apr 2019 14:53:49 -0700 Subject: usb: dwc2: optionally assert phy reset when waking up On the rk3288 USB host-only port (the one that's not the OTG-enabled port) the PHY can get into a bad state when a wakeup is asserted (not just a wakeup from full system suspend but also a wakeup from autosuspend). We can get the PHY out of its bad state by asserting its "port reset", but unfortunately that seems to assert a reset onto the USB bus so it could confuse things if we don't actually deenumerate / reenumerate the device. We can also get the PHY out of its bad state by fully resetting it using the reset from the CRU (clock reset unit), which does a more full reset. The CRU-based reset appears to actually cause devices on the bus to be removed and reinserted, which fixes the problem (albeit in a hacky way). It's unfortunate that we need to do a full re-enumeration of devices at wakeup time, but this is better than alternative of letting the bus get wedged. Signed-off-by: Douglas Anderson Signed-off-by: Yunzhi Li Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 8 ++++++++ drivers/usb/dwc2/core_intr.c | 12 ++++++++++++ drivers/usb/dwc2/hcd.c | 18 +++++++++++++++--- drivers/usb/dwc2/platform.c | 9 +++++++++ 4 files changed, 44 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 30bab8463c96..764c78ebee28 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -859,6 +859,8 @@ struct dwc2_hregs_backup { * @gadget_enabled: Peripheral mode sub-driver initialization indicator. * @ll_hw_enabled: Status of low-level hardware resources. * @hibernated: True if core is hibernated + * @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a + * remote wakeup. * @frame_number: Frame number read from the core. For both device * and host modes. The value ranges are from 0 * to HFNUM_MAX_FRNUM. @@ -972,6 +974,7 @@ struct dwc2_hregs_backup { * @status_buf_dma: DMA address for status_buf * @start_work: Delayed work for handling host A-cable connection * @reset_work: Delayed work for handling a port reset + * @phy_reset_work: Work structure for doing a PHY reset * @otg_port: OTG port number * @frame_list: Frame list * @frame_list_dma: Frame list DMA address @@ -1045,6 +1048,7 @@ struct dwc2_hsotg { unsigned int gadget_enabled:1; unsigned int ll_hw_enabled:1; unsigned int hibernated:1; + unsigned int reset_phy_on_wake:1; u16 frame_number; struct phy *phy; @@ -1147,6 +1151,7 @@ struct dwc2_hsotg { struct delayed_work start_work; struct delayed_work reset_work; + struct work_struct phy_reset_work; u8 otg_port; u32 *frame_list; dma_addr_t frame_list_dma; @@ -1431,6 +1436,8 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg); int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg); int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset); +static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) +{ schedule_work(&hsotg->phy_reset_work); } #else static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) { return 0; } @@ -1454,6 +1461,7 @@ static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset) { return 0; } +static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {} #endif diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 19ae2595f1c3..6af6add3d4c0 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -435,6 +435,18 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) /* Restart the Phy Clock */ pcgcctl &= ~PCGCTL_STOPPCLK; dwc2_writel(hsotg, pcgcctl, PCGCTL); + + /* + * If we've got this quirk then the PHY is stuck upon + * wakeup. Assert reset. This will propagate out and + * eventually we'll re-enumerate the device. Not great + * but the best we can do. We can't call phy_reset() + * at interrupt time but there's no hurry, so we'll + * schedule it for later. + */ + if (hsotg->reset_phy_on_wake) + dwc2_host_schedule_phy_reset(hsotg); + mod_timer(&hsotg->wkp_timer, jiffies + msecs_to_jiffies(71)); } else { diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 8667ddf3ca74..978232a9e4a8 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4376,6 +4376,17 @@ static void dwc2_hcd_reset_func(struct work_struct *work) spin_unlock_irqrestore(&hsotg->lock, flags); } +static void dwc2_hcd_phy_reset_func(struct work_struct *work) +{ + struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg, + phy_reset_work); + int ret; + + ret = phy_reset(hsotg->phy); + if (ret) + dev_warn(hsotg->dev, "PHY reset failed\n"); +} + /* * ========================================================================= * Linux HC Driver Functions @@ -5152,6 +5163,8 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) destroy_workqueue(hsotg->wq_otg); } + cancel_work_sync(&hsotg->phy_reset_work); + del_timer(&hsotg->wkp_timer); } @@ -5293,11 +5306,10 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg) hsotg->hc_ptr_array[i] = channel; } - /* Initialize hsotg start work */ + /* Initialize work */ INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func); - - /* Initialize port reset work */ INIT_DELAYED_WORK(&hsotg->reset_work, dwc2_hcd_reset_func); + INIT_WORK(&hsotg->phy_reset_work, dwc2_hcd_phy_reset_func); /* * Allocate space for storing data on status transactions. Normally no diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 9aa9682a5cd2..c01fa8ffc0c8 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -481,6 +481,15 @@ static int dwc2_driver_probe(struct platform_device *dev) hsotg->gadget_enabled = 1; } + hsotg->reset_phy_on_wake = + of_property_read_bool(dev->dev.of_node, + "snps,reset-phy-on-wake"); + if (hsotg->reset_phy_on_wake && !hsotg->phy) { + dev_warn(hsotg->dev, + "Quirk reset-phy-on-wake only supports generic PHYs\n"); + hsotg->reset_phy_on_wake = false; + } + if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) { retval = dwc2_hcd_init(hsotg); if (retval) { -- cgit v1.2.3 From a89bae709b3492b478480a2c9734e7e9393b279c Mon Sep 17 00:00:00 2001 From: Jules Maselbas Date: Fri, 5 Apr 2019 15:35:29 +0200 Subject: usb: dwc2: Move UTMI_PHY_DATA defines closer Makes GHWCFG4_UTMI_PHY_DATA* defines closer to their relative shift and mask defines to improve readability. Acked-by: Minas Harutyunyan Signed-off-by: Jules Maselbas Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hw.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 1bc394dcfa9d..510e87ec0be8 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -310,12 +310,12 @@ #define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14) #define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14 -#define GHWCFG4_ACG_SUPPORTED BIT(12) -#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11) -#define GHWCFG4_SERVICE_INTERVAL_SUPPORTED BIT(10) #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2 +#define GHWCFG4_ACG_SUPPORTED BIT(12) +#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11) +#define GHWCFG4_SERVICE_INTERVAL_SUPPORTED BIT(10) #define GHWCFG4_XHIBER BIT(7) #define GHWCFG4_HIBER BIT(6) #define GHWCFG4_MIN_AHB_FREQ BIT(5) -- cgit v1.2.3 From fb26b553bf2627ff96c38236daab0138a82c613a Mon Sep 17 00:00:00 2001 From: Jules Maselbas Date: Fri, 5 Apr 2019 15:35:30 +0200 Subject: usb: dwc2: gadget: Remove duplicated phy init The function dwc2_hsotg_init is only called once just before calling dwc2_hsotg_core_init_disconnected which does the same initialization: setting the usbcfg register with turnaround time, timeout calibration and phy width. Acked-by: Minas Harutyunyan Signed-off-by: Jules Maselbas Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 6ac850d6ad44..9b737c4e8f50 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -4328,8 +4328,6 @@ static const struct usb_ep_ops dwc2_hsotg_ep_ops = { */ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg) { - u32 trdtim; - u32 usbcfg; /* unmask subset of endpoint interrupts */ dwc2_writel(hsotg, DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | @@ -4353,17 +4351,6 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg) dwc2_hsotg_init_fifo(hsotg); - /* keep other bits untouched (so e.g. forced modes are not lost) */ - usbcfg = dwc2_readl(hsotg, GUSBCFG); - usbcfg &= ~(GUSBCFG_TOUTCAL_MASK | GUSBCFG_PHYIF16 | GUSBCFG_SRPCAP | - GUSBCFG_HNPCAP | GUSBCFG_USBTRDTIM_MASK); - - /* set the PLL on, remove the HNP/SRP and set the PHY */ - trdtim = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5; - usbcfg |= hsotg->phyif | GUSBCFG_TOUTCAL(7) | - (trdtim << GUSBCFG_USBTRDTIM_SHIFT); - dwc2_writel(hsotg, usbcfg, GUSBCFG); - if (using_dma(hsotg)) dwc2_set_bit(hsotg, GAHBCFG, GAHBCFG_DMA_EN); } -- cgit v1.2.3 From 707d80f0a3c5fb58e61404277f6b103955fac294 Mon Sep 17 00:00:00 2001 From: Jules Maselbas Date: Fri, 5 Apr 2019 15:35:31 +0200 Subject: usb: dwc2: gadget: Replace phyif with phy_utmi_width The phy utmi width information is already set in hsotg params, phyif is only used in few places and I don't see any reason to not use hsotg's params. Moreover the utmi width was being forced to 16 bits by platform initialization which doesn't take in account HW configuration. Acked-by: Minas Harutyunyan Signed-off-by: Jules Maselbas Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 2 -- drivers/usb/dwc2/gadget.c | 20 ++++++++++++++------ drivers/usb/dwc2/platform.c | 5 +---- 3 files changed, 15 insertions(+), 12 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 764c78ebee28..8e3edf10d76d 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -871,7 +871,6 @@ struct dwc2_hregs_backup { * removed once all SoCs support usb transceiver. * @supplies: Definition of USB power supplies * @vbus_supply: Regulator supplying vbus. - * @phyif: PHY interface width * @lock: Spinlock that protects all the driver data structures * @priv: Stores a pointer to the struct usb_hcd * @queuing_high_bandwidth: True if multiple packets of a high-bandwidth @@ -1056,7 +1055,6 @@ struct dwc2_hsotg { struct dwc2_hsotg_plat *plat; struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES]; struct regulator *vbus_supply; - u32 phyif; spinlock_t lock; void *priv; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 9b737c4e8f50..614f8c34d759 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3314,20 +3314,28 @@ void dwc2_hsotg_core_init_disconnecte