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.
|
||||
|
||||
- reset-supply: phandle to the regulator that provides power to the PHY.
|
||||
- reset-gpios: Should specify the GPIO for reset.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -25,10 +25,9 @@ Example:
|
|||
clocks = <&osc 0>;
|
||||
clock-names = "main_clk";
|
||||
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
|
||||
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
|
||||
controls RESET.
|
||||
hsusb1_vcc_regulator provides power to the PHY and GPIO 7 controls RESET.
|
||||
|
|
|
@ -289,18 +289,12 @@ static struct regulator_consumer_supply beagle_vsim_supply[] = {
|
|||
|
||||
static struct gpio_led gpio_leds[];
|
||||
|
||||
/* PHY's VCC regulator might be added later, so flag that we need it */
|
||||
static struct usb_phy_gen_xceiv_platform_data hsusb2_phy_data = {
|
||||
.needs_vcc = true,
|
||||
};
|
||||
|
||||
static struct usbhs_phy_data phy_data[] = {
|
||||
{
|
||||
.port = 2,
|
||||
.reset_gpio = 147,
|
||||
.vcc_gpio = -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;
|
||||
char *phy_id;
|
||||
struct platform_device_info pdevinfo;
|
||||
struct usb_phy_gen_xceiv_platform_data nop_pdata;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
||||
pdevinfo.name = nop_name;
|
||||
pdevinfo.id = phy->port;
|
||||
pdevinfo.data = phy->platform_data;
|
||||
pdevinfo.data = &nop_pdata;
|
||||
pdevinfo.size_data =
|
||||
sizeof(struct usb_phy_gen_xceiv_platform_data);
|
||||
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);
|
||||
|
||||
/* 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 ? */
|
||||
if (gpio_is_valid(phy->vcc_gpio)) {
|
||||
scnprintf(rail_name, MAX_STR, "hsusb%d_vcc", phy->port);
|
||||
|
|
|
@ -58,7 +58,6 @@ struct usbhs_phy_data {
|
|||
int reset_gpio;
|
||||
int vcc_gpio;
|
||||
bool vcc_polarity; /* 1 active high, 0 active low */
|
||||
void *platform_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))
|
||||
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
|
||||
* 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->usb3_phy, 1);
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
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->usb2_phy);
|
||||
msleep(100);
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
|
|
|
@ -58,6 +58,20 @@ config USB_GADGET_DEBUG
|
|||
trying to track down. Never enable these messages for a
|
||||
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
|
||||
boolean "Debugging information files (DEVELOPMENT)"
|
||||
depends on PROC_FS
|
||||
|
@ -525,6 +539,9 @@ config USB_F_SUBSET
|
|||
config USB_F_RNDIS
|
||||
tristate
|
||||
|
||||
config USB_F_MASS_STORAGE
|
||||
tristate
|
||||
|
||||
choice
|
||||
tristate "USB Gadget Drivers"
|
||||
default USB_ETH
|
||||
|
@ -662,6 +679,16 @@ config USB_CONFIGFS_PHONET
|
|||
help
|
||||
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
|
||||
tristate "Gadget Zero (DEVELOPMENT)"
|
||||
select USB_LIBCOMPOSITE
|
||||
|
@ -878,6 +905,7 @@ config USB_MASS_STORAGE
|
|||
tristate "Mass Storage Gadget"
|
||||
depends on BLOCK
|
||||
select USB_LIBCOMPOSITE
|
||||
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
|
||||
|
@ -1001,6 +1029,7 @@ config USB_G_ACM_MS
|
|||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
select USB_F_ACM
|
||||
select USB_F_MASS_STORAGE
|
||||
help
|
||||
This driver provides two functions in one configuration:
|
||||
a mass storage, and a CDC ACM (serial port) link.
|
||||
|
@ -1015,8 +1044,8 @@ config USB_G_MULTI
|
|||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
select USB_U_ETHER
|
||||
select USB_U_RNDIS
|
||||
select USB_F_ACM
|
||||
select USB_F_MASS_STORAGE
|
||||
help
|
||||
The Multifunction Composite Gadget provides Ethernet (RNDIS
|
||||
and/or CDC Ethernet), mass storage and ACM serial link
|
||||
|
@ -1035,6 +1064,8 @@ config USB_G_MULTI
|
|||
config USB_G_MULTI_RNDIS
|
||||
bool "RNDIS + CDC Serial + Storage configuration"
|
||||
depends on USB_G_MULTI
|
||||
select USB_U_RNDIS
|
||||
select USB_F_RNDIS
|
||||
default y
|
||||
help
|
||||
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"
|
||||
depends on USB_G_MULTI
|
||||
default n
|
||||
select USB_F_ECM
|
||||
help
|
||||
This option enables a configuration with CDC Ethernet (ECM), CDC
|
||||
Serial and Mass Storage functions available in the Multifunction
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#
|
||||
# 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_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
|
||||
usb_f_rndis-y := 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
|
||||
|
|
|
@ -31,16 +31,7 @@
|
|||
#define ACM_MS_VENDOR_NUM 0x1d6b /* Linux Foundation */
|
||||
#define ACM_MS_PRODUCT_NUM 0x0106 /* Composite Gadget: ACM + MS*/
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* 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"
|
||||
#include "f_mass_storage.h"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
|
@ -104,18 +95,35 @@ static struct usb_gadget_strings *dev_strings[] = {
|
|||
/****************************** Configurations ******************************/
|
||||
|
||||
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_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.
|
||||
*/
|
||||
static int __init acm_ms_do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct fsg_opts *opts;
|
||||
int status;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
f_acm_inst = usb_get_function_instance("acm");
|
||||
if (IS_ERR(f_acm_inst))
|
||||
return PTR_ERR(f_acm_inst);
|
||||
opts = fsg_opts_from_func_inst(fi_msg);
|
||||
|
||||
f_acm = usb_get_function(f_acm_inst);
|
||||
if (IS_ERR(f_acm)) {
|
||||
status = PTR_ERR(f_acm);
|
||||
goto err_func;
|
||||
if (IS_ERR(f_acm))
|
||||
return PTR_ERR(f_acm);
|
||||
|
||||
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);
|
||||
if (status < 0)
|
||||
goto err_conf;
|
||||
goto put_msg;
|
||||
|
||||
status = fsg_bind_config(c->cdev, c, &fsg_common);
|
||||
if (status < 0)
|
||||
goto err_fsg;
|
||||
status = fsg_common_run_thread(opts->common);
|
||||
if (status)
|
||||
goto remove_acm;
|
||||
|
||||
status = usb_add_function(c, f_msg);
|
||||
if (status)
|
||||
goto remove_acm;
|
||||
|
||||
return 0;
|
||||
err_fsg:
|
||||
remove_acm:
|
||||
usb_remove_function(c, f_acm);
|
||||
err_conf:
|
||||
put_msg:
|
||||
usb_put_function(f_msg);
|
||||
put_acm:
|
||||
usb_put_function(f_acm);
|
||||
err_func:
|
||||
usb_put_function_instance(f_acm_inst);
|
||||
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)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct fsg_opts *opts;
|
||||
struct fsg_config config;
|
||||
int status;
|
||||
void *retp;
|
||||
|
||||
/* set up mass storage function */
|
||||
retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
|
||||
if (IS_ERR(retp)) {
|
||||
status = PTR_ERR(retp);
|
||||
return PTR_ERR(retp);
|
||||
f_acm_inst = usb_get_function_instance("acm");
|
||||
if (IS_ERR(f_acm_inst))
|
||||
return PTR_ERR(f_acm_inst);
|
||||
|
||||
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
|
||||
* contents can be overridden by the composite_dev glue.
|
||||
*/
|
||||
status = usb_string_ids_tab(cdev, strings_dev);
|
||||
if (status < 0)
|
||||
goto fail1;
|
||||
goto fail_string_ids;
|
||||
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
|
||||
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
|
||||
|
||||
/* register our configuration */
|
||||
status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config);
|
||||
if (status < 0)
|
||||
goto fail1;
|
||||
goto fail_string_ids;
|
||||
|
||||
usb_composite_overwrite_options(cdev, &coverwrite);
|
||||
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
|
||||
DRIVER_DESC);
|
||||
fsg_common_put(&fsg_common);
|
||||
return 0;
|
||||
|
||||
/* error recovery */
|
||||
fail1:
|
||||
fsg_common_put(&fsg_common);
|
||||
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);
|
||||
fail_get_msg:
|
||||
usb_put_function_instance(f_acm_inst);
|
||||
return status;
|
||||
}
|
||||
|
||||
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_instance(f_acm_inst);
|
||||
return 0;
|
||||
|
|
|
@ -557,7 +557,7 @@ static struct config_group *function_make(
|
|||
|
||||
fi = usb_get_function_instance(func_name);
|
||||
if (IS_ERR(fi))
|
||||
return ERR_PTR(PTR_ERR(fi));
|
||||
return ERR_CAST(fi);
|
||||
|
||||
ret = config_item_set_name(&fi->group.cg_item, name);
|
||||
if (ret) {
|
||||
|
@ -991,6 +991,14 @@ static struct configfs_subsystem gadget_subsys = {
|
|||
.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)
|
||||
{
|
||||
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_VERSION "2009/09/11"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
#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();
|
||||
|
@ -97,11 +97,28 @@ static struct usb_gadget_strings *dev_strings[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_function_instance *fi_msg;
|
||||
static struct usb_function *f_msg;
|
||||
|
||||
/****************************** Configurations ******************************/
|
||||
|
||||
static struct fsg_module_parameters 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_GADGET_DEBUG_FILES */
|
||||
|
||||
FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
|
||||
|
||||
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 const struct fsg_operations ops = {
|
||||
.thread_exits = msg_thread_exits,
|
||||
};
|
||||
static struct fsg_common common;
|
||||
|
||||
struct fsg_common *retp;
|
||||
struct fsg_config config;
|
||||
struct fsg_opts *opts;
|
||||
int ret;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
fsg_config_from_params(&config, &mod_data);
|
||||
config.ops = &ops;
|
||||
opts = fsg_opts_from_func_inst(fi_msg);
|
||||
|
||||
retp = fsg_common_init(&common, c->cdev, &config);
|
||||
if (IS_ERR(retp))
|
||||
return PTR_ERR(retp);
|
||||
f_msg = usb_get_function(fi_msg);
|
||||
if (IS_ERR(f_msg))
|
||||
return PTR_ERR(f_msg);
|
||||
|
||||
ret = fsg_bind_config(c->cdev, c, &common);
|
||||
fsg_common_put(&common);
|
||||
ret = fsg_common_run_thread(opts->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;
|
||||
}
|
||||
|
||||
|
@ -152,23 +172,79 @@ static struct usb_configuration msg_config_driver = {
|
|||
|
||||
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;
|
||||
|
||||
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);
|
||||
if (status < 0)
|
||||
return status;
|
||||
goto fail_string_ids;
|
||||
msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
|
||||
|
||||
status = usb_add_config(cdev, &msg_config_driver, msg_do_config);
|
||||
if (status < 0)
|
||||
return status;
|
||||
goto fail_string_ids;
|
||||
|
||||
usb_composite_overwrite_options(cdev, &coverwrite);
|
||||
dev_info(&cdev->gadget->dev,
|
||||
DRIVER_DESC ", version: " DRIVER_VERSION "\n");
|
||||
set_bit(0, &msg_registered);
|
||||
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 ******************************/
|
||||
|
||||
|
@ -179,6 +255,7 @@ static __refdata struct usb_composite_driver msg_driver = {
|
|||
.needs_serial = 1,
|
||||
.strings = dev_strings,
|
||||
.bind = msg_bind,
|
||||
.unbind = msg_unbind,
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include "u_serial.h"
|
||||
#if defined USB_ETH_RNDIS
|
||||
|
@ -32,22 +33,11 @@ MODULE_AUTHOR("Michal Nazarewicz");
|
|||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
/***************************** All the files... *****************************/
|
||||
#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"
|
||||
|
||||
#define USBF_ECM_INCLUDED
|
||||
#include "f_ecm.c"
|
||||
#include "u_ecm.h"
|
||||
#ifdef USB_ETH_RNDIS
|
||||
# define USB_FRNDIS_INCLUDED
|
||||
# include "f_rndis.c"
|
||||
# include "u_rndis.h"
|
||||
# include "rndis.h"
|
||||
#endif
|
||||
#include "u_ether.h"
|
||||
|
@ -132,22 +122,36 @@ static struct usb_gadget_strings *dev_strings[] = {
|
|||
/****************************** Configurations ******************************/
|
||||
|
||||
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);
|
||||
|
||||
static struct fsg_common fsg_common;
|
||||
|
||||
static u8 host_mac[ETH_ALEN];
|
||||
|
||||
static struct usb_function_instance *fi_acm;
|
||||
static struct eth_dev *the_dev;
|
||||
static struct usb_function_instance *fi_msg;
|
||||
|
||||
/********** RNDIS **********/
|
||||
|
||||
#ifdef USB_ETH_RNDIS
|
||||
static struct usb_function_instance *fi_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)
|
||||
{
|
||||
struct fsg_opts *fsg_opts;
|
||||
int ret;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
return ret;
|
||||
goto err_func_rndis;
|
||||
|
||||
f_acm_rndis = usb_get_function(fi_acm);
|
||||
if (IS_ERR(f_acm_rndis))
|
||||
return PTR_ERR(f_acm_rndis);
|
||||
if (IS_ERR(f_acm_rndis)) {
|
||||
ret = PTR_ERR(f_acm_rndis);
|
||||
goto err_func_acm;
|
||||
}
|
||||
|
||||
ret = usb_add_function(c, f_acm_rndis);
|
||||
if (ret)
|
||||
goto err_conf;
|
||||
|
||||
ret = fsg_bind_config(c->cdev, c, &fsg_common);
|
||||
if (ret < 0)
|
||||
f_msg_rndis = usb_get_function(fi_msg);
|
||||
if (IS_ERR(f_msg_rndis)) {
|
||||
ret = PTR_ERR(f_msg_rndis);
|
||||
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;
|
||||
err_run:
|
||||
usb_put_function(f_msg_rndis);
|
||||
err_fsg:
|
||||
usb_remove_function(c, f_acm_rndis);
|
||||
err_conf:
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -205,10 +232,14 @@ static __ref int rndis_config_register(struct usb_composite_dev *cdev)
|
|||
/********** CDC ECM **********/
|
||||
|
||||
#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_ecm;
|
||||
static struct usb_function *f_msg_multi;
|
||||
|
||||
static __init int cdc_do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct fsg_opts *fsg_opts;
|
||||
int ret;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
return ret;
|
||||
goto err_func_ecm;
|
||||
|
||||
/* implicit port_num is zero */
|
||||
f_acm_multi = usb_get_function(fi_acm);
|
||||
if (IS_ERR(f_acm_multi))
|
||||
return PTR_ERR(f_acm_multi);
|
||||
if (IS_ERR(f_acm_multi)) {
|
||||
ret = PTR_ERR(f_acm_multi);
|
||||
goto err_func_acm;
|
||||
}
|
||||
|
||||
ret = usb_add_function(c, f_acm_multi);
|
||||
if (ret)
|
||||
goto err_conf;
|
||||
|
||||
ret = fsg_bind_config(c->cdev, c, &fsg_common);
|
||||
if (ret < 0)
|
||||
f_msg_multi = usb_get_function(fi_msg);
|
||||
if (IS_ERR(f_msg_multi)) {
|
||||
ret = PTR_ERR(f_msg_multi);
|
||||
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;
|
||||
err_run:
|
||||
usb_put_function(f_msg_multi);
|
||||
err_fsg:
|
||||
usb_remove_function(c, f_acm_multi);
|
||||
err_conf:
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
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;
|
||||
|
||||
if (!can_support_ecm(cdev->gadget)) {
|
||||
dev_err(&gadget->dev, "controller '%s' not usable\n",
|
||||
gadget->name);
|
||||
gadget->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* set up network link layer */
|
||||
the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, host_mac,
|
||||
qmult);
|
||||
if (IS_ERR(the_dev))
|
||||
return PTR_ERR(the_dev);
|
||||
#ifdef CONFIG_USB_G_MULTI_CDC
|
||||
fi_ecm = usb_get_function_instance("ecm");
|
||||
if (IS_ERR(fi_ecm))
|
||||
return PTR_ERR(fi_ecm);
|
||||
|
||||
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 */
|
||||
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 */
|
||||
{
|
||||
void *retp;
|
||||
retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
|
||||
if (IS_ERR(retp)) {
|
||||
status = PTR_ERR(retp);
|
||||
goto fail1;
|
||||
}
|
||||
fi_msg = usb_get_function_instance("mass_storage");
|
||||
if (IS_ERR(fi_msg)) {
|
||||
status = PTR_ERR(fi_msg);
|
||||
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 */
|
||||
status = usb_string_ids_tab(cdev, strings_dev);
|
||||
if (unlikely(status < 0))
|
||||
goto fail2;
|
||||
goto fail_string_ids;
|
||||
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
|
||||
|
||||
/* register configurations */
|
||||
status = rndis_config_register(cdev);
|
||||
if (unlikely(status < 0))
|
||||
goto fail2;
|
||||
goto fail_string_ids;
|
||||
|
||||
status = cdc_config_register(cdev);
|
||||
if (unlikely(status < 0))
|
||||
goto fail2;
|
||||
goto fail_string_ids;
|
||||
usb_composite_overwrite_options(cdev, &coverwrite);
|
||||
|
||||
/* we're done */
|
||||
dev_info(&gadget->dev, DRIVER_DESC "\n");
|
||||
fsg_common_put(&fsg_common);
|
||||
return 0;
|
||||
|
||||
|
||||
/* 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:
|
||||
fsg_common_put(&fsg_common);
|
||||
usb_put_function_instance(fi_msg);
|
||||
fail1:
|
||||
usb_put_function_instance(fi_acm);
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
usb_put_function(f_acm_multi);
|
||||
#endif
|
||||
|
@ -342,7 +482,14 @@ static int __exit multi_unbind(struct usb_composite_dev *cdev)
|
|||
usb_put_function(f_acm_rndis);
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
if (!trb_hw) {
|
||||
kfree(trb);
|
||||
dev_err(u3d->dev,
|
||||
"%s, dma_pool_alloc fail\n", __func__);
|
||||
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);
|
||||
if (!trb_hw) {
|
||||
kfree(trb);
|
||||
dev_err(u3d->dev,
|
||||
"%s, trb_hw alloc fail\n", __func__);
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -83,9 +83,12 @@ struct s3c_hsotg_req;
|
|||
* @dir_in: Set to true if this endpoint is of the IN direction, which
|
||||
* means that it is sending data to the Host.
|
||||
* @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.
|
||||
* @halted: Set if the endpoint has been halted.
|
||||
* @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.
|
||||
* @total_data: The total number of data bytes done.
|
||||
* @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 index;
|
||||
unsigned char mc;
|
||||
unsigned char interval;
|
||||
|
||||
unsigned int halted:1;
|
||||
unsigned int periodic:1;
|
||||
unsigned int isochronous:1;
|
||||
unsigned int sent_zlp:1;
|
||||
|
||||
char name[10];
|
||||
|
@ -468,6 +474,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
|
|||
void *data;
|
||||
int can_write;
|
||||
int pkt_round;
|
||||
int max_transfer;
|
||||
|
||||
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. */
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n",
|
||||
__func__, gnptxsts, can_write, to_write, hs_ep->ep.maxpacket);
|
||||
max_transfer = hs_ep->ep.maxpacket * hs_ep->mc;
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
* doing it.
|
||||
*/
|
||||
if (to_write > hs_ep->ep.maxpacket) {
|
||||
to_write = hs_ep->ep.maxpacket;
|
||||
if (to_write > max_transfer) {
|
||||
to_write = max_transfer;
|
||||
|
||||
s3c_hsotg_en_gsint(hsotg,
|
||||
periodic ? GINTSTS_PTxFEmp :
|
||||
GINTSTS_NPTxFEmp);
|
||||
/* it's needed only when we do not use dedicated fifos */
|
||||
if (!hsotg->dedicated_fifos)
|
||||
s3c_hsotg_en_gsint(hsotg,
|
||||
periodic ? GINTSTS_PTxFEmp :
|
||||
GINTSTS_NPTxFEmp);
|
||||
}
|
||||
|
||||
/* see if we can write data */
|
||||
|
||||
if (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
|
||||
|
@ -581,9 +592,11 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
|
|||
* is more room left.
|
||||
*/
|
||||
|
||||
s3c_hsotg_en_gsint(hsotg,
|
||||
periodic ? GINTSTS_PTxFEmp :
|
||||
GINTSTS_NPTxFEmp);
|
||||
/* it's needed only when we do not use dedicated fifos */
|
||||
if (!hsotg->dedicated_fifos)
|
||||
s3c_hsotg_en_gsint(hsotg,
|
||||
periodic ? GINTSTS_PTxFEmp :
|
||||
GINTSTS_NPTxFEmp);
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
epsize = DxEPTSIZ_MC(1);
|
||||
if (hs_ep->isochronous)
|
||||
epsize = DxEPTSIZ_MC(packets);
|
||||
else
|
||||
epsize = DxEPTSIZ_MC(1);
|
||||
else
|
||||
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",
|
||||
__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);
|
||||
struct s3c_hsotg_ep *ep;
|
||||
int ret;
|
||||
bool halted;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: %s_FEATURE\n",
|
||||
__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)) {
|
||||
case USB_ENDPOINT_HALT:
|
||||
halted = ep->halted;
|
||||
|
||||
s3c_hsotg_ep_sethalt(&ep->ep, set);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
* then complete it
|
||||
|
@ -1147,6 +1179,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
|
||||
|
||||
/**
|
||||
* s3c_hsotg_process_control - process a control request
|
||||
* @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
|
||||
* 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
|
||||
* @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];
|
||||
void __iomem *regs = hsotg->regs;
|
||||
u32 mpsval;
|
||||
u32 mcval;
|
||||
u32 reg;
|
||||
|
||||
if (ep == 0) {
|
||||
|
@ -1705,15 +1744,19 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg,
|
|||
mpsval = s3c_hsotg_ep0_mps(mps);
|
||||
if (mpsval > 3)
|
||||
goto bad_mps;
|
||||
hs_ep->ep.maxpacket = mps;
|
||||
hs_ep->mc = 1;
|
||||
} else {
|
||||
if (mps >= DxEPCTL_MPS_LIMIT+1)
|
||||
mpsval = mps & DxEPCTL_MPS_MASK;
|
||||
if (mpsval > 1024)
|
||||
goto bad_mps;
|
||||
|
||||
mpsval = mps;
|
||||
mcval = ((mps >> 11) & 0x3) + 1;
|
||||
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
|
||||
* 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (hs_req->req.actual < hs_req->req.length) {
|
||||
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 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx);
|
||||
u32 ints;
|
||||
u32 ctrl;
|
||||
|
||||
ints = readl(hsotg->regs + epint_reg);
|
||||
ctrl = readl(hsotg->regs + epctl_reg);
|
||||
|
||||
/* Clear endpoint interrupts */
|
||||
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);
|
||||
|
||||
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,
|
||||
"%s: XferCompl: DxEPCTL=0x%08x, DxEPTSIZ=%08x\n",
|
||||
__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)
|
||||
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 */
|
||||
if (ints & DIEPMSK_INTknTXFEmpMsk) {
|
||||
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) \
|
||||
do { \
|
||||
if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
|
||||
(_hs)->driver && (_hs)->driver->_entry) { \
|
||||
spin_unlock(&_hs->lock); \
|
||||
(_hs)->driver->_entry(&(_hs)->gadget); \
|
||||
spin_lock(&_hs->lock); \
|
||||
}
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* s3c_hsotg_disconnect - disconnect service
|
||||
|
@ -2241,15 +2304,19 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
|
|||
GAHBCFG_HBstLen_Incr4,
|
||||
hsotg->regs + GAHBCFG);
|
||||
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
|
||||
* up being flooded with interrupts if the host is polling the
|
||||
* endpoint to try and read data.
|
||||
* If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts
|
||||
* when we have no data to transfer. Otherwise we get being flooded by
|
||||
* interrupts.
|
||||
*/
|
||||
|
||||
writel(((hsotg->dedicated_fifos) ? DIEPMSK_TxFIFOEmpty : 0) |
|
||||
writel(((hsotg->dedicated_fifos) ? DIEPMSK_TxFIFOEmpty |
|
||||
DIEPMSK_INTknTXFEmpMsk : 0) |
|
||||
DIEPMSK_EPDisbldMsk | DIEPMSK_XferComplMsk |
|
||||
DIEPMSK_TimeOUTMsk | DIEPMSK_AHBErrMsk |
|
||||
DIEPMSK_INTknEPMisMsk,
|
||||
|
@ -2378,10 +2445,14 @@ static irqreturn_t s3c_hsotg_irq(int irq, void *pw)
|
|||
|
||||
if (gintsts & (GINTSTS_OEPInt | GINTSTS_IEPInt)) {
|
||||
u32 daint = readl(hsotg->regs + DAINT);
|
||||
u32 daint_out = daint >> DAINT_OutEP_SHIFT;
|
||||
u32 daint_in = daint & ~(daint_out << DAINT_OutEP_SHIFT);
|
||||
u32 daintmsk = readl(hsotg->regs + DAINTMSK);
|
||||
u32 daint_out, daint_in;
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
/* update the endpoint state */
|
||||
hs_ep->ep.maxpacket = mps;
|
||||
s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps);
|
||||
|
||||
/* default, set to non-periodic */
|
||||
hs_ep->isochronous = 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) {
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
dev_err(hsotg->dev, "no current ISOC support\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
epctrl |= DxEPCTL_EPType_Iso;
|
||||
epctrl |= DxEPCTL_SetEvenFr;
|
||||
hs_ep->isochronous = 1;
|
||||
if (dir_in)
|
||||
hs_ep->periodic = 1;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
epctrl |= DxEPCTL_EPType_Bulk;
|
||||
|
@ -2634,7 +2714,6 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
|
|||
/* enable the endpoint interrupt */
|
||||
s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
@ -2776,6 +2855,8 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
|
|||
|
||||
writel(epctl, hs->regs + epreg);
|
||||
|
||||
hs_ep->halted = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2903,7 +2984,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
|
|||
int ret;
|
||||
|
||||
if (!hsotg) {
|
||||
printk(KERN_ERR "%s: called with no device\n", __func__);
|
||||
pr_err("%s: called with no device\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -3066,7 +3147,7 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
|
|||
|
||||
hs_ep->parent = hsotg;
|
||||
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;
|
||||
|
||||
/*
|
||||
|
@ -3200,7 +3281,7 @@ static int state_show(struct seq_file *seq, void *v)
|
|||
readl(regs + GNPTXSTS),
|
||||
readl(regs + GRXSTSR));
|
||||
|
||||
seq_printf(seq, "\nEndpoint status:\n");
|
||||
seq_puts(seq, "\nEndpoint status:\n");
|
||||
|
||||
for (idx = 0; idx < 15; idx++) {
|
||||
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",
|
||||
in, out);
|
||||
|
||||
seq_printf(seq, "\n");
|
||||
seq_puts(seq, "\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -3251,7 +3332,7 @@ static int fifo_show(struct seq_file *seq, void *v)
|
|||
u32 val;
|
||||
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));
|
||||
|
||||
val = readl(regs + GNPTXFSIZ);
|
||||
|
@ -3259,7 +3340,7 @@ static int fifo_show(struct seq_file *seq, void *v)
|
|||
val >> GNPTXFSIZ_NPTxFDep_SHIFT,
|
||||
val & GNPTXFSIZ_NPTxFStAddr_MASK);
|
||||
|
||||
seq_printf(seq, "\nPeriodic TXFIFOs:\n");
|
||||
seq_puts(seq, "\nPeriodic TXFIFOs:\n");
|
||||
|
||||
for (idx = 1; idx <= 15; 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 + DOEPTSIZ(index)));
|
||||
|
||||
seq_printf(seq, "\n");
|
||||
seq_puts(seq, "\n");
|
||||
seq_printf(seq, "mps %d\n", ep->ep.maxpacket);
|
||||
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) {
|
||||
if (--show_limit < 0) {
|
||||
seq_printf(seq, "not showing more requests...\n");
|
||||
seq_puts(seq, "not showing more requests...\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,242 +23,17 @@
|
|||
* 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 <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
|
||||
};
|
||||
|
||||
#include "storage_common.h"
|
||||
|
||||
/* There is only one interface. */
|
||||
|
||||
static struct usb_interface_descriptor
|
||||
fsg_intf_desc = {
|
||||
struct usb_interface_descriptor fsg_intf_desc = {
|
||||
.bLength = sizeof fsg_intf_desc,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
|
@ -268,14 +43,14 @@ fsg_intf_desc = {
|
|||
.bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */
|
||||
.iInterface = FSG_STRING_INTERFACE,
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_intf_desc);
|
||||
|
||||
/*
|
||||
* Three full-speed endpoint descriptors: bulk-in, bulk-out, and
|
||||
* interrupt-in.
|
||||
*/
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_fs_bulk_in_desc = {
|
||||
struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
@ -283,9 +58,9 @@ fsg_fs_bulk_in_desc = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
/* wMaxPacketSize set by autoconfiguration */
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_fs_bulk_in_desc);
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_fs_bulk_out_desc = {
|
||||
struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
@ -293,13 +68,15 @@ fsg_fs_bulk_out_desc = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
/* 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_fs_bulk_in_desc,
|
||||
(struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
|
||||
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
|
||||
* for the configuration descriptor.
|
||||
*/
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_hs_bulk_in_desc = {
|
||||
struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
@ -319,9 +95,9 @@ fsg_hs_bulk_in_desc = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_hs_bulk_in_desc);
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_hs_bulk_out_desc = {
|
||||
struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
@ -330,17 +106,18 @@ fsg_hs_bulk_out_desc = {
|
|||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
.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_hs_bulk_in_desc,
|
||||
(struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
|
||||
NULL,
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_hs_function);
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_ss_bulk_in_desc = {
|
||||
struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
@ -348,16 +125,17 @@ fsg_ss_bulk_in_desc = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.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),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
/*.bMaxBurst = DYNAMIC, */
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_ss_bulk_in_comp_desc);
|
||||
|
||||
static struct usb_endpoint_descriptor
|
||||
fsg_ss_bulk_out_desc = {
|
||||
struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
@ -365,15 +143,17 @@ fsg_ss_bulk_out_desc = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.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),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
/*.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_ss_bulk_in_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,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* 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,
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_ss_function);
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -401,7 +171,7 @@ static struct usb_gadget_strings fsg_stringtab = {
|
|||
* 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) {
|
||||
LDBG(curlun, "close backing file\n");
|
||||
|
@ -409,9 +179,9 @@ static void fsg_lun_close(struct fsg_lun *curlun)
|
|||
curlun->filp = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_lun_close);
|
||||
|
||||
|
||||
static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
|
||||
int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
|
||||
{
|
||||
int ro;
|
||||
struct file *filp = NULL;
|
||||
|
@ -508,6 +278,7 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
|
|||
fput(filp);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_lun_open);
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -516,7 +287,7 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
|
|||
* Sync the file data, don't bother with the metadata.
|
||||
* 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;
|
||||
|
||||
|
@ -524,8 +295,9 @@ static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
|
|||
return 0;
|
||||
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) {
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(store_cdrom_address);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static ssize_t ro_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf)
|
||||
{
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
|
||||
? curlun->ro
|
||||
: curlun->initially_ro);
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_show_ro);
|
||||
|
||||
static ssize_t nofua_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf)
|
||||
{
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", curlun->nofua);
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_show_nofua);
|
||||
|
||||
static ssize_t file_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
|
||||
char *buf)
|
||||
{
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
struct rw_semaphore *filesem = dev_get_drvdata(dev);
|
||||
char *p;
|
||||
ssize_t rc;
|
||||
|
||||
|
@ -591,17 +357,44 @@ static ssize_t file_show(struct device *dev, struct device_attribute *attr,
|
|||
up_read(filesem);
|
||||
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,
|
||||
const char *buf, size_t count)
|
||||
ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
|
||||
{
|
||||
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;
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
struct rw_semaphore *filesem = dev_get_drvdata(dev);
|
||||
unsigned ro;
|
||||
bool ro;
|
||||
|
||||
rc = kstrtouint(buf, 2, &ro);
|
||||
rc = strtobool(buf, &ro);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
@ -610,27 +403,21 @@ static ssize_t ro_store(struct device *dev, struct device_attribute *attr,
|
|||
* backing file is closed.
|
||||
*/
|
||||
down_read(filesem);
|
||||
if (fsg_lun_is_open(curlun)) {
|
||||
LDBG(curlun, "read-only status change prevented\n");
|
||||
rc = -EBUSY;
|
||||
} else {
|
||||
curlun->ro = ro;
|
||||
curlun->initially_ro = ro;
|
||||
LDBG(curlun, "read-only status set to %d\n", curlun->ro);
|
||||
rc = _fsg_store_ro(curlun, ro);
|
||||
if (!rc)
|
||||
rc = count;
|
||||
}
|
||||
up_read(filesem);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_store_ro);
|
||||
|
||||
static ssize_t nofua_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
|
||||
{
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
unsigned nofua;
|
||||
bool nofua;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 2, &nofua);
|
||||
ret = strtobool(buf, &nofua);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -642,12 +429,11 @@ static ssize_t nofua_store(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_store_nofua);
|
||||
|
||||
static ssize_t file_store(struct device *dev, struct device_attribute *attr,
|
||||
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)
|
||||
{
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
struct rw_semaphore *filesem = dev_get_drvdata(dev);
|
||||
int rc = 0;
|
||||
|
||||
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);
|
||||
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);
|
||||
return 0;
|
||||
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 = NULL;
|
||||
udc->dev.driver = NULL;
|
||||
|
|
|
@ -95,6 +95,18 @@ unsigned autoresume = DEFAULT_AUTORESUME;
|
|||
module_param(autoresume, uint, S_IRUGO);
|
||||
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 = {
|
||||
|
@ -183,8 +195,16 @@ static void zero_suspend(struct usb_composite_dev *cdev)
|
|||
return;
|
||||
|
||||
if (autoresume) {
|
||||
mod_timer(&autoresume_timer, jiffies + (HZ * autoresume));
|
||||
DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume);
|
||||
if (max_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
|
||||
DBG(cdev, "%s\n", __func__);
|
||||
}
|
||||
|
@ -316,6 +336,7 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
|
|||
if (autoresume) {
|
||||
sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
autoresume_step_ms = autoresume * 1000;
|
||||
}
|
||||
|
||||
/* support OTG systems */
|
||||
|
|
|
@ -91,7 +91,7 @@ config USB_MUSB_BLACKFIN
|
|||
depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523)
|
||||
|
||||
config USB_MUSB_UX500
|
||||
tristate "U8500 and U5500"
|
||||
tristate "Ux500 platforms"
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -113,7 +113,7 @@ choice
|
|||
allow using DMA on multiplatform kernels.
|
||||
|
||||
config USB_UX500_DMA
|
||||
bool 'ST Ericsson U8500 and U5500'
|
||||
bool 'ST Ericsson Ux500'
|
||||
depends on USB_MUSB_UX500
|
||||
help
|
||||
Enable DMA transfers on UX500 platforms.
|
||||
|
|
|
@ -89,7 +89,6 @@ struct am35x_glue {
|
|||
struct clk *phy_clk;
|
||||
struct clk *clk;
|
||||
};
|
||||
#define glue_to_musb(g) platform_get_drvdata(g->musb)
|
||||
|
||||
/*
|
||||
* am35x_musb_enable - enable interrupts
|
||||
|
@ -452,14 +451,18 @@ static const struct musb_platform_ops am35x_ops = {
|
|||
.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)
|
||||
{
|
||||
struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct platform_device *musb;
|
||||
struct am35x_glue *glue;
|
||||
|
||||
struct platform_device_info pinfo;
|
||||
struct clk *phy_clk;
|
||||
struct clk *clk;
|
||||
|
||||
|
@ -471,12 +474,6 @@ static int am35x_probe(struct platform_device *pdev)
|
|||
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");
|
||||
if (IS_ERR(phy_clk)) {
|
||||
dev_err(&pdev->dev, "failed to get PHY clock\n");
|
||||
|
@ -503,12 +500,7 @@ static int am35x_probe(struct platform_device *pdev)
|
|||
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->musb = musb;
|
||||
glue->phy_clk = phy_clk;
|
||||
glue->clk = clk;
|
||||
|
||||
|
@ -516,22 +508,17 @@ static int am35x_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, glue);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err7;
|
||||
}
|
||||
pinfo = am35x_dev_info;
|
||||
pinfo.parent = &pdev->dev;
|
||||
pinfo.res = pdev->resource;
|
||||
pinfo.num_res = pdev->num_resources;
|
||||
pinfo.data = pdata;
|
||||
pinfo.size_data = sizeof(*pdata);
|
||||
|
||||
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add platform_data\n");
|
||||
goto err7;
|
||||
}
|
||||
|
||||
ret = platform_device_add(musb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
glue->musb = musb = platform_device_register_full(&pinfo);
|
||||
if (IS_ERR(musb)) {
|
||||
ret = PTR_ERR(musb);
|
||||
dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
|
||||
goto err7;
|
||||
}
|
||||
|
||||
|
@ -550,9 +537,6 @@ static int am35x_probe(struct platform_device *pdev)
|
|||
clk_put(phy_clk);
|
||||
|
||||
err3:
|
||||
platform_device_put(musb);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
|
@ -615,23 +599,16 @@ static int am35x_resume(struct device *dev)
|
|||
|
||||
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
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(am35x_pm_ops, am35x_suspend, am35x_resume);
|
||||
|
||||
static struct platform_driver am35x_driver = {
|
||||
.probe = am35x_probe,
|
||||
.remove = am35x_remove,
|
||||
.driver = {
|
||||
.name = "musb-am35x",
|
||||
.pm = DEV_PM_OPS,
|
||||
.pm = &am35x_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -561,23 +561,16 @@ static int bfin_resume(struct device *dev)
|
|||
|
||||
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
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(bfin_pm_ops, bfin_suspend, bfin_resume);
|
||||
|
||||
static struct platform_driver bfin_driver = {
|
||||
.probe = bfin_probe,
|
||||
.remove = __exit_p(bfin_remove),
|
||||
.driver = {
|
||||
.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,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -480,7 +484,7 @@ static int da8xx_probe(struct platform_device *pdev)
|
|||
struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct platform_device *musb;
|
||||
struct da8xx_glue *glue;
|
||||
|
||||
struct platform_device_info pinfo;
|
||||
struct clk *clk;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
@ -491,12 +495,6 @@ static int da8xx_probe(struct platform_device *pdev)
|
|||
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");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock\n");
|
||||
|
@ -510,12 +508,7 @@ static int da8xx_probe(struct platform_device *pdev)
|
|||
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->musb = musb;
|
||||
glue->clk = clk;
|
||||
|
||||
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].flags = pdev->resource[1].flags;
|
||||
|
||||
ret = platform_device_add_resources(musb, musb_resources,
|
||||
ARRAY_SIZE(musb_resources));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err5;
|
||||
}
|
||||
pinfo = da8xx_dev_info;
|
||||
pinfo.parent = &pdev->dev;
|
||||
pinfo.res = musb_resources;
|
||||
pinfo.num_res = ARRAY_SIZE(musb_resources);
|
||||
pinfo.data = pdata;
|
||||
pinfo.size_data = sizeof(*pdata);
|
||||
|
||||
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add platform_data\n");
|
||||
goto err5;
|
||||
}
|
||||
|
||||
ret = platform_device_add(musb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
glue->musb = musb = platform_device_register_full(&pinfo);
|
||||
if (IS_ERR(musb)) {
|
||||
ret = PTR_ERR(musb);
|
||||
dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
|
||||
goto err5;
|
||||
}
|
||||
|
||||
|
@ -563,9 +551,6 @@ static int da8xx_probe(struct platform_device *pdev)
|
|||
clk_put(clk);
|
||||
|
||||
err3:
|
||||
platform_device_put(musb);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
|
|
|
@ -505,14 +505,19 @@ static const struct musb_platform_ops davinci_ops = {
|
|||
.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)
|
||||
{
|
||||
struct resource musb_resources[2];
|
||||
struct resource musb_resources[3];
|
||||
struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct platform_device *musb;
|
||||
struct davinci_glue *glue;
|
||||
struct platform_device_info pinfo;
|
||||
struct clk *clk;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
@ -523,12 +528,6 @@ static int davinci_probe(struct platform_device *pdev)
|
|||
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");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock\n");
|
||||
|
@ -542,12 +541,7 @@ static int davinci_probe(struct platform_device *pdev)
|
|||
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->musb = musb;
|
||||
glue->clk = clk;
|
||||
|
||||
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].flags = pdev->resource[1].flags;
|
||||
|
||||
ret = platform_device_add_resources(musb, musb_resources,
|
||||
ARRAY_SIZE(musb_resources));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err5;
|
||||
}
|
||||
/*
|
||||
* For DM6467 3 resources are passed. A placeholder for the 3rd
|
||||
* resource is always there, so it's safe to always copy it...
|
||||
*/
|
||||
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));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add platform_data\n");
|
||||
goto err5;
|
||||
}
|
||||
pinfo = davinci_dev_info;
|
||||
pinfo.parent = &pdev->dev;
|
||||
pinfo.res = musb_resources;
|
||||
pinfo.num_res = ARRAY_SIZE(musb_resources);
|
||||
pinfo.data = pdata;
|
||||
pinfo.size_data = sizeof(*pdata);
|
||||
|
||||
ret = platform_device_add(musb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
glue->musb = musb = platform_device_register_full(&pinfo);
|
||||
if (IS_ERR(musb)) {
|
||||
ret = PTR_ERR(musb);
|
||||
dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
|
||||
goto err5;
|
||||
}
|
||||
|
||||
|
@ -595,9 +593,6 @@ static int davinci_probe(struct platform_device *pdev)
|
|||
clk_put(clk);
|
||||
|
||||
err3:
|
||||
platform_device_put(musb);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
|
|
|
@ -46,7 +46,7 @@ static struct platform_driver am335x_child_driver = {
|
|||
.remove = am335x_child_remove,
|
||||
.driver = {
|
||||
.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);
|
||||
free_irq(musb->nIrq, musb);
|
||||
}
|
||||
if (musb->dma_controller)
|
||||
dma_controller_destroy(musb->dma_controller);
|
||||
cancel_work_sync(&musb->irq_work);
|
||||
|
||||
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);
|
||||
|
||||
if (use_dma && dev->dma_mask)
|
||||
if (use_dma && dev->dma_mask) {
|
||||
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 */
|
||||
musb_platform_disable(musb);
|
||||
|
@ -1946,6 +1950,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
|
|||
if (status < 0)
|
||||
goto fail3;
|
||||
status = musb_gadget_setup(musb);
|
||||
if (status)
|
||||
musb_host_cleanup(musb);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "unsupported port mode %d\n", musb->port_mode);
|
||||
|
@ -1972,10 +1978,12 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
|
|||
|
||||
fail4:
|
||||
musb_gadget_cleanup(musb);
|
||||
musb_host_cleanup(musb);
|
||||
|
||||
fail3:
|
||||
if (musb->dma_controller)
|
||||
dma_controller_destroy(musb->dma_controller);
|
||||
fail2_5:
|
||||
pm_runtime_put_sync(musb->controller);
|
||||
|
||||
fail2:
|
||||
|
@ -2032,6 +2040,9 @@ static int musb_remove(struct platform_device *pdev)
|
|||
musb_exit_debugfs(musb);
|
||||
musb_shutdown(pdev);
|
||||
|
||||
if (musb->dma_controller)
|
||||
dma_controller_destroy(musb->dma_controller);
|
||||
|
||||
musb_free(musb);
|
||||
device_init_wakeup(dev, 0);
|
||||
return 0;
|
||||
|
|
|
@ -484,6 +484,7 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
|
|||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (port > MUSB_DMA_NUM_CHANNELS || !port)
|
||||
goto err;
|
||||
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);
|
||||
if (!dc) {
|
||||
dev_err(dev, "Falied to request %s.\n", str);
|
||||
ret = -EPROBE_DEFER;
|
||||
goto err;
|
||||
}
|
||||
cppi41_channel->dc = dc;
|
||||
|
@ -510,7 +512,7 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
|
|||
return 0;
|
||||
err:
|
||||
cppi41_release_all_dma_chans(controller);
|
||||
return -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dma_controller_destroy(struct dma_controller *c)
|
||||
|
@ -526,7 +528,7 @@ struct dma_controller *dma_controller_create(struct musb *musb,
|
|||
void __iomem *base)
|
||||
{
|
||||
struct cppi41_dma_controller *controller;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (!musb->controller->of_node) {
|
||||
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:
|
||||
kfree(controller);
|
||||
kzalloc_fail:
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ERR_PTR(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -121,6 +121,43 @@ struct dsps_glue {
|
|||
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
|
||||
*/
|
||||
|
@ -143,6 +180,7 @@ static void dsps_musb_enable(struct musb *musb)
|
|||
/* Force the DRVVBUS IRQ so we can start polling for ID change. */
|
||||
dsps_writel(reg_base, wrp->coreintr_set,
|
||||
(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;
|
||||
u8 devctl;
|
||||
unsigned long flags;
|
||||
int skip_session = 0;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
devctl &= ~MUSB_DEVCTL_SESSION;
|
||||
dsps_writeb(musb->mregs, MUSB_DEVCTL, devctl);
|
||||
dsps_writeb(musb->mregs, MUSB_DEVCTL, 0);
|
||||
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) {
|
||||
musb->xceiv->state = OTG_STATE_B_IDLE;
|
||||
MUSB_DEV_MODE(musb);
|
||||
|
@ -194,60 +235,21 @@ static void otg_timer(unsigned long _musb)
|
|||
musb->xceiv->state = OTG_STATE_A_IDLE;
|
||||
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;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
|
||||
dsps_writel(musb->ctrl_base, wrp->coreintr_set,
|
||||
MUSB_INTR_VBUSERROR << wrp->usb_shift);
|
||||
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:
|
||||
break;
|
||||
}
|
||||
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)
|
||||
{
|
||||
struct musb *musb = hci;
|
||||
|
@ -631,7 +633,7 @@ static struct platform_driver dsps_usbss_driver = {
|
|||
.remove = dsps_remove,
|
||||
.driver = {
|
||||
.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;
|
||||
}
|
||||
|
||||
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(
|
||||
struct usb_hcd *hcd,
|
||||
u16 typeReq,
|
||||
|
@ -362,7 +379,7 @@ int musb_hub_control(
|
|||
* initialization logic, e.g. for OTG, or change any
|
||||
* 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);
|
||||
break;
|
||||
case USB_PORT_FEAT_RESET:
|
||||
|
|
|
@ -306,6 +306,9 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
|
|||
default:
|
||||
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,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -1160,7 +1164,7 @@ static int tusb_probe(struct platform_device *pdev)
|
|||
struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct platform_device *musb;
|
||||
struct tusb6010_glue *glue;
|
||||
|
||||
struct platform_device_info pinfo;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
||||
|
@ -1169,18 +1173,7 @@ static int tusb_probe(struct platform_device *pdev)
|
|||
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->musb = musb;
|
||||
|
||||
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].flags = pdev->resource[2].flags;
|
||||
|
||||
ret = platform_device_add_resources(musb, musb_resources,
|
||||
ARRAY_SIZE(musb_resources));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err3;
|
||||
}
|
||||
pinfo = tusb_dev_info;
|
||||
pinfo.parent = &pdev->dev;
|
||||
pinfo.res = musb_resources;
|
||||
pinfo.num_res = ARRAY_SIZE(musb_resources);
|
||||
pinfo.data = pdata;
|
||||
pinfo.size_data = sizeof(*pdata);
|
||||
|
||||
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add platform_data\n");
|
||||
goto err3;
|
||||
}
|
||||
|
||||
ret = platform_device_add(musb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
glue->musb = musb = platform_device_register_full(&pinfo);
|
||||
if (IS_ERR(musb)) {
|
||||
ret = PTR_ERR(musb);
|
||||
dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
|
||||
goto err3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
platform_device_put(musb);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
|
|
|
@ -376,17 +376,10 @@ static int ux500_resume(struct device *dev)
|
|||
|
||||
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
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ux500_pm_ops, ux500_suspend, ux500_resume);
|
||||
|
||||
static const struct of_device_id ux500_match[] = {
|
||||
{ .compatible = "stericsson,db8500-musb", },
|
||||
{}
|
||||
|
@ -397,7 +390,7 @@ static struct platform_driver ux500_driver = {
|
|||
.remove = ux500_remove,
|
||||
.driver = {
|
||||
.name = "musb-ux500",
|
||||
.pm = DEV_PM_OPS,
|
||||
.pm = &ux500_pm_ops,
|
||||
.of_match_table = ux500_match,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -194,6 +194,19 @@ config USB_RCAR_PHY
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called phy-rcar-usb.
|
||||
|
||||
config USB_RCAR_GEN2_PHY
|
||||
tristate "Renesas R-Car Gen2 USB PHY support"
|
||||
depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST
|
||||
select USB_PHY
|
||||
help
|
||||
Say Y here to add support for the Renesas R-Car Gen2 USB PHY driver.
|
||||
It is typically used to control internal USB PHY for USBHS,
|
||||
and to configure shared USB channels 0 and 2.
|
||||
This driver supports R8A7790 and R8A7791.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called phy-rcar-gen2-usb.
|
||||
|
||||
config USB_ULPI
|
||||
bool "Generic ULPI Transceiver Driver"
|
||||
depends on ARM
|
||||
|
|
|
@ -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_MXS_PHY) += phy-mxs-usb.o
|
||||
obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o
|
||||
obj-$(CONFIG_USB_RCAR_GEN2_PHY) += phy-rcar-gen2-usb.o
|
||||
obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
|
||||
obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o
|
||||
|
|
|
@ -26,6 +26,41 @@ struct am335x_control_usb {
|
|||
#define USBPHY_OTGVDET_EN (1 << 19)
|
||||
#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)
|
||||
{
|
||||
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 = {
|
||||
.phy_power = am335x_phy_power,
|
||||
.phy_wkup = am335x_phy_wkup,
|
||||
};
|
||||
|
||||
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);
|
||||
if (IS_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);
|
||||
ctrl_usb->phy_ctrl = *phy_ctrl;
|
||||
|
||||
|
@ -129,7 +171,7 @@ static struct platform_driver am335x_control_driver = {
|
|||
.driver = {
|
||||
.name = "am335x-control-usb",
|
||||
.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,
|
||||
USB_PHY_TYPE_USB2, 0, false, false);
|
||||
USB_PHY_TYPE_USB2, 0, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = usb_add_phy_dev(&am_phy->usb_phy_gen.phy);
|
||||
if (ret)
|
||||
goto err_add;
|
||||
return ret;
|
||||
am_phy->usb_phy_gen.phy.init = am335x_init;
|
||||
am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown;
|
||||
|
||||
platform_set_drvdata(pdev, am_phy);
|
||||
|
||||
return 0;
|
||||
|
||||
err_add:
|
||||
usb_phy_gen_cleanup_phy(&am_phy->usb_phy_gen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -79,6 +78,40 @@ static int am335x_phy_remove(struct platform_device *pdev)
|
|||
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[] = {
|
||||
{ .compatible = "ti,am335x-usb-phy" },
|
||||
{ }
|
||||
|
@ -91,7 +124,8 @@ static struct platform_driver am335x_phy_driver = {
|
|||
.driver = {
|
||||
.name = "am335x-phy-driver",
|
||||
.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 */
|
||||
|
||||
/* 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;
|
||||
|
||||
|
@ -170,7 +170,7 @@ void fsl_otg_dischrg_vbus(int on)
|
|||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
|
@ -188,7 +188,7 @@ void fsl_otg_drv_vbus(int on)
|
|||
* Pull-up D+, signalling connect by periperal. Also used in
|
||||
* 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;
|
||||
|
||||
|
@ -207,7 +207,7 @@ void fsl_otg_loc_conn(int on)
|
|||
* port. In host mode, controller will automatically send SOF.
|
||||
* 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;
|
||||
|
||||
|
@ -222,7 +222,7 @@ void fsl_otg_loc_sof(int on)
|
|||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
|
@ -235,7 +235,7 @@ void fsl_otg_start_pulse(void)
|
|||
fsl_otg_loc_conn(1);
|
||||
#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)
|
||||
|
@ -252,14 +252,14 @@ void b_data_pulse_end(unsigned long foo)
|
|||
void fsl_otg_pulse_vbus(void)
|
||||
{
|
||||
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 */
|
||||
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)
|
||||
{
|
||||
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
|
||||
|
@ -267,7 +267,7 @@ void b_vbus_pulse_end(unsigned long foo)
|
|||
* residual voltage of vbus pulsing and A device pull up
|
||||
*/
|
||||
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)
|
||||
|
@ -289,7 +289,7 @@ void a_wait_enum(unsigned long foo)
|
|||
{
|
||||
VDBG("a_wait_enum timeout\n");
|
||||
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
|
||||
otg_statemachine(&fsl_otg_dev->fsm);
|
||||
}
|
||||
|
@ -375,8 +375,42 @@ void fsl_otg_uninit_timers(void)
|
|||
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 */
|
||||
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 *tmp_timer;
|
||||
|
@ -394,8 +428,19 @@ void fsl_otg_add_timer(void *gtimer)
|
|||
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 */
|
||||
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 *tmp_timer, *del_tmp;
|
||||
|
@ -405,6 +450,17 @@ void fsl_otg_del_timer(void *gtimer)
|
|||
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.
|
||||
* 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);
|
||||
if (fsm->id) {
|
||||
/* default-b */
|
||||
fsl_otg_drv_vbus(1);
|
||||
fsl_otg_drv_vbus(fsm, 1);
|
||||
/*
|
||||
* Workaround: b_host can't driver
|
||||
* 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);
|
||||
if (fsm->id)
|
||||
/* default-b */
|
||||
fsl_otg_drv_vbus(0);
|
||||
fsl_otg_drv_vbus(fsm, 0);
|
||||
}
|
||||
otg_dev->host_working = 0;
|
||||
}
|
||||
|
@ -757,8 +813,8 @@ static struct otg_fsm_ops fsl_otg_ops = {
|
|||
.loc_sof = fsl_otg_loc_sof,
|
||||
.start_pulse = fsl_otg_start_pulse,
|
||||
|
||||
.add_timer = fsl_otg_add_timer,
|
||||
.del_timer = fsl_otg_del_timer,
|
||||
.add_timer = fsl_otg_fsm_add_timer,
|
||||
.del_timer = fsl_otg_fsm_del_timer,
|
||||
|
||||
.start_host = fsl_otg_start_host,
|
||||
.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_conn: %d\n"
|
||||
"b_se0_srp: %d\n"
|
||||
"b_sess_end: %d\n"
|
||||
"b_ssend_srp: %d\n"
|
||||
"b_sess_vld: %d\n"
|
||||
"id: %d\n",
|
||||
fsm->a_bus_req,
|
||||
|
@ -1026,7 +1082,7 @@ static int show_fsl_usb2_otg_state(struct device *dev,
|
|||
fsm->b_bus_suspend,
|
||||
fsm->b_conn,
|
||||
fsm->b_se0_srp,
|
||||
fsm->b_sess_end,
|
||||
fsm->b_ssend_srp,
|
||||
fsm->b_sess_vld,
|
||||
fsm->id);
|
||||
size -= t;
|
||||
|
@ -1057,7 +1113,7 @@ static long fsl_otg_ioctl(struct file *file, unsigned int cmd,
|
|||
break;
|
||||
|
||||
case SET_A_SUSPEND_REQ:
|
||||
fsl_otg_dev->fsm.a_suspend_req = arg;
|
||||
fsl_otg_dev->fsm.a_suspend_req_inf = arg;
|
||||
break;
|
||||
|
||||
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_B_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 9, int)
|
||||
|
||||
void fsl_otg_add_timer(void *timer);
|
||||
void fsl_otg_del_timer(void *timer);
|
||||
void fsl_otg_add_timer(struct otg_fsm *fsm, void *timer);
|
||||
void fsl_otg_del_timer(struct otg_fsm *fsm, void *timer);
|
||||
void fsl_otg_pulse_vbus(void);
|
||||
|
|
|
@ -41,17 +41,17 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
|
|||
fsm->protocol, protocol);
|
||||
/* stop old protocol */
|
||||
if (fsm->protocol == PROTO_HOST)
|
||||
ret = fsm->ops->start_host(fsm, 0);
|
||||
ret = otg_start_host(fsm, 0);
|
||||
else if (fsm->protocol == PROTO_GADGET)
|
||||
ret = fsm->ops->start_gadget(fsm, 0);
|
||||
ret = otg_start_gadget(fsm, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* start new protocol */
|
||||
if (protocol == PROTO_HOST)
|
||||
ret = fsm->ops->start_host(fsm, 1);
|
||||
ret = otg_start_host(fsm, 1);
|
||||
else if (protocol == PROTO_GADGET)
|
||||
ret = fsm->ops->start_gadget(fsm, 1);
|
||||
ret = otg_start_gadget(fsm, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -69,42 +69,50 @@ void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
|
|||
{
|
||||
switch (old_state) {
|
||||
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->adp_sns = 0;
|
||||
fsm->adp_prb = 0;
|
||||
break;
|
||||
case OTG_STATE_B_SRP_INIT:
|
||||
fsm->data_pulse = 0;
|
||||
fsm->b_srp_done = 0;
|
||||
break;
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
case OTG_STATE_B_HOST:
|
||||
break;
|
||||
case OTG_STATE_A_IDLE:
|
||||
fsm->adp_prb = 0;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
case OTG_STATE_A_HOST:
|
||||
otg_del_timer(fsm, a_wait_enum_tmr);
|
||||
otg_del_timer(fsm, A_WAIT_ENUM);
|
||||
break;
|
||||
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_suspend_req = 0;
|
||||
fsm->a_suspend_req_inf = 0;
|
||||
break;
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
otg_del_timer(fsm, A_BIDL_ADIS);
|
||||
fsm->a_bidl_adis_tmout = 0;
|
||||
break;
|
||||
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;
|
||||
case OTG_STATE_A_VBUS_ERR:
|
||||
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_loc_conn(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_add_timer(fsm, b_se0_srp_tmr);
|
||||
otg_add_timer(fsm, B_SE0_SRP);
|
||||
break;
|
||||
case OTG_STATE_B_SRP_INIT:
|
||||
otg_start_pulse(fsm);
|
||||
otg_loc_sof(fsm, 0);
|
||||
otg_set_protocol(fsm, PROTO_UNDEF);
|
||||
otg_add_timer(fsm, b_srp_fail_tmr);
|
||||
otg_add_timer(fsm, B_SRP_FAIL);
|
||||
break;
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
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_sof(fsm, 0);
|
||||
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;
|
||||
break;
|
||||
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_loc_conn(fsm, 0);
|
||||
otg_loc_sof(fsm, 0);
|
||||
otg_start_adp_prb(fsm);
|
||||
otg_set_protocol(fsm, PROTO_HOST);
|
||||
break;
|
||||
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_sof(fsm, 0);
|
||||
otg_set_protocol(fsm, PROTO_HOST);
|
||||
otg_add_timer(fsm, a_wait_vrise_tmr);
|
||||
otg_add_timer(fsm, A_WAIT_VRISE);
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
otg_drv_vbus(fsm, 1);
|
||||
otg_loc_conn(fsm, 0);
|
||||
otg_loc_sof(fsm, 0);
|
||||
otg_set_protocol(fsm, PROTO_HOST);
|
||||
otg_add_timer(fsm, a_wait_bcon_tmr);
|
||||
otg_add_timer(fsm, A_WAIT_BCON);
|
||||
break;
|
||||
case OTG_STATE_A_HOST:
|
||||
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
|
||||
* suspend too fast to complete a_set_b_hnp_en
|
||||
*/
|
||||
if (!fsm->a_bus_req || fsm->a_suspend_req)
|
||||
otg_add_timer(fsm, a_wait_enum_tmr);
|
||||
if (!fsm->a_bus_req || fsm->a_suspend_req_inf)
|
||||
otg_add_timer(fsm, A_WAIT_ENUM);
|
||||
break;
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
otg_drv_vbus(fsm, 1);
|
||||
otg_loc_conn(fsm, 0);
|
||||
otg_loc_sof(fsm, 0);
|
||||
otg_set_protocol(fsm, PROTO_HOST);
|
||||
otg_add_timer(fsm, a_aidl_bdis_tmr);
|
||||
otg_add_timer(fsm, A_AIDL_BDIS);
|
||||
|
||||
break;
|
||||
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_set_protocol(fsm, PROTO_GADGET);
|
||||
otg_drv_vbus(fsm, 1);
|
||||
otg_add_timer(fsm, A_BIDL_ADIS);
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
otg_drv_vbus(fsm, 0);
|
||||
otg_loc_conn(fsm, 0);
|
||||
otg_loc_sof(fsm, 0);
|
||||
otg_set_protocol(fsm, PROTO_HOST);
|
||||
otg_add_timer(fsm, A_WAIT_VFALL);
|
||||
break;
|
||||
case OTG_STATE_A_VBUS_ERR:
|
||||
otg_drv_vbus(fsm, 0);
|
||||
|
@ -250,7 +266,8 @@ int otg_statemachine(struct otg_fsm *fsm)
|
|||
otg_set_state(fsm, OTG_STATE_A_IDLE);
|
||||
else if (fsm->b_sess_vld && fsm->otg->gadget)
|
||||
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);
|
||||
break;
|
||||
case OTG_STATE_B_SRP_INIT:
|
||||
|
@ -277,13 +294,14 @@ int otg_statemachine(struct otg_fsm *fsm)
|
|||
case OTG_STATE_B_HOST:
|
||||
if (!fsm->id || !fsm->b_sess_vld)
|
||||
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);
|
||||
break;
|
||||
case OTG_STATE_A_IDLE:
|
||||
if (fsm->id)
|
||||
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);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
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)
|
||||
otg_set_state(fsm, OTG_STATE_A_SUSPEND);
|
||||
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:
|
||||
if (fsm->id || fsm->a_bus_drop)
|
||||
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);
|
||||
else if (!fsm->a_vbus_vld)
|
||||
otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld &&
|
||||
!fsm->b_conn))
|
||||
if (fsm->a_wait_vfall_tmout || fsm->id || fsm->a_bus_req ||
|
||||
(!fsm->a_sess_vld && !fsm->b_conn))
|
||||
otg_set_state(fsm, OTG_STATE_A_IDLE);
|
||||
break;
|
||||
case OTG_STATE_A_VBUS_ERR:
|
||||
|
|
|
@ -34,45 +34,76 @@
|
|||
#define PROTO_HOST (1)
|
||||
#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 */
|
||||
struct otg_fsm {
|
||||
/* 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_suspend;
|
||||
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_srp_det;
|
||||
int a_vbus_vld;
|
||||
int b_bus_resume;
|
||||
int b_bus_suspend;
|
||||
int b_conn;
|
||||
int b_se0_srp;
|
||||
int b_sess_end;
|
||||
int b_sess_vld;
|
||||
int id;
|
||||
|
||||
/* Output */
|
||||
int data_pulse;
|
||||
int drv_vbus;
|
||||
int loc_conn;
|
||||
int loc_sof;
|
||||
int adp_prb;
|
||||
int adp_sns;
|
||||
|
||||
/* Internal variables */
|
||||
int a_set_b_hnp_en;
|
||||
int b_srp_done;
|
||||
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 */
|
||||
int a_wait_vrise_tmout;
|
||||
int a_wait_vfall_tmout;
|
||||
int a_wait_bcon_tmout;
|
||||
int a_aidl_bdis_tmout;
|
||||
int b_ase0_brst_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;
|
||||
int a_bidl_adis_tmout;
|
||||
|
||||
struct otg_fsm_ops *ops;
|
||||
struct usb_otg *otg;
|
||||
|
@ -83,65 +114,123 @@ struct otg_fsm {
|
|||
};
|
||||
|
||||
struct otg_fsm_ops {
|
||||
void (*chrg_vbus)(int on);
|
||||
void (*drv_vbus)(int on);
|
||||
void (*loc_conn)(int on);
|
||||
void (*loc_sof)(int on);
|
||||
void (*start_pulse)(void);
|
||||
void (*add_timer)(void *timer);
|
||||
void (*del_timer)(void *timer);
|
||||
void (*chrg_vbus)(struct otg_fsm *fsm, int on);
|
||||
void (*drv_vbus)(struct otg_fsm *fsm, int on);
|
||||
void (*loc_conn)(struct otg_fsm *fsm, int on);
|
||||
void (*loc_sof)(struct otg_fsm *fsm, int on);
|
||||
void (*start_pulse)(struct otg_fsm *fsm);
|
||||
void (*start_adp_prb)(struct otg_fsm *fsm);
|
||||
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_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) {
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
|
||||
/* 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/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "phy-generic.h"
|
||||
|
||||
|
@ -64,6 +67,23 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
|
|||
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)
|
||||
{
|
||||
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))
|
||||
clk_enable(nop->clk);
|
||||
clk_prepare_enable(nop->clk);
|
||||
|
||||
if (!IS_ERR(nop->reset)) {
|
||||
/* De-assert RESET */
|
||||
if (regulator_enable(nop->reset))
|
||||
dev_err(phy->dev, "Failed to de-assert reset\n");
|
||||
}
|
||||
/* De-assert RESET */
|
||||
nop_reset_set(nop, 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);
|
||||
|
||||
if (!IS_ERR(nop->reset)) {
|
||||
/* Assert RESET */
|
||||
if (regulator_disable(nop->reset))
|
||||
dev_err(phy->dev, "Failed to assert reset\n");
|
||||
}
|
||||
/* Assert RESET */
|
||||
nop_reset_set(nop, 1);
|
||||
|
||||
if (!IS_ERR(nop->clk))
|
||||
clk_disable(nop->clk);
|
||||
clk_disable_unprepare(nop->clk);
|
||||
|
||||
if (!IS_ERR(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,
|
||||
enum usb_phy_type type, u32 clk_rate, bool needs_vcc,
|
||||
bool needs_reset)
|
||||
enum usb_phy_type type, u32 clk_rate, bool needs_vcc)
|
||||
{
|
||||
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");
|
||||
if (IS_ERR(nop->vcc)) {
|
||||
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;
|
||||
}
|
||||
|
||||
nop->reset = devm_regulator_get(dev, "reset");
|
||||
if (IS_ERR(nop->reset)) {
|
||||
dev_dbg(dev, "Error getting reset regulator: %ld\n",
|
||||
PTR_ERR(nop->reset));
|
||||
if (needs_reset)
|
||||
return -EPROBE_DEFER;
|
||||
if (gpio_is_valid(nop->gpio_reset)) {
|
||||
unsigned long gpio_flags;
|
||||
|
||||
/* Assert RESET */
|
||||
if (nop->reset_active_low)
|
||||
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;
|
||||
|
@ -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);
|
||||
|
||||
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)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -217,31 +225,36 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev)
|
|||
int err;
|
||||
u32 clk_rate = 0;
|
||||
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);
|
||||
if (!nop)
|
||||
return -ENOMEM;
|
||||
|
||||
nop->reset_active_low = true; /* default behaviour */
|
||||
|
||||
err = usb_phy_gen_create_phy(dev, nop, type, clk_rate, needs_vcc,
|
||||
needs_reset);
|
||||
if (dev->of_node) {
|
||||
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)
|
||||
return err;
|
||||
|
||||
|
@ -252,15 +265,13 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev)
|
|||
if (err) {
|
||||
dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
|
||||
err);
|
||||
goto err_add;
|
||||
return err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, nop);
|
||||
|
||||
return 0;
|
||||
|
||||
err_add:
|
||||
usb_phy_gen_cleanup_phy(nop);
|
||||
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);
|
||||
|
||||
usb_phy_gen_cleanup_phy(nop);
|
||||
usb_remove_phy(&nop->phy);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -6,15 +6,14 @@ struct usb_phy_gen_xceiv {
|
|||
struct device *dev;
|
||||
struct clk *clk;
|
||||
struct regulator *vcc;
|
||||
struct regulator *reset;
|
||||
int gpio_reset;
|
||||
bool reset_active_low;
|
||||
};
|
||||
|
||||
int usb_gen_phy_init(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,
|
||||
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);
|
||||
enum usb_phy_type type, u32 clk_rate, bool needs_vcc);
|
||||
|
||||
#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 = {
|
||||
.name = "tegra-phy",
|
||||
.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);
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/* usb register definitions */
|
||||
#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);
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
phy = usb_get_phy(type);
|
||||
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 */
|
||||
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))
|
||||
|
|
Loading…
Add table
Reference in a new issue