usb: patches for v3.13
Final conversions to configfs for mass storage, acm_ms, and multi gadgets. MUSB should now work out of the box on AM335x-based boards (beagle bone white and black) with DMA thanks to Sebastian's work. We can now enable VERBOSE_DEBUG on builds of drivers/usb/gadget/ by selecting CONFIG_USB_GADGET_VERBOSE. s3c-hsotg got quite a few non-critical fixes but also learned a few new tricks (isochronous transfers, multi count support). The Marvel USB3 Controller driver got a memory leak fix. devm_usb_get_phy() learned not to return NULL, ever. Other than these patches, we have the usual set of cleanups ranging from removal of unnecessary *_set_drvdata() to using SIMPLE_DEV_PM_OPS. Signed-of-by: Felipe Balbi <balbi@ti.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.15 (GNU/Linux) iQIcBAABAgAGBQJSZRSSAAoJEIaOsuA1yqRE6EAP/AuF0dWV3wNSuv5h3ZPOybu8 uULX0E/VA2aGs8/55FeuQIJAn499zdt0KH5l8P3CGrKBPk8BN/rD55a6uwYEfWO8 wwTJpVeRyRQS8jes22vPqA22TXgl88SJO0RrsCarrzOcMNloVtOA4zyorITuGZQB jEKmf9BdjIUlzZkH9t33v3O8kB5pJ1YvBQGRWXbBZvxSzohPC2LYerZKMPN99hDB 2YnJXVKZqZzKbcQQmJklWqRo0RTprWz0Mqcu2r8Lnnn2ZqnT3RBmCBsYXefsp4nF egRJy51DiypEYt3/OEBf21BySjZHjO6+9jfzOmuOGoEiqw6XCRFMydVpqJJHC8WX MoCs31VnGwwwBpSOz9ECS9QYXne9jx/bJ6iKoS736sgA20ZA6wBbEDhJlTckcZtm TEC+UTKevNACAP8cjhGEquqwt5H/rMaYFMXEYQj+gvO2jDsNUGWb74l5VDaBiIm7 GzdUmgmYym8HKT80tgEcgvsUoUphDeNE84OW/jo1nFUDvCniLfQBAYZooEnTHY2H AW+DqimJzNnKcHo4w/HUQhRgK9147aRbskmVIbepIIW7WQdFQBOPVy7BbfnVD2vA j01JPshgtnjb+MZb6VqnbcaWKPnJr3KTnOpTTMyk5pKnBOf6PHb1S1Tq5uqtW2Ki gqi/SXZJ1bYDVaaspNWr =LDY7 -----END PGP SIGNATURE----- Merge tag 'usb-for-v3.13' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next Felipe writes: usb: patches for v3.13 Final conversions to configfs for mass storage, acm_ms, and multi gadgets. MUSB should now work out of the box on AM335x-based boards (beagle bone white and black) with DMA thanks to Sebastian's work. We can now enable VERBOSE_DEBUG on builds of drivers/usb/gadget/ by selecting CONFIG_USB_GADGET_VERBOSE. s3c-hsotg got quite a few non-critical fixes but also learned a few new tricks (isochronous transfers, multi count support). The Marvel USB3 Controller driver got a memory leak fix. devm_usb_get_phy() learned not to return NULL, ever. Other than these patches, we have the usual set of cleanups ranging from removal of unnecessary *_set_drvdata() to using SIMPLE_DEV_PM_OPS. Signed-of-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
		
				commit
				
					
						5328f35b15
					
				
			
		
					 51 changed files with 2965 additions and 1196 deletions
				
			
		
							
								
								
									
										31
									
								
								Documentation/ABI/testing/configfs-usb-gadget-mass-storage
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								Documentation/ABI/testing/configfs-usb-gadget-mass-storage
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | What:		/config/usb-gadget/gadget/functions/mass_storage.name | ||||||
|  | Date:		Oct 2013 | ||||||
|  | KenelVersion:	3.13 | ||||||
|  | Description: | ||||||
|  | 		The attributes: | ||||||
|  | 
 | ||||||
|  | 		stall		- Set to permit function to halt bulk endpoints. | ||||||
|  | 				Disabled on some USB devices known not to work | ||||||
|  | 				correctly. You should set it to true. | ||||||
|  | 		num_buffers	- Number of pipeline buffers. Valid numbers | ||||||
|  | 				are 2..4. Available only if | ||||||
|  | 				CONFIG_USB_GADGET_DEBUG_FILES is set. | ||||||
|  | 
 | ||||||
|  | What:		/config/usb-gadget/gadget/functions/mass_storage.name/lun.name | ||||||
|  | Date:		Oct 2013 | ||||||
|  | KenelVersion:	3.13 | ||||||
|  | Description: | ||||||
|  | 		The attributes: | ||||||
|  | 
 | ||||||
|  | 		file		- The path to the backing file for the LUN. | ||||||
|  | 				Required if LUN is not marked as removable. | ||||||
|  | 		ro		- Flag specifying access to the LUN shall be | ||||||
|  | 				read-only. This is implied if CD-ROM emulation | ||||||
|  | 				is enabled as well as when it was impossible | ||||||
|  | 				to open "filename" in R/W mode. | ||||||
|  | 		removable	- Flag specifying that LUN shall be indicated as | ||||||
|  | 				being removable. | ||||||
|  | 		cdrom		- Flag specifying that LUN shall be reported as | ||||||
|  | 				being a CD-ROM. | ||||||
|  | 		nofua		- Flag specifying that FUA flag | ||||||
|  | 				in SCSI WRITE(10,12) | ||||||
|  | @ -15,7 +15,7 @@ Optional properties: | ||||||
| 
 | 
 | ||||||
| - vcc-supply: phandle to the regulator that provides RESET to the PHY. | - vcc-supply: phandle to the regulator that provides RESET to the PHY. | ||||||
| 
 | 
 | ||||||
| - reset-supply: phandle to the regulator that provides power to the PHY. | - reset-gpios: Should specify the GPIO for reset. | ||||||
| 
 | 
 | ||||||
| Example: | Example: | ||||||
| 
 | 
 | ||||||
|  | @ -25,10 +25,9 @@ Example: | ||||||
| 		clocks = <&osc 0>; | 		clocks = <&osc 0>; | ||||||
| 		clock-names = "main_clk"; | 		clock-names = "main_clk"; | ||||||
| 		vcc-supply = <&hsusb1_vcc_regulator>; | 		vcc-supply = <&hsusb1_vcc_regulator>; | ||||||
| 		reset-supply = <&hsusb1_reset_regulator>; | 		reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| hsusb1_phy is a NOP USB PHY device that gets its clock from an oscillator | hsusb1_phy is a NOP USB PHY device that gets its clock from an oscillator | ||||||
| and expects that clock to be configured to 19.2MHz by the NOP PHY driver. | and expects that clock to be configured to 19.2MHz by the NOP PHY driver. | ||||||
| hsusb1_vcc_regulator provides power to the PHY and hsusb1_reset_regulator | hsusb1_vcc_regulator provides power to the PHY and GPIO 7 controls RESET. | ||||||
| controls RESET. |  | ||||||
|  |  | ||||||
|  | @ -289,18 +289,12 @@ static struct regulator_consumer_supply beagle_vsim_supply[] = { | ||||||
| 
 | 
 | ||||||
| static struct gpio_led gpio_leds[]; | static struct gpio_led gpio_leds[]; | ||||||
| 
 | 
 | ||||||
| /* PHY's VCC regulator might be added later, so flag that we need it */ |  | ||||||
| static struct usb_phy_gen_xceiv_platform_data hsusb2_phy_data = { |  | ||||||
| 	.needs_vcc = true, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static struct usbhs_phy_data phy_data[] = { | static struct usbhs_phy_data phy_data[] = { | ||||||
| 	{ | 	{ | ||||||
| 		.port = 2, | 		.port = 2, | ||||||
| 		.reset_gpio = 147, | 		.reset_gpio = 147, | ||||||
| 		.vcc_gpio = -1,		/* updated in beagle_twl_gpio_setup */ | 		.vcc_gpio = -1,		/* updated in beagle_twl_gpio_setup */ | ||||||
| 		.vcc_polarity = 1,	/* updated in beagle_twl_gpio_setup */ | 		.vcc_polarity = 1,	/* updated in beagle_twl_gpio_setup */ | ||||||
| 		.platform_data = &hsusb2_phy_data, |  | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -435,6 +435,7 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys) | ||||||
| 	struct platform_device *pdev; | 	struct platform_device *pdev; | ||||||
| 	char *phy_id; | 	char *phy_id; | ||||||
| 	struct platform_device_info pdevinfo; | 	struct platform_device_info pdevinfo; | ||||||
|  | 	struct usb_phy_gen_xceiv_platform_data nop_pdata; | ||||||
| 
 | 
 | ||||||
| 	for (i = 0; i < num_phys; i++) { | 	for (i = 0; i < num_phys; i++) { | ||||||
| 
 | 
 | ||||||
|  | @ -455,11 +456,18 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys) | ||||||
| 			return -ENOMEM; | 			return -ENOMEM; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		/* set platform data */ | ||||||
|  | 		memset(&nop_pdata, 0, sizeof(nop_pdata)); | ||||||
|  | 		if (gpio_is_valid(phy->vcc_gpio)) | ||||||
|  | 			nop_pdata.needs_vcc = true; | ||||||
|  | 		nop_pdata.gpio_reset = phy->reset_gpio; | ||||||
|  | 		nop_pdata.type = USB_PHY_TYPE_USB2; | ||||||
|  | 
 | ||||||
| 		/* create a NOP PHY device */ | 		/* create a NOP PHY device */ | ||||||
| 		memset(&pdevinfo, 0, sizeof(pdevinfo)); | 		memset(&pdevinfo, 0, sizeof(pdevinfo)); | ||||||
| 		pdevinfo.name = nop_name; | 		pdevinfo.name = nop_name; | ||||||
| 		pdevinfo.id = phy->port; | 		pdevinfo.id = phy->port; | ||||||
| 		pdevinfo.data = phy->platform_data; | 		pdevinfo.data = &nop_pdata; | ||||||
| 		pdevinfo.size_data = | 		pdevinfo.size_data = | ||||||
| 			sizeof(struct usb_phy_gen_xceiv_platform_data); | 			sizeof(struct usb_phy_gen_xceiv_platform_data); | ||||||
| 		scnprintf(phy_id, MAX_STR, "usb_phy_gen_xceiv.%d", | 		scnprintf(phy_id, MAX_STR, "usb_phy_gen_xceiv.%d", | ||||||
|  | @ -474,14 +482,6 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys) | ||||||
| 
 | 
 | ||||||
| 		usb_bind_phy("ehci-omap.0", phy->port - 1, phy_id); | 		usb_bind_phy("ehci-omap.0", phy->port - 1, phy_id); | ||||||
| 
 | 
 | ||||||
| 		/* Do we need RESET regulator ? */ |  | ||||||
| 		if (gpio_is_valid(phy->reset_gpio)) { |  | ||||||
| 			scnprintf(rail_name, MAX_STR, |  | ||||||
| 					"hsusb%d_reset", phy->port); |  | ||||||
| 			usbhs_add_regulator(rail_name, phy_id, "reset", |  | ||||||
| 						phy->reset_gpio, 1); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/* Do we need VCC regulator ? */ | 		/* Do we need VCC regulator ? */ | ||||||
| 		if (gpio_is_valid(phy->vcc_gpio)) { | 		if (gpio_is_valid(phy->vcc_gpio)) { | ||||||
| 			scnprintf(rail_name, MAX_STR, "hsusb%d_vcc", phy->port); | 			scnprintf(rail_name, MAX_STR, "hsusb%d_vcc", phy->port); | ||||||
|  |  | ||||||
|  | @ -58,7 +58,6 @@ struct usbhs_phy_data { | ||||||
| 	int reset_gpio; | 	int reset_gpio; | ||||||
| 	int vcc_gpio; | 	int vcc_gpio; | ||||||
| 	bool vcc_polarity;	/* 1 active high, 0 active low */ | 	bool vcc_polarity;	/* 1 active high, 0 active low */ | ||||||
| 	void *platform_data; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| extern void usb_musb_init(struct omap_musb_board_data *board_data); | extern void usb_musb_init(struct omap_musb_board_data *board_data); | ||||||
|  |  | ||||||
|  | @ -724,6 +724,8 @@ static int twl4030_usb_probe(struct platform_device *pdev) | ||||||
| 	if (device_create_file(&pdev->dev, &dev_attr_vbus)) | 	if (device_create_file(&pdev->dev, &dev_attr_vbus)) | ||||||
| 		dev_warn(&pdev->dev, "could not create sysfs file\n"); | 		dev_warn(&pdev->dev, "could not create sysfs file\n"); | ||||||
| 
 | 
 | ||||||
|  | 	ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); | ||||||
|  | 
 | ||||||
| 	/* Our job is to use irqs and status from the power module
 | 	/* Our job is to use irqs and status from the power module
 | ||||||
| 	 * to keep the transceiver disabled when nothing's connected. | 	 * to keep the transceiver disabled when nothing's connected. | ||||||
| 	 * | 	 * | ||||||
|  |  | ||||||
|  | @ -584,7 +584,7 @@ static int dwc3_remove(struct platform_device *pdev) | ||||||
| 	usb_phy_set_suspend(dwc->usb2_phy, 1); | 	usb_phy_set_suspend(dwc->usb2_phy, 1); | ||||||
| 	usb_phy_set_suspend(dwc->usb3_phy, 1); | 	usb_phy_set_suspend(dwc->usb3_phy, 1); | ||||||
| 
 | 
 | ||||||
| 	pm_runtime_put(&pdev->dev); | 	pm_runtime_put_sync(&pdev->dev); | ||||||
| 	pm_runtime_disable(&pdev->dev); | 	pm_runtime_disable(&pdev->dev); | ||||||
| 
 | 
 | ||||||
| 	dwc3_debugfs_exit(dwc); | 	dwc3_debugfs_exit(dwc); | ||||||
|  | @ -691,7 +691,6 @@ static int dwc3_resume(struct device *dev) | ||||||
| 
 | 
 | ||||||
| 	usb_phy_init(dwc->usb3_phy); | 	usb_phy_init(dwc->usb3_phy); | ||||||
| 	usb_phy_init(dwc->usb2_phy); | 	usb_phy_init(dwc->usb2_phy); | ||||||
| 	msleep(100); |  | ||||||
| 
 | 
 | ||||||
| 	spin_lock_irqsave(&dwc->lock, flags); | 	spin_lock_irqsave(&dwc->lock, flags); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -58,6 +58,20 @@ config USB_GADGET_DEBUG | ||||||
| 	   trying to track down.  Never enable these messages for a | 	   trying to track down.  Never enable these messages for a | ||||||
| 	   production build. | 	   production build. | ||||||
| 
 | 
 | ||||||
|  | config USB_GADGET_VERBOSE | ||||||
|  | 	bool "Verbose debugging Messages (DEVELOPMENT)" | ||||||
|  | 	depends on USB_GADGET_DEBUG | ||||||
|  | 	help | ||||||
|  | 	   Many controller and gadget drivers will print verbose debugging | ||||||
|  | 	   messages if you use this option to ask for those messages. | ||||||
|  | 
 | ||||||
|  | 	   Avoid enabling these messages, even if you're actively | ||||||
|  | 	   debugging such a driver.  Many drivers will emit so many | ||||||
|  | 	   messages that the driver timings are affected, which will | ||||||
|  | 	   either create new failure modes or remove the one you're | ||||||
|  | 	   trying to track down.  Never enable these messages for a | ||||||
|  | 	   production build. | ||||||
|  | 
 | ||||||
| config USB_GADGET_DEBUG_FILES | config USB_GADGET_DEBUG_FILES | ||||||
| 	boolean "Debugging information files (DEVELOPMENT)" | 	boolean "Debugging information files (DEVELOPMENT)" | ||||||
| 	depends on PROC_FS | 	depends on PROC_FS | ||||||
|  | @ -525,6 +539,9 @@ config USB_F_SUBSET | ||||||
| config USB_F_RNDIS | config USB_F_RNDIS | ||||||
| 	tristate | 	tristate | ||||||
| 
 | 
 | ||||||
|  | config USB_F_MASS_STORAGE | ||||||
|  | 	tristate | ||||||
|  | 
 | ||||||
| choice | choice | ||||||
| 	tristate "USB Gadget Drivers" | 	tristate "USB Gadget Drivers" | ||||||
| 	default USB_ETH | 	default USB_ETH | ||||||
|  | @ -662,6 +679,16 @@ config USB_CONFIGFS_PHONET | ||||||
| 	help | 	help | ||||||
| 	  The Phonet protocol implementation for USB device. | 	  The Phonet protocol implementation for USB device. | ||||||
| 
 | 
 | ||||||
|  | config USB_CONFIGFS_MASS_STORAGE | ||||||
|  | 	boolean "Mass storage" | ||||||
|  | 	depends on USB_CONFIGFS | ||||||
|  | 	select USB_F_MASS_STORAGE | ||||||
|  | 	help | ||||||
|  | 	  The Mass Storage Gadget acts as a USB Mass Storage disk drive. | ||||||
|  | 	  As its storage repository it can use a regular file or a block | ||||||
|  | 	  device (in much the same way as the "loop" device driver), | ||||||
|  | 	  specified as a module parameter or sysfs option. | ||||||
|  | 
 | ||||||
| config USB_ZERO | config USB_ZERO | ||||||
| 	tristate "Gadget Zero (DEVELOPMENT)" | 	tristate "Gadget Zero (DEVELOPMENT)" | ||||||
| 	select USB_LIBCOMPOSITE | 	select USB_LIBCOMPOSITE | ||||||
|  | @ -878,6 +905,7 @@ config USB_MASS_STORAGE | ||||||
| 	tristate "Mass Storage Gadget" | 	tristate "Mass Storage Gadget" | ||||||
| 	depends on BLOCK | 	depends on BLOCK | ||||||
| 	select USB_LIBCOMPOSITE | 	select USB_LIBCOMPOSITE | ||||||
|  | 	select USB_F_MASS_STORAGE | ||||||
| 	help | 	help | ||||||
| 	  The Mass Storage Gadget acts as a USB Mass Storage disk drive. | 	  The Mass Storage Gadget acts as a USB Mass Storage disk drive. | ||||||
| 	  As its storage repository it can use a regular file or a block | 	  As its storage repository it can use a regular file or a block | ||||||
|  | @ -1001,6 +1029,7 @@ config USB_G_ACM_MS | ||||||
| 	select USB_LIBCOMPOSITE | 	select USB_LIBCOMPOSITE | ||||||
| 	select USB_U_SERIAL | 	select USB_U_SERIAL | ||||||
| 	select USB_F_ACM | 	select USB_F_ACM | ||||||
|  | 	select USB_F_MASS_STORAGE | ||||||
| 	help | 	help | ||||||
| 	  This driver provides two functions in one configuration: | 	  This driver provides two functions in one configuration: | ||||||
| 	  a mass storage, and a CDC ACM (serial port) link. | 	  a mass storage, and a CDC ACM (serial port) link. | ||||||
|  | @ -1015,8 +1044,8 @@ config USB_G_MULTI | ||||||
| 	select USB_LIBCOMPOSITE | 	select USB_LIBCOMPOSITE | ||||||
| 	select USB_U_SERIAL | 	select USB_U_SERIAL | ||||||
| 	select USB_U_ETHER | 	select USB_U_ETHER | ||||||
| 	select USB_U_RNDIS |  | ||||||
| 	select USB_F_ACM | 	select USB_F_ACM | ||||||
|  | 	select USB_F_MASS_STORAGE | ||||||
| 	help | 	help | ||||||
| 	  The Multifunction Composite Gadget provides Ethernet (RNDIS | 	  The Multifunction Composite Gadget provides Ethernet (RNDIS | ||||||
| 	  and/or CDC Ethernet), mass storage and ACM serial link | 	  and/or CDC Ethernet), mass storage and ACM serial link | ||||||
|  | @ -1035,6 +1064,8 @@ config USB_G_MULTI | ||||||
| config USB_G_MULTI_RNDIS | config USB_G_MULTI_RNDIS | ||||||
| 	bool "RNDIS + CDC Serial + Storage configuration" | 	bool "RNDIS + CDC Serial + Storage configuration" | ||||||
| 	depends on USB_G_MULTI | 	depends on USB_G_MULTI | ||||||
|  | 	select USB_U_RNDIS | ||||||
|  | 	select USB_F_RNDIS | ||||||
| 	default y | 	default y | ||||||
| 	help | 	help | ||||||
| 	  This option enables a configuration with RNDIS, CDC Serial and | 	  This option enables a configuration with RNDIS, CDC Serial and | ||||||
|  | @ -1048,6 +1079,7 @@ config USB_G_MULTI_CDC | ||||||
| 	bool "CDC Ethernet + CDC Serial + Storage configuration" | 	bool "CDC Ethernet + CDC Serial + Storage configuration" | ||||||
| 	depends on USB_G_MULTI | 	depends on USB_G_MULTI | ||||||
| 	default n | 	default n | ||||||
|  | 	select USB_F_ECM | ||||||
| 	help | 	help | ||||||
| 	  This option enables a configuration with CDC Ethernet (ECM), CDC | 	  This option enables a configuration with CDC Ethernet (ECM), CDC | ||||||
| 	  Serial and Mass Storage functions available in the Multifunction | 	  Serial and Mass Storage functions available in the Multifunction | ||||||
|  |  | ||||||
|  | @ -1,7 +1,8 @@ | ||||||
| #
 | #
 | ||||||
| # USB peripheral controller drivers
 | # USB peripheral controller drivers
 | ||||||
| #
 | #
 | ||||||
| ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG | ccflags-$(CONFIG_USB_GADGET_DEBUG)	:= -DDEBUG | ||||||
|  | ccflags-$(CONFIG_USB_GADGET_VERBOSE)	+= -DVERBOSE_DEBUG | ||||||
| 
 | 
 | ||||||
| obj-$(CONFIG_USB_GADGET)	+= udc-core.o | obj-$(CONFIG_USB_GADGET)	+= udc-core.o | ||||||
| obj-$(CONFIG_USB_LIBCOMPOSITE)	+= libcomposite.o | obj-$(CONFIG_USB_LIBCOMPOSITE)	+= libcomposite.o | ||||||
|  | @ -60,6 +61,8 @@ usb_f_ecm_subset-y		:= f_subset.o | ||||||
| obj-$(CONFIG_USB_F_SUBSET)	+= usb_f_ecm_subset.o | obj-$(CONFIG_USB_F_SUBSET)	+= usb_f_ecm_subset.o | ||||||
| usb_f_rndis-y			:= f_rndis.o | usb_f_rndis-y			:= f_rndis.o | ||||||
| obj-$(CONFIG_USB_F_RNDIS)	+= usb_f_rndis.o | obj-$(CONFIG_USB_F_RNDIS)	+= usb_f_rndis.o | ||||||
|  | usb_f_mass_storage-y		:= f_mass_storage.o storage_common.o | ||||||
|  | obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o | ||||||
| 
 | 
 | ||||||
| #
 | #
 | ||||||
| # USB gadget drivers
 | # USB gadget drivers
 | ||||||
|  |  | ||||||
|  | @ -31,16 +31,7 @@ | ||||||
| #define ACM_MS_VENDOR_NUM	0x1d6b	/* Linux Foundation */ | #define ACM_MS_VENDOR_NUM	0x1d6b	/* Linux Foundation */ | ||||||
| #define ACM_MS_PRODUCT_NUM	0x0106	/* Composite Gadget: ACM + MS*/ | #define ACM_MS_PRODUCT_NUM	0x0106	/* Composite Gadget: ACM + MS*/ | ||||||
| 
 | 
 | ||||||
| /*-------------------------------------------------------------------------*/ | #include "f_mass_storage.h" | ||||||
| 
 |  | ||||||
| /*
 |  | ||||||
|  * Kbuild is not very cooperative with respect to linking separately |  | ||||||
|  * compiled library objects into one module.  So for now we won't use |  | ||||||
|  * separate compilation ... ensuring init/exit sections work to shrink |  | ||||||
|  * the runtime footprint, and giving us at least some parts of what |  | ||||||
|  * a "gcc --combine ... part1.c part2.c part3.c ... " build would. |  | ||||||
|  */ |  | ||||||
| #include "f_mass_storage.c" |  | ||||||
| 
 | 
 | ||||||
| /*-------------------------------------------------------------------------*/ | /*-------------------------------------------------------------------------*/ | ||||||
| USB_GADGET_COMPOSITE_OPTIONS(); | USB_GADGET_COMPOSITE_OPTIONS(); | ||||||
|  | @ -104,18 +95,35 @@ static struct usb_gadget_strings *dev_strings[] = { | ||||||
| /****************************** Configurations ******************************/ | /****************************** Configurations ******************************/ | ||||||
| 
 | 
 | ||||||
| static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; | static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; | ||||||
| FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); | #ifdef CONFIG_USB_GADGET_DEBUG_FILES | ||||||
| 
 | 
 | ||||||
| static struct fsg_common fsg_common; | static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; | ||||||
|  | 
 | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Number of buffers we will use. | ||||||
|  |  * 2 is usually enough for good buffering pipeline | ||||||
|  |  */ | ||||||
|  | #define fsg_num_buffers	CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS | ||||||
|  | 
 | ||||||
|  | #endif /* CONFIG_USB_DEBUG */ | ||||||
|  | 
 | ||||||
|  | FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); | ||||||
| 
 | 
 | ||||||
| /*-------------------------------------------------------------------------*/ | /*-------------------------------------------------------------------------*/ | ||||||
| static struct usb_function *f_acm; | static struct usb_function *f_acm; | ||||||
| static struct usb_function_instance *f_acm_inst; | static struct usb_function_instance *f_acm_inst; | ||||||
|  | 
 | ||||||
|  | static struct usb_function_instance *fi_msg; | ||||||
|  | static struct usb_function *f_msg; | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * We _always_ have both ACM and mass storage functions. |  * We _always_ have both ACM and mass storage functions. | ||||||
|  */ |  */ | ||||||
| static int __init acm_ms_do_config(struct usb_configuration *c) | static int __init acm_ms_do_config(struct usb_configuration *c) | ||||||
| { | { | ||||||
|  | 	struct fsg_opts *opts; | ||||||
| 	int	status; | 	int	status; | ||||||
| 
 | 
 | ||||||
| 	if (gadget_is_otg(c->cdev->gadget)) { | 	if (gadget_is_otg(c->cdev->gadget)) { | ||||||
|  | @ -123,31 +131,37 @@ static int __init acm_ms_do_config(struct usb_configuration *c) | ||||||
| 		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | 		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	f_acm_inst = usb_get_function_instance("acm"); | 	opts = fsg_opts_from_func_inst(fi_msg); | ||||||
| 	if (IS_ERR(f_acm_inst)) |  | ||||||
| 		return PTR_ERR(f_acm_inst); |  | ||||||
| 
 | 
 | ||||||
| 	f_acm = usb_get_function(f_acm_inst); | 	f_acm = usb_get_function(f_acm_inst); | ||||||
| 	if (IS_ERR(f_acm)) { | 	if (IS_ERR(f_acm)) | ||||||
| 		status = PTR_ERR(f_acm); | 		return PTR_ERR(f_acm); | ||||||
| 		goto err_func; | 
 | ||||||
|  | 	f_msg = usb_get_function(fi_msg); | ||||||
|  | 	if (IS_ERR(f_msg)) { | ||||||
|  | 		status = PTR_ERR(f_msg); | ||||||
|  | 		goto put_acm; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	status = usb_add_function(c, f_acm); | 	status = usb_add_function(c, f_acm); | ||||||
| 	if (status < 0) | 	if (status < 0) | ||||||
| 		goto err_conf; | 		goto put_msg; | ||||||
| 
 | 
 | ||||||
| 	status = fsg_bind_config(c->cdev, c, &fsg_common); | 	status = fsg_common_run_thread(opts->common); | ||||||
| 	if (status < 0) | 	if (status) | ||||||
| 		goto err_fsg; | 		goto remove_acm; | ||||||
|  | 
 | ||||||
|  | 	status = usb_add_function(c, f_msg); | ||||||
|  | 	if (status) | ||||||
|  | 		goto remove_acm; | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| err_fsg: | remove_acm: | ||||||
| 	usb_remove_function(c, f_acm); | 	usb_remove_function(c, f_acm); | ||||||
| err_conf: | put_msg: | ||||||
|  | 	usb_put_function(f_msg); | ||||||
|  | put_acm: | ||||||
| 	usb_put_function(f_acm); | 	usb_put_function(f_acm); | ||||||
| err_func: |  | ||||||
| 	usb_put_function_instance(f_acm_inst); |  | ||||||
| 	return status; | 	return status; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -163,45 +177,82 @@ static struct usb_configuration acm_ms_config_driver = { | ||||||
| static int __init acm_ms_bind(struct usb_composite_dev *cdev) | static int __init acm_ms_bind(struct usb_composite_dev *cdev) | ||||||
| { | { | ||||||
| 	struct usb_gadget	*gadget = cdev->gadget; | 	struct usb_gadget	*gadget = cdev->gadget; | ||||||
|  | 	struct fsg_opts		*opts; | ||||||
|  | 	struct fsg_config	config; | ||||||
| 	int			status; | 	int			status; | ||||||
| 	void			*retp; |  | ||||||
| 
 | 
 | ||||||
| 	/* set up mass storage function */ | 	f_acm_inst = usb_get_function_instance("acm"); | ||||||
| 	retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data); | 	if (IS_ERR(f_acm_inst)) | ||||||
| 	if (IS_ERR(retp)) { | 		return PTR_ERR(f_acm_inst); | ||||||
| 		status = PTR_ERR(retp); | 
 | ||||||
| 		return PTR_ERR(retp); | 	fi_msg = usb_get_function_instance("mass_storage"); | ||||||
|  | 	if (IS_ERR(fi_msg)) { | ||||||
|  | 		status = PTR_ERR(fi_msg); | ||||||
|  | 		goto fail_get_msg; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/* set up mass storage function */ | ||||||
|  | 	fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers); | ||||||
|  | 	opts = fsg_opts_from_func_inst(fi_msg); | ||||||
|  | 
 | ||||||
|  | 	opts->no_configfs = true; | ||||||
|  | 	status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers); | ||||||
|  | 	if (status) | ||||||
|  | 		goto fail; | ||||||
|  | 
 | ||||||
|  | 	status = fsg_common_set_nluns(opts->common, config.nluns); | ||||||
|  | 	if (status) | ||||||
|  | 		goto fail_set_nluns; | ||||||
|  | 
 | ||||||
|  | 	status = fsg_common_set_cdev(opts->common, cdev, config.can_stall); | ||||||
|  | 	if (status) | ||||||
|  | 		goto fail_set_cdev; | ||||||
|  | 
 | ||||||
|  | 	fsg_common_set_sysfs(opts->common, true); | ||||||
|  | 	status = fsg_common_create_luns(opts->common, &config); | ||||||
|  | 	if (status) | ||||||
|  | 		goto fail_set_cdev; | ||||||
|  | 
 | ||||||
|  | 	fsg_common_set_inquiry_string(opts->common, config.vendor_name, | ||||||
|  | 				      config.product_name); | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Allocate string descriptor numbers ... note that string | 	 * Allocate string descriptor numbers ... note that string | ||||||
| 	 * contents can be overridden by the composite_dev glue. | 	 * contents can be overridden by the composite_dev glue. | ||||||
| 	 */ | 	 */ | ||||||
| 	status = usb_string_ids_tab(cdev, strings_dev); | 	status = usb_string_ids_tab(cdev, strings_dev); | ||||||
| 	if (status < 0) | 	if (status < 0) | ||||||
| 		goto fail1; | 		goto fail_string_ids; | ||||||
| 	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; | 	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; | ||||||
| 	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; | 	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; | ||||||
| 
 | 
 | ||||||
| 	/* register our configuration */ | 	/* register our configuration */ | ||||||
| 	status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config); | 	status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config); | ||||||
| 	if (status < 0) | 	if (status < 0) | ||||||
| 		goto fail1; | 		goto fail_string_ids; | ||||||
| 
 | 
 | ||||||
| 	usb_composite_overwrite_options(cdev, &coverwrite); | 	usb_composite_overwrite_options(cdev, &coverwrite); | ||||||
| 	dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", | 	dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", | ||||||
| 			DRIVER_DESC); | 			DRIVER_DESC); | ||||||
| 	fsg_common_put(&fsg_common); |  | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| 	/* error recovery */ | 	/* error recovery */ | ||||||
| fail1: | fail_string_ids: | ||||||
| 	fsg_common_put(&fsg_common); | 	fsg_common_remove_luns(opts->common); | ||||||
|  | fail_set_cdev: | ||||||
|  | 	fsg_common_free_luns(opts->common); | ||||||
|  | fail_set_nluns: | ||||||
|  | 	fsg_common_free_buffers(opts->common); | ||||||
|  | fail: | ||||||
|  | 	usb_put_function_instance(fi_msg); | ||||||
|  | fail_get_msg: | ||||||
|  | 	usb_put_function_instance(f_acm_inst); | ||||||
| 	return status; | 	return status; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int __exit acm_ms_unbind(struct usb_composite_dev *cdev) | static int __exit acm_ms_unbind(struct usb_composite_dev *cdev) | ||||||
| { | { | ||||||
|  | 	usb_put_function(f_msg); | ||||||
|  | 	usb_put_function_instance(fi_msg); | ||||||
| 	usb_put_function(f_acm); | 	usb_put_function(f_acm); | ||||||
| 	usb_put_function_instance(f_acm_inst); | 	usb_put_function_instance(f_acm_inst); | ||||||
| 	return 0; | 	return 0; | ||||||
|  |  | ||||||
|  | @ -557,7 +557,7 @@ static struct config_group *function_make( | ||||||
| 
 | 
 | ||||||
| 	fi = usb_get_function_instance(func_name); | 	fi = usb_get_function_instance(func_name); | ||||||
| 	if (IS_ERR(fi)) | 	if (IS_ERR(fi)) | ||||||
| 		return ERR_PTR(PTR_ERR(fi)); | 		return ERR_CAST(fi); | ||||||
| 
 | 
 | ||||||
| 	ret = config_item_set_name(&fi->group.cg_item, name); | 	ret = config_item_set_name(&fi->group.cg_item, name); | ||||||
| 	if (ret) { | 	if (ret) { | ||||||
|  | @ -991,6 +991,14 @@ static struct configfs_subsystem gadget_subsys = { | ||||||
| 	.su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex), | 	.su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex), | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | void unregister_gadget_item(struct config_item *item) | ||||||
|  | { | ||||||
|  | 	struct gadget_info *gi = to_gadget_info(item); | ||||||
|  | 
 | ||||||
|  | 	unregister_gadget(gi); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(unregister_gadget_item); | ||||||
|  | 
 | ||||||
| static int __init gadget_cfs_init(void) | static int __init gadget_cfs_init(void) | ||||||
| { | { | ||||||
| 	int ret; | 	int ret; | ||||||
|  |  | ||||||
							
								
								
									
										6
									
								
								drivers/usb/gadget/configfs.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								drivers/usb/gadget/configfs.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | ||||||
|  | #ifndef USB__GADGET__CONFIGFS__H | ||||||
|  | #define USB__GADGET__CONFIGFS__H | ||||||
|  | 
 | ||||||
|  | void unregister_gadget_item(struct config_item *item); | ||||||
|  | 
 | ||||||
|  | #endif /*  USB__GADGET__CONFIGFS__H */ | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										166
									
								
								drivers/usb/gadget/f_mass_storage.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								drivers/usb/gadget/f_mass_storage.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,166 @@ | ||||||
|  | #ifndef USB_F_MASS_STORAGE_H | ||||||
|  | #define USB_F_MASS_STORAGE_H | ||||||
|  | 
 | ||||||
|  | #include <linux/usb/composite.h> | ||||||
|  | #include "storage_common.h" | ||||||
|  | 
 | ||||||
|  | struct fsg_module_parameters { | ||||||
|  | 	char		*file[FSG_MAX_LUNS]; | ||||||
|  | 	bool		ro[FSG_MAX_LUNS]; | ||||||
|  | 	bool		removable[FSG_MAX_LUNS]; | ||||||
|  | 	bool		cdrom[FSG_MAX_LUNS]; | ||||||
|  | 	bool		nofua[FSG_MAX_LUNS]; | ||||||
|  | 
 | ||||||
|  | 	unsigned int	file_count, ro_count, removable_count, cdrom_count; | ||||||
|  | 	unsigned int	nofua_count; | ||||||
|  | 	unsigned int	luns;	/* nluns */ | ||||||
|  | 	bool		stall;	/* can_stall */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc)	\ | ||||||
|  | 	module_param_array_named(prefix ## name, params.name, type,	\ | ||||||
|  | 				 &prefix ## params.name ## _count,	\ | ||||||
|  | 				 S_IRUGO);				\ | ||||||
|  | 	MODULE_PARM_DESC(prefix ## name, desc) | ||||||
|  | 
 | ||||||
|  | #define _FSG_MODULE_PARAM(prefix, params, name, type, desc)		\ | ||||||
|  | 	module_param_named(prefix ## name, params.name, type,		\ | ||||||
|  | 			   S_IRUGO);					\ | ||||||
|  | 	MODULE_PARM_DESC(prefix ## name, desc) | ||||||
|  | 
 | ||||||
|  | #define __FSG_MODULE_PARAMETERS(prefix, params)				\ | ||||||
|  | 	_FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp,		\ | ||||||
|  | 				"names of backing files or devices");	\ | ||||||
|  | 	_FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool,		\ | ||||||
|  | 				"true to force read-only");		\ | ||||||
|  | 	_FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool,	\ | ||||||
|  | 				"true to simulate removable media");	\ | ||||||
|  | 	_FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool,		\ | ||||||
|  | 				"true to simulate CD-ROM instead of disk"); \ | ||||||
|  | 	_FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool,		\ | ||||||
|  | 				"true to ignore SCSI WRITE(10,12) FUA bit"); \ | ||||||
|  | 	_FSG_MODULE_PARAM(prefix, params, luns, uint,			\ | ||||||
|  | 			  "number of LUNs");				\ | ||||||
|  | 	_FSG_MODULE_PARAM(prefix, params, stall, bool,			\ | ||||||
|  | 			  "false to prevent bulk stalls") | ||||||
|  | 
 | ||||||
|  | #ifdef CONFIG_USB_GADGET_DEBUG_FILES | ||||||
|  | 
 | ||||||
|  | #define FSG_MODULE_PARAMETERS(prefix, params)				\ | ||||||
|  | 	__FSG_MODULE_PARAMETERS(prefix, params);			\ | ||||||
|  | 	module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);\ | ||||||
|  | 	MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers") | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | #define FSG_MODULE_PARAMETERS(prefix, params)				\ | ||||||
|  | 	__FSG_MODULE_PARAMETERS(prefix, params) | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | struct fsg_common; | ||||||
|  | 
 | ||||||
|  | /* FSF callback functions */ | ||||||
|  | struct fsg_operations { | ||||||
|  | 	/*
 | ||||||
|  | 	 * Callback function to call when thread exits.  If no | ||||||
|  | 	 * callback is set or it returns value lower then zero MSF | ||||||
|  | 	 * will force eject all LUNs it operates on (including those | ||||||
|  | 	 * marked as non-removable or with prevent_medium_removal flag | ||||||
|  | 	 * set). | ||||||
|  | 	 */ | ||||||
|  | 	int (*thread_exits)(struct fsg_common *common); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct fsg_lun_opts { | ||||||
|  | 	struct config_group group; | ||||||
|  | 	struct fsg_lun *lun; | ||||||
|  | 	int lun_id; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct fsg_opts { | ||||||
|  | 	struct fsg_common *common; | ||||||
|  | 	struct usb_function_instance func_inst; | ||||||
|  | 	struct fsg_lun_opts lun0; | ||||||
|  | 	struct config_group *default_groups[2]; | ||||||
|  | 	bool no_configfs; /* for legacy gadgets */ | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Read/write access to configfs attributes is handled by configfs. | ||||||
|  | 	 * | ||||||
|  | 	 * This is to protect the data from concurrent access by read/write | ||||||
|  | 	 * and create symlink/remove symlink. | ||||||
|  | 	 */ | ||||||
|  | 	struct mutex			lock; | ||||||
|  | 	int				refcnt; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct fsg_lun_config { | ||||||
|  | 	const char *filename; | ||||||
|  | 	char ro; | ||||||
|  | 	char removable; | ||||||
|  | 	char cdrom; | ||||||
|  | 	char nofua; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct fsg_config { | ||||||
|  | 	unsigned nluns; | ||||||
|  | 	struct fsg_lun_config luns[FSG_MAX_LUNS]; | ||||||
|  | 
 | ||||||
|  | 	/* Callback functions. */ | ||||||
|  | 	const struct fsg_operations	*ops; | ||||||
|  | 	/* Gadget's private data. */ | ||||||
|  | 	void			*private_data; | ||||||
|  | 
 | ||||||
|  | 	const char *vendor_name;		/*  8 characters or less */ | ||||||
|  | 	const char *product_name;		/* 16 characters or less */ | ||||||
|  | 
 | ||||||
|  | 	char			can_stall; | ||||||
|  | 	unsigned int		fsg_num_buffers; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static inline struct fsg_opts * | ||||||
|  | fsg_opts_from_func_inst(const struct usb_function_instance *fi) | ||||||
|  | { | ||||||
|  | 	return container_of(fi, struct fsg_opts, func_inst); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void fsg_common_get(struct fsg_common *common); | ||||||
|  | 
 | ||||||
|  | void fsg_common_put(struct fsg_common *common); | ||||||
|  | 
 | ||||||
|  | void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs); | ||||||
|  | 
 | ||||||
|  | int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n); | ||||||
|  | 
 | ||||||
|  | void fsg_common_free_buffers(struct fsg_common *common); | ||||||
|  | 
 | ||||||
|  | int fsg_common_set_cdev(struct fsg_common *common, | ||||||
|  | 			struct usb_composite_dev *cdev, bool can_stall); | ||||||
|  | 
 | ||||||
|  | void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs); | ||||||
|  | 
 | ||||||
|  | void fsg_common_remove_luns(struct fsg_common *common); | ||||||
|  | 
 | ||||||
|  | void fsg_common_free_luns(struct fsg_common *common); | ||||||
|  | 
 | ||||||
|  | int fsg_common_set_nluns(struct fsg_common *common, int nluns); | ||||||
|  | 
 | ||||||
|  | void fsg_common_set_ops(struct fsg_common *common, | ||||||
|  | 			const struct fsg_operations *ops); | ||||||
|  | 
 | ||||||
|  | int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg, | ||||||
|  | 			  unsigned int id, const char *name, | ||||||
|  | 			  const char **name_pfx); | ||||||
|  | 
 | ||||||
|  | int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg); | ||||||
|  | 
 | ||||||
|  | void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn, | ||||||
|  | 				   const char *pn); | ||||||
|  | 
 | ||||||
|  | int fsg_common_run_thread(struct fsg_common *common); | ||||||
|  | 
 | ||||||
|  | void fsg_config_from_params(struct fsg_config *cfg, | ||||||
|  | 			    const struct fsg_module_parameters *params, | ||||||
|  | 			    unsigned int fsg_num_buffers); | ||||||
|  | 
 | ||||||
|  | #endif /* USB_F_MASS_STORAGE_H */ | ||||||
|  | @ -37,16 +37,16 @@ | ||||||
| #define DRIVER_DESC		"Mass Storage Gadget" | #define DRIVER_DESC		"Mass Storage Gadget" | ||||||
| #define DRIVER_VERSION		"2009/09/11" | #define DRIVER_VERSION		"2009/09/11" | ||||||
| 
 | 
 | ||||||
| /*-------------------------------------------------------------------------*/ |  | ||||||
| 
 |  | ||||||
| /*
 | /*
 | ||||||
|  * kbuild is not very cooperative with respect to linking separately |  * Thanks to NetChip Technologies for donating this product ID. | ||||||
|  * compiled library objects into one module.  So for now we won't use |  * | ||||||
|  * separate compilation ... ensuring init/exit sections work to shrink |  * DO NOT REUSE THESE IDs with any other driver!!  Ever!! | ||||||
|  * the runtime footprint, and giving us at least some parts of what |  * Instead:  allocate your own, using normal USB-IF procedures. | ||||||
|  * a "gcc --combine ... part1.c part2.c part3.c ... " build would. |  | ||||||
|  */ |  */ | ||||||
| #include "f_mass_storage.c" | #define FSG_VENDOR_ID	0x0525	/* NetChip */ | ||||||
|  | #define FSG_PRODUCT_ID	0xa4a5	/* Linux-USB File-backed Storage Gadget */ | ||||||
|  | 
 | ||||||
|  | #include "f_mass_storage.h" | ||||||
| 
 | 
 | ||||||
| /*-------------------------------------------------------------------------*/ | /*-------------------------------------------------------------------------*/ | ||||||
| USB_GADGET_COMPOSITE_OPTIONS(); | USB_GADGET_COMPOSITE_OPTIONS(); | ||||||
|  | @ -97,11 +97,28 @@ static struct usb_gadget_strings *dev_strings[] = { | ||||||
| 	NULL, | 	NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static struct usb_function_instance *fi_msg; | ||||||
|  | static struct usb_function *f_msg; | ||||||
|  | 
 | ||||||
| /****************************** Configurations ******************************/ | /****************************** Configurations ******************************/ | ||||||
| 
 | 
 | ||||||
| static struct fsg_module_parameters mod_data = { | static struct fsg_module_parameters mod_data = { | ||||||
| 	.stall = 1 | 	.stall = 1 | ||||||
| }; | }; | ||||||
|  | #ifdef CONFIG_USB_GADGET_DEBUG_FILES | ||||||
|  | 
 | ||||||
|  | static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; | ||||||
|  | 
 | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Number of buffers we will use. | ||||||
|  |  * 2 is usually enough for good buffering pipeline | ||||||
|  |  */ | ||||||
|  | #define fsg_num_buffers	CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS | ||||||
|  | 
 | ||||||
|  | #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ | ||||||
|  | 
 | ||||||
| FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); | FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); | ||||||
| 
 | 
 | ||||||
| static unsigned long msg_registered; | static unsigned long msg_registered; | ||||||
|  | @ -115,13 +132,7 @@ static int msg_thread_exits(struct fsg_common *common) | ||||||
| 
 | 
 | ||||||
| static int __init msg_do_config(struct usb_configuration *c) | static int __init msg_do_config(struct usb_configuration *c) | ||||||
| { | { | ||||||
| 	static const struct fsg_operations ops = { | 	struct fsg_opts *opts; | ||||||
| 		.thread_exits = msg_thread_exits, |  | ||||||
| 	}; |  | ||||||
| 	static struct fsg_common common; |  | ||||||
| 
 |  | ||||||
| 	struct fsg_common *retp; |  | ||||||
| 	struct fsg_config config; |  | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	if (gadget_is_otg(c->cdev->gadget)) { | 	if (gadget_is_otg(c->cdev->gadget)) { | ||||||
|  | @ -129,15 +140,24 @@ static int __init msg_do_config(struct usb_configuration *c) | ||||||
| 		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | 		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fsg_config_from_params(&config, &mod_data); | 	opts = fsg_opts_from_func_inst(fi_msg); | ||||||
| 	config.ops = &ops; |  | ||||||
| 
 | 
 | ||||||
| 	retp = fsg_common_init(&common, c->cdev, &config); | 	f_msg = usb_get_function(fi_msg); | ||||||
| 	if (IS_ERR(retp)) | 	if (IS_ERR(f_msg)) | ||||||
| 		return PTR_ERR(retp); | 		return PTR_ERR(f_msg); | ||||||
| 
 | 
 | ||||||
| 	ret = fsg_bind_config(c->cdev, c, &common); | 	ret = fsg_common_run_thread(opts->common); | ||||||
| 	fsg_common_put(&common); | 	if (ret) | ||||||
|  | 		goto put_func; | ||||||
|  | 
 | ||||||
|  | 	ret = usb_add_function(c, f_msg); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto put_func; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | 
 | ||||||
|  | put_func: | ||||||
|  | 	usb_put_function(f_msg); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -152,23 +172,79 @@ static struct usb_configuration msg_config_driver = { | ||||||
| 
 | 
 | ||||||
| static int __init msg_bind(struct usb_composite_dev *cdev) | static int __init msg_bind(struct usb_composite_dev *cdev) | ||||||
| { | { | ||||||
|  | 	static const struct fsg_operations ops = { | ||||||
|  | 		.thread_exits = msg_thread_exits, | ||||||
|  | 	}; | ||||||
|  | 	struct fsg_opts *opts; | ||||||
|  | 	struct fsg_config config; | ||||||
| 	int status; | 	int status; | ||||||
| 
 | 
 | ||||||
|  | 	fi_msg = usb_get_function_instance("mass_storage"); | ||||||
|  | 	if (IS_ERR(fi_msg)) | ||||||
|  | 		return PTR_ERR(fi_msg); | ||||||
|  | 
 | ||||||
|  | 	fsg_config_from_params(&config, &mod_data, fsg_num_buffers); | ||||||
|  | 	opts = fsg_opts_from_func_inst(fi_msg); | ||||||
|  | 
 | ||||||
|  | 	opts->no_configfs = true; | ||||||
|  | 	status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers); | ||||||
|  | 	if (status) | ||||||
|  | 		goto fail; | ||||||
|  | 
 | ||||||
|  | 	status = fsg_common_set_nluns(opts->common, config.nluns); | ||||||
|  | 	if (status) | ||||||
|  | 		goto fail_set_nluns; | ||||||
|  | 
 | ||||||
|  | 	fsg_common_set_ops(opts->common, &ops); | ||||||
|  | 
 | ||||||
|  | 	status = fsg_common_set_cdev(opts->common, cdev, config.can_stall); | ||||||
|  | 	if (status) | ||||||
|  | 		goto fail_set_cdev; | ||||||
|  | 
 | ||||||
|  | 	fsg_common_set_sysfs(opts->common, true); | ||||||
|  | 	status = fsg_common_create_luns(opts->common, &config); | ||||||
|  | 	if (status) | ||||||
|  | 		goto fail_set_cdev; | ||||||
|  | 
 | ||||||
|  | 	fsg_common_set_inquiry_string(opts->common, config.vendor_name, | ||||||
|  | 				      config.product_name); | ||||||
|  | 
 | ||||||
| 	status = usb_string_ids_tab(cdev, strings_dev); | 	status = usb_string_ids_tab(cdev, strings_dev); | ||||||
| 	if (status < 0) | 	if (status < 0) | ||||||
| 		return status; | 		goto fail_string_ids; | ||||||
| 	msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; | 	msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; | ||||||
| 
 | 
 | ||||||
| 	status = usb_add_config(cdev, &msg_config_driver, msg_do_config); | 	status = usb_add_config(cdev, &msg_config_driver, msg_do_config); | ||||||
| 	if (status < 0) | 	if (status < 0) | ||||||
| 		return status; | 		goto fail_string_ids; | ||||||
|  | 
 | ||||||
| 	usb_composite_overwrite_options(cdev, &coverwrite); | 	usb_composite_overwrite_options(cdev, &coverwrite); | ||||||
| 	dev_info(&cdev->gadget->dev, | 	dev_info(&cdev->gadget->dev, | ||||||
| 		 DRIVER_DESC ", version: " DRIVER_VERSION "\n"); | 		 DRIVER_DESC ", version: " DRIVER_VERSION "\n"); | ||||||
| 	set_bit(0, &msg_registered); | 	set_bit(0, &msg_registered); | ||||||
| 	return 0; | 	return 0; | ||||||
|  | 
 | ||||||
|  | fail_string_ids: | ||||||
|  | 	fsg_common_remove_luns(opts->common); | ||||||
|  | fail_set_cdev: | ||||||
|  | 	fsg_common_free_luns(opts->common); | ||||||
|  | fail_set_nluns: | ||||||
|  | 	fsg_common_free_buffers(opts->common); | ||||||
|  | fail: | ||||||
|  | 	usb_put_function_instance(fi_msg); | ||||||
|  | 	return status; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int msg_unbind(struct usb_composite_dev *cdev) | ||||||
|  | { | ||||||
|  | 	if (!IS_ERR(f_msg)) | ||||||
|  | 		usb_put_function(f_msg); | ||||||
|  | 
 | ||||||
|  | 	if (!IS_ERR(fi_msg)) | ||||||
|  | 		usb_put_function_instance(fi_msg); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| /****************************** Some noise ******************************/ | /****************************** Some noise ******************************/ | ||||||
| 
 | 
 | ||||||
|  | @ -179,6 +255,7 @@ static __refdata struct usb_composite_driver msg_driver = { | ||||||
| 	.needs_serial	= 1, | 	.needs_serial	= 1, | ||||||
| 	.strings	= dev_strings, | 	.strings	= dev_strings, | ||||||
| 	.bind		= msg_bind, | 	.bind		= msg_bind, | ||||||
|  | 	.unbind		= msg_unbind, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| MODULE_DESCRIPTION(DRIVER_DESC); | MODULE_DESCRIPTION(DRIVER_DESC); | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <linux/kernel.h> | #include <linux/kernel.h> | ||||||
| #include <linux/module.h> | #include <linux/module.h> | ||||||
|  | #include <linux/netdevice.h> | ||||||
| 
 | 
 | ||||||
| #include "u_serial.h" | #include "u_serial.h" | ||||||
| #if defined USB_ETH_RNDIS | #if defined USB_ETH_RNDIS | ||||||
|  | @ -32,22 +33,11 @@ MODULE_AUTHOR("Michal Nazarewicz"); | ||||||
| MODULE_LICENSE("GPL"); | MODULE_LICENSE("GPL"); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /***************************** All the files... *****************************/ | #include "f_mass_storage.h" | ||||||
| 
 | 
 | ||||||
| /*
 | #include "u_ecm.h" | ||||||
|  * kbuild is not very cooperative with respect to linking separately |  | ||||||
|  * compiled library objects into one module.  So for now we won't use |  | ||||||
|  * separate compilation ... ensuring init/exit sections work to shrink |  | ||||||
|  * the runtime footprint, and giving us at least some parts of what |  | ||||||
|  * a "gcc --combine ... part1.c part2.c part3.c ... " build would. |  | ||||||
|  */ |  | ||||||
| #include "f_mass_storage.c" |  | ||||||
| 
 |  | ||||||
| #define USBF_ECM_INCLUDED |  | ||||||
| #include "f_ecm.c" |  | ||||||
| #ifdef USB_ETH_RNDIS | #ifdef USB_ETH_RNDIS | ||||||
| #  define USB_FRNDIS_INCLUDED | #  include "u_rndis.h" | ||||||
| #  include "f_rndis.c" |  | ||||||
| #  include "rndis.h" | #  include "rndis.h" | ||||||
| #endif | #endif | ||||||
| #include "u_ether.h" | #include "u_ether.h" | ||||||
|  | @ -132,22 +122,36 @@ static struct usb_gadget_strings *dev_strings[] = { | ||||||
| /****************************** Configurations ******************************/ | /****************************** Configurations ******************************/ | ||||||
| 
 | 
 | ||||||
| static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; | static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; | ||||||
|  | #ifdef CONFIG_USB_GADGET_DEBUG_FILES | ||||||
|  | 
 | ||||||
|  | static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; | ||||||
|  | 
 | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Number of buffers we will use. | ||||||
|  |  * 2 is usually enough for good buffering pipeline | ||||||
|  |  */ | ||||||
|  | #define fsg_num_buffers	CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS | ||||||
|  | 
 | ||||||
|  | #endif /* CONFIG_USB_DEBUG */ | ||||||
|  | 
 | ||||||
| FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); | FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); | ||||||
| 
 | 
 | ||||||
| static struct fsg_common fsg_common; |  | ||||||
| 
 |  | ||||||
| static u8 host_mac[ETH_ALEN]; |  | ||||||
| 
 |  | ||||||
| static struct usb_function_instance *fi_acm; | static struct usb_function_instance *fi_acm; | ||||||
| static struct eth_dev *the_dev; | static struct usb_function_instance *fi_msg; | ||||||
| 
 | 
 | ||||||
| /********** RNDIS **********/ | /********** RNDIS **********/ | ||||||
| 
 | 
 | ||||||
| #ifdef USB_ETH_RNDIS | #ifdef USB_ETH_RNDIS | ||||||
|  | static struct usb_function_instance *fi_rndis; | ||||||
| static struct usb_function *f_acm_rndis; | static struct usb_function *f_acm_rndis; | ||||||
|  | static struct usb_function *f_rndis; | ||||||
|  | static struct usb_function *f_msg_rndis; | ||||||
| 
 | 
 | ||||||
| static __init int rndis_do_config(struct usb_configuration *c) | static __init int rndis_do_config(struct usb_configuration *c) | ||||||
| { | { | ||||||
|  | 	struct fsg_opts *fsg_opts; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	if (gadget_is_otg(c->cdev->gadget)) { | 	if (gadget_is_otg(c->cdev->gadget)) { | ||||||
|  | @ -155,27 +159,50 @@ static __init int rndis_do_config(struct usb_configuration *c) | ||||||
| 		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | 		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ret = rndis_bind_config(c, host_mac, the_dev); | 	f_rndis = usb_get_function(fi_rndis); | ||||||
|  | 	if (IS_ERR(f_rndis)) | ||||||
|  | 		return PTR_ERR(f_rndis); | ||||||
|  | 
 | ||||||
|  | 	ret = usb_add_function(c, f_rndis); | ||||||
| 	if (ret < 0) | 	if (ret < 0) | ||||||
| 		return ret; | 		goto err_func_rndis; | ||||||
| 
 | 
 | ||||||
| 	f_acm_rndis = usb_get_function(fi_acm); | 	f_acm_rndis = usb_get_function(fi_acm); | ||||||
| 	if (IS_ERR(f_acm_rndis)) | 	if (IS_ERR(f_acm_rndis)) { | ||||||
| 		return PTR_ERR(f_acm_rndis); | 		ret = PTR_ERR(f_acm_rndis); | ||||||
|  | 		goto err_func_acm; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	ret = usb_add_function(c, f_acm_rndis); | 	ret = usb_add_function(c, f_acm_rndis); | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		goto err_conf; | 		goto err_conf; | ||||||
| 
 | 
 | ||||||
| 	ret = fsg_bind_config(c->cdev, c, &fsg_common); | 	f_msg_rndis = usb_get_function(fi_msg); | ||||||
| 	if (ret < 0) | 	if (IS_ERR(f_msg_rndis)) { | ||||||
|  | 		ret = PTR_ERR(f_msg_rndis); | ||||||
| 		goto err_fsg; | 		goto err_fsg; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fsg_opts = fsg_opts_from_func_inst(fi_msg); | ||||||
|  | 	ret = fsg_common_run_thread(fsg_opts->common); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err_run; | ||||||
|  | 
 | ||||||
|  | 	ret = usb_add_function(c, f_msg_rndis); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err_run; | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
|  | err_run: | ||||||
|  | 	usb_put_function(f_msg_rndis); | ||||||
| err_fsg: | err_fsg: | ||||||
| 	usb_remove_function(c, f_acm_rndis); | 	usb_remove_function(c, f_acm_rndis); | ||||||
| err_conf: | err_conf: | ||||||
| 	usb_put_function(f_acm_rndis); | 	usb_put_function(f_acm_rndis); | ||||||
|  | err_func_acm: | ||||||
|  | 	usb_remove_function(c, f_rndis); | ||||||
|  | err_func_rndis: | ||||||
|  | 	usb_put_function(f_rndis); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -205,10 +232,14 @@ static __ref int rndis_config_register(struct usb_composite_dev *cdev) | ||||||
| /********** CDC ECM **********/ | /********** CDC ECM **********/ | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_USB_G_MULTI_CDC | #ifdef CONFIG_USB_G_MULTI_CDC | ||||||
|  | static struct usb_function_instance *fi_ecm; | ||||||
| static struct usb_function *f_acm_multi; | static struct usb_function *f_acm_multi; | ||||||
|  | static struct usb_function *f_ecm; | ||||||
|  | static struct usb_function *f_msg_multi; | ||||||
| 
 | 
 | ||||||
| static __init int cdc_do_config(struct usb_configuration *c) | static __init int cdc_do_config(struct usb_configuration *c) | ||||||
| { | { | ||||||
|  | 	struct fsg_opts *fsg_opts; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	if (gadget_is_otg(c->cdev->gadget)) { | 	if (gadget_is_otg(c->cdev->gadget)) { | ||||||
|  | @ -216,28 +247,51 @@ static __init int cdc_do_config(struct usb_configuration *c) | ||||||
| 		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | 		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ret = ecm_bind_config(c, host_mac, the_dev); | 	f_ecm = usb_get_function(fi_ecm); | ||||||
|  | 	if (IS_ERR(f_ecm)) | ||||||
|  | 		return PTR_ERR(f_ecm); | ||||||
|  | 
 | ||||||
|  | 	ret = usb_add_function(c, f_ecm); | ||||||
| 	if (ret < 0) | 	if (ret < 0) | ||||||
| 		return ret; | 		goto err_func_ecm; | ||||||
| 
 | 
 | ||||||
| 	/* implicit port_num is zero */ | 	/* implicit port_num is zero */ | ||||||
| 	f_acm_multi = usb_get_function(fi_acm); | 	f_acm_multi = usb_get_function(fi_acm); | ||||||
| 	if (IS_ERR(f_acm_multi)) | 	if (IS_ERR(f_acm_multi)) { | ||||||
| 		return PTR_ERR(f_acm_multi); | 		ret = PTR_ERR(f_acm_multi); | ||||||
|  | 		goto err_func_acm; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	ret = usb_add_function(c, f_acm_multi); | 	ret = usb_add_function(c, f_acm_multi); | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		goto err_conf; | 		goto err_conf; | ||||||
| 
 | 
 | ||||||
| 	ret = fsg_bind_config(c->cdev, c, &fsg_common); | 	f_msg_multi = usb_get_function(fi_msg); | ||||||
| 	if (ret < 0) | 	if (IS_ERR(f_msg_multi)) { | ||||||
|  | 		ret = PTR_ERR(f_msg_multi); | ||||||
| 		goto err_fsg; | 		goto err_fsg; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fsg_opts = fsg_opts_from_func_inst(fi_msg); | ||||||
|  | 	ret = fsg_common_run_thread(fsg_opts->common); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err_run; | ||||||
|  | 
 | ||||||
|  | 	ret = usb_add_function(c, f_msg_multi); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err_run; | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
|  | err_run: | ||||||
|  | 	usb_put_function(f_msg_multi); | ||||||
| err_fsg: | err_fsg: | ||||||
| 	usb_remove_function(c, f_acm_multi); | 	usb_remove_function(c, f_acm_multi); | ||||||
| err_conf: | err_conf: | ||||||
| 	usb_put_function(f_acm_multi); | 	usb_put_function(f_acm_multi); | ||||||
|  | err_func_acm: | ||||||
|  | 	usb_remove_function(c, f_ecm); | ||||||
|  | err_func_ecm: | ||||||
|  | 	usb_put_function(f_ecm); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -270,19 +324,67 @@ static __ref int cdc_config_register(struct usb_composite_dev *cdev) | ||||||
| static int __ref multi_bind(struct usb_composite_dev *cdev) | static int __ref multi_bind(struct usb_composite_dev *cdev) | ||||||
| { | { | ||||||
| 	struct usb_gadget *gadget = cdev->gadget; | 	struct usb_gadget *gadget = cdev->gadget; | ||||||
|  | #ifdef CONFIG_USB_G_MULTI_CDC | ||||||
|  | 	struct f_ecm_opts *ecm_opts; | ||||||
|  | #endif | ||||||
|  | #ifdef USB_ETH_RNDIS | ||||||
|  | 	struct f_rndis_opts *rndis_opts; | ||||||
|  | #endif | ||||||
|  | 	struct fsg_opts *fsg_opts; | ||||||
|  | 	struct fsg_config config; | ||||||
| 	int status; | 	int status; | ||||||
| 
 | 
 | ||||||
| 	if (!can_support_ecm(cdev->gadget)) { | 	if (!can_support_ecm(cdev->gadget)) { | ||||||
| 		dev_err(&gadget->dev, "controller '%s' not usable\n", | 		dev_err(&gadget->dev, "controller '%s' not usable\n", | ||||||
| 		        gadget->name); | 			gadget->name); | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* set up network link layer */ | #ifdef CONFIG_USB_G_MULTI_CDC | ||||||
| 	the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, host_mac, | 	fi_ecm = usb_get_function_instance("ecm"); | ||||||
| 			       qmult); | 	if (IS_ERR(fi_ecm)) | ||||||
| 	if (IS_ERR(the_dev)) | 		return PTR_ERR(fi_ecm); | ||||||
| 		return PTR_ERR(the_dev); | 
 | ||||||
|  | 	ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); | ||||||
|  | 
 | ||||||
|  | 	gether_set_qmult(ecm_opts->net, qmult); | ||||||
|  | 	if (!gether_set_host_addr(ecm_opts->net, host_addr)) | ||||||
|  | 		pr_info("using host ethernet address: %s", host_addr); | ||||||
|  | 	if (!gether_set_dev_addr(ecm_opts->net, dev_addr)) | ||||||
|  | 		pr_info("using self ethernet address: %s", dev_addr); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef USB_ETH_RNDIS | ||||||
|  | 	fi_rndis = usb_get_function_instance("rndis"); | ||||||
|  | 	if (IS_ERR(fi_rndis)) { | ||||||
|  | 		status = PTR_ERR(fi_rndis); | ||||||
|  | 		goto fail; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rndis_opts = container_of(fi_rndis, struct f_rndis_opts, func_inst); | ||||||
|  | 
 | ||||||
|  | 	gether_set_qmult(rndis_opts->net, qmult); | ||||||
|  | 	if (!gether_set_host_addr(rndis_opts->net, host_addr)) | ||||||
|  | 		pr_info("using host ethernet address: %s", host_addr); | ||||||
|  | 	if (!gether_set_dev_addr(rndis_opts->net, dev_addr)) | ||||||
|  | 		pr_info("using self ethernet address: %s", dev_addr); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if (defined CONFIG_USB_G_MULTI_CDC && defined USB_ETH_RNDIS) | ||||||
|  | 	/*
 | ||||||
|  | 	 * If both ecm and rndis are selected then: | ||||||
|  | 	 *	1) rndis borrows the net interface from ecm | ||||||
|  | 	 *	2) since the interface is shared it must not be bound | ||||||
|  | 	 *	twice - in ecm's _and_ rndis' binds, so do it here. | ||||||
|  | 	 */ | ||||||
|  | 	gether_set_gadget(ecm_opts->net, cdev->gadget); | ||||||
|  | 	status = gether_register_netdev(ecm_opts->net); | ||||||
|  | 	if (status) | ||||||
|  | 		goto fail0; | ||||||
|  | 
 | ||||||
|  | 	rndis_borrow_net(fi_rndis, ecm_opts->net); | ||||||
|  | 	ecm_opts->bound = true; | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| 	/* set up serial link layer */ | 	/* set up serial link layer */ | ||||||
| 	fi_acm = usb_get_function_instance("acm"); | 	fi_acm = usb_get_function_instance("acm"); | ||||||
|  | @ -292,49 +394,87 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* set up mass storage function */ | 	/* set up mass storage function */ | ||||||
| 	{ | 	fi_msg = usb_get_function_instance("mass_storage"); | ||||||
| 		void *retp; | 	if (IS_ERR(fi_msg)) { | ||||||
| 		retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data); | 		status = PTR_ERR(fi_msg); | ||||||
| 		if (IS_ERR(retp)) { | 		goto fail1; | ||||||
| 			status = PTR_ERR(retp); |  | ||||||
| 			goto fail1; |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  | 	fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers); | ||||||
|  | 	fsg_opts = fsg_opts_from_func_inst(fi_msg); | ||||||
|  | 
 | ||||||
|  | 	fsg_opts->no_configfs = true; | ||||||
|  | 	status = fsg_common_set_num_buffers(fsg_opts->common, fsg_num_buffers); | ||||||
|  | 	if (status) | ||||||
|  | 		goto fail2; | ||||||
|  | 
 | ||||||
|  | 	status = fsg_common_set_nluns(fsg_opts->common, config.nluns); | ||||||
|  | 	if (status) | ||||||
|  | 		goto fail_set_nluns; | ||||||
|  | 
 | ||||||
|  | 	status = fsg_common_set_cdev(fsg_opts->common, cdev, config.can_stall); | ||||||
|  | 	if (status) | ||||||
|  | 		goto fail_set_cdev; | ||||||
|  | 
 | ||||||
|  | 	fsg_common_set_sysfs(fsg_opts->common, true); | ||||||
|  | 	status = fsg_common_create_luns(fsg_opts->common, &config); | ||||||
|  | 	if (status) | ||||||
|  | 		goto fail_set_cdev; | ||||||
|  | 
 | ||||||
|  | 	fsg_common_set_inquiry_string(fsg_opts->common, config.vendor_name, | ||||||
|  | 				      config.product_name); | ||||||
| 
 | 
 | ||||||
| 	/* allocate string IDs */ | 	/* allocate string IDs */ | ||||||
| 	status = usb_string_ids_tab(cdev, strings_dev); | 	status = usb_string_ids_tab(cdev, strings_dev); | ||||||
| 	if (unlikely(status < 0)) | 	if (unlikely(status < 0)) | ||||||
| 		goto fail2; | 		goto fail_string_ids; | ||||||
| 	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; | 	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; | ||||||
| 
 | 
 | ||||||
| 	/* register configurations */ | 	/* register configurations */ | ||||||
| 	status = rndis_config_register(cdev); | 	status = rndis_config_register(cdev); | ||||||
| 	if (unlikely(status < 0)) | 	if (unlikely(status < 0)) | ||||||
| 		goto fail2; | 		goto fail_string_ids; | ||||||
| 
 | 
 | ||||||
| 	status = cdc_config_register(cdev); | 	status = cdc_config_register(cdev); | ||||||
| 	if (unlikely(status < 0)) | 	if (unlikely(status < 0)) | ||||||
| 		goto fail2; | 		goto fail_string_ids; | ||||||
| 	usb_composite_overwrite_options(cdev, &coverwrite); | 	usb_composite_overwrite_options(cdev, &coverwrite); | ||||||
| 
 | 
 | ||||||
| 	/* we're done */ | 	/* we're done */ | ||||||
| 	dev_info(&gadget->dev, DRIVER_DESC "\n"); | 	dev_info(&gadget->dev, DRIVER_DESC "\n"); | ||||||
| 	fsg_common_put(&fsg_common); |  | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 	/* error recovery */ | 	/* error recovery */ | ||||||
|  | fail_string_ids: | ||||||
|  | 	fsg_common_remove_luns(fsg_opts->common); | ||||||
|  | fail_set_cdev: | ||||||
|  | 	fsg_common_free_luns(fsg_opts->common); | ||||||
|  | fail_set_nluns: | ||||||
|  | 	fsg_common_free_buffers(fsg_opts->common); | ||||||
| fail2: | fail2: | ||||||
| 	fsg_common_put(&fsg_common); | 	usb_put_function_instance(fi_msg); | ||||||
| fail1: | fail1: | ||||||
| 	usb_put_function_instance(fi_acm); | 	usb_put_function_instance(fi_acm); | ||||||
| fail0: | fail0: | ||||||
| 	gether_cleanup(the_dev); | #ifdef USB_ETH_RNDIS | ||||||
|  | 	usb_put_function_instance(fi_rndis); | ||||||
|  | fail: | ||||||
|  | #endif | ||||||
|  | #ifdef CONFIG_USB_G_MULTI_CDC | ||||||
|  | 	usb_put_function_instance(fi_ecm); | ||||||
|  | #endif | ||||||
| 	return status; | 	return status; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int __exit multi_unbind(struct usb_composite_dev *cdev) | static int __exit multi_unbind(struct usb_composite_dev *cdev) | ||||||
| { | { | ||||||
|  | #ifdef CONFIG_USB_G_MULTI_CDC | ||||||
|  | 	usb_put_function(f_msg_multi); | ||||||
|  | #endif | ||||||
|  | #ifdef USB_ETH_RNDIS | ||||||
|  | 	usb_put_function(f_msg_rndis); | ||||||
|  | #endif | ||||||
|  | 	usb_put_function_instance(fi_msg); | ||||||
| #ifdef CONFIG_USB_G_MULTI_CDC | #ifdef CONFIG_USB_G_MULTI_CDC | ||||||
| 	usb_put_function(f_acm_multi); | 	usb_put_function(f_acm_multi); | ||||||
| #endif | #endif | ||||||
|  | @ -342,7 +482,14 @@ static int __exit multi_unbind(struct usb_composite_dev *cdev) | ||||||
| 	usb_put_function(f_acm_rndis); | 	usb_put_function(f_acm_rndis); | ||||||
| #endif | #endif | ||||||
| 	usb_put_function_instance(fi_acm); | 	usb_put_function_instance(fi_acm); | ||||||
| 	gether_cleanup(the_dev); | #ifdef USB_ETH_RNDIS | ||||||
|  | 	usb_put_function(f_rndis); | ||||||
|  | 	usb_put_function_instance(fi_rndis); | ||||||
|  | #endif | ||||||
|  | #ifdef CONFIG_USB_G_MULTI_CDC | ||||||
|  | 	usb_put_function(f_ecm); | ||||||
|  | 	usb_put_function_instance(fi_ecm); | ||||||
|  | #endif | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -310,6 +310,7 @@ static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req, | ||||||
| 	 */ | 	 */ | ||||||
| 	trb_hw = dma_pool_alloc(u3d->trb_pool, GFP_ATOMIC, dma); | 	trb_hw = dma_pool_alloc(u3d->trb_pool, GFP_ATOMIC, dma); | ||||||
| 	if (!trb_hw) { | 	if (!trb_hw) { | ||||||
|  | 		kfree(trb); | ||||||
| 		dev_err(u3d->dev, | 		dev_err(u3d->dev, | ||||||
| 			"%s, dma_pool_alloc fail\n", __func__); | 			"%s, dma_pool_alloc fail\n", __func__); | ||||||
| 		return NULL; | 		return NULL; | ||||||
|  | @ -454,6 +455,7 @@ static int mv_u3d_req_to_trb(struct mv_u3d_req *req) | ||||||
| 
 | 
 | ||||||
| 		trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC); | 		trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC); | ||||||
| 		if (!trb_hw) { | 		if (!trb_hw) { | ||||||
|  | 			kfree(trb); | ||||||
| 			dev_err(u3d->dev, | 			dev_err(u3d->dev, | ||||||
| 					"%s, trb_hw alloc fail\n", __func__); | 					"%s, trb_hw alloc fail\n", __func__); | ||||||
| 			return -ENOMEM; | 			return -ENOMEM; | ||||||
|  |  | ||||||
|  | @ -83,9 +83,12 @@ struct s3c_hsotg_req; | ||||||
|  * @dir_in: Set to true if this endpoint is of the IN direction, which |  * @dir_in: Set to true if this endpoint is of the IN direction, which | ||||||
|  *	    means that it is sending data to the Host. |  *	    means that it is sending data to the Host. | ||||||
|  * @index: The index for the endpoint registers. |  * @index: The index for the endpoint registers. | ||||||
|  |  * @mc: Multi Count - number of transactions per microframe | ||||||
|  |  * @interval - Interval for periodic endpoints | ||||||
|  * @name: The name array passed to the USB core. |  * @name: The name array passed to the USB core. | ||||||
|  * @halted: Set if the endpoint has been halted. |  * @halted: Set if the endpoint has been halted. | ||||||
|  * @periodic: Set if this is a periodic ep, such as Interrupt |  * @periodic: Set if this is a periodic ep, such as Interrupt | ||||||
|  |  * @isochronous: Set if this is a isochronous ep | ||||||
|  * @sent_zlp: Set if we've sent a zero-length packet. |  * @sent_zlp: Set if we've sent a zero-length packet. | ||||||
|  * @total_data: The total number of data bytes done. |  * @total_data: The total number of data bytes done. | ||||||
|  * @fifo_size: The size of the FIFO (for periodic IN endpoints) |  * @fifo_size: The size of the FIFO (for periodic IN endpoints) | ||||||
|  | @ -121,9 +124,12 @@ struct s3c_hsotg_ep { | ||||||
| 
 | 
 | ||||||
| 	unsigned char		dir_in; | 	unsigned char		dir_in; | ||||||
| 	unsigned char		index; | 	unsigned char		index; | ||||||
|  | 	unsigned char		mc; | ||||||
|  | 	unsigned char		interval; | ||||||
| 
 | 
 | ||||||
| 	unsigned int		halted:1; | 	unsigned int		halted:1; | ||||||
| 	unsigned int		periodic:1; | 	unsigned int		periodic:1; | ||||||
|  | 	unsigned int		isochronous:1; | ||||||
| 	unsigned int		sent_zlp:1; | 	unsigned int		sent_zlp:1; | ||||||
| 
 | 
 | ||||||
| 	char			name[10]; | 	char			name[10]; | ||||||
|  | @ -468,6 +474,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, | ||||||
| 	void *data; | 	void *data; | ||||||
| 	int can_write; | 	int can_write; | ||||||
| 	int pkt_round; | 	int pkt_round; | ||||||
|  | 	int max_transfer; | ||||||
| 
 | 
 | ||||||
| 	to_write -= (buf_pos - hs_ep->last_load); | 	to_write -= (buf_pos - hs_ep->last_load); | ||||||
| 
 | 
 | ||||||
|  | @ -535,8 +542,10 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, | ||||||
| 		can_write *= 4;	/* fifo size is in 32bit quantities. */ | 		can_write *= 4;	/* fifo size is in 32bit quantities. */ | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n", | 	max_transfer = hs_ep->ep.maxpacket * hs_ep->mc; | ||||||
| 		 __func__, gnptxsts, can_write, to_write, hs_ep->ep.maxpacket); | 
 | ||||||
|  | 	dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, max_transfer %d\n", | ||||||
|  | 		 __func__, gnptxsts, can_write, to_write, max_transfer); | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * limit to 512 bytes of data, it seems at least on the non-periodic | 	 * limit to 512 bytes of data, it seems at least on the non-periodic | ||||||
|  | @ -551,19 +560,21 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, | ||||||
| 	 * the transfer to return that it did not run out of fifo space | 	 * the transfer to return that it did not run out of fifo space | ||||||
| 	 * doing it. | 	 * doing it. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (to_write > hs_ep->ep.maxpacket) { | 	if (to_write > max_transfer) { | ||||||
| 		to_write = hs_ep->ep.maxpacket; | 		to_write = max_transfer; | ||||||
| 
 | 
 | ||||||
| 		s3c_hsotg_en_gsint(hsotg, | 		/* it's needed only when we do not use dedicated fifos */ | ||||||
| 				   periodic ? GINTSTS_PTxFEmp : | 		if (!hsotg->dedicated_fifos) | ||||||
| 				   GINTSTS_NPTxFEmp); | 			s3c_hsotg_en_gsint(hsotg, | ||||||
|  | 					   periodic ? GINTSTS_PTxFEmp : | ||||||
|  | 					   GINTSTS_NPTxFEmp); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* see if we can write data */ | 	/* see if we can write data */ | ||||||
| 
 | 
 | ||||||
| 	if (to_write > can_write) { | 	if (to_write > can_write) { | ||||||
| 		to_write = can_write; | 		to_write = can_write; | ||||||
| 		pkt_round = to_write % hs_ep->ep.maxpacket; | 		pkt_round = to_write % max_transfer; | ||||||
| 
 | 
 | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * Round the write down to an | 		 * Round the write down to an | ||||||
|  | @ -581,9 +592,11 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, | ||||||
| 		 * is more room left. | 		 * is more room left. | ||||||
| 		 */ | 		 */ | ||||||
| 
 | 
 | ||||||
| 		s3c_hsotg_en_gsint(hsotg, | 		/* it's needed only when we do not use dedicated fifos */ | ||||||
| 				   periodic ? GINTSTS_PTxFEmp : | 		if (!hsotg->dedicated_fifos) | ||||||
| 				   GINTSTS_NPTxFEmp); | 			s3c_hsotg_en_gsint(hsotg, | ||||||
|  | 					   periodic ? GINTSTS_PTxFEmp : | ||||||
|  | 					   GINTSTS_NPTxFEmp); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	dev_dbg(hsotg->dev, "write %d/%d, can_write %d, done %d\n", | 	dev_dbg(hsotg->dev, "write %d/%d, can_write %d, done %d\n", | ||||||
|  | @ -727,8 +740,16 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, | ||||||
| 	else | 	else | ||||||
| 		packets = 1;	/* send one packet if length is zero. */ | 		packets = 1;	/* send one packet if length is zero. */ | ||||||
| 
 | 
 | ||||||
|  | 	if (hs_ep->isochronous && length > (hs_ep->mc * hs_ep->ep.maxpacket)) { | ||||||
|  | 		dev_err(hsotg->dev, "req length > maxpacket*mc\n"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if (dir_in && index != 0) | 	if (dir_in && index != 0) | ||||||
| 		epsize = DxEPTSIZ_MC(1); | 		if (hs_ep->isochronous) | ||||||
|  | 			epsize = DxEPTSIZ_MC(packets); | ||||||
|  | 		else | ||||||
|  | 			epsize = DxEPTSIZ_MC(1); | ||||||
| 	else | 	else | ||||||
| 		epsize = 0; | 		epsize = 0; | ||||||
| 
 | 
 | ||||||
|  | @ -820,6 +841,9 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, | ||||||
| 
 | 
 | ||||||
| 	dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", | 	dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", | ||||||
| 		__func__, readl(hsotg->regs + epctrl_reg)); | 		__func__, readl(hsotg->regs + epctrl_reg)); | ||||||
|  | 
 | ||||||
|  | 	/* enable ep interrupts */ | ||||||
|  | 	s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -1091,6 +1115,7 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, | ||||||
| 	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); | 	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); | ||||||
| 	struct s3c_hsotg_ep *ep; | 	struct s3c_hsotg_ep *ep; | ||||||
| 	int ret; | 	int ret; | ||||||
|  | 	bool halted; | ||||||
| 
 | 
 | ||||||
| 	dev_dbg(hsotg->dev, "%s: %s_FEATURE\n", | 	dev_dbg(hsotg->dev, "%s: %s_FEATURE\n", | ||||||
| 		__func__, set ? "SET" : "CLEAR"); | 		__func__, set ? "SET" : "CLEAR"); | ||||||
|  | @ -1105,6 +1130,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, | ||||||
| 
 | 
 | ||||||
| 		switch (le16_to_cpu(ctrl->wValue)) { | 		switch (le16_to_cpu(ctrl->wValue)) { | ||||||
| 		case USB_ENDPOINT_HALT: | 		case USB_ENDPOINT_HALT: | ||||||
|  | 			halted = ep->halted; | ||||||
|  | 
 | ||||||
| 			s3c_hsotg_ep_sethalt(&ep->ep, set); | 			s3c_hsotg_ep_sethalt(&ep->ep, set); | ||||||
| 
 | 
 | ||||||
| 			ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); | 			ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); | ||||||
|  | @ -1114,7 +1141,12 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, | ||||||
| 				return ret; | 				return ret; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (!set) { | 			/*
 | ||||||
|  | 			 * we have to complete all requests for ep if it was | ||||||
|  | 			 * halted, and the halt was cleared by CLEAR_FEATURE | ||||||
|  | 			 */ | ||||||
|  | 
 | ||||||
|  | 			if (!set && halted) { | ||||||
| 				/*
 | 				/*
 | ||||||
| 				 * If we have request in progress, | 				 * If we have request in progress, | ||||||
| 				 * then complete it | 				 * then complete it | ||||||
|  | @ -1147,6 +1179,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, | ||||||
| 	return 1; | 	return 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * s3c_hsotg_process_control - process a control request |  * s3c_hsotg_process_control - process a control request | ||||||
|  * @hsotg: The device state |  * @hsotg: The device state | ||||||
|  | @ -1246,11 +1280,15 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg, | ||||||
| 		 * don't believe we need to anything more to get the EP | 		 * don't believe we need to anything more to get the EP | ||||||
| 		 * to reply with a STALL packet | 		 * to reply with a STALL packet | ||||||
| 		 */ | 		 */ | ||||||
|  | 
 | ||||||
|  | 		 /*
 | ||||||
|  | 		  * complete won't be called, so we enqueue | ||||||
|  | 		  * setup request here | ||||||
|  | 		  */ | ||||||
|  | 		 s3c_hsotg_enqueue_setup(hsotg); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg); |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * s3c_hsotg_complete_setup - completion of a setup transfer |  * s3c_hsotg_complete_setup - completion of a setup transfer | ||||||
|  * @ep: The endpoint the request was on. |  * @ep: The endpoint the request was on. | ||||||
|  | @ -1698,6 +1736,7 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg, | ||||||
| 	struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep]; | 	struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep]; | ||||||
| 	void __iomem *regs = hsotg->regs; | 	void __iomem *regs = hsotg->regs; | ||||||
| 	u32 mpsval; | 	u32 mpsval; | ||||||
|  | 	u32 mcval; | ||||||
| 	u32 reg; | 	u32 reg; | ||||||
| 
 | 
 | ||||||
| 	if (ep == 0) { | 	if (ep == 0) { | ||||||
|  | @ -1705,15 +1744,19 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg, | ||||||
| 		mpsval = s3c_hsotg_ep0_mps(mps); | 		mpsval = s3c_hsotg_ep0_mps(mps); | ||||||
| 		if (mpsval > 3) | 		if (mpsval > 3) | ||||||
| 			goto bad_mps; | 			goto bad_mps; | ||||||
|  | 		hs_ep->ep.maxpacket = mps; | ||||||
|  | 		hs_ep->mc = 1; | ||||||
| 	} else { | 	} else { | ||||||
| 		if (mps >= DxEPCTL_MPS_LIMIT+1) | 		mpsval = mps & DxEPCTL_MPS_MASK; | ||||||
|  | 		if (mpsval > 1024) | ||||||
| 			goto bad_mps; | 			goto bad_mps; | ||||||
| 
 | 		mcval = ((mps >> 11) & 0x3) + 1; | ||||||
| 		mpsval = mps; | 		hs_ep->mc = mcval; | ||||||
|  | 		if (mcval > 3) | ||||||
|  | 			goto bad_mps; | ||||||
|  | 		hs_ep->ep.maxpacket = mpsval; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	hs_ep->ep.maxpacket = mps; |  | ||||||
| 
 |  | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * update both the in and out endpoint controldir_ registers, even | 	 * update both the in and out endpoint controldir_ registers, even | ||||||
| 	 * if one of the directions may not be in use. | 	 * if one of the directions may not be in use. | ||||||
|  | @ -1782,8 +1825,16 @@ static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg, | ||||||
| { | { | ||||||
| 	struct s3c_hsotg_req *hs_req = hs_ep->req; | 	struct s3c_hsotg_req *hs_req = hs_ep->req; | ||||||
| 
 | 
 | ||||||
| 	if (!hs_ep->dir_in || !hs_req) | 	if (!hs_ep->dir_in || !hs_req) { | ||||||
|  | 		/**
 | ||||||
|  | 		 * if request is not enqueued, we disable interrupts | ||||||
|  | 		 * for endpoints, excepting ep0 | ||||||
|  | 		 */ | ||||||
|  | 		if (hs_ep->index != 0) | ||||||
|  | 			s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, | ||||||
|  | 					     hs_ep->dir_in, 0); | ||||||
| 		return 0; | 		return 0; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if (hs_req->req.actual < hs_req->req.length) { | 	if (hs_req->req.actual < hs_req->req.length) { | ||||||
| 		dev_dbg(hsotg->dev, "trying to write more for ep%d\n", | 		dev_dbg(hsotg->dev, "trying to write more for ep%d\n", | ||||||
|  | @ -1887,8 +1938,10 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, | ||||||
| 	u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); | 	u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); | ||||||
| 	u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx); | 	u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx); | ||||||
| 	u32 ints; | 	u32 ints; | ||||||
|  | 	u32 ctrl; | ||||||
| 
 | 
 | ||||||
| 	ints = readl(hsotg->regs + epint_reg); | 	ints = readl(hsotg->regs + epint_reg); | ||||||
|  | 	ctrl = readl(hsotg->regs + epctl_reg); | ||||||
| 
 | 
 | ||||||
| 	/* Clear endpoint interrupts */ | 	/* Clear endpoint interrupts */ | ||||||
| 	writel(ints, hsotg->regs + epint_reg); | 	writel(ints, hsotg->regs + epint_reg); | ||||||
|  | @ -1897,6 +1950,14 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, | ||||||
| 		__func__, idx, dir_in ? "in" : "out", ints); | 		__func__, idx, dir_in ? "in" : "out", ints); | ||||||
| 
 | 
 | ||||||
| 	if (ints & DxEPINT_XferCompl) { | 	if (ints & DxEPINT_XferCompl) { | ||||||
|  | 		if (hs_ep->isochronous && hs_ep->interval == 1) { | ||||||
|  | 			if (ctrl & DxEPCTL_EOFrNum) | ||||||
|  | 				ctrl |= DxEPCTL_SetEvenFr; | ||||||
|  | 			else | ||||||
|  | 				ctrl |= DxEPCTL_SetOddFr; | ||||||
|  | 			writel(ctrl, hsotg->regs + epctl_reg); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		dev_dbg(hsotg->dev, | 		dev_dbg(hsotg->dev, | ||||||
| 			"%s: XferCompl: DxEPCTL=0x%08x, DxEPTSIZ=%08x\n", | 			"%s: XferCompl: DxEPCTL=0x%08x, DxEPTSIZ=%08x\n", | ||||||
| 			__func__, readl(hsotg->regs + epctl_reg), | 			__func__, readl(hsotg->regs + epctl_reg), | ||||||
|  | @ -1963,7 +2024,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, | ||||||
| 	if (ints & DxEPINT_Back2BackSetup) | 	if (ints & DxEPINT_Back2BackSetup) | ||||||
| 		dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__); | 		dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__); | ||||||
| 
 | 
 | ||||||
| 	if (dir_in) { | 	if (dir_in && !hs_ep->isochronous) { | ||||||
| 		/* not sure if this is important, but we'll clear it anyway */ | 		/* not sure if this is important, but we'll clear it anyway */ | ||||||
| 		if (ints & DIEPMSK_INTknTXFEmpMsk) { | 		if (ints & DIEPMSK_INTknTXFEmpMsk) { | ||||||
| 			dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n", | 			dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n", | ||||||
|  | @ -2092,12 +2153,14 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #define call_gadget(_hs, _entry) \ | #define call_gadget(_hs, _entry) \ | ||||||
|  | do { \ | ||||||
| 	if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN &&	\ | 	if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN &&	\ | ||||||
| 	    (_hs)->driver && (_hs)->driver->_entry) { \ | 	    (_hs)->driver && (_hs)->driver->_entry) { \ | ||||||
| 		spin_unlock(&_hs->lock); \ | 		spin_unlock(&_hs->lock); \ | ||||||
| 		(_hs)->driver->_entry(&(_hs)->gadget); \ | 		(_hs)->driver->_entry(&(_hs)->gadget); \ | ||||||
| 		spin_lock(&_hs->lock); \ | 		spin_lock(&_hs->lock); \ | ||||||
| 		} | 	} \ | ||||||
|  | } while (0) | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * s3c_hsotg_disconnect - disconnect service |  * s3c_hsotg_disconnect - disconnect service | ||||||
|  | @ -2241,15 +2304,19 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg) | ||||||
| 		       GAHBCFG_HBstLen_Incr4, | 		       GAHBCFG_HBstLen_Incr4, | ||||||
| 		       hsotg->regs + GAHBCFG); | 		       hsotg->regs + GAHBCFG); | ||||||
| 	else | 	else | ||||||
| 		writel(GAHBCFG_GlblIntrEn, hsotg->regs + GAHBCFG); | 		writel(((hsotg->dedicated_fifos) ? (GAHBCFG_NPTxFEmpLvl | | ||||||
|  | 						    GAHBCFG_PTxFEmpLvl) : 0) | | ||||||
|  | 		       GAHBCFG_GlblIntrEn, | ||||||
|  | 		       hsotg->regs + GAHBCFG); | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Enabling INTknTXFEmpMsk here seems to be a big mistake, we end | 	 * If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts | ||||||
| 	 * up being flooded with interrupts if the host is polling the | 	 * when we have no data to transfer. Otherwise we get being flooded by | ||||||
| 	 * endpoint to try and read data. | 	 * interrupts. | ||||||
| 	 */ | 	 */ | ||||||
| 
 | 
 | ||||||
| 	writel(((hsotg->dedicated_fifos) ? DIEPMSK_TxFIFOEmpty : 0) | | 	writel(((hsotg->dedicated_fifos) ? DIEPMSK_TxFIFOEmpty | | ||||||
|  | 	       DIEPMSK_INTknTXFEmpMsk : 0) | | ||||||
| 	       DIEPMSK_EPDisbldMsk | DIEPMSK_XferComplMsk | | 	       DIEPMSK_EPDisbldMsk | DIEPMSK_XferComplMsk | | ||||||
| 	       DIEPMSK_TimeOUTMsk | DIEPMSK_AHBErrMsk | | 	       DIEPMSK_TimeOUTMsk | DIEPMSK_AHBErrMsk | | ||||||
| 	       DIEPMSK_INTknEPMisMsk, | 	       DIEPMSK_INTknEPMisMsk, | ||||||
|  | @ -2378,10 +2445,14 @@ irq_retry: | ||||||
| 
 | 
 | ||||||
| 	if (gintsts & (GINTSTS_OEPInt | GINTSTS_IEPInt)) { | 	if (gintsts & (GINTSTS_OEPInt | GINTSTS_IEPInt)) { | ||||||
| 		u32 daint = readl(hsotg->regs + DAINT); | 		u32 daint = readl(hsotg->regs + DAINT); | ||||||
| 		u32 daint_out = daint >> DAINT_OutEP_SHIFT; | 		u32 daintmsk = readl(hsotg->regs + DAINTMSK); | ||||||
| 		u32 daint_in = daint & ~(daint_out << DAINT_OutEP_SHIFT); | 		u32 daint_out, daint_in; | ||||||
| 		int ep; | 		int ep; | ||||||
| 
 | 
 | ||||||
|  | 		daint &= daintmsk; | ||||||
|  | 		daint_out = daint >> DAINT_OutEP_SHIFT; | ||||||
|  | 		daint_in = daint & ~(daint_out << DAINT_OutEP_SHIFT); | ||||||
|  | 
 | ||||||
| 		dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint); | 		dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint); | ||||||
| 
 | 
 | ||||||
| 		for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) { | 		for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) { | ||||||
|  | @ -2577,16 +2648,25 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, | ||||||
| 	epctrl |= DxEPCTL_SNAK; | 	epctrl |= DxEPCTL_SNAK; | ||||||
| 
 | 
 | ||||||
| 	/* update the endpoint state */ | 	/* update the endpoint state */ | ||||||
| 	hs_ep->ep.maxpacket = mps; | 	s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps); | ||||||
| 
 | 
 | ||||||
| 	/* default, set to non-periodic */ | 	/* default, set to non-periodic */ | ||||||
|  | 	hs_ep->isochronous = 0; | ||||||
| 	hs_ep->periodic = 0; | 	hs_ep->periodic = 0; | ||||||
|  | 	hs_ep->halted = 0; | ||||||
|  | 	hs_ep->interval = desc->bInterval; | ||||||
|  | 
 | ||||||
|  | 	if (hs_ep->interval > 1 && hs_ep->mc > 1) | ||||||
|  | 		dev_err(hsotg->dev, "MC > 1 when interval is not 1\n"); | ||||||
| 
 | 
 | ||||||
| 	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { | 	switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { | ||||||
| 	case USB_ENDPOINT_XFER_ISOC: | 	case USB_ENDPOINT_XFER_ISOC: | ||||||
| 		dev_err(hsotg->dev, "no current ISOC support\n"); | 		epctrl |= DxEPCTL_EPType_Iso; | ||||||
| 		ret = -EINVAL; | 		epctrl |= DxEPCTL_SetEvenFr; | ||||||
| 		goto out; | 		hs_ep->isochronous = 1; | ||||||
|  | 		if (dir_in) | ||||||
|  | 			hs_ep->periodic = 1; | ||||||
|  | 		break; | ||||||
| 
 | 
 | ||||||
| 	case USB_ENDPOINT_XFER_BULK: | 	case USB_ENDPOINT_XFER_BULK: | ||||||
| 		epctrl |= DxEPCTL_EPType_Bulk; | 		epctrl |= DxEPCTL_EPType_Bulk; | ||||||
|  | @ -2634,7 +2714,6 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, | ||||||
| 	/* enable the endpoint interrupt */ | 	/* enable the endpoint interrupt */ | ||||||
| 	s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1); | 	s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1); | ||||||
| 
 | 
 | ||||||
| out: |  | ||||||
| 	spin_unlock_irqrestore(&hsotg->lock, flags); | 	spin_unlock_irqrestore(&hsotg->lock, flags); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  | @ -2776,6 +2855,8 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) | ||||||
| 
 | 
 | ||||||
| 	writel(epctl, hs->regs + epreg); | 	writel(epctl, hs->regs + epreg); | ||||||
| 
 | 
 | ||||||
|  | 	hs_ep->halted = value; | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2903,7 +2984,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	if (!hsotg) { | 	if (!hsotg) { | ||||||
| 		printk(KERN_ERR "%s: called with no device\n", __func__); | 		pr_err("%s: called with no device\n", __func__); | ||||||
| 		return -ENODEV; | 		return -ENODEV; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -3066,7 +3147,7 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, | ||||||
| 
 | 
 | ||||||
| 	hs_ep->parent = hsotg; | 	hs_ep->parent = hsotg; | ||||||
| 	hs_ep->ep.name = hs_ep->name; | 	hs_ep->ep.name = hs_ep->name; | ||||||
| 	hs_ep->ep.maxpacket = epnum ? 512 : EP0_MPS_LIMIT; | 	hs_ep->ep.maxpacket = epnum ? 1024 : EP0_MPS_LIMIT; | ||||||
| 	hs_ep->ep.ops = &s3c_hsotg_ep_ops; | 	hs_ep->ep.ops = &s3c_hsotg_ep_ops; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
|  | @ -3200,7 +3281,7 @@ static int state_show(struct seq_file *seq, void *v) | ||||||
| 		   readl(regs + GNPTXSTS), | 		   readl(regs + GNPTXSTS), | ||||||
| 		   readl(regs + GRXSTSR)); | 		   readl(regs + GRXSTSR)); | ||||||
| 
 | 
 | ||||||
| 	seq_printf(seq, "\nEndpoint status:\n"); | 	seq_puts(seq, "\nEndpoint status:\n"); | ||||||
| 
 | 
 | ||||||
| 	for (idx = 0; idx < 15; idx++) { | 	for (idx = 0; idx < 15; idx++) { | ||||||
| 		u32 in, out; | 		u32 in, out; | ||||||
|  | @ -3217,7 +3298,7 @@ static int state_show(struct seq_file *seq, void *v) | ||||||
| 		seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", | 		seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", | ||||||
| 			   in, out); | 			   in, out); | ||||||
| 
 | 
 | ||||||
| 		seq_printf(seq, "\n"); | 		seq_puts(seq, "\n"); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -3251,7 +3332,7 @@ static int fifo_show(struct seq_file *seq, void *v) | ||||||
| 	u32 val; | 	u32 val; | ||||||
| 	int idx; | 	int idx; | ||||||
| 
 | 
 | ||||||
| 	seq_printf(seq, "Non-periodic FIFOs:\n"); | 	seq_puts(seq, "Non-periodic FIFOs:\n"); | ||||||
| 	seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); | 	seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); | ||||||
| 
 | 
 | ||||||
| 	val = readl(regs + GNPTXFSIZ); | 	val = readl(regs + GNPTXFSIZ); | ||||||
|  | @ -3259,7 +3340,7 @@ static int fifo_show(struct seq_file *seq, void *v) | ||||||
| 		   val >> GNPTXFSIZ_NPTxFDep_SHIFT, | 		   val >> GNPTXFSIZ_NPTxFDep_SHIFT, | ||||||
| 		   val & GNPTXFSIZ_NPTxFStAddr_MASK); | 		   val & GNPTXFSIZ_NPTxFStAddr_MASK); | ||||||
| 
 | 
 | ||||||
| 	seq_printf(seq, "\nPeriodic TXFIFOs:\n"); | 	seq_puts(seq, "\nPeriodic TXFIFOs:\n"); | ||||||
| 
 | 
 | ||||||
| 	for (idx = 1; idx <= 15; idx++) { | 	for (idx = 1; idx <= 15; idx++) { | ||||||
| 		val = readl(regs + DPTXFSIZn(idx)); | 		val = readl(regs + DPTXFSIZn(idx)); | ||||||
|  | @ -3330,7 +3411,7 @@ static int ep_show(struct seq_file *seq, void *v) | ||||||
| 		   readl(regs + DIEPTSIZ(index)), | 		   readl(regs + DIEPTSIZ(index)), | ||||||
| 		   readl(regs + DOEPTSIZ(index))); | 		   readl(regs + DOEPTSIZ(index))); | ||||||
| 
 | 
 | ||||||
| 	seq_printf(seq, "\n"); | 	seq_puts(seq, "\n"); | ||||||
| 	seq_printf(seq, "mps %d\n", ep->ep.maxpacket); | 	seq_printf(seq, "mps %d\n", ep->ep.maxpacket); | ||||||
| 	seq_printf(seq, "total_data=%ld\n", ep->total_data); | 	seq_printf(seq, "total_data=%ld\n", ep->total_data); | ||||||
| 
 | 
 | ||||||
|  | @ -3341,7 +3422,7 @@ static int ep_show(struct seq_file *seq, void *v) | ||||||
| 
 | 
 | ||||||
| 	list_for_each_entry(req, &ep->queue, queue) { | 	list_for_each_entry(req, &ep->queue, queue) { | ||||||
| 		if (--show_limit < 0) { | 		if (--show_limit < 0) { | ||||||
| 			seq_printf(seq, "not showing more requests...\n"); | 			seq_puts(seq, "not showing more requests...\n"); | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -23,242 +23,17 @@ | ||||||
|  * The valid range of num_buffers is: num >= 2 && num <= 4. |  * The valid range of num_buffers is: num >= 2 && num <= 4. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | #include <linux/module.h> | ||||||
|  | #include <linux/blkdev.h> | ||||||
|  | #include <linux/file.h> | ||||||
|  | #include <linux/fs.h> | ||||||
|  | #include <linux/usb/composite.h> | ||||||
| 
 | 
 | ||||||
| #include <linux/usb/storage.h> | #include "storage_common.h" | ||||||
| #include <scsi/scsi.h> |  | ||||||
| #include <asm/unaligned.h> |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /*
 |  | ||||||
|  * Thanks to NetChip Technologies for donating this product ID. |  | ||||||
|  * |  | ||||||
|  * DO NOT REUSE THESE IDs with any other driver!!  Ever!! |  | ||||||
|  * Instead:  allocate your own, using normal USB-IF procedures. |  | ||||||
|  */ |  | ||||||
| #define FSG_VENDOR_ID	0x0525	/* NetChip */ |  | ||||||
| #define FSG_PRODUCT_ID	0xa4a5	/* Linux-USB File-backed Storage Gadget */ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /*-------------------------------------------------------------------------*/ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #ifndef DEBUG |  | ||||||
| #undef VERBOSE_DEBUG |  | ||||||
| #undef DUMP_MSGS |  | ||||||
| #endif /* !DEBUG */ |  | ||||||
| 
 |  | ||||||
| #ifdef VERBOSE_DEBUG |  | ||||||
| #define VLDBG	LDBG |  | ||||||
| #else |  | ||||||
| #define VLDBG(lun, fmt, args...) do { } while (0) |  | ||||||
| #endif /* VERBOSE_DEBUG */ |  | ||||||
| 
 |  | ||||||
| #define LDBG(lun, fmt, args...)   dev_dbg (&(lun)->dev, fmt, ## args) |  | ||||||
| #define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args) |  | ||||||
| #define LWARN(lun, fmt, args...)  dev_warn(&(lun)->dev, fmt, ## args) |  | ||||||
| #define LINFO(lun, fmt, args...)  dev_info(&(lun)->dev, fmt, ## args) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #ifdef DUMP_MSGS |  | ||||||
| 
 |  | ||||||
| #  define dump_msg(fsg, /* const char * */ label,			\ |  | ||||||
| 		   /* const u8 * */ buf, /* unsigned */ length) do {	\ |  | ||||||
| 	if (length < 512) {						\ |  | ||||||
| 		DBG(fsg, "%s, length %u:\n", label, length);		\ |  | ||||||
| 		print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,	\ |  | ||||||
| 			       16, 1, buf, length, 0);			\ |  | ||||||
| 	}								\ |  | ||||||
| } while (0) |  | ||||||
| 
 |  | ||||||
| #  define dump_cdb(fsg) do { } while (0) |  | ||||||
| 
 |  | ||||||
| #else |  | ||||||
| 
 |  | ||||||
| #  define dump_msg(fsg, /* const char * */ label, \ |  | ||||||
| 		   /* const u8 * */ buf, /* unsigned */ length) do { } while (0) |  | ||||||
| 
 |  | ||||||
| #  ifdef VERBOSE_DEBUG |  | ||||||
| 
 |  | ||||||
| #    define dump_cdb(fsg)						\ |  | ||||||
| 	print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE,	\ |  | ||||||
| 		       16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0)		\ |  | ||||||
| 
 |  | ||||||
| #  else |  | ||||||
| 
 |  | ||||||
| #    define dump_cdb(fsg) do { } while (0) |  | ||||||
| 
 |  | ||||||
| #  endif /* VERBOSE_DEBUG */ |  | ||||||
| 
 |  | ||||||
| #endif /* DUMP_MSGS */ |  | ||||||
| 
 |  | ||||||
| /*-------------------------------------------------------------------------*/ |  | ||||||
| 
 |  | ||||||
| /* Length of a SCSI Command Data Block */ |  | ||||||
| #define MAX_COMMAND_SIZE	16 |  | ||||||
| 
 |  | ||||||
| /* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ |  | ||||||
| #define SS_NO_SENSE				0 |  | ||||||
| #define SS_COMMUNICATION_FAILURE		0x040800 |  | ||||||
| #define SS_INVALID_COMMAND			0x052000 |  | ||||||
| #define SS_INVALID_FIELD_IN_CDB			0x052400 |  | ||||||
| #define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE	0x052100 |  | ||||||
| #define SS_LOGICAL_UNIT_NOT_SUPPORTED		0x052500 |  | ||||||
| #define SS_MEDIUM_NOT_PRESENT			0x023a00 |  | ||||||
| #define SS_MEDIUM_REMOVAL_PREVENTED		0x055302 |  | ||||||
| #define SS_NOT_READY_TO_READY_TRANSITION	0x062800 |  | ||||||
| #define SS_RESET_OCCURRED			0x062900 |  | ||||||
| #define SS_SAVING_PARAMETERS_NOT_SUPPORTED	0x053900 |  | ||||||
| #define SS_UNRECOVERED_READ_ERROR		0x031100 |  | ||||||
| #define SS_WRITE_ERROR				0x030c02 |  | ||||||
| #define SS_WRITE_PROTECTED			0x072700 |  | ||||||
| 
 |  | ||||||
| #define SK(x)		((u8) ((x) >> 16))	/* Sense Key byte, etc. */ |  | ||||||
| #define ASC(x)		((u8) ((x) >> 8)) |  | ||||||
| #define ASCQ(x)		((u8) (x)) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /*-------------------------------------------------------------------------*/ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| struct fsg_lun { |  | ||||||
| 	struct file	*filp; |  | ||||||
| 	loff_t		file_length; |  | ||||||
| 	loff_t		num_sectors; |  | ||||||
| 
 |  | ||||||
| 	unsigned int	initially_ro:1; |  | ||||||
| 	unsigned int	ro:1; |  | ||||||
| 	unsigned int	removable:1; |  | ||||||
| 	unsigned int	cdrom:1; |  | ||||||
| 	unsigned int	prevent_medium_removal:1; |  | ||||||
| 	unsigned int	registered:1; |  | ||||||
| 	unsigned int	info_valid:1; |  | ||||||
| 	unsigned int	nofua:1; |  | ||||||
| 
 |  | ||||||
| 	u32		sense_data; |  | ||||||
| 	u32		sense_data_info; |  | ||||||
| 	u32		unit_attention_data; |  | ||||||
| 
 |  | ||||||
| 	unsigned int	blkbits;	/* Bits of logical block size of bound block device */ |  | ||||||
| 	unsigned int	blksize;	/* logical block size of bound block device */ |  | ||||||
| 	struct device	dev; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static inline bool fsg_lun_is_open(struct fsg_lun *curlun) |  | ||||||
| { |  | ||||||
| 	return curlun->filp != NULL; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev) |  | ||||||
| { |  | ||||||
| 	return container_of(dev, struct fsg_lun, dev); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /* Big enough to hold our biggest descriptor */ |  | ||||||
| #define EP0_BUFSIZE	256 |  | ||||||
| #define DELAYED_STATUS	(EP0_BUFSIZE + 999)	/* An impossibly large value */ |  | ||||||
| 
 |  | ||||||
| #ifdef CONFIG_USB_GADGET_DEBUG_FILES |  | ||||||
| 
 |  | ||||||
| static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; |  | ||||||
| module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO); |  | ||||||
| MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers"); |  | ||||||
| 
 |  | ||||||
| #else |  | ||||||
| 
 |  | ||||||
| /*
 |  | ||||||
|  * Number of buffers we will use. |  | ||||||
|  * 2 is usually enough for good buffering pipeline |  | ||||||
|  */ |  | ||||||
| #define fsg_num_buffers	CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS |  | ||||||
| 
 |  | ||||||
| #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ |  | ||||||
| 
 |  | ||||||
| /* check if fsg_num_buffers is within a valid range */ |  | ||||||
| static inline int fsg_num_buffers_validate(void) |  | ||||||
| { |  | ||||||
| 	if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4) |  | ||||||
| 		return 0; |  | ||||||
| 	pr_err("fsg_num_buffers %u is out of range (%d to %d)\n", |  | ||||||
| 	       fsg_num_buffers, 2 ,4); |  | ||||||
| 	return -EINVAL; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* Default size of buffer length. */ |  | ||||||
| #define FSG_BUFLEN	((u32)16384) |  | ||||||
| 
 |  | ||||||
| /* Maximal number of LUNs supported in mass storage function */ |  | ||||||
| #define FSG_MAX_LUNS	8 |  | ||||||
| 
 |  | ||||||
| enum fsg_buffer_state { |  | ||||||
| 	BUF_STATE_EMPTY = 0, |  | ||||||
| 	BUF_STATE_FULL, |  | ||||||
| 	BUF_STATE_BUSY |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct fsg_buffhd { |  | ||||||
| 	void				*buf; |  | ||||||
| 	enum fsg_buffer_state		state; |  | ||||||
| 	struct fsg_buffhd		*next; |  | ||||||
| 
 |  | ||||||
| 	/*
 |  | ||||||
| 	 * The NetChip 2280 is faster, and handles some protocol faults |  | ||||||
| 	 * better, if we don't submit any short bulk-out read requests. |  | ||||||
| 	 * So we will record the intended request length here. |  | ||||||
| 	 */ |  | ||||||
| 	unsigned int			bulk_out_intended_length; |  | ||||||
| 
 |  | ||||||
| 	struct usb_request		*inreq; |  | ||||||
| 	int				inreq_busy; |  | ||||||
| 	struct usb_request		*outreq; |  | ||||||
| 	int				outreq_busy; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| enum fsg_state { |  | ||||||
| 	/* This one isn't used anywhere */ |  | ||||||
| 	FSG_STATE_COMMAND_PHASE = -10, |  | ||||||
| 	FSG_STATE_DATA_PHASE, |  | ||||||
| 	FSG_STATE_STATUS_PHASE, |  | ||||||
| 
 |  | ||||||
| 	FSG_STATE_IDLE = 0, |  | ||||||
| 	FSG_STATE_ABORT_BULK_OUT, |  | ||||||
| 	FSG_STATE_RESET, |  | ||||||
| 	FSG_STATE_INTERFACE_CHANGE, |  | ||||||
| 	FSG_STATE_CONFIG_CHANGE, |  | ||||||
| 	FSG_STATE_DISCONNECT, |  | ||||||
| 	FSG_STATE_EXIT, |  | ||||||
| 	FSG_STATE_TERMINATED |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| enum data_direction { |  | ||||||
| 	DATA_DIR_UNKNOWN = 0, |  | ||||||
| 	DATA_DIR_FROM_HOST, |  | ||||||
| 	DATA_DIR_TO_HOST, |  | ||||||
| 	DATA_DIR_NONE |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /*-------------------------------------------------------------------------*/ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static inline u32 get_unaligned_be24(u8 *buf) |  | ||||||
| { |  | ||||||
| 	return 0xffffff & (u32) get_unaligned_be32(buf - 1); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /*-------------------------------------------------------------------------*/ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| enum { |  | ||||||
| 	FSG_STRING_INTERFACE |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| /* There is only one interface. */ | /* There is only one interface. */ | ||||||
| 
 | 
 | ||||||
| static struct usb_interface_descriptor | struct usb_interface_descriptor fsg_intf_desc = { | ||||||
| fsg_intf_desc = { |  | ||||||
| 	.bLength =		sizeof fsg_intf_desc, | 	.bLength =		sizeof fsg_intf_desc, | ||||||
| 	.bDescriptorType =	USB_DT_INTERFACE, | 	.bDescriptorType =	USB_DT_INTERFACE, | ||||||
| 
 | 
 | ||||||
|  | @ -268,14 +43,14 @@ fsg_intf_desc = { | ||||||
| 	.bInterfaceProtocol =	USB_PR_BULK,	/* Adjusted during fsg_bind() */ | 	.bInterfaceProtocol =	USB_PR_BULK,	/* Adjusted during fsg_bind() */ | ||||||
| 	.iInterface =		FSG_STRING_INTERFACE, | 	.iInterface =		FSG_STRING_INTERFACE, | ||||||
| }; | }; | ||||||
|  | EXPORT_SYMBOL(fsg_intf_desc); | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Three full-speed endpoint descriptors: bulk-in, bulk-out, and |  * Three full-speed endpoint descriptors: bulk-in, bulk-out, and | ||||||
|  * interrupt-in. |  * interrupt-in. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| static struct usb_endpoint_descriptor | struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = { | ||||||
| fsg_fs_bulk_in_desc = { |  | ||||||
| 	.bLength =		USB_DT_ENDPOINT_SIZE, | 	.bLength =		USB_DT_ENDPOINT_SIZE, | ||||||
| 	.bDescriptorType =	USB_DT_ENDPOINT, | 	.bDescriptorType =	USB_DT_ENDPOINT, | ||||||
| 
 | 
 | ||||||
|  | @ -283,9 +58,9 @@ fsg_fs_bulk_in_desc = { | ||||||
| 	.bmAttributes =		USB_ENDPOINT_XFER_BULK, | 	.bmAttributes =		USB_ENDPOINT_XFER_BULK, | ||||||
| 	/* wMaxPacketSize set by autoconfiguration */ | 	/* wMaxPacketSize set by autoconfiguration */ | ||||||
| }; | }; | ||||||
|  | EXPORT_SYMBOL(fsg_fs_bulk_in_desc); | ||||||
| 
 | 
 | ||||||
| static struct usb_endpoint_descriptor | struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = { | ||||||
| fsg_fs_bulk_out_desc = { |  | ||||||
| 	.bLength =		USB_DT_ENDPOINT_SIZE, | 	.bLength =		USB_DT_ENDPOINT_SIZE, | ||||||
| 	.bDescriptorType =	USB_DT_ENDPOINT, | 	.bDescriptorType =	USB_DT_ENDPOINT, | ||||||
| 
 | 
 | ||||||
|  | @ -293,13 +68,15 @@ fsg_fs_bulk_out_desc = { | ||||||
| 	.bmAttributes =		USB_ENDPOINT_XFER_BULK, | 	.bmAttributes =		USB_ENDPOINT_XFER_BULK, | ||||||
| 	/* wMaxPacketSize set by autoconfiguration */ | 	/* wMaxPacketSize set by autoconfiguration */ | ||||||
| }; | }; | ||||||
|  | EXPORT_SYMBOL(fsg_fs_bulk_out_desc); | ||||||
| 
 | 
 | ||||||
| static struct usb_descriptor_header *fsg_fs_function[] = { | struct usb_descriptor_header *fsg_fs_function[] = { | ||||||
| 	(struct usb_descriptor_header *) &fsg_intf_desc, | 	(struct usb_descriptor_header *) &fsg_intf_desc, | ||||||
| 	(struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, | 	(struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, | ||||||
| 	(struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, | 	(struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, | ||||||
| 	NULL, | 	NULL, | ||||||
| }; | }; | ||||||
|  | EXPORT_SYMBOL(fsg_fs_function); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -310,8 +87,7 @@ static struct usb_descriptor_header *fsg_fs_function[] = { | ||||||
|  * and a "device qualifier" ... plus more construction options |  * and a "device qualifier" ... plus more construction options | ||||||
|  * for the configuration descriptor. |  * for the configuration descriptor. | ||||||
|  */ |  */ | ||||||
| static struct usb_endpoint_descriptor | struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = { | ||||||
| fsg_hs_bulk_in_desc = { |  | ||||||
| 	.bLength =		USB_DT_ENDPOINT_SIZE, | 	.bLength =		USB_DT_ENDPOINT_SIZE, | ||||||
| 	.bDescriptorType =	USB_DT_ENDPOINT, | 	.bDescriptorType =	USB_DT_ENDPOINT, | ||||||
| 
 | 
 | ||||||
|  | @ -319,9 +95,9 @@ fsg_hs_bulk_in_desc = { | ||||||
| 	.bmAttributes =		USB_ENDPOINT_XFER_BULK, | 	.bmAttributes =		USB_ENDPOINT_XFER_BULK, | ||||||
| 	.wMaxPacketSize =	cpu_to_le16(512), | 	.wMaxPacketSize =	cpu_to_le16(512), | ||||||
| }; | }; | ||||||
|  | EXPORT_SYMBOL(fsg_hs_bulk_in_desc); | ||||||
| 
 | 
 | ||||||
| static struct usb_endpoint_descriptor | struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = { | ||||||
| fsg_hs_bulk_out_desc = { |  | ||||||
| 	.bLength =		USB_DT_ENDPOINT_SIZE, | 	.bLength =		USB_DT_ENDPOINT_SIZE, | ||||||
| 	.bDescriptorType =	USB_DT_ENDPOINT, | 	.bDescriptorType =	USB_DT_ENDPOINT, | ||||||
| 
 | 
 | ||||||
|  | @ -330,17 +106,18 @@ fsg_hs_bulk_out_desc = { | ||||||
| 	.wMaxPacketSize =	cpu_to_le16(512), | 	.wMaxPacketSize =	cpu_to_le16(512), | ||||||
| 	.bInterval =		1,	/* NAK every 1 uframe */ | 	.bInterval =		1,	/* NAK every 1 uframe */ | ||||||
| }; | }; | ||||||
|  | EXPORT_SYMBOL(fsg_hs_bulk_out_desc); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static struct usb_descriptor_header *fsg_hs_function[] = { | struct usb_descriptor_header *fsg_hs_function[] = { | ||||||
| 	(struct usb_descriptor_header *) &fsg_intf_desc, | 	(struct usb_descriptor_header *) &fsg_intf_desc, | ||||||
| 	(struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, | 	(struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, | ||||||
| 	(struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, | 	(struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, | ||||||
| 	NULL, | 	NULL, | ||||||
| }; | }; | ||||||
|  | EXPORT_SYMBOL(fsg_hs_function); | ||||||
| 
 | 
 | ||||||
| static struct usb_endpoint_descriptor | struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = { | ||||||
| fsg_ss_bulk_in_desc = { |  | ||||||
| 	.bLength =		USB_DT_ENDPOINT_SIZE, | 	.bLength =		USB_DT_ENDPOINT_SIZE, | ||||||
| 	.bDescriptorType =	USB_DT_ENDPOINT, | 	.bDescriptorType =	USB_DT_ENDPOINT, | ||||||
| 
 | 
 | ||||||
|  | @ -348,16 +125,17 @@ fsg_ss_bulk_in_desc = { | ||||||
| 	.bmAttributes =		USB_ENDPOINT_XFER_BULK, | 	.bmAttributes =		USB_ENDPOINT_XFER_BULK, | ||||||
| 	.wMaxPacketSize =	cpu_to_le16(1024), | 	.wMaxPacketSize =	cpu_to_le16(1024), | ||||||
| }; | }; | ||||||
|  | EXPORT_SYMBOL(fsg_ss_bulk_in_desc); | ||||||
| 
 | 
 | ||||||
| static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = { | struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = { | ||||||
| 	.bLength =		sizeof(fsg_ss_bulk_in_comp_desc), | 	.bLength =		sizeof(fsg_ss_bulk_in_comp_desc), | ||||||
| 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP, | 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP, | ||||||
| 
 | 
 | ||||||
| 	/*.bMaxBurst =		DYNAMIC, */ | 	/*.bMaxBurst =		DYNAMIC, */ | ||||||
| }; | }; | ||||||
|  | EXPORT_SYMBOL(fsg_ss_bulk_in_comp_desc); | ||||||
| 
 | 
 | ||||||
| static struct usb_endpoint_descriptor | struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = { | ||||||
| fsg_ss_bulk_out_desc = { |  | ||||||
| 	.bLength =		USB_DT_ENDPOINT_SIZE, | 	.bLength =		USB_DT_ENDPOINT_SIZE, | ||||||
| 	.bDescriptorType =	USB_DT_ENDPOINT, | 	.bDescriptorType =	USB_DT_ENDPOINT, | ||||||
| 
 | 
 | ||||||
|  | @ -365,15 +143,17 @@ fsg_ss_bulk_out_desc = { | ||||||
| 	.bmAttributes =		USB_ENDPOINT_XFER_BULK, | 	.bmAttributes =		USB_ENDPOINT_XFER_BULK, | ||||||
| 	.wMaxPacketSize =	cpu_to_le16(1024), | 	.wMaxPacketSize =	cpu_to_le16(1024), | ||||||
| }; | }; | ||||||
|  | EXPORT_SYMBOL(fsg_ss_bulk_out_desc); | ||||||
| 
 | 
 | ||||||
| static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = { | struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = { | ||||||
| 	.bLength =		sizeof(fsg_ss_bulk_in_comp_desc), | 	.bLength =		sizeof(fsg_ss_bulk_in_comp_desc), | ||||||
| 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP, | 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP, | ||||||
| 
 | 
 | ||||||
| 	/*.bMaxBurst =		DYNAMIC, */ | 	/*.bMaxBurst =		DYNAMIC, */ | ||||||
| }; | }; | ||||||
|  | EXPORT_SYMBOL(fsg_ss_bulk_out_comp_desc); | ||||||
| 
 | 
 | ||||||
| static struct usb_descriptor_header *fsg_ss_function[] = { | struct usb_descriptor_header *fsg_ss_function[] = { | ||||||
| 	(struct usb_descriptor_header *) &fsg_intf_desc, | 	(struct usb_descriptor_header *) &fsg_intf_desc, | ||||||
| 	(struct usb_descriptor_header *) &fsg_ss_bulk_in_desc, | 	(struct usb_descriptor_header *) &fsg_ss_bulk_in_desc, | ||||||
| 	(struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc, | 	(struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc, | ||||||
|  | @ -381,17 +161,7 @@ static struct usb_descriptor_header *fsg_ss_function[] = { | ||||||
| 	(struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc, | 	(struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc, | ||||||
| 	NULL, | 	NULL, | ||||||
| }; | }; | ||||||
| 
 | EXPORT_SYMBOL(fsg_ss_function); | ||||||
| /* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ |  | ||||||
| static struct usb_string		fsg_strings[] = { |  | ||||||
| 	{FSG_STRING_INTERFACE,		fsg_string_interface}, |  | ||||||
| 	{} |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static struct usb_gadget_strings	fsg_stringtab = { |  | ||||||
| 	.language	= 0x0409,		/* en-us */ |  | ||||||
| 	.strings	= fsg_strings, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  /*-------------------------------------------------------------------------*/ |  /*-------------------------------------------------------------------------*/ | ||||||
|  | @ -401,7 +171,7 @@ static struct usb_gadget_strings	fsg_stringtab = { | ||||||
|  * the caller must own fsg->filesem for writing. |  * the caller must own fsg->filesem for writing. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| static void fsg_lun_close(struct fsg_lun *curlun) | void fsg_lun_close(struct fsg_lun *curlun) | ||||||
| { | { | ||||||
| 	if (curlun->filp) { | 	if (curlun->filp) { | ||||||
| 		LDBG(curlun, "close backing file\n"); | 		LDBG(curlun, "close backing file\n"); | ||||||
|  | @ -409,9 +179,9 @@ static void fsg_lun_close(struct fsg_lun *curlun) | ||||||
| 		curlun->filp = NULL; | 		curlun->filp = NULL; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | EXPORT_SYMBOL(fsg_lun_close); | ||||||
| 
 | 
 | ||||||
| 
 | int fsg_lun_open(struct fsg_lun *curlun, const char *filename) | ||||||
| static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) |  | ||||||
| { | { | ||||||
| 	int				ro; | 	int				ro; | ||||||
| 	struct file			*filp = NULL; | 	struct file			*filp = NULL; | ||||||
|  | @ -508,6 +278,7 @@ out: | ||||||
| 	fput(filp); | 	fput(filp); | ||||||
| 	return rc; | 	return rc; | ||||||
| } | } | ||||||
|  | EXPORT_SYMBOL(fsg_lun_open); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /*-------------------------------------------------------------------------*/ | /*-------------------------------------------------------------------------*/ | ||||||
|  | @ -516,7 +287,7 @@ out: | ||||||
|  * Sync the file data, don't bother with the metadata. |  * Sync the file data, don't bother with the metadata. | ||||||
|  * This code was copied from fs/buffer.c:sys_fdatasync(). |  * This code was copied from fs/buffer.c:sys_fdatasync(). | ||||||
|  */ |  */ | ||||||
| static int fsg_lun_fsync_sub(struct fsg_lun *curlun) | int fsg_lun_fsync_sub(struct fsg_lun *curlun) | ||||||
| { | { | ||||||
| 	struct file	*filp = curlun->filp; | 	struct file	*filp = curlun->filp; | ||||||
| 
 | 
 | ||||||
|  | @ -524,8 +295,9 @@ static int fsg_lun_fsync_sub(struct fsg_lun *curlun) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	return vfs_fsync(filp, 1); | 	return vfs_fsync(filp, 1); | ||||||
| } | } | ||||||
|  | EXPORT_SYMBOL(fsg_lun_fsync_sub); | ||||||
| 
 | 
 | ||||||
| static void store_cdrom_address(u8 *dest, int msf, u32 addr) | void store_cdrom_address(u8 *dest, int msf, u32 addr) | ||||||
| { | { | ||||||
| 	if (msf) { | 	if (msf) { | ||||||
| 		/* Convert to Minutes-Seconds-Frames */ | 		/* Convert to Minutes-Seconds-Frames */ | ||||||
|  | @ -542,34 +314,28 @@ static void store_cdrom_address(u8 *dest, int msf, u32 addr) | ||||||
| 		put_unaligned_be32(addr, dest); | 		put_unaligned_be32(addr, dest); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | EXPORT_SYMBOL(store_cdrom_address); | ||||||
| 
 | 
 | ||||||
| /*-------------------------------------------------------------------------*/ | /*-------------------------------------------------------------------------*/ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static ssize_t ro_show(struct device *dev, struct device_attribute *attr, | ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf) | ||||||
| 		       char *buf) |  | ||||||
| { | { | ||||||
| 	struct fsg_lun	*curlun = fsg_lun_from_dev(dev); |  | ||||||
| 
 |  | ||||||
| 	return sprintf(buf, "%d\n", fsg_lun_is_open(curlun) | 	return sprintf(buf, "%d\n", fsg_lun_is_open(curlun) | ||||||
| 				  ? curlun->ro | 				  ? curlun->ro | ||||||
| 				  : curlun->initially_ro); | 				  : curlun->initially_ro); | ||||||
| } | } | ||||||
|  | EXPORT_SYMBOL(fsg_show_ro); | ||||||
| 
 | 
 | ||||||
| static ssize_t nofua_show(struct device *dev, struct device_attribute *attr, | ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf) | ||||||
| 			  char *buf) |  | ||||||
| { | { | ||||||
| 	struct fsg_lun	*curlun = fsg_lun_from_dev(dev); |  | ||||||
| 
 |  | ||||||
| 	return sprintf(buf, "%u\n", curlun->nofua); | 	return sprintf(buf, "%u\n", curlun->nofua); | ||||||
| } | } | ||||||
|  | EXPORT_SYMBOL(fsg_show_nofua); | ||||||
| 
 | 
 | ||||||
| static ssize_t file_show(struct device *dev, struct device_attribute *attr, | ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, | ||||||
| 			 char *buf) | 		      char *buf) | ||||||
| { | { | ||||||
| 	struct fsg_lun	*curlun = fsg_lun_from_dev(dev); |  | ||||||
| 	struct rw_semaphore	*filesem = dev_get_drvdata(dev); |  | ||||||
| 	char		*p; | 	char		*p; | ||||||
| 	ssize_t		rc; | 	ssize_t		rc; | ||||||
| 
 | 
 | ||||||
|  | @ -591,17 +357,44 @@ static ssize_t file_show(struct device *dev, struct device_attribute *attr, | ||||||
| 	up_read(filesem); | 	up_read(filesem); | ||||||
| 	return rc; | 	return rc; | ||||||
| } | } | ||||||
|  | EXPORT_SYMBOL(fsg_show_file); | ||||||
| 
 | 
 | ||||||
|  | ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf) | ||||||
|  | { | ||||||
|  | 	return sprintf(buf, "%u\n", curlun->cdrom); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(fsg_show_cdrom); | ||||||
| 
 | 
 | ||||||
| static ssize_t ro_store(struct device *dev, struct device_attribute *attr, | ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf) | ||||||
| 			const char *buf, size_t count) | { | ||||||
|  | 	return sprintf(buf, "%u\n", curlun->removable); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(fsg_show_removable); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * The caller must hold fsg->filesem for reading when calling this function. | ||||||
|  |  */ | ||||||
|  | static ssize_t _fsg_store_ro(struct fsg_lun *curlun, bool ro) | ||||||
|  | { | ||||||
|  | 	if (fsg_lun_is_open(curlun)) { | ||||||
|  | 		LDBG(curlun, "read-only status change prevented\n"); | ||||||
|  | 		return -EBUSY; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	curlun->ro = ro; | ||||||
|  | 	curlun->initially_ro = ro; | ||||||
|  | 	LDBG(curlun, "read-only status set to %d\n", curlun->ro); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, | ||||||
|  | 		     const char *buf, size_t count) | ||||||
| { | { | ||||||
| 	ssize_t		rc; | 	ssize_t		rc; | ||||||
| 	struct fsg_lun	*curlun = fsg_lun_from_dev(dev); | 	bool		ro; | ||||||
| 	struct rw_semaphore	*filesem = dev_get_drvdata(dev); |  | ||||||
| 	unsigned	ro; |  | ||||||
| 
 | 
 | ||||||
| 	rc = kstrtouint(buf, 2, &ro); | 	rc = strtobool(buf, &ro); | ||||||
| 	if (rc) | 	if (rc) | ||||||
| 		return rc; | 		return rc; | ||||||
| 
 | 
 | ||||||
|  | @ -610,27 +403,21 @@ static ssize_t ro_store(struct device *dev, struct device_attribute *attr, | ||||||
| 	 * backing file is closed. | 	 * backing file is closed. | ||||||
| 	 */ | 	 */ | ||||||
| 	down_read(filesem); | 	down_read(filesem); | ||||||
| 	if (fsg_lun_is_open(curlun)) { | 	rc = _fsg_store_ro(curlun, ro); | ||||||
| 		LDBG(curlun, "read-only status change prevented\n"); | 	if (!rc) | ||||||
| 		rc = -EBUSY; |  | ||||||
| 	} else { |  | ||||||
| 		curlun->ro = ro; |  | ||||||
| 		curlun->initially_ro = ro; |  | ||||||
| 		LDBG(curlun, "read-only status set to %d\n", curlun->ro); |  | ||||||
| 		rc = count; | 		rc = count; | ||||||
| 	} |  | ||||||
| 	up_read(filesem); | 	up_read(filesem); | ||||||
|  | 
 | ||||||
| 	return rc; | 	return rc; | ||||||
| } | } | ||||||
|  | EXPORT_SYMBOL(fsg_store_ro); | ||||||
| 
 | 
 | ||||||
| static ssize_t nofua_store(struct device *dev, struct device_attribute *attr, | ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count) | ||||||
| 			   const char *buf, size_t count) |  | ||||||
| { | { | ||||||
| 	struct fsg_lun	*curlun = fsg_lun_from_dev(dev); | 	bool		nofua; | ||||||
| 	unsigned	nofua; |  | ||||||
| 	int		ret; | 	int		ret; | ||||||
| 
 | 
 | ||||||
| 	ret = kstrtouint(buf, 2, &nofua); | 	ret = strtobool(buf, &nofua); | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		return ret; | 		return ret; | ||||||
| 
 | 
 | ||||||
|  | @ -642,12 +429,11 @@ static ssize_t nofua_store(struct device *dev, struct device_attribute *attr, | ||||||
| 
 | 
 | ||||||
| 	return count; | 	return count; | ||||||
| } | } | ||||||
|  | EXPORT_SYMBOL(fsg_store_nofua); | ||||||
| 
 | 
 | ||||||
| static ssize_t file_store(struct device *dev, struct device_attribute *attr, | ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, | ||||||
| 			  const char *buf, size_t count) | 		       const char *buf, size_t count) | ||||||
| { | { | ||||||
| 	struct fsg_lun	*curlun = fsg_lun_from_dev(dev); |  | ||||||
| 	struct rw_semaphore	*filesem = dev_get_drvdata(dev); |  | ||||||
| 	int		rc = 0; | 	int		rc = 0; | ||||||
| 
 | 
 | ||||||
| 	if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { | 	if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { | ||||||
|  | @ -674,3 +460,45 @@ static ssize_t file_store(struct device *dev, struct device_attribute *attr, | ||||||
| 	up_write(filesem); | 	up_write(filesem); | ||||||
| 	return (rc < 0 ? rc : count); | 	return (rc < 0 ? rc : count); | ||||||
| } | } | ||||||
|  | EXPORT_SYMBOL(fsg_store_file); | ||||||
|  | 
 | ||||||
|  | ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem, | ||||||
|  | 			const char *buf, size_t count) | ||||||
|  | { | ||||||
|  | 	bool		cdrom; | ||||||
|  | 	int		ret; | ||||||
|  | 
 | ||||||
|  | 	ret = strtobool(buf, &cdrom); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	down_read(filesem); | ||||||
|  | 	ret = cdrom ? _fsg_store_ro(curlun, true) : 0; | ||||||
|  | 
 | ||||||
|  | 	if (!ret) { | ||||||
|  | 		curlun->cdrom = cdrom; | ||||||
|  | 		ret = count; | ||||||
|  | 	} | ||||||
|  | 	up_read(filesem); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(fsg_store_cdrom); | ||||||
|  | 
 | ||||||
|  | ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, | ||||||
|  | 			    size_t count) | ||||||
|  | { | ||||||
|  | 	bool		removable; | ||||||
|  | 	int		ret; | ||||||
|  | 
 | ||||||
|  | 	ret = strtobool(buf, &removable); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	curlun->removable = removable; | ||||||
|  | 
 | ||||||
|  | 	return count; | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(fsg_store_removable); | ||||||
|  | 
 | ||||||
|  | MODULE_LICENSE("GPL"); | ||||||
|  |  | ||||||
							
								
								
									
										229
									
								
								drivers/usb/gadget/storage_common.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								drivers/usb/gadget/storage_common.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,229 @@ | ||||||
|  | #ifndef USB_STORAGE_COMMON_H | ||||||
|  | #define USB_STORAGE_COMMON_H | ||||||
|  | 
 | ||||||
|  | #include <linux/device.h> | ||||||
|  | #include <linux/usb/storage.h> | ||||||
|  | #include <scsi/scsi.h> | ||||||
|  | #include <asm/unaligned.h> | ||||||
|  | 
 | ||||||
|  | #ifndef DEBUG | ||||||
|  | #undef VERBOSE_DEBUG | ||||||
|  | #undef DUMP_MSGS | ||||||
|  | #endif /* !DEBUG */ | ||||||
|  | 
 | ||||||
|  | #ifdef VERBOSE_DEBUG | ||||||
|  | #define VLDBG	LDBG | ||||||
|  | #else | ||||||
|  | #define VLDBG(lun, fmt, args...) do { } while (0) | ||||||
|  | #endif /* VERBOSE_DEBUG */ | ||||||
|  | 
 | ||||||
|  | #define _LMSG(func, lun, fmt, args...)					\ | ||||||
|  | 	do {								\ | ||||||
|  | 		if ((lun)->name_pfx && *(lun)->name_pfx)		\ | ||||||
|  | 			func("%s/%s: " fmt, *(lun)->name_pfx,		\ | ||||||
|  | 				 (lun)->name, ## args);			\ | ||||||
|  | 		else							\ | ||||||
|  | 			func("%s: " fmt, (lun)->name, ## args);		\ | ||||||
|  | 	} while (0) | ||||||
|  | 
 | ||||||
|  | #define LDBG(lun, fmt, args...)		_LMSG(pr_debug, lun, fmt, ## args) | ||||||
|  | #define LERROR(lun, fmt, args...)	_LMSG(pr_err, lun, fmt, ## args) | ||||||
|  | #define LWARN(lun, fmt, args...)	_LMSG(pr_warn, lun, fmt, ## args) | ||||||
|  | #define LINFO(lun, fmt, args...)	_LMSG(pr_info, lun, fmt, ## args) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #ifdef DUMP_MSGS | ||||||
|  | 
 | ||||||
|  | #  define dump_msg(fsg, /* const char * */ label,			\ | ||||||
|  | 		   /* const u8 * */ buf, /* unsigned */ length)		\ | ||||||
|  | do {									\ | ||||||
|  | 	if (length < 512) {						\ | ||||||
|  | 		DBG(fsg, "%s, length %u:\n", label, length);		\ | ||||||
|  | 		print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,	\ | ||||||
|  | 			       16, 1, buf, length, 0);			\ | ||||||
|  | 	}								\ | ||||||
|  | } while (0) | ||||||
|  | 
 | ||||||
|  | #  define dump_cdb(fsg) do { } while (0) | ||||||
|  | 
 | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | #  define dump_msg(fsg, /* const char * */ label, \ | ||||||
|  | 		   /* const u8 * */ buf, /* unsigned */ length) do { } while (0) | ||||||
|  | 
 | ||||||
|  | #  ifdef VERBOSE_DEBUG | ||||||
|  | 
 | ||||||
|  | #    define dump_cdb(fsg)						\ | ||||||
|  | 	print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE,	\ | ||||||
|  | 		       16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0)		\ | ||||||
|  | 
 | ||||||
|  | #  else | ||||||
|  | 
 | ||||||
|  | #    define dump_cdb(fsg) do { } while (0) | ||||||
|  | 
 | ||||||
|  | #  endif /* VERBOSE_DEBUG */ | ||||||
|  | 
 | ||||||
|  | #endif /* DUMP_MSGS */ | ||||||
|  | 
 | ||||||
|  | /* Length of a SCSI Command Data Block */ | ||||||
|  | #define MAX_COMMAND_SIZE	16 | ||||||
|  | 
 | ||||||
|  | /* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ | ||||||
|  | #define SS_NO_SENSE				0 | ||||||
|  | #define SS_COMMUNICATION_FAILURE		0x040800 | ||||||
|  | #define SS_INVALID_COMMAND			0x052000 | ||||||
|  | #define SS_INVALID_FIELD_IN_CDB			0x052400 | ||||||
|  | #define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE	0x052100 | ||||||
|  | #define SS_LOGICAL_UNIT_NOT_SUPPORTED		0x052500 | ||||||
|  | #define SS_MEDIUM_NOT_PRESENT			0x023a00 | ||||||
|  | #define SS_MEDIUM_REMOVAL_PREVENTED		0x055302 | ||||||
|  | #define SS_NOT_READY_TO_READY_TRANSITION	0x062800 | ||||||
|  | #define SS_RESET_OCCURRED			0x062900 | ||||||
|  | #define SS_SAVING_PARAMETERS_NOT_SUPPORTED	0x053900 | ||||||
|  | #define SS_UNRECOVERED_READ_ERROR		0x031100 | ||||||
|  | #define SS_WRITE_ERROR				0x030c02 | ||||||
|  | #define SS_WRITE_PROTECTED			0x072700 | ||||||
|  | 
 | ||||||
|  | #define SK(x)		((u8) ((x) >> 16))	/* Sense Key byte, etc. */ | ||||||
|  | #define ASC(x)		((u8) ((x) >> 8)) | ||||||
|  | #define ASCQ(x)		((u8) (x)) | ||||||
|  | 
 | ||||||
|  | struct fsg_lun { | ||||||
|  | 	struct file	*filp; | ||||||
|  | 	loff_t		file_length; | ||||||
|  | 	loff_t		num_sectors; | ||||||
|  | 
 | ||||||
|  | 	unsigned int	initially_ro:1; | ||||||
|  | 	unsigned int	ro:1; | ||||||
|  | 	unsigned int	removable:1; | ||||||
|  | 	unsigned int	cdrom:1; | ||||||
|  | 	unsigned int	prevent_medium_removal:1; | ||||||
|  | 	unsigned int	registered:1; | ||||||
|  | 	unsigned int	info_valid:1; | ||||||
|  | 	unsigned int	nofua:1; | ||||||
|  | 
 | ||||||
|  | 	u32		sense_data; | ||||||
|  | 	u32		sense_data_info; | ||||||
|  | 	u32		unit_attention_data; | ||||||
|  | 
 | ||||||
|  | 	unsigned int	blkbits; /* Bits of logical block size
 | ||||||
|  | 						       of bound block device */ | ||||||
|  | 	unsigned int	blksize; /* logical block size of bound block device */ | ||||||
|  | 	struct device	dev; | ||||||
|  | 	const char	*name;		/* "lun.name" */ | ||||||
|  | 	const char	**name_pfx;	/* "function.name" */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static inline bool fsg_lun_is_open(struct fsg_lun *curlun) | ||||||
|  | { | ||||||
|  | 	return curlun->filp != NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Big enough to hold our biggest descriptor */ | ||||||
|  | #define EP0_BUFSIZE	256 | ||||||
|  | #define DELAYED_STATUS	(EP0_BUFSIZE + 999)	/* An impossibly large value */ | ||||||
|  | 
 | ||||||
|  | /* Default size of buffer length. */ | ||||||
|  | #define FSG_BUFLEN	((u32)16384) | ||||||
|  | 
 | ||||||
|  | /* Maximal number of LUNs supported in mass storage function */ | ||||||
|  | #define FSG_MAX_LUNS	8 | ||||||
|  | 
 | ||||||
|  | enum fsg_buffer_state { | ||||||
|  | 	BUF_STATE_EMPTY = 0, | ||||||
|  | 	BUF_STATE_FULL, | ||||||
|  | 	BUF_STATE_BUSY | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct fsg_buffhd { | ||||||
|  | 	void				*buf; | ||||||
|  | 	enum fsg_buffer_state		state; | ||||||
|  | 	struct fsg_buffhd		*next; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * The NetChip 2280 is faster, and handles some protocol faults | ||||||
|  | 	 * better, if we don't submit any short bulk-out read requests. | ||||||
|  | 	 * So we will record the intended request length here. | ||||||
|  | 	 */ | ||||||
|  | 	unsigned int			bulk_out_intended_length; | ||||||
|  | 
 | ||||||
|  | 	struct usb_request		*inreq; | ||||||
|  | 	int				inreq_busy; | ||||||
|  | 	struct usb_request		*outreq; | ||||||
|  | 	int				outreq_busy; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum fsg_state { | ||||||
|  | 	/* This one isn't used anywhere */ | ||||||
|  | 	FSG_STATE_COMMAND_PHASE = -10, | ||||||
|  | 	FSG_STATE_DATA_PHASE, | ||||||
|  | 	FSG_STATE_STATUS_PHASE, | ||||||
|  | 
 | ||||||
|  | 	FSG_STATE_IDLE = 0, | ||||||
|  | 	FSG_STATE_ABORT_BULK_OUT, | ||||||
|  | 	FSG_STATE_RESET, | ||||||
|  | 	FSG_STATE_INTERFACE_CHANGE, | ||||||
|  | 	FSG_STATE_CONFIG_CHANGE, | ||||||
|  | 	FSG_STATE_DISCONNECT, | ||||||
|  | 	FSG_STATE_EXIT, | ||||||
|  | 	FSG_STATE_TERMINATED | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum data_direction { | ||||||
|  | 	DATA_DIR_UNKNOWN = 0, | ||||||
|  | 	DATA_DIR_FROM_HOST, | ||||||
|  | 	DATA_DIR_TO_HOST, | ||||||
|  | 	DATA_DIR_NONE | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static inline u32 get_unaligned_be24(u8 *buf) | ||||||
|  | { | ||||||
|  | 	return 0xffffff & (u32) get_unaligned_be32(buf - 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev) | ||||||
|  | { | ||||||
|  | 	return container_of(dev, struct fsg_lun, dev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  | 	FSG_STRING_INTERFACE | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | extern struct usb_interface_descriptor fsg_intf_desc; | ||||||
|  | 
 | ||||||
|  | extern struct usb_endpoint_descriptor fsg_fs_bulk_in_desc; | ||||||
|  | extern struct usb_endpoint_descriptor fsg_fs_bulk_out_desc; | ||||||
|  | extern struct usb_descriptor_header *fsg_fs_function[]; | ||||||
|  | 
 | ||||||
|  | extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc; | ||||||
|  | extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc; | ||||||
|  | extern struct usb_descriptor_header *fsg_hs_function[]; | ||||||
|  | 
 | ||||||
|  | extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc; | ||||||
|  | extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc; | ||||||
|  | extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc; | ||||||
|  | extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc; | ||||||
|  | extern struct usb_descriptor_header *fsg_ss_function[]; | ||||||
|  | 
 | ||||||
|  | void fsg_lun_close(struct fsg_lun *curlun); | ||||||
|  | int fsg_lun_open(struct fsg_lun *curlun, const char *filename); | ||||||
|  | int fsg_lun_fsync_sub(struct fsg_lun *curlun); | ||||||
|  | void store_cdrom_address(u8 *dest, int msf, u32 addr); | ||||||
|  | ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf); | ||||||
|  | ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf); | ||||||
|  | ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, | ||||||
|  | 		      char *buf); | ||||||
|  | ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf); | ||||||
|  | ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf); | ||||||
|  | ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, | ||||||
|  | 		     const char *buf, size_t count); | ||||||
|  | ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count); | ||||||
|  | ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, | ||||||
|  | 		       const char *buf, size_t count); | ||||||
|  | ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem, | ||||||
|  | 			const char *buf, size_t count); | ||||||
|  | ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, | ||||||
|  | 			    size_t count); | ||||||
|  | 
 | ||||||
|  | #endif /* USB_STORAGE_COMMON_H */ | ||||||
|  | @ -356,7 +356,8 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri | ||||||
| 	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); | 	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); | ||||||
| 	return 0; | 	return 0; | ||||||
| err1: | err1: | ||||||
| 	dev_err(&udc->dev, "failed to start %s: %d\n", | 	if (ret != -EISNAM) | ||||||
|  | 		dev_err(&udc->dev, "failed to start %s: %d\n", | ||||||
| 			udc->driver->function, ret); | 			udc->driver->function, ret); | ||||||
| 	udc->driver = NULL; | 	udc->driver = NULL; | ||||||
| 	udc->dev.driver = NULL; | 	udc->dev.driver = NULL; | ||||||
|  |  | ||||||
|  | @ -95,6 +95,18 @@ unsigned autoresume = DEFAULT_AUTORESUME; | ||||||
| module_param(autoresume, uint, S_IRUGO); | module_param(autoresume, uint, S_IRUGO); | ||||||
| MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup"); | MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup"); | ||||||
| 
 | 
 | ||||||
|  | /* Maximum Autoresume time */ | ||||||
|  | unsigned max_autoresume; | ||||||
|  | module_param(max_autoresume, uint, S_IRUGO); | ||||||
|  | MODULE_PARM_DESC(max_autoresume, "maximum seconds before remote wakeup"); | ||||||
|  | 
 | ||||||
|  | /* Interval between two remote wakeups */ | ||||||
|  | unsigned autoresume_interval_ms; | ||||||
|  | module_param(autoresume_interval_ms, uint, S_IRUGO); | ||||||
|  | MODULE_PARM_DESC(autoresume_interval_ms, | ||||||
|  | 		"milliseconds to increase successive wakeup delays"); | ||||||
|  | 
 | ||||||
|  | static unsigned autoresume_step_ms; | ||||||
| /*-------------------------------------------------------------------------*/ | /*-------------------------------------------------------------------------*/ | ||||||
| 
 | 
 | ||||||
| static struct usb_device_descriptor device_desc = { | static struct usb_device_descriptor device_desc = { | ||||||
|  | @ -183,8 +195,16 @@ static void zero_suspend(struct usb_composite_dev *cdev) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	if (autoresume) { | 	if (autoresume) { | ||||||
| 		mod_timer(&autoresume_timer, jiffies + (HZ * autoresume)); | 		if (max_autoresume && | ||||||
| 		DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume); | 			(autoresume_step_ms > max_autoresume * 1000)) | ||||||
|  | 				autoresume_step_ms = autoresume * 1000; | ||||||
|  | 
 | ||||||
|  | 		mod_timer(&autoresume_timer, jiffies + | ||||||
|  | 			msecs_to_jiffies(autoresume_step_ms)); | ||||||
|  | 		DBG(cdev, "suspend, wakeup in %d milliseconds\n", | ||||||
|  | 			autoresume_step_ms); | ||||||
|  | 
 | ||||||
|  | 		autoresume_step_ms += autoresume_interval_ms; | ||||||
| 	} else | 	} else | ||||||
| 		DBG(cdev, "%s\n", __func__); | 		DBG(cdev, "%s\n", __func__); | ||||||
| } | } | ||||||
|  | @ -316,6 +336,7 @@ static int __init zero_bind(struct usb_composite_dev *cdev) | ||||||
| 	if (autoresume) { | 	if (autoresume) { | ||||||
| 		sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; | 		sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; | ||||||
| 		loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; | 		loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; | ||||||
|  | 		autoresume_step_ms = autoresume * 1000; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* support OTG systems */ | 	/* support OTG systems */ | ||||||
|  |  | ||||||
|  | @ -91,7 +91,7 @@ config USB_MUSB_BLACKFIN | ||||||
| 	depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523) | 	depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523) | ||||||
| 
 | 
 | ||||||
| config USB_MUSB_UX500 | config USB_MUSB_UX500 | ||||||
| 	tristate "U8500 and U5500" | 	tristate "Ux500 platforms" | ||||||
| 
 | 
 | ||||||
| endchoice | endchoice | ||||||
| 
 | 
 | ||||||
|  | @ -113,7 +113,7 @@ choice | ||||||
| 	  allow using DMA on multiplatform kernels. | 	  allow using DMA on multiplatform kernels. | ||||||
| 
 | 
 | ||||||
| config USB_UX500_DMA | config USB_UX500_DMA | ||||||
| 	bool 'ST Ericsson U8500 and U5500' | 	bool 'ST Ericsson Ux500' | ||||||
| 	depends on USB_MUSB_UX500 | 	depends on USB_MUSB_UX500 | ||||||
| 	help | 	help | ||||||
| 	  Enable DMA transfers on UX500 platforms. | 	  Enable DMA transfers on UX500 platforms. | ||||||
|  |  | ||||||
|  | @ -89,7 +89,6 @@ struct am35x_glue { | ||||||
| 	struct clk		*phy_clk; | 	struct clk		*phy_clk; | ||||||
| 	struct clk		*clk; | 	struct clk		*clk; | ||||||
| }; | }; | ||||||
| #define glue_to_musb(g)		platform_get_drvdata(g->musb) |  | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * am35x_musb_enable - enable interrupts |  * am35x_musb_enable - enable interrupts | ||||||
|  | @ -452,14 +451,18 @@ static const struct musb_platform_ops am35x_ops = { | ||||||
| 	.set_vbus	= am35x_musb_set_vbus, | 	.set_vbus	= am35x_musb_set_vbus, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static u64 am35x_dmamask = DMA_BIT_MASK(32); | static const struct platform_device_info am35x_dev_info = { | ||||||
|  | 	.name		= "musb-hdrc", | ||||||
|  | 	.id		= PLATFORM_DEVID_AUTO, | ||||||
|  | 	.dma_mask	= DMA_BIT_MASK(32), | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| static int am35x_probe(struct platform_device *pdev) | static int am35x_probe(struct platform_device *pdev) | ||||||
| { | { | ||||||
| 	struct musb_hdrc_platform_data	*pdata = dev_get_platdata(&pdev->dev); | 	struct musb_hdrc_platform_data	*pdata = dev_get_platdata(&pdev->dev); | ||||||
| 	struct platform_device		*musb; | 	struct platform_device		*musb; | ||||||
| 	struct am35x_glue		*glue; | 	struct am35x_glue		*glue; | ||||||
| 
 | 	struct platform_device_info	pinfo; | ||||||
| 	struct clk			*phy_clk; | 	struct clk			*phy_clk; | ||||||
| 	struct clk			*clk; | 	struct clk			*clk; | ||||||
| 
 | 
 | ||||||
|  | @ -471,12 +474,6 @@ static int am35x_probe(struct platform_device *pdev) | ||||||
| 		goto err0; | 		goto err0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); |  | ||||||
| 	if (!musb) { |  | ||||||
| 		dev_err(&pdev->dev, "failed to allocate musb device\n"); |  | ||||||
| 		goto err1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	phy_clk = clk_get(&pdev->dev, "fck"); | 	phy_clk = clk_get(&pdev->dev, "fck"); | ||||||
| 	if (IS_ERR(phy_clk)) { | 	if (IS_ERR(phy_clk)) { | ||||||
| 		dev_err(&pdev->dev, "failed to get PHY clock\n"); | 		dev_err(&pdev->dev, "failed to get PHY clock\n"); | ||||||
|  | @ -503,12 +500,7 @@ static int am35x_probe(struct platform_device *pdev) | ||||||
| 		goto err6; | 		goto err6; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	musb->dev.parent		= &pdev->dev; |  | ||||||
| 	musb->dev.dma_mask		= &am35x_dmamask; |  | ||||||
| 	musb->dev.coherent_dma_mask	= am35x_dmamask; |  | ||||||
| 
 |  | ||||||
| 	glue->dev			= &pdev->dev; | 	glue->dev			= &pdev->dev; | ||||||
| 	glue->musb			= musb; |  | ||||||
| 	glue->phy_clk			= phy_clk; | 	glue->phy_clk			= phy_clk; | ||||||
| 	glue->clk			= clk; | 	glue->clk			= clk; | ||||||
| 
 | 
 | ||||||
|  | @ -516,22 +508,17 @@ static int am35x_probe(struct platform_device *pdev) | ||||||
| 
 | 
 | ||||||
| 	platform_set_drvdata(pdev, glue); | 	platform_set_drvdata(pdev, glue); | ||||||
| 
 | 
 | ||||||
| 	ret = platform_device_add_resources(musb, pdev->resource, | 	pinfo = am35x_dev_info; | ||||||
| 			pdev->num_resources); | 	pinfo.parent = &pdev->dev; | ||||||
| 	if (ret) { | 	pinfo.res = pdev->resource; | ||||||
| 		dev_err(&pdev->dev, "failed to add resources\n"); | 	pinfo.num_res = pdev->num_resources; | ||||||
| 		goto err7; | 	pinfo.data = pdata; | ||||||
| 	} | 	pinfo.size_data = sizeof(*pdata); | ||||||
| 
 | 
 | ||||||
| 	ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); | 	glue->musb = musb = platform_device_register_full(&pinfo); | ||||||
| 	if (ret) { | 	if (IS_ERR(musb)) { | ||||||
| 		dev_err(&pdev->dev, "failed to add platform_data\n"); | 		ret = PTR_ERR(musb); | ||||||
| 		goto err7; | 		dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ret = platform_device_add(musb); |  | ||||||
| 	if (ret) { |  | ||||||
| 		dev_err(&pdev->dev, "failed to register musb device\n"); |  | ||||||
| 		goto err7; | 		goto err7; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -550,9 +537,6 @@ err4: | ||||||
| 	clk_put(phy_clk); | 	clk_put(phy_clk); | ||||||
| 
 | 
 | ||||||
| err3: | err3: | ||||||
| 	platform_device_put(musb); |  | ||||||
| 
 |  | ||||||
| err1: |  | ||||||
| 	kfree(glue); | 	kfree(glue); | ||||||
| 
 | 
 | ||||||
| err0: | err0: | ||||||
|  | @ -615,23 +599,16 @@ static int am35x_resume(struct device *dev) | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| static struct dev_pm_ops am35x_pm_ops = { |  | ||||||
| 	.suspend	= am35x_suspend, |  | ||||||
| 	.resume		= am35x_resume, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| #define DEV_PM_OPS	&am35x_pm_ops |  | ||||||
| #else |  | ||||||
| #define DEV_PM_OPS	NULL |  | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | static SIMPLE_DEV_PM_OPS(am35x_pm_ops, am35x_suspend, am35x_resume); | ||||||
|  | 
 | ||||||
| static struct platform_driver am35x_driver = { | static struct platform_driver am35x_driver = { | ||||||
| 	.probe		= am35x_probe, | 	.probe		= am35x_probe, | ||||||
| 	.remove		= am35x_remove, | 	.remove		= am35x_remove, | ||||||
| 	.driver		= { | 	.driver		= { | ||||||
| 		.name	= "musb-am35x", | 		.name	= "musb-am35x", | ||||||
| 		.pm	= DEV_PM_OPS, | 		.pm	= &am35x_pm_ops, | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -561,23 +561,16 @@ static int bfin_resume(struct device *dev) | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| static struct dev_pm_ops bfin_pm_ops = { |  | ||||||
| 	.suspend	= bfin_suspend, |  | ||||||
| 	.resume		= bfin_resume, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| #define DEV_PM_OPS	&bfin_pm_ops |  | ||||||
| #else |  | ||||||
| #define DEV_PM_OPS	NULL |  | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | static SIMPLE_DEV_PM_OPS(bfin_pm_ops, bfin_suspend, bfin_resume); | ||||||
|  | 
 | ||||||
| static struct platform_driver bfin_driver = { | static struct platform_driver bfin_driver = { | ||||||
| 	.probe		= bfin_probe, | 	.probe		= bfin_probe, | ||||||
| 	.remove		= __exit_p(bfin_remove), | 	.remove		= __exit_p(bfin_remove), | ||||||
| 	.driver		= { | 	.driver		= { | ||||||
| 		.name	= "musb-blackfin", | 		.name	= "musb-blackfin", | ||||||
| 		.pm	= DEV_PM_OPS, | 		.pm	= &bfin_pm_ops, | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -472,7 +472,11 @@ static const struct musb_platform_ops da8xx_ops = { | ||||||
| 	.set_vbus	= da8xx_musb_set_vbus, | 	.set_vbus	= da8xx_musb_set_vbus, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static u64 da8xx_dmamask = DMA_BIT_MASK(32); | static const struct platform_device_info da8xx_dev_info = { | ||||||
|  | 	.name		= "musb-hdrc", | ||||||
|  | 	.id		= PLATFORM_DEVID_AUTO, | ||||||
|  | 	.dma_mask	= DMA_BIT_MASK(32), | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| static int da8xx_probe(struct platform_device *pdev) | static int da8xx_probe(struct platform_device *pdev) | ||||||
| { | { | ||||||
|  | @ -480,7 +484,7 @@ static int da8xx_probe(struct platform_device *pdev) | ||||||
| 	struct musb_hdrc_platform_data	*pdata = dev_get_platdata(&pdev->dev); | 	struct musb_hdrc_platform_data	*pdata = dev_get_platdata(&pdev->dev); | ||||||
| 	struct platform_device		*musb; | 	struct platform_device		*musb; | ||||||
| 	struct da8xx_glue		*glue; | 	struct da8xx_glue		*glue; | ||||||
| 
 | 	struct platform_device_info	pinfo; | ||||||
| 	struct clk			*clk; | 	struct clk			*clk; | ||||||
| 
 | 
 | ||||||
| 	int				ret = -ENOMEM; | 	int				ret = -ENOMEM; | ||||||
|  | @ -491,12 +495,6 @@ static int da8xx_probe(struct platform_device *pdev) | ||||||
| 		goto err0; | 		goto err0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); |  | ||||||
| 	if (!musb) { |  | ||||||
| 		dev_err(&pdev->dev, "failed to allocate musb device\n"); |  | ||||||
| 		goto err1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	clk = clk_get(&pdev->dev, "usb20"); | 	clk = clk_get(&pdev->dev, "usb20"); | ||||||
| 	if (IS_ERR(clk)) { | 	if (IS_ERR(clk)) { | ||||||
| 		dev_err(&pdev->dev, "failed to get clock\n"); | 		dev_err(&pdev->dev, "failed to get clock\n"); | ||||||
|  | @ -510,12 +508,7 @@ static int da8xx_probe(struct platform_device *pdev) | ||||||
| 		goto err4; | 		goto err4; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	musb->dev.parent		= &pdev->dev; |  | ||||||
| 	musb->dev.dma_mask		= &da8xx_dmamask; |  | ||||||
| 	musb->dev.coherent_dma_mask	= da8xx_dmamask; |  | ||||||
| 
 |  | ||||||
| 	glue->dev			= &pdev->dev; | 	glue->dev			= &pdev->dev; | ||||||
| 	glue->musb			= musb; |  | ||||||
| 	glue->clk			= clk; | 	glue->clk			= clk; | ||||||
| 
 | 
 | ||||||
| 	pdata->platform_ops		= &da8xx_ops; | 	pdata->platform_ops		= &da8xx_ops; | ||||||
|  | @ -535,22 +528,17 @@ static int da8xx_probe(struct platform_device *pdev) | ||||||
| 	musb_resources[1].end = pdev->resource[1].end; | 	musb_resources[1].end = pdev->resource[1].end; | ||||||
| 	musb_resources[1].flags = pdev->resource[1].flags; | 	musb_resources[1].flags = pdev->resource[1].flags; | ||||||
| 
 | 
 | ||||||
| 	ret = platform_device_add_resources(musb, musb_resources, | 	pinfo = da8xx_dev_info; | ||||||
| 			ARRAY_SIZE(musb_resources)); | 	pinfo.parent = &pdev->dev; | ||||||
| 	if (ret) { | 	pinfo.res = musb_resources; | ||||||
| 		dev_err(&pdev->dev, "failed to add resources\n"); | 	pinfo.num_res = ARRAY_SIZE(musb_resources); | ||||||
| 		goto err5; | 	pinfo.data = pdata; | ||||||
| 	} | 	pinfo.size_data = sizeof(*pdata); | ||||||
| 
 | 
 | ||||||
| 	ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); | 	glue->musb = musb = platform_device_register_full(&pinfo); | ||||||
| 	if (ret) { | 	if (IS_ERR(musb)) { | ||||||
| 		dev_err(&pdev->dev, "failed to add platform_data\n"); | 		ret = PTR_ERR(musb); | ||||||
| 		goto err5; | 		dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ret = platform_device_add(musb); |  | ||||||
| 	if (ret) { |  | ||||||
| 		dev_err(&pdev->dev, "failed to register musb device\n"); |  | ||||||
| 		goto err5; | 		goto err5; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -563,9 +551,6 @@ err4: | ||||||
| 	clk_put(clk); | 	clk_put(clk); | ||||||
| 
 | 
 | ||||||
| err3: | err3: | ||||||
| 	platform_device_put(musb); |  | ||||||
| 
 |  | ||||||
| err1: |  | ||||||
| 	kfree(glue); | 	kfree(glue); | ||||||
| 
 | 
 | ||||||
| err0: | err0: | ||||||
|  |  | ||||||
|  | @ -505,14 +505,19 @@ static const struct musb_platform_ops davinci_ops = { | ||||||
| 	.set_vbus	= davinci_musb_set_vbus, | 	.set_vbus	= davinci_musb_set_vbus, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static u64 davinci_dmamask = DMA_BIT_MASK(32); | static const struct platform_device_info davinci_dev_info = { | ||||||
|  | 	.name		= "musb-hdrc", | ||||||
|  | 	.id		= PLATFORM_DEVID_AUTO, | ||||||
|  | 	.dma_mask	= DMA_BIT_MASK(32), | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| static int davinci_probe(struct platform_device *pdev) | static int davinci_probe(struct platform_device *pdev) | ||||||
| { | { | ||||||
| 	struct resource musb_resources[2]; | 	struct resource			musb_resources[3]; | ||||||
| 	struct musb_hdrc_platform_data	*pdata = dev_get_platdata(&pdev->dev); | 	struct musb_hdrc_platform_data	*pdata = dev_get_platdata(&pdev->dev); | ||||||
| 	struct platform_device		*musb; | 	struct platform_device		*musb; | ||||||
| 	struct davinci_glue		*glue; | 	struct davinci_glue		*glue; | ||||||
|  | 	struct platform_device_info	pinfo; | ||||||
| 	struct clk			*clk; | 	struct clk			*clk; | ||||||
| 
 | 
 | ||||||
| 	int				ret = -ENOMEM; | 	int				ret = -ENOMEM; | ||||||
|  | @ -523,12 +528,6 @@ static int davinci_probe(struct platform_device *pdev) | ||||||
| 		goto err0; | 		goto err0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); |  | ||||||
| 	if (!musb) { |  | ||||||
| 		dev_err(&pdev->dev, "failed to allocate musb device\n"); |  | ||||||
| 		goto err1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	clk = clk_get(&pdev->dev, "usb"); | 	clk = clk_get(&pdev->dev, "usb"); | ||||||
| 	if (IS_ERR(clk)) { | 	if (IS_ERR(clk)) { | ||||||
| 		dev_err(&pdev->dev, "failed to get clock\n"); | 		dev_err(&pdev->dev, "failed to get clock\n"); | ||||||
|  | @ -542,12 +541,7 @@ static int davinci_probe(struct platform_device *pdev) | ||||||
| 		goto err4; | 		goto err4; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	musb->dev.parent		= &pdev->dev; |  | ||||||
| 	musb->dev.dma_mask		= &davinci_dmamask; |  | ||||||
| 	musb->dev.coherent_dma_mask	= davinci_dmamask; |  | ||||||
| 
 |  | ||||||
| 	glue->dev			= &pdev->dev; | 	glue->dev			= &pdev->dev; | ||||||
| 	glue->musb			= musb; |  | ||||||
| 	glue->clk			= clk; | 	glue->clk			= clk; | ||||||
| 
 | 
 | ||||||
| 	pdata->platform_ops		= &davinci_ops; | 	pdata->platform_ops		= &davinci_ops; | ||||||
|  | @ -567,22 +561,26 @@ static int davinci_probe(struct platform_device *pdev) | ||||||
| 	musb_resources[1].end = pdev->resource[1].end; | 	musb_resources[1].end = pdev->resource[1].end; | ||||||
| 	musb_resources[1].flags = pdev->resource[1].flags; | 	musb_resources[1].flags = pdev->resource[1].flags; | ||||||
| 
 | 
 | ||||||
| 	ret = platform_device_add_resources(musb, musb_resources, | 	/*
 | ||||||
| 			ARRAY_SIZE(musb_resources)); | 	 * For DM6467 3 resources are passed. A placeholder for the 3rd | ||||||
| 	if (ret) { | 	 * resource is always there, so it's safe to always copy it... | ||||||
| 		dev_err(&pdev->dev, "failed to add resources\n"); | 	 */ | ||||||
| 		goto err5; | 	musb_resources[2].name = pdev->resource[2].name; | ||||||
| 	} | 	musb_resources[2].start = pdev->resource[2].start; | ||||||
|  | 	musb_resources[2].end = pdev->resource[2].end; | ||||||
|  | 	musb_resources[2].flags = pdev->resource[2].flags; | ||||||
| 
 | 
 | ||||||
| 	ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); | 	pinfo = davinci_dev_info; | ||||||
| 	if (ret) { | 	pinfo.parent = &pdev->dev; | ||||||
| 		dev_err(&pdev->dev, "failed to add platform_data\n"); | 	pinfo.res = musb_resources; | ||||||
| 		goto err5; | 	pinfo.num_res = ARRAY_SIZE(musb_resources); | ||||||
| 	} | 	pinfo.data = pdata; | ||||||
|  | 	pinfo.size_data = sizeof(*pdata); | ||||||
| 
 | 
 | ||||||
| 	ret = platform_device_add(musb); | 	glue->musb = musb = platform_device_register_full(&pinfo); | ||||||
| 	if (ret) { | 	if (IS_ERR(musb)) { | ||||||
| 		dev_err(&pdev->dev, "failed to register musb device\n"); | 		ret = PTR_ERR(musb); | ||||||
|  | 		dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); | ||||||
| 		goto err5; | 		goto err5; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -595,9 +593,6 @@ err4: | ||||||
| 	clk_put(clk); | 	clk_put(clk); | ||||||
| 
 | 
 | ||||||
| err3: | err3: | ||||||
| 	platform_device_put(musb); |  | ||||||
| 
 |  | ||||||
| err1: |  | ||||||
| 	kfree(glue); | 	kfree(glue); | ||||||
| 
 | 
 | ||||||
| err0: | err0: | ||||||
|  |  | ||||||
|  | @ -46,7 +46,7 @@ static struct platform_driver am335x_child_driver = { | ||||||
| 	.remove         = am335x_child_remove, | 	.remove         = am335x_child_remove, | ||||||
| 	.driver         = { | 	.driver         = { | ||||||
| 		.name   = "am335x-usb-childs", | 		.name   = "am335x-usb-childs", | ||||||
| 		.of_match_table	= of_match_ptr(am335x_child_of_match), | 		.of_match_table	= am335x_child_of_match, | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1809,8 +1809,7 @@ static void musb_free(struct musb *musb) | ||||||
| 			disable_irq_wake(musb->nIrq); | 			disable_irq_wake(musb->nIrq); | ||||||
| 		free_irq(musb->nIrq, musb); | 		free_irq(musb->nIrq, musb); | ||||||
| 	} | 	} | ||||||
| 	if (musb->dma_controller) | 	cancel_work_sync(&musb->irq_work); | ||||||
| 		dma_controller_destroy(musb->dma_controller); |  | ||||||
| 
 | 
 | ||||||
| 	musb_host_free(musb); | 	musb_host_free(musb); | ||||||
| } | } | ||||||
|  | @ -1885,8 +1884,13 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) | ||||||
| 
 | 
 | ||||||
| 	pm_runtime_get_sync(musb->controller); | 	pm_runtime_get_sync(musb->controller); | ||||||
| 
 | 
 | ||||||
| 	if (use_dma && dev->dma_mask) | 	if (use_dma && dev->dma_mask) { | ||||||
| 		musb->dma_controller = dma_controller_create(musb, musb->mregs); | 		musb->dma_controller = dma_controller_create(musb, musb->mregs); | ||||||
|  | 		if (IS_ERR(musb->dma_controller)) { | ||||||
|  | 			status = PTR_ERR(musb->dma_controller); | ||||||
|  | 			goto fail2_5; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	/* be sure interrupts are disabled before connecting ISR */ | 	/* be sure interrupts are disabled before connecting ISR */ | ||||||
| 	musb_platform_disable(musb); | 	musb_platform_disable(musb); | ||||||
|  | @ -1946,6 +1950,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) | ||||||
| 		if (status < 0) | 		if (status < 0) | ||||||
| 			goto fail3; | 			goto fail3; | ||||||
| 		status = musb_gadget_setup(musb); | 		status = musb_gadget_setup(musb); | ||||||
|  | 		if (status) | ||||||
|  | 			musb_host_cleanup(musb); | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		dev_err(dev, "unsupported port mode %d\n", musb->port_mode); | 		dev_err(dev, "unsupported port mode %d\n", musb->port_mode); | ||||||
|  | @ -1972,10 +1978,12 @@ fail5: | ||||||
| 
 | 
 | ||||||
| fail4: | fail4: | ||||||
| 	musb_gadget_cleanup(musb); | 	musb_gadget_cleanup(musb); | ||||||
|  | 	musb_host_cleanup(musb); | ||||||
| 
 | 
 | ||||||
| fail3: | fail3: | ||||||
| 	if (musb->dma_controller) | 	if (musb->dma_controller) | ||||||
| 		dma_controller_destroy(musb->dma_controller); | 		dma_controller_destroy(musb->dma_controller); | ||||||
|  | fail2_5: | ||||||
| 	pm_runtime_put_sync(musb->controller); | 	pm_runtime_put_sync(musb->controller); | ||||||
| 
 | 
 | ||||||
| fail2: | fail2: | ||||||
|  | @ -2032,6 +2040,9 @@ static int musb_remove(struct platform_device *pdev) | ||||||
| 	musb_exit_debugfs(musb); | 	musb_exit_debugfs(musb); | ||||||
| 	musb_shutdown(pdev); | 	musb_shutdown(pdev); | ||||||
| 
 | 
 | ||||||
|  | 	if (musb->dma_controller) | ||||||
|  | 		dma_controller_destroy(musb->dma_controller); | ||||||
|  | 
 | ||||||
| 	musb_free(musb); | 	musb_free(musb); | ||||||
| 	device_init_wakeup(dev, 0); | 	device_init_wakeup(dev, 0); | ||||||
| 	return 0; | 	return 0; | ||||||
|  |  | ||||||
|  | @ -484,6 +484,7 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) | ||||||
| 		if (ret) | 		if (ret) | ||||||
| 			goto err; | 			goto err; | ||||||
| 
 | 
 | ||||||
|  | 		ret = -EINVAL; | ||||||
| 		if (port > MUSB_DMA_NUM_CHANNELS || !port) | 		if (port > MUSB_DMA_NUM_CHANNELS || !port) | ||||||
| 			goto err; | 			goto err; | ||||||
| 		if (is_tx) | 		if (is_tx) | ||||||
|  | @ -503,6 +504,7 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) | ||||||
| 		dc = dma_request_slave_channel(dev, str); | 		dc = dma_request_slave_channel(dev, str); | ||||||
| 		if (!dc) { | 		if (!dc) { | ||||||
| 			dev_err(dev, "Falied to request %s.\n", str); | 			dev_err(dev, "Falied to request %s.\n", str); | ||||||
|  | 			ret = -EPROBE_DEFER; | ||||||
| 			goto err; | 			goto err; | ||||||
| 		} | 		} | ||||||
| 		cppi41_channel->dc = dc; | 		cppi41_channel->dc = dc; | ||||||
|  | @ -510,7 +512,7 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) | ||||||
| 	return 0; | 	return 0; | ||||||
| err: | err: | ||||||
| 	cppi41_release_all_dma_chans(controller); | 	cppi41_release_all_dma_chans(controller); | ||||||
| 	return -EINVAL; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void dma_controller_destroy(struct dma_controller *c) | void dma_controller_destroy(struct dma_controller *c) | ||||||
|  | @ -526,7 +528,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, | ||||||
| 					void __iomem *base) | 					void __iomem *base) | ||||||
| { | { | ||||||
| 	struct cppi41_dma_controller *controller; | 	struct cppi41_dma_controller *controller; | ||||||
| 	int ret; | 	int ret = 0; | ||||||
| 
 | 
 | ||||||
| 	if (!musb->controller->of_node) { | 	if (!musb->controller->of_node) { | ||||||
| 		dev_err(musb->controller, "Need DT for the DMA engine.\n"); | 		dev_err(musb->controller, "Need DT for the DMA engine.\n"); | ||||||
|  | @ -553,5 +555,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, | ||||||
| plat_get_fail: | plat_get_fail: | ||||||
| 	kfree(controller); | 	kfree(controller); | ||||||
| kzalloc_fail: | kzalloc_fail: | ||||||
|  | 	if (ret == -EPROBE_DEFER) | ||||||
|  | 		return ERR_PTR(ret); | ||||||
| 	return NULL; | 	return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -121,6 +121,43 @@ struct dsps_glue { | ||||||
| 	unsigned long last_timer;    /* last timer data for each instance */ | 	unsigned long last_timer;    /* last timer data for each instance */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) | ||||||
|  | { | ||||||
|  | 	struct device *dev = musb->controller; | ||||||
|  | 	struct dsps_glue *glue = dev_get_drvdata(dev->parent); | ||||||
|  | 
 | ||||||
|  | 	if (timeout == 0) | ||||||
|  | 		timeout = jiffies + msecs_to_jiffies(3); | ||||||
|  | 
 | ||||||
|  | 	/* Never idle if active, or when VBUS timeout is not set as host */ | ||||||
|  | 	if (musb->is_active || (musb->a_wait_bcon == 0 && | ||||||
|  | 				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); | ||||||
|  | 		glue->last_timer = jiffies; | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	if (!musb->g.dev.driver) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	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 = 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, timeout); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * dsps_musb_enable - enable interrupts |  * dsps_musb_enable - enable interrupts | ||||||
|  */ |  */ | ||||||
|  | @ -143,6 +180,7 @@ static void dsps_musb_enable(struct musb *musb) | ||||||
| 	/* Force the DRVVBUS IRQ so we can start polling for ID change. */ | 	/* Force the DRVVBUS IRQ so we can start polling for ID change. */ | ||||||
| 	dsps_writel(reg_base, wrp->coreintr_set, | 	dsps_writel(reg_base, wrp->coreintr_set, | ||||||
| 		    (1 << wrp->drvvbus) << wrp->usb_shift); | 		    (1 << wrp->drvvbus) << wrp->usb_shift); | ||||||
|  | 	dsps_musb_try_idle(musb, 0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -171,6 +209,7 @@ static void otg_timer(unsigned long _musb) | ||||||
| 	const struct dsps_musb_wrapper *wrp = glue->wrp; | 	const struct dsps_musb_wrapper *wrp = glue->wrp; | ||||||
| 	u8 devctl; | 	u8 devctl; | ||||||
| 	unsigned long flags; | 	unsigned long flags; | ||||||
|  | 	int skip_session = 0; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * We poll because DSPS IP's won't expose several OTG-critical | 	 * We poll because DSPS IP's won't expose several OTG-critical | ||||||
|  | @ -183,10 +222,12 @@ static void otg_timer(unsigned long _musb) | ||||||
| 	spin_lock_irqsave(&musb->lock, flags); | 	spin_lock_irqsave(&musb->lock, flags); | ||||||
| 	switch (musb->xceiv->state) { | 	switch (musb->xceiv->state) { | ||||||
| 	case OTG_STATE_A_WAIT_BCON: | 	case OTG_STATE_A_WAIT_BCON: | ||||||
| 		devctl &= ~MUSB_DEVCTL_SESSION; | 		dsps_writeb(musb->mregs, MUSB_DEVCTL, 0); | ||||||
| 		dsps_writeb(musb->mregs, MUSB_DEVCTL, devctl); | 		skip_session = 1; | ||||||
|  | 		/* fall */ | ||||||
| 
 | 
 | ||||||
| 		devctl = dsps_readb(musb->mregs, MUSB_DEVCTL); | 	case OTG_STATE_A_IDLE: | ||||||
|  | 	case OTG_STATE_B_IDLE: | ||||||
| 		if (devctl & MUSB_DEVCTL_BDEVICE) { | 		if (devctl & MUSB_DEVCTL_BDEVICE) { | ||||||
| 			musb->xceiv->state = OTG_STATE_B_IDLE; | 			musb->xceiv->state = OTG_STATE_B_IDLE; | ||||||
| 			MUSB_DEV_MODE(musb); | 			MUSB_DEV_MODE(musb); | ||||||
|  | @ -194,60 +235,21 @@ static void otg_timer(unsigned long _musb) | ||||||
| 			musb->xceiv->state = OTG_STATE_A_IDLE; | 			musb->xceiv->state = OTG_STATE_A_IDLE; | ||||||
| 			MUSB_HST_MODE(musb); | 			MUSB_HST_MODE(musb); | ||||||
| 		} | 		} | ||||||
|  | 		if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session) | ||||||
|  | 			dsps_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); | ||||||
|  | 		mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_WAIT_VFALL: | 	case OTG_STATE_A_WAIT_VFALL: | ||||||
| 		musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; | 		musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; | ||||||
| 		dsps_writel(musb->ctrl_base, wrp->coreintr_set, | 		dsps_writel(musb->ctrl_base, wrp->coreintr_set, | ||||||
| 			    MUSB_INTR_VBUSERROR << wrp->usb_shift); | 			    MUSB_INTR_VBUSERROR << wrp->usb_shift); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_B_IDLE: |  | ||||||
| 		devctl = dsps_readb(mregs, MUSB_DEVCTL); |  | ||||||
| 		if (devctl & MUSB_DEVCTL_BDEVICE) |  | ||||||
| 			mod_timer(&glue->timer, |  | ||||||
| 					jiffies + wrp->poll_seconds * HZ); |  | ||||||
| 		else |  | ||||||
| 			musb->xceiv->state = OTG_STATE_A_IDLE; |  | ||||||
| 		break; |  | ||||||
| 	default: | 	default: | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 	spin_unlock_irqrestore(&musb->lock, flags); | 	spin_unlock_irqrestore(&musb->lock, flags); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) |  | ||||||
| { |  | ||||||
| 	struct device *dev = musb->controller; |  | ||||||
| 	struct dsps_glue *glue = dev_get_drvdata(dev->parent); |  | ||||||
| 
 |  | ||||||
| 	if (timeout == 0) |  | ||||||
| 		timeout = jiffies + msecs_to_jiffies(3); |  | ||||||
| 
 |  | ||||||
| 	/* Never idle if active, or when VBUS timeout is not set as host */ |  | ||||||
| 	if (musb->is_active || (musb->a_wait_bcon == 0 && |  | ||||||
| 				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); |  | ||||||
| 		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)) { |  | ||||||
| 		dev_dbg(musb->controller, |  | ||||||
| 			"Longer idle timer already pending, ignoring...\n"); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 	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, timeout); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static irqreturn_t dsps_interrupt(int irq, void *hci) | static irqreturn_t dsps_interrupt(int irq, void *hci) | ||||||
| { | { | ||||||
| 	struct musb  *musb = hci; | 	struct musb  *musb = hci; | ||||||
|  | @ -631,7 +633,7 @@ static struct platform_driver dsps_usbss_driver = { | ||||||
| 	.remove         = dsps_remove, | 	.remove         = dsps_remove, | ||||||
| 	.driver         = { | 	.driver         = { | ||||||
| 		.name   = "musb-dsps", | 		.name   = "musb-dsps", | ||||||
| 		.of_match_table	= of_match_ptr(musb_dsps_of_match), | 		.of_match_table	= musb_dsps_of_match, | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -220,6 +220,23 @@ int musb_hub_status_data(struct usb_hcd *hcd, char *buf) | ||||||
| 	return retval; | 	return retval; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int musb_has_gadget(struct musb *musb) | ||||||
|  | { | ||||||
|  | 	/*
 | ||||||
|  | 	 * In host-only mode we start a connection right away. In OTG mode | ||||||
|  | 	 * we have to wait until we loaded a gadget. We don't really need a | ||||||
|  | 	 * gadget if we operate as a host but we should not start a session | ||||||
|  | 	 * as a device without a gadget or else we explode. | ||||||
|  | 	 */ | ||||||
|  | #ifdef CONFIG_USB_MUSB_HOST | ||||||
|  | 	return 1; | ||||||
|  | #else | ||||||
|  | 	if (musb->port_mode == MUSB_PORT_MODE_HOST) | ||||||
|  | 		return 1; | ||||||
|  | 	return musb->g.dev.driver != NULL; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int musb_hub_control( | int musb_hub_control( | ||||||
| 	struct usb_hcd	*hcd, | 	struct usb_hcd	*hcd, | ||||||
| 	u16		typeReq, | 	u16		typeReq, | ||||||
|  | @ -362,7 +379,7 @@ int musb_hub_control( | ||||||
| 			 * initialization logic, e.g. for OTG, or change any | 			 * initialization logic, e.g. for OTG, or change any | ||||||
| 			 * logic relating to VBUS power-up. | 			 * logic relating to VBUS power-up. | ||||||
| 			 */ | 			 */ | ||||||
| 			if (!hcd->self.is_b_host) | 			if (!hcd->self.is_b_host && musb_has_gadget(musb)) | ||||||
| 				musb_start(musb); | 				musb_start(musb); | ||||||
| 			break; | 			break; | ||||||
| 		case USB_PORT_FEAT_RESET: | 		case USB_PORT_FEAT_RESET: | ||||||
|  |  | ||||||
|  | @ -306,6 +306,9 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) | ||||||
| 	default: | 	default: | ||||||
| 		dev_dbg(dev, "ID float\n"); | 		dev_dbg(dev, "ID float\n"); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	atomic_notifier_call_chain(&musb->xceiv->notifier, | ||||||
|  | 			musb->xceiv->last_event, NULL); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1152,7 +1152,11 @@ static const struct musb_platform_ops tusb_ops = { | ||||||
| 	.set_vbus	= tusb_musb_set_vbus, | 	.set_vbus	= tusb_musb_set_vbus, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static u64 tusb_dmamask = DMA_BIT_MASK(32); | static const struct platform_device_info tusb_dev_info = { | ||||||
|  | 	.name		= "musb-hdrc", | ||||||
|  | 	.id		= PLATFORM_DEVID_AUTO, | ||||||
|  | 	.dma_mask	= DMA_BIT_MASK(32), | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| static int tusb_probe(struct platform_device *pdev) | static int tusb_probe(struct platform_device *pdev) | ||||||
| { | { | ||||||
|  | @ -1160,7 +1164,7 @@ static int tusb_probe(struct platform_device *pdev) | ||||||
| 	struct musb_hdrc_platform_data	*pdata = dev_get_platdata(&pdev->dev); | 	struct musb_hdrc_platform_data	*pdata = dev_get_platdata(&pdev->dev); | ||||||
| 	struct platform_device		*musb; | 	struct platform_device		*musb; | ||||||
| 	struct tusb6010_glue		*glue; | 	struct tusb6010_glue		*glue; | ||||||
| 
 | 	struct platform_device_info	pinfo; | ||||||
| 	int				ret = -ENOMEM; | 	int				ret = -ENOMEM; | ||||||
| 
 | 
 | ||||||
| 	glue = kzalloc(sizeof(*glue), GFP_KERNEL); | 	glue = kzalloc(sizeof(*glue), GFP_KERNEL); | ||||||
|  | @ -1169,18 +1173,7 @@ static int tusb_probe(struct platform_device *pdev) | ||||||
| 		goto err0; | 		goto err0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); |  | ||||||
| 	if (!musb) { |  | ||||||
| 		dev_err(&pdev->dev, "failed to allocate musb device\n"); |  | ||||||
| 		goto err1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	musb->dev.parent		= &pdev->dev; |  | ||||||
| 	musb->dev.dma_mask		= &tusb_dmamask; |  | ||||||
| 	musb->dev.coherent_dma_mask	= tusb_dmamask; |  | ||||||
| 
 |  | ||||||
| 	glue->dev			= &pdev->dev; | 	glue->dev			= &pdev->dev; | ||||||
| 	glue->musb			= musb; |  | ||||||
| 
 | 
 | ||||||
| 	pdata->platform_ops		= &tusb_ops; | 	pdata->platform_ops		= &tusb_ops; | ||||||
| 
 | 
 | ||||||
|  | @ -1204,31 +1197,23 @@ static int tusb_probe(struct platform_device *pdev) | ||||||
| 	musb_resources[2].end = pdev->resource[2].end; | 	musb_resources[2].end = pdev->resource[2].end; | ||||||
| 	musb_resources[2].flags = pdev->resource[2].flags; | 	musb_resources[2].flags = pdev->resource[2].flags; | ||||||
| 
 | 
 | ||||||
| 	ret = platform_device_add_resources(musb, musb_resources, | 	pinfo = tusb_dev_info; | ||||||
| 			ARRAY_SIZE(musb_resources)); | 	pinfo.parent = &pdev->dev; | ||||||
| 	if (ret) { | 	pinfo.res = musb_resources; | ||||||
| 		dev_err(&pdev->dev, "failed to add resources\n"); | 	pinfo.num_res = ARRAY_SIZE(musb_resources); | ||||||
| 		goto err3; | 	pinfo.data = pdata; | ||||||
| 	} | 	pinfo.size_data = sizeof(*pdata); | ||||||
| 
 | 
 | ||||||
| 	ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); | 	glue->musb = musb = platform_device_register_full(&pinfo); | ||||||
| 	if (ret) { | 	if (IS_ERR(musb)) { | ||||||
| 		dev_err(&pdev->dev, "failed to add platform_data\n"); | 		ret = PTR_ERR(musb); | ||||||
| 		goto err3; | 		dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ret = platform_device_add(musb); |  | ||||||
| 	if (ret) { |  | ||||||
| 		dev_err(&pdev->dev, "failed to register musb device\n"); |  | ||||||
| 		goto err3; | 		goto err3; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| err3: | err3: | ||||||
| 	platform_device_put(musb); |  | ||||||
| 
 |  | ||||||
| err1: |  | ||||||
| 	kfree(glue); | 	kfree(glue); | ||||||
| 
 | 
 | ||||||
| err0: | err0: | ||||||
|  |  | ||||||
|  | @ -376,17 +376,10 @@ static int ux500_resume(struct device *dev) | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| static const struct dev_pm_ops ux500_pm_ops = { |  | ||||||
| 	.suspend	= ux500_suspend, |  | ||||||
| 	.resume		= ux500_resume, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| #define DEV_PM_OPS	(&ux500_pm_ops) |  | ||||||
| #else |  | ||||||
| #define DEV_PM_OPS	NULL |  | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | static SIMPLE_DEV_PM_OPS(ux500_pm_ops, ux500_suspend, ux500_resume); | ||||||
|  | 
 | ||||||
| static const struct of_device_id ux500_match[] = { | static const struct of_device_id ux500_match[] = { | ||||||
|         { .compatible = "stericsson,db8500-musb", }, |         { .compatible = "stericsson,db8500-musb", }, | ||||||
|         {} |         {} | ||||||
|  | @ -397,7 +390,7 @@ static struct platform_driver ux500_driver = { | ||||||
| 	.remove		= ux500_remove, | 	.remove		= ux500_remove, | ||||||
| 	.driver		= { | 	.driver		= { | ||||||
| 		.name	= "musb-ux500", | 		.name	= "musb-ux500", | ||||||
| 		.pm	= DEV_PM_OPS, | 		.pm	= &ux500_pm_ops, | ||||||
| 		.of_match_table = ux500_match, | 		.of_match_table = ux500_match, | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -194,6 +194,19 @@ config USB_RCAR_PHY | ||||||
| 	  To compile this driver as a module, choose M here: the | 	  To compile this driver as a module, choose M here: the | ||||||
| 	  module will be called phy-rcar-usb. | 	  module will be called phy-rcar-usb. | ||||||
| 
 | 
 | ||||||
|  | config USB_RCAR_GEN2_PHY | ||||||
|  | 	tristate "Renesas R-Car Gen2 USB PHY support" | ||||||
|  | 	depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST | ||||||
|  | 	select USB_PHY | ||||||
|  | 	help | ||||||
|  | 	  Say Y here to add support for the Renesas R-Car Gen2 USB PHY driver. | ||||||
|  | 	  It is typically used to control internal USB PHY for USBHS, | ||||||
|  | 	  and to configure shared USB channels 0 and 2. | ||||||
|  | 	  This driver supports R8A7790 and R8A7791. | ||||||
|  | 
 | ||||||
|  | 	  To compile this driver as a module, choose M here: the | ||||||
|  | 	  module will be called phy-rcar-gen2-usb. | ||||||
|  | 
 | ||||||
| config USB_ULPI | config USB_ULPI | ||||||
| 	bool "Generic ULPI Transceiver Driver" | 	bool "Generic ULPI Transceiver Driver" | ||||||
| 	depends on ARM | 	depends on ARM | ||||||
|  |  | ||||||
|  | @ -27,5 +27,6 @@ obj-$(CONFIG_USB_MSM_OTG)		+= phy-msm-usb.o | ||||||
| obj-$(CONFIG_USB_MV_OTG)		+= phy-mv-usb.o | obj-$(CONFIG_USB_MV_OTG)		+= phy-mv-usb.o | ||||||
| obj-$(CONFIG_USB_MXS_PHY)		+= phy-mxs-usb.o | obj-$(CONFIG_USB_MXS_PHY)		+= phy-mxs-usb.o | ||||||
| obj-$(CONFIG_USB_RCAR_PHY)		+= phy-rcar-usb.o | obj-$(CONFIG_USB_RCAR_PHY)		+= phy-rcar-usb.o | ||||||
|  | obj-$(CONFIG_USB_RCAR_GEN2_PHY)		+= phy-rcar-gen2-usb.o | ||||||
| obj-$(CONFIG_USB_ULPI)			+= phy-ulpi.o | obj-$(CONFIG_USB_ULPI)			+= phy-ulpi.o | ||||||
| obj-$(CONFIG_USB_ULPI_VIEWPORT)		+= phy-ulpi-viewport.o | obj-$(CONFIG_USB_ULPI_VIEWPORT)		+= phy-ulpi-viewport.o | ||||||
|  |  | ||||||
|  | @ -26,6 +26,41 @@ struct am335x_control_usb { | ||||||
| #define USBPHY_OTGVDET_EN	(1 << 19) | #define USBPHY_OTGVDET_EN	(1 << 19) | ||||||
| #define USBPHY_OTGSESSEND_EN	(1 << 20) | #define USBPHY_OTGSESSEND_EN	(1 << 20) | ||||||
| 
 | 
 | ||||||
|  | #define AM335X_PHY0_WK_EN	(1 << 0) | ||||||
|  | #define AM335X_PHY1_WK_EN	(1 << 8) | ||||||
|  | 
 | ||||||
|  | static void am335x_phy_wkup(struct  phy_control *phy_ctrl, u32 id, bool on) | ||||||
|  | { | ||||||
|  | 	struct am335x_control_usb *usb_ctrl; | ||||||
|  | 	u32 val; | ||||||
|  | 	u32 reg; | ||||||
|  | 
 | ||||||
|  | 	usb_ctrl = container_of(phy_ctrl, struct am335x_control_usb, phy_ctrl); | ||||||
|  | 
 | ||||||
|  | 	switch (id) { | ||||||
|  | 	case 0: | ||||||
|  | 		reg = AM335X_PHY0_WK_EN; | ||||||
|  | 		break; | ||||||
|  | 	case 1: | ||||||
|  | 		reg = AM335X_PHY1_WK_EN; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		WARN_ON(1); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	spin_lock(&usb_ctrl->lock); | ||||||
|  | 	val = readl(usb_ctrl->wkup); | ||||||
|  | 
 | ||||||
|  | 	if (on) | ||||||
|  | 		val |= reg; | ||||||
|  | 	else | ||||||
|  | 		val &= ~reg; | ||||||
|  | 
 | ||||||
|  | 	writel(val, usb_ctrl->wkup); | ||||||
|  | 	spin_unlock(&usb_ctrl->lock); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on) | static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on) | ||||||
| { | { | ||||||
| 	struct am335x_control_usb *usb_ctrl; | 	struct am335x_control_usb *usb_ctrl; | ||||||
|  | @ -59,6 +94,7 @@ static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on) | ||||||
| 
 | 
 | ||||||
| static const struct phy_control ctrl_am335x = { | static const struct phy_control ctrl_am335x = { | ||||||
| 	.phy_power = am335x_phy_power, | 	.phy_power = am335x_phy_power, | ||||||
|  | 	.phy_wkup = am335x_phy_wkup, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static const struct of_device_id omap_control_usb_id_table[] = { | static const struct of_device_id omap_control_usb_id_table[] = { | ||||||
|  | @ -117,6 +153,12 @@ static int am335x_control_usb_probe(struct platform_device *pdev) | ||||||
| 	ctrl_usb->phy_reg = devm_ioremap_resource(&pdev->dev, res); | 	ctrl_usb->phy_reg = devm_ioremap_resource(&pdev->dev, res); | ||||||
| 	if (IS_ERR(ctrl_usb->phy_reg)) | 	if (IS_ERR(ctrl_usb->phy_reg)) | ||||||
| 		return PTR_ERR(ctrl_usb->phy_reg); | 		return PTR_ERR(ctrl_usb->phy_reg); | ||||||
|  | 
 | ||||||
|  | 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wakeup"); | ||||||
|  | 	ctrl_usb->wkup = devm_ioremap_resource(&pdev->dev, res); | ||||||
|  | 	if (IS_ERR(ctrl_usb->wkup)) | ||||||
|  | 		return PTR_ERR(ctrl_usb->wkup); | ||||||
|  | 
 | ||||||
| 	spin_lock_init(&ctrl_usb->lock); | 	spin_lock_init(&ctrl_usb->lock); | ||||||
| 	ctrl_usb->phy_ctrl = *phy_ctrl; | 	ctrl_usb->phy_ctrl = *phy_ctrl; | ||||||
| 
 | 
 | ||||||
|  | @ -129,7 +171,7 @@ static struct platform_driver am335x_control_driver = { | ||||||
| 	.driver		= { | 	.driver		= { | ||||||
| 		.name	= "am335x-control-usb", | 		.name	= "am335x-control-usb", | ||||||
| 		.owner	= THIS_MODULE, | 		.owner	= THIS_MODULE, | ||||||
| 		.of_match_table = of_match_ptr(omap_control_usb_id_table), | 		.of_match_table = omap_control_usb_id_table, | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -53,21 +53,20 @@ static int am335x_phy_probe(struct platform_device *pdev) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, | 	ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, | ||||||
| 			USB_PHY_TYPE_USB2, 0, false, false); | 			USB_PHY_TYPE_USB2, 0, false); | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		return ret; | 		return ret; | ||||||
| 
 | 
 | ||||||
| 	ret = usb_add_phy_dev(&am_phy->usb_phy_gen.phy); | 	ret = usb_add_phy_dev(&am_phy->usb_phy_gen.phy); | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		goto err_add; | 		return ret; | ||||||
| 	am_phy->usb_phy_gen.phy.init = am335x_init; | 	am_phy->usb_phy_gen.phy.init = am335x_init; | ||||||
| 	am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown; | 	am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown; | ||||||
| 
 | 
 | ||||||
| 	platform_set_drvdata(pdev, am_phy); | 	platform_set_drvdata(pdev, am_phy); | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| err_add: |  | ||||||
| 	usb_phy_gen_cleanup_phy(&am_phy->usb_phy_gen); |  | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -79,6 +78,40 @@ static int am335x_phy_remove(struct platform_device *pdev) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #ifdef CONFIG_PM_RUNTIME | ||||||
|  | 
 | ||||||
|  | static int am335x_phy_runtime_suspend(struct device *dev) | ||||||
|  | { | ||||||
|  | 	struct platform_device	*pdev = to_platform_device(dev); | ||||||
|  | 	struct am335x_phy *am_phy = platform_get_drvdata(pdev); | ||||||
|  | 
 | ||||||
|  | 	if (device_may_wakeup(dev)) | ||||||
|  | 		phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, true); | ||||||
|  | 	phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int am335x_phy_runtime_resume(struct device *dev) | ||||||
|  | { | ||||||
|  | 	struct platform_device	*pdev = to_platform_device(dev); | ||||||
|  | 	struct am335x_phy	*am_phy = platform_get_drvdata(pdev); | ||||||
|  | 
 | ||||||
|  | 	phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, true); | ||||||
|  | 	if (device_may_wakeup(dev)) | ||||||
|  | 		phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, false); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct dev_pm_ops am335x_pm_ops = { | ||||||
|  | 	SET_RUNTIME_PM_OPS(am335x_phy_runtime_suspend, | ||||||
|  | 			am335x_phy_runtime_resume, NULL) | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define DEV_PM_OPS	(&am335x_pm_ops) | ||||||
|  | #else | ||||||
|  | #define DEV_PM_OPS	NULL | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| static const struct of_device_id am335x_phy_ids[] = { | static const struct of_device_id am335x_phy_ids[] = { | ||||||
| 	{ .compatible = "ti,am335x-usb-phy" }, | 	{ .compatible = "ti,am335x-usb-phy" }, | ||||||
| 	{ } | 	{ } | ||||||
|  | @ -91,7 +124,8 @@ static struct platform_driver am335x_phy_driver = { | ||||||
| 	.driver         = { | 	.driver         = { | ||||||
| 		.name   = "am335x-phy-driver", | 		.name   = "am335x-phy-driver", | ||||||
| 		.owner  = THIS_MODULE, | 		.owner  = THIS_MODULE, | ||||||
| 		.of_match_table = of_match_ptr(am335x_phy_ids), | 		.pm = DEV_PM_OPS, | ||||||
|  | 		.of_match_table = am335x_phy_ids, | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -134,7 +134,7 @@ int write_ulpi(u8 addr, u8 data) | ||||||
| /* Operations that will be called from OTG Finite State Machine */ | /* Operations that will be called from OTG Finite State Machine */ | ||||||
| 
 | 
 | ||||||
| /* Charge vbus for vbus pulsing in SRP */ | /* Charge vbus for vbus pulsing in SRP */ | ||||||
| void fsl_otg_chrg_vbus(int on) | void fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on) | ||||||
| { | { | ||||||
| 	u32 tmp; | 	u32 tmp; | ||||||
| 
 | 
 | ||||||
|  | @ -170,7 +170,7 @@ void fsl_otg_dischrg_vbus(int on) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* A-device driver vbus, controlled through PP bit in PORTSC */ | /* A-device driver vbus, controlled through PP bit in PORTSC */ | ||||||
| void fsl_otg_drv_vbus(int on) | void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on) | ||||||
| { | { | ||||||
| 	u32 tmp; | 	u32 tmp; | ||||||
| 
 | 
 | ||||||
|  | @ -188,7 +188,7 @@ void fsl_otg_drv_vbus(int on) | ||||||
|  * Pull-up D+, signalling connect by periperal. Also used in |  * Pull-up D+, signalling connect by periperal. Also used in | ||||||
|  * data-line pulsing in SRP |  * data-line pulsing in SRP | ||||||
|  */ |  */ | ||||||
| void fsl_otg_loc_conn(int on) | void fsl_otg_loc_conn(struct otg_fsm *fsm, int on) | ||||||
| { | { | ||||||
| 	u32 tmp; | 	u32 tmp; | ||||||
| 
 | 
 | ||||||
|  | @ -207,7 +207,7 @@ void fsl_otg_loc_conn(int on) | ||||||
|  * port.  In host mode, controller will automatically send SOF. |  * port.  In host mode, controller will automatically send SOF. | ||||||
|  * Suspend will block the data on the port. |  * Suspend will block the data on the port. | ||||||
|  */ |  */ | ||||||
| void fsl_otg_loc_sof(int on) | void fsl_otg_loc_sof(struct otg_fsm *fsm, int on) | ||||||
| { | { | ||||||
| 	u32 tmp; | 	u32 tmp; | ||||||
| 
 | 
 | ||||||
|  | @ -222,7 +222,7 @@ void fsl_otg_loc_sof(int on) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */ | /* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */ | ||||||
| void fsl_otg_start_pulse(void) | void fsl_otg_start_pulse(struct otg_fsm *fsm) | ||||||
| { | { | ||||||
| 	u32 tmp; | 	u32 tmp; | ||||||
| 
 | 
 | ||||||
|  | @ -235,7 +235,7 @@ void fsl_otg_start_pulse(void) | ||||||
| 	fsl_otg_loc_conn(1); | 	fsl_otg_loc_conn(1); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| 	fsl_otg_add_timer(b_data_pulse_tmr); | 	fsl_otg_add_timer(fsm, b_data_pulse_tmr); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void b_data_pulse_end(unsigned long foo) | void b_data_pulse_end(unsigned long foo) | ||||||
|  | @ -252,14 +252,14 @@ void b_data_pulse_end(unsigned long foo) | ||||||
| void fsl_otg_pulse_vbus(void) | void fsl_otg_pulse_vbus(void) | ||||||
| { | { | ||||||
| 	srp_wait_done = 0; | 	srp_wait_done = 0; | ||||||
| 	fsl_otg_chrg_vbus(1); | 	fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 1); | ||||||
| 	/* start the timer to end vbus charge */ | 	/* start the timer to end vbus charge */ | ||||||
| 	fsl_otg_add_timer(b_vbus_pulse_tmr); | 	fsl_otg_add_timer(&fsl_otg_dev->fsm, b_vbus_pulse_tmr); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void b_vbus_pulse_end(unsigned long foo) | void b_vbus_pulse_end(unsigned long foo) | ||||||
| { | { | ||||||
| 	fsl_otg_chrg_vbus(0); | 	fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 0); | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * As USB3300 using the same a_sess_vld and b_sess_vld voltage | 	 * As USB3300 using the same a_sess_vld and b_sess_vld voltage | ||||||
|  | @ -267,7 +267,7 @@ void b_vbus_pulse_end(unsigned long foo) | ||||||
| 	 * residual voltage of vbus pulsing and A device pull up | 	 * residual voltage of vbus pulsing and A device pull up | ||||||
| 	 */ | 	 */ | ||||||
| 	fsl_otg_dischrg_vbus(1); | 	fsl_otg_dischrg_vbus(1); | ||||||
| 	fsl_otg_add_timer(b_srp_wait_tmr); | 	fsl_otg_add_timer(&fsl_otg_dev->fsm, b_srp_wait_tmr); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void b_srp_end(unsigned long foo) | void b_srp_end(unsigned long foo) | ||||||
|  | @ -289,7 +289,7 @@ void a_wait_enum(unsigned long foo) | ||||||
| { | { | ||||||
| 	VDBG("a_wait_enum timeout\n"); | 	VDBG("a_wait_enum timeout\n"); | ||||||
| 	if (!fsl_otg_dev->phy.otg->host->b_hnp_enable) | 	if (!fsl_otg_dev->phy.otg->host->b_hnp_enable) | ||||||
| 		fsl_otg_add_timer(a_wait_enum_tmr); | 		fsl_otg_add_timer(&fsl_otg_dev->fsm, a_wait_enum_tmr); | ||||||
| 	else | 	else | ||||||
| 		otg_statemachine(&fsl_otg_dev->fsm); | 		otg_statemachine(&fsl_otg_dev->fsm); | ||||||
| } | } | ||||||
|  | @ -375,8 +375,42 @@ void fsl_otg_uninit_timers(void) | ||||||
| 	kfree(b_vbus_pulse_tmr); | 	kfree(b_vbus_pulse_tmr); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static struct fsl_otg_timer *fsl_otg_get_timer(enum otg_fsm_timer t) | ||||||
|  | { | ||||||
|  | 	struct fsl_otg_timer *timer; | ||||||
|  | 
 | ||||||
|  | 	/* REVISIT: use array of pointers to timers instead */ | ||||||
|  | 	switch (t) { | ||||||
|  | 	case A_WAIT_VRISE: | ||||||
|  | 		timer = a_wait_vrise_tmr; | ||||||
|  | 		break; | ||||||
|  | 	case A_WAIT_BCON: | ||||||
|  | 		timer = a_wait_vrise_tmr; | ||||||
|  | 		break; | ||||||
|  | 	case A_AIDL_BDIS: | ||||||
|  | 		timer = a_wait_vrise_tmr; | ||||||
|  | 		break; | ||||||
|  | 	case B_ASE0_BRST: | ||||||
|  | 		timer = a_wait_vrise_tmr; | ||||||
|  | 		break; | ||||||
|  | 	case B_SE0_SRP: | ||||||
|  | 		timer = a_wait_vrise_tmr; | ||||||
|  | 		break; | ||||||
|  | 	case B_SRP_FAIL: | ||||||
|  | 		timer = a_wait_vrise_tmr; | ||||||
|  | 		break; | ||||||
|  | 	case A_WAIT_ENUM: | ||||||
|  | 		timer = a_wait_vrise_tmr; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		timer = NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return timer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Add timer to timer list */ | /* Add timer to timer list */ | ||||||
| void fsl_otg_add_timer(void *gtimer) | void fsl_otg_add_timer(struct otg_fsm *fsm, void *gtimer) | ||||||
| { | { | ||||||
| 	struct fsl_otg_timer *timer = gtimer; | 	struct fsl_otg_timer *timer = gtimer; | ||||||
| 	struct fsl_otg_timer *tmp_timer; | 	struct fsl_otg_timer *tmp_timer; | ||||||
|  | @ -394,8 +428,19 @@ void fsl_otg_add_timer(void *gtimer) | ||||||
| 	list_add_tail(&timer->list, &active_timers); | 	list_add_tail(&timer->list, &active_timers); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void fsl_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t) | ||||||
|  | { | ||||||
|  | 	struct fsl_otg_timer *timer; | ||||||
|  | 
 | ||||||
|  | 	timer = fsl_otg_get_timer(t); | ||||||
|  | 	if (!timer) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	fsl_otg_add_timer(fsm, timer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Remove timer from the timer list; clear timeout status */ | /* Remove timer from the timer list; clear timeout status */ | ||||||
| void fsl_otg_del_timer(void *gtimer) | void fsl_otg_del_timer(struct otg_fsm *fsm, void *gtimer) | ||||||
| { | { | ||||||
| 	struct fsl_otg_timer *timer = gtimer; | 	struct fsl_otg_timer *timer = gtimer; | ||||||
| 	struct fsl_otg_timer *tmp_timer, *del_tmp; | 	struct fsl_otg_timer *tmp_timer, *del_tmp; | ||||||
|  | @ -405,6 +450,17 @@ void fsl_otg_del_timer(void *gtimer) | ||||||
| 			list_del(&timer->list); | 			list_del(&timer->list); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t) | ||||||
|  | { | ||||||
|  | 	struct fsl_otg_timer *timer; | ||||||
|  | 
 | ||||||
|  | 	timer = fsl_otg_get_timer(t); | ||||||
|  | 	if (!timer) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	fsl_otg_del_timer(fsm, timer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Reduce timer count by 1, and find timeout conditions. |  * Reduce timer count by 1, and find timeout conditions. | ||||||
|  * Called by fsl_otg 1ms timer interrupt |  * Called by fsl_otg 1ms timer interrupt | ||||||
|  | @ -468,7 +524,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on) | ||||||
| 				retval = dev->driver->pm->resume(dev); | 				retval = dev->driver->pm->resume(dev); | ||||||
| 				if (fsm->id) { | 				if (fsm->id) { | ||||||
| 					/* default-b */ | 					/* default-b */ | ||||||
| 					fsl_otg_drv_vbus(1); | 					fsl_otg_drv_vbus(fsm, 1); | ||||||
| 					/*
 | 					/*
 | ||||||
| 					 * Workaround: b_host can't driver | 					 * Workaround: b_host can't driver | ||||||
| 					 * vbus, but PP in PORTSC needs to | 					 * vbus, but PP in PORTSC needs to | ||||||
|  | @ -493,7 +549,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on) | ||||||
| 					retval = dev->driver->pm->suspend(dev); | 					retval = dev->driver->pm->suspend(dev); | ||||||
| 				if (fsm->id) | 				if (fsm->id) | ||||||
| 					/* default-b */ | 					/* default-b */ | ||||||
| 					fsl_otg_drv_vbus(0); | 					fsl_otg_drv_vbus(fsm, 0); | ||||||
| 			} | 			} | ||||||
| 			otg_dev->host_working = 0; | 			otg_dev->host_working = 0; | ||||||
| 		} | 		} | ||||||
|  | @ -757,8 +813,8 @@ static struct otg_fsm_ops fsl_otg_ops = { | ||||||
| 	.loc_sof = fsl_otg_loc_sof, | 	.loc_sof = fsl_otg_loc_sof, | ||||||
| 	.start_pulse = fsl_otg_start_pulse, | 	.start_pulse = fsl_otg_start_pulse, | ||||||
| 
 | 
 | ||||||
| 	.add_timer = fsl_otg_add_timer, | 	.add_timer = fsl_otg_fsm_add_timer, | ||||||
| 	.del_timer = fsl_otg_del_timer, | 	.del_timer = fsl_otg_fsm_del_timer, | ||||||
| 
 | 
 | ||||||
| 	.start_host = fsl_otg_start_host, | 	.start_host = fsl_otg_start_host, | ||||||
| 	.start_gadget = fsl_otg_start_gadget, | 	.start_gadget = fsl_otg_start_gadget, | ||||||
|  | @ -1011,7 +1067,7 @@ static int show_fsl_usb2_otg_state(struct device *dev, | ||||||
| 			"b_bus_suspend: %d\n" | 			"b_bus_suspend: %d\n" | ||||||
| 			"b_conn: %d\n" | 			"b_conn: %d\n" | ||||||
| 			"b_se0_srp: %d\n" | 			"b_se0_srp: %d\n" | ||||||
| 			"b_sess_end: %d\n" | 			"b_ssend_srp: %d\n" | ||||||
| 			"b_sess_vld: %d\n" | 			"b_sess_vld: %d\n" | ||||||
| 			"id: %d\n", | 			"id: %d\n", | ||||||
| 			fsm->a_bus_req, | 			fsm->a_bus_req, | ||||||
|  | @ -1026,7 +1082,7 @@ static int show_fsl_usb2_otg_state(struct device *dev, | ||||||
| 			fsm->b_bus_suspend, | 			fsm->b_bus_suspend, | ||||||
| 			fsm->b_conn, | 			fsm->b_conn, | ||||||
| 			fsm->b_se0_srp, | 			fsm->b_se0_srp, | ||||||
| 			fsm->b_sess_end, | 			fsm->b_ssend_srp, | ||||||
| 			fsm->b_sess_vld, | 			fsm->b_sess_vld, | ||||||
| 			fsm->id); | 			fsm->id); | ||||||
| 	size -= t; | 	size -= t; | ||||||
|  | @ -1057,7 +1113,7 @@ static long fsl_otg_ioctl(struct file *file, unsigned int cmd, | ||||||
| 		break; | 		break; | ||||||
| 
 | 
 | ||||||
| 	case SET_A_SUSPEND_REQ: | 	case SET_A_SUSPEND_REQ: | ||||||
| 		fsl_otg_dev->fsm.a_suspend_req = arg; | 		fsl_otg_dev->fsm.a_suspend_req_inf = arg; | ||||||
| 		break; | 		break; | ||||||
| 
 | 
 | ||||||
| 	case SET_A_BUS_DROP: | 	case SET_A_BUS_DROP: | ||||||
|  |  | ||||||
|  | @ -401,6 +401,6 @@ struct fsl_otg_config { | ||||||
| #define GET_A_BUS_REQ		_IOR(OTG_IOCTL_MAGIC, 8, int) | #define GET_A_BUS_REQ		_IOR(OTG_IOCTL_MAGIC, 8, int) | ||||||
| #define GET_B_BUS_REQ		_IOR(OTG_IOCTL_MAGIC, 9, int) | #define GET_B_BUS_REQ		_IOR(OTG_IOCTL_MAGIC, 9, int) | ||||||
| 
 | 
 | ||||||
| void fsl_otg_add_timer(void *timer); | void fsl_otg_add_timer(struct otg_fsm *fsm, void *timer); | ||||||
| void fsl_otg_del_timer(void *timer); | void fsl_otg_del_timer(struct otg_fsm *fsm, void *timer); | ||||||
| void fsl_otg_pulse_vbus(void); | void fsl_otg_pulse_vbus(void); | ||||||
|  |  | ||||||
|  | @ -41,17 +41,17 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol) | ||||||
| 			fsm->protocol, protocol); | 			fsm->protocol, protocol); | ||||||
| 		/* stop old protocol */ | 		/* stop old protocol */ | ||||||
| 		if (fsm->protocol == PROTO_HOST) | 		if (fsm->protocol == PROTO_HOST) | ||||||
| 			ret = fsm->ops->start_host(fsm, 0); | 			ret = otg_start_host(fsm, 0); | ||||||
| 		else if (fsm->protocol == PROTO_GADGET) | 		else if (fsm->protocol == PROTO_GADGET) | ||||||
| 			ret = fsm->ops->start_gadget(fsm, 0); | 			ret = otg_start_gadget(fsm, 0); | ||||||
| 		if (ret) | 		if (ret) | ||||||
| 			return ret; | 			return ret; | ||||||
| 
 | 
 | ||||||
| 		/* start new protocol */ | 		/* start new protocol */ | ||||||
| 		if (protocol == PROTO_HOST) | 		if (protocol == PROTO_HOST) | ||||||
| 			ret = fsm->ops->start_host(fsm, 1); | 			ret = otg_start_host(fsm, 1); | ||||||
| 		else if (protocol == PROTO_GADGET) | 		else if (protocol == PROTO_GADGET) | ||||||
| 			ret = fsm->ops->start_gadget(fsm, 1); | 			ret = otg_start_gadget(fsm, 1); | ||||||
| 		if (ret) | 		if (ret) | ||||||
| 			return ret; | 			return ret; | ||||||
| 
 | 
 | ||||||
|  | @ -69,42 +69,50 @@ void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) | ||||||
| { | { | ||||||
| 	switch (old_state) { | 	switch (old_state) { | ||||||
| 	case OTG_STATE_B_IDLE: | 	case OTG_STATE_B_IDLE: | ||||||
| 		otg_del_timer(fsm, b_se0_srp_tmr); | 		otg_del_timer(fsm, B_SE0_SRP); | ||||||
| 		fsm->b_se0_srp = 0; | 		fsm->b_se0_srp = 0; | ||||||
|  | 		fsm->adp_sns = 0; | ||||||
|  | 		fsm->adp_prb = 0; | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_B_SRP_INIT: | 	case OTG_STATE_B_SRP_INIT: | ||||||
|  | 		fsm->data_pulse = 0; | ||||||
| 		fsm->b_srp_done = 0; | 		fsm->b_srp_done = 0; | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_B_PERIPHERAL: | 	case OTG_STATE_B_PERIPHERAL: | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_B_WAIT_ACON: | 	case OTG_STATE_B_WAIT_ACON: | ||||||
| 		otg_del_timer(fsm, b_ase0_brst_tmr); | 		otg_del_timer(fsm, B_ASE0_BRST); | ||||||
| 		fsm->b_ase0_brst_tmout = 0; | 		fsm->b_ase0_brst_tmout = 0; | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_B_HOST: | 	case OTG_STATE_B_HOST: | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_IDLE: | 	case OTG_STATE_A_IDLE: | ||||||
|  | 		fsm->adp_prb = 0; | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_WAIT_VRISE: | 	case OTG_STATE_A_WAIT_VRISE: | ||||||
| 		otg_del_timer(fsm, a_wait_vrise_tmr); | 		otg_del_timer(fsm, A_WAIT_VRISE); | ||||||
| 		fsm->a_wait_vrise_tmout = 0; | 		fsm->a_wait_vrise_tmout = 0; | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_WAIT_BCON: | 	case OTG_STATE_A_WAIT_BCON: | ||||||
| 		otg_del_timer(fsm, a_wait_bcon_tmr); | 		otg_del_timer(fsm, A_WAIT_BCON); | ||||||
| 		fsm->a_wait_bcon_tmout = 0; | 		fsm->a_wait_bcon_tmout = 0; | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_HOST: | 	case OTG_STATE_A_HOST: | ||||||
| 		otg_del_timer(fsm, a_wait_enum_tmr); | 		otg_del_timer(fsm, A_WAIT_ENUM); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_SUSPEND: | 	case OTG_STATE_A_SUSPEND: | ||||||
| 		otg_del_timer(fsm, a_aidl_bdis_tmr); | 		otg_del_timer(fsm, A_AIDL_BDIS); | ||||||
| 		fsm->a_aidl_bdis_tmout = 0; | 		fsm->a_aidl_bdis_tmout = 0; | ||||||
| 		fsm->a_suspend_req = 0; | 		fsm->a_suspend_req_inf = 0; | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_PERIPHERAL: | 	case OTG_STATE_A_PERIPHERAL: | ||||||
|  | 		otg_del_timer(fsm, A_BIDL_ADIS); | ||||||
|  | 		fsm->a_bidl_adis_tmout = 0; | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_WAIT_VFALL: | 	case OTG_STATE_A_WAIT_VFALL: | ||||||
| 		otg_del_timer(fsm, a_wait_vrise_tmr); | 		otg_del_timer(fsm, A_WAIT_VFALL); | ||||||
|  | 		fsm->a_wait_vfall_tmout = 0; | ||||||
|  | 		otg_del_timer(fsm, A_WAIT_VRISE); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_VBUS_ERR: | 	case OTG_STATE_A_VBUS_ERR: | ||||||
| 		break; | 		break; | ||||||
|  | @ -127,14 +135,19 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) | ||||||
| 		otg_chrg_vbus(fsm, 0); | 		otg_chrg_vbus(fsm, 0); | ||||||
| 		otg_loc_conn(fsm, 0); | 		otg_loc_conn(fsm, 0); | ||||||
| 		otg_loc_sof(fsm, 0); | 		otg_loc_sof(fsm, 0); | ||||||
|  | 		/*
 | ||||||
|  | 		 * Driver is responsible for starting ADP probing | ||||||
|  | 		 * if ADP sensing times out. | ||||||
|  | 		 */ | ||||||
|  | 		otg_start_adp_sns(fsm); | ||||||
| 		otg_set_protocol(fsm, PROTO_UNDEF); | 		otg_set_protocol(fsm, PROTO_UNDEF); | ||||||
| 		otg_add_timer(fsm, b_se0_srp_tmr); | 		otg_add_timer(fsm, B_SE0_SRP); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_B_SRP_INIT: | 	case OTG_STATE_B_SRP_INIT: | ||||||
| 		otg_start_pulse(fsm); | 		otg_start_pulse(fsm); | ||||||
| 		otg_loc_sof(fsm, 0); | 		otg_loc_sof(fsm, 0); | ||||||
| 		otg_set_protocol(fsm, PROTO_UNDEF); | 		otg_set_protocol(fsm, PROTO_UNDEF); | ||||||
| 		otg_add_timer(fsm, b_srp_fail_tmr); | 		otg_add_timer(fsm, B_SRP_FAIL); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_B_PERIPHERAL: | 	case OTG_STATE_B_PERIPHERAL: | ||||||
| 		otg_chrg_vbus(fsm, 0); | 		otg_chrg_vbus(fsm, 0); | ||||||
|  | @ -147,7 +160,7 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) | ||||||
| 		otg_loc_conn(fsm, 0); | 		otg_loc_conn(fsm, 0); | ||||||
| 		otg_loc_sof(fsm, 0); | 		otg_loc_sof(fsm, 0); | ||||||
| 		otg_set_protocol(fsm, PROTO_HOST); | 		otg_set_protocol(fsm, PROTO_HOST); | ||||||
| 		otg_add_timer(fsm, b_ase0_brst_tmr); | 		otg_add_timer(fsm, B_ASE0_BRST); | ||||||
| 		fsm->a_bus_suspend = 0; | 		fsm->a_bus_suspend = 0; | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_B_HOST: | 	case OTG_STATE_B_HOST: | ||||||
|  | @ -163,6 +176,7 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) | ||||||
| 		otg_chrg_vbus(fsm, 0); | 		otg_chrg_vbus(fsm, 0); | ||||||
| 		otg_loc_conn(fsm, 0); | 		otg_loc_conn(fsm, 0); | ||||||
| 		otg_loc_sof(fsm, 0); | 		otg_loc_sof(fsm, 0); | ||||||
|  | 		otg_start_adp_prb(fsm); | ||||||
| 		otg_set_protocol(fsm, PROTO_HOST); | 		otg_set_protocol(fsm, PROTO_HOST); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_WAIT_VRISE: | 	case OTG_STATE_A_WAIT_VRISE: | ||||||
|  | @ -170,14 +184,14 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) | ||||||
| 		otg_loc_conn(fsm, 0); | 		otg_loc_conn(fsm, 0); | ||||||
| 		otg_loc_sof(fsm, 0); | 		otg_loc_sof(fsm, 0); | ||||||
| 		otg_set_protocol(fsm, PROTO_HOST); | 		otg_set_protocol(fsm, PROTO_HOST); | ||||||
| 		otg_add_timer(fsm, a_wait_vrise_tmr); | 		otg_add_timer(fsm, A_WAIT_VRISE); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_WAIT_BCON: | 	case OTG_STATE_A_WAIT_BCON: | ||||||
| 		otg_drv_vbus(fsm, 1); | 		otg_drv_vbus(fsm, 1); | ||||||
| 		otg_loc_conn(fsm, 0); | 		otg_loc_conn(fsm, 0); | ||||||
| 		otg_loc_sof(fsm, 0); | 		otg_loc_sof(fsm, 0); | ||||||
| 		otg_set_protocol(fsm, PROTO_HOST); | 		otg_set_protocol(fsm, PROTO_HOST); | ||||||
| 		otg_add_timer(fsm, a_wait_bcon_tmr); | 		otg_add_timer(fsm, A_WAIT_BCON); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_HOST: | 	case OTG_STATE_A_HOST: | ||||||
| 		otg_drv_vbus(fsm, 1); | 		otg_drv_vbus(fsm, 1); | ||||||
|  | @ -188,15 +202,15 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) | ||||||
| 		 * When HNP is triggered while a_bus_req = 0, a_host will | 		 * When HNP is triggered while a_bus_req = 0, a_host will | ||||||
| 		 * suspend too fast to complete a_set_b_hnp_en | 		 * suspend too fast to complete a_set_b_hnp_en | ||||||
| 		 */ | 		 */ | ||||||
| 		if (!fsm->a_bus_req || fsm->a_suspend_req) | 		if (!fsm->a_bus_req || fsm->a_suspend_req_inf) | ||||||
| 			otg_add_timer(fsm, a_wait_enum_tmr); | 			otg_add_timer(fsm, A_WAIT_ENUM); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_SUSPEND: | 	case OTG_STATE_A_SUSPEND: | ||||||
| 		otg_drv_vbus(fsm, 1); | 		otg_drv_vbus(fsm, 1); | ||||||
| 		otg_loc_conn(fsm, 0); | 		otg_loc_conn(fsm, 0); | ||||||
| 		otg_loc_sof(fsm, 0); | 		otg_loc_sof(fsm, 0); | ||||||
| 		otg_set_protocol(fsm, PROTO_HOST); | 		otg_set_protocol(fsm, PROTO_HOST); | ||||||
| 		otg_add_timer(fsm, a_aidl_bdis_tmr); | 		otg_add_timer(fsm, A_AIDL_BDIS); | ||||||
| 
 | 
 | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_PERIPHERAL: | 	case OTG_STATE_A_PERIPHERAL: | ||||||
|  | @ -204,12 +218,14 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) | ||||||
| 		otg_loc_sof(fsm, 0); | 		otg_loc_sof(fsm, 0); | ||||||
| 		otg_set_protocol(fsm, PROTO_GADGET); | 		otg_set_protocol(fsm, PROTO_GADGET); | ||||||
| 		otg_drv_vbus(fsm, 1); | 		otg_drv_vbus(fsm, 1); | ||||||
|  | 		otg_add_timer(fsm, A_BIDL_ADIS); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_WAIT_VFALL: | 	case OTG_STATE_A_WAIT_VFALL: | ||||||
| 		otg_drv_vbus(fsm, 0); | 		otg_drv_vbus(fsm, 0); | ||||||
| 		otg_loc_conn(fsm, 0); | 		otg_loc_conn(fsm, 0); | ||||||
| 		otg_loc_sof(fsm, 0); | 		otg_loc_sof(fsm, 0); | ||||||
| 		otg_set_protocol(fsm, PROTO_HOST); | 		otg_set_protocol(fsm, PROTO_HOST); | ||||||
|  | 		otg_add_timer(fsm, A_WAIT_VFALL); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_VBUS_ERR: | 	case OTG_STATE_A_VBUS_ERR: | ||||||
| 		otg_drv_vbus(fsm, 0); | 		otg_drv_vbus(fsm, 0); | ||||||
|  | @ -250,7 +266,8 @@ int otg_statemachine(struct otg_fsm *fsm) | ||||||
| 			otg_set_state(fsm, OTG_STATE_A_IDLE); | 			otg_set_state(fsm, OTG_STATE_A_IDLE); | ||||||
| 		else if (fsm->b_sess_vld && fsm->otg->gadget) | 		else if (fsm->b_sess_vld && fsm->otg->gadget) | ||||||
| 			otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); | 			otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); | ||||||
| 		else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp) | 		else if ((fsm->b_bus_req || fsm->adp_change || fsm->power_up) && | ||||||
|  | 				fsm->b_ssend_srp && fsm->b_se0_srp) | ||||||
| 			otg_set_state(fsm, OTG_STATE_B_SRP_INIT); | 			otg_set_state(fsm, OTG_STATE_B_SRP_INIT); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_B_SRP_INIT: | 	case OTG_STATE_B_SRP_INIT: | ||||||
|  | @ -277,13 +294,14 @@ int otg_statemachine(struct otg_fsm *fsm) | ||||||
| 	case OTG_STATE_B_HOST: | 	case OTG_STATE_B_HOST: | ||||||
| 		if (!fsm->id || !fsm->b_sess_vld) | 		if (!fsm->id || !fsm->b_sess_vld) | ||||||
| 			otg_set_state(fsm, OTG_STATE_B_IDLE); | 			otg_set_state(fsm, OTG_STATE_B_IDLE); | ||||||
| 		else if (!fsm->b_bus_req || !fsm->a_conn) | 		else if (!fsm->b_bus_req || !fsm->a_conn || fsm->test_device) | ||||||
| 			otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); | 			otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_IDLE: | 	case OTG_STATE_A_IDLE: | ||||||
| 		if (fsm->id) | 		if (fsm->id) | ||||||
| 			otg_set_state(fsm, OTG_STATE_B_IDLE); | 			otg_set_state(fsm, OTG_STATE_B_IDLE); | ||||||
| 		else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det)) | 		else if (!fsm->a_bus_drop && (fsm->a_bus_req || | ||||||
|  | 			  fsm->a_srp_det || fsm->adp_change || fsm->power_up)) | ||||||
| 			otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE); | 			otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_WAIT_VRISE: | 	case OTG_STATE_A_WAIT_VRISE: | ||||||
|  | @ -301,7 +319,7 @@ int otg_statemachine(struct otg_fsm *fsm) | ||||||
| 			otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); | 			otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_HOST: | 	case OTG_STATE_A_HOST: | ||||||
| 		if ((!fsm->a_bus_req || fsm->a_suspend_req) && | 		if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) && | ||||||
| 				fsm->otg->host->b_hnp_enable) | 				fsm->otg->host->b_hnp_enable) | ||||||
| 			otg_set_state(fsm, OTG_STATE_A_SUSPEND); | 			otg_set_state(fsm, OTG_STATE_A_SUSPEND); | ||||||
| 		else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop) | 		else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop) | ||||||
|  | @ -324,14 +342,14 @@ int otg_statemachine(struct otg_fsm *fsm) | ||||||
| 	case OTG_STATE_A_PERIPHERAL: | 	case OTG_STATE_A_PERIPHERAL: | ||||||
| 		if (fsm->id || fsm->a_bus_drop) | 		if (fsm->id || fsm->a_bus_drop) | ||||||
| 			otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); | 			otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); | ||||||
| 		else if (fsm->b_bus_suspend) | 		else if (fsm->a_bidl_adis_tmout || fsm->b_bus_suspend) | ||||||
| 			otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); | 			otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); | ||||||
| 		else if (!fsm->a_vbus_vld) | 		else if (!fsm->a_vbus_vld) | ||||||
| 			otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); | 			otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_WAIT_VFALL: | 	case OTG_STATE_A_WAIT_VFALL: | ||||||
| 		if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld && | 		if (fsm->a_wait_vfall_tmout || fsm->id || fsm->a_bus_req || | ||||||
| 					!fsm->b_conn)) | 				(!fsm->a_sess_vld && !fsm->b_conn)) | ||||||
| 			otg_set_state(fsm, OTG_STATE_A_IDLE); | 			otg_set_state(fsm, OTG_STATE_A_IDLE); | ||||||
| 		break; | 		break; | ||||||
| 	case OTG_STATE_A_VBUS_ERR: | 	case OTG_STATE_A_VBUS_ERR: | ||||||
|  |  | ||||||
|  | @ -34,45 +34,76 @@ | ||||||
| #define PROTO_HOST	(1) | #define PROTO_HOST	(1) | ||||||
| #define PROTO_GADGET	(2) | #define PROTO_GADGET	(2) | ||||||
| 
 | 
 | ||||||
|  | enum otg_fsm_timer { | ||||||
|  | 	/* Standard OTG timers */ | ||||||
|  | 	A_WAIT_VRISE, | ||||||
|  | 	A_WAIT_VFALL, | ||||||
|  | 	A_WAIT_BCON, | ||||||
|  | 	A_AIDL_BDIS, | ||||||
|  | 	B_ASE0_BRST, | ||||||
|  | 	A_BIDL_ADIS, | ||||||
|  | 
 | ||||||
|  | 	/* Auxiliary timers */ | ||||||
|  | 	B_SE0_SRP, | ||||||
|  | 	B_SRP_FAIL, | ||||||
|  | 	A_WAIT_ENUM, | ||||||
|  | 
 | ||||||
|  | 	NUM_OTG_FSM_TIMERS, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /* OTG state machine according to the OTG spec */ | /* OTG state machine according to the OTG spec */ | ||||||
| struct otg_fsm { | struct otg_fsm { | ||||||
| 	/* Input */ | 	/* Input */ | ||||||
|  | 	int id; | ||||||
|  | 	int adp_change; | ||||||
|  | 	int power_up; | ||||||
|  | 	int test_device; | ||||||
|  | 	int a_bus_drop; | ||||||
|  | 	int a_bus_req; | ||||||
|  | 	int a_srp_det; | ||||||
|  | 	int a_vbus_vld; | ||||||
|  | 	int b_conn; | ||||||
| 	int a_bus_resume; | 	int a_bus_resume; | ||||||
| 	int a_bus_suspend; | 	int a_bus_suspend; | ||||||
| 	int a_conn; | 	int a_conn; | ||||||
|  | 	int b_bus_req; | ||||||
|  | 	int b_se0_srp; | ||||||
|  | 	int b_ssend_srp; | ||||||
|  | 	int b_sess_vld; | ||||||
|  | 	/* Auxilary inputs */ | ||||||
| 	int a_sess_vld; | 	int a_sess_vld; | ||||||
| 	int a_srp_det; |  | ||||||
| 	int a_vbus_vld; |  | ||||||
| 	int b_bus_resume; | 	int b_bus_resume; | ||||||
| 	int b_bus_suspend; | 	int b_bus_suspend; | ||||||
| 	int b_conn; | 
 | ||||||
| 	int b_se0_srp; | 	/* Output */ | ||||||
| 	int b_sess_end; | 	int data_pulse; | ||||||
| 	int b_sess_vld; | 	int drv_vbus; | ||||||
| 	int id; | 	int loc_conn; | ||||||
|  | 	int loc_sof; | ||||||
|  | 	int adp_prb; | ||||||
|  | 	int adp_sns; | ||||||
| 
 | 
 | ||||||
| 	/* Internal variables */ | 	/* Internal variables */ | ||||||
| 	int a_set_b_hnp_en; | 	int a_set_b_hnp_en; | ||||||
| 	int b_srp_done; | 	int b_srp_done; | ||||||
| 	int b_hnp_enable; | 	int b_hnp_enable; | ||||||
|  | 	int a_clr_err; | ||||||
|  | 
 | ||||||
|  | 	/* Informative variables */ | ||||||
|  | 	int a_bus_drop_inf; | ||||||
|  | 	int a_bus_req_inf; | ||||||
|  | 	int a_clr_err_inf; | ||||||
|  | 	int b_bus_req_inf; | ||||||
|  | 	/* Auxilary informative variables */ | ||||||
|  | 	int a_suspend_req_inf; | ||||||
| 
 | 
 | ||||||
| 	/* Timeout indicator for timers */ | 	/* Timeout indicator for timers */ | ||||||
| 	int a_wait_vrise_tmout; | 	int a_wait_vrise_tmout; | ||||||
|  | 	int a_wait_vfall_tmout; | ||||||
| 	int a_wait_bcon_tmout; | 	int a_wait_bcon_tmout; | ||||||
| 	int a_aidl_bdis_tmout; | 	int a_aidl_bdis_tmout; | ||||||
| 	int b_ase0_brst_tmout; | 	int b_ase0_brst_tmout; | ||||||
| 
 | 	int a_bidl_adis_tmout; | ||||||
| 	/* Informative variables */ |  | ||||||
| 	int a_bus_drop; |  | ||||||
| 	int a_bus_req; |  | ||||||
| 	int a_clr_err; |  | ||||||
| 	int a_suspend_req; |  | ||||||
| 	int b_bus_req; |  | ||||||
| 
 |  | ||||||
| 	/* Output */ |  | ||||||
| 	int drv_vbus; |  | ||||||
| 	int loc_conn; |  | ||||||
| 	int loc_sof; |  | ||||||
| 
 | 
 | ||||||
| 	struct otg_fsm_ops *ops; | 	struct otg_fsm_ops *ops; | ||||||
| 	struct usb_otg *otg; | 	struct usb_otg *otg; | ||||||
|  | @ -83,65 +114,123 @@ struct otg_fsm { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct otg_fsm_ops { | struct otg_fsm_ops { | ||||||
| 	void	(*chrg_vbus)(int on); | 	void	(*chrg_vbus)(struct otg_fsm *fsm, int on); | ||||||
| 	void	(*drv_vbus)(int on); | 	void	(*drv_vbus)(struct otg_fsm *fsm, int on); | ||||||
| 	void	(*loc_conn)(int on); | 	void	(*loc_conn)(struct otg_fsm *fsm, int on); | ||||||
| 	void	(*loc_sof)(int on); | 	void	(*loc_sof)(struct otg_fsm *fsm, int on); | ||||||
| 	void	(*start_pulse)(void); | 	void	(*start_pulse)(struct otg_fsm *fsm); | ||||||
| 	void	(*add_timer)(void *timer); | 	void	(*start_adp_prb)(struct otg_fsm *fsm); | ||||||
| 	void	(*del_timer)(void *timer); | 	void	(*start_adp_sns)(struct otg_fsm *fsm); | ||||||
|  | 	void	(*add_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer); | ||||||
|  | 	void	(*del_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer); | ||||||
| 	int	(*start_host)(struct otg_fsm *fsm, int on); | 	int	(*start_host)(struct otg_fsm *fsm, int on); | ||||||
| 	int	(*start_gadget)(struct otg_fsm *fsm, int on); | 	int	(*start_gadget)(struct otg_fsm *fsm, int on); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static inline void otg_chrg_vbus(struct otg_fsm *fsm, int on) | static inline int otg_chrg_vbus(struct otg_fsm *fsm, int on) | ||||||
| { | { | ||||||
| 	fsm->ops->chrg_vbus(on); | 	if (!fsm->ops->chrg_vbus) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	fsm->ops->chrg_vbus(fsm, on); | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline void otg_drv_vbus(struct otg_fsm *fsm, int on) | static inline int otg_drv_vbus(struct otg_fsm *fsm, int on) | ||||||
| { | { | ||||||
|  | 	if (!fsm->ops->drv_vbus) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
| 	if (fsm->drv_vbus != on) { | 	if (fsm->drv_vbus != on) { | ||||||
| 		fsm->drv_vbus = on; | 		fsm->drv_vbus = on; | ||||||
| 		fsm->ops->drv_vbus(on); | 		fsm->ops->drv_vbus(fsm, on); | ||||||
| 	} | 	} | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline void otg_loc_conn(struct otg_fsm *fsm, int on) | static inline int otg_loc_conn(struct otg_fsm *fsm, int on) | ||||||
| { | { | ||||||
|  | 	if (!fsm->ops->loc_conn) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
| 	if (fsm->loc_conn != on) { | 	if (fsm->loc_conn != on) { | ||||||
| 		fsm->loc_conn = on; | 		fsm->loc_conn = on; | ||||||
| 		fsm->ops->loc_conn(on); | 		fsm->ops->loc_conn(fsm, on); | ||||||
| 	} | 	} | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline void otg_loc_sof(struct otg_fsm *fsm, int on) | static inline int otg_loc_sof(struct otg_fsm *fsm, int on) | ||||||
| { | { | ||||||
|  | 	if (!fsm->ops->loc_sof) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
| 	if (fsm->loc_sof != on) { | 	if (fsm->loc_sof != on) { | ||||||
| 		fsm->loc_sof = on; | 		fsm->loc_sof = on; | ||||||
| 		fsm->ops->loc_sof(on); | 		fsm->ops->loc_sof(fsm, on); | ||||||
| 	} | 	} | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline void otg_start_pulse(struct otg_fsm *fsm) | static inline int otg_start_pulse(struct otg_fsm *fsm) | ||||||
| { | { | ||||||
| 	fsm->ops->start_pulse(); | 	if (!fsm->ops->start_pulse) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	if (!fsm->data_pulse) { | ||||||
|  | 		fsm->data_pulse = 1; | ||||||
|  | 		fsm->ops->start_pulse(fsm); | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline void otg_add_timer(struct otg_fsm *fsm, void *timer) | static inline int otg_start_adp_prb(struct otg_fsm *fsm) | ||||||
| { | { | ||||||
| 	fsm->ops->add_timer(timer); | 	if (!fsm->ops->start_adp_prb) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	if (!fsm->adp_prb) { | ||||||
|  | 		fsm->adp_sns = 0; | ||||||
|  | 		fsm->adp_prb = 1; | ||||||
|  | 		fsm->ops->start_adp_prb(fsm); | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline void otg_del_timer(struct otg_fsm *fsm, void *timer) | static inline int otg_start_adp_sns(struct otg_fsm *fsm) | ||||||
| { | { | ||||||
| 	fsm->ops->del_timer(timer); | 	if (!fsm->ops->start_adp_sns) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	if (!fsm->adp_sns) { | ||||||
|  | 		fsm->adp_sns = 1; | ||||||
|  | 		fsm->ops->start_adp_sns(fsm); | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline int otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer) | ||||||
|  | { | ||||||
|  | 	if (!fsm->ops->add_timer) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	fsm->ops->add_timer(fsm, timer); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline int otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer) | ||||||
|  | { | ||||||
|  | 	if (!fsm->ops->del_timer) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	fsm->ops->del_timer(fsm, timer); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline int otg_start_host(struct otg_fsm *fsm, int on) | ||||||
|  | { | ||||||
|  | 	if (!fsm->ops->start_host) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	return fsm->ops->start_host(fsm, on); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline int otg_start_gadget(struct otg_fsm *fsm, int on) | ||||||
|  | { | ||||||
|  | 	if (!fsm->ops->start_gadget) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	return fsm->ops->start_gadget(fsm, on); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int otg_statemachine(struct otg_fsm *fsm); | int otg_statemachine(struct otg_fsm *fsm); | ||||||
| 
 |  | ||||||
| /* Defined by device specific driver, for different timer implementation */ |  | ||||||
| extern struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, |  | ||||||
| 	*a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_fail_tmr, |  | ||||||
| 	*a_wait_enum_tmr; |  | ||||||
|  |  | ||||||
|  | @ -35,6 +35,9 @@ | ||||||
| #include <linux/clk.h> | #include <linux/clk.h> | ||||||
| #include <linux/regulator/consumer.h> | #include <linux/regulator/consumer.h> | ||||||
| #include <linux/of.h> | #include <linux/of.h> | ||||||
|  | #include <linux/of_gpio.h> | ||||||
|  | #include <linux/gpio.h> | ||||||
|  | #include <linux/delay.h> | ||||||
| 
 | 
 | ||||||
| #include "phy-generic.h" | #include "phy-generic.h" | ||||||
| 
 | 
 | ||||||
|  | @ -64,6 +67,23 @@ static int nop_set_suspend(struct usb_phy *x, int suspend) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void nop_reset_set(struct usb_phy_gen_xceiv *nop, int asserted) | ||||||
|  | { | ||||||
|  | 	int value; | ||||||
|  | 
 | ||||||
|  | 	if (!gpio_is_valid(nop->gpio_reset)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	value = asserted; | ||||||
|  | 	if (nop->reset_active_low) | ||||||
|  | 		value = !value; | ||||||
|  | 
 | ||||||
|  | 	gpio_set_value_cansleep(nop->gpio_reset, value); | ||||||
|  | 
 | ||||||
|  | 	if (!asserted) | ||||||
|  | 		usleep_range(10000, 20000); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int usb_gen_phy_init(struct usb_phy *phy) | int usb_gen_phy_init(struct usb_phy *phy) | ||||||
| { | { | ||||||
| 	struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); | 	struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); | ||||||
|  | @ -74,13 +94,10 @@ int usb_gen_phy_init(struct usb_phy *phy) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (!IS_ERR(nop->clk)) | 	if (!IS_ERR(nop->clk)) | ||||||
| 		clk_enable(nop->clk); | 		clk_prepare_enable(nop->clk); | ||||||
| 
 | 
 | ||||||
| 	if (!IS_ERR(nop->reset)) { | 	/* De-assert RESET */ | ||||||
| 		/* De-assert RESET */ | 	nop_reset_set(nop, 0); | ||||||
| 		if (regulator_enable(nop->reset)) |  | ||||||
| 			dev_err(phy->dev, "Failed to de-assert reset\n"); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  | @ -90,14 +107,11 @@ void usb_gen_phy_shutdown(struct usb_phy *phy) | ||||||
| { | { | ||||||
| 	struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); | 	struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); | ||||||
| 
 | 
 | ||||||
| 	if (!IS_ERR(nop->reset)) { | 	/* Assert RESET */ | ||||||
| 		/* Assert RESET */ | 	nop_reset_set(nop, 1); | ||||||
| 		if (regulator_disable(nop->reset)) |  | ||||||
| 			dev_err(phy->dev, "Failed to assert reset\n"); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	if (!IS_ERR(nop->clk)) | 	if (!IS_ERR(nop->clk)) | ||||||
| 		clk_disable(nop->clk); | 		clk_disable_unprepare(nop->clk); | ||||||
| 
 | 
 | ||||||
| 	if (!IS_ERR(nop->vcc)) { | 	if (!IS_ERR(nop->vcc)) { | ||||||
| 		if (regulator_disable(nop->vcc)) | 		if (regulator_disable(nop->vcc)) | ||||||
|  | @ -136,8 +150,7 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, | int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, | ||||||
| 		enum usb_phy_type type, u32 clk_rate, bool needs_vcc, | 		enum usb_phy_type type, u32 clk_rate, bool needs_vcc) | ||||||
| 		bool needs_reset) |  | ||||||
| { | { | ||||||
| 	int err; | 	int err; | ||||||
| 
 | 
 | ||||||
|  | @ -160,14 +173,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (!IS_ERR(nop->clk)) { |  | ||||||
| 		err = clk_prepare(nop->clk); |  | ||||||
| 		if (err) { |  | ||||||
| 			dev_err(dev, "Error preparing clock\n"); |  | ||||||
| 			return err; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	nop->vcc = devm_regulator_get(dev, "vcc"); | 	nop->vcc = devm_regulator_get(dev, "vcc"); | ||||||
| 	if (IS_ERR(nop->vcc)) { | 	if (IS_ERR(nop->vcc)) { | ||||||
| 		dev_dbg(dev, "Error getting vcc regulator: %ld\n", | 		dev_dbg(dev, "Error getting vcc regulator: %ld\n", | ||||||
|  | @ -176,12 +181,22 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, | ||||||
| 			return -EPROBE_DEFER; | 			return -EPROBE_DEFER; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	nop->reset = devm_regulator_get(dev, "reset"); | 	if (gpio_is_valid(nop->gpio_reset)) { | ||||||
| 	if (IS_ERR(nop->reset)) { | 		unsigned long gpio_flags; | ||||||
| 		dev_dbg(dev, "Error getting reset regulator: %ld\n", | 
 | ||||||
| 					PTR_ERR(nop->reset)); | 		/* Assert RESET */ | ||||||
| 		if (needs_reset) | 		if (nop->reset_active_low) | ||||||
| 			return -EPROBE_DEFER; | 			gpio_flags = GPIOF_OUT_INIT_LOW; | ||||||
|  | 		else | ||||||
|  | 			gpio_flags = GPIOF_OUT_INIT_HIGH; | ||||||
|  | 
 | ||||||
|  | 		err = devm_gpio_request_one(dev, nop->gpio_reset, | ||||||
|  | 						gpio_flags, dev_name(dev)); | ||||||
|  | 		if (err) { | ||||||
|  | 			dev_err(dev, "Error requesting RESET GPIO %d\n", | ||||||
|  | 					nop->gpio_reset); | ||||||
|  | 			return err; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	nop->dev		= dev; | 	nop->dev		= dev; | ||||||
|  | @ -200,13 +215,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy); | EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy); | ||||||
| 
 | 
 | ||||||
| void usb_phy_gen_cleanup_phy(struct usb_phy_gen_xceiv *nop) |  | ||||||
| { |  | ||||||
| 	if (!IS_ERR(nop->clk)) |  | ||||||
| 		clk_unprepare(nop->clk); |  | ||||||
| } |  | ||||||
| EXPORT_SYMBOL_GPL(usb_phy_gen_cleanup_phy); |  | ||||||
| 
 |  | ||||||
| static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) | static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) | ||||||
| { | { | ||||||
| 	struct device *dev = &pdev->dev; | 	struct device *dev = &pdev->dev; | ||||||
|  | @ -217,31 +225,36 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) | ||||||
| 	int err; | 	int err; | ||||||
| 	u32 clk_rate = 0; | 	u32 clk_rate = 0; | ||||||
| 	bool needs_vcc = false; | 	bool needs_vcc = false; | ||||||
| 	bool needs_reset = false; |  | ||||||
| 
 |  | ||||||
| 	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 = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL); | 	nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL); | ||||||
| 	if (!nop) | 	if (!nop) | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 
 | 
 | ||||||
|  | 	nop->reset_active_low = true;	/* default behaviour */ | ||||||
| 
 | 
 | ||||||
| 	err = usb_phy_gen_create_phy(dev, nop, type, clk_rate, needs_vcc, | 	if (dev->of_node) { | ||||||
| 			needs_reset); | 		struct device_node *node = dev->of_node; | ||||||
|  | 		enum of_gpio_flags flags; | ||||||
|  | 
 | ||||||
|  | 		if (of_property_read_u32(node, "clock-frequency", &clk_rate)) | ||||||
|  | 			clk_rate = 0; | ||||||
|  | 
 | ||||||
|  | 		needs_vcc = of_property_read_bool(node, "vcc-supply"); | ||||||
|  | 		nop->gpio_reset = of_get_named_gpio_flags(node, "reset-gpios", | ||||||
|  | 								0, &flags); | ||||||
|  | 		if (nop->gpio_reset == -EPROBE_DEFER) | ||||||
|  | 			return -EPROBE_DEFER; | ||||||
|  | 
 | ||||||
|  | 		nop->reset_active_low = flags & OF_GPIO_ACTIVE_LOW; | ||||||
|  | 
 | ||||||
|  | 	} else if (pdata) { | ||||||
|  | 		type = pdata->type; | ||||||
|  | 		clk_rate = pdata->clk_rate; | ||||||
|  | 		needs_vcc = pdata->needs_vcc; | ||||||
|  | 		nop->gpio_reset = pdata->gpio_reset; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = usb_phy_gen_create_phy(dev, nop, type, clk_rate, needs_vcc); | ||||||
| 	if (err) | 	if (err) | ||||||
| 		return err; | 		return err; | ||||||
| 
 | 
 | ||||||
|  | @ -252,15 +265,13 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) | ||||||
| 	if (err) { | 	if (err) { | ||||||
| 		dev_err(&pdev->dev, "can't register transceiver, err: %d\n", | 		dev_err(&pdev->dev, "can't register transceiver, err: %d\n", | ||||||
| 			err); | 			err); | ||||||
| 		goto err_add; | 		return err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	platform_set_drvdata(pdev, nop); | 	platform_set_drvdata(pdev, nop); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| err_add: |  | ||||||
| 	usb_phy_gen_cleanup_phy(nop); |  | ||||||
| 	return err; | 	return err; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -268,7 +279,6 @@ static int usb_phy_gen_xceiv_remove(struct platform_device *pdev) | ||||||
| { | { | ||||||
| 	struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev); | 	struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev); | ||||||
| 
 | 
 | ||||||
| 	usb_phy_gen_cleanup_phy(nop); |  | ||||||
| 	usb_remove_phy(&nop->phy); | 	usb_remove_phy(&nop->phy); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
|  |  | ||||||
|  | @ -6,15 +6,14 @@ struct usb_phy_gen_xceiv { | ||||||
| 	struct device *dev; | 	struct device *dev; | ||||||
| 	struct clk *clk; | 	struct clk *clk; | ||||||
| 	struct regulator *vcc; | 	struct regulator *vcc; | ||||||
| 	struct regulator *reset; | 	int gpio_reset; | ||||||
|  | 	bool reset_active_low; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| int usb_gen_phy_init(struct usb_phy *phy); | int usb_gen_phy_init(struct usb_phy *phy); | ||||||
| void usb_gen_phy_shutdown(struct usb_phy *phy); | void usb_gen_phy_shutdown(struct usb_phy *phy); | ||||||
| 
 | 
 | ||||||
| int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, | int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, | ||||||
| 		enum usb_phy_type type, u32 clk_rate, bool needs_vcc, | 		enum usb_phy_type type, u32 clk_rate, bool needs_vcc); | ||||||
| 		bool needs_reset); |  | ||||||
| void usb_phy_gen_cleanup_phy(struct usb_phy_gen_xceiv *nop); |  | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
							
								
								
									
										248
									
								
								drivers/usb/phy/phy-rcar-gen2-usb.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								drivers/usb/phy/phy-rcar-gen2-usb.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,248 @@ | ||||||
|  | /*
 | ||||||
|  |  * Renesas R-Car Gen2 USB phy driver | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2013 Renesas Solutions Corp. | ||||||
|  |  * Copyright (C) 2013 Cogent Embedded, Inc. | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License version 2 as | ||||||
|  |  * published by the Free Software Foundation. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/clk.h> | ||||||
|  | #include <linux/delay.h> | ||||||
|  | #include <linux/io.h> | ||||||
|  | #include <linux/module.h> | ||||||
|  | #include <linux/platform_data/usb-rcar-gen2-phy.h> | ||||||
|  | #include <linux/platform_device.h> | ||||||
|  | #include <linux/spinlock.h> | ||||||
|  | #include <linux/usb/otg.h> | ||||||
|  | 
 | ||||||
|  | struct rcar_gen2_usb_phy_priv { | ||||||
|  | 	struct usb_phy phy; | ||||||
|  | 	void __iomem *base; | ||||||
|  | 	struct clk *clk; | ||||||
|  | 	spinlock_t lock; | ||||||
|  | 	int usecount; | ||||||
|  | 	u32 ugctrl2; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define usb_phy_to_priv(p) container_of(p, struct rcar_gen2_usb_phy_priv, phy) | ||||||
|  | 
 | ||||||
|  | /* Low Power Status register */ | ||||||
|  | #define USBHS_LPSTS_REG			0x02 | ||||||
|  | #define USBHS_LPSTS_SUSPM		(1 << 14) | ||||||
|  | 
 | ||||||
|  | /* USB General control register */ | ||||||
|  | #define USBHS_UGCTRL_REG		0x80 | ||||||
|  | #define USBHS_UGCTRL_CONNECT		(1 << 2) | ||||||
|  | #define USBHS_UGCTRL_PLLRESET		(1 << 0) | ||||||
|  | 
 | ||||||
|  | /* USB General control register 2 */ | ||||||
|  | #define USBHS_UGCTRL2_REG		0x84 | ||||||
|  | #define USBHS_UGCTRL2_USB0_PCI		(1 << 4) | ||||||
|  | #define USBHS_UGCTRL2_USB0_HS		(3 << 4) | ||||||
|  | #define USBHS_UGCTRL2_USB2_PCI		(0 << 31) | ||||||
|  | #define USBHS_UGCTRL2_USB2_SS		(1 << 31) | ||||||
|  | 
 | ||||||
|  | /* USB General status register */ | ||||||
|  | #define USBHS_UGSTS_REG			0x88 | ||||||
|  | #define USBHS_UGSTS_LOCK		(3 << 8) | ||||||
|  | 
 | ||||||
|  | /* Enable USBHS internal phy */ | ||||||
|  | static int __rcar_gen2_usbhs_phy_enable(void __iomem *base) | ||||||
|  | { | ||||||
|  | 	u32 val; | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	/* USBHS PHY power on */ | ||||||
|  | 	val = ioread32(base + USBHS_UGCTRL_REG); | ||||||
|  | 	val &= ~USBHS_UGCTRL_PLLRESET; | ||||||
|  | 	iowrite32(val, base + USBHS_UGCTRL_REG); | ||||||
|  | 
 | ||||||
|  | 	val = ioread16(base + USBHS_LPSTS_REG); | ||||||
|  | 	val |= USBHS_LPSTS_SUSPM; | ||||||
|  | 	iowrite16(val, base + USBHS_LPSTS_REG); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < 20; i++) { | ||||||
|  | 		val = ioread32(base + USBHS_UGSTS_REG); | ||||||
|  | 		if ((val & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) { | ||||||
|  | 			val = ioread32(base + USBHS_UGCTRL_REG); | ||||||
|  | 			val |= USBHS_UGCTRL_CONNECT; | ||||||
|  | 			iowrite32(val, base + USBHS_UGCTRL_REG); | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 		udelay(1); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Timed out waiting for the PLL lock */ | ||||||
|  | 	return -ETIMEDOUT; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Disable USBHS internal phy */ | ||||||
|  | static int __rcar_gen2_usbhs_phy_disable(void __iomem *base) | ||||||
|  | { | ||||||
|  | 	u32 val; | ||||||
|  | 
 | ||||||
|  | 	/* USBHS PHY power off */ | ||||||
|  | 	val = ioread32(base + USBHS_UGCTRL_REG); | ||||||
|  | 	val &= ~USBHS_UGCTRL_CONNECT; | ||||||
|  | 	iowrite32(val, base + USBHS_UGCTRL_REG); | ||||||
|  | 
 | ||||||
|  | 	val = ioread16(base + USBHS_LPSTS_REG); | ||||||
|  | 	val &= ~USBHS_LPSTS_SUSPM; | ||||||
|  | 	iowrite16(val, base + USBHS_LPSTS_REG); | ||||||
|  | 
 | ||||||
|  | 	val = ioread32(base + USBHS_UGCTRL_REG); | ||||||
|  | 	val |= USBHS_UGCTRL_PLLRESET; | ||||||
|  | 	iowrite32(val, base + USBHS_UGCTRL_REG); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Setup USB channels */ | ||||||
|  | static void __rcar_gen2_usb_phy_init(struct rcar_gen2_usb_phy_priv *priv) | ||||||
|  | { | ||||||
|  | 	u32 val; | ||||||
|  | 
 | ||||||
|  | 	clk_prepare_enable(priv->clk); | ||||||
|  | 
 | ||||||
|  | 	/* Set USB channels in the USBHS UGCTRL2 register */ | ||||||
|  | 	val = ioread32(priv->base); | ||||||
|  | 	val &= ~(USBHS_UGCTRL2_USB0_HS | USBHS_UGCTRL2_USB2_SS); | ||||||
|  | 	val |= priv->ugctrl2; | ||||||
|  | 	iowrite32(val, priv->base); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Shutdown USB channels */ | ||||||
|  | static void __rcar_gen2_usb_phy_shutdown(struct rcar_gen2_usb_phy_priv *priv) | ||||||
|  | { | ||||||
|  | 	__rcar_gen2_usbhs_phy_disable(priv->base); | ||||||
|  | 	clk_disable_unprepare(priv->clk); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int rcar_gen2_usb_phy_set_suspend(struct usb_phy *phy, int suspend) | ||||||
|  | { | ||||||
|  | 	struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); | ||||||
|  | 	unsigned long flags; | ||||||
|  | 	int retval; | ||||||
|  | 
 | ||||||
|  | 	spin_lock_irqsave(&priv->lock, flags); | ||||||
|  | 	retval = suspend ? __rcar_gen2_usbhs_phy_disable(priv->base) : | ||||||
|  | 			   __rcar_gen2_usbhs_phy_enable(priv->base); | ||||||
|  | 	spin_unlock_irqrestore(&priv->lock, flags); | ||||||
|  | 	return retval; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int rcar_gen2_usb_phy_init(struct usb_phy *phy) | ||||||
|  | { | ||||||
|  | 	struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); | ||||||
|  | 	unsigned long flags; | ||||||
|  | 
 | ||||||
|  | 	spin_lock_irqsave(&priv->lock, flags); | ||||||
|  | 	/*
 | ||||||
|  | 	 * Enable the clock and setup USB channels | ||||||
|  | 	 * if it's the first user | ||||||
|  | 	 */ | ||||||
|  | 	if (!priv->usecount++) | ||||||
|  | 		__rcar_gen2_usb_phy_init(priv); | ||||||
|  | 	spin_unlock_irqrestore(&priv->lock, flags); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void rcar_gen2_usb_phy_shutdown(struct usb_phy *phy) | ||||||
|  | { | ||||||
|  | 	struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); | ||||||
|  | 	unsigned long flags; | ||||||
|  | 
 | ||||||
|  | 	spin_lock_irqsave(&priv->lock, flags); | ||||||
|  | 	if (!priv->usecount) { | ||||||
|  | 		dev_warn(phy->dev, "Trying to disable phy with 0 usecount\n"); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Disable everything if it's the last user */ | ||||||
|  | 	if (!--priv->usecount) | ||||||
|  | 		__rcar_gen2_usb_phy_shutdown(priv); | ||||||
|  | out: | ||||||
|  | 	spin_unlock_irqrestore(&priv->lock, flags); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int rcar_gen2_usb_phy_probe(struct platform_device *pdev) | ||||||
|  | { | ||||||
|  | 	struct device *dev = &pdev->dev; | ||||||
|  | 	struct rcar_gen2_phy_platform_data *pdata; | ||||||
|  | 	struct rcar_gen2_usb_phy_priv *priv; | ||||||
|  | 	struct resource *res; | ||||||
|  | 	void __iomem *base; | ||||||
|  | 	struct clk *clk; | ||||||
|  | 	int retval; | ||||||
|  | 
 | ||||||
|  | 	pdata = dev_get_platdata(&pdev->dev); | ||||||
|  | 	if (!pdata) { | ||||||
|  | 		dev_err(dev, "No platform data\n"); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	clk = devm_clk_get(&pdev->dev, "usbhs"); | ||||||
|  | 	if (IS_ERR(clk)) { | ||||||
|  | 		dev_err(&pdev->dev, "Can't get the clock\n"); | ||||||
|  | 		return PTR_ERR(clk); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||||
|  | 	base = devm_ioremap_resource(dev, res); | ||||||
|  | 	if (IS_ERR(base)) | ||||||
|  | 		return PTR_ERR(base); | ||||||
|  | 
 | ||||||
|  | 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||||||
|  | 	if (!priv) { | ||||||
|  | 		dev_err(dev, "Memory allocation failed\n"); | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	spin_lock_init(&priv->lock); | ||||||
|  | 	priv->clk = clk; | ||||||
|  | 	priv->base = base; | ||||||
|  | 	priv->ugctrl2 = pdata->chan0_pci ? | ||||||
|  | 			USBHS_UGCTRL2_USB0_PCI : USBHS_UGCTRL2_USB0_HS; | ||||||
|  | 	priv->ugctrl2 |= pdata->chan2_pci ? | ||||||
|  | 			USBHS_UGCTRL2_USB2_PCI : USBHS_UGCTRL2_USB2_SS; | ||||||
|  | 	priv->phy.dev = dev; | ||||||
|  | 	priv->phy.label = dev_name(dev); | ||||||
|  | 	priv->phy.init = rcar_gen2_usb_phy_init; | ||||||
|  | 	priv->phy.shutdown = rcar_gen2_usb_phy_shutdown; | ||||||
|  | 	priv->phy.set_suspend = rcar_gen2_usb_phy_set_suspend; | ||||||
|  | 
 | ||||||
|  | 	retval = usb_add_phy(&priv->phy, USB_PHY_TYPE_USB2); | ||||||
|  | 	if (retval < 0) { | ||||||
|  | 		dev_err(dev, "Failed to add USB phy\n"); | ||||||
|  | 		return retval; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	platform_set_drvdata(pdev, priv); | ||||||
|  | 
 | ||||||
|  | 	return retval; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int rcar_gen2_usb_phy_remove(struct platform_device *pdev) | ||||||
|  | { | ||||||
|  | 	struct rcar_gen2_usb_phy_priv *priv = platform_get_drvdata(pdev); | ||||||
|  | 
 | ||||||
|  | 	usb_remove_phy(&priv->phy); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct platform_driver rcar_gen2_usb_phy_driver = { | ||||||
|  | 	.driver = { | ||||||
|  | 		.name = "usb_phy_rcar_gen2", | ||||||
|  | 	}, | ||||||
|  | 	.probe = rcar_gen2_usb_phy_probe, | ||||||
|  | 	.remove = rcar_gen2_usb_phy_remove, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | module_platform_driver(rcar_gen2_usb_phy_driver); | ||||||
|  | 
 | ||||||
|  | MODULE_LICENSE("GPL v2"); | ||||||
|  | MODULE_DESCRIPTION("Renesas R-Car Gen2 USB phy"); | ||||||
|  | MODULE_AUTHOR("Valentine Barshak <valentine.barshak@cogentembedded.com>"); | ||||||
|  | @ -1090,7 +1090,7 @@ static struct platform_driver tegra_usb_phy_driver = { | ||||||
| 	.driver		= { | 	.driver		= { | ||||||
| 		.name	= "tegra-phy", | 		.name	= "tegra-phy", | ||||||
| 		.owner	= THIS_MODULE, | 		.owner	= THIS_MODULE, | ||||||
| 		.of_match_table = of_match_ptr(tegra_usb_phy_id_table), | 		.of_match_table = tegra_usb_phy_id_table, | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
| module_platform_driver(tegra_usb_phy_driver); | module_platform_driver(tegra_usb_phy_driver); | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ | ||||||
| #include <linux/err.h> | #include <linux/err.h> | ||||||
| #include <linux/slab.h> | #include <linux/slab.h> | ||||||
| #include <linux/delay.h> | #include <linux/delay.h> | ||||||
|  | #include <linux/of.h> | ||||||
| 
 | 
 | ||||||
| /* usb register definitions */ | /* usb register definitions */ | ||||||
| #define USB_VENDOR_ID_LSB		0x00 | #define USB_VENDOR_ID_LSB		0x00 | ||||||
|  |  | ||||||
|  | @ -98,7 +98,7 @@ struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type) | ||||||
| 
 | 
 | ||||||
| 	ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); | 	ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); | ||||||
| 	if (!ptr) | 	if (!ptr) | ||||||
| 		return NULL; | 		return ERR_PTR(-ENOMEM); | ||||||
| 
 | 
 | ||||||
| 	phy = usb_get_phy(type); | 	phy = usb_get_phy(type); | ||||||
| 	if (!IS_ERR(phy)) { | 	if (!IS_ERR(phy)) { | ||||||
|  |  | ||||||
							
								
								
									
										22
									
								
								include/linux/platform_data/usb-rcar-gen2-phy.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								include/linux/platform_data/usb-rcar-gen2-phy.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2013 Renesas Solutions Corp. | ||||||
|  |  * Copyright (C) 2013 Cogent Embedded, Inc. | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License version 2 as | ||||||
|  |  * published by the Free Software Foundation. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef __USB_RCAR_GEN2_PHY_H | ||||||
|  | #define __USB_RCAR_GEN2_PHY_H | ||||||
|  | 
 | ||||||
|  | #include <linux/types.h> | ||||||
|  | 
 | ||||||
|  | struct rcar_gen2_phy_platform_data { | ||||||
|  | 	/* USB channel 0 configuration */ | ||||||
|  | 	bool chan0_pci:1;	/* true: PCI USB host 0, false: USBHS */ | ||||||
|  | 	/* USB channel 2 configuration */ | ||||||
|  | 	bool chan2_pci:1;	/* true: PCI USB host 2, false: USBSS */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -9,7 +9,8 @@ struct usb_phy_gen_xceiv_platform_data { | ||||||
| 
 | 
 | ||||||
| 	/* if set fails with -EPROBE_DEFER if can't get regulator */ | 	/* if set fails with -EPROBE_DEFER if can't get regulator */ | ||||||
| 	unsigned int needs_vcc:1; | 	unsigned int needs_vcc:1; | ||||||
| 	unsigned int needs_reset:1; | 	unsigned int needs_reset:1;	/* deprecated */ | ||||||
|  | 	int gpio_reset; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) | #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Greg Kroah-Hartman
				Greg Kroah-Hartman