From 22a5aa170c5231e64e1598a55dee92ea9a26183e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 2 Jul 2013 21:20:24 +0300 Subject: usb: dwc3: core: switch to snps,dwc3 all other drivers using Synopsys IPs with DT have a compatible of snps,$driver, in order to add consistency, we are switching over to snps,dwc3 but keeping synopsys,dwc3 in the core driver to maintain backwards compatibility. New DTS bindings should NOT use synopsys,dwc3. Signed-off-by: Felipe Balbi --- arch/arm/boot/dts/omap5.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi index e643620417a9..07be2cd7b318 100644 --- a/arch/arm/boot/dts/omap5.dtsi +++ b/arch/arm/boot/dts/omap5.dtsi @@ -644,7 +644,7 @@ utmi-mode = <2>; ranges; dwc3@4a030000 { - compatible = "synopsys,dwc3"; + compatible = "snps,dwc3"; reg = <0x4a030000 0x1000>; interrupts = ; usb-phy = <&usb2_phy>, <&usb3_phy>; -- cgit v1.2.3 From c49667e56fcb5e92efcbe816c34c3e373b33cd01 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Wed, 17 Jul 2013 09:31:00 +0300 Subject: arm: dts: tegra20: Rename USB UTMI parameters according to new definitions This patch changes the Tegra20 USB PHY nodes to use the UTMI configuration parameter names as specified in the device tree binding documentation after patch "ARM: tegra: finalize USB EHCI and PHY bindings". Signed-off-by: Mikko Perttunen Acked-by: Stephen Warren Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- arch/arm/boot/dts/tegra20.dtsi | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'arch') diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 9653fd8288d2..e4570834512e 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -477,13 +477,13 @@ <&tegra_car TEGRA20_CLK_USBD>; clock-names = "reg", "pll_u", "timer", "utmi-pads"; nvidia,has-legacy-mode; - hssync_start_delay = <9>; - idle_wait_delay = <17>; - elastic_limit = <16>; - term_range_adj = <6>; - xcvr_setup = <9>; - xcvr_lsfslew = <1>; - xcvr_lsrslew = <1>; + nvidia,hssync-start-delay = <9>; + nvidia,idle-wait-delay = <17>; + nvidia,elastic-limit = <16>; + nvidia,term-range-adj = <6>; + nvidia,xcvr-setup = <9>; + nvidia,xcvr-lsfslew = <1>; + nvidia,xcvr-lsrslew = <1>; status = "disabled"; }; @@ -527,13 +527,13 @@ <&tegra_car TEGRA20_CLK_CLK_M>, <&tegra_car TEGRA20_CLK_USBD>; clock-names = "reg", "pll_u", "timer", "utmi-pads"; - hssync_start_delay = <9>; - idle_wait_delay = <17>; - elastic_limit = <16>; - term_range_adj = <6>; - xcvr_setup = <9>; - xcvr_lsfslew = <2>; - xcvr_lsrslew = <2>; + nvidia,hssync-start-delay = <9>; + nvidia,idle-wait-delay = <17>; + nvidia,elastic-limit = <16>; + nvidia,term-range-adj = <6>; + nvidia,xcvr-setup = <9>; + nvidia,xcvr-lsfslew = <2>; + nvidia,xcvr-lsrslew = <2>; status = "disabled"; }; -- cgit v1.2.3 From 103566e40d0a57b028a51671d852b2650947a5ec Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Wed, 17 Jul 2013 10:37:51 +0300 Subject: arm: tegra: Remove obsolete nvidia,vbus-gpio properties USB VBUS regulators are now specified with the vbus-supply property instead of nvidia,vbus-gpio, so remove the obsolete properties. The equivalent vbus-supply properties were already added in patch "ARM: tegra: update device trees for USB binding rework". Signed-off-by: Mikko Perttunen Acked-by: Stephen Warren Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- arch/arm/boot/dts/tegra20-seaboard.dts | 1 - arch/arm/boot/dts/tegra20-trimslice.dts | 1 - arch/arm/boot/dts/tegra20-whistler.dts | 2 -- 3 files changed, 4 deletions(-) (limited to 'arch') diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts index 365760b33a26..52526c9a1329 100644 --- a/arch/arm/boot/dts/tegra20-seaboard.dts +++ b/arch/arm/boot/dts/tegra20-seaboard.dts @@ -566,7 +566,6 @@ usb@c5000000 { status = "okay"; - nvidia,vbus-gpio = <&gpio TEGRA_GPIO(D, 0) GPIO_ACTIVE_HIGH>; dr_mode = "otg"; }; diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts index ed4b901b0227..3e57b87cc75f 100644 --- a/arch/arm/boot/dts/tegra20-trimslice.dts +++ b/arch/arm/boot/dts/tegra20-trimslice.dts @@ -312,7 +312,6 @@ usb@c5000000 { status = "okay"; - nvidia,vbus-gpio = <&gpio TEGRA_GPIO(V, 2) GPIO_ACTIVE_HIGH>; }; usb-phy@c5000000 { diff --git a/arch/arm/boot/dts/tegra20-whistler.dts b/arch/arm/boot/dts/tegra20-whistler.dts index ab67c94db280..a6b1b5bdf288 100644 --- a/arch/arm/boot/dts/tegra20-whistler.dts +++ b/arch/arm/boot/dts/tegra20-whistler.dts @@ -509,7 +509,6 @@ usb@c5000000 { status = "okay"; - nvidia,vbus-gpio = <&tca6416 0 GPIO_ACTIVE_HIGH>; }; usb-phy@c5000000 { @@ -519,7 +518,6 @@ usb@c5008000 { status = "okay"; - nvidia,vbus-gpio = <&tca6416 1 GPIO_ACTIVE_HIGH>; }; usb-phy@c5008000 { -- cgit v1.2.3 From 5fed6828318119656e243c4f0a11955cefc8eebd Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 21:38:04 +0300 Subject: ARM: tegra: Remove USB platform data USB-related platform data is not used anymore in the Tegra USB drivers, so remove all of it. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Felipe Balbi --- arch/arm/mach-tegra/tegra.c | 38 +-------------------------------- include/linux/platform_data/tegra_usb.h | 32 --------------------------- include/linux/usb/tegra_usb_phy.h | 5 ----- 3 files changed, 1 insertion(+), 74 deletions(-) delete mode 100644 include/linux/platform_data/tegra_usb.h (limited to 'arch') diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index 0d1e4128d460..fc97cfd52769 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -46,40 +45,6 @@ #include "fuse.h" #include "iomap.h" -static struct tegra_ehci_platform_data tegra_ehci1_pdata = { - .operating_mode = TEGRA_USB_OTG, - .power_down_on_bus_suspend = 1, - .vbus_gpio = -1, -}; - -static struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config = { - .reset_gpio = -1, - .clk = "cdev2", -}; - -static struct tegra_ehci_platform_data tegra_ehci2_pdata = { - .phy_config = &tegra_ehci2_ulpi_phy_config, - .operating_mode = TEGRA_USB_HOST, - .power_down_on_bus_suspend = 1, - .vbus_gpio = -1, -}; - -static struct tegra_ehci_platform_data tegra_ehci3_pdata = { - .operating_mode = TEGRA_USB_HOST, - .power_down_on_bus_suspend = 1, - .vbus_gpio = -1, -}; - -static struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = { - OF_DEV_AUXDATA("nvidia,tegra20-ehci", 0xC5000000, "tegra-ehci.0", - &tegra_ehci1_pdata), - OF_DEV_AUXDATA("nvidia,tegra20-ehci", 0xC5004000, "tegra-ehci.1", - &tegra_ehci2_pdata), - OF_DEV_AUXDATA("nvidia,tegra20-ehci", 0xC5008000, "tegra-ehci.2", - &tegra_ehci3_pdata), - {} -}; - static void __init tegra_dt_init(void) { struct soc_device_attribute *soc_dev_attr; @@ -112,8 +77,7 @@ static void __init tegra_dt_init(void) * devices */ out: - of_platform_populate(NULL, of_default_bus_match_table, - tegra20_auxdata_lookup, parent); + of_platform_populate(NULL, of_default_bus_match_table, NULL, parent); } static void __init trimslice_init(void) diff --git a/include/linux/platform_data/tegra_usb.h b/include/linux/platform_data/tegra_usb.h deleted file mode 100644 index 66c673fef408..000000000000 --- a/include/linux/platform_data/tegra_usb.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2010 Google, Inc. - * - * 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. - * - */ - -#ifndef _TEGRA_USB_H_ -#define _TEGRA_USB_H_ - -enum tegra_usb_operating_modes { - TEGRA_USB_DEVICE, - TEGRA_USB_HOST, - TEGRA_USB_OTG, -}; - -struct tegra_ehci_platform_data { - enum tegra_usb_operating_modes operating_mode; - /* power down the phy on bus suspend */ - int power_down_on_bus_suspend; - void *phy_config; - int vbus_gpio; -}; - -#endif /* _TEGRA_USB_H_ */ diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index c2bc7106aaa8..847415313ad2 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -28,11 +28,6 @@ struct tegra_utmip_config { u8 xcvr_lsrslew; }; -struct tegra_ulpi_config { - int reset_gpio; - const char *clk; -}; - enum tegra_usb_phy_port_speed { TEGRA_USB_PHY_PORT_SPEED_FULL = 0, TEGRA_USB_PHY_PORT_SPEED_LOW, -- cgit v1.2.3 From 3fa4d7344be0afebd80382ffeea6b1787cccf971 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 26 Jul 2013 12:16:42 +0200 Subject: usb: phy: rename nop_usb_xceiv => usb_phy_gen_xceiv The "nop" driver isn't a do-nothing-stub but supports a couple functions like clock on/off or is able to use a voltage regulator. This patch simply renames the driver to "generic" since it is easy possible to extend it by a simple function istead of writing a complete driver. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- arch/arm/mach-omap2/board-omap3beagle.c | 4 +- arch/arm/mach-omap2/board-omap3evm.c | 4 +- arch/arm/mach-omap2/board-omap3pandora.c | 2 +- arch/arm/mach-omap2/usb-host.c | 10 +- drivers/usb/dwc3/dwc3-exynos.c | 8 +- drivers/usb/dwc3/dwc3-pci.c | 8 +- drivers/usb/musb/am35x.c | 2 +- drivers/usb/musb/blackfin.c | 2 +- drivers/usb/musb/da8xx.c | 2 +- drivers/usb/musb/davinci.c | 2 +- drivers/usb/musb/musb_dsps.c | 2 +- drivers/usb/musb/tusb6010.c | 2 +- drivers/usb/phy/Makefile | 2 +- drivers/usb/phy/phy-generic.c | 292 +++++++++++++++++++++++++++++++ drivers/usb/phy/phy-nop.c | 292 ------------------------------- include/linux/usb/nop-usb-xceiv.h | 29 --- include/linux/usb/usb_phy_gen_xceiv.h | 29 +++ 17 files changed, 346 insertions(+), 346 deletions(-) create mode 100644 drivers/usb/phy/phy-generic.c delete mode 100644 drivers/usb/phy/phy-nop.c delete mode 100644 include/linux/usb/nop-usb-xceiv.h create mode 100644 include/linux/usb/usb_phy_gen_xceiv.h (limited to 'arch') diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index 04c116555412..1c6ae5f5bae7 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -279,7 +279,7 @@ static struct regulator_consumer_supply beagle_vsim_supply[] = { static struct gpio_led gpio_leds[]; /* PHY's VCC regulator might be added later, so flag that we need it */ -static struct nop_usb_xceiv_platform_data hsusb2_phy_data = { +static struct usb_phy_gen_xceiv_platform_data hsusb2_phy_data = { .needs_vcc = true, }; diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index 8c026269baca..52bdddd41e0e 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -468,7 +468,7 @@ struct wl12xx_platform_data omap3evm_wlan_data __initdata = { static struct regulator_consumer_supply omap3evm_vaux2_supplies[] = { REGULATOR_SUPPLY("VDD_CSIPHY1", "omap3isp"), /* OMAP ISP */ REGULATOR_SUPPLY("VDD_CSIPHY2", "omap3isp"), /* OMAP ISP */ - REGULATOR_SUPPLY("vcc", "nop_usb_xceiv.2"), /* hsusb port 2 */ + REGULATOR_SUPPLY("vcc", "usb_phy_gen_xceiv.2"), /* hsusb port 2 */ REGULATOR_SUPPLY("vaux2", NULL), }; diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index b1547a0edfcd..d2b455e70486 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -352,7 +352,7 @@ static struct regulator_consumer_supply pandora_vcc_lcd_supply[] = { }; static struct regulator_consumer_supply pandora_usb_phy_supply[] = { - REGULATOR_SUPPLY("vcc", "nop_usb_xceiv.2"), /* hsusb port 2 */ + REGULATOR_SUPPLY("vcc", "usb_phy_gen_xceiv.2"), /* hsusb port 2 */ }; /* ads7846 on SPI and 2 nub controllers on I2C */ diff --git a/arch/arm/mach-omap2/usb-host.c b/arch/arm/mach-omap2/usb-host.c index 2eb19d4d0aa1..e83a6a4b184a 100644 --- a/arch/arm/mach-omap2/usb-host.c +++ b/arch/arm/mach-omap2/usb-host.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "soc.h" #include "omap_device.h" @@ -349,7 +349,7 @@ static struct fixed_voltage_config hsusb_reg_config = { /* .init_data filled later */ }; -static const char *nop_name = "nop_usb_xceiv"; /* NOP PHY driver */ +static const char *nop_name = "usb_phy_gen_xceiv"; /* NOP PHY driver */ static const char *reg_name = "reg-fixed-voltage"; /* Regulator driver */ /** @@ -460,9 +460,9 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys) pdevinfo.name = nop_name; pdevinfo.id = phy->port; pdevinfo.data = phy->platform_data; - pdevinfo.size_data = sizeof(struct nop_usb_xceiv_platform_data); - - scnprintf(phy_id, MAX_STR, "nop_usb_xceiv.%d", + pdevinfo.size_data = + sizeof(struct usb_phy_gen_xceiv_platform_data); + scnprintf(phy_id, MAX_STR, "usb_phy_gen_xceiv.%d", phy->port); pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(pdev)) { diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 8ce9d7fd6cfc..a179c5a54b39 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include @@ -34,13 +34,13 @@ struct dwc3_exynos { static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) { - struct nop_usb_xceiv_platform_data pdata; + struct usb_phy_gen_xceiv_platform_data pdata; struct platform_device *pdev; int ret; memset(&pdata, 0x00, sizeof(pdata)); - pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO); + pdev = platform_device_alloc("usb_phy_gen_xceiv", PLATFORM_DEVID_AUTO); if (!pdev) return -ENOMEM; @@ -51,7 +51,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) if (ret) goto err1; - pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO); + pdev = platform_device_alloc("usb_phy_gen_xceiv", PLATFORM_DEVID_AUTO); if (!pdev) { ret = -ENOMEM; goto err1; diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index ed07ec04a962..87ea3d503fc5 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -43,7 +43,7 @@ #include #include -#include +#include /* FIXME define these in */ #define PCI_VENDOR_ID_SYNOPSYS 0x16c3 @@ -58,13 +58,13 @@ struct dwc3_pci { static int dwc3_pci_register_phys(struct dwc3_pci *glue) { - struct nop_usb_xceiv_platform_data pdata; + struct usb_phy_gen_xceiv_platform_data pdata; struct platform_device *pdev; int ret; memset(&pdata, 0x00, sizeof(pdata)); - pdev = platform_device_alloc("nop_usb_xceiv", 0); + pdev = platform_device_alloc("usb_phy_gen_xceiv", 0); if (!pdev) return -ENOMEM; @@ -75,7 +75,7 @@ static int dwc3_pci_register_phys(struct dwc3_pci *glue) if (ret) goto err1; - pdev = platform_device_alloc("nop_usb_xceiv", 1); + pdev = platform_device_alloc("usb_phy_gen_xceiv", 1); if (!pdev) { ret = -ENOMEM; goto err1; diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 2231850c0625..5733a209ab95 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include "musb_core.h" diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 6ba8439bd5a6..195e96674fff 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 0da6f648a9fe..ccc6b63bc380 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index f8aeaf2e2cd1..91f300e93cf4 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 5233804d66b1..55fe3c292cd2 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 2c06a8969a9f..20f182c6184a 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include "musb_core.h" diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 070eca3af18b..24c5816409fd 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -14,7 +14,7 @@ phy-fsl-usb2-objs := phy-fsl-usb.o phy-fsm-usb.o obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb2.o obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o -obj-$(CONFIG_NOP_USB_XCEIV) += phy-nop.o +obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c new file mode 100644 index 000000000000..f379b7ded037 --- /dev/null +++ b/drivers/usb/phy/phy-generic.c @@ -0,0 +1,292 @@ +/* + * drivers/usb/otg/nop-usb-xceiv.c + * + * NOP USB transceiver for all USB transceiver which are either built-in + * into USB IP or which are mostly autonomous. + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Ajay Kumar Gupta + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Current status: + * This provides a "nop" transceiver for PHYs which are + * autonomous such as isp1504, isp1707, etc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct usb_phy_gen_xceiv { + struct usb_phy phy; + struct device *dev; + struct clk *clk; + struct regulator *vcc; + struct regulator *reset; +}; + +static struct platform_device *pd; + +void usb_nop_xceiv_register(void) +{ + if (pd) + return; + pd = platform_device_register_simple("usb_phy_gen_xceiv", -1, NULL, 0); + if (!pd) { + pr_err("Unable to register generic usb transceiver\n"); + return; + } +} +EXPORT_SYMBOL(usb_nop_xceiv_register); + +void usb_nop_xceiv_unregister(void) +{ + platform_device_unregister(pd); + pd = NULL; +} +EXPORT_SYMBOL(usb_nop_xceiv_unregister); + +static int nop_set_suspend(struct usb_phy *x, int suspend) +{ + return 0; +} + +static int nop_init(struct usb_phy *phy) +{ + struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); + + if (!IS_ERR(nop->vcc)) { + if (regulator_enable(nop->vcc)) + dev_err(phy->dev, "Failed to enable power\n"); + } + + if (!IS_ERR(nop->clk)) + clk_enable(nop->clk); + + if (!IS_ERR(nop->reset)) { + /* De-assert RESET */ + if (regulator_enable(nop->reset)) + dev_err(phy->dev, "Failed to de-assert reset\n"); + } + + return 0; +} + +static void nop_shutdown(struct usb_phy *phy) +{ + struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); + + if (!IS_ERR(nop->reset)) { + /* Assert RESET */ + if (regulator_disable(nop->reset)) + dev_err(phy->dev, "Failed to assert reset\n"); + } + + if (!IS_ERR(nop->clk)) + clk_disable(nop->clk); + + if (!IS_ERR(nop->vcc)) { + if (regulator_disable(nop->vcc)) + dev_err(phy->dev, "Failed to disable power\n"); + } +} + +static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) +{ + if (!otg) + return -ENODEV; + + if (!gadget) { + otg->gadget = NULL; + return -ENODEV; + } + + otg->gadget = gadget; + otg->phy->state = OTG_STATE_B_IDLE; + return 0; +} + +static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + + if (!host) { + otg->host = NULL; + return -ENODEV; + } + + otg->host = host; + return 0; +} + +static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_phy_gen_xceiv_platform_data *pdata = pdev->dev.platform_data; + struct usb_phy_gen_xceiv *nop; + enum usb_phy_type type = USB_PHY_TYPE_USB2; + int err; + u32 clk_rate = 0; + bool needs_vcc = false; + bool needs_reset = false; + + nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); + if (!nop) + return -ENOMEM; + + nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg), + GFP_KERNEL); + if (!nop->phy.otg) + return -ENOMEM; + + if (dev->of_node) { + struct device_node *node = dev->of_node; + + if (of_property_read_u32(node, "clock-frequency", &clk_rate)) + clk_rate = 0; + + needs_vcc = of_property_read_bool(node, "vcc-supply"); + needs_reset = of_property_read_bool(node, "reset-supply"); + + } else if (pdata) { + type = pdata->type; + clk_rate = pdata->clk_rate; + needs_vcc = pdata->needs_vcc; + needs_reset = pdata->needs_reset; + } + + nop->clk = devm_clk_get(&pdev->dev, "main_clk"); + if (IS_ERR(nop->clk)) { + dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n", + PTR_ERR(nop->clk)); + } + + if (!IS_ERR(nop->clk) && clk_rate) { + err = clk_set_rate(nop->clk, clk_rate); + if (err) { + dev_err(&pdev->dev, "Error setting clock rate\n"); + return err; + } + } + + if (!IS_ERR(nop->clk)) { + err = clk_prepare(nop->clk); + if (err) { + dev_err(&pdev->dev, "Error preparing clock\n"); + return err; + } + } + + nop->vcc = devm_regulator_get(&pdev->dev, "vcc"); + if (IS_ERR(nop->vcc)) { + dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", + PTR_ERR(nop->vcc)); + if (needs_vcc) + return -EPROBE_DEFER; + } + + nop->reset = devm_regulator_get(&pdev->dev, "reset"); + if (IS_ERR(nop->reset)) { + dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", + PTR_ERR(nop->reset)); + if (needs_reset) + return -EPROBE_DEFER; + } + + nop->dev = &pdev->dev; + nop->phy.dev = nop->dev; + nop->phy.label = "nop-xceiv"; + nop->phy.set_suspend = nop_set_suspend; + nop->phy.init = nop_init; + nop->phy.shutdown = nop_shutdown; + nop->phy.state = OTG_STATE_UNDEFINED; + nop->phy.type = type; + + nop->phy.otg->phy = &nop->phy; + nop->phy.otg->set_host = nop_set_host; + nop->phy.otg->set_peripheral = nop_set_peripheral; + + err = usb_add_phy_dev(&nop->phy); + if (err) { + dev_err(&pdev->dev, "can't register transceiver, err: %d\n", + err); + goto err_add; + } + + platform_set_drvdata(pdev, nop); + + ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); + + return 0; + +err_add: + if (!IS_ERR(nop->clk)) + clk_unprepare(nop->clk); + return err; +} + +static int usb_phy_gen_xceiv_remove(struct platform_device *pdev) +{ + struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev); + + if (!IS_ERR(nop->clk)) + clk_unprepare(nop->clk); + + usb_remove_phy(&nop->phy); + + return 0; +} + +static const struct of_device_id nop_xceiv_dt_ids[] = { + { .compatible = "usb-nop-xceiv" }, + { } +}; + +MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); + +static struct platform_driver usb_phy_gen_xceiv_driver = { + .probe = usb_phy_gen_xceiv_probe, + .remove = usb_phy_gen_xceiv_remove, + .driver = { + .name = "usb_phy_gen_xceiv", + .owner = THIS_MODULE, + .of_match_table = nop_xceiv_dt_ids, + }, +}; + +static int __init usb_phy_gen_xceiv_init(void) +{ + return platform_driver_register(&usb_phy_gen_xceiv_driver); +} +subsys_initcall(usb_phy_gen_xceiv_init); + +static void __exit usb_phy_gen_xceiv_exit(void) +{ + platform_driver_unregister(&usb_phy_gen_xceiv_driver); +} +module_exit(usb_phy_gen_xceiv_exit); + +MODULE_ALIAS("platform:usb_phy_gen_xceiv"); +MODULE_AUTHOR("Texas Instruments Inc"); +MODULE_DESCRIPTION("NOP USB Transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/phy-nop.c b/drivers/usb/phy/phy-nop.c deleted file mode 100644 index 55445e5d72e5..000000000000 --- a/drivers/usb/phy/phy-nop.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * drivers/usb/otg/nop-usb-xceiv.c - * - * NOP USB transceiver for all USB transceiver which are either built-in - * into USB IP or which are mostly autonomous. - * - * Copyright (C) 2009 Texas Instruments Inc - * Author: Ajay Kumar Gupta - * - * 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 of the License, 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Current status: - * This provides a "nop" transceiver for PHYs which are - * autonomous such as isp1504, isp1707, etc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct nop_usb_xceiv { - struct usb_phy phy; - struct device *dev; - struct clk *clk; - struct regulator *vcc; - struct regulator *reset; -}; - -static struct platform_device *pd; - -void usb_nop_xceiv_register(void) -{ - if (pd) - return; - pd = platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0); - if (!pd) { - printk(KERN_ERR "Unable to register usb nop transceiver\n"); - return; - } -} -EXPORT_SYMBOL(usb_nop_xceiv_register); - -void usb_nop_xceiv_unregister(void) -{ - platform_device_unregister(pd); - pd = NULL; -} -EXPORT_SYMBOL(usb_nop_xceiv_unregister); - -static int nop_set_suspend(struct usb_phy *x, int suspend) -{ - return 0; -} - -static int nop_init(struct usb_phy *phy) -{ - struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); - - if (!IS_ERR(nop->vcc)) { - if (regulator_enable(nop->vcc)) - dev_err(phy->dev, "Failed to enable power\n"); - } - - if (!IS_ERR(nop->clk)) - clk_enable(nop->clk); - - if (!IS_ERR(nop->reset)) { - /* De-assert RESET */ - if (regulator_enable(nop->reset)) - dev_err(phy->dev, "Failed to de-assert reset\n"); - } - - return 0; -} - -static void nop_shutdown(struct usb_phy *phy) -{ - struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); - - if (!IS_ERR(nop->reset)) { - /* Assert RESET */ - if (regulator_disable(nop->reset)) - dev_err(phy->dev, "Failed to assert reset\n"); - } - - if (!IS_ERR(nop->clk)) - clk_disable(nop->clk); - - if (!IS_ERR(nop->vcc)) { - if (regulator_disable(nop->vcc)) - dev_err(phy->dev, "Failed to disable power\n"); - } -} - -static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) -{ - if (!otg) - return -ENODEV; - - if (!gadget) { - otg->gadget = NULL; - return -ENODEV; - } - - otg->gadget = gadget; - otg->phy->state = OTG_STATE_B_IDLE; - return 0; -} - -static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - if (!otg) - return -ENODEV; - - if (!host) { - otg->host = NULL; - return -ENODEV; - } - - otg->host = host; - return 0; -} - -static int nop_usb_xceiv_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data; - struct nop_usb_xceiv *nop; - enum usb_phy_type type = USB_PHY_TYPE_USB2; - int err; - u32 clk_rate = 0; - bool needs_vcc = false; - bool needs_reset = false; - - nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); - if (!nop) - return -ENOMEM; - - nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg), - GFP_KERNEL); - if (!nop->phy.otg) - return -ENOMEM; - - if (dev->of_node) { - struct device_node *node = dev->of_node; - - if (of_property_read_u32(node, "clock-frequency", &clk_rate)) - clk_rate = 0; - - needs_vcc = of_property_read_bool(node, "vcc-supply"); - needs_reset = of_property_read_bool(node, "reset-supply"); - - } else if (pdata) { - type = pdata->type; - clk_rate = pdata->clk_rate; - needs_vcc = pdata->needs_vcc; - needs_reset = pdata->needs_reset; - } - - nop->clk = devm_clk_get(&pdev->dev, "main_clk"); - if (IS_ERR(nop->clk)) { - dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n", - PTR_ERR(nop->clk)); - } - - if (!IS_ERR(nop->clk) && clk_rate) { - err = clk_set_rate(nop->clk, clk_rate); - if (err) { - dev_err(&pdev->dev, "Error setting clock rate\n"); - return err; - } - } - - if (!IS_ERR(nop->clk)) { - err = clk_prepare(nop->clk); - if (err) { - dev_err(&pdev->dev, "Error preparing clock\n"); - return err; - } - } - - nop->vcc = devm_regulator_get(&pdev->dev, "vcc"); - if (IS_ERR(nop->vcc)) { - dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", - PTR_ERR(nop->vcc)); - if (needs_vcc) - return -EPROBE_DEFER; - } - - nop->reset = devm_regulator_get(&pdev->dev, "reset"); - if (IS_ERR(nop->reset)) { - dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", - PTR_ERR(nop->reset)); - if (needs_reset) - return -EPROBE_DEFER; - } - - nop->dev = &pdev->dev; - nop->phy.dev = nop->dev; - nop->phy.label = "nop-xceiv"; - nop->phy.set_suspend = nop_set_suspend; - nop->phy.init = nop_init; - nop->phy.shutdown = nop_shutdown; - nop->phy.state = OTG_STATE_UNDEFINED; - nop->phy.type = type; - - nop->phy.otg->phy = &nop->phy; - nop->phy.otg->set_host = nop_set_host; - nop->phy.otg->set_peripheral = nop_set_peripheral; - - err = usb_add_phy_dev(&nop->phy); - if (err) { - dev_err(&pdev->dev, "can't register transceiver, err: %d\n", - err); - goto err_add; - } - - platform_set_drvdata(pdev, nop); - - ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); - - return 0; - -err_add: - if (!IS_ERR(nop->clk)) - clk_unprepare(nop->clk); - return err; -} - -static int nop_usb_xceiv_remove(struct platform_device *pdev) -{ - struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); - - if (!IS_ERR(nop->clk)) - clk_unprepare(nop->clk); - - usb_remove_phy(&nop->phy); - - return 0; -} - -static const struct of_device_id nop_xceiv_dt_ids[] = { - { .compatible = "usb-nop-xceiv" }, - { } -}; - -MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); - -static struct platform_driver nop_usb_xceiv_driver = { - .probe = nop_usb_xceiv_probe, - .remove = nop_usb_xceiv_remove, - .driver = { - .name = "nop_usb_xceiv", - .owner = THIS_MODULE, - .of_match_table = nop_xceiv_dt_ids, - }, -}; - -static int __init nop_usb_xceiv_init(void) -{ - return platform_driver_register(&nop_usb_xceiv_driver); -} -subsys_initcall(nop_usb_xceiv_init); - -static void __exit nop_usb_xceiv_exit(void) -{ - platform_driver_unregister(&nop_usb_xceiv_driver); -} -module_exit(nop_usb_xceiv_exit); - -MODULE_ALIAS("platform:nop_usb_xceiv"); -MODULE_AUTHOR("Texas Instruments Inc"); -MODULE_DESCRIPTION("NOP USB Transceiver driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/usb/nop-usb-xceiv.h b/include/linux/usb/nop-usb-xceiv.h deleted file mode 100644 index 148d35171aac..000000000000 --- a/include/linux/usb/nop-usb-xceiv.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __LINUX_USB_NOP_XCEIV_H -#define __LINUX_USB_NOP_XCEIV_H - -#include - -struct nop_usb_xceiv_platform_data { - enum usb_phy_type type; - unsigned long clk_rate; - - /* if set fails with -EPROBE_DEFER if can't get regulator */ - unsigned int needs_vcc:1; - unsigned int needs_reset:1; -}; - -#if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) -/* sometimes transceivers are accessed only through e.g. ULPI */ -extern void usb_nop_xceiv_register(void); -extern void usb_nop_xceiv_unregister(void); -#else -static inline void usb_nop_xceiv_register(void) -{ -} - -static inline void usb_nop_xceiv_unregister(void) -{ -} -#endif - -#endif /* __LINUX_USB_NOP_XCEIV_H */ diff --git a/include/linux/usb/usb_phy_gen_xceiv.h b/include/linux/usb/usb_phy_gen_xceiv.h new file mode 100644 index 000000000000..f9a7e7bc925b --- /dev/null +++ b/include/linux/usb/usb_phy_gen_xceiv.h @@ -0,0 +1,29 @@ +#ifndef __LINUX_USB_NOP_XCEIV_H +#define __LINUX_USB_NOP_XCEIV_H + +#include + +struct usb_phy_gen_xceiv_platform_data { + enum usb_phy_type type; + unsigned long clk_rate; + + /* if set fails with -EPROBE_DEFER if can't get regulator */ + unsigned int needs_vcc:1; + unsigned int needs_reset:1; +}; + +#if IS_ENABLED(CONFIG_NOP_USB_XCEIV) +/* sometimes transceivers are accessed only through e.g. ULPI */ +extern void usb_nop_xceiv_register(void); +extern void usb_nop_xceiv_unregister(void); +#else +static inline void usb_nop_xceiv_register(void) +{ +} + +static inline void usb_nop_xceiv_unregister(void) +{ +} +#endif + +#endif /* __LINUX_USB_NOP_XCEIV_H */ -- cgit v1.2.3 From 97238b35d5bbb5d5312d83c30a429824b777619f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 5 Jul 2013 14:51:33 +0200 Subject: usb: musb: dsps: use proper child nodes This moves the two instances from the big node into two child nodes. The glue layer ontop does almost nothing. There is one devices containing the control module for USB (2) phy, (2) usb and later the dma engine. The usb device is the "glue device" which contains the musb device as a child. This is what we do ever since. The new file musb_am335x is just here to prob the new bus and populate child devices. There are a lot of changes to the dsps file as a result of the changes: - musb_core_offset This is gone. The device tree provides memory ressources information for the device there is no need to "fix" things - instances This is gone as well. If we have two instances then we have have two child enabled nodes in the device tree. For instance the SoC in beagle bone has two USB instances but only one has been wired up so there is no need to load and init the second instance since it won't be used. - dsps_glue is now per glue device In the past there was one of this structs but with an array of two and each instance accessed its variable depending on the platform device id. - no unneeded copy of structs I do not know why struct dsps_musb_wrapper is copied but it is not necessary. The same goes for musb_hdrc_platform_data which allocated on demand and then again by platform_device_add_data(). One copy is enough. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- arch/arm/boot/dts/am335x-bone.dts | 16 +++ arch/arm/boot/dts/am335x-evm.dts | 24 ++++ arch/arm/boot/dts/am335x-evmsk.dts | 16 +++ arch/arm/boot/dts/am33xx.dtsi | 97 ++++++++++++-- drivers/usb/musb/Kconfig | 4 + drivers/usb/musb/Makefile | 3 + drivers/usb/musb/musb_am335x.c | 55 ++++++++ drivers/usb/musb/musb_dsps.c | 255 ++++++++++++++----------------------- 8 files changed, 299 insertions(+), 171 deletions(-) create mode 100644 drivers/usb/musb/musb_am335x.c (limited to 'arch') diff --git a/arch/arm/boot/dts/am335x-bone.dts b/arch/arm/boot/dts/am335x-bone.dts index 444b4ede0d60..a8907b57c75c 100644 --- a/arch/arm/boot/dts/am335x-bone.dts +++ b/arch/arm/boot/dts/am335x-bone.dts @@ -120,6 +120,22 @@ status = "okay"; }; + musb: usb@47400000 { + status = "okay"; + + control@44e10000 { + status = "okay"; + }; + + phy@47401300 { + status = "okay"; + }; + + usb@47401000 { + status = "okay"; + }; + }; + i2c0: i2c@44e0b000 { pinctrl-names = "default"; pinctrl-0 = <&i2c0_pins>; diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts index 3aee1a43782d..b2987e03b19f 100644 --- a/arch/arm/boot/dts/am335x-evm.dts +++ b/arch/arm/boot/dts/am335x-evm.dts @@ -171,6 +171,30 @@ }; }; + musb: usb@47400000 { + status = "okay"; + + control@44e10000 { + status = "okay"; + }; + + phy@47401300 { + status = "okay"; + }; + + phy@47401b00 { + status = "okay"; + }; + + usb@47401000 { + status = "okay"; + }; + + usb@47401800 { + status = "okay"; + }; + }; + i2c1: i2c@4802a000 { pinctrl-names = "default"; pinctrl-0 = <&i2c1_pins>; diff --git a/arch/arm/boot/dts/am335x-evmsk.dts b/arch/arm/boot/dts/am335x-evmsk.dts index 0c8ad173d2b0..e92446c6846e 100644 --- a/arch/arm/boot/dts/am335x-evmsk.dts +++ b/arch/arm/boot/dts/am335x-evmsk.dts @@ -207,6 +207,22 @@ }; }; + musb: usb@47400000 { + status = "okay"; + + control@44e10000 { + status = "okay"; + }; + + phy@47401300 { + status = "okay"; + }; + + usb@47401000 { + status = "okay"; + }; + }; + epwmss2: epwmss@48304000 { status = "okay"; diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 38b446ba1ce1..e1e773cd2556 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -26,6 +26,10 @@ serial5 = &uart5; d_can0 = &dcan0; d_can1 = &dcan1; + usb0 = &usb0; + usb1 = &usb1; + phy0 = &usb0_phy; + phy1 = &usb1_phy; }; cpus { @@ -333,21 +337,86 @@ status = "disabled"; }; - usb@47400000 { - compatible = "ti,musb-am33xx"; - reg = <0x47400000 0x1000 /* usbss */ - 0x47401000 0x800 /* musb instance 0 */ - 0x47401800 0x800>; /* musb instance 1 */ - interrupts = <17 /* usbss */ - 18 /* musb instance 0 */ - 19>; /* musb instance 1 */ - multipoint = <1>; - num-eps = <16>; - ram-bits = <12>; - port0-mode = <3>; - port1-mode = <3>; - power = <250>; + usb: usb@47400000 { + compatible = "ti,am33xx-usb"; + reg = <0x47400000 0x1000>; + ranges; + #address-cells = <1>; + #size-cells = <1>; ti,hwmods = "usb_otg_hs"; + status = "disabled"; + + ctrl_mod: control@44e10000 { + compatible = "ti,am335x-usb-ctrl-module"; + reg = <0x44e10620 0x10 + 0x44e10648 0x4>; + reg-names = "phy_ctrl", "wakeup"; + status = "disabled"; + }; + + usb0_phy: phy@47401300 { + compatible = "ti,am335x-usb-phy"; + reg = <0x47401300 0x100>; + reg-names = "phy"; + status = "disabled"; + ti,ctrl_mod = <&ctrl_mod>; + }; + + usb0: usb@47401000 { + compatible = "ti,musb-am33xx"; + ranges; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x47401000 0x200>; + reg-names = "control"; + status = "disabled"; + + musb0: usb@47401400 { + compatible = "mg,musbmhdrc"; + reg = <0x47401400 0x400>; + reg-names = "mc"; + interrupts = <18>; + interrupt-names = "mc"; + multipoint = <1>; + num-eps = <16>; + ram-bits = <12>; + port-mode = <3>; + power = <250>; + phys = <&usb0_phy>; + }; + }; + + usb1_phy: phy@47401b00 { + compatible = "ti,am335x-usb-phy"; + reg = <0x47401b00 0x100>; + reg-names = "phy"; + status = "disabled"; + ti,ctrl_mod = <&ctrl_mod>; + }; + + usb1: usb@47401800 { + compatible = "ti,musb-am33xx"; + ranges; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x47401800 0x200>; + reg-names = "control"; + status = "disabled"; + + musb1: usb@47401c00 { + compatible = "mg,musbmhdrc"; + reg = <0x47401c00 0x400>; + reg-names = "mc"; + interrupts = <19>; + interrupt-names = "mc"; + multipoint = <1>; + num-eps = <16>; + ram-bits = <12>; + port-mode = <3>; + power = <250>; + phys = <&usb1_phy>; + }; + }; }; epwmss0: epwmss@48300000 { diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 797e3fd45510..b7257ae038fd 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -83,6 +83,7 @@ config USB_MUSB_AM35X config USB_MUSB_DSPS tristate "TI DSPS platforms" + select USB_MUSB_AM335X_CHILD config USB_MUSB_BLACKFIN tristate "Blackfin" @@ -93,6 +94,9 @@ config USB_MUSB_UX500 endchoice +config USB_MUSB_AM335X_CHILD + tristate + choice prompt 'MUSB DMA mode' default MUSB_PIO_ONLY if ARCH_MULTIPLATFORM diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 2b82ed7c85ca..52f552c1ba2b 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -20,6 +20,9 @@ obj-$(CONFIG_USB_MUSB_DA8XX) += da8xx.o obj-$(CONFIG_USB_MUSB_BLACKFIN) += blackfin.o obj-$(CONFIG_USB_MUSB_UX500) += ux500.o + +obj-$(CONFIG_USB_MUSB_AM335X_CHILD) += musb_am335x.o + # the kconfig must guarantee that only one of the # possible I/O schemes will be enabled at a time ... # PIO only, or DMA (several potential schemes). diff --git a/drivers/usb/musb/musb_am335x.c b/drivers/usb/musb/musb_am335x.c new file mode 100644 index 000000000000..41ac5b5b57ce --- /dev/null +++ b/drivers/usb/musb/musb_am335x.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include + +static int am335x_child_probe(struct platform_device *pdev) +{ + int ret; + + pm_runtime_enable(&pdev->dev); + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) + goto err; + + return 0; +err: + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int of_remove_populated_child(struct device *dev, void *d) +{ + struct platform_device *pdev = to_platform_device(dev); + + of_device_unregister(pdev); + return 0; +} + +static int am335x_child_remove(struct platform_device *pdev) +{ + device_for_each_child(&pdev->dev, NULL, of_remove_populated_child); + pm_runtime_disable(&pdev->dev); + return 0; +} + +static const struct of_device_id am335x_child_of_match[] = { + { .compatible = "ti,am33xx-usb" }, + { }, +}; +MODULE_DEVICE_TABLE(of, am335x_child_of_match); + +static struct platform_driver am335x_child_driver = { + .probe = am335x_child_probe, + .remove = am335x_child_remove, + .driver = { + .name = "am335x-usb-childs", + .of_match_table = of_match_ptr(am335x_child_of_match), + }, +}; + +module_platform_driver(am335x_child_driver); +MODULE_DESCRIPTION("AM33xx child devices"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 74333321cb62..4ffbaace7913 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "musb_core.h" @@ -105,10 +106,7 @@ struct dsps_musb_wrapper { /* bit positions for mode */ unsigned iddig:5; /* miscellaneous stuff */ - u32 musb_core_offset; u8 poll_seconds; - /* number of musb instances */ - u8 instances; }; /** @@ -116,10 +114,10 @@ struct dsps_musb_wrapper { */ struct dsps_glue { struct device *dev; - struct platform_device *musb[2]; /* child musb pdev */ + struct platform_device *musb; /* child musb pdev */ const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */ - struct timer_list timer[2]; /* otg_workaround timer */ - unsigned long last_timer[2]; /* last timer data for each instance */ + struct timer_list timer; /* otg_workaround timer */ + unsigned long last_timer; /* last timer data for each instance */ }; /** @@ -168,7 +166,6 @@ static void otg_timer(unsigned long _musb) struct musb *musb = (void *)_musb; void __iomem *mregs = musb->mregs; struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); struct dsps_glue *glue = dev_get_drvdata(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; u8 devctl; @@ -205,7 +202,7 @@ static void otg_timer(unsigned long _musb) case OTG_STATE_B_IDLE: devctl = dsps_readb(mregs, MUSB_DEVCTL); if (devctl & MUSB_DEVCTL_BDEVICE) - mod_timer(&glue->timer[pdev->id], + mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); else musb->xceiv->state = OTG_STATE_A_IDLE; @@ -219,7 +216,6 @@ static void otg_timer(unsigned long _musb) static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) { struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); struct dsps_glue *glue = dev_get_drvdata(dev->parent); if (timeout == 0) @@ -230,23 +226,23 @@ static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { dev_dbg(musb->controller, "%s active, deleting timer\n", usb_otg_state_string(musb->xceiv->state)); - del_timer(&glue->timer[pdev->id]); - glue->last_timer[pdev->id] = jiffies; + del_timer(&glue->timer); + glue->last_timer = jiffies; return; } - if (time_after(glue->last_timer[pdev->id], timeout) && - timer_pending(&glue->timer[pdev->id])) { + if (time_after(glue->last_timer, timeout) && + timer_pending(&glue->timer)) { dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n"); return; } - glue->last_timer[pdev->id] = timeout; + glue->last_timer = timeout; dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", usb_otg_state_string(musb->xceiv->state), jiffies_to_msecs(timeout - jiffies)); - mod_timer(&glue->timer[pdev->id], timeout); + mod_timer(&glue->timer, timeout); } static irqreturn_t dsps_interrupt(int irq, void *hci) @@ -254,7 +250,6 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) struct musb *musb = hci; void __iomem *reg_base = musb->ctrl_base; struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); struct dsps_glue *glue = dev_get_drvdata(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; unsigned long flags; @@ -314,7 +309,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) */ musb->int_usb &= ~MUSB_INTR_VBUSERROR; musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; - mod_timer(&glue->timer[pdev->id], + mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); WARNING("VBUS error workaround (delay coming)\n"); } else if (drvvbus) { @@ -322,7 +317,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) MUSB_HST_MODE(musb); musb->xceiv->otg->default_a = 1; musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; - del_timer(&glue->timer[pdev->id]); + del_timer(&glue->timer); } else { musb->is_active = 0; MUSB_DEV_MODE(musb); @@ -344,8 +339,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) /* Poll for ID change */ if (musb->xceiv->state == OTG_STATE_B_IDLE) - mod_timer(&glue->timer[pdev->id], - jiffies + wrp->poll_seconds * HZ); + mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); out: spin_unlock_irqrestore(&musb->lock, flags); @@ -355,31 +349,34 @@ out: static int dsps_musb_init(struct musb *musb) { struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); struct dsps_glue *glue = dev_get_drvdata(dev->parent); + struct platform_device *parent = to_platform_device(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; - void __iomem *reg_base = musb->ctrl_base; + void __iomem *reg_base; + struct resource *r; u32 rev, val; - int status; - /* mentor core register starts at offset of 0x400 from musb base */ - musb->mregs += wrp->musb_core_offset; + r = platform_get_resource_byname(parent, IORESOURCE_MEM, "control"); + if (!r) + return -EINVAL; + + reg_base = devm_ioremap_resource(dev, r); + if (!musb->ctrl_base) + return -EINVAL; + musb->ctrl_base = reg_base; /* NOP driver needs change if supporting dual instance */ - musb->xceiv = devm_usb_get_phy_by_phandle(glue->dev, "phys", 0); - if (IS_ERR_OR_NULL(musb->xceiv)) - return -EPROBE_DEFER; + musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0); + if (IS_ERR(musb->xceiv)) + return PTR_ERR(musb->xceiv); /* Returns zero if e.g. not clocked */ rev = dsps_readl(reg_base, wrp->revision); - if (!rev) { - status = -ENODEV; - goto err0; - } + if (!rev) + return -ENODEV; usb_phy_init(musb->xceiv); - - setup_timer(&glue->timer[pdev->id], otg_timer, (unsigned long) musb); + setup_timer(&glue->timer, otg_timer, (unsigned long) musb); /* Reset the musb */ dsps_writel(reg_base, wrp->control, (1 << wrp->reset)); @@ -392,17 +389,14 @@ static int dsps_musb_init(struct musb *musb) dsps_writel(musb->ctrl_base, wrp->phy_utmi, val); return 0; -err0: - return status; } static int dsps_musb_exit(struct musb *musb) { struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev); struct dsps_glue *glue = dev_get_drvdata(dev->parent); - del_timer_sync(&glue->timer[pdev->id]); + del_timer_sync(&glue->timer); usb_phy_shutdown(musb->xceiv); return 0; @@ -420,106 +414,98 @@ static struct musb_platform_ops dsps_ops = { static u64 musb_dmamask = DMA_BIT_MASK(32); -static int dsps_create_musb_pdev(struct dsps_glue *glue, u8 id) +static int get_int_prop(struct device_node *dn, const char *s) { - struct device *dev = glue->dev; - struct platform_device *pdev = to_platform_device(dev); - struct musb_hdrc_platform_data *pdata = dev_get_platdata(dev); - struct device_node *np = pdev->dev.of_node; - struct musb_hdrc_config *config; - struct platform_device *musb; - struct resource *res; + int ret; + u32 val; + + ret = of_property_read_u32(dn, s, &val); + if (ret) + return 0; + return val; +} + +static int dsps_create_musb_pdev(struct dsps_glue *glue, + struct platform_device *parent) +{ + struct musb_hdrc_platform_data pdata; struct resource resources[2]; - char res_name[11]; + struct device *dev = &parent->dev; + struct musb_hdrc_config *config; + struct platform_device *musb; + struct device_node *dn = parent->dev.of_node; + struct device_node *child_node; int ret; - /* first resource is for usbss, so start index from 1 */ - res = platform_get_resource(pdev, IORESOURCE_MEM, id + 1); - if (!res) { - dev_err(dev, "failed to get memory for instance %d\n", id); - ret = -ENODEV; - goto err0; + child_node = of_get_child_by_name(dn, "usb"); + if (!child_node) + return -EINVAL; + + memset(resources, 0, sizeof(resources)); + ret = of_address_to_resource(child_node, 0, &resources[0]); + if (ret) { + dev_err(dev, "failed to get memory.\n"); + return ret; } - res->parent = NULL; - resources[0] = *res; - - /* first resource is for usbss, so start index from 1 */ - res = platform_get_resource(pdev, IORESOURCE_IRQ, id + 1); - if (!res) { - dev_err(dev, "failed to get irq for instance %d\n", id); - ret = -ENODEV; - goto err0; + + ret = of_irq_to_resource(child_node, 0, &resources[1]); + if (ret == 0) { + dev_err(dev, "failed to get irq.\n"); + ret = -EINVAL; + return ret; } - res->parent = NULL; - resources[1] = *res; - resources[1].name = "mc"; /* allocate the child platform device */ musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); if (!musb) { dev_err(dev, "failed to allocate musb device\n"); - ret = -ENOMEM; - goto err0; + return -ENOMEM; } musb->dev.parent = dev; musb->dev.dma_mask = &musb_dmamask; musb->dev.coherent_dma_mask = musb_dmamask; + musb->dev.of_node = of_node_get(child_node); - glue->musb[id] = musb; + glue->musb = musb; - ret = platform_device_add_resources(musb, resources, 2); + ret = platform_device_add_resources(musb, resources, + ARRAY_SIZE(resources)); if (ret) { dev_err(dev, "failed to add resources\n"); - goto err2; + goto err; } - if (np) { - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(&pdev->dev, - "failed to allocate musb platform data\n"); - ret = -ENOMEM; - goto err2; - } - - config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); - if (!config) { - dev_err(&pdev->dev, - "failed to allocate musb hdrc config\n"); - ret = -ENOMEM; - goto err2; - } - - of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps); - of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits); - snprintf(res_name, sizeof(res_name), "port%d-mode", id); - of_property_read_u32(np, res_name, (u32 *)&pdata->mode); - of_property_read_u32(np, "power", (u32 *)&pdata->power); - config->multipoint = of_property_read_bool(np, "multipoint"); - - pdata->config = config; + config = devm_kzalloc(&parent->dev, sizeof(*config), GFP_KERNEL); + if (!config) { + dev_err(dev, "failed to allocate musb hdrc config\n"); + ret = -ENOMEM; + goto err; } + pdata.config = config; + pdata.platform_ops = &dsps_ops; - pdata->platform_ops = &dsps_ops; + config->num_eps = get_int_prop(child_node, "num-eps"); + config->ram_bits = get_int_prop(child_node, "ram-bits"); + pdata.mode = get_int_prop(child_node, "port-mode"); + pdata.power = get_int_prop(child_node, "power"); + config->multipoint = of_property_read_bool(child_node, "multipoint"); - ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); + ret = platform_device_add_data(musb, &pdata, sizeof(pdata)); if (ret) { dev_err(dev, "failed to add platform_data\n"); - goto err2; + goto err; } ret = platform_device_add(musb); if (ret) { dev_err(dev, "failed to register musb device\n"); - goto err2; + goto err; } - return 0; -err2: +err: platform_device_put(musb); -err0: return ret; } @@ -528,14 +514,12 @@ static int dsps_probe(struct platform_device *pdev) const struct of_device_id *match; const struct dsps_musb_wrapper *wrp; struct dsps_glue *glue; - struct resource *iomem; - int ret, i; + int ret; match = of_match_node(musb_dsps_of_match, pdev->dev.of_node); if (!match) { dev_err(&pdev->dev, "fail to get matching of_match struct\n"); - ret = -EINVAL; - goto err0; + return -EINVAL; } wrp = match->data; @@ -543,29 +527,13 @@ static int dsps_probe(struct platform_device *pdev) glue = kzalloc(sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&pdev->dev, "unable to allocate glue memory\n"); - ret = -ENOMEM; - goto err0; - } - - /* get memory resource */ - iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iomem) { - dev_err(&pdev->dev, "failed to get usbss mem resourse\n"); - ret = -ENODEV; - goto err1; + return -ENOMEM; } glue->dev = &pdev->dev; + glue->wrp = wrp; - glue->wrp = kmemdup(wrp, sizeof(*wrp), GFP_KERNEL); - if (!glue->wrp) { - dev_err(&pdev->dev, "failed to duplicate wrapper struct memory\n"); - ret = -ENOMEM; - goto err1; - } platform_set_drvdata(pdev, glue); - - /* enable the usbss clocks */ pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); @@ -574,17 +542,9 @@ static int dsps_probe(struct platform_device *pdev) goto err2; } - /* create the child platform device for all instances of musb */ - for (i = 0; i < wrp->instances ; i++) { - ret = dsps_create_musb_pdev(glue, i); - if (ret != 0) { - dev_err(&pdev->dev, "failed to create child pdev\n"); - /* release resources of previously created instances */ - for (i--; i >= 0 ; i--) - platform_device_unregister(glue->musb[i]); - goto err3; - } - } + ret = dsps_create_musb_pdev(glue, pdev); + if (ret) + goto err3; return 0; @@ -592,26 +552,19 @@ err3: pm_runtime_put(&pdev->dev); err2: pm_runtime_disable(&pdev->dev); - kfree(glue->wrp); -err1: kfree(glue); -err0: return ret; } + static int dsps_remove(struct platform_device *pdev) { struct dsps_glue *glue = platform_get_drvdata(pdev); - const struct dsps_musb_wrapper *wrp = glue->wrp; - int i; - /* delete the child platform device */ - for (i = 0; i < wrp->instances ; i++) - platform_device_unregister(glue->musb[i]); + platform_device_unregister(glue->musb); /* disable usbss clocks */ pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); - kfree(glue->wrp); kfree(glue); return 0; } @@ -641,9 +594,7 @@ static const struct dsps_musb_wrapper am33xx_driver_data = { .rxep_shift = 16, .rxep_mask = 0xfffe, .rxep_bitmap = (0xfffe << 16), - .musb_core_offset = 0x400, .poll_seconds = 2, - .instances = 1, }; static const struct of_device_id musb_dsps_of_match[] = { @@ -667,14 +618,4 @@ MODULE_AUTHOR("Ravi B "); MODULE_AUTHOR("Ajay Kumar Gupta "); MODULE_LICENSE("GPL v2"); -static int __init dsps_init(void) -{ - return platform_driver_register(&dsps_usbss_driver); -} -subsys_initcall(dsps_init); - -static void __exit dsps_exit(void) -{ - platform_driver_unregister(&dsps_usbss_driver); -} -module_exit(dsps_exit); +module_platform_driver(dsps_usbss_driver); -- cgit v1.2.3 From 9b3452d1fa3c017d3664ff9e6a601daa6e0576eb Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 20 Jun 2013 12:13:04 +0200 Subject: usb: musb dma: add cppi41 dma driver This driver is currently used by musb' cppi41 couter part. I may merge both dma engine user of musb at some point but not just yet. The driver seems to work in RX/TX mode in host mode, tested on mass storage. I increaed the size of the TX / RX transfers and waited for the core code to cancel a transfers and it seems to recover. v2..3: - use mall transfers on RX side and check data toggle. - use rndis mode on tx side so we haveon interrupt for 4096 transfers. - remove custom "transferred" hack and use dmaengine_tx_status() to compute the total amount of data that has been transferred. - cancel transfers and reclaim descriptors v1..v2: - RX path added - dma mode 0 & 1 is working - device tree nodes re-created. Cc: Vinod Koul Cc: Dan Williams Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- arch/arm/boot/dts/am335x-evm.dts | 4 + arch/arm/boot/dts/am33xx.dtsi | 61 +++ drivers/dma/Kconfig | 8 + drivers/dma/Makefile | 1 + drivers/dma/cppi41.c | 1048 ++++++++++++++++++++++++++++++++++++++ drivers/usb/musb/Kconfig | 4 + drivers/usb/musb/Makefile | 1 + drivers/usb/musb/musb_cppi41.c | 552 ++++++++++++++++++++ drivers/usb/musb/musb_dma.h | 2 +- 9 files changed, 1680 insertions(+), 1 deletion(-) create mode 100644 drivers/dma/cppi41.c create mode 100644 drivers/usb/musb/musb_cppi41.c (limited to 'arch') diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts index b2987e03b19f..c26c16cace3c 100644 --- a/arch/arm/boot/dts/am335x-evm.dts +++ b/arch/arm/boot/dts/am335x-evm.dts @@ -193,6 +193,10 @@ usb@47401800 { status = "okay"; }; + + dma@07402000 { + status = "okay"; + }; }; i2c1: i2c@4802a000 { diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index e1e773cd2556..24d63095ab83 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -383,6 +383,29 @@ port-mode = <3>; power = <250>; phys = <&usb0_phy>; + + dmas = <&cppi41dma 0 0 &cppi41dma 1 0 + &cppi41dma 2 0 &cppi41dma 3 0 + &cppi41dma 4 0 &cppi41dma 5 0 + &cppi41dma 6 0 &cppi41dma 7 0 + &cppi41dma 8 0 &cppi41dma 9 0 + &cppi41dma 10 0 &cppi41dma 11 0 + &cppi41dma 12 0 &cppi41dma 13 0 + &cppi41dma 14 0 &cppi41dma 0 1 + &cppi41dma 1 1 &cppi41dma 2 1 + &cppi41dma 3 1 &cppi41dma 4 1 + &cppi41dma 5 1 &cppi41dma 6 1 + &cppi41dma 7 1 &cppi41dma 8 1 + &cppi41dma 9 1 &cppi41dma 10 1 + &cppi41dma 11 1 &cppi41dma 12 1 + &cppi41dma 13 1 &cppi41dma 14 1>; + dma-names = + "rx1", "rx2", "rx3", "rx4", "rx5", "rx6", "rx7", + "rx8", "rx9", "rx10", "rx11", "rx12", "rx13", + "rx14", "rx15", + "tx1", "tx2", "tx3", "tx4", "tx5", "tx6", "tx7", + "tx8", "tx9", "tx10", "tx11", "tx12", "tx13", + "tx14", "tx15"; }; }; @@ -415,8 +438,46 @@ port-mode = <3>; power = <250>; phys = <&usb1_phy>; + + dmas = <&cppi41dma 15 0 &cppi41dma 16 0 + &cppi41dma 17 0 &cppi41dma 18 0 + &cppi41dma 19 0 &cppi41dma 20 0 + &cppi41dma 21 0 &cppi41dma 22 0 + &cppi41dma 23 0 &cppi41dma 24 0 + &cppi41dma 25 0 &cppi41dma 26 0 + &cppi41dma 27 0 &cppi41dma 28 0 + &cppi41dma 29 0 &cppi41dma 15 1 + &cppi41dma 16 1 &cppi41dma 17 1 + &cppi41dma 18 1 &cppi41dma 19 1 + &cppi41dma 20 1 &cppi41dma 21 1 + &cppi41dma 22 1 &cppi41dma 23 1 + &cppi41dma 24 1 &cppi41dma 25 1 + &cppi41dma 26 1 &cppi41dma 27 1 + &cppi41dma 28 1 &cppi41dma 29 1>; + dma-names = + "rx1", "rx2", "rx3", "rx4", "rx5", "rx6", "rx7", + "rx8", "rx9", "rx10", "rx11", "rx12", "rx13", + "rx14", "rx15", + "tx1", "tx2", "tx3", "tx4", "tx5", "tx6", "tx7", + "tx8", "tx9", "tx10", "tx11", "tx12", "tx13", + "tx14", "tx15"; }; }; + + cppi41dma: dma@07402000 { + compatible = "ti,am3359-cppi41"; + reg = <0x47400000 0x1000 + 0x47402000 0x1000 + 0x47403000 0x1000 + 0x47404000 0x4000>; + reg-names = "glue controller scheduler queuemgr"; + interrupts = <17>; + interrupt-names = "glue"; + #dma-cells = <2>; + #dma-channels = <30>; + #dma-requests = <256>; + status = "disabled"; + }; }; epwmss0: epwmss@48300000 { diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 6825957c97fb..77bc480117b7 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -287,6 +287,14 @@ config DMA_OMAP select DMA_ENGINE select DMA_VIRTUAL_CHANNELS +config TI_CPPI41 + tristate "AM33xx CPPI41 DMA support" + depends on ARCH_OMAP + select DMA_ENGINE + help + The Communications Port Programming Interface (CPPI) 4.1 DMA engine + is currently used by the USB driver on AM335x platforms. + config MMP_PDMA bool "MMP PDMA support" depends on (ARCH_MMP || ARCH_PXA) diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 5e0f2ef85614..6d62ec30c4bc 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -39,3 +39,4 @@ obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o obj-$(CONFIG_DMA_OMAP) += omap-dma.o obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o +obj-$(CONFIG_TI_CPPI41) += cppi41.o diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c new file mode 100644 index 000000000000..5dcebca37760 --- /dev/null +++ b/drivers/dma/cppi41.c @@ -0,0 +1,1048 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dmaengine.h" + +#define DESC_TYPE 27 +#define DESC_TYPE_HOST 0x10 +#define DESC_TYPE_TEARD 0x13 + +#define TD_DESC_IS_RX (1 << 16) +#define TD_DESC_DMA_NUM 10 + +#define DESC_LENGTH_BITS_NUM 21 + +#define DESC_TYPE_USB (5 << 26) +#define DESC_PD_COMPLETE (1 << 31) + +/* DMA engine */ +#define DMA_TDFDQ 4 +#define DMA_TXGCR(x) (0x800 + (x) * 0x20) +#define DMA_RXGCR(x) (0x808 + (x) * 0x20) +#define RXHPCRA0 4 + +#define GCR_CHAN_ENABLE (1 << 31) +#define GCR_TEARDOWN (1 << 30) +#define GCR_STARV_RETRY (1 << 24) +#define GCR_DESC_TYPE_HOST (1 << 14) + +/* DMA scheduler */ +#define DMA_SCHED_CTRL 0 +#define DMA_SCHED_CTRL_EN (1 << 31) +#define DMA_SCHED_WORD(x) ((x) * 4 + 0x800) + +#define SCHED_ENTRY0_CHAN(x) ((x) << 0) +#define SCHED_ENTRY0_IS_RX (1 << 7) + +#define SCHED_ENTRY1_CHAN(x) ((x) << 8) +#define SCHED_ENTRY1_IS_RX (1 << 15) + +#define SCHED_ENTRY2_CHAN(x) ((x) << 16) +#define SCHED_ENTRY2_IS_RX (1 << 23) + +#define SCHED_ENTRY3_CHAN(x) ((x) << 24) +#define SCHED_ENTRY3_IS_RX (1 << 31) + +/* Queue manager */ +/* 4 KiB of memory for descriptors, 2 for each endpoint */ +#define ALLOC_DECS_NUM 128 +#define DESCS_AREAS 1 +#define TOTAL_DESCS_NUM (ALLOC_DECS_NUM * DESCS_AREAS) +#define QMGR_SCRATCH_SIZE (TOTAL_DESCS_NUM * 4) + +#define QMGR_LRAM0_BASE 0x80 +#define QMGR_LRAM_SIZE 0x84 +#define QMGR_LRAM1_BASE 0x88 +#define QMGR_MEMBASE(x) (0x1000 + (x) * 0x10) +#define QMGR_MEMCTRL(x) (0x1004 + (x) * 0x10) +#define QMGR_MEMCTRL_IDX_SH 16 +#define QMGR_MEMCTRL_DESC_SH 8 + +#define QMGR_NUM_PEND 5 +#define QMGR_PEND(x) (0x90 + (x) * 4) + +#define QMGR_PENDING_SLOT_Q(x) (x / 32) +#define QMGR_PENDING_BIT_Q(x) (x % 32) + +#define QMGR_QUEUE_A(n) (0x2000 + (n) * 0x10) +#define QMGR_QUEUE_B(n) (0x2004 + (n) * 0x10) +#define QMGR_QUEUE_C(n) (0x2008 + (n) * 0x10) +#define QMGR_QUEUE_D(n) (0x200c + (n) * 0x10) + +/* Glue layer specific */ +/* USBSS / USB AM335x */ +#define USBSS_IRQ_STATUS 0x28 +#define USBSS_IRQ_ENABLER 0x2c +#define USBSS_IRQ_CLEARR 0x30 + +#define USBSS_IRQ_PD_COMP (1 << 2) + +struct cppi41_channel { + struct dma_chan chan; + struct dma_async_tx_descriptor txd; + struct cppi41_dd *cdd; + struct cppi41_desc *desc; + dma_addr_t desc_phys; + void __iomem *gcr_reg; + int is_tx; + u32 residue; + + unsigned int q_num; + unsigned int q_comp_num; + unsigned int port_num; + + unsigned td_retry; + unsigned td_queued:1; + unsigned td_seen:1; + unsigned td_desc_seen:1; +}; + +struct cppi41_desc { + u32 pd0; + u32 pd1; + u32 pd2; + u32 pd3; + u32 pd4; + u32 pd5; + u32 pd6; + u32 pd7; +} __aligned(32); + +struct chan_queues { + u16 submit; + u16 complete; +}; + +struct cppi41_dd { + struct dma_device ddev; + + void *qmgr_scratch; + dma_addr_t scratch_phys; + + struct cppi41_desc *cd; + dma_addr_t descs_phys; + u32 first_td_desc; + struct cppi41_channel *chan_busy[ALLOC_DECS_NUM]; + + void __iomem *usbss_mem; + void __iomem *ctrl_mem; + void __iomem *sched_mem; + void __iomem *qmgr_mem; + unsigned int irq; + const struct chan_queues *queues_rx; + const struct chan_queues *queues_tx; + struct chan_queues td_queue; +}; + +#define FIST_COMPLETION_QUEUE 93 +static struct chan_queues usb_queues_tx[] = { + /* USB0 ENDP 1 */ + [ 0] = { .submit = 32, .complete = 93}, + [ 1] = { .submit = 34, .complete = 94}, + [ 2] = { .submit = 36, .complete = 95}, + [ 3] = { .submit = 38, .complete = 96}, + [ 4] = { .submit = 40, .complete = 97}, + [ 5] = { .submit = 42, .complete = 98}, + [ 6] = { .submit = 44, .complete = 99}, + [ 7] = { .submit = 46, .complete = 100}, + [ 8] = { .submit = 48, .complete = 101}, + [ 9] = { .submit = 50, .complete = 102}, + [10] = { .submit = 52, .complete = 103}, + [11] = { .submit = 54, .complete = 104}, + [12] = { .submit = 56, .complete = 105}, + [13] = { .submit = 58, .complete = 106}, + [14] = { .submit = 60, .complete = 107}, + + /* USB1 ENDP1 */ + [15] = { .submit = 62, .complete = 125}, + [16] = { .submit = 64, .complete = 126}, + [17] = { .submit = 66, .complete = 127}, + [18] = { .submit = 68, .complete = 128}, + [19] = { .submit = 70, .complete = 129}, + [20] = { .submit = 72, .complete = 130}, + [21] = { .submit = 74, .complete = 131}, + [22] = { .submit = 76, .complete = 132}, + [23] = { .submit = 78, .complete = 133}, + [24] = { .submit = 80, .complete = 134}, + [25] = { .submit = 82, .complete = 135}, + [26] = { .submit = 84, .complete = 136}, + [27] = { .submit = 86, .complete = 137}, + [28] = { .submit = 88, .complete = 138}, + [29] = { .submit = 90, .complete = 139}, +}; + +static const struct chan_queues usb_queues_rx[] = { + /* USB0 ENDP 1 */ + [ 0] = { .submit = 1, .complete = 109}, + [ 1] = { .submit = 2, .complete = 110}, + [ 2] = { .submit = 3, .complete = 111}, + [ 3] = { .submit = 4, .complete = 112}, + [ 4] = { .submit = 5, .complete = 113}, + [ 5] = { .submit = 6, .complete = 114}, + [ 6] = { .submit = 7, .complete = 115}, + [ 7] = { .submit = 8, .complete = 116}, + [ 8] = { .submit = 9, .complete = 117}, + [ 9] = { .submit = 10, .complete = 118}, + [10] = { .submit = 11, .complete = 119}, + [11] = { .submit = 12, .complete = 120}, + [12] = { .submit = 13, .complete = 121}, + [13] = { .submit = 14, .complete = 122}, + [14] = { .submit = 15, .complete = 123}, + + /* USB1 ENDP 1 */ + [15] = { .submit = 16, .complete = 141}, + [16] = { .submit = 17, .complete = 142}, + [17] = { .submit = 18, .complete = 143}, + [18] = { .submit = 19, .complete = 144}, + [19] = { .submit = 20, .complete = 145}, + [20] = { .submit = 21, .complete = 146}, + [21] = { .submit = 22, .complete = 147}, + [22] = { .submit = 23, .complete = 148}, + [23] = { .submit = 24, .complete = 149}, + [24] = { .submit = 25, .complete = 150}, + [25] = { .submit = 26, .complete = 151}, + [26] = { .submit = 27, .complete = 152}, + [27] = { .submit = 28, .complete = 153}, + [28] = { .submit = 29, .complete = 154}, + [29] = { .submit = 30, .complete = 155}, +}; + +struct cppi_glue_infos { + irqreturn_t (*isr)(int irq, void *data); + const struct chan_queues *queues_rx; + const struct chan_queues *queues_tx; + struct chan_queues td_queue; +}; + +static struct cppi41_channel *to_cpp41_chan(struct dma_chan *c) +{ + return container_of(c, struct cppi41_channel, chan); +} + +static struct cppi41_channel *desc_to_chan(struct cppi41_dd *cdd, u32 desc) +{ + struct cppi41_channel *c; + u32 descs_size; + u32 desc_num; + + descs_size = sizeof(struct cppi41_desc) * ALLOC_DECS_NUM; + + if (!((desc >= cdd->descs_phys) && + (desc < (cdd->descs_phys + descs_size)))) { + return NULL; + } + + desc_num = (desc - cdd->descs_phys) / sizeof(struct cppi41_desc); + BUG_ON(desc_num > ALLOC_DECS_NUM); + c = cdd->chan_busy[desc_num]; + cdd->chan_busy[desc_num] = NULL; + return c; +} + +static void cppi_writel(u32 val, void *__iomem *mem) +{ + __raw_writel(val, mem); +} + +static u32 cppi_readl(void *__iomem *mem) +{ + return __raw_readl(mem); +} + +static u32 pd_trans_len(u32 val) +{ + return val & ((1 << (DESC_LENGTH_BITS_NUM + 1)) - 1); +} + +static irqreturn_t cppi41_irq(int irq, void *data) +{ + struct cppi41_dd *cdd = data; + struct cppi41_channel *c; + u32 status; + int i; + + status = cppi_readl(cdd->usbss_mem + USBSS_IRQ_STATUS); + if (!(status & USBSS_IRQ_PD_COMP)) + return IRQ_NONE; + cppi_writel(status, cdd->usbss_mem + USBSS_IRQ_STATUS); + + for (i = QMGR_PENDING_SLOT_Q(FIST_COMPLETION_QUEUE); i < QMGR_NUM_PEND; + i++) { + u32 val; + u32 q_num; + + val = cppi_readl(cdd->qmgr_mem + QMGR_PEND(i)); + if (i == QMGR_PENDING_SLOT_Q(FIST_COMPLETION_QUEUE) && val) { + u32 mask; + /* set corresponding bit for completetion Q 93 */ + mask = 1 << QMGR_PENDING_BIT_Q(FIST_COMPLETION_QUEUE); + /* not set all bits for queues less than Q 93 */ + mask--; + /* now invert and keep only Q 93+ set */ + val &= ~mask; + } + + if (val) + __iormb(); + + while (val) { + u32 desc; + + q_num = __fls(val); + val &= ~(1 << q_num); + q_num += 32 * i; + desc = cppi_readl(cdd->qmgr_mem + QMGR_QUEUE_D(q_num)); + desc &= ~0x1f; + c = desc_to_chan(cdd, desc); + if (WARN_ON(!c)) { + pr_err("%s() q %d desc %08x\n", __func__, + q_num, desc); + continue; + } + c->residue = pd_trans_len(c->desc->pd6) - + pd_trans_len(c->desc->pd0); + + dma_cookie_complete(&c->txd); + c->txd.callback(c->txd.callback_param); + } + } + return IRQ_HANDLED; +} + +static dma_cookie_t cppi41_tx_submit(struct dma_async_tx_descriptor *tx) +{ + dma_cookie_t cookie; + + cookie = dma_cookie_assign(tx); + + return cookie; +} + +static int cppi41_dma_alloc_chan_resources(struct dma_chan *chan) +{ + struct cppi41_channel *c = to_cpp41_chan(chan); + + dma_cookie_init(chan); + dma_async_tx_descriptor_init(&c->txd, chan); + c->txd.tx_submit = cppi41_tx_submit; + + if (!c->is_tx) + cppi_writel(c->q_num, c->gcr_reg + RXHPCRA0); + + return 0; +} + +static void cppi41_dma_free_chan_resources(struct dma_chan *chan) +{ +} + +static enum dma_status cppi41_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + struct cppi41_channel *c = to_cpp41_chan(chan); + enum dma_status ret; + + /* lock */ + ret = dma_cookie_status(chan, cookie, txstate); + if (txstate && ret == DMA_SUCCESS) + txstate->residue = c->residue; + /* unlock */ + + return ret; +} + +static void push_desc_queue(struct cppi41_channel *c) +{ + struct cppi41_dd *cdd = c->cdd; + u32 desc_num; + u32 desc_phys; + u32 reg; + + desc_phys = lower_32_bits(c->desc_phys); + desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc); + WARN_ON(cdd->chan_busy[desc_num]); + cdd->chan_busy[desc_num] = c; + + reg = (sizeof(struct cppi41_desc) - 24) / 4; + reg |= desc_phys; + cppi_writel(reg, cdd->qmgr_mem + QMGR_QUEUE_D(c->q_num)); +} + +static void cppi41_dma_issue_pending(struct dma_chan *chan) +{ + struct cppi41_channel *c = to_cpp41_chan(chan); + u32 reg; + + c->residue = 0; + + reg = GCR_CHAN_ENABLE; + if (!c->is_tx) { + reg |= GCR_STARV_RETRY; + reg |= GCR_DESC_TYPE_HOST; + reg |= c->q_comp_num; + } + + cppi_writel(reg, c->gcr_reg); + + /* + * We don't use writel() but __raw_writel() so we have to make sure + * that the DMA descriptor in coherent memory made to the main memory + * before starting the dma engine. + */ + __iowmb(); + push_desc_queue(c); +} + +static u32 get_host_pd0(u32 length) +{ + u32 reg; + + reg = DESC_TYPE_HOST << DESC_TYPE; + reg |= length; + + return reg; +} + +static u32 get_host_pd1(struct cppi41_channel *c) +{ + u32 reg; + + reg = 0; + + return reg; +} + +static u32 get_host_pd2(struct cppi41_channel *c) +{ + u32 reg; + + reg = DESC_TYPE_USB; + reg |= c->q_comp_num; + + return reg; +} + +static u32 get_host_pd3(u32 length) +{ + u32 reg; + + /* PD3 = packet size */ + reg = length; + + return reg; +} + +static u32 get_host_pd6(u32 length) +{ + u32 reg; + + /* PD6 buffer size */ + reg = DESC_PD_COMPLETE; + reg |= length; + + return reg; +} + +static u32 get_host_pd4_or_7(u32 addr) +{ + u32 reg; + + reg = addr; + + return reg; +} + +static u32 get_host_pd5(void) +{ + u32 reg; + + reg = 0; + + return reg; +} + +static struct dma_async_tx_descriptor *cppi41_dma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, unsigned sg_len, + enum dma_transfer_direction dir, unsigned long tx_flags, void *context) +{ + struct cppi41_channel *c = to_cpp41_chan(chan); + struct cppi41_desc *d; + struct scatterlist *sg; + unsigned int i; + unsigned int num; + + num = 0; + d = c->desc; + for_each_sg(sgl, sg, sg_len, i) { + u32 addr; + u32 len; + + /* We need to use more than one desc once musb supports sg */ + BUG_ON(num > 0); + addr = lower_32_bits(sg_dma_address(sg)); + len = sg_dma_len(sg); + + d->pd0 = get_host_pd0(len); + d->pd1 = get_host_pd1(c); + d->pd2 = get_host_pd2(c); + d->pd3 = get_host_pd3(len); + d->pd4 = get_host_pd4_or_7(addr); + d->pd5 = get_host_pd5(); + d->pd6 = get_host_pd6(len); + d->pd7 = get_host_pd4_or_7(addr); + + d++; + } + + return &c->txd; +} + +static int cpp41_cfg_chan(struct cppi41_channel *c, + struct dma_slave_config *cfg) +{ + return 0; +} + +static void cppi41_compute_td_desc(struct cppi41_desc *d) +{ + d->pd0 = DESC_TYPE_TEARD << DESC_TYPE; +} + +static u32 cppi41_pop_desc(struct cppi41_dd *cdd, unsigned queue_num) +{ + u32 desc; + + desc = cppi_readl(cdd->qmgr_mem + QMGR_QUEUE_D(queue_num)); + desc &= ~0x1f; + return desc; +} + +static int cppi41_tear_down_chan(struct cppi41_channel *c) +{ + struct cppi41_dd *cdd = c->cdd; + struct cppi41_desc *td; + u32 reg; + u32 desc_phys; + u32 td_desc_phys; + + td = cdd->cd; + td += cdd->first_td_desc; + + td_desc_phys = cdd->descs_phys; + td_desc_phys += cdd->first_td_desc * sizeof(struct cppi41_desc); + + if (!c->td_queued) { + cppi41_compute_td_desc(td); + __iowmb(); + + reg = (sizeof(struct cppi41_desc) - 24) / 4; + reg |= td_desc_phys; + cppi_writel(reg, cdd->qmgr_mem + + QMGR_QUEUE_D(cdd->td_queue.submit)); + + reg = GCR_CHAN_ENABLE; + if (!c->is_tx) { + reg |= GCR_STARV_RETRY; + reg |= GCR_DESC_TYPE_HOST; + reg |= c->q_comp_num; + } + reg |= GCR_TEARDOWN; + cppi_writel(reg, c->gcr_reg); + c->td_queued = 1; + c->td_retry = 100; + } + + if (!c->td_seen) { + unsigned td_comp_queue; + + if (c->is_tx) + td_comp_queue = cdd->td_queue.complete; + else + td_comp_queue = c->q_comp_num; + + desc_phys = cppi41_pop_desc(cdd, td_comp_queue); + if (desc_phys) { + __iormb(); + + if (desc_phys == td_desc_phys) { + u32 pd0; + pd0 = td->pd0; + WARN_ON((pd0 >> DESC_TYPE) != DESC_TYPE_TEARD); + WARN_ON(!c->is_tx && !(pd0 & TD_DESC_IS_RX)); + WARN_ON((pd0 & 0x1f) != c->port_num); + } else { + __WARN(); + } + c->td_seen = 1; + } + } + if (!c->td_desc_seen) { + desc_phys = cppi41_pop_desc(cdd, c->q_comp_num); + if (desc_phys) { + __iormb(); + WARN_ON(c->desc_phys != desc_phys); + c->td_desc_seen = 1; + } + } + c->td_retry--; + /* + * If the TX descriptor / channel is in use, the caller needs to poke + * his TD bit multiple times. After that he hardware releases the + * transfer descriptor followed by TD descriptor. Waiting seems not to + * cause any difference. + * RX seems to be thrown out right away. However once the TearDown + * descriptor gets through we are done. If we have seens the transfer + * descriptor before the TD we fetch it from enqueue, it has to be + * there waiting for us. + */ + if (!c->td_seen && c->td_retry) + return -EAGAIN; + + WARN_ON(!c->td_retry); + if (!c->td_desc_seen) { + desc_phys = cppi_readl(cdd->qmgr_mem + QMGR_QUEUE_D(c->q_num)); + WARN_ON(!desc_phys); + } + + c->td_queued = 0; + c->td_seen = 0; + c->td_desc_seen = 0; + cppi_writel(0, c->gcr_reg); + return 0; +} + +static int cppi41_stop_chan(struct dma_chan *chan) +{ + struct cppi41_channel *c = to_cpp41_chan(chan); + struct cppi41_dd *cdd = c->cdd; + u32 desc_num; + u32 desc_phys; + int ret; + + ret = cppi41_tear_down_chan(c); + if (ret) + return ret; + + desc_phys = lower_32_bits(c->desc_phys); + desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc); + WARN_ON(!cdd->chan_busy[desc_num]); + cdd->chan_busy[desc_num] = NULL; + + return 0; +} + +static int cppi41_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct cppi41_channel *c = to_cpp41_chan(chan); + int ret; + + switch (cmd) { + case DMA_SLAVE_CONFIG: + ret = cpp41_cfg_chan(c, (struct dma_slave_config *) arg); + break; + + case DMA_TERMINATE_ALL: + ret = cppi41_stop_chan(chan); + break; + + default: + ret = -ENXIO; + break; + } + return ret; +} + +static void cleanup_chans(struct cppi41_dd *cdd) +{ + while (!list_empty(&cdd->ddev.channels)) { + struct cppi41_channel *cchan; + + cchan = list_first_entry(&cdd->ddev.channels, + struct cppi41_channel, chan.device_node); + list_del(&cchan->chan.device_node); + kfree(cchan); + } +} + +static int cppi41_add_chans(struct platform_device *pdev, struct cppi41_dd *cdd) +{ + struct cppi41_channel *cchan; + int i; + int ret; + u32 n_chans; + + ret = of_property_read_u32(pdev->dev.of_node, "#dma-channels", + &n_chans); + if (ret) + return ret; + /* + * The channels can only be used as TX or as RX. So we add twice + * that much dma channels because USB can only do RX or TX. + */ + n_chans *= 2; + + for (i = 0; i < n_chans; i++) { + cchan = kzalloc(sizeof(*cchan), GFP_KERNEL); + if (!cchan) + goto err; + + cchan->cdd = cdd; + if (i & 1) { + cchan->gcr_reg = cdd->ctrl_mem + DMA_TXGCR(i >> 1); + cchan->is_tx = 1; + } else { + cchan->gcr_reg = cdd->ctrl_mem + DMA_RXGCR(i >> 1); + cchan->is_tx = 0; + } + cchan->port_num = i >> 1; + cchan->desc = &cdd->cd[i]; + cchan->desc_phys = cdd->descs_phys; + cchan->desc_phys += i * sizeof(struct cppi41_desc); + cchan->chan.device = &cdd->ddev; + list_add_tail(&cchan->chan.device_node, &cdd->ddev.channels); + } + cdd->first_td_desc = n_chans; + + return 0; +err: + cleanup_chans(cdd); + return -ENOMEM; +} + +static void purge_descs(struct platform_device *pdev, struct cppi41_dd *cdd) +{ + unsigned int mem_decs; + int i; + + mem_decs = ALLOC_DECS_NUM * sizeof(struct cppi41_desc); + + for (i = 0; i < DESCS_AREAS; i++) { + + cppi_writel(0, cdd->qmgr_mem + QMGR_MEMBASE(i)); + cppi_writel(0, cdd->qmgr_mem + QMGR_MEMCTRL(i)); + + dma_free_coherent(&pdev->dev, mem_decs, cdd->cd, + cdd->descs_phys); + } +} + +static void disable_sched(struct cppi41_dd *cdd) +{ + cppi_writel(0, cdd->sched_mem + DMA_SCHED_CTRL); +} + +static void deinit_cpii41(struct platform_device *pdev, struct cppi41_dd *cdd) +{ + disable_sched(cdd); + + purge_descs(pdev, cdd); + + cppi_writel(0, cdd->qmgr_mem + QMGR_LRAM0_BASE); + cppi_writel(0, cdd->qmgr_mem + QMGR_LRAM0_BASE); + dma_free_coherent(&pdev->dev, QMGR_SCRATCH_SIZE, cdd->qmgr_scratch, + cdd->scratch_phys); +} + +static int init_descs(struct platform_device *pdev, struct cppi41_dd *cdd) +{ + unsigned int desc_size; + unsigned int mem_decs; + int i; + u32 reg; + u32 idx; + + BUILD_BUG_ON(sizeof(struct cppi41_desc) & + (sizeof(struct cppi41_desc) - 1)); + BUILD_BUG_ON(sizeof(struct cppi41_desc) < 32); + BUILD_BUG_ON(ALLOC_DECS_NUM < 32); + + desc_size = sizeof(struct cppi41_desc); + mem_decs = ALLOC_DECS_NUM * desc_size; + + idx = 0; + for (i = 0; i < DESCS_AREAS; i++) { + + reg = idx << QMGR_MEMCTRL_IDX_SH; + reg |= (ilog2(desc_size) - 5) << QMGR_MEMCTRL_DESC_SH; + reg |= ilog2(ALLOC_DECS_NUM) - 5; + + BUILD_BUG_ON(DESCS_AREAS != 1); + cdd->cd = dma_alloc_coherent(&pdev->dev, mem_decs, + &cdd->descs_phys, GFP_KERNEL); + if (!cdd->cd) + return -ENOMEM; + + cppi_writel(cdd->descs_phys, cdd->qmgr_mem + QMGR_MEMBASE(i)); + cppi_writel(reg, cdd->qmgr_mem + QMGR_MEMCTRL(i)); + + idx += ALLOC_DECS_NUM; + } + return 0; +} + +static void init_sched(struct cppi41_dd *cdd) +{ + unsigned ch; + unsigned word; + u32 reg; + + word = 0; + cppi_writel(0, cdd->sched_mem + DMA_SCHED_CTRL); + for (ch = 0; ch < 15 * 2; ch += 2) { + + reg = SCHED_ENTRY0_CHAN(ch); + reg |= SCHED_ENTRY1_CHAN(ch) | SCHED_ENTRY1_IS_RX; + + reg |= SCHED_ENTRY2_CHAN(ch + 1); + reg |= SCHED_ENTRY3_CHAN(ch + 1) | SCHED_ENTRY3_IS_RX; + cppi_writel(reg, cdd->sched_mem + DMA_SCHED_WORD(word)); + word++; + } + reg = 15 * 2 * 2 - 1; + reg |= DMA_SCHED_CTRL_EN; + cppi_writel(reg, cdd->sched_mem + DMA_SCHED_CTRL); +} + +static int init_cppi41(struct platform_device *pdev, struct cppi41_dd *cdd) +{ + int ret; + + BUILD_BUG_ON(QMGR_SCRATCH_SIZE > ((1 << 14) - 1)); + cdd->qmgr_scratch = dma_alloc_coherent(&pdev->dev, QMGR_SCRATCH_SIZE, + &cdd->scratch_phys, GFP_KERNEL); + if (!cdd->qmgr_scratch) + return -ENOMEM; + + cppi_writel(cdd->scratch_phys, cdd->qmgr_mem + QMGR_LRAM0_BASE); + cppi_writel(QMGR_SCRATCH_SIZE, cdd->qmgr_mem + QMGR_LRAM_SIZE); + cppi_writel(0, cdd->qmgr_mem + QMGR_LRAM1_BASE); + + ret = init_descs(pdev, cdd); + if (ret) + goto err_td; + + cppi_writel(cdd->td_queue.submit, cdd->ctrl_mem + DMA_TDFDQ); + init_sched(cdd); + return 0; +err_td: + deinit_cpii41(pdev, cdd); + return ret; +} + +static struct platform_driver cpp41_dma_driver; +/* + * The param format is: + * X Y + * X: Port + * Y: 0 = RX else TX + */ +#define INFO_PORT 0 +#define INFO_IS_TX 1 + +static bool cpp41_dma_filter_fn(struct dma_chan *chan, void *param) +{ + struct cppi41_channel *cchan; + struct cppi41_dd *cdd; + const struct chan_queues *queues; + u32 *num = param; + + if (chan->device->dev->driver != &cpp41_dma_driver.driver) + return false; + + cchan = to_cpp41_chan(chan); + + if (cchan->port_num != num[INFO_PORT]) + return false; + + if (cchan->is_tx && !num[INFO_IS_TX]) + return false; + cdd = cchan->cdd; + if (cchan->is_tx) + queues = cdd->queues_tx; + else + queues = cdd->queues_rx; + + BUILD_BUG_ON(ARRAY_SIZE(usb_queues_rx) != ARRAY_SIZE(usb_queues_tx)); + if (WARN_ON(cchan->port_num > ARRAY_SIZE(usb_queues_rx))) + return false; + + cchan->q_num = queues[cchan->port_num].submit; + cchan->q_comp_num = queues[cchan->port_num].complete; + return true; +} + +static struct of_dma_filter_info cpp41_dma_info = { + .filter_fn = cpp41_dma_filter_fn, +}; + +static struct dma_chan *cppi41_dma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + int count = dma_spec->args_count; + struct of_dma_filter_info *info = ofdma->of_dma_data; + + if (!info || !info->filter_fn) + return NULL; + + if (count != 2) + return NULL; + + return dma_request_channel(info->dma_cap, info->filter_fn, + &dma_spec->args[0]); +} + +static const struct cppi_glue_infos usb_infos = { + .isr = cppi41_irq, + .queues_rx = usb_queues_rx, + .queues_tx = usb_queues_tx, + .td_queue = { .submit = 31, .complete = 0 }, +}; + +static const struct of_device_id cppi41_dma_ids[] = { + { .compatible = "ti,am3359-cppi41", .data = &usb_infos}, + {}, +}; +MODULE_DEVICE_TABLE(of, cppi41_dma_ids); + +static const struct cppi_glue_infos *get_glue_info(struct platform_device *pdev) +{ + const struct of_device_id *of_id; + + of_id = of_match_node(cppi41_dma_ids, pdev->dev.of_node); + if (!of_id) + return NULL; + return of_id->data; +} + +static int cppi41_dma_probe(struct platform_device *pdev) +{ + struct cppi41_dd *cdd; + const struct cppi_glue_infos *glue_info; + int irq; + int ret; + + glue_info = get_glue_info(pdev); + if (!glue_info) + return -EINVAL; + + cdd = kzalloc(sizeof(*cdd), GFP_KERNEL); + if (!cdd) + return -ENOMEM; + + dma_cap_set(DMA_SLAVE, cdd->ddev.cap_mask); + cdd->ddev.device_alloc_chan_resources = cppi41_dma_alloc_chan_resources; + cdd->ddev.device_free_chan_resources = cppi41_dma_free_chan_resources; + cdd->ddev.device_tx_status = cppi41_dma_tx_status; + cdd->ddev.device_issue_pending = cppi41_dma_issue_pending; + cdd->ddev.device_prep_slave_sg = cppi41_dma_prep_slave_sg; + cdd->ddev.device_control = cppi41_dma_control; + cdd->ddev.dev = &pdev->dev; + INIT_LIST_HEAD(&cdd->ddev.channels); + cpp41_dma_info.dma_cap = cdd->ddev.cap_mask; + + cdd->usbss_mem = of_iomap(pdev->dev.of_node, 0); + cdd->ctrl_mem = of_iomap(pdev->dev.of_node, 1); + cdd->sched_mem = of_iomap(pdev->dev.of_node, 2); + cdd->qmgr_mem = of_iomap(pdev->dev.of_node, 3); + + if (!cdd->usbss_mem || !cdd->ctrl_mem || !cdd->sched_mem || + !cdd->qmgr_mem) { + ret = -ENXIO; + goto err_remap; + } + + cdd->queues_rx = glue_info->queues_rx; + cdd->queues_tx = glue_info->queues_tx; + cdd->td_queue = glue_info->td_queue; + + ret = init_cppi41(pdev, cdd); + if (ret) + goto err_init_cppi; + + ret = cppi41_add_chans(pdev, cdd); + if (ret) + goto err_chans; + + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (!irq) + goto err_irq; + + cppi_writel(USBSS_IRQ_PD_COMP, cdd->usbss_mem + USBSS_IRQ_ENABLER); + + ret = request_irq(irq, glue_info->isr, IRQF_SHARED, + dev_name(&pdev->dev), cdd); + if (ret) + goto err_irq; + cdd->irq = irq; + + ret = dma_async_device_register(&cdd->ddev); + if (ret) + goto err_dma_reg; + + ret = of_dma_controller_register(pdev->dev.of_node, + cppi41_dma_xlate, &cpp41_dma_info); + if (ret) + goto err_of; + + platform_set_drvdata(pdev, cdd); + return 0; +err_of: + dma_async_device_unregister(&cdd->ddev); +err_dma_reg: + free_irq(irq, cdd); +err_irq: + cppi_writel(0, cdd->usbss_mem + USBSS_IRQ_CLEARR); + cleanup_chans(cdd); +err_chans: + deinit_cpii41(pdev, cdd); +err_init_cppi: + iounmap(cdd->usbss_mem); + iounmap(cdd->ctrl_mem); + iounmap(cdd->sched_mem); + iounmap(cdd->qmgr_mem); +err_remap: + kfree(cdd); + return ret; +} + +static int cppi41_dma_remove(struct platform_device *pdev) +{ + struct cppi41_dd *cdd = platform_get_drvdata(pdev); + + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&cdd->ddev); + + cppi_writel(0, cdd->usbss_mem + USBSS_IRQ_CLEARR); + free_irq(cdd->irq, cdd); + cleanup_chans(cdd); + deinit_cpii41(pdev, cdd); + iounmap(cdd->usbss_mem); + iounmap(cdd->ctrl_mem); + iounmap(cdd->sched_mem); + iounmap(cdd->qmgr_mem); + kfree(cdd); + return 0; +} + +static struct platform_driver cpp41_dma_driver = { + .probe = cppi41_dma_probe, + .remove = cppi41_dma_remove, + .driver = { + .name = "cppi41-dma-engine", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(cppi41_dma_ids), + }, +}; + +module_platform_driver(cpp41_dma_driver); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sebastian Andrzej Siewior "); diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index b7257ae038fd..04658d7666e9 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -129,6 +129,10 @@ config USB_TI_CPPI_DMA help Enable DMA transfers when TI CPPI DMA is available. +config USB_TI_CPPI41_DMA + bool 'TI CPPI 4.1 (AM335x)' + depends on ARCH_OMAP + config USB_TUSB_OMAP_DMA bool 'TUSB 6010' depends on USB_MUSB_TUSB6010 diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 52f552c1ba2b..c5ea5c6dc169 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -32,3 +32,4 @@ musb_hdrc-$(CONFIG_USB_INVENTRA_DMA) += musbhsdma.o musb_hdrc-$(CONFIG_USB_TI_CPPI_DMA) += cppi_dma.o musb_hdrc-$(CONFIG_USB_TUSB_OMAP_DMA) += tusb6010_omap.o musb_hdrc-$(CONFIG_USB_UX500_DMA) += ux500_dma.o +musb_hdrc-$(CONFIG_USB_TI_CPPI41_DMA) += musb_cppi41.o diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c new file mode 100644 index 000000000000..a74259ea0306 --- /dev/null +++ b/drivers/usb/musb/musb_cppi41.c @@ -0,0 +1,552 @@ +#include +#include +#include +#include +#include +#include + +#include "musb_core.h" + +#define RNDIS_REG(x) (0x80 + ((x - 1) * 4)) + +#define EP_MODE_AUTOREG_NONE 0 +#define EP_MODE_AUTOREG_ALL_NEOP 1 +#define EP_MODE_AUTOREG_ALWAYS 3 + +#define EP_MODE_DMA_TRANSPARENT 0 +#define EP_MODE_DMA_RNDIS 1 +#define EP_MODE_DMA_GEN_RNDIS 3 + +#define USB_CTRL_TX_MODE 0x70 +#define USB_CTRL_RX_MODE 0x74 +#define USB_CTRL_AUTOREQ 0xd0 +#define USB_TDOWN 0xd8 + +struct cppi41_dma_channel { + struct dma_channel channel; + struct cppi41_dma_controller *controller; + struct musb_hw_ep *hw_ep; + struct dma_chan *dc; + dma_cookie_t cookie; + u8 port_num; + u8 is_tx; + u8 is_allocated; + u8 usb_toggle; + + dma_addr_t buf_addr; + u32 total_len; + u32 prog_len; + u32 transferred; + u32 packet_sz; +}; + +#define MUSB_DMA_NUM_CHANNELS 15 + +struct cppi41_dma_controller { + struct dma_controller controller; + struct cppi41_dma_channel rx_channel[MUSB_DMA_NUM_CHANNELS]; + struct cppi41_dma_channel tx_channel[MUSB_DMA_NUM_CHANNELS]; + struct musb *musb; + u32 rx_mode; + u32 tx_mode; + u32 auto_req; +}; + +static void save_rx_toggle(struct cppi41_dma_channel *cppi41_channel) +{ + u16 csr; + u8 toggle; + + if (cppi41_channel->is_tx) + return; + if (!is_host_active(cppi41_channel->controller->musb)) + return; + + csr = musb_readw(cppi41_channel->hw_ep->regs, MUSB_RXCSR); + toggle = csr & MUSB_RXCSR_H_DATATOGGLE ? 1 : 0; + + cppi41_channel->usb_toggle = toggle; +} + +static void update_rx_toggle(struct cppi41_dma_channel *cppi41_channel) +{ + u16 csr; + u8 toggle; + + if (cppi41_channel->is_tx) + return; + if (!is_host_active(cppi41_channel->controller->musb)) + return; + + csr = musb_readw(cppi41_channel->hw_ep->regs, MUSB_RXCSR); + toggle = csr & MUSB_RXCSR_H_DATATOGGLE ? 1 : 0; + + /* + * AM335x Advisory 1.0.13: Due to internal synchronisation error the + * data toggle may reset from DATA1 to DATA0 during receiving data from + * more than one endpoint. + */ + if (!toggle && toggle == cppi41_channel->usb_toggle) { + csr |= MUSB_RXCSR_H_DATATOGGLE | MUSB_RXCSR_H_WR_DATATOGGLE; + musb_writew(cppi41_channel->hw_ep->regs, MUSB_RXCSR, csr); + dev_dbg(cppi41_channel->controller->musb->controller, + "Restoring DATA1 toggle.\n"); + } + + cppi41_channel->usb_toggle = toggle; +} + +static void cppi41_dma_callback(void *private_data) +{ + struct dma_channel *channel = private_data; + struct cppi41_dma_channel *cppi41_channel = channel->private_data; + struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; + struct musb *musb = hw_ep->musb; + unsigned long flags; + struct dma_tx_state txstate; + u32 transferred; + + spin_lock_irqsave(&musb->lock, flags); + + dmaengine_tx_status(cppi41_channel->dc, cppi41_channel->cookie, + &txstate); + transferred = cppi41_channel->prog_len - txstate.residue; + cppi41_channel->transferred += transferred; + + dev_dbg(musb->controller, "DMA transfer done on hw_ep=%d bytes=%d/%d\n", + hw_ep->epnum, cppi41_channel->transferred, + cppi41_channel->total_len); + + update_rx_toggle(cppi41_channel); + + if (cppi41_channel->transferred == cppi41_channel->total_len || + transferred < cppi41_channel->packet_sz) { + + /* done, complete */ + cppi41_channel->channel.actual_len = + cppi41_channel->transferred; + cppi41_channel->channel.status = MUSB_DMA_STATUS_FREE; + musb_dma_completion(musb, hw_ep->epnum, cppi41_channel->is_tx); + } else { + /* next iteration, reload */ + struct dma_chan *dc = cppi41_channel->dc; + struct dma_async_tx_descriptor *dma_desc; + enum dma_transfer_direction direction; + u16 csr; + u32 remain_bytes; + void __iomem *epio = cppi41_channel->hw_ep->regs; + + cppi41_channel->buf_addr += cppi41_channel->packet_sz; + + remain_bytes = cppi41_channel->total_len; + remain_bytes -= cppi41_channel->transferred; + remain_bytes = min(remain_bytes, cppi41_channel->packet_sz); + cppi41_channel->prog_len = remain_bytes; + + direction = cppi41_channel->is_tx ? DMA_MEM_TO_DEV + : DMA_DEV_TO_MEM; + dma_desc = dmaengine_prep_slave_single(dc, + cppi41_channel->buf_addr, + remain_bytes, + direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (WARN_ON(!dma_desc)) + return; + + dma_desc->callback = cppi41_dma_callback; + dma_desc->callback_param = channel; + cppi41_channel->cookie = dma_desc->tx_submit(dma_desc); + dma_async_issue_pending(dc); + + if (!cppi41_channel->is_tx) { + csr = musb_readw(epio, MUSB_RXCSR); + csr |= MUSB_RXCSR_H_REQPKT; + musb_writew(epio, MUSB_RXCSR, csr); + } + } + spin_unlock_irqrestore(&musb->lock, flags); +} + +static u32 update_ep_mode(unsigned ep, unsigned mode, u32 old) +{ + unsigned shift; + + shift = (ep - 1) * 2; + old &= ~(3 << shift); + old |= mode << shift; + return old; +} + +static void cppi41_set_dma_mode(struct cppi41_dma_channel *cppi41_channel, + unsigned mode) +{ + struct cppi41_dma_controller *controller = cppi41_channel->controller; + u32 port; + u32 new_mode; + u32 old_mode; + + if (cppi41_channel->is_tx) + old_mode = controller->tx_mode; + else + old_mode = controller->rx_mode; + port = cppi41_channel->port_num; + new_mode = update_ep_mode(port, mode, old_mode); + + if (new_mode == old_mode) + return; + if (cppi41_channel->is_tx) { + controller->tx_mode = new_mode; + musb_writel(controller->musb->ctrl_base, USB_CTRL_TX_MODE, + new_mode); + } else { + controller->rx_mode = new_mode; + musb_writel(controller->musb->ctrl_base, USB_CTRL_RX_MODE, + new_mode); + } +} + +static void cppi41_set_autoreq_mode(struct cppi41_dma_channel *cppi41_channel, + unsigned mode) +{ + struct cppi41_dma_controller *controller = cppi41_channel->controller; + u32 port; + u32 new_mode; + u32 old_mode; + + old_mode = controller->auto_req; + port = cppi41_channel->port_num; + new_mode = update_ep_mode(port, mode, old_mode); + + if (new_mode == old_mode) + return; + controller->auto_req = new_mode; + musb_writel(controller->musb->ctrl_base, USB_CTRL_AUTOREQ, new_mode); +} + +static bool cppi41_configure_channel(struct dma_channel *channel, + u16 packet_sz, u8 mode, + dma_addr_t dma_addr, u32 len) +{ + struct cppi41_dma_channel *cppi41_channel = channel->private_data; + struct dma_chan *dc = cppi41_channel->dc; + struct dma_async_tx_descriptor *dma_desc; + enum dma_transfer_direction direction; + struct musb *musb = cppi41_channel->controller->musb; + unsigned use_gen_rndis = 0; + + dev_dbg(musb->controller, + "configure ep%d/%x packet_sz=%d, mode=%d, dma_addr=0x%llx, len=%d is_tx=%d\n", + cppi41_channel->port_num, RNDIS_REG(cppi41_channel->port_num), + packet_sz, mode, (unsigned long long) dma_addr, + len, cppi41_channel->is_tx); + + cppi41_channel->buf_addr = dma_addr; + cppi41_channel->total_len = len; + cppi41_channel->transferred = 0; + cppi41_channel->packet_sz = packet_sz; + + /* + * Due to AM335x' Advisory 1.0.13 we are not allowed to transfer more + * than max packet size at a time. + */ + if (cppi41_channel->is_tx) + use_gen_rndis = 1; + + if (use_gen_rndis) { + /* RNDIS mode */ + if (len > packet_sz) { + musb_writel(musb->ctrl_base, + RNDIS_REG(cppi41_channel->port_num), len); + /* gen rndis */ + cppi41_set_dma_mode(cppi41_channel, + EP_MODE_DMA_GEN_RNDIS); + + /* auto req */ + cppi41_set_autoreq_mode(cppi41_channel, + EP_MODE_AUTOREG_ALL_NEOP); + } else { + musb_writel(musb->ctrl_base, + RNDIS_REG(cppi41_channel->port_num), 0); + cppi41_set_dma_mode(cppi41_channel, + EP_MODE_DMA_TRANSPARENT); + cppi41_set_autoreq_mode(cppi41_channel, + EP_MODE_AUTOREG_NONE); + } + } else { + /* fallback mode */ + cppi41_set_dma_mode(cppi41_channel, EP_MODE_DMA_TRANSPARENT); + cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREG_NONE); + len = min_t(u32, packet_sz, len); + } + cppi41_channel->prog_len = len; + direction = cppi41_channel->is_tx ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + dma_desc = dmaengine_prep_slave_single(dc, dma_addr, len, direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!dma_desc) + return false; + + dma_desc->callback = cppi41_dma_callback; + dma_desc->callback_param = channel; + cppi41_channel->cookie = dma_desc->tx_submit(dma_desc); + + save_rx_toggle(cppi41_channel); + dma_async_issue_pending(dc); + return true; +} + +static struct dma_channel *cppi41_dma_channel_allocate(struct dma_controller *c, + struct musb_hw_ep *hw_ep, u8 is_tx) +{ + struct cppi41_dma_controller *controller = container_of(c, + struct cppi41_dma_controller, controller); + struct cppi41_dma_channel *cppi41_channel = NULL; + u8 ch_num = hw_ep->epnum - 1; + + if (ch_num >= MUSB_DMA_NUM_CHANNELS) + return NULL; + + if (is_tx) + cppi41_channel = &controller->tx_channel[ch_num]; + else + cppi41_channel = &controller->rx_channel[ch_num]; + + if (!cppi41_channel->dc) + return NULL; + + if (cppi41_channel->is_allocated) + return NULL; + + cppi41_channel->hw_ep = hw_ep; + cppi41_channel->is_allocated = 1; + + return &cppi41_channel->channel; +} + +static void cppi41_dma_channel_release(struct dma_channel *channel) +{ + struct cppi41_dma_channel *cppi41_channel = channel->private_data; + + if (cppi41_channel->is_allocated) { + cppi41_channel->is_allocated = 0; + channel->status = MUSB_DMA_STATUS_FREE; + channel->actual_len = 0; + } +} + +static int cppi41_dma_channel_program(struct dma_channel *channel, + u16 packet_sz, u8 mode, + dma_addr_t dma_addr, u32 len) +{ + int ret; + + BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN || + channel->status == MUSB_DMA_STATUS_BUSY); + + channel->status = MUSB_DMA_STATUS_BUSY; + channel->actual_len = 0; + ret = cppi41_configure_channel(channel, packet_sz, mode, dma_addr, len); + if (!ret) + channel->status = MUSB_DMA_STATUS_FREE; + + return ret; +} + +static int cppi41_is_compatible(struct dma_channel *channel, u16 maxpacket, + void *buf, u32 length) +{ + struct cppi41_dma_channel *cppi41_channel = channel->private_data; + struct cppi41_dma_controller *controller = cppi41_channel->controller; + struct musb *musb = controller->musb; + + if (is_host_active(musb)) { + WARN_ON(1); + return 1; + } + return 0; +} + +static int cppi41_dma_channel_abort(struct dma_channel *channel) +{ + struct cppi41_dma_channel *cppi41_channel = channel->private_data; + struct cppi41_dma_controller *controller = cppi41_channel->controller; + struct musb *musb = controller->musb; + void __iomem *epio = cppi41_channel->hw_ep->regs; + int tdbit; + int ret; + unsigned is_tx; + u16 csr; + + is_tx = cppi41_channel->is_tx; + dev_dbg(musb->controller, "abort channel=%d, is_tx=%d\n", + cppi41_channel->port_num, is_tx); + + if (cppi41_channel->channel.status == MUSB_DMA_STATUS_FREE) + return 0; + + if (is_tx) { + csr = musb_readw(epio, MUSB_TXCSR); + csr &= ~MUSB_TXCSR_DMAENAB; + musb_writew(epio, MUSB_TXCSR, csr); + } else { + csr = musb_readw(epio, MUSB_RXCSR); + csr &= ~(MUSB_RXCSR_H_REQPKT | MUSB_RXCSR_DMAENAB); + musb_writew(epio, MUSB_RXCSR, csr); + + csr = musb_readw(epio, MUSB_RXCSR); + if (csr & MUSB_RXCSR_RXPKTRDY) { + csr |= MUSB_RXCSR_FLUSHFIFO; + musb_writew(epio, MUSB_RXCSR, csr); + musb_writew(epio, MUSB_RXCSR, csr); + } + } + + tdbit = 1 << cppi41_channel->port_num; + if (is_tx) + tdbit <<= 16; + + do { + musb_writel(musb->ctrl_base, USB_TDOWN, tdbit); + ret = dmaengine_terminate_all(cppi41_channel->dc); + } while (ret == -EAGAIN); + + musb_writel(musb->ctrl_base, USB_TDOWN, tdbit); + + if (is_tx) { + csr = musb_readw(epio, MUSB_TXCSR); + if (csr & MUSB_TXCSR_TXPKTRDY) { + csr |= MUSB_TXCSR_FLUSHFIFO; + musb_writew(epio, MUSB_TXCSR, csr); + } + } + + cppi41_channel->channel.status = MUSB_DMA_STATUS_FREE; + return 0; +} + +static void cppi41_release_all_dma_chans(struct cppi41_dma_controller *ctrl) +{ + struct dma_chan *dc; + int i; + + for (i = 0; i < MUSB_DMA_NUM_CHANNELS; i++) { + dc = ctrl->tx_channel[i].dc; + if (dc) + dma_release_channel(dc); + dc = ctrl->rx_channel[i].dc; + if (dc) + dma_release_channel(dc); + } +} + +static void cppi41_dma_controller_stop(struct cppi41_dma_controller *controller) +{ + cppi41_release_all_dma_chans(controller); +} + +static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) +{ + struct musb *musb = controller->musb; + struct device *dev = musb->controller; + struct device_node *np = dev->of_node; + struct cppi41_dma_channel *cppi41_channel; + int count; + int i; + int ret; + + count = of_property_count_strings(np, "dma-names"); + if (count < 0) + return count; + + for (i = 0; i < count; i++) { + struct dma_chan *dc; + struct dma_channel *musb_dma; + const char *str; + unsigned is_tx; + unsigned int port; + + ret = of_property_read_string_index(np, "dma-names", i, &str); + if (ret) + goto err; + if (!strncmp(str, "tx", 2)) + is_tx = 1; + else if (!strncmp(str, "rx", 2)) + is_tx = 0; + else { + dev_err(dev, "Wrong dmatype %s\n", str); + goto err; + } + ret = kstrtouint(str + 2, 0, &port); + if (ret) + goto err; + + if (port > MUSB_DMA_NUM_CHANNELS || !port) + goto err; + if (is_tx) + cppi41_channel = &controller->tx_channel[port - 1]; + else + cppi41_channel = &controller->rx_channel[port - 1]; + + cppi41_channel->controller = controller; + cppi41_channel->port_num = port; + cppi41_channel->is_tx = is_tx; + + musb_dma = &cppi41_channel->channel; + musb_dma->private_data = cppi41_channel; + musb_dma->status = MUSB_DMA_STATUS_FREE; + musb_dma->max_len = SZ_4M; + + dc = dma_request_slave_channel(dev, str); + if (!dc) { + dev_err(dev, "Falied to request %s.\n", str); + goto err; + } + cppi41_channel->dc = dc; + } + return 0; +err: + cppi41_release_all_dma_chans(controller); + return -EINVAL; +} + +void dma_controller_destroy(struct dma_controller *c) +{ + struct cppi41_dma_controller *controller = container_of(c, + struct cppi41_dma_controller, controller); + + cppi41_dma_controller_stop(controller); + kfree(controller); +} + +struct dma_controller *dma_controller_create(struct musb *musb, + void __iomem *base) +{ + struct cppi41_dma_controller *controller; + int ret; + + if (!musb->controller->of_node) { + dev_err(musb->controller, "Need DT for the DMA engine.\n"); + return NULL; + } + + controller = kzalloc(sizeof(*controller), GFP_KERNEL); + if (!controller) + goto kzalloc_fail; + + controller->musb = musb; + + controller->controller.channel_alloc = cppi41_dma_channel_allocate; + controller->controller.channel_release = cppi41_dma_channel_release; + controller->controller.channel_program = cppi41_dma_channel_program; + controller->controller.channel_abort = cppi41_dma_channel_abort; + controller->controller.is_compatible = cppi41_is_compatible; + + ret = cppi41_dma_controller_start(controller); + if (ret) + goto plat_get_fail; + return &controller->controller; + +plat_get_fail: + kfree(controller); +kzalloc_fail: + return NULL; +} diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index c8e67fde2156..1345a4ff041a 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -68,7 +68,7 @@ struct musb_hw_ep; #define is_dma_capable() (1) #endif -#ifdef CONFIG_USB_TI_CPPI_DMA +#if defined(CONFIG_USB_TI_CPPI_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA) #define is_cppi_enabled() 1 #else #define is_cppi_enabled() 0 -- cgit v1.2.3 From 3b6394b4faccb7dceaaf76fc4e5e40937b61102e Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 20 Aug 2013 18:35:45 +0200 Subject: usb: musb: dsps fix the typo in reg-names of the dma node I forgot to separete the different names in the reg-names property. This didn't cause anything to fail because the driver does not use the names and simply relies on the order of the memory offsets in reg. This patch fixes this in case it is used later. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- arch/arm/boot/dts/am33xx.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 24d63095ab83..a38f8d362a86 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -470,7 +470,7 @@ 0x47402000 0x1000 0x47403000 0x1000 0x47404000 0x4000>; - reg-names = "glue controller scheduler queuemgr"; + reg-names = "glue", "controller", "scheduler", "queuemgr"; interrupts = <17>; interrupt-names = "glue"; #dma-cells = <2>; -- cgit v1.2.3 From c031a7d419340f30e66dc18fab4373ad646ca7e6 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 20 Aug 2013 18:35:47 +0200 Subject: usb: usb: dsps: update code according to the binding document This relfects the code and dts requires changes due to recent .dts binding updates: - use mg prefix for the Metor Graphics specific attributes - use power in mA not in mA/2 as specifed in the USB2.0 specification - remove the child node for USB. This is driver specific on won't be reflected in the device tree - use the "mentor" prefix instead of "mg". - use "dr_mode" istead of "mg,port-mode" for the port mode. The former is used by a few other drivers. Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Stephen Warren Cc: Ian Campbell Cc: devicetree@vger.kernel.org Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- arch/arm/boot/dts/am335x-bone.dts | 2 +- arch/arm/boot/dts/am335x-evm.dts | 6 ++-- arch/arm/boot/dts/am335x-evmsk.dts | 2 +- arch/arm/boot/dts/am33xx.dtsi | 67 +++++++++++++++----------------------- drivers/usb/musb/musb_dsps.c | 54 ++++++++++++++++++++---------- 5 files changed, 67 insertions(+), 64 deletions(-) (limited to 'arch') diff --git a/arch/arm/boot/dts/am335x-bone.dts b/arch/arm/boot/dts/am335x-bone.dts index a8907b57c75c..e8447a79dc7e 100644 --- a/arch/arm/boot/dts/am335x-bone.dts +++ b/arch/arm/boot/dts/am335x-bone.dts @@ -127,7 +127,7 @@ status = "okay"; }; - phy@47401300 { + usb-phy@47401300 { status = "okay"; }; diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts index c26c16cace3c..648a67e74d1b 100644 --- a/arch/arm/boot/dts/am335x-evm.dts +++ b/arch/arm/boot/dts/am335x-evm.dts @@ -178,11 +178,11 @@ status = "okay"; }; - phy@47401300 { + usb-phy@47401300 { status = "okay"; }; - phy@47401b00 { + usb-phy@47401b00 { status = "okay"; }; @@ -194,7 +194,7 @@ status = "okay"; }; - dma@07402000 { + dma-controller@07402000 { status = "okay"; }; }; diff --git a/arch/arm/boot/dts/am335x-evmsk.dts b/arch/arm/boot/dts/am335x-evmsk.dts index e92446c6846e..a6c5033be442 100644 --- a/arch/arm/boot/dts/am335x-evmsk.dts +++ b/arch/arm/boot/dts/am335x-evmsk.dts @@ -214,7 +214,7 @@ status = "okay"; }; - phy@47401300 { + usb-phy@47401300 { status = "okay"; }; diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index a38f8d362a86..f9c5da9c7fe1 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -354,7 +354,7 @@ status = "disabled"; }; - usb0_phy: phy@47401300 { + usb0_phy: usb-phy@47401300 { compatible = "ti,am335x-usb-phy"; reg = <0x47401300 0x100>; reg-names = "phy"; @@ -364,25 +364,19 @@ usb0: usb@47401000 { compatible = "ti,musb-am33xx"; - ranges; - #address-cells = <1>; - #size-cells = <1>; - reg = <0x47401000 0x200>; - reg-names = "control"; status = "disabled"; - - musb0: usb@47401400 { - compatible = "mg,musbmhdrc"; - reg = <0x47401400 0x400>; - reg-names = "mc"; - interrupts = <18>; - interrupt-names = "mc"; - multipoint = <1>; - num-eps = <16>; - ram-bits = <12>; - port-mode = <3>; - power = <250>; - phys = <&usb0_phy>; + reg = <0x47401400 0x400 + 0x47401000 0x200>; + reg-names = "mc", "control"; + + interrupts = <18>; + interrupt-names = "mc"; + dr_mode = "otg"; + mentor,multipoint = <1>; + mentor,num-eps = <16>; + mentor,ram-bits = <12>; + mentor,power = <500>; + phys = <&usb0_phy>; dmas = <&cppi41dma 0 0 &cppi41dma 1 0 &cppi41dma 2 0 &cppi41dma 3 0 @@ -406,10 +400,9 @@ "tx1", "tx2", "tx3", "tx4", "tx5", "tx6", "tx7", "tx8", "tx9", "tx10", "tx11", "tx12", "tx13", "tx14", "tx15"; - }; }; - usb1_phy: phy@47401b00 { + usb1_phy: usb-phy@47401b00 { compatible = "ti,am335x-usb-phy"; reg = <0x47401b00 0x100>; reg-names = "phy"; @@ -419,25 +412,18 @@ usb1: usb@47401800 { compatible = "ti,musb-am33xx"; - ranges; - #address-cells = <1>; - #size-cells = <1>; - reg = <0x47401800 0x200>; - reg-names = "control"; status = "disabled"; - - musb1: usb@47401c00 { - compatible = "mg,musbmhdrc"; - reg = <0x47401c00 0x400>; - reg-names = "mc"; - interrupts = <19>; - interrupt-names = "mc"; - multipoint = <1>; - num-eps = <16>; - ram-bits = <12>; - port-mode = <3>; - power = <250>; - phys = <&usb1_phy>; + reg = <0x47401c00 0x400 + 0x47401800 0x200>; + reg-names = "mc", "control"; + interrupts = <19>; + interrupt-names = "mc"; + dr_mode = "otg"; + mentor,multipoint = <1>; + mentor,num-eps = <16>; + mentor,ram-bits = <12>; + mentor,power = <500>; + phys = <&usb1_phy>; dmas = <&cppi41dma 15 0 &cppi41dma 16 0 &cppi41dma 17 0 &cppi41dma 18 0 @@ -461,10 +447,9 @@ "tx1", "tx2", "tx3", "tx4", "tx5", "tx6", "tx7", "tx8", "tx9", "tx10", "tx11", "tx12", "tx13", "tx14", "tx15"; - }; }; - cppi41dma: dma@07402000 { + cppi41dma: dma-controller@07402000 { compatible = "ti,am3359-cppi41"; reg = <0x47400000 0x1000 0x47402000 0x1000 diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 4ad52e76c4dd..4ead0f5f1f4c 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "musb_core.h" @@ -425,35 +426,51 @@ static int get_int_prop(struct device_node *dn, const char *s) return val; } +static int get_musb_port_mode(struct device *dev) +{ + enum usb_dr_mode mode; + + mode = of_usb_get_dr_mode(dev->of_node); + switch (mode) { + case USB_DR_MODE_HOST: + return MUSB_PORT_MODE_HOST; + + case USB_DR_MODE_PERIPHERAL: + return MUSB_PORT_MODE_GADGET; + + case USB_DR_MODE_UNKNOWN: + case USB_DR_MODE_OTG: + default: + return MUSB_PORT_MODE_DUAL_ROLE; + }; +} + static int dsps_create_musb_pdev(struct dsps_glue *glue, struct platform_device *parent) { struct musb_hdrc_platform_data pdata; struct resource resources[2]; + struct resource *res; struct device *dev = &parent->dev; struct musb_hdrc_config *config; struct platform_device *musb; struct device_node *dn = parent->dev.of_node; - struct device_node *child_node; int ret; - child_node = of_get_child_by_name(dn, "usb"); - if (!child_node) - return -EINVAL; - memset(resources, 0, sizeof(resources)); - ret = of_address_to_resource(child_node, 0, &resources[0]); - if (ret) { + res = platform_get_resource_byname(parent, IORESOURCE_MEM, "mc"); + if (!res) { dev_err(dev, "failed to get memory.\n"); - return ret; + return -EINVAL; } + resources[0] = *res; - ret = of_irq_to_resource(child_node, 0, &resources[1]); - if (ret == 0) { + res = platform_get_resource_byname(parent, IORESOURCE_IRQ, "mc"); + if (!res) { dev_err(dev, "failed to get irq.\n"); - ret = -EINVAL; - return ret; + return -EINVAL; } + resources[1] = *res; /* allocate the child platform device */ musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); @@ -465,7 +482,7 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, musb->dev.parent = dev; musb->dev.dma_mask = &musb_dmamask; musb->dev.coherent_dma_mask = musb_dmamask; - musb->dev.of_node = of_node_get(child_node); + musb->dev.of_node = of_node_get(dn); glue->musb = musb; @@ -485,11 +502,12 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, pdata.config = config; pdata.platform_ops = &dsps_ops; - config->num_eps = get_int_prop(child_node, "num-eps"); - config->ram_bits = get_int_prop(child_node, "ram-bits"); - pdata.mode = get_int_prop(child_node, "port-mode"); - pdata.power = get_int_prop(child_node, "power"); - config->multipoint = of_property_read_bool(child_node, "multipoint"); + config->num_eps = get_int_prop(dn, "mentor,num-eps"); + config->ram_bits = get_int_prop(dn, "mentor,ram-bits"); + pdata.mode = get_musb_port_mode(dev); + /* DT keeps this entry in mA, musb expects it as per USB spec */ + pdata.power = get_int_prop(dn, "mentor,power") / 2; + config->multipoint = of_property_read_bool(dn, "mentor,multipoint"); ret = platform_device_add_data(musb, &pdata, sizeof(pdata)); if (ret) { -- cgit v1.2.3 From 781f17983015dae33324e34d1bb831e715fa04d4 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 20 Aug 2013 18:35:49 +0200 Subject: usb: musb: am335x-evm: Do not remove the session bit HOST-only mode This is what I observe: On the first connect, the musb starts with DEVCTL.Session set. On disconnect, musb_core calls try_idle. That functions removes the Session bit signalizing that the session is over (something that only in OTG is required). A new device, that is plugged, is no longer recognized. I've setup a timer and checked the DEVCTL register and I haven't seen a change in VBus and I saw the B-Device bit set. After setting the IDDIG into A mode and forcing the device to behave like a A device, I didn't see a change. Neither VBUS goes to 0b11 nor does a session start request comes. In the TI-v3.2 kernel they skip to call musb_platform_try_idle() in the OTG_STATE_A_WAIT_BCON state while not in OTG mode. Since the second port hast a standard A plug the patch changes the port to run in host mode only and skips the timer which would remove DEVCTL.Session so we can reconnect to another device later. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- arch/arm/boot/dts/am335x-evm.dts | 1 + drivers/usb/musb/musb_dsps.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'arch') diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts index 648a67e74d1b..e8ec8756e498 100644 --- a/arch/arm/boot/dts/am335x-evm.dts +++ b/arch/arm/boot/dts/am335x-evm.dts @@ -192,6 +192,7 @@ usb@47401800 { status = "okay"; + dr_mode = "host"; }; dma-controller@07402000 { diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 392406da3751..4047cbb91bac 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -231,6 +231,8 @@ static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) glue->last_timer = jiffies; return; } + if (musb->port_mode == MUSB_PORT_MODE_HOST) + return; if (time_after(glue->last_timer, timeout) && timer_pending(&glue->timer)) { -- cgit v1.2.3 From 2ae847a1b10f3da5931fc9d04cc5950624418cf4 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 20 Aug 2013 18:35:50 +0200 Subject: usb: musb: am335x: add second port to beagle bone So I assumed that Beagle bone has only one USB port in host mode because the micro USB connector had an USB-UART there. I was wrong a little. The second port runs on host mode, but the micro USB plug is connected to an internal HUB with two ports: one to the USB-UART and one to musb instance one. For that reason, this patch enables both ports: the primary in device mode only and the second in host mode only. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- arch/arm/boot/dts/am335x-bone.dts | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'arch') diff --git a/arch/arm/boot/dts/am335x-bone.dts b/arch/arm/boot/dts/am335x-bone.dts index e8447a79dc7e..d318987d44a1 100644 --- a/arch/arm/boot/dts/am335x-bone.dts +++ b/arch/arm/boot/dts/am335x-bone.dts @@ -131,9 +131,22 @@ status = "okay"; }; + usb-phy@47401b00 { + status = "okay"; + }; + usb@47401000 { status = "okay"; }; + + usb@47401800 { + status = "okay"; + dr_mode = "host"; + }; + + dma-controller@07402000 { + status = "okay"; + }; }; i2c0: i2c@44e0b000 { -- cgit v1.2.3