mirror of https://gitee.com/openkylin/linux.git
Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (144 commits) USB: add support for Dream Cheeky DL100B Webmail Notifier (1d34:0004) USB: serial: ftdi_sio: add support for TIOCSERGETLSR USB: ehci-mxc: Setup portsc register prior to accessing OTG viewport USB: atmel_usba_udc: fix freeing irq in usba_udc_remove() usb: ehci-omap: fix tll channel enable mask usb: ohci-omap3: fix trivial typo USB: gadget: ci13xxx: don't assume that PAGE_SIZE is 4096 USB: gadget: ci13xxx: fix complete() callback for no_interrupt rq's USB: gadget: update ci13xxx to work with g_ether USB: gadgets: ci13xxx: fix probing of compiled-in gadget drivers Revert "USB: musb: pm: don't rely fully on clock support" Revert "USB: musb: blackfin: pm: make it work" USB: uas: Use GFP_NOIO instead of GFP_KERNEL in I/O submission path USB: uas: Ensure we only bind to a UAS interface USB: uas: Rename sense pipe and sense urb to status pipe and status urb USB: uas: Use kzalloc instead of kmalloc USB: uas: Fix up the Sense IU usb: musb: core: kill unneeded #include's DA8xx: assign name to MUSB IRQ resource usb: gadget: g_ncm added ... Manually fix up trivial conflicts in USB Kconfig changes in: arch/arm/mach-omap2/Kconfig arch/sh/Kconfig drivers/usb/Kconfig drivers/usb/host/ehci-hcd.c and annoying chip clock data conflicts in: arch/arm/mach-omap2/clock3xxx_data.c arch/arm/mach-omap2/clock44xx_data.c
This commit is contained in:
commit
3e5b08cbbf
|
@ -2,7 +2,7 @@
|
|||
|
||||
Alan Stern <stern@rowland.harvard.edu>
|
||||
|
||||
December 11, 2009
|
||||
October 28, 2010
|
||||
|
||||
|
||||
|
||||
|
@ -107,9 +107,14 @@ allowed to issue dynamic suspends.
|
|||
The user interface for controlling dynamic PM is located in the power/
|
||||
subdirectory of each USB device's sysfs directory, that is, in
|
||||
/sys/bus/usb/devices/.../power/ where "..." is the device's ID. The
|
||||
relevant attribute files are: wakeup, control, and autosuspend.
|
||||
(There may also be a file named "level"; this file was deprecated
|
||||
as of the 2.6.35 kernel and replaced by the "control" file.)
|
||||
relevant attribute files are: wakeup, control, and
|
||||
autosuspend_delay_ms. (There may also be a file named "level"; this
|
||||
file was deprecated as of the 2.6.35 kernel and replaced by the
|
||||
"control" file. In 2.6.38 the "autosuspend" file will be deprecated
|
||||
and replaced by the "autosuspend_delay_ms" file. The only difference
|
||||
is that the newer file expresses the delay in milliseconds whereas the
|
||||
older file uses seconds. Confusingly, both files are present in 2.6.37
|
||||
but only "autosuspend" works.)
|
||||
|
||||
power/wakeup
|
||||
|
||||
|
@ -140,33 +145,36 @@ as of the 2.6.35 kernel and replaced by the "control" file.)
|
|||
suspended and autoresume was not allowed. This
|
||||
setting is no longer supported.)
|
||||
|
||||
power/autosuspend
|
||||
power/autosuspend_delay_ms
|
||||
|
||||
This file contains an integer value, which is the
|
||||
number of seconds the device should remain idle before
|
||||
the kernel will autosuspend it (the idle-delay time).
|
||||
The default is 2. 0 means to autosuspend as soon as
|
||||
the device becomes idle, and negative values mean
|
||||
never to autosuspend. You can write a number to the
|
||||
file to change the autosuspend idle-delay time.
|
||||
number of milliseconds the device should remain idle
|
||||
before the kernel will autosuspend it (the idle-delay
|
||||
time). The default is 2000. 0 means to autosuspend
|
||||
as soon as the device becomes idle, and negative
|
||||
values mean never to autosuspend. You can write a
|
||||
number to the file to change the autosuspend
|
||||
idle-delay time.
|
||||
|
||||
Writing "-1" to power/autosuspend and writing "on" to power/control do
|
||||
essentially the same thing -- they both prevent the device from being
|
||||
autosuspended. Yes, this is a redundancy in the API.
|
||||
Writing "-1" to power/autosuspend_delay_ms and writing "on" to
|
||||
power/control do essentially the same thing -- they both prevent the
|
||||
device from being autosuspended. Yes, this is a redundancy in the
|
||||
API.
|
||||
|
||||
(In 2.6.21 writing "0" to power/autosuspend would prevent the device
|
||||
from being autosuspended; the behavior was changed in 2.6.22. The
|
||||
power/autosuspend attribute did not exist prior to 2.6.21, and the
|
||||
power/level attribute did not exist prior to 2.6.22. power/control
|
||||
was added in 2.6.34.)
|
||||
was added in 2.6.34, and power/autosuspend_delay_ms was added in
|
||||
2.6.37 but did not become functional until 2.6.38.)
|
||||
|
||||
|
||||
Changing the default idle-delay time
|
||||
------------------------------------
|
||||
|
||||
The default autosuspend idle-delay time is controlled by a module
|
||||
parameter in usbcore. You can specify the value when usbcore is
|
||||
loaded. For example, to set it to 5 seconds instead of 2 you would
|
||||
The default autosuspend idle-delay time (in seconds) is controlled by
|
||||
a module parameter in usbcore. You can specify the value when usbcore
|
||||
is loaded. For example, to set it to 5 seconds instead of 2 you would
|
||||
do:
|
||||
|
||||
modprobe usbcore autosuspend=5
|
||||
|
@ -234,25 +242,23 @@ every device.
|
|||
|
||||
If a driver knows that its device has proper suspend/resume support,
|
||||
it can enable autosuspend all by itself. For example, the video
|
||||
driver for a laptop's webcam might do this, since these devices are
|
||||
rarely used and so should normally be autosuspended.
|
||||
driver for a laptop's webcam might do this (in recent kernels they
|
||||
do), since these devices are rarely used and so should normally be
|
||||
autosuspended.
|
||||
|
||||
Sometimes it turns out that even when a device does work okay with
|
||||
autosuspend there are still problems. For example, there are
|
||||
experimental patches adding autosuspend support to the usbhid driver,
|
||||
which manages keyboards and mice, among other things. Tests with a
|
||||
number of keyboards showed that typing on a suspended keyboard, while
|
||||
causing the keyboard to do a remote wakeup all right, would
|
||||
nonetheless frequently result in lost keystrokes. Tests with mice
|
||||
showed that some of them would issue a remote-wakeup request in
|
||||
response to button presses but not to motion, and some in response to
|
||||
neither.
|
||||
autosuspend there are still problems. For example, the usbhid driver,
|
||||
which manages keyboards and mice, has autosuspend support. Tests with
|
||||
a number of keyboards show that typing on a suspended keyboard, while
|
||||
causing the keyboard to do a remote wakeup all right, will nonetheless
|
||||
frequently result in lost keystrokes. Tests with mice show that some
|
||||
of them will issue a remote-wakeup request in response to button
|
||||
presses but not to motion, and some in response to neither.
|
||||
|
||||
The kernel will not prevent you from enabling autosuspend on devices
|
||||
that can't handle it. It is even possible in theory to damage a
|
||||
device by suspending it at the wrong time -- for example, suspending a
|
||||
USB hard disk might cause it to spin down without parking the heads.
|
||||
(Highly unlikely, but possible.) Take care.
|
||||
device by suspending it at the wrong time. (Highly unlikely, but
|
||||
possible.) Take care.
|
||||
|
||||
|
||||
The driver interface for Power Management
|
||||
|
@ -336,10 +342,6 @@ autosuspend the interface's device. When the usage counter is = 0
|
|||
then the interface is considered to be idle, and the kernel may
|
||||
autosuspend the device.
|
||||
|
||||
(There is a similar usage counter field in struct usb_device,
|
||||
associated with the device itself rather than any of its interfaces.
|
||||
This counter is used only by the USB core.)
|
||||
|
||||
Drivers need not be concerned about balancing changes to the usage
|
||||
counter; the USB core will undo any remaining "get"s when a driver
|
||||
is unbound from its interface. As a corollary, drivers must not call
|
||||
|
@ -409,11 +411,11 @@ during autosuspend. For example, there's not much point
|
|||
autosuspending a keyboard if the user can't cause the keyboard to do a
|
||||
remote wakeup by typing on it. If the driver sets
|
||||
intf->needs_remote_wakeup to 1, the kernel won't autosuspend the
|
||||
device if remote wakeup isn't available or has been disabled through
|
||||
the power/wakeup attribute. (If the device is already autosuspended,
|
||||
though, setting this flag won't cause the kernel to autoresume it.
|
||||
Normally a driver would set this flag in its probe method, at which
|
||||
time the device is guaranteed not to be autosuspended.)
|
||||
device if remote wakeup isn't available. (If the device is already
|
||||
autosuspended, though, setting this flag won't cause the kernel to
|
||||
autoresume it. Normally a driver would set this flag in its probe
|
||||
method, at which time the device is guaranteed not to be
|
||||
autosuspended.)
|
||||
|
||||
If a driver does its I/O asynchronously in interrupt context, it
|
||||
should call usb_autopm_get_interface_async() before starting output and
|
||||
|
@ -422,20 +424,19 @@ it receives an input event, it should call
|
|||
|
||||
usb_mark_last_busy(struct usb_device *udev);
|
||||
|
||||
in the event handler. This sets udev->last_busy to the current time.
|
||||
udev->last_busy is the field used for idle-delay calculations;
|
||||
updating it will cause any pending autosuspend to be moved back. Most
|
||||
of the usb_autopm_* routines will also set the last_busy field to the
|
||||
current time.
|
||||
in the event handler. This tells the PM core that the device was just
|
||||
busy and therefore the next autosuspend idle-delay expiration should
|
||||
be pushed back. Many of the usb_autopm_* routines also make this call,
|
||||
so drivers need to worry only when interrupt-driven input arrives.
|
||||
|
||||
Asynchronous operation is always subject to races. For example, a
|
||||
driver may call one of the usb_autopm_*_interface_async() routines at
|
||||
a time when the core has just finished deciding the device has been
|
||||
idle for long enough but not yet gotten around to calling the driver's
|
||||
suspend method. The suspend method must be responsible for
|
||||
synchronizing with the output request routine and the URB completion
|
||||
handler; it should cause autosuspends to fail with -EBUSY if the
|
||||
driver needs to use the device.
|
||||
driver may call the usb_autopm_get_interface_async() routine at a time
|
||||
when the core has just finished deciding the device has been idle for
|
||||
long enough but not yet gotten around to calling the driver's suspend
|
||||
method. The suspend method must be responsible for synchronizing with
|
||||
the I/O request routine and the URB completion handler; it should
|
||||
cause autosuspends to fail with -EBUSY if the driver needs to use the
|
||||
device.
|
||||
|
||||
External suspend calls should never be allowed to fail in this way,
|
||||
only autosuspend calls. The driver can tell them apart by checking
|
||||
|
@ -472,7 +473,9 @@ Firstly, a device may already be autosuspended when a system suspend
|
|||
occurs. Since system suspends are supposed to be as transparent as
|
||||
possible, the device should remain suspended following the system
|
||||
resume. But this theory may not work out well in practice; over time
|
||||
the kernel's behavior in this regard has changed.
|
||||
the kernel's behavior in this regard has changed. As of 2.6.37 the
|
||||
policy is to resume all devices during a system resume and let them
|
||||
handle their own runtime suspends afterward.
|
||||
|
||||
Secondly, a dynamic power-management event may occur as a system
|
||||
suspend is underway. The window for this is short, since system
|
||||
|
|
|
@ -64,17 +64,19 @@ static struct resource usb_resources[] = {
|
|||
{
|
||||
.start = IRQ_USBINT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
.name = "mc"
|
||||
},
|
||||
{
|
||||
/* placeholder for the dedicated CPPI IRQ */
|
||||
.flags = IORESOURCE_IRQ,
|
||||
.name = "dma"
|
||||
},
|
||||
};
|
||||
|
||||
static u64 usb_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static struct platform_device usb_dev = {
|
||||
.name = "musb_hdrc",
|
||||
.name = "musb-davinci",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &usb_data,
|
||||
|
@ -110,6 +112,7 @@ static struct resource da8xx_usb20_resources[] = {
|
|||
{
|
||||
.start = IRQ_DA8XX_USB_INT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
.name = "mc",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -121,6 +124,7 @@ int __init da8xx_register_usb20(unsigned mA, unsigned potpgt)
|
|||
|
||||
usb_dev.resource = da8xx_usb20_resources;
|
||||
usb_dev.num_resources = ARRAY_SIZE(da8xx_usb20_resources);
|
||||
usb_dev.name = "musb-da8xx";
|
||||
|
||||
return platform_device_register(&usb_dev);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ config ARCH_OMAP4
|
|||
select ARM_ERRATA_720789
|
||||
select ARCH_HAS_OPP
|
||||
select PM_OPP if PM
|
||||
select USB_ARCH_HAS_EHCI
|
||||
|
||||
comment "OMAP Core Type"
|
||||
depends on ARCH_OMAP2
|
||||
|
|
|
@ -209,9 +209,11 @@ obj-$(CONFIG_MACH_IGEP0030) += board-igep0030.o \
|
|||
obj-$(CONFIG_MACH_OMAP3_TOUCHBOOK) += board-omap3touchbook.o \
|
||||
hsmmc.o
|
||||
obj-$(CONFIG_MACH_OMAP_4430SDP) += board-4430sdp.o \
|
||||
hsmmc.o
|
||||
hsmmc.o \
|
||||
omap_phy_internal.o
|
||||
obj-$(CONFIG_MACH_OMAP4_PANDA) += board-omap4panda.o \
|
||||
hsmmc.o
|
||||
hsmmc.o \
|
||||
omap_phy_internal.o
|
||||
|
||||
obj-$(CONFIG_MACH_OMAP3517EVM) += board-am3517evm.o
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#define ETH_KS8851_IRQ 34
|
||||
#define ETH_KS8851_POWER_ON 48
|
||||
#define ETH_KS8851_QUART 138
|
||||
#define OMAP4SDP_MDM_PWR_EN_GPIO 157
|
||||
#define OMAP4_SFH7741_SENSOR_OUTPUT_GPIO 184
|
||||
#define OMAP4_SFH7741_ENABLE_GPIO 188
|
||||
|
||||
|
@ -250,12 +251,29 @@ static void __init omap_4430sdp_init_irq(void)
|
|||
gic_init_irq();
|
||||
}
|
||||
|
||||
static const struct ehci_hcd_omap_platform_data ehci_pdata __initconst = {
|
||||
.port_mode[0] = EHCI_HCD_OMAP_MODE_PHY,
|
||||
.port_mode[1] = EHCI_HCD_OMAP_MODE_UNKNOWN,
|
||||
.port_mode[2] = EHCI_HCD_OMAP_MODE_UNKNOWN,
|
||||
.phy_reset = false,
|
||||
.reset_gpio_port[0] = -EINVAL,
|
||||
.reset_gpio_port[1] = -EINVAL,
|
||||
.reset_gpio_port[2] = -EINVAL,
|
||||
};
|
||||
|
||||
static struct omap_musb_board_data musb_board_data = {
|
||||
.interface_type = MUSB_INTERFACE_UTMI,
|
||||
.mode = MUSB_PERIPHERAL,
|
||||
.mode = MUSB_OTG,
|
||||
.power = 100,
|
||||
};
|
||||
|
||||
static struct twl4030_usb_data omap4_usbphy_data = {
|
||||
.phy_init = omap4430_phy_init,
|
||||
.phy_exit = omap4430_phy_exit,
|
||||
.phy_power = omap4430_phy_power,
|
||||
.phy_set_clock = omap4430_phy_set_clk,
|
||||
};
|
||||
|
||||
static struct omap2_hsmmc_info mmc[] = {
|
||||
{
|
||||
.mmc = 1,
|
||||
|
@ -475,6 +493,7 @@ static struct twl4030_platform_data sdp4430_twldata = {
|
|||
.vaux1 = &sdp4430_vaux1,
|
||||
.vaux2 = &sdp4430_vaux2,
|
||||
.vaux3 = &sdp4430_vaux3,
|
||||
.usb = &omap4_usbphy_data
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata sdp4430_i2c_boardinfo[] = {
|
||||
|
@ -555,10 +574,14 @@ static void __init omap_4430sdp_init(void)
|
|||
platform_add_devices(sdp4430_devices, ARRAY_SIZE(sdp4430_devices));
|
||||
omap_serial_init();
|
||||
omap4_twl6030_hsmmc_init(mmc);
|
||||
/* OMAP4 SDP uses internal transceiver so register nop transceiver */
|
||||
usb_nop_xceiv_register();
|
||||
/* FIXME: allow multi-omap to boot until musb is updated for omap4 */
|
||||
if (!cpu_is_omap44xx())
|
||||
|
||||
/* Power on the ULPI PHY */
|
||||
if (gpio_is_valid(OMAP4SDP_MDM_PWR_EN_GPIO)) {
|
||||
/* FIXME: Assumes pad is already muxed for GPIO mode */
|
||||
gpio_request(OMAP4SDP_MDM_PWR_EN_GPIO, "USBB1 PHY VMDM_3V3");
|
||||
gpio_direction_output(OMAP4SDP_MDM_PWR_EN_GPIO, 1);
|
||||
}
|
||||
usb_ehci_init(&ehci_pdata);
|
||||
usb_musb_init(&musb_board_data);
|
||||
|
||||
status = omap_ethernet_init();
|
||||
|
|
|
@ -46,8 +46,7 @@ static struct device *mmc_device;
|
|||
#define TUSB6010_GPIO_ENABLE 0
|
||||
#define TUSB6010_DMACHAN 0x3f
|
||||
|
||||
#if defined(CONFIG_USB_TUSB6010) || \
|
||||
defined(CONFIG_USB_TUSB6010_MODULE)
|
||||
#ifdef CONFIG_USB_MUSB_TUSB6010
|
||||
/*
|
||||
* Enable or disable power to TUSB6010. When enabling, turn on 3.3 V and
|
||||
* 1.5 V voltage regulators of PM companion chip. Companion chip will then
|
||||
|
@ -134,7 +133,7 @@ static void __init n8x0_usb_init(void)
|
|||
|
||||
static void __init n8x0_usb_init(void) {}
|
||||
|
||||
#endif /*CONFIG_USB_TUSB6010 */
|
||||
#endif /*CONFIG_USB_MUSB_TUSB6010 */
|
||||
|
||||
|
||||
static struct omap2_mcspi_device_config p54spi_mcspi_config = {
|
||||
|
|
|
@ -134,10 +134,17 @@ static void __init omap4_ehci_init(void)
|
|||
|
||||
static struct omap_musb_board_data musb_board_data = {
|
||||
.interface_type = MUSB_INTERFACE_UTMI,
|
||||
.mode = MUSB_PERIPHERAL,
|
||||
.mode = MUSB_OTG,
|
||||
.power = 100,
|
||||
};
|
||||
|
||||
static struct twl4030_usb_data omap4_usbphy_data = {
|
||||
.phy_init = omap4430_phy_init,
|
||||
.phy_exit = omap4430_phy_exit,
|
||||
.phy_power = omap4430_phy_power,
|
||||
.phy_set_clock = omap4430_phy_set_clk,
|
||||
};
|
||||
|
||||
static struct omap2_hsmmc_info mmc[] = {
|
||||
{
|
||||
.mmc = 1,
|
||||
|
@ -347,6 +354,7 @@ static struct twl4030_platform_data omap4_panda_twldata = {
|
|||
.vaux1 = &omap4_panda_vaux1,
|
||||
.vaux2 = &omap4_panda_vaux2,
|
||||
.vaux3 = &omap4_panda_vaux3,
|
||||
.usb = &omap4_usbphy_data,
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata omap4_panda_i2c_boardinfo[] = {
|
||||
|
@ -394,8 +402,6 @@ static void __init omap4_panda_init(void)
|
|||
/* OMAP4 Panda uses internal transceiver so register nop transceiver */
|
||||
usb_nop_xceiv_register();
|
||||
omap4_ehci_init();
|
||||
/* FIXME: allow multi-omap to boot until musb is updated for omap4 */
|
||||
if (!cpu_is_omap44xx())
|
||||
usb_musb_init(&musb_board_data);
|
||||
}
|
||||
|
||||
|
|
|
@ -1877,7 +1877,7 @@ static struct omap_clk omap2420_clks[] = {
|
|||
CLK("omap-aes", "ick", &aes_ick, CK_242X),
|
||||
CLK(NULL, "pka_ick", &pka_ick, CK_242X),
|
||||
CLK(NULL, "usb_fck", &usb_fck, CK_242X),
|
||||
CLK("musb_hdrc", "fck", &osc_ck, CK_242X),
|
||||
CLK("musb-hdrc", "fck", &osc_ck, CK_242X),
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -1983,7 +1983,7 @@ static struct omap_clk omap2430_clks[] = {
|
|||
CLK("omap-aes", "ick", &aes_ick, CK_243X),
|
||||
CLK(NULL, "pka_ick", &pka_ick, CK_243X),
|
||||
CLK(NULL, "usb_fck", &usb_fck, CK_243X),
|
||||
CLK("musb_hdrc", "ick", &usbhs_ick, CK_243X),
|
||||
CLK("musb-omap2430", "ick", &usbhs_ick, CK_243X),
|
||||
CLK("mmci-omap-hs.0", "ick", &mmchs1_ick, CK_243X),
|
||||
CLK("mmci-omap-hs.0", "fck", &mmchs1_fck, CK_243X),
|
||||
CLK("mmci-omap-hs.1", "ick", &mmchs2_ick, CK_243X),
|
||||
|
|
|
@ -3286,6 +3286,7 @@ static struct omap_clk omap3xxx_clks[] = {
|
|||
CLK(NULL, "cpefuse_fck", &cpefuse_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK(NULL, "ts_fck", &ts_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK(NULL, "usbtll_fck", &usbtll_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK("ehci-omap.0", "usbtll_fck", &usbtll_fck, CK_3430ES2 | CK_AM35XX),
|
||||
CLK("omap-mcbsp.1", "prcm_fck", &core_96m_fck, CK_3XXX),
|
||||
CLK("omap-mcbsp.5", "prcm_fck", &core_96m_fck, CK_3XXX),
|
||||
CLK(NULL, "core_96m_fck", &core_96m_fck, CK_3XXX),
|
||||
|
@ -3313,14 +3314,15 @@ static struct omap_clk omap3xxx_clks[] = {
|
|||
CLK(NULL, "ssi_sst_fck", &ssi_sst_fck_3430es1, CK_3430ES1),
|
||||
CLK(NULL, "ssi_sst_fck", &ssi_sst_fck_3430es2, CK_3430ES2PLUS | CK_36XX),
|
||||
CLK(NULL, "core_l3_ick", &core_l3_ick, CK_3XXX),
|
||||
CLK("musb_hdrc", "ick", &hsotgusb_ick_3430es1, CK_3430ES1),
|
||||
CLK("musb_hdrc", "ick", &hsotgusb_ick_3430es2, CK_3430ES2PLUS | CK_36XX),
|
||||
CLK("musb-omap2430", "ick", &hsotgusb_ick_3430es1, CK_3430ES1),
|
||||
CLK("musb-omap2430", "ick", &hsotgusb_ick_3430es2, CK_3430ES2PLUS | CK_36XX),
|
||||
CLK(NULL, "sdrc_ick", &sdrc_ick, CK_3XXX),
|
||||
CLK(NULL, "gpmc_fck", &gpmc_fck, CK_3XXX),
|
||||
CLK(NULL, "security_l3_ick", &security_l3_ick, CK_34XX | CK_36XX),
|
||||
CLK(NULL, "pka_ick", &pka_ick, CK_34XX | CK_36XX),
|
||||
CLK(NULL, "core_l4_ick", &core_l4_ick, CK_3XXX),
|
||||
CLK(NULL, "usbtll_ick", &usbtll_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK("ehci-omap.0", "usbtll_ick", &usbtll_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK("mmci-omap-hs.2", "ick", &mmchs3_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK(NULL, "icr_ick", &icr_ick, CK_34XX | CK_36XX),
|
||||
CLK("omap-aes", "ick", &aes2_ick, CK_34XX | CK_36XX),
|
||||
|
@ -3366,8 +3368,11 @@ static struct omap_clk omap3xxx_clks[] = {
|
|||
CLK(NULL, "cam_ick", &cam_ick, CK_34XX | CK_36XX),
|
||||
CLK(NULL, "csi2_96m_fck", &csi2_96m_fck, CK_34XX | CK_36XX),
|
||||
CLK(NULL, "usbhost_120m_fck", &usbhost_120m_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK("ehci-omap.0", "hs_fck", &usbhost_120m_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK(NULL, "usbhost_48m_fck", &usbhost_48m_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK("ehci-omap.0", "fs_fck", &usbhost_48m_fck, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK(NULL, "usbhost_ick", &usbhost_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK("ehci-omap.0", "usbhost_ick", &usbhost_ick, CK_3430ES2PLUS | CK_AM35XX | CK_36XX),
|
||||
CLK(NULL, "usim_fck", &usim_fck, CK_3430ES2PLUS | CK_36XX),
|
||||
CLK(NULL, "gpt1_fck", &gpt1_fck, CK_3XXX),
|
||||
CLK(NULL, "wkup_32k_fck", &wkup_32k_fck, CK_3XXX),
|
||||
|
@ -3445,8 +3450,8 @@ static struct omap_clk omap3xxx_clks[] = {
|
|||
CLK("davinci_emac", "phy_clk", &emac_fck, CK_AM35XX),
|
||||
CLK("vpfe-capture", "master", &vpfe_ick, CK_AM35XX),
|
||||
CLK("vpfe-capture", "slave", &vpfe_fck, CK_AM35XX),
|
||||
CLK("musb_hdrc", "ick", &hsotgusb_ick_am35xx, CK_AM35XX),
|
||||
CLK("musb_hdrc", "fck", &hsotgusb_fck_am35xx, CK_AM35XX),
|
||||
CLK("musb-am35x", "ick", &hsotgusb_ick_am35xx, CK_AM35XX),
|
||||
CLK("musb-am35x", "fck", &hsotgusb_fck_am35xx, CK_AM35XX),
|
||||
CLK(NULL, "hecc_ck", &hecc_ck, CK_AM35XX),
|
||||
CLK(NULL, "uart4_ick", &uart4_ick_am35xx, CK_AM35XX),
|
||||
};
|
||||
|
|
|
@ -3198,6 +3198,7 @@ static struct omap_clk omap44xx_clks[] = {
|
|||
CLK(NULL, "uart3_fck", &uart3_fck, CK_443X),
|
||||
CLK(NULL, "uart4_fck", &uart4_fck, CK_443X),
|
||||
CLK(NULL, "usb_host_fs_fck", &usb_host_fs_fck, CK_443X),
|
||||
CLK("ehci-omap.0", "fs_fck", &usb_host_fs_fck, CK_443X),
|
||||
CLK(NULL, "utmi_p1_gfclk", &utmi_p1_gfclk, CK_443X),
|
||||
CLK(NULL, "usb_host_hs_utmi_p1_clk", &usb_host_hs_utmi_p1_clk, CK_443X),
|
||||
CLK(NULL, "utmi_p2_gfclk", &utmi_p2_gfclk, CK_443X),
|
||||
|
@ -3209,14 +3210,18 @@ static struct omap_clk omap44xx_clks[] = {
|
|||
CLK(NULL, "usb_host_hs_hsic480m_p2_clk", &usb_host_hs_hsic480m_p2_clk, CK_443X),
|
||||
CLK(NULL, "usb_host_hs_func48mclk", &usb_host_hs_func48mclk, CK_443X),
|
||||
CLK(NULL, "usb_host_hs_fck", &usb_host_hs_fck, CK_443X),
|
||||
CLK("ehci-omap.0", "hs_fck", &usb_host_hs_fck, CK_443X),
|
||||
CLK("ehci-omap.0", "usbhost_ick", &dummy_ck, CK_443X),
|
||||
CLK(NULL, "otg_60m_gfclk", &otg_60m_gfclk, CK_443X),
|
||||
CLK(NULL, "usb_otg_hs_xclk", &usb_otg_hs_xclk, CK_443X),
|
||||
CLK("musb_hdrc", "ick", &usb_otg_hs_ick, CK_443X),
|
||||
CLK("musb-omap2430", "ick", &usb_otg_hs_ick, CK_443X),
|
||||
CLK(NULL, "usb_phy_cm_clk32k", &usb_phy_cm_clk32k, CK_443X),
|
||||
CLK(NULL, "usb_tll_hs_usb_ch2_clk", &usb_tll_hs_usb_ch2_clk, CK_443X),
|
||||
CLK(NULL, "usb_tll_hs_usb_ch0_clk", &usb_tll_hs_usb_ch0_clk, CK_443X),
|
||||
CLK(NULL, "usb_tll_hs_usb_ch1_clk", &usb_tll_hs_usb_ch1_clk, CK_443X),
|
||||
CLK(NULL, "usb_tll_hs_ick", &usb_tll_hs_ick, CK_443X),
|
||||
CLK("ehci-omap.0", "usbtll_ick", &usb_tll_hs_ick, CK_443X),
|
||||
CLK("ehci-omap.0", "usbtll_fck", &dummy_ck, CK_443X),
|
||||
CLK(NULL, "usim_ck", &usim_ck, CK_443X),
|
||||
CLK(NULL, "usim_fclk", &usim_fclk, CK_443X),
|
||||
CLK(NULL, "usim_fck", &usim_fck, CK_443X),
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* This file configures the internal USB PHY in OMAP4430. Used
|
||||
* with TWL6030 transceiver and MUSB on OMAP4430.
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Author: Hema HK <hemahk@ti.com>
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <plat/usb.h>
|
||||
|
||||
/* OMAP control module register for UTMI PHY */
|
||||
#define CONTROL_DEV_CONF 0x300
|
||||
#define PHY_PD 0x1
|
||||
|
||||
#define USBOTGHS_CONTROL 0x33c
|
||||
#define AVALID BIT(0)
|
||||
#define BVALID BIT(1)
|
||||
#define VBUSVALID BIT(2)
|
||||
#define SESSEND BIT(3)
|
||||
#define IDDIG BIT(4)
|
||||
|
||||
static struct clk *phyclk, *clk48m, *clk32k;
|
||||
static void __iomem *ctrl_base;
|
||||
|
||||
int omap4430_phy_init(struct device *dev)
|
||||
{
|
||||
ctrl_base = ioremap(OMAP443X_SCM_BASE, SZ_1K);
|
||||
if (!ctrl_base) {
|
||||
dev_err(dev, "control module ioremap failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Power down the phy */
|
||||
__raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF);
|
||||
phyclk = clk_get(dev, "ocp2scp_usb_phy_ick");
|
||||
|
||||
if (IS_ERR(phyclk)) {
|
||||
dev_err(dev, "cannot clk_get ocp2scp_usb_phy_ick\n");
|
||||
iounmap(ctrl_base);
|
||||
return PTR_ERR(phyclk);
|
||||
}
|
||||
|
||||
clk48m = clk_get(dev, "ocp2scp_usb_phy_phy_48m");
|
||||
if (IS_ERR(clk48m)) {
|
||||
dev_err(dev, "cannot clk_get ocp2scp_usb_phy_phy_48m\n");
|
||||
clk_put(phyclk);
|
||||
iounmap(ctrl_base);
|
||||
return PTR_ERR(clk48m);
|
||||
}
|
||||
|
||||
clk32k = clk_get(dev, "usb_phy_cm_clk32k");
|
||||
if (IS_ERR(clk32k)) {
|
||||
dev_err(dev, "cannot clk_get usb_phy_cm_clk32k\n");
|
||||
clk_put(phyclk);
|
||||
clk_put(clk48m);
|
||||
iounmap(ctrl_base);
|
||||
return PTR_ERR(clk32k);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omap4430_phy_set_clk(struct device *dev, int on)
|
||||
{
|
||||
static int state;
|
||||
|
||||
if (on && !state) {
|
||||
/* Enable the phy clocks */
|
||||
clk_enable(phyclk);
|
||||
clk_enable(clk48m);
|
||||
clk_enable(clk32k);
|
||||
state = 1;
|
||||
} else if (state) {
|
||||
/* Disable the phy clocks */
|
||||
clk_disable(phyclk);
|
||||
clk_disable(clk48m);
|
||||
clk_disable(clk32k);
|
||||
state = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omap4430_phy_power(struct device *dev, int ID, int on)
|
||||
{
|
||||
if (on) {
|
||||
/* enabled the clocks */
|
||||
omap4430_phy_set_clk(dev, 1);
|
||||
/* power on the phy */
|
||||
if (__raw_readl(ctrl_base + CONTROL_DEV_CONF) & PHY_PD) {
|
||||
__raw_writel(~PHY_PD, ctrl_base + CONTROL_DEV_CONF);
|
||||
mdelay(200);
|
||||
}
|
||||
if (ID)
|
||||
/* enable VBUS valid, IDDIG groung */
|
||||
__raw_writel(AVALID | VBUSVALID, ctrl_base +
|
||||
USBOTGHS_CONTROL);
|
||||
else
|
||||
/*
|
||||
* Enable VBUS Valid, AValid and IDDIG
|
||||
* high impedence
|
||||
*/
|
||||
__raw_writel(IDDIG | AVALID | VBUSVALID,
|
||||
ctrl_base + USBOTGHS_CONTROL);
|
||||
} else {
|
||||
/* Enable session END and IDIG to high impedence. */
|
||||
__raw_writel(SESSEND | IDDIG, ctrl_base +
|
||||
USBOTGHS_CONTROL);
|
||||
/* Disable the clocks */
|
||||
omap4430_phy_set_clk(dev, 0);
|
||||
/* Power down the phy */
|
||||
__raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omap4430_phy_exit(struct device *dev)
|
||||
{
|
||||
if (ctrl_base)
|
||||
iounmap(ctrl_base);
|
||||
if (phyclk)
|
||||
clk_put(phyclk);
|
||||
if (clk48m)
|
||||
clk_put(clk48m);
|
||||
if (clk32k)
|
||||
clk_put(clk32k);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -34,22 +34,15 @@
|
|||
|
||||
static struct resource ehci_resources[] = {
|
||||
{
|
||||
.start = OMAP34XX_EHCI_BASE,
|
||||
.end = OMAP34XX_EHCI_BASE + SZ_1K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.start = OMAP34XX_UHH_CONFIG_BASE,
|
||||
.end = OMAP34XX_UHH_CONFIG_BASE + SZ_1K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.start = OMAP34XX_USBTLL_BASE,
|
||||
.end = OMAP34XX_USBTLL_BASE + SZ_4K - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{ /* general IRQ */
|
||||
.start = INT_34XX_EHCI_IRQ,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
@ -214,13 +207,148 @@ static void setup_ehci_io_mux(const enum ehci_hcd_omap_mode *port_mode)
|
|||
return;
|
||||
}
|
||||
|
||||
static void setup_4430ehci_io_mux(const enum ehci_hcd_omap_mode *port_mode)
|
||||
{
|
||||
switch (port_mode[0]) {
|
||||
case EHCI_HCD_OMAP_MODE_PHY:
|
||||
omap_mux_init_signal("usbb1_ulpiphy_stp",
|
||||
OMAP_PIN_OUTPUT);
|
||||
omap_mux_init_signal("usbb1_ulpiphy_clk",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpiphy_dir",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpiphy_nxt",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpiphy_dat0",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpiphy_dat1",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpiphy_dat2",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpiphy_dat3",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpiphy_dat4",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpiphy_dat5",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpiphy_dat6",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpiphy_dat7",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
break;
|
||||
case EHCI_HCD_OMAP_MODE_TLL:
|
||||
omap_mux_init_signal("usbb1_ulpitll_stp",
|
||||
OMAP_PIN_INPUT_PULLUP);
|
||||
omap_mux_init_signal("usbb1_ulpitll_clk",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpitll_dir",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpitll_nxt",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpitll_dat0",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpitll_dat1",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpitll_dat2",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpitll_dat3",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpitll_dat4",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpitll_dat5",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpitll_dat6",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb1_ulpitll_dat7",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
break;
|
||||
case EHCI_HCD_OMAP_MODE_UNKNOWN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (port_mode[1]) {
|
||||
case EHCI_HCD_OMAP_MODE_PHY:
|
||||
omap_mux_init_signal("usbb2_ulpiphy_stp",
|
||||
OMAP_PIN_OUTPUT);
|
||||
omap_mux_init_signal("usbb2_ulpiphy_clk",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpiphy_dir",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpiphy_nxt",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpiphy_dat0",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpiphy_dat1",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpiphy_dat2",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpiphy_dat3",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpiphy_dat4",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpiphy_dat5",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpiphy_dat6",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpiphy_dat7",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
break;
|
||||
case EHCI_HCD_OMAP_MODE_TLL:
|
||||
omap_mux_init_signal("usbb2_ulpitll_stp",
|
||||
OMAP_PIN_INPUT_PULLUP);
|
||||
omap_mux_init_signal("usbb2_ulpitll_clk",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpitll_dir",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpitll_nxt",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpitll_dat0",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpitll_dat1",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpitll_dat2",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpitll_dat3",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpitll_dat4",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpitll_dat5",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpitll_dat6",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
omap_mux_init_signal("usbb2_ulpitll_dat7",
|
||||
OMAP_PIN_INPUT_PULLDOWN);
|
||||
break;
|
||||
case EHCI_HCD_OMAP_MODE_UNKNOWN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void __init usb_ehci_init(const struct ehci_hcd_omap_platform_data *pdata)
|
||||
{
|
||||
platform_device_add_data(&ehci_device, pdata, sizeof(*pdata));
|
||||
|
||||
/* Setup Pin IO MUX for EHCI */
|
||||
if (cpu_is_omap34xx())
|
||||
if (cpu_is_omap34xx()) {
|
||||
ehci_resources[0].start = OMAP34XX_EHCI_BASE;
|
||||
ehci_resources[0].end = OMAP34XX_EHCI_BASE + SZ_1K - 1;
|
||||
ehci_resources[1].start = OMAP34XX_UHH_CONFIG_BASE;
|
||||
ehci_resources[1].end = OMAP34XX_UHH_CONFIG_BASE + SZ_1K - 1;
|
||||
ehci_resources[2].start = OMAP34XX_USBTLL_BASE;
|
||||
ehci_resources[2].end = OMAP34XX_USBTLL_BASE + SZ_4K - 1;
|
||||
ehci_resources[3].start = INT_34XX_EHCI_IRQ;
|
||||
setup_ehci_io_mux(pdata->port_mode);
|
||||
} else if (cpu_is_omap44xx()) {
|
||||
ehci_resources[0].start = OMAP44XX_HSUSB_EHCI_BASE;
|
||||
ehci_resources[0].end = OMAP44XX_HSUSB_EHCI_BASE + SZ_1K - 1;
|
||||
ehci_resources[1].start = OMAP44XX_UHH_CONFIG_BASE;
|
||||
ehci_resources[1].end = OMAP44XX_UHH_CONFIG_BASE + SZ_2K - 1;
|
||||
ehci_resources[2].start = OMAP44XX_USBTLL_BASE;
|
||||
ehci_resources[2].end = OMAP44XX_USBTLL_BASE + SZ_4K - 1;
|
||||
ehci_resources[3].start = OMAP44XX_IRQ_EHCI;
|
||||
setup_4430ehci_io_mux(pdata->port_mode);
|
||||
}
|
||||
|
||||
if (platform_device_register(&ehci_device) < 0) {
|
||||
printk(KERN_ERR "Unable to register HS-USB (EHCI) device\n");
|
||||
|
|
|
@ -30,8 +30,101 @@
|
|||
#include <mach/irqs.h>
|
||||
#include <mach/am35xx.h>
|
||||
#include <plat/usb.h>
|
||||
#include "control.h"
|
||||
|
||||
#ifdef CONFIG_USB_MUSB_SOC
|
||||
#if defined(CONFIG_USB_MUSB_OMAP2PLUS) || defined (CONFIG_USB_MUSB_AM35X)
|
||||
|
||||
static void am35x_musb_reset(void)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
/* Reset the musb interface */
|
||||
regval = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET);
|
||||
|
||||
regval |= AM35XX_USBOTGSS_SW_RST;
|
||||
omap_ctrl_writel(regval, AM35XX_CONTROL_IP_SW_RESET);
|
||||
|
||||
regval &= ~AM35XX_USBOTGSS_SW_RST;
|
||||
omap_ctrl_writel(regval, AM35XX_CONTROL_IP_SW_RESET);
|
||||
|
||||
regval = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET);
|
||||
}
|
||||
|
||||
static void am35x_musb_phy_power(u8 on)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(100);
|
||||
u32 devconf2;
|
||||
|
||||
if (on) {
|
||||
/*
|
||||
* Start the on-chip PHY and its PLL.
|
||||
*/
|
||||
devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
|
||||
|
||||
devconf2 &= ~(CONF2_RESET | CONF2_PHYPWRDN | CONF2_OTGPWRDN);
|
||||
devconf2 |= CONF2_PHY_PLLON;
|
||||
|
||||
omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
|
||||
|
||||
pr_info(KERN_INFO "Waiting for PHY clock good...\n");
|
||||
while (!(omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2)
|
||||
& CONF2_PHYCLKGD)) {
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
pr_err(KERN_ERR "musb PHY clock good timed out\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Power down the on-chip PHY.
|
||||
*/
|
||||
devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
|
||||
|
||||
devconf2 &= ~CONF2_PHY_PLLON;
|
||||
devconf2 |= CONF2_PHYPWRDN | CONF2_OTGPWRDN;
|
||||
omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
|
||||
}
|
||||
}
|
||||
|
||||
static void am35x_musb_clear_irq(void)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
regval = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR);
|
||||
regval |= AM35XX_USBOTGSS_INT_CLR;
|
||||
omap_ctrl_writel(regval, AM35XX_CONTROL_LVL_INTR_CLEAR);
|
||||
regval = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR);
|
||||
}
|
||||
|
||||
static void am35x_musb_set_mode(u8 musb_mode)
|
||||
{
|
||||
u32 devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
|
||||
|
||||
devconf2 &= ~CONF2_OTGMODE;
|
||||
switch (musb_mode) {
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
case MUSB_HOST: /* Force VBUS valid, ID = 0 */
|
||||
devconf2 |= CONF2_FORCE_HOST;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
case MUSB_PERIPHERAL: /* Force VBUS valid, ID = 1 */
|
||||
devconf2 |= CONF2_FORCE_DEVICE;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
case MUSB_OTG: /* Don't override the VBUS/ID comparators */
|
||||
devconf2 |= CONF2_NO_OVERRIDE;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
pr_info(KERN_INFO "Unsupported mode %u\n", musb_mode);
|
||||
}
|
||||
|
||||
omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
|
||||
}
|
||||
|
||||
static struct resource musb_resources[] = {
|
||||
[0] = { /* start and end set dynamically */
|
||||
|
@ -40,10 +133,12 @@ static struct resource musb_resources[] = {
|
|||
[1] = { /* general IRQ */
|
||||
.start = INT_243X_HS_USB_MC,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
.name = "mc",
|
||||
},
|
||||
[2] = { /* DMA IRQ */
|
||||
.start = INT_243X_HS_USB_DMA,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
.name = "dma",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -75,7 +170,7 @@ static struct musb_hdrc_platform_data musb_plat = {
|
|||
static u64 musb_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static struct platform_device musb_device = {
|
||||
.name = "musb_hdrc",
|
||||
.name = "musb-omap2430",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.dma_mask = &musb_dmamask,
|
||||
|
@ -91,8 +186,13 @@ void __init usb_musb_init(struct omap_musb_board_data *board_data)
|
|||
if (cpu_is_omap243x()) {
|
||||
musb_resources[0].start = OMAP243X_HS_BASE;
|
||||
} else if (cpu_is_omap3517() || cpu_is_omap3505()) {
|
||||
musb_device.name = "musb-am35x";
|
||||
musb_resources[0].start = AM35XX_IPSS_USBOTGSS_BASE;
|
||||
musb_resources[1].start = INT_35XX_USBOTG_IRQ;
|
||||
board_data->set_phy_power = am35x_musb_phy_power;
|
||||
board_data->clear_irq = am35x_musb_clear_irq;
|
||||
board_data->set_mode = am35x_musb_set_mode;
|
||||
board_data->reset = am35x_musb_reset;
|
||||
} else if (cpu_is_omap34xx()) {
|
||||
musb_resources[0].start = OMAP34XX_HSUSB_OTG_BASE;
|
||||
} else if (cpu_is_omap44xx()) {
|
||||
|
|
|
@ -224,7 +224,7 @@ static struct resource tusb_resources[] = {
|
|||
static u64 tusb_dmamask = ~(u32)0;
|
||||
|
||||
static struct platform_device tusb_device = {
|
||||
.name = "musb_hdrc",
|
||||
.name = "musb-tusb",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.dma_mask = &tusb_dmamask,
|
||||
|
|
|
@ -52,5 +52,10 @@
|
|||
#define OMAP4_MMU1_BASE 0x55082000
|
||||
#define OMAP4_MMU2_BASE 0x4A066000
|
||||
|
||||
#define OMAP44XX_USBTLL_BASE (L4_44XX_BASE + 0x62000)
|
||||
#define OMAP44XX_UHH_CONFIG_BASE (L4_44XX_BASE + 0x64000)
|
||||
#define OMAP44XX_HSUSB_OHCI_BASE (L4_44XX_BASE + 0x64800)
|
||||
#define OMAP44XX_HSUSB_EHCI_BASE (L4_44XX_BASE + 0x64C00)
|
||||
|
||||
#endif /* __ASM_ARCH_OMAP44XX_H */
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ enum ehci_hcd_omap_mode {
|
|||
EHCI_HCD_OMAP_MODE_UNKNOWN,
|
||||
EHCI_HCD_OMAP_MODE_PHY,
|
||||
EHCI_HCD_OMAP_MODE_TLL,
|
||||
EHCI_HCD_OMAP_MODE_HSIC,
|
||||
};
|
||||
|
||||
enum ohci_omap3_port_mode {
|
||||
|
@ -69,6 +70,10 @@ struct omap_musb_board_data {
|
|||
u8 mode;
|
||||
u16 power;
|
||||
unsigned extvbus:1;
|
||||
void (*set_phy_power)(u8 on);
|
||||
void (*clear_irq)(void);
|
||||
void (*set_mode)(u8 mode);
|
||||
void (*reset)(void);
|
||||
};
|
||||
|
||||
enum musb_interface {MUSB_INTERFACE_ULPI, MUSB_INTERFACE_UTMI};
|
||||
|
@ -79,6 +84,11 @@ extern void usb_ehci_init(const struct ehci_hcd_omap_platform_data *pdata);
|
|||
|
||||
extern void usb_ohci_init(const struct ohci_hcd_omap_platform_data *pdata);
|
||||
|
||||
extern int omap4430_phy_power(struct device *dev, int ID, int on);
|
||||
extern int omap4430_phy_set_clk(struct device *dev, int on);
|
||||
extern int omap4430_phy_init(struct device *dev);
|
||||
extern int omap4430_phy_exit(struct device *dev);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ static struct musb_hdrc_platform_data musb_plat = {
|
|||
static u64 musb_dmamask = ~(u32)0;
|
||||
|
||||
static struct platform_device musb_device = {
|
||||
.name = "musb_hdrc",
|
||||
.name = "musb-blackfin",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &musb_dmamask,
|
||||
|
|
|
@ -82,11 +82,13 @@ static struct resource musb_resources[] = {
|
|||
.start = IRQ_USB_INT0,
|
||||
.end = IRQ_USB_INT0,
|
||||
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
|
||||
.name = "mc"
|
||||
},
|
||||
[2] = { /* DMA IRQ */
|
||||
.start = IRQ_USB_DMA,
|
||||
.end = IRQ_USB_DMA,
|
||||
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
|
||||
.name = "dma"
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -118,7 +120,7 @@ static struct musb_hdrc_platform_data musb_plat = {
|
|||
static u64 musb_dmamask = ~(u32)0;
|
||||
|
||||
static struct platform_device musb_device = {
|
||||
.name = "musb_hdrc",
|
||||
.name = "musb-blackfin",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &musb_dmamask,
|
||||
|
|
|
@ -46,11 +46,13 @@ static struct resource musb_resources[] = {
|
|||
.start = IRQ_USB_INT0,
|
||||
.end = IRQ_USB_INT0,
|
||||
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
|
||||
.name = "mc"
|
||||
},
|
||||
[2] = { /* DMA IRQ */
|
||||
.start = IRQ_USB_DMA,
|
||||
.end = IRQ_USB_DMA,
|
||||
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
|
||||
.name = "dma"
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -82,7 +84,7 @@ static struct musb_hdrc_platform_data musb_plat = {
|
|||
static u64 musb_dmamask = ~(u32)0;
|
||||
|
||||
static struct platform_device musb_device = {
|
||||
.name = "musb_hdrc",
|
||||
.name = "musb-blackfin",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &musb_dmamask,
|
||||
|
|
|
@ -86,11 +86,13 @@ static struct resource musb_resources[] = {
|
|||
.start = IRQ_USB_INT0,
|
||||
.end = IRQ_USB_INT0,
|
||||
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
|
||||
.name = "mc"
|
||||
},
|
||||
[2] = { /* DMA IRQ */
|
||||
.start = IRQ_USB_DMA,
|
||||
.end = IRQ_USB_DMA,
|
||||
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
|
||||
.name = "dma"
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -122,7 +124,7 @@ static struct musb_hdrc_platform_data musb_plat = {
|
|||
static u64 musb_dmamask = ~(u32)0;
|
||||
|
||||
static struct platform_device musb_device = {
|
||||
.name = "musb_hdrc",
|
||||
.name = "musb-blackfin",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &musb_dmamask,
|
||||
|
|
|
@ -91,7 +91,7 @@ static struct musb_hdrc_platform_data musb_plat = {
|
|||
static u64 musb_dmamask = ~(u32)0;
|
||||
|
||||
static struct platform_device musb_device = {
|
||||
.name = "musb_hdrc",
|
||||
.name = "musb-blackfin",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &musb_dmamask,
|
||||
|
|
|
@ -482,11 +482,13 @@ static struct resource musb_resources[] = {
|
|||
.start = IRQ_USB_INT0,
|
||||
.end = IRQ_USB_INT0,
|
||||
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
|
||||
.name = "mc"
|
||||
},
|
||||
[2] = { /* DMA IRQ */
|
||||
.start = IRQ_USB_DMA,
|
||||
.end = IRQ_USB_DMA,
|
||||
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
|
||||
.name = "dma"
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -518,7 +520,7 @@ static struct musb_hdrc_platform_data musb_plat = {
|
|||
static u64 musb_dmamask = ~(u32)0;
|
||||
|
||||
static struct platform_device musb_device = {
|
||||
.name = "musb_hdrc",
|
||||
.name = "musb-blackfin",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &musb_dmamask,
|
||||
|
|
|
@ -587,11 +587,13 @@ static struct resource musb_resources[] = {
|
|||
.start = IRQ_USB_INT0,
|
||||
.end = IRQ_USB_INT0,
|
||||
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
|
||||
.name = "mc"
|
||||
},
|
||||
[2] = { /* DMA IRQ */
|
||||
.start = IRQ_USB_DMA,
|
||||
.end = IRQ_USB_DMA,
|
||||
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
|
||||
.name = "dma"
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -623,7 +625,7 @@ static struct musb_hdrc_platform_data musb_plat = {
|
|||
static u64 musb_dmamask = ~(u32)0;
|
||||
|
||||
static struct platform_device musb_device = {
|
||||
.name = "musb_hdrc",
|
||||
.name = "musb-blackfin",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.dma_mask = &musb_dmamask,
|
||||
|
|
|
@ -349,6 +349,7 @@ config CPU_SUBTYPE_SH7720
|
|||
select CPU_HAS_DSP
|
||||
select SYS_SUPPORTS_CMT
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select USB_ARCH_HAS_OHCI
|
||||
help
|
||||
Select SH7720 if you have a SH3-DSP SH7720 CPU.
|
||||
|
||||
|
@ -357,6 +358,7 @@ config CPU_SUBTYPE_SH7721
|
|||
select CPU_SH3
|
||||
select CPU_HAS_DSP
|
||||
select SYS_SUPPORTS_CMT
|
||||
select USB_ARCH_HAS_OHCI
|
||||
help
|
||||
Select SH7721 if you have a SH3-DSP SH7721 CPU.
|
||||
|
||||
|
@ -437,6 +439,7 @@ config CPU_SUBTYPE_SH7757
|
|||
config CPU_SUBTYPE_SH7763
|
||||
bool "Support SH7763 processor"
|
||||
select CPU_SH4A
|
||||
select USB_ARCH_HAS_OHCI
|
||||
help
|
||||
Select SH7763 if you have a SH4A SH7763(R5S77631) CPU.
|
||||
|
||||
|
@ -463,6 +466,8 @@ config CPU_SUBTYPE_SH7786
|
|||
select CPU_HAS_PTEAEX
|
||||
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select USB_ARCH_HAS_OHCI
|
||||
select USB_ARCH_HAS_EHCI
|
||||
|
||||
config CPU_SUBTYPE_SHX3
|
||||
bool "Support SH-X3 processor"
|
||||
|
|
|
@ -522,10 +522,37 @@ static struct platform_device dma0_device = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct resource usb_ohci_resources[] = {
|
||||
#define USB_EHCI_START 0xffe70000
|
||||
#define USB_OHCI_START 0xffe70400
|
||||
|
||||
static struct resource usb_ehci_resources[] = {
|
||||
[0] = {
|
||||
.start = 0xffe70400,
|
||||
.end = 0xffe704ff,
|
||||
.start = USB_EHCI_START,
|
||||
.end = USB_EHCI_START + 0x3ff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = 77,
|
||||
.end = 77,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device usb_ehci_device = {
|
||||
.name = "sh_ehci",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.dma_mask = &usb_ehci_device.dev.coherent_dma_mask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
},
|
||||
.num_resources = ARRAY_SIZE(usb_ehci_resources),
|
||||
.resource = usb_ehci_resources,
|
||||
};
|
||||
|
||||
static struct resource usb_ohci_resources[] = {
|
||||
[0] = {
|
||||
.start = USB_OHCI_START,
|
||||
.end = USB_OHCI_START + 0x3ff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
|
@ -535,12 +562,11 @@ static struct resource usb_ohci_resources[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static u64 usb_ohci_dma_mask = DMA_BIT_MASK(32);
|
||||
static struct platform_device usb_ohci_device = {
|
||||
.name = "sh_ohci",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.dma_mask = &usb_ohci_dma_mask,
|
||||
.dma_mask = &usb_ohci_device.dev.coherent_dma_mask,
|
||||
.coherent_dma_mask = DMA_BIT_MASK(32),
|
||||
},
|
||||
.num_resources = ARRAY_SIZE(usb_ohci_resources),
|
||||
|
@ -570,6 +596,7 @@ static struct platform_device *sh7786_early_devices[] __initdata = {
|
|||
|
||||
static struct platform_device *sh7786_devices[] __initdata = {
|
||||
&dma0_device,
|
||||
&usb_ehci_device,
|
||||
&usb_ohci_device,
|
||||
};
|
||||
|
||||
|
|
|
@ -1604,6 +1604,7 @@ static const struct hid_device_id hid_ignore_list[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) },
|
||||
|
|
|
@ -200,6 +200,8 @@
|
|||
#define USB_VENDOR_ID_ELECOM 0x056e
|
||||
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
|
||||
|
||||
#define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34
|
||||
|
||||
#define USB_VENDOR_ID_ELO 0x04E7
|
||||
#define USB_DEVICE_ID_ELO_TS2700 0x0020
|
||||
|
||||
|
|
|
@ -452,7 +452,8 @@ static int poseidon_probe(struct usb_interface *interface,
|
|||
|
||||
device_init_wakeup(&udev->dev, 1);
|
||||
#ifdef CONFIG_PM
|
||||
pd->udev->autosuspend_delay = HZ * PM_SUSPEND_DELAY;
|
||||
pm_runtime_set_autosuspend_delay(&pd->udev->dev,
|
||||
1000 * PM_SUSPEND_DELAY);
|
||||
usb_enable_autosuspend(pd->udev);
|
||||
|
||||
if (in_hibernation(pd)) {
|
||||
|
|
|
@ -95,7 +95,8 @@
|
|||
#define twl_has_rtc() false
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TWL4030_USB) || defined(CONFIG_TWL4030_USB_MODULE)
|
||||
#if defined(CONFIG_TWL4030_USB) || defined(CONFIG_TWL4030_USB_MODULE) ||\
|
||||
defined(CONFIG_TWL6030_USB) || defined(CONFIG_TWL6030_USB_MODULE)
|
||||
#define twl_has_usb() true
|
||||
#else
|
||||
#define twl_has_usb() false
|
||||
|
@ -682,6 +683,43 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
|
|||
usb3v1.dev = child;
|
||||
}
|
||||
}
|
||||
if (twl_has_usb() && pdata->usb && twl_class_is_6030()) {
|
||||
|
||||
static struct regulator_consumer_supply usb3v3 = {
|
||||
.supply = "vusb",
|
||||
};
|
||||
|
||||
if (twl_has_regulator()) {
|
||||
/* this is a template that gets copied */
|
||||
struct regulator_init_data usb_fixed = {
|
||||
.constraints.valid_modes_mask =
|
||||
REGULATOR_MODE_NORMAL
|
||||
| REGULATOR_MODE_STANDBY,
|
||||
.constraints.valid_ops_mask =
|
||||
REGULATOR_CHANGE_MODE
|
||||
| REGULATOR_CHANGE_STATUS,
|
||||
};
|
||||
|
||||
child = add_regulator_linked(TWL6030_REG_VUSB,
|
||||
&usb_fixed, &usb3v3, 1);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
}
|
||||
|
||||
child = add_child(0, "twl6030_usb",
|
||||
pdata->usb, sizeof(*pdata->usb),
|
||||
true,
|
||||
/* irq1 = VBUS_PRES, irq0 = USB ID */
|
||||
pdata->irq_base + USBOTG_INTR_OFFSET,
|
||||
pdata->irq_base + USB_PRES_INTR_OFFSET);
|
||||
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
/* we need to connect regulators to this transceiver */
|
||||
if (twl_has_regulator() && child)
|
||||
usb3v3.dev = child;
|
||||
|
||||
}
|
||||
|
||||
if (twl_has_watchdog()) {
|
||||
child = add_child(0, "twl4030_wdt", NULL, 0, false, 0, 0);
|
||||
|
@ -815,10 +853,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
|
|||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
||||
child = add_regulator(TWL6030_REG_VUSB, pdata->vusb);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
||||
child = add_regulator(TWL6030_REG_VAUX1_6030, pdata->vaux1);
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
|
|
@ -74,7 +74,7 @@ static int twl6030_interrupt_mapping[24] = {
|
|||
USBOTG_INTR_OFFSET, /* Bit 16 ID_WKUP */
|
||||
USBOTG_INTR_OFFSET, /* Bit 17 VBUS_WKUP */
|
||||
USBOTG_INTR_OFFSET, /* Bit 18 ID */
|
||||
USBOTG_INTR_OFFSET, /* Bit 19 VBUS */
|
||||
USB_PRES_INTR_OFFSET, /* Bit 19 VBUS */
|
||||
CHARGER_INTR_OFFSET, /* Bit 20 CHRG_CTRL */
|
||||
CHARGER_INTR_OFFSET, /* Bit 21 EXT_CHRG */
|
||||
CHARGER_INTR_OFFSET, /* Bit 22 INT_CHRG */
|
||||
|
@ -128,6 +128,13 @@ static int twl6030_irq_thread(void *data)
|
|||
|
||||
sts.bytes[3] = 0; /* Only 24 bits are valid*/
|
||||
|
||||
/*
|
||||
* Since VBUS status bit is not reliable for VBUS disconnect
|
||||
* use CHARGER VBUS detection status bit instead.
|
||||
*/
|
||||
if (sts.bytes[2] & 0x10)
|
||||
sts.bytes[2] |= 0x08;
|
||||
|
||||
for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
|
||||
local_irq_disable();
|
||||
if (sts.int_sts & 0x1) {
|
||||
|
|
|
@ -514,7 +514,7 @@ int i2400mu_probe(struct usb_interface *iface,
|
|||
#ifdef CONFIG_PM
|
||||
iface->needs_remote_wakeup = 1; /* autosuspend (15s delay) */
|
||||
device_init_wakeup(dev, 1);
|
||||
usb_dev->autosuspend_delay = 15 * HZ;
|
||||
pm_runtime_set_autosuspend_delay(&usb_dev->dev, 15000);
|
||||
usb_enable_autosuspend(usb_dev);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -277,7 +277,7 @@ usbbcm_device_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|||
if(psAdapter->bDoSuspend)
|
||||
{
|
||||
#ifdef CONFIG_PM
|
||||
udev->autosuspend_delay = 0;
|
||||
pm_runtime_set_autosuspend_delay(&udev->dev, 0);
|
||||
intf->needs_remote_wakeup = 1;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
|
||||
udev->autosuspend_disabled = 0;
|
||||
|
|
|
@ -42,17 +42,13 @@ config USB_ARCH_HAS_OHCI
|
|||
default y if ARCH_W90X900
|
||||
default y if ARCH_DAVINCI_DA8XX
|
||||
default y if ARCH_CNS3XXX
|
||||
default y if PLAT_SPEAR
|
||||
# PPC:
|
||||
default y if STB03xxx
|
||||
default y if PPC_MPC52xx
|
||||
# MIPS:
|
||||
default y if MIPS_ALCHEMY
|
||||
default y if MACH_JZ4740
|
||||
# SH:
|
||||
default y if CPU_SUBTYPE_SH7720
|
||||
default y if CPU_SUBTYPE_SH7721
|
||||
default y if CPU_SUBTYPE_SH7763
|
||||
default y if CPU_SUBTYPE_SH7786
|
||||
# more:
|
||||
default PCI
|
||||
|
||||
|
@ -68,6 +64,9 @@ config USB_ARCH_HAS_EHCI
|
|||
default y if ARCH_MXC
|
||||
default y if ARCH_OMAP3
|
||||
default y if ARCH_CNS3XXX
|
||||
default y if ARCH_VT8500
|
||||
default y if PLAT_SPEAR
|
||||
default y if ARCH_MSM
|
||||
default PCI
|
||||
|
||||
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <linux/usb.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
|
@ -1262,6 +1261,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
|
|||
udev->reset_resume);
|
||||
}
|
||||
}
|
||||
usb_mark_last_busy(udev);
|
||||
|
||||
done:
|
||||
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
|
||||
|
@ -1329,7 +1329,6 @@ int usb_resume(struct device *dev, pm_message_t msg)
|
|||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
udev->last_busy = jiffies;
|
||||
do_unbind_rebind(udev, DO_REBIND);
|
||||
}
|
||||
}
|
||||
|
@ -1397,33 +1396,8 @@ void usb_autosuspend_device(struct usb_device *udev)
|
|||
{
|
||||
int status;
|
||||
|
||||
udev->last_busy = jiffies;
|
||||
status = pm_runtime_put_sync(&udev->dev);
|
||||
dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n",
|
||||
__func__, atomic_read(&udev->dev.power.usage_count),
|
||||
status);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_try_autosuspend_device - attempt an autosuspend of a USB device and its interfaces
|
||||
* @udev: the usb_device to autosuspend
|
||||
*
|
||||
* This routine should be called when a core subsystem thinks @udev may
|
||||
* be ready to autosuspend.
|
||||
*
|
||||
* @udev's usage counter left unchanged. If it is 0 and all the interfaces
|
||||
* are inactive then an autosuspend will be attempted. The attempt may
|
||||
* fail or be delayed.
|
||||
*
|
||||
* The caller must hold @udev's device lock.
|
||||
*
|
||||
* This routine can run only in process context.
|
||||
*/
|
||||
void usb_try_autosuspend_device(struct usb_device *udev)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = pm_runtime_idle(&udev->dev);
|
||||
usb_mark_last_busy(udev);
|
||||
status = pm_runtime_put_sync_autosuspend(&udev->dev);
|
||||
dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n",
|
||||
__func__, atomic_read(&udev->dev.power.usage_count),
|
||||
status);
|
||||
|
@ -1482,7 +1456,7 @@ void usb_autopm_put_interface(struct usb_interface *intf)
|
|||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
int status;
|
||||
|
||||
udev->last_busy = jiffies;
|
||||
usb_mark_last_busy(udev);
|
||||
atomic_dec(&intf->pm_usage_cnt);
|
||||
status = pm_runtime_put_sync(&intf->dev);
|
||||
dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n",
|
||||
|
@ -1509,32 +1483,11 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
|
|||
void usb_autopm_put_interface_async(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
unsigned long last_busy;
|
||||
int status = 0;
|
||||
int status;
|
||||
|
||||
last_busy = udev->last_busy;
|
||||
udev->last_busy = jiffies;
|
||||
usb_mark_last_busy(udev);
|
||||
atomic_dec(&intf->pm_usage_cnt);
|
||||
pm_runtime_put_noidle(&intf->dev);
|
||||
|
||||
if (udev->dev.power.runtime_auto) {
|
||||
/* Optimization: Don't schedule a delayed autosuspend if
|
||||
* the timer is already running and the expiration time
|
||||
* wouldn't change.
|
||||
*
|
||||
* We have to use the interface's timer. Attempts to
|
||||
* schedule a suspend for the device would fail because
|
||||
* the interface is still active.
|
||||
*/
|
||||
if (intf->dev.power.timer_expires == 0 ||
|
||||
round_jiffies_up(last_busy) !=
|
||||
round_jiffies_up(jiffies)) {
|
||||
status = pm_schedule_suspend(&intf->dev,
|
||||
jiffies_to_msecs(
|
||||
round_jiffies_up_relative(
|
||||
udev->autosuspend_delay)));
|
||||
}
|
||||
}
|
||||
status = pm_runtime_put(&intf->dev);
|
||||
dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n",
|
||||
__func__, atomic_read(&intf->dev.power.usage_count),
|
||||
status);
|
||||
|
@ -1554,7 +1507,7 @@ void usb_autopm_put_interface_no_suspend(struct usb_interface *intf)
|
|||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
|
||||
udev->last_busy = jiffies;
|
||||
usb_mark_last_busy(udev);
|
||||
atomic_dec(&intf->pm_usage_cnt);
|
||||
pm_runtime_put_noidle(&intf->dev);
|
||||
}
|
||||
|
@ -1612,18 +1565,9 @@ EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
|
|||
*/
|
||||
int usb_autopm_get_interface_async(struct usb_interface *intf)
|
||||
{
|
||||
int status = 0;
|
||||
enum rpm_status s;
|
||||
|
||||
/* Don't request a resume unless the interface is already suspending
|
||||
* or suspended. Doing so would force a running suspend timer to be
|
||||
* cancelled.
|
||||
*/
|
||||
pm_runtime_get_noresume(&intf->dev);
|
||||
s = ACCESS_ONCE(intf->dev.power.runtime_status);
|
||||
if (s == RPM_SUSPENDING || s == RPM_SUSPENDED)
|
||||
status = pm_request_resume(&intf->dev);
|
||||
int status;
|
||||
|
||||
status = pm_runtime_get(&intf->dev);
|
||||
if (status < 0 && status != -EINPROGRESS)
|
||||
pm_runtime_put_noidle(&intf->dev);
|
||||
else
|
||||
|
@ -1650,7 +1594,7 @@ void usb_autopm_get_interface_no_resume(struct usb_interface *intf)
|
|||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
|
||||
udev->last_busy = jiffies;
|
||||
usb_mark_last_busy(udev);
|
||||
atomic_inc(&intf->pm_usage_cnt);
|
||||
pm_runtime_get_noresume(&intf->dev);
|
||||
}
|
||||
|
@ -1661,7 +1605,6 @@ static int autosuspend_check(struct usb_device *udev)
|
|||
{
|
||||
int w, i;
|
||||
struct usb_interface *intf;
|
||||
unsigned long suspend_time, j;
|
||||
|
||||
/* Fail if autosuspend is disabled, or any interfaces are in use, or
|
||||
* any interface drivers require remote wakeup but it isn't available.
|
||||
|
@ -1701,87 +1644,46 @@ static int autosuspend_check(struct usb_device *udev)
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
udev->do_remote_wakeup = w;
|
||||
|
||||
/* If everything is okay but the device hasn't been idle for long
|
||||
* enough, queue a delayed autosuspend request.
|
||||
*/
|
||||
j = ACCESS_ONCE(jiffies);
|
||||
suspend_time = udev->last_busy + udev->autosuspend_delay;
|
||||
if (time_before(j, suspend_time)) {
|
||||
pm_schedule_suspend(&udev->dev, jiffies_to_msecs(
|
||||
round_jiffies_up_relative(suspend_time - j)));
|
||||
return -EAGAIN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_runtime_suspend(struct device *dev)
|
||||
{
|
||||
int status = 0;
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int status;
|
||||
|
||||
/* A USB device can be suspended if it passes the various autosuspend
|
||||
* checks. Runtime suspend for a USB device means suspending all the
|
||||
* interfaces and then the device itself.
|
||||
*/
|
||||
if (is_usb_device(dev)) {
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
if (autosuspend_check(udev) != 0)
|
||||
return -EAGAIN;
|
||||
|
||||
status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);
|
||||
|
||||
/* If an interface fails the suspend, adjust the last_busy
|
||||
* time so that we don't get another suspend attempt right
|
||||
* away.
|
||||
*/
|
||||
if (status) {
|
||||
udev->last_busy = jiffies +
|
||||
(udev->autosuspend_delay == 0 ?
|
||||
HZ/2 : 0);
|
||||
}
|
||||
|
||||
/* Prevent the parent from suspending immediately after */
|
||||
else if (udev->parent)
|
||||
udev->parent->last_busy = jiffies;
|
||||
}
|
||||
|
||||
/* Runtime suspend for a USB interface doesn't mean anything. */
|
||||
return status;
|
||||
}
|
||||
|
||||
static int usb_runtime_resume(struct device *dev)
|
||||
{
|
||||
/* Runtime resume for a USB device means resuming both the device
|
||||
* and all its interfaces.
|
||||
*/
|
||||
if (is_usb_device(dev)) {
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int status;
|
||||
|
||||
/* Runtime resume for a USB device means resuming both the device
|
||||
* and all its interfaces.
|
||||
*/
|
||||
status = usb_resume_both(udev, PMSG_AUTO_RESUME);
|
||||
udev->last_busy = jiffies;
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Runtime resume for a USB interface doesn't mean anything. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_runtime_idle(struct device *dev)
|
||||
{
|
||||
/* An idle USB device can be suspended if it passes the various
|
||||
* autosuspend checks. An idle interface can be suspended at
|
||||
* any time.
|
||||
*/
|
||||
if (is_usb_device(dev)) {
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
if (autosuspend_check(udev) != 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
pm_runtime_suspend(dev);
|
||||
/* An idle USB device can be suspended if it passes the various
|
||||
* autosuspend checks.
|
||||
*/
|
||||
if (autosuspend_check(udev) == 0)
|
||||
pm_runtime_autosuspend(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include <asm/unaligned.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <linux/kthread.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
@ -1804,8 +1803,15 @@ int usb_new_device(struct usb_device *udev)
|
|||
|
||||
/* Tell the runtime-PM framework the device is active */
|
||||
pm_runtime_set_active(&udev->dev);
|
||||
pm_runtime_get_noresume(&udev->dev);
|
||||
pm_runtime_use_autosuspend(&udev->dev);
|
||||
pm_runtime_enable(&udev->dev);
|
||||
|
||||
/* By default, forbid autosuspend for all devices. It will be
|
||||
* allowed for hubs during binding.
|
||||
*/
|
||||
usb_disable_autosuspend(udev);
|
||||
|
||||
err = usb_enumerate_device(udev); /* Read descriptors */
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
@ -1831,6 +1837,8 @@ int usb_new_device(struct usb_device *udev)
|
|||
}
|
||||
|
||||
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
|
||||
usb_mark_last_busy(udev);
|
||||
pm_runtime_put_sync_autosuspend(&udev->dev);
|
||||
return err;
|
||||
|
||||
fail:
|
||||
|
@ -2221,6 +2229,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
|||
usb_set_device_state(udev, USB_STATE_SUSPENDED);
|
||||
msleep(10);
|
||||
}
|
||||
usb_mark_last_busy(hub->hdev);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
|
@ -1804,6 +1804,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
|
|||
INIT_WORK(&intf->reset_ws, __usb_queue_reset_device);
|
||||
intf->minor = -1;
|
||||
device_initialize(&intf->dev);
|
||||
pm_runtime_no_callbacks(&intf->dev);
|
||||
dev_set_name(&intf->dev, "%d-%s:%d.%d",
|
||||
dev->bus->busnum, dev->devpath,
|
||||
configuration, alt->desc.bInterfaceNumber);
|
||||
|
|
|
@ -117,21 +117,6 @@ void usb_detect_quirks(struct usb_device *udev)
|
|||
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
|
||||
udev->quirks);
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
/* By default, disable autosuspend for all devices. The hub driver
|
||||
* will enable it for hubs.
|
||||
*/
|
||||
usb_disable_autosuspend(udev);
|
||||
|
||||
/* Autosuspend can also be disabled if the initial autosuspend_delay
|
||||
* is negative.
|
||||
*/
|
||||
if (udev->autosuspend_delay < 0)
|
||||
usb_autoresume_device(udev);
|
||||
|
||||
#endif
|
||||
|
||||
/* For the present, all devices default to USB-PERSIST enabled */
|
||||
#if 0 /* was: #ifdef CONFIG_PM */
|
||||
/* Hubs are automatically enabled for USB-PERSIST */
|
||||
|
|
|
@ -233,8 +233,6 @@ static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL);
|
|||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static const char power_group[] = "power";
|
||||
|
||||
static ssize_t
|
||||
show_persist(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
@ -278,7 +276,7 @@ static int add_persist_attributes(struct device *dev)
|
|||
if (udev->descriptor.bDeviceClass != USB_CLASS_HUB)
|
||||
rc = sysfs_add_file_to_group(&dev->kobj,
|
||||
&dev_attr_persist.attr,
|
||||
power_group);
|
||||
power_group_name);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@ -287,7 +285,7 @@ static void remove_persist_attributes(struct device *dev)
|
|||
{
|
||||
sysfs_remove_file_from_group(&dev->kobj,
|
||||
&dev_attr_persist.attr,
|
||||
power_group);
|
||||
power_group_name);
|
||||
}
|
||||
#else
|
||||
|
||||
|
@ -336,44 +334,20 @@ static DEVICE_ATTR(active_duration, S_IRUGO, show_active_duration, NULL);
|
|||
static ssize_t
|
||||
show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", udev->autosuspend_delay / HZ);
|
||||
return sprintf(buf, "%d\n", dev->power.autosuspend_delay / 1000);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
set_autosuspend(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int value, old_delay;
|
||||
int rc;
|
||||
int value;
|
||||
|
||||
if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/HZ ||
|
||||
value <= - INT_MAX/HZ)
|
||||
if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/1000 ||
|
||||
value <= -INT_MAX/1000)
|
||||
return -EINVAL;
|
||||
value *= HZ;
|
||||
|
||||
usb_lock_device(udev);
|
||||
old_delay = udev->autosuspend_delay;
|
||||
udev->autosuspend_delay = value;
|
||||
|
||||
if (old_delay < 0) { /* Autosuspend wasn't allowed */
|
||||
if (value >= 0)
|
||||
usb_autosuspend_device(udev);
|
||||
} else { /* Autosuspend was allowed */
|
||||
if (value < 0) {
|
||||
rc = usb_autoresume_device(udev);
|
||||
if (rc < 0) {
|
||||
count = rc;
|
||||
udev->autosuspend_delay = old_delay;
|
||||
}
|
||||
} else {
|
||||
usb_try_autosuspend_device(udev);
|
||||
}
|
||||
}
|
||||
|
||||
usb_unlock_device(udev);
|
||||
pm_runtime_set_autosuspend_delay(dev, value * 1000);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -438,44 +412,30 @@ set_level(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
|
||||
|
||||
static struct attribute *power_attrs[] = {
|
||||
&dev_attr_autosuspend.attr,
|
||||
&dev_attr_level.attr,
|
||||
&dev_attr_connected_duration.attr,
|
||||
&dev_attr_active_duration.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group power_attr_group = {
|
||||
.name = power_group_name,
|
||||
.attrs = power_attrs,
|
||||
};
|
||||
|
||||
static int add_power_attributes(struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (is_usb_device(dev)) {
|
||||
rc = sysfs_add_file_to_group(&dev->kobj,
|
||||
&dev_attr_autosuspend.attr,
|
||||
power_group);
|
||||
if (rc == 0)
|
||||
rc = sysfs_add_file_to_group(&dev->kobj,
|
||||
&dev_attr_level.attr,
|
||||
power_group);
|
||||
if (rc == 0)
|
||||
rc = sysfs_add_file_to_group(&dev->kobj,
|
||||
&dev_attr_connected_duration.attr,
|
||||
power_group);
|
||||
if (rc == 0)
|
||||
rc = sysfs_add_file_to_group(&dev->kobj,
|
||||
&dev_attr_active_duration.attr,
|
||||
power_group);
|
||||
}
|
||||
if (is_usb_device(dev))
|
||||
rc = sysfs_merge_group(&dev->kobj, &power_attr_group);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void remove_power_attributes(struct device *dev)
|
||||
{
|
||||
sysfs_remove_file_from_group(&dev->kobj,
|
||||
&dev_attr_active_duration.attr,
|
||||
power_group);
|
||||
sysfs_remove_file_from_group(&dev->kobj,
|
||||
&dev_attr_connected_duration.attr,
|
||||
power_group);
|
||||
sysfs_remove_file_from_group(&dev->kobj,
|
||||
&dev_attr_level.attr,
|
||||
power_group);
|
||||
sysfs_remove_file_from_group(&dev->kobj,
|
||||
&dev_attr_autosuspend.attr,
|
||||
power_group);
|
||||
sysfs_unmerge_group(&dev->kobj, &power_attr_group);
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
|
@ -445,7 +445,8 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
|||
INIT_LIST_HEAD(&dev->filelist);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
dev->autosuspend_delay = usb_autosuspend_delay * HZ;
|
||||
pm_runtime_set_autosuspend_delay(&dev->dev,
|
||||
usb_autosuspend_delay * 1000);
|
||||
dev->connect_time = jiffies;
|
||||
dev->active_duration = -jiffies;
|
||||
#endif
|
||||
|
|
|
@ -75,14 +75,12 @@ static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
|||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
extern void usb_autosuspend_device(struct usb_device *udev);
|
||||
extern void usb_try_autosuspend_device(struct usb_device *udev);
|
||||
extern int usb_autoresume_device(struct usb_device *udev);
|
||||
extern int usb_remote_wakeup(struct usb_device *dev);
|
||||
|
||||
#else
|
||||
|
||||
#define usb_autosuspend_device(udev) do {} while (0)
|
||||
#define usb_try_autosuspend_device(udev) do {} while (0)
|
||||
static inline int usb_autoresume_device(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
|
|
|
@ -338,6 +338,19 @@ config USB_S3C2410_DEBUG
|
|||
boolean "S3C2410 udc debug messages"
|
||||
depends on USB_GADGET_S3C2410
|
||||
|
||||
config USB_GADGET_PXA_U2O
|
||||
boolean "PXA9xx Processor USB2.0 controller"
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
PXA9xx Processor series include a high speed USB2.0 device
|
||||
controller, which support high speed and full speed USB peripheral.
|
||||
|
||||
config USB_PXA_U2O
|
||||
tristate
|
||||
depends on USB_GADGET_PXA_U2O
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
#
|
||||
# Controllers available in both integrated and discrete versions
|
||||
#
|
||||
|
@ -414,8 +427,8 @@ config USB_FSL_QE
|
|||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_CI13XXX
|
||||
boolean "MIPS USB CI13xxx"
|
||||
config USB_GADGET_CI13XXX_PCI
|
||||
boolean "MIPS USB CI13xxx PCI UDC"
|
||||
depends on PCI
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
|
@ -426,9 +439,9 @@ config USB_GADGET_CI13XXX
|
|||
dynamically linked module called "ci13xxx_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_CI13XXX
|
||||
config USB_CI13XXX_PCI
|
||||
tristate
|
||||
depends on USB_GADGET_CI13XXX
|
||||
depends on USB_GADGET_CI13XXX_PCI
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
|
@ -495,6 +508,49 @@ config USB_LANGWELL
|
|||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_EG20T
|
||||
boolean "Intel EG20T(Topcliff) USB Device controller"
|
||||
depends on PCI
|
||||
select USB_GADGET_DUALSPEED
|
||||
help
|
||||
This is a USB device driver for EG20T PCH.
|
||||
EG20T PCH is the platform controller hub that is used in Intel's
|
||||
general embedded platform. EG20T PCH has USB device interface.
|
||||
Using this interface, it is able to access system devices connected
|
||||
to USB device.
|
||||
This driver enables USB device function.
|
||||
USB device is a USB peripheral controller which
|
||||
supports both full and high speed USB 2.0 data transfers.
|
||||
This driver supports both control transfer and bulk transfer modes.
|
||||
This driver dose not support interrupt transfer or isochronous
|
||||
transfer modes.
|
||||
|
||||
config USB_EG20T
|
||||
tristate
|
||||
depends on USB_GADGET_EG20T
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
config USB_GADGET_CI13XXX_MSM
|
||||
boolean "MIPS USB CI13xxx for MSM"
|
||||
depends on ARCH_MSM
|
||||
select USB_GADGET_DUALSPEED
|
||||
select USB_MSM_OTG_72K
|
||||
help
|
||||
MSM SoC has chipidea USB controller. This driver uses
|
||||
ci13xxx_udc core.
|
||||
This driver depends on OTG driver for PHY initialization,
|
||||
clock management, powering up VBUS, and power management.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "ci13xxx_msm" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_CI13XXX_MSM
|
||||
tristate
|
||||
depends on USB_GADGET_CI13XXX_MSM
|
||||
default USB_GADGET
|
||||
select USB_GADGET_SELECTED
|
||||
|
||||
#
|
||||
# LAST -- dummy/emulated controller
|
||||
|
@ -685,6 +741,19 @@ config USB_ETH_EEM
|
|||
If you say "y" here, the Ethernet gadget driver will use the EEM
|
||||
protocol rather than ECM. If unsure, say "n".
|
||||
|
||||
config USB_G_NCM
|
||||
tristate "Network Control Model (NCM) support"
|
||||
depends on NET
|
||||
select CRC32
|
||||
help
|
||||
This driver implements USB CDC NCM subclass standard. NCM is
|
||||
an advanced protocol for Ethernet encapsulation, allows grouping
|
||||
of several ethernet frames into one USB transfer and diffferent
|
||||
alignment possibilities.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "g_ncm".
|
||||
|
||||
config USB_GADGETFS
|
||||
tristate "Gadget Filesystem (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL
|
||||
|
|
|
@ -21,9 +21,13 @@ fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
|
|||
obj-$(CONFIG_USB_M66592) += m66592-udc.o
|
||||
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
|
||||
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
|
||||
obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o
|
||||
obj-$(CONFIG_USB_CI13XXX_PCI) += ci13xxx_pci.o
|
||||
obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o
|
||||
obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o
|
||||
obj-$(CONFIG_USB_EG20T) += pch_udc.o
|
||||
obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o
|
||||
mv_udc-y := mv_udc_core.o mv_udc_phy.o
|
||||
obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o
|
||||
|
||||
#
|
||||
# USB gadget drivers
|
||||
|
@ -43,6 +47,7 @@ g_hid-y := hid.o
|
|||
g_dbgp-y := dbgp.o
|
||||
g_nokia-y := nokia.o
|
||||
g_webcam-y := webcam.o
|
||||
g_ncm-y := ncm.o
|
||||
|
||||
obj-$(CONFIG_USB_ZERO) += g_zero.o
|
||||
obj-$(CONFIG_USB_AUDIO) += g_audio.o
|
||||
|
@ -60,3 +65,4 @@ obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
|
|||
obj-$(CONFIG_USB_G_MULTI) += g_multi.o
|
||||
obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
|
||||
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
|
||||
obj-$(CONFIG_USB_G_NCM) += g_ncm.o
|
||||
|
|
|
@ -3359,7 +3359,6 @@ static int udc_probe(struct udc *dev)
|
|||
dev_set_name(&dev->gadget.dev, "gadget");
|
||||
dev->gadget.dev.release = gadget_release;
|
||||
dev->gadget.name = name;
|
||||
dev->gadget.name = name;
|
||||
dev->gadget.is_dualspeed = 1;
|
||||
|
||||
/* init registers, interrupts, ... */
|
||||
|
|
|
@ -2057,8 +2057,10 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
|
|||
usba_ep_cleanup_debugfs(&usba_ep[i]);
|
||||
usba_cleanup_debugfs(udc);
|
||||
|
||||
if (gpio_is_valid(udc->vbus_pin))
|
||||
if (gpio_is_valid(udc->vbus_pin)) {
|
||||
free_irq(gpio_to_irq(udc->vbus_pin), udc);
|
||||
gpio_free(udc->vbus_pin);
|
||||
}
|
||||
|
||||
free_irq(udc->irq, udc);
|
||||
kfree(usba_ep);
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/usb/msm_hsusb_hw.h>
|
||||
#include <linux/usb/ulpi.h>
|
||||
|
||||
#include "ci13xxx_udc.c"
|
||||
|
||||
#define MSM_USB_BASE (udc->regs)
|
||||
|
||||
static irqreturn_t msm_udc_irq(int irq, void *data)
|
||||
{
|
||||
return udc_irq();
|
||||
}
|
||||
|
||||
static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
|
||||
{
|
||||
struct device *dev = udc->gadget.dev.parent;
|
||||
int val;
|
||||
|
||||
switch (event) {
|
||||
case CI13XXX_CONTROLLER_RESET_EVENT:
|
||||
dev_dbg(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
|
||||
writel(0, USB_AHBBURST);
|
||||
writel(0, USB_AHBMODE);
|
||||
break;
|
||||
case CI13XXX_CONTROLLER_STOPPED_EVENT:
|
||||
dev_dbg(dev, "CI13XXX_CONTROLLER_STOPPED_EVENT received\n");
|
||||
/*
|
||||
* Put the transceiver in non-driving mode. Otherwise host
|
||||
* may not detect soft-disconnection.
|
||||
*/
|
||||
val = otg_io_read(udc->transceiver, ULPI_FUNC_CTRL);
|
||||
val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
|
||||
val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
|
||||
otg_io_write(udc->transceiver, val, ULPI_FUNC_CTRL);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dev, "unknown ci13xxx_udc event\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
|
||||
.name = "ci13xxx_msm",
|
||||
.flags = CI13XXX_REGS_SHARED |
|
||||
CI13XXX_REQUIRE_TRANSCEIVER |
|
||||
CI13XXX_PULLUP_ON_VBUS |
|
||||
CI13XXX_DISABLE_STREAMING,
|
||||
|
||||
.notify_event = ci13xxx_msm_notify_event,
|
||||
};
|
||||
|
||||
static int ci13xxx_msm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to get platform resource mem\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
regs = ioremap(res->start, resource_size(res));
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "ioremap failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, regs);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "udc_probe failed\n");
|
||||
goto iounmap;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "IRQ not found\n");
|
||||
ret = -ENXIO;
|
||||
goto udc_remove;
|
||||
}
|
||||
|
||||
ret = request_irq(irq, msm_udc_irq, IRQF_SHARED, pdev->name, pdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "request_irq failed\n");
|
||||
goto udc_remove;
|
||||
}
|
||||
|
||||
pm_runtime_no_callbacks(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
udc_remove:
|
||||
udc_remove();
|
||||
iounmap:
|
||||
iounmap(regs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver ci13xxx_msm_driver = {
|
||||
.probe = ci13xxx_msm_probe,
|
||||
.driver = { .name = "msm_hsusb", },
|
||||
};
|
||||
|
||||
static int __init ci13xxx_msm_init(void)
|
||||
{
|
||||
return platform_driver_register(&ci13xxx_msm_driver);
|
||||
}
|
||||
module_init(ci13xxx_msm_init);
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* ci13xxx_pci.c - MIPS USB IP core family device controller
|
||||
*
|
||||
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
|
||||
*
|
||||
* Author: David Lopo
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "ci13xxx_udc.c"
|
||||
|
||||
/* driver name */
|
||||
#define UDC_DRIVER_NAME "ci13xxx_pci"
|
||||
|
||||
/******************************************************************************
|
||||
* PCI block
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* ci13xxx_pci_irq: interrut handler
|
||||
* @irq: irq number
|
||||
* @pdev: USB Device Controller interrupt source
|
||||
*
|
||||
* This function returns IRQ_HANDLED if the IRQ has been handled
|
||||
* This is an ISR don't trace, use attribute interface instead
|
||||
*/
|
||||
static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev)
|
||||
{
|
||||
if (irq == 0) {
|
||||
dev_err(&((struct pci_dev *)pdev)->dev, "Invalid IRQ0 usage!");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return udc_irq();
|
||||
}
|
||||
|
||||
static struct ci13xxx_udc_driver ci13xxx_pci_udc_driver = {
|
||||
.name = UDC_DRIVER_NAME,
|
||||
};
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_probe: PCI probe
|
||||
* @pdev: USB device controller being probed
|
||||
* @id: PCI hotplug ID connecting controller to UDC framework
|
||||
*
|
||||
* This function returns an error code
|
||||
* Allocates basic PCI resources for this USB device controller, and then
|
||||
* invokes the udc_probe() method to start the UDC associated with it
|
||||
*/
|
||||
static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
void __iomem *regs = NULL;
|
||||
int retval = 0;
|
||||
|
||||
if (id == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
retval = pci_enable_device(pdev);
|
||||
if (retval)
|
||||
goto done;
|
||||
|
||||
if (!pdev->irq) {
|
||||
dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!");
|
||||
retval = -ENODEV;
|
||||
goto disable_device;
|
||||
}
|
||||
|
||||
retval = pci_request_regions(pdev, UDC_DRIVER_NAME);
|
||||
if (retval)
|
||||
goto disable_device;
|
||||
|
||||
/* BAR 0 holds all the registers */
|
||||
regs = pci_iomap(pdev, 0, 0);
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "Error mapping memory!");
|
||||
retval = -EFAULT;
|
||||
goto release_regions;
|
||||
}
|
||||
pci_set_drvdata(pdev, (__force void *)regs);
|
||||
|
||||
pci_set_master(pdev);
|
||||
pci_try_set_mwi(pdev);
|
||||
|
||||
retval = udc_probe(&ci13xxx_pci_udc_driver, &pdev->dev, regs);
|
||||
if (retval)
|
||||
goto iounmap;
|
||||
|
||||
/* our device does not have MSI capability */
|
||||
|
||||
retval = request_irq(pdev->irq, ci13xxx_pci_irq, IRQF_SHARED,
|
||||
UDC_DRIVER_NAME, pdev);
|
||||
if (retval)
|
||||
goto gadget_remove;
|
||||
|
||||
return 0;
|
||||
|
||||
gadget_remove:
|
||||
udc_remove();
|
||||
iounmap:
|
||||
pci_iounmap(pdev, regs);
|
||||
release_regions:
|
||||
pci_release_regions(pdev);
|
||||
disable_device:
|
||||
pci_disable_device(pdev);
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_remove: PCI remove
|
||||
* @pdev: USB Device Controller being removed
|
||||
*
|
||||
* Reverses the effect of ci13xxx_pci_probe(),
|
||||
* first invoking the udc_remove() and then releases
|
||||
* all PCI resources allocated for this USB device controller
|
||||
*/
|
||||
static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
free_irq(pdev->irq, pdev);
|
||||
udc_remove();
|
||||
pci_iounmap(pdev, (__force void __iomem *)pci_get_drvdata(pdev));
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* PCI device table
|
||||
* PCI device structure
|
||||
*
|
||||
* Check "pci.h" for details
|
||||
*/
|
||||
static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = {
|
||||
{ PCI_DEVICE(0x153F, 0x1004) },
|
||||
{ PCI_DEVICE(0x153F, 0x1006) },
|
||||
{ 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ci13xxx_pci_id_table);
|
||||
|
||||
static struct pci_driver ci13xxx_pci_driver = {
|
||||
.name = UDC_DRIVER_NAME,
|
||||
.id_table = ci13xxx_pci_id_table,
|
||||
.probe = ci13xxx_pci_probe,
|
||||
.remove = __devexit_p(ci13xxx_pci_remove),
|
||||
};
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_init: module init
|
||||
*
|
||||
* Driver load
|
||||
*/
|
||||
static int __init ci13xxx_pci_init(void)
|
||||
{
|
||||
return pci_register_driver(&ci13xxx_pci_driver);
|
||||
}
|
||||
module_init(ci13xxx_pci_init);
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_exit: module exit
|
||||
*
|
||||
* Driver unload
|
||||
*/
|
||||
static void __exit ci13xxx_pci_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&ci13xxx_pci_driver);
|
||||
}
|
||||
module_exit(ci13xxx_pci_exit);
|
||||
|
||||
MODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>");
|
||||
MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("June 2008");
|
|
@ -22,7 +22,6 @@
|
|||
* - ENDPT: endpoint operations (Gadget API)
|
||||
* - GADGET: gadget operations (Gadget API)
|
||||
* - BUS: bus glue code, bus abstraction layer
|
||||
* - PCI: PCI core interface and PCI resources (interrupts, memory...)
|
||||
*
|
||||
* Compile Options
|
||||
* - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities
|
||||
|
@ -60,11 +59,11 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
#include "ci13xxx_udc.h"
|
||||
|
||||
|
@ -75,9 +74,6 @@
|
|||
/* ctrl register bank access */
|
||||
static DEFINE_SPINLOCK(udc_lock);
|
||||
|
||||
/* driver name */
|
||||
#define UDC_DRIVER_NAME "ci13xxx_udc"
|
||||
|
||||
/* control endpoint description */
|
||||
static const struct usb_endpoint_descriptor
|
||||
ctrl_endpt_desc = {
|
||||
|
@ -132,6 +128,9 @@ static struct {
|
|||
size_t size; /* bank size */
|
||||
} hw_bank;
|
||||
|
||||
/* MSM specific */
|
||||
#define ABS_AHBBURST (0x0090UL)
|
||||
#define ABS_AHBMODE (0x0098UL)
|
||||
/* UDC register map */
|
||||
#define ABS_CAPLENGTH (0x100UL)
|
||||
#define ABS_HCCPARAMS (0x108UL)
|
||||
|
@ -248,13 +247,7 @@ static u32 hw_ctest_and_write(u32 addr, u32 mask, u32 data)
|
|||
return (reg & mask) >> ffs_nr(mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_device_reset: resets chip (execute without interruption)
|
||||
* @base: register base address
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
static int hw_device_reset(void __iomem *base)
|
||||
static int hw_device_init(void __iomem *base)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
|
@ -271,25 +264,6 @@ static int hw_device_reset(void __iomem *base)
|
|||
hw_bank.size += CAP_LAST;
|
||||
hw_bank.size /= sizeof(u32);
|
||||
|
||||
/* should flush & stop before reset */
|
||||
hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0);
|
||||
hw_cwrite(CAP_USBCMD, USBCMD_RS, 0);
|
||||
|
||||
hw_cwrite(CAP_USBCMD, USBCMD_RST, USBCMD_RST);
|
||||
while (hw_cread(CAP_USBCMD, USBCMD_RST))
|
||||
udelay(10); /* not RTOS friendly */
|
||||
|
||||
/* USBMODE should be configured step by step */
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE);
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); /* HW >= 2.3 */
|
||||
|
||||
if (hw_cread(CAP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) {
|
||||
pr_err("cannot enter in device mode");
|
||||
pr_err("lpm = %i", hw_bank.lpm);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN);
|
||||
if (reg == 0 || reg > ENDPT_MAX)
|
||||
return -ENODEV;
|
||||
|
@ -304,6 +278,43 @@ static int hw_device_reset(void __iomem *base)
|
|||
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* hw_device_reset: resets chip (execute without interruption)
|
||||
* @base: register base address
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
static int hw_device_reset(struct ci13xxx *udc)
|
||||
{
|
||||
/* should flush & stop before reset */
|
||||
hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0);
|
||||
hw_cwrite(CAP_USBCMD, USBCMD_RS, 0);
|
||||
|
||||
hw_cwrite(CAP_USBCMD, USBCMD_RST, USBCMD_RST);
|
||||
while (hw_cread(CAP_USBCMD, USBCMD_RST))
|
||||
udelay(10); /* not RTOS friendly */
|
||||
|
||||
|
||||
if (udc->udc_driver->notify_event)
|
||||
udc->udc_driver->notify_event(udc,
|
||||
CI13XXX_CONTROLLER_RESET_EVENT);
|
||||
|
||||
if (udc->udc_driver->flags && CI13XXX_DISABLE_STREAMING)
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_SDIS, USBMODE_SDIS);
|
||||
|
||||
/* USBMODE should be configured step by step */
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE);
|
||||
hw_cwrite(CAP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); /* HW >= 2.3 */
|
||||
|
||||
if (hw_cread(CAP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) {
|
||||
pr_err("cannot enter in device mode");
|
||||
pr_err("lpm = %i", hw_bank.lpm);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_device_state: enables/disables interrupts & starts/stops device (execute
|
||||
|
@ -1449,7 +1460,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
|
|||
mReq->ptr->page[0] = mReq->req.dma;
|
||||
for (i = 1; i < 5; i++)
|
||||
mReq->ptr->page[i] =
|
||||
(mReq->req.dma + i * PAGE_SIZE) & ~TD_RESERVED_MASK;
|
||||
(mReq->req.dma + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK;
|
||||
|
||||
/*
|
||||
* QH configuration
|
||||
|
@ -1540,7 +1551,7 @@ __acquires(mEp->lock)
|
|||
list_del_init(&mReq->queue);
|
||||
mReq->req.status = -ESHUTDOWN;
|
||||
|
||||
if (!mReq->req.no_interrupt && mReq->req.complete != NULL) {
|
||||
if (mReq->req.complete != NULL) {
|
||||
spin_unlock(mEp->lock);
|
||||
mReq->req.complete(&mEp->ep, &mReq->req);
|
||||
spin_lock(mEp->lock);
|
||||
|
@ -1557,8 +1568,6 @@ __acquires(mEp->lock)
|
|||
* Caller must hold lock
|
||||
*/
|
||||
static int _gadget_stop_activity(struct usb_gadget *gadget)
|
||||
__releases(udc->lock)
|
||||
__acquires(udc->lock)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget);
|
||||
|
@ -1570,8 +1579,6 @@ __acquires(udc->lock)
|
|||
if (gadget == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
spin_unlock(udc->lock);
|
||||
|
||||
/* flush all endpoints */
|
||||
gadget_for_each_ep(ep, gadget) {
|
||||
usb_ep_fifo_flush(ep);
|
||||
|
@ -1591,8 +1598,6 @@ __acquires(udc->lock)
|
|||
mEp->status = NULL;
|
||||
}
|
||||
|
||||
spin_lock(udc->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1621,6 +1626,7 @@ __acquires(udc->lock)
|
|||
|
||||
dbg_event(0xFF, "BUS RST", 0);
|
||||
|
||||
spin_unlock(udc->lock);
|
||||
retval = _gadget_stop_activity(&udc->gadget);
|
||||
if (retval)
|
||||
goto done;
|
||||
|
@ -1629,10 +1635,9 @@ __acquires(udc->lock)
|
|||
if (retval)
|
||||
goto done;
|
||||
|
||||
spin_unlock(udc->lock);
|
||||
retval = usb_ep_enable(&mEp->ep, &ctrl_endpt_desc);
|
||||
if (!retval) {
|
||||
mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_KERNEL);
|
||||
mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_ATOMIC);
|
||||
if (mEp->status == NULL) {
|
||||
usb_ep_disable(&mEp->ep);
|
||||
retval = -ENOMEM;
|
||||
|
@ -1789,18 +1794,20 @@ __acquires(mEp->lock)
|
|||
|
||||
dbg_done(_usb_addr(mEp), mReq->ptr->token, retval);
|
||||
|
||||
if (!mReq->req.no_interrupt && mReq->req.complete != NULL) {
|
||||
if (!list_empty(&mEp->qh[mEp->dir].queue)) {
|
||||
struct ci13xxx_req* mReqEnq;
|
||||
|
||||
mReqEnq = list_entry(mEp->qh[mEp->dir].queue.next,
|
||||
struct ci13xxx_req, queue);
|
||||
_hardware_enqueue(mEp, mReqEnq);
|
||||
}
|
||||
|
||||
if (mReq->req.complete != NULL) {
|
||||
spin_unlock(mEp->lock);
|
||||
mReq->req.complete(&mEp->ep, &mReq->req);
|
||||
spin_lock(mEp->lock);
|
||||
}
|
||||
|
||||
if (!list_empty(&mEp->qh[mEp->dir].queue)) {
|
||||
mReq = list_entry(mEp->qh[mEp->dir].queue.next,
|
||||
struct ci13xxx_req, queue);
|
||||
_hardware_enqueue(mEp, mReq);
|
||||
}
|
||||
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
@ -2061,7 +2068,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
|
|||
{
|
||||
struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
|
||||
struct ci13xxx_req *mReq = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
trace("%p, %i", ep, gfp_flags);
|
||||
|
||||
|
@ -2070,8 +2076,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(mEp->lock, flags);
|
||||
|
||||
mReq = kzalloc(sizeof(struct ci13xxx_req), gfp_flags);
|
||||
if (mReq != NULL) {
|
||||
INIT_LIST_HEAD(&mReq->queue);
|
||||
|
@ -2086,8 +2090,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
|
|||
|
||||
dbg_event(_usb_addr(mEp), "ALLOC", mReq == NULL);
|
||||
|
||||
spin_unlock_irqrestore(mEp->lock, flags);
|
||||
|
||||
return (mReq == NULL) ? NULL : &mReq->req;
|
||||
}
|
||||
|
||||
|
@ -2157,8 +2159,8 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req,
|
|||
goto done;
|
||||
}
|
||||
|
||||
if (req->length > (4 * PAGE_SIZE)) {
|
||||
req->length = (4 * PAGE_SIZE);
|
||||
if (req->length > (4 * CI13XXX_PAGE_SIZE)) {
|
||||
req->length = (4 * CI13XXX_PAGE_SIZE);
|
||||
retval = -EMSGSIZE;
|
||||
warn("request length truncated");
|
||||
}
|
||||
|
@ -2170,8 +2172,10 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req,
|
|||
mReq->req.actual = 0;
|
||||
list_add_tail(&mReq->queue, &mEp->qh[mEp->dir].queue);
|
||||
|
||||
if (list_is_singular(&mEp->qh[mEp->dir].queue))
|
||||
retval = _hardware_enqueue(mEp, mReq);
|
||||
if (retval == -EALREADY || retval == -EBUSY) {
|
||||
|
||||
if (retval == -EALREADY) {
|
||||
dbg_event(_usb_addr(mEp), "QUEUE", retval);
|
||||
retval = 0;
|
||||
}
|
||||
|
@ -2209,7 +2213,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
|
|||
list_del_init(&mReq->queue);
|
||||
req->status = -ECONNRESET;
|
||||
|
||||
if (!mReq->req.no_interrupt && mReq->req.complete != NULL) {
|
||||
if (mReq->req.complete != NULL) {
|
||||
spin_unlock(mEp->lock);
|
||||
mReq->req.complete(&mEp->ep, &mReq->req);
|
||||
spin_lock(mEp->lock);
|
||||
|
@ -2332,12 +2336,47 @@ static const struct usb_ep_ops usb_ep_ops = {
|
|||
/******************************************************************************
|
||||
* GADGET block
|
||||
*****************************************************************************/
|
||||
static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
|
||||
{
|
||||
struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
|
||||
unsigned long flags;
|
||||
int gadget_ready = 0;
|
||||
|
||||
if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
spin_lock_irqsave(udc->lock, flags);
|
||||
udc->vbus_active = is_active;
|
||||
if (udc->driver)
|
||||
gadget_ready = 1;
|
||||
spin_unlock_irqrestore(udc->lock, flags);
|
||||
|
||||
if (gadget_ready) {
|
||||
if (is_active) {
|
||||
pm_runtime_get_sync(&_gadget->dev);
|
||||
hw_device_reset(udc);
|
||||
hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma);
|
||||
} else {
|
||||
hw_device_state(0);
|
||||
if (udc->udc_driver->notify_event)
|
||||
udc->udc_driver->notify_event(udc,
|
||||
CI13XXX_CONTROLLER_STOPPED_EVENT);
|
||||
_gadget_stop_activity(&udc->gadget);
|
||||
pm_runtime_put_sync(&_gadget->dev);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Device operations part of the API to the USB controller hardware,
|
||||
* which don't involve endpoints (or i/o)
|
||||
* Check "usb_gadget.h" for details
|
||||
*/
|
||||
static const struct usb_gadget_ops usb_gadget_ops;
|
||||
static const struct usb_gadget_ops usb_gadget_ops = {
|
||||
.vbus_session = ci13xxx_vbus_session,
|
||||
};
|
||||
|
||||
/**
|
||||
* usb_gadget_probe_driver: register a gadget driver
|
||||
|
@ -2358,7 +2397,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
|
|||
|
||||
if (driver == NULL ||
|
||||
bind == NULL ||
|
||||
driver->unbind == NULL ||
|
||||
driver->setup == NULL ||
|
||||
driver->disconnect == NULL ||
|
||||
driver->suspend == NULL ||
|
||||
|
@ -2372,13 +2410,13 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
|
|||
/* alloc resources */
|
||||
udc->qh_pool = dma_pool_create("ci13xxx_qh", &udc->gadget.dev,
|
||||
sizeof(struct ci13xxx_qh),
|
||||
64, PAGE_SIZE);
|
||||
64, CI13XXX_PAGE_SIZE);
|
||||
if (udc->qh_pool == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
udc->td_pool = dma_pool_create("ci13xxx_td", &udc->gadget.dev,
|
||||
sizeof(struct ci13xxx_td),
|
||||
64, PAGE_SIZE);
|
||||
64, CI13XXX_PAGE_SIZE);
|
||||
if (udc->td_pool == NULL) {
|
||||
dma_pool_destroy(udc->qh_pool);
|
||||
udc->qh_pool = NULL;
|
||||
|
@ -2390,7 +2428,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
|
|||
info("hw_ep_max = %d", hw_ep_max);
|
||||
|
||||
udc->driver = driver;
|
||||
udc->gadget.ops = NULL;
|
||||
udc->gadget.dev.driver = NULL;
|
||||
|
||||
retval = 0;
|
||||
|
@ -2410,9 +2447,11 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
|
|||
/* this allocation cannot be random */
|
||||
for (k = RX; k <= TX; k++) {
|
||||
INIT_LIST_HEAD(&mEp->qh[k].queue);
|
||||
spin_unlock_irqrestore(udc->lock, flags);
|
||||
mEp->qh[k].ptr = dma_pool_alloc(udc->qh_pool,
|
||||
GFP_KERNEL,
|
||||
&mEp->qh[k].dma);
|
||||
spin_lock_irqsave(udc->lock, flags);
|
||||
if (mEp->qh[k].ptr == NULL)
|
||||
retval = -ENOMEM;
|
||||
else
|
||||
|
@ -2429,7 +2468,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
|
|||
|
||||
/* bind gadget */
|
||||
driver->driver.bus = NULL;
|
||||
udc->gadget.ops = &usb_gadget_ops;
|
||||
udc->gadget.dev.driver = &driver->driver;
|
||||
|
||||
spin_unlock_irqrestore(udc->lock, flags);
|
||||
|
@ -2437,12 +2475,24 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
|
|||
spin_lock_irqsave(udc->lock, flags);
|
||||
|
||||
if (retval) {
|
||||
udc->gadget.ops = NULL;
|
||||
udc->gadget.dev.driver = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(&udc->gadget.dev);
|
||||
if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) {
|
||||
if (udc->vbus_active) {
|
||||
if (udc->udc_driver->flags & CI13XXX_REGS_SHARED)
|
||||
hw_device_reset(udc);
|
||||
} else {
|
||||
pm_runtime_put_sync(&udc->gadget.dev);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
retval = hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma);
|
||||
if (retval)
|
||||
pm_runtime_put_sync(&udc->gadget.dev);
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(udc->lock, flags);
|
||||
|
@ -2475,19 +2525,22 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
|
|||
|
||||
spin_lock_irqsave(udc->lock, flags);
|
||||
|
||||
if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) ||
|
||||
udc->vbus_active) {
|
||||
hw_device_state(0);
|
||||
if (udc->udc_driver->notify_event)
|
||||
udc->udc_driver->notify_event(udc,
|
||||
CI13XXX_CONTROLLER_STOPPED_EVENT);
|
||||
_gadget_stop_activity(&udc->gadget);
|
||||
pm_runtime_put(&udc->gadget.dev);
|
||||
}
|
||||
|
||||
/* unbind gadget */
|
||||
if (udc->gadget.ops != NULL) {
|
||||
_gadget_stop_activity(&udc->gadget);
|
||||
|
||||
spin_unlock_irqrestore(udc->lock, flags);
|
||||
driver->unbind(&udc->gadget); /* MAY SLEEP */
|
||||
spin_lock_irqsave(udc->lock, flags);
|
||||
|
||||
udc->gadget.ops = NULL;
|
||||
udc->gadget.dev.driver = NULL;
|
||||
}
|
||||
|
||||
/* free resources */
|
||||
for (i = 0; i < hw_ep_max; i++) {
|
||||
|
@ -2544,6 +2597,14 @@ static irqreturn_t udc_irq(void)
|
|||
}
|
||||
|
||||
spin_lock(udc->lock);
|
||||
|
||||
if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) {
|
||||
if (hw_cread(CAP_USBMODE, USBMODE_CM) !=
|
||||
USBMODE_CM_DEVICE) {
|
||||
spin_unlock(udc->lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
}
|
||||
intr = hw_test_and_clear_intr_active();
|
||||
if (intr) {
|
||||
isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intr;
|
||||
|
@ -2602,14 +2663,16 @@ static void udc_release(struct device *dev)
|
|||
* No interrupts active, the IRQ has not been requested yet
|
||||
* Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask
|
||||
*/
|
||||
static int udc_probe(struct device *dev, void __iomem *regs, const char *name)
|
||||
static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
|
||||
void __iomem *regs)
|
||||
{
|
||||
struct ci13xxx *udc;
|
||||
int retval = 0;
|
||||
|
||||
trace("%p, %p, %p", dev, regs, name);
|
||||
|
||||
if (dev == NULL || regs == NULL || name == NULL)
|
||||
if (dev == NULL || regs == NULL || driver == NULL ||
|
||||
driver->name == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL);
|
||||
|
@ -2617,42 +2680,77 @@ static int udc_probe(struct device *dev, void __iomem *regs, const char *name)
|
|||
return -ENOMEM;
|
||||
|
||||
udc->lock = &udc_lock;
|
||||
udc->regs = regs;
|
||||
udc->udc_driver = driver;
|
||||
|
||||
retval = hw_device_reset(regs);
|
||||
if (retval)
|
||||
goto done;
|
||||
|
||||
udc->gadget.ops = NULL;
|
||||
udc->gadget.ops = &usb_gadget_ops;
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
udc->gadget.is_dualspeed = 1;
|
||||
udc->gadget.is_otg = 0;
|
||||
udc->gadget.name = name;
|
||||
udc->gadget.name = driver->name;
|
||||
|
||||
INIT_LIST_HEAD(&udc->gadget.ep_list);
|
||||
udc->gadget.ep0 = NULL;
|
||||
|
||||
dev_set_name(&udc->gadget.dev, "gadget");
|
||||
udc->gadget.dev.dma_mask = dev->dma_mask;
|
||||
udc->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask;
|
||||
udc->gadget.dev.parent = dev;
|
||||
udc->gadget.dev.release = udc_release;
|
||||
|
||||
retval = device_register(&udc->gadget.dev);
|
||||
retval = hw_device_init(regs);
|
||||
if (retval < 0)
|
||||
goto free_udc;
|
||||
|
||||
udc->transceiver = otg_get_transceiver();
|
||||
|
||||
if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
|
||||
if (udc->transceiver == NULL) {
|
||||
retval = -ENODEV;
|
||||
goto free_udc;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
|
||||
retval = hw_device_reset(udc);
|
||||
if (retval)
|
||||
goto done;
|
||||
goto put_transceiver;
|
||||
}
|
||||
|
||||
retval = device_register(&udc->gadget.dev);
|
||||
if (retval) {
|
||||
put_device(&udc->gadget.dev);
|
||||
goto put_transceiver;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
retval = dbg_create_files(&udc->gadget.dev);
|
||||
#endif
|
||||
if (retval) {
|
||||
device_unregister(&udc->gadget.dev);
|
||||
goto done;
|
||||
if (retval)
|
||||
goto unreg_device;
|
||||
|
||||
if (udc->transceiver) {
|
||||
retval = otg_set_peripheral(udc->transceiver, &udc->gadget);
|
||||
if (retval)
|
||||
goto remove_dbg;
|
||||
}
|
||||
pm_runtime_no_callbacks(&udc->gadget.dev);
|
||||
pm_runtime_enable(&udc->gadget.dev);
|
||||
|
||||
_udc = udc;
|
||||
return retval;
|
||||
|
||||
done:
|
||||
err("error = %i", retval);
|
||||
remove_dbg:
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
dbg_remove_files(&udc->gadget.dev);
|
||||
#endif
|
||||
unreg_device:
|
||||
device_unregister(&udc->gadget.dev);
|
||||
put_transceiver:
|
||||
if (udc->transceiver)
|
||||
otg_put_transceiver(udc->transceiver);
|
||||
free_udc:
|
||||
kfree(udc);
|
||||
_udc = NULL;
|
||||
return retval;
|
||||
|
@ -2672,6 +2770,10 @@ static void udc_remove(void)
|
|||
return;
|
||||
}
|
||||
|
||||
if (udc->transceiver) {
|
||||
otg_set_peripheral(udc->transceiver, &udc->gadget);
|
||||
otg_put_transceiver(udc->transceiver);
|
||||
}
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
dbg_remove_files(&udc->gadget.dev);
|
||||
#endif
|
||||
|
@ -2680,156 +2782,3 @@ static void udc_remove(void)
|
|||
kfree(udc);
|
||||
_udc = NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* PCI block
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* ci13xxx_pci_irq: interrut handler
|
||||
* @irq: irq number
|
||||
* @pdev: USB Device Controller interrupt source
|
||||
*
|
||||
* This function returns IRQ_HANDLED if the IRQ has been handled
|
||||
* This is an ISR don't trace, use attribute interface instead
|
||||
*/
|
||||
static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev)
|
||||
{
|
||||
if (irq == 0) {
|
||||
dev_err(&((struct pci_dev *)pdev)->dev, "Invalid IRQ0 usage!");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return udc_irq();
|
||||
}
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_probe: PCI probe
|
||||
* @pdev: USB device controller being probed
|
||||
* @id: PCI hotplug ID connecting controller to UDC framework
|
||||
*
|
||||
* This function returns an error code
|
||||
* Allocates basic PCI resources for this USB device controller, and then
|
||||
* invokes the udc_probe() method to start the UDC associated with it
|
||||
*/
|
||||
static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
void __iomem *regs = NULL;
|
||||
int retval = 0;
|
||||
|
||||
if (id == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
retval = pci_enable_device(pdev);
|
||||
if (retval)
|
||||
goto done;
|
||||
|
||||
if (!pdev->irq) {
|
||||
dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!");
|
||||
retval = -ENODEV;
|
||||
goto disable_device;
|
||||
}
|
||||
|
||||
retval = pci_request_regions(pdev, UDC_DRIVER_NAME);
|
||||
if (retval)
|
||||
goto disable_device;
|
||||
|
||||
/* BAR 0 holds all the registers */
|
||||
regs = pci_iomap(pdev, 0, 0);
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "Error mapping memory!");
|
||||
retval = -EFAULT;
|
||||
goto release_regions;
|
||||
}
|
||||
pci_set_drvdata(pdev, (__force void *)regs);
|
||||
|
||||
pci_set_master(pdev);
|
||||
pci_try_set_mwi(pdev);
|
||||
|
||||
retval = udc_probe(&pdev->dev, regs, UDC_DRIVER_NAME);
|
||||
if (retval)
|
||||
goto iounmap;
|
||||
|
||||
/* our device does not have MSI capability */
|
||||
|
||||
retval = request_irq(pdev->irq, ci13xxx_pci_irq, IRQF_SHARED,
|
||||
UDC_DRIVER_NAME, pdev);
|
||||
if (retval)
|
||||
goto gadget_remove;
|
||||
|
||||
return 0;
|
||||
|
||||
gadget_remove:
|
||||
udc_remove();
|
||||
iounmap:
|
||||
pci_iounmap(pdev, regs);
|
||||
release_regions:
|
||||
pci_release_regions(pdev);
|
||||
disable_device:
|
||||
pci_disable_device(pdev);
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_remove: PCI remove
|
||||
* @pdev: USB Device Controller being removed
|
||||
*
|
||||
* Reverses the effect of ci13xxx_pci_probe(),
|
||||
* first invoking the udc_remove() and then releases
|
||||
* all PCI resources allocated for this USB device controller
|
||||
*/
|
||||
static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
free_irq(pdev->irq, pdev);
|
||||
udc_remove();
|
||||
pci_iounmap(pdev, (__force void __iomem *)pci_get_drvdata(pdev));
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* PCI device table
|
||||
* PCI device structure
|
||||
*
|
||||
* Check "pci.h" for details
|
||||
*/
|
||||
static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = {
|
||||
{ PCI_DEVICE(0x153F, 0x1004) },
|
||||
{ PCI_DEVICE(0x153F, 0x1006) },
|
||||
{ 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ci13xxx_pci_id_table);
|
||||
|
||||
static struct pci_driver ci13xxx_pci_driver = {
|
||||
.name = UDC_DRIVER_NAME,
|
||||
.id_table = ci13xxx_pci_id_table,
|
||||
.probe = ci13xxx_pci_probe,
|
||||
.remove = __devexit_p(ci13xxx_pci_remove),
|
||||
};
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_init: module init
|
||||
*
|
||||
* Driver load
|
||||
*/
|
||||
static int __init ci13xxx_pci_init(void)
|
||||
{
|
||||
return pci_register_driver(&ci13xxx_pci_driver);
|
||||
}
|
||||
module_init(ci13xxx_pci_init);
|
||||
|
||||
/**
|
||||
* ci13xxx_pci_exit: module exit
|
||||
*
|
||||
* Driver unload
|
||||
*/
|
||||
static void __exit ci13xxx_pci_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&ci13xxx_pci_driver);
|
||||
}
|
||||
module_exit(ci13xxx_pci_exit);
|
||||
|
||||
MODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>");
|
||||
MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("June 2008");
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
/******************************************************************************
|
||||
* DEFINE
|
||||
*****************************************************************************/
|
||||
#define CI13XXX_PAGE_SIZE 4096ul /* page size for TD's */
|
||||
#define ENDPT_MAX (16)
|
||||
#define CTRL_PAYLOAD_MAX (64)
|
||||
#define RX (0) /* similar to USB_DIR_OUT but can be used as an index */
|
||||
|
@ -97,9 +98,24 @@ struct ci13xxx_ep {
|
|||
struct dma_pool *td_pool;
|
||||
};
|
||||
|
||||
struct ci13xxx;
|
||||
struct ci13xxx_udc_driver {
|
||||
const char *name;
|
||||
unsigned long flags;
|
||||
#define CI13XXX_REGS_SHARED BIT(0)
|
||||
#define CI13XXX_REQUIRE_TRANSCEIVER BIT(1)
|
||||
#define CI13XXX_PULLUP_ON_VBUS BIT(2)
|
||||
#define CI13XXX_DISABLE_STREAMING BIT(3)
|
||||
|
||||
#define CI13XXX_CONTROLLER_RESET_EVENT 0
|
||||
#define CI13XXX_CONTROLLER_STOPPED_EVENT 1
|
||||
void (*notify_event) (struct ci13xxx *udc, unsigned event);
|
||||
};
|
||||
|
||||
/* CI13XXX UDC descriptor & global resources */
|
||||
struct ci13xxx {
|
||||
spinlock_t *lock; /* ctrl register bank access */
|
||||
void __iomem *regs; /* registers address space */
|
||||
|
||||
struct dma_pool *qh_pool; /* DMA pool for queue heads */
|
||||
struct dma_pool *td_pool; /* DMA pool for transfer descs */
|
||||
|
@ -108,6 +124,9 @@ struct ci13xxx {
|
|||
struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX]; /* extended endpts */
|
||||
|
||||
struct usb_gadget_driver *driver; /* 3rd party gadget driver */
|
||||
struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
|
||||
int vbus_active; /* is VBUS active */
|
||||
struct otg_transceiver *transceiver; /* Transceiver struct */
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
|
@ -157,6 +176,7 @@ struct ci13xxx {
|
|||
#define USBMODE_CM_DEVICE (0x02UL << 0)
|
||||
#define USBMODE_CM_HOST (0x03UL << 0)
|
||||
#define USBMODE_SLOM BIT(3)
|
||||
#define USBMODE_SDIS BIT(4)
|
||||
|
||||
/* ENDPTCTRL */
|
||||
#define ENDPTCTRL_RXS BIT(0)
|
||||
|
|
|
@ -1126,7 +1126,7 @@ static int composite_bind(struct usb_gadget *gadget)
|
|||
if (bcdDevice)
|
||||
cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
|
||||
|
||||
/* stirng overrides */
|
||||
/* string overrides */
|
||||
if (iManufacturer || !cdev->desc.iManufacturer) {
|
||||
if (!iManufacturer && !composite->iManufacturer &&
|
||||
!*composite_manufacturer)
|
||||
|
@ -1188,6 +1188,8 @@ composite_suspend(struct usb_gadget *gadget)
|
|||
composite->suspend(cdev);
|
||||
|
||||
cdev->suspended = 1;
|
||||
|
||||
usb_gadget_vbus_draw(gadget, 2);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1195,6 +1197,7 @@ composite_resume(struct usb_gadget *gadget)
|
|||
{
|
||||
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
||||
struct usb_function *f;
|
||||
u8 maxpower;
|
||||
|
||||
/* REVISIT: should we have config level
|
||||
* suspend/resume callbacks?
|
||||
|
@ -1207,6 +1210,11 @@ composite_resume(struct usb_gadget *gadget)
|
|||
if (f->resume)
|
||||
f->resume(f);
|
||||
}
|
||||
|
||||
maxpower = cdev->config->bMaxPower;
|
||||
|
||||
usb_gadget_vbus_draw(gadget, maxpower ?
|
||||
(2 * maxpower) : CONFIG_USB_GADGET_VBUS_DRAW);
|
||||
}
|
||||
|
||||
cdev->suspended = 0;
|
||||
|
|
|
@ -1197,6 +1197,139 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address)
|
|||
#define Ep_Request (USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)
|
||||
#define Ep_InRequest (Ep_Request | USB_DIR_IN)
|
||||
|
||||
|
||||
/**
|
||||
* handle_control_request() - handles all control transfers
|
||||
* @dum: pointer to dummy (the_controller)
|
||||
* @urb: the urb request to handle
|
||||
* @setup: pointer to the setup data for a USB device control
|
||||
* request
|
||||
* @status: pointer to request handling status
|
||||
*
|
||||
* Return 0 - if the request was handled
|
||||
* 1 - if the request wasn't handles
|
||||
* error code on error
|
||||
*/
|
||||
static int handle_control_request(struct dummy *dum, struct urb *urb,
|
||||
struct usb_ctrlrequest *setup,
|
||||
int *status)
|
||||
{
|
||||
struct dummy_ep *ep2;
|
||||
int ret_val = 1;
|
||||
unsigned w_index;
|
||||
unsigned w_value;
|
||||
|
||||
w_index = le16_to_cpu(setup->wIndex);
|
||||
w_value = le16_to_cpu(setup->wValue);
|
||||
switch (setup->bRequest) {
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
if (setup->bRequestType != Dev_Request)
|
||||
break;
|
||||
dum->address = w_value;
|
||||
*status = 0;
|
||||
dev_dbg(udc_dev(dum), "set_address = %d\n",
|
||||
w_value);
|
||||
ret_val = 0;
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
if (setup->bRequestType == Dev_Request) {
|
||||
ret_val = 0;
|
||||
switch (w_value) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
break;
|
||||
case USB_DEVICE_B_HNP_ENABLE:
|
||||
dum->gadget.b_hnp_enable = 1;
|
||||
break;
|
||||
case USB_DEVICE_A_HNP_SUPPORT:
|
||||
dum->gadget.a_hnp_support = 1;
|
||||
break;
|
||||
case USB_DEVICE_A_ALT_HNP_SUPPORT:
|
||||
dum->gadget.a_alt_hnp_support = 1;
|
||||
break;
|
||||
default:
|
||||
ret_val = -EOPNOTSUPP;
|
||||
}
|
||||
if (ret_val == 0) {
|
||||
dum->devstatus |= (1 << w_value);
|
||||
*status = 0;
|
||||
}
|
||||
} else if (setup->bRequestType == Ep_Request) {
|
||||
/* endpoint halt */
|
||||
ep2 = find_endpoint(dum, w_index);
|
||||
if (!ep2 || ep2->ep.name == ep0name) {
|
||||
ret_val = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
ep2->halted = 1;
|
||||
ret_val = 0;
|
||||
*status = 0;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
if (setup->bRequestType == Dev_Request) {
|
||||
ret_val = 0;
|
||||
switch (w_value) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
w_value = USB_DEVICE_REMOTE_WAKEUP;
|
||||
break;
|
||||
default:
|
||||
ret_val = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
if (ret_val == 0) {
|
||||
dum->devstatus &= ~(1 << w_value);
|
||||
*status = 0;
|
||||
}
|
||||
} else if (setup->bRequestType == Ep_Request) {
|
||||
/* endpoint halt */
|
||||
ep2 = find_endpoint(dum, w_index);
|
||||
if (!ep2) {
|
||||
ret_val = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
if (!ep2->wedged)
|
||||
ep2->halted = 0;
|
||||
ret_val = 0;
|
||||
*status = 0;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
if (setup->bRequestType == Dev_InRequest
|
||||
|| setup->bRequestType == Intf_InRequest
|
||||
|| setup->bRequestType == Ep_InRequest) {
|
||||
char *buf;
|
||||
/*
|
||||
* device: remote wakeup, selfpowered
|
||||
* interface: nothing
|
||||
* endpoint: halt
|
||||
*/
|
||||
buf = (char *)urb->transfer_buffer;
|
||||
if (urb->transfer_buffer_length > 0) {
|
||||
if (setup->bRequestType == Ep_InRequest) {
|
||||
ep2 = find_endpoint(dum, w_index);
|
||||
if (!ep2) {
|
||||
ret_val = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
buf[0] = ep2->halted;
|
||||
} else if (setup->bRequestType ==
|
||||
Dev_InRequest) {
|
||||
buf[0] = (u8)dum->devstatus;
|
||||
} else
|
||||
buf[0] = 0;
|
||||
}
|
||||
if (urb->transfer_buffer_length > 1)
|
||||
buf[1] = 0;
|
||||
urb->actual_length = min_t(u32, 2,
|
||||
urb->transfer_buffer_length);
|
||||
ret_val = 0;
|
||||
*status = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/* drive both sides of the transfers; looks like irq handlers to
|
||||
* both drivers except the callbacks aren't in_irq().
|
||||
*/
|
||||
|
@ -1299,14 +1432,8 @@ static void dummy_timer (unsigned long _dum)
|
|||
if (ep == &dum->ep [0] && ep->setup_stage) {
|
||||
struct usb_ctrlrequest setup;
|
||||
int value = 1;
|
||||
struct dummy_ep *ep2;
|
||||
unsigned w_index;
|
||||
unsigned w_value;
|
||||
|
||||
setup = *(struct usb_ctrlrequest*) urb->setup_packet;
|
||||
w_index = le16_to_cpu(setup.wIndex);
|
||||
w_value = le16_to_cpu(setup.wValue);
|
||||
|
||||
/* paranoia, in case of stale queued data */
|
||||
list_for_each_entry (req, &ep->queue, queue) {
|
||||
list_del_init (&req->queue);
|
||||
|
@ -1328,117 +1455,9 @@ static void dummy_timer (unsigned long _dum)
|
|||
ep->last_io = jiffies;
|
||||
ep->setup_stage = 0;
|
||||
ep->halted = 0;
|
||||
switch (setup.bRequest) {
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
if (setup.bRequestType != Dev_Request)
|
||||
break;
|
||||
dum->address = w_value;
|
||||
status = 0;
|
||||
dev_dbg (udc_dev(dum), "set_address = %d\n",
|
||||
w_value);
|
||||
value = 0;
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
if (setup.bRequestType == Dev_Request) {
|
||||
value = 0;
|
||||
switch (w_value) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
break;
|
||||
case USB_DEVICE_B_HNP_ENABLE:
|
||||
dum->gadget.b_hnp_enable = 1;
|
||||
break;
|
||||
case USB_DEVICE_A_HNP_SUPPORT:
|
||||
dum->gadget.a_hnp_support = 1;
|
||||
break;
|
||||
case USB_DEVICE_A_ALT_HNP_SUPPORT:
|
||||
dum->gadget.a_alt_hnp_support
|
||||
= 1;
|
||||
break;
|
||||
default:
|
||||
value = -EOPNOTSUPP;
|
||||
}
|
||||
if (value == 0) {
|
||||
dum->devstatus |=
|
||||
(1 << w_value);
|
||||
status = 0;
|
||||
}
|
||||
|
||||
} else if (setup.bRequestType == Ep_Request) {
|
||||
// endpoint halt
|
||||
ep2 = find_endpoint (dum, w_index);
|
||||
if (!ep2 || ep2->ep.name == ep0name) {
|
||||
value = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
ep2->halted = 1;
|
||||
value = 0;
|
||||
status = 0;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
if (setup.bRequestType == Dev_Request) {
|
||||
switch (w_value) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
dum->devstatus &= ~(1 <<
|
||||
USB_DEVICE_REMOTE_WAKEUP);
|
||||
value = 0;
|
||||
status = 0;
|
||||
break;
|
||||
default:
|
||||
value = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
} else if (setup.bRequestType == Ep_Request) {
|
||||
// endpoint halt
|
||||
ep2 = find_endpoint (dum, w_index);
|
||||
if (!ep2) {
|
||||
value = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
if (!ep2->wedged)
|
||||
ep2->halted = 0;
|
||||
value = 0;
|
||||
status = 0;
|
||||
}
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
if (setup.bRequestType == Dev_InRequest
|
||||
|| setup.bRequestType
|
||||
== Intf_InRequest
|
||||
|| setup.bRequestType
|
||||
== Ep_InRequest
|
||||
) {
|
||||
char *buf;
|
||||
|
||||
// device: remote wakeup, selfpowered
|
||||
// interface: nothing
|
||||
// endpoint: halt
|
||||
buf = (char *)urb->transfer_buffer;
|
||||
if (urb->transfer_buffer_length > 0) {
|
||||
if (setup.bRequestType ==
|
||||
Ep_InRequest) {
|
||||
ep2 = find_endpoint (dum, w_index);
|
||||
if (!ep2) {
|
||||
value = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
buf [0] = ep2->halted;
|
||||
} else if (setup.bRequestType ==
|
||||
Dev_InRequest) {
|
||||
buf [0] = (u8)
|
||||
dum->devstatus;
|
||||
} else
|
||||
buf [0] = 0;
|
||||
}
|
||||
if (urb->transfer_buffer_length > 1)
|
||||
buf [1] = 0;
|
||||
urb->actual_length = min_t(u32, 2,
|
||||
urb->transfer_buffer_length);
|
||||
value = 0;
|
||||
status = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
value = handle_control_request(dum, urb, &setup,
|
||||
&status);
|
||||
|
||||
/* gadget driver handles all other requests. block
|
||||
* until setup() returns; no reentrancy issues etc.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -3392,25 +3392,28 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
|||
dev_set_name(&curlun->dev,"%s-lun%d",
|
||||
dev_name(&gadget->dev), i);
|
||||
|
||||
if ((rc = device_register(&curlun->dev)) != 0) {
|
||||
kref_get(&fsg->ref);
|
||||
rc = device_register(&curlun->dev);
|
||||
if (rc) {
|
||||
INFO(fsg, "failed to register LUN%d: %d\n", i, rc);
|
||||
goto out;
|
||||
}
|
||||
if ((rc = device_create_file(&curlun->dev,
|
||||
&dev_attr_ro)) != 0 ||
|
||||
(rc = device_create_file(&curlun->dev,
|
||||
&dev_attr_nofua)) != 0 ||
|
||||
(rc = device_create_file(&curlun->dev,
|
||||
&dev_attr_file)) != 0) {
|
||||
device_unregister(&curlun->dev);
|
||||
put_device(&curlun->dev);
|
||||
goto out;
|
||||
}
|
||||
curlun->registered = 1;
|
||||
kref_get(&fsg->ref);
|
||||
|
||||
rc = device_create_file(&curlun->dev, &dev_attr_ro);
|
||||
if (rc)
|
||||
goto out;
|
||||
rc = device_create_file(&curlun->dev, &dev_attr_nofua);
|
||||
if (rc)
|
||||
goto out;
|
||||
rc = device_create_file(&curlun->dev, &dev_attr_file);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
if (mod_data.file[i] && *mod_data.file[i]) {
|
||||
if ((rc = fsg_lun_open(curlun,
|
||||
mod_data.file[i])) != 0)
|
||||
rc = fsg_lun_open(curlun, mod_data.file[i]);
|
||||
if (rc)
|
||||
goto out;
|
||||
} else if (!mod_data.removable) {
|
||||
ERROR(fsg, "no file given for LUN%d\n", i);
|
||||
|
|
|
@ -1,7 +1,29 @@
|
|||
/*
|
||||
* g_ffs.c -- user mode file system API for USB composite function controllers
|
||||
*
|
||||
* Copyright (C) 2010 Samsung Electronics
|
||||
* Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "g_ffs: " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/utsname.h>
|
||||
|
||||
|
||||
/*
|
||||
* kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
|
@ -43,7 +65,6 @@ static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
|
|||
|
||||
#include "f_fs.c"
|
||||
|
||||
|
||||
#define DRIVER_NAME "g_ffs"
|
||||
#define DRIVER_DESC "USB Function Filesystem"
|
||||
#define DRIVER_VERSION "24 Aug 2004"
|
||||
|
@ -73,8 +94,6 @@ MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass");
|
|||
module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644);
|
||||
MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol");
|
||||
|
||||
|
||||
|
||||
static const struct usb_descriptor_header *gfs_otg_desc[] = {
|
||||
(const struct usb_descriptor_header *)
|
||||
&(const struct usb_otg_descriptor) {
|
||||
|
@ -91,8 +110,7 @@ static const struct usb_descriptor_header *gfs_otg_desc[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
/* String IDs are assigned dynamically */
|
||||
static struct usb_string gfs_strings[] = {
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
{ .s = "FunctionFS + RNDIS" },
|
||||
|
@ -114,8 +132,6 @@ static struct usb_gadget_strings *gfs_dev_strings[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct gfs_configuration {
|
||||
struct usb_configuration c;
|
||||
int (*eth)(struct usb_configuration *c, u8 *ethaddr);
|
||||
|
@ -138,7 +154,6 @@ struct gfs_configuration {
|
|||
#endif
|
||||
};
|
||||
|
||||
|
||||
static int gfs_bind(struct usb_composite_dev *cdev);
|
||||
static int gfs_unbind(struct usb_composite_dev *cdev);
|
||||
static int gfs_do_config(struct usb_configuration *c);
|
||||
|
@ -151,11 +166,9 @@ static struct usb_composite_driver gfs_driver = {
|
|||
.iProduct = DRIVER_DESC,
|
||||
};
|
||||
|
||||
|
||||
static struct ffs_data *gfs_ffs_data;
|
||||
static unsigned long gfs_registered;
|
||||
|
||||
|
||||
static int gfs_init(void)
|
||||
{
|
||||
ENTER();
|
||||
|
@ -175,7 +188,6 @@ static void gfs_exit(void)
|
|||
}
|
||||
module_exit(gfs_exit);
|
||||
|
||||
|
||||
static int functionfs_ready_callback(struct ffs_data *ffs)
|
||||
{
|
||||
int ret;
|
||||
|
@ -200,14 +212,11 @@ static void functionfs_closed_callback(struct ffs_data *ffs)
|
|||
usb_composite_unregister(&gfs_driver);
|
||||
}
|
||||
|
||||
|
||||
static int functionfs_check_dev_callback(const char *dev_name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int gfs_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int ret, i;
|
||||
|
@ -274,7 +283,6 @@ static int gfs_unbind(struct usb_composite_dev *cdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int gfs_do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct gfs_configuration *gc =
|
||||
|
@ -315,7 +323,6 @@ static int gfs_do_config(struct usb_configuration *c)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
|
||||
static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
||||
|
|
|
@ -96,7 +96,7 @@
|
|||
|
||||
/* Mentor high speed "dual role" controller, in peripheral role */
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
#define gadget_is_musbhdrc(g) !strcmp("musb_hdrc", (g)->name)
|
||||
#define gadget_is_musbhdrc(g) !strcmp("musb-hdrc", (g)->name)
|
||||
#else
|
||||
#define gadget_is_musbhdrc(g) 0
|
||||
#endif
|
||||
|
@ -120,10 +120,10 @@
|
|||
#define gadget_is_fsl_qe(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_CI13XXX
|
||||
#define gadget_is_ci13xxx(g) (!strcmp("ci13xxx_udc", (g)->name))
|
||||
#ifdef CONFIG_USB_GADGET_CI13XXX_PCI
|
||||
#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name))
|
||||
#else
|
||||
#define gadget_is_ci13xxx(g) 0
|
||||
#define gadget_is_ci13xxx_pci(g) 0
|
||||
#endif
|
||||
|
||||
// CONFIG_USB_GADGET_SX2
|
||||
|
@ -142,6 +142,17 @@
|
|||
#define gadget_is_s3c_hsotg(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_EG20T
|
||||
#define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name))
|
||||
#else
|
||||
#define gadget_is_pch(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_CI13XXX_MSM
|
||||
#define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name))
|
||||
#else
|
||||
#define gadget_is_ci13xxx_msm(g) 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* usb_gadget_controller_number - support bcdDevice id convention
|
||||
|
@ -192,7 +203,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
|
|||
return 0x21;
|
||||
else if (gadget_is_fsl_qe(gadget))
|
||||
return 0x22;
|
||||
else if (gadget_is_ci13xxx(gadget))
|
||||
else if (gadget_is_ci13xxx_pci(gadget))
|
||||
return 0x23;
|
||||
else if (gadget_is_langwell(gadget))
|
||||
return 0x24;
|
||||
|
@ -200,6 +211,10 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
|
|||
return 0x25;
|
||||
else if (gadget_is_s3c_hsotg(gadget))
|
||||
return 0x26;
|
||||
else if (gadget_is_pch(gadget))
|
||||
return 0x27;
|
||||
else if (gadget_is_ci13xxx_msm(gadget))
|
||||
return 0x28;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
|
|
@ -1191,13 +1191,17 @@ static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#ifndef MX1_INT_USBD0
|
||||
#define MX1_INT_USBD0 MX1_USBD_INT0
|
||||
#endif
|
||||
|
||||
static irqreturn_t imx_udc_bulk_irq(int irq, void *dev)
|
||||
{
|
||||
struct imx_udc_struct *imx_usb = dev;
|
||||
struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - USBD_INT0];
|
||||
struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - MX1_INT_USBD0];
|
||||
int intr = __raw_readl(imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
|
||||
|
||||
dump_ep_intr(__func__, irq - USBD_INT0, intr, imx_usb->dev);
|
||||
dump_ep_intr(__func__, irq - MX1_INT_USBD0, intr, imx_usb->dev);
|
||||
|
||||
if (!imx_usb->driver) {
|
||||
__raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
|
||||
|
|
|
@ -23,9 +23,6 @@
|
|||
/* Helper macros */
|
||||
#define EP_NO(ep) ((ep->bEndpointAddress) & ~USB_DIR_IN) /* IN:1, OUT:0 */
|
||||
#define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0)
|
||||
#define irq_to_ep(irq) (((irq) >= USBD_INT0) || ((irq) <= USBD_INT6) \
|
||||
? ((irq) - USBD_INT0) : (USBD_INT6)) /*should not happen*/
|
||||
#define ep_to_irq(ep) (EP_NO((ep)) + USBD_INT0)
|
||||
#define IMX_USB_NB_EP 6
|
||||
|
||||
/* Driver structures */
|
||||
|
|
|
@ -2225,6 +2225,7 @@ static void handle_setup_packet(struct langwell_udc *dev,
|
|||
u16 wValue = le16_to_cpu(setup->wValue);
|
||||
u16 wIndex = le16_to_cpu(setup->wIndex);
|
||||
u16 wLength = le16_to_cpu(setup->wLength);
|
||||
u32 portsc1;
|
||||
|
||||
dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
|
||||
|
||||
|
@ -2313,6 +2314,28 @@ static void handle_setup_packet(struct langwell_udc *dev,
|
|||
dev->dev_status &= ~(1 << wValue);
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_TEST_MODE:
|
||||
dev_dbg(&dev->pdev->dev, "SETUP: TEST MODE\n");
|
||||
if ((wIndex & 0xff) ||
|
||||
(dev->gadget.speed != USB_SPEED_HIGH))
|
||||
ep0_stall(dev);
|
||||
|
||||
switch (wIndex >> 8) {
|
||||
case TEST_J:
|
||||
case TEST_K:
|
||||
case TEST_SE0_NAK:
|
||||
case TEST_PACKET:
|
||||
case TEST_FORCE_EN:
|
||||
if (prime_status_phase(dev, EP_DIR_IN))
|
||||
ep0_stall(dev);
|
||||
portsc1 = readl(&dev->op_regs->portsc1);
|
||||
portsc1 |= (wIndex & 0xf00) << 8;
|
||||
writel(portsc1, &dev->op_regs->portsc1);
|
||||
goto end;
|
||||
default:
|
||||
rc = -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rc = -EOPNOTSUPP;
|
||||
break;
|
||||
|
|
|
@ -102,7 +102,7 @@ static struct fsg_module_parameters mod_data = {
|
|||
};
|
||||
FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
|
||||
|
||||
static unsigned long msg_registered = 0;
|
||||
static unsigned long msg_registered;
|
||||
static void msg_cleanup(void);
|
||||
|
||||
static int msg_thread_exits(struct fsg_common *common)
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
|
||||
#ifndef __MV_UDC_H
|
||||
#define __MV_UDC_H
|
||||
|
||||
#define VUSBHS_MAX_PORTS 8
|
||||
|
||||
#define DQH_ALIGNMENT 2048
|
||||
#define DTD_ALIGNMENT 64
|
||||
#define DMA_BOUNDARY 4096
|
||||
|
||||
#define EP_DIR_IN 1
|
||||
#define EP_DIR_OUT 0
|
||||
|
||||
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
||||
|
||||
#define EP0_MAX_PKT_SIZE 64
|
||||
/* ep0 transfer state */
|
||||
#define WAIT_FOR_SETUP 0
|
||||
#define DATA_STATE_XMIT 1
|
||||
#define DATA_STATE_NEED_ZLP 2
|
||||
#define WAIT_FOR_OUT_STATUS 3
|
||||
#define DATA_STATE_RECV 4
|
||||
|
||||
#define CAPLENGTH_MASK (0xff)
|
||||
#define DCCPARAMS_DEN_MASK (0x1f)
|
||||
|
||||
#define HCSPARAMS_PPC (0x10)
|
||||
|
||||
/* Frame Index Register Bit Masks */
|
||||
#define USB_FRINDEX_MASKS 0x3fff
|
||||
|
||||
/* Command Register Bit Masks */
|
||||
#define USBCMD_RUN_STOP (0x00000001)
|
||||
#define USBCMD_CTRL_RESET (0x00000002)
|
||||
#define USBCMD_SETUP_TRIPWIRE_SET (0x00002000)
|
||||
#define USBCMD_SETUP_TRIPWIRE_CLEAR (~USBCMD_SETUP_TRIPWIRE_SET)
|
||||
|
||||
#define USBCMD_ATDTW_TRIPWIRE_SET (0x00004000)
|
||||
#define USBCMD_ATDTW_TRIPWIRE_CLEAR (~USBCMD_ATDTW_TRIPWIRE_SET)
|
||||
|
||||
/* bit 15,3,2 are for frame list size */
|
||||
#define USBCMD_FRAME_SIZE_1024 (0x00000000) /* 000 */
|
||||
#define USBCMD_FRAME_SIZE_512 (0x00000004) /* 001 */
|
||||
#define USBCMD_FRAME_SIZE_256 (0x00000008) /* 010 */
|
||||
#define USBCMD_FRAME_SIZE_128 (0x0000000C) /* 011 */
|
||||
#define USBCMD_FRAME_SIZE_64 (0x00008000) /* 100 */
|
||||
#define USBCMD_FRAME_SIZE_32 (0x00008004) /* 101 */
|
||||
#define USBCMD_FRAME_SIZE_16 (0x00008008) /* 110 */
|
||||
#define USBCMD_FRAME_SIZE_8 (0x0000800C) /* 111 */
|
||||
|
||||
#define EPCTRL_TX_ALL_MASK (0xFFFF0000)
|
||||
#define EPCTRL_RX_ALL_MASK (0x0000FFFF)
|
||||
|
||||
#define EPCTRL_TX_DATA_TOGGLE_RST (0x00400000)
|
||||
#define EPCTRL_TX_EP_STALL (0x00010000)
|
||||
#define EPCTRL_RX_EP_STALL (0x00000001)
|
||||
#define EPCTRL_RX_DATA_TOGGLE_RST (0x00000040)
|
||||
#define EPCTRL_RX_ENABLE (0x00000080)
|
||||
#define EPCTRL_TX_ENABLE (0x00800000)
|
||||
#define EPCTRL_CONTROL (0x00000000)
|
||||
#define EPCTRL_ISOCHRONOUS (0x00040000)
|
||||
#define EPCTRL_BULK (0x00080000)
|
||||
#define EPCTRL_INT (0x000C0000)
|
||||
#define EPCTRL_TX_TYPE (0x000C0000)
|
||||
#define EPCTRL_RX_TYPE (0x0000000C)
|
||||
#define EPCTRL_DATA_TOGGLE_INHIBIT (0x00000020)
|
||||
#define EPCTRL_TX_EP_TYPE_SHIFT (18)
|
||||
#define EPCTRL_RX_EP_TYPE_SHIFT (2)
|
||||
|
||||
#define EPCOMPLETE_MAX_ENDPOINTS (16)
|
||||
|
||||
/* endpoint list address bit masks */
|
||||
#define USB_EP_LIST_ADDRESS_MASK 0xfffff800
|
||||
|
||||
#define PORTSCX_W1C_BITS 0x2a
|
||||
#define PORTSCX_PORT_RESET 0x00000100
|
||||
#define PORTSCX_PORT_POWER 0x00001000
|
||||
#define PORTSCX_FORCE_FULL_SPEED_CONNECT 0x01000000
|
||||
#define PORTSCX_PAR_XCVR_SELECT 0xC0000000
|
||||
#define PORTSCX_PORT_FORCE_RESUME 0x00000040
|
||||
#define PORTSCX_PORT_SUSPEND 0x00000080
|
||||
#define PORTSCX_PORT_SPEED_FULL 0x00000000
|
||||
#define PORTSCX_PORT_SPEED_LOW 0x04000000
|
||||
#define PORTSCX_PORT_SPEED_HIGH 0x08000000
|
||||
#define PORTSCX_PORT_SPEED_MASK 0x0C000000
|
||||
|
||||
/* USB MODE Register Bit Masks */
|
||||
#define USBMODE_CTRL_MODE_IDLE 0x00000000
|
||||
#define USBMODE_CTRL_MODE_DEVICE 0x00000002
|
||||
#define USBMODE_CTRL_MODE_HOST 0x00000003
|
||||
#define USBMODE_CTRL_MODE_RSV 0x00000001
|
||||
#define USBMODE_SETUP_LOCK_OFF 0x00000008
|
||||
#define USBMODE_STREAM_DISABLE 0x00000010
|
||||
|
||||
/* USB STS Register Bit Masks */
|
||||
#define USBSTS_INT 0x00000001
|
||||
#define USBSTS_ERR 0x00000002
|
||||
#define USBSTS_PORT_CHANGE 0x00000004
|
||||
#define USBSTS_FRM_LST_ROLL 0x00000008
|
||||
#define USBSTS_SYS_ERR 0x00000010
|
||||
#define USBSTS_IAA 0x00000020
|
||||
#define USBSTS_RESET 0x00000040
|
||||
#define USBSTS_SOF 0x00000080
|
||||
#define USBSTS_SUSPEND 0x00000100
|
||||
#define USBSTS_HC_HALTED 0x00001000
|
||||
#define USBSTS_RCL 0x00002000
|
||||
#define USBSTS_PERIODIC_SCHEDULE 0x00004000
|
||||
#define USBSTS_ASYNC_SCHEDULE 0x00008000
|
||||
|
||||
|
||||
/* Interrupt Enable Register Bit Masks */
|
||||
#define USBINTR_INT_EN (0x00000001)
|
||||
#define USBINTR_ERR_INT_EN (0x00000002)
|
||||
#define USBINTR_PORT_CHANGE_DETECT_EN (0x00000004)
|
||||
|
||||
#define USBINTR_ASYNC_ADV_AAE (0x00000020)
|
||||
#define USBINTR_ASYNC_ADV_AAE_ENABLE (0x00000020)
|
||||
#define USBINTR_ASYNC_ADV_AAE_DISABLE (0xFFFFFFDF)
|
||||
|
||||
#define USBINTR_RESET_EN (0x00000040)
|
||||
#define USBINTR_SOF_UFRAME_EN (0x00000080)
|
||||
#define USBINTR_DEVICE_SUSPEND (0x00000100)
|
||||
|
||||
#define USB_DEVICE_ADDRESS_MASK (0xfe000000)
|
||||
#define USB_DEVICE_ADDRESS_BIT_SHIFT (25)
|
||||
|
||||
struct mv_cap_regs {
|
||||
u32 caplength_hciversion;
|
||||
u32 hcsparams; /* HC structural parameters */
|
||||
u32 hccparams; /* HC Capability Parameters*/
|
||||
u32 reserved[5];
|
||||
u32 dciversion; /* DC version number and reserved 16 bits */
|
||||
u32 dccparams; /* DC Capability Parameters */
|
||||
};
|
||||
|
||||
struct mv_op_regs {
|
||||
u32 usbcmd; /* Command register */
|
||||
u32 usbsts; /* Status register */
|
||||
u32 usbintr; /* Interrupt enable */
|
||||
u32 frindex; /* Frame index */
|
||||
u32 reserved1[1];
|
||||
u32 deviceaddr; /* Device Address */
|
||||
u32 eplistaddr; /* Endpoint List Address */
|
||||
u32 ttctrl; /* HOST TT status and control */
|
||||
u32 burstsize; /* Programmable Burst Size */
|
||||
u32 txfilltuning; /* Host Transmit Pre-Buffer Packet Tuning */
|
||||
u32 reserved[4];
|
||||
u32 epnak; /* Endpoint NAK */
|
||||
u32 epnaken; /* Endpoint NAK Enable */
|
||||
u32 configflag; /* Configured Flag register */
|
||||
u32 portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */
|
||||
u32 otgsc;
|
||||
u32 usbmode; /* USB Host/Device mode */
|
||||
u32 epsetupstat; /* Endpoint Setup Status */
|
||||
u32 epprime; /* Endpoint Initialize */
|
||||
u32 epflush; /* Endpoint De-initialize */
|
||||
u32 epstatus; /* Endpoint Status */
|
||||
u32 epcomplete; /* Endpoint Interrupt On Complete */
|
||||
u32 epctrlx[16]; /* Endpoint Control, where x = 0.. 15 */
|
||||
u32 mcr; /* Mux Control */
|
||||
u32 isr; /* Interrupt Status */
|
||||
u32 ier; /* Interrupt Enable */
|
||||
};
|
||||
|
||||
struct mv_udc {
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
spinlock_t lock;
|
||||
struct completion *done;
|
||||
struct platform_device *dev;
|
||||
int irq;
|
||||
|
||||
struct mv_cap_regs __iomem *cap_regs;
|
||||
struct mv_op_regs __iomem *op_regs;
|
||||
unsigned int phy_regs;
|
||||
unsigned int max_eps;
|
||||
struct mv_dqh *ep_dqh;
|
||||
size_t ep_dqh_size;
|
||||
dma_addr_t ep_dqh_dma;
|
||||
|
||||
struct dma_pool *dtd_pool;
|
||||
struct mv_ep *eps;
|
||||
|
||||
struct mv_dtd *dtd_head;
|
||||
struct mv_dtd *dtd_tail;
|
||||
unsigned int dtd_entries;
|
||||
|
||||
struct mv_req *status_req;
|
||||
struct usb_ctrlrequest local_setup_buff;
|
||||
|
||||
unsigned int resume_state; /* USB state to resume */
|
||||
unsigned int usb_state; /* USB current state */
|
||||
unsigned int ep0_state; /* Endpoint zero state */
|
||||
unsigned int ep0_dir;
|
||||
|
||||
unsigned int dev_addr;
|
||||
|
||||
int errors;
|
||||
unsigned softconnect:1,
|
||||
vbus_active:1,
|
||||
remote_wakeup:1,
|
||||
softconnected:1,
|
||||
force_fs:1;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
/* endpoint data structure */
|
||||
struct mv_ep {
|
||||
struct usb_ep ep;
|
||||
struct mv_udc *udc;
|
||||
struct list_head queue;
|
||||
struct mv_dqh *dqh;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
u32 direction;
|
||||
char name[14];
|
||||
unsigned stopped:1,
|
||||
wedge:1,
|
||||
ep_type:2,
|
||||
ep_num:8;
|
||||
};
|
||||
|
||||
/* request data structure */
|
||||
struct mv_req {
|
||||
struct usb_request req;
|
||||
struct mv_dtd *dtd, *head, *tail;
|
||||
struct mv_ep *ep;
|
||||
struct list_head queue;
|
||||
unsigned dtd_count;
|
||||
unsigned mapped:1;
|
||||
};
|
||||
|
||||
#define EP_QUEUE_HEAD_MULT_POS 30
|
||||
#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000
|
||||
#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16
|
||||
#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff)
|
||||
#define EP_QUEUE_HEAD_IOS 0x00008000
|
||||
#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001
|
||||
#define EP_QUEUE_HEAD_IOC 0x00008000
|
||||
#define EP_QUEUE_HEAD_MULTO 0x00000C00
|
||||
#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040
|
||||
#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080
|
||||
#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF
|
||||
#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0
|
||||
#define EP_QUEUE_FRINDEX_MASK 0x000007FF
|
||||
#define EP_MAX_LENGTH_TRANSFER 0x4000
|
||||
|
||||
struct mv_dqh {
|
||||
/* Bits 16..26 Bit 15 is Interrupt On Setup */
|
||||
u32 max_packet_length;
|
||||
u32 curr_dtd_ptr; /* Current dTD Pointer */
|
||||
u32 next_dtd_ptr; /* Next dTD Pointer */
|
||||
/* Total bytes (16..30), IOC (15), INT (8), STS (0-7) */
|
||||
u32 size_ioc_int_sts;
|
||||
u32 buff_ptr0; /* Buffer pointer Page 0 (12-31) */
|
||||
u32 buff_ptr1; /* Buffer pointer Page 1 (12-31) */
|
||||
u32 buff_ptr2; /* Buffer pointer Page 2 (12-31) */
|
||||
u32 buff_ptr3; /* Buffer pointer Page 3 (12-31) */
|
||||
u32 buff_ptr4; /* Buffer pointer Page 4 (12-31) */
|
||||
u32 reserved1;
|
||||
/* 8 bytes of setup data that follows the Setup PID */
|
||||
u8 setup_buffer[8];
|
||||
u32 reserved2[4];
|
||||
};
|
||||
|
||||
|
||||
#define DTD_NEXT_TERMINATE (0x00000001)
|
||||
#define DTD_IOC (0x00008000)
|
||||
#define DTD_STATUS_ACTIVE (0x00000080)
|
||||
#define DTD_STATUS_HALTED (0x00000040)
|
||||
#define DTD_STATUS_DATA_BUFF_ERR (0x00000020)
|
||||
#define DTD_STATUS_TRANSACTION_ERR (0x00000008)
|
||||
#define DTD_RESERVED_FIELDS (0x00007F00)
|
||||
#define DTD_ERROR_MASK (0x68)
|
||||
#define DTD_ADDR_MASK (0xFFFFFFE0)
|
||||
#define DTD_PACKET_SIZE 0x7FFF0000
|
||||
#define DTD_LENGTH_BIT_POS (16)
|
||||
|
||||
struct mv_dtd {
|
||||
u32 dtd_next;
|
||||
u32 size_ioc_sts;
|
||||
u32 buff_ptr0; /* Buffer pointer Page 0 */
|
||||
u32 buff_ptr1; /* Buffer pointer Page 1 */
|
||||
u32 buff_ptr2; /* Buffer pointer Page 2 */
|
||||
u32 buff_ptr3; /* Buffer pointer Page 3 */
|
||||
u32 buff_ptr4; /* Buffer pointer Page 4 */
|
||||
u32 scratch_ptr;
|
||||
/* 32 bytes */
|
||||
dma_addr_t td_dma; /* dma address for this td */
|
||||
struct mv_dtd *next_dtd_virt;
|
||||
};
|
||||
|
||||
extern int mv_udc_phy_init(unsigned int base);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,214 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#include <mach/cputype.h>
|
||||
|
||||
#ifdef CONFIG_ARCH_MMP
|
||||
|
||||
#define UTMI_REVISION 0x0
|
||||
#define UTMI_CTRL 0x4
|
||||
#define UTMI_PLL 0x8
|
||||
#define UTMI_TX 0xc
|
||||
#define UTMI_RX 0x10
|
||||
#define UTMI_IVREF 0x14
|
||||
#define UTMI_T0 0x18
|
||||
#define UTMI_T1 0x1c
|
||||
#define UTMI_T2 0x20
|
||||
#define UTMI_T3 0x24
|
||||
#define UTMI_T4 0x28
|
||||
#define UTMI_T5 0x2c
|
||||
#define UTMI_RESERVE 0x30
|
||||
#define UTMI_USB_INT 0x34
|
||||
#define UTMI_DBG_CTL 0x38
|
||||
#define UTMI_OTG_ADDON 0x3c
|
||||
|
||||
/* For UTMICTRL Register */
|
||||
#define UTMI_CTRL_USB_CLK_EN (1 << 31)
|
||||
/* pxa168 */
|
||||
#define UTMI_CTRL_SUSPEND_SET1 (1 << 30)
|
||||
#define UTMI_CTRL_SUSPEND_SET2 (1 << 29)
|
||||
#define UTMI_CTRL_RXBUF_PDWN (1 << 24)
|
||||
#define UTMI_CTRL_TXBUF_PDWN (1 << 11)
|
||||
|
||||
#define UTMI_CTRL_INPKT_DELAY_SHIFT 30
|
||||
#define UTMI_CTRL_INPKT_DELAY_SOF_SHIFT 28
|
||||
#define UTMI_CTRL_PU_REF_SHIFT 20
|
||||
#define UTMI_CTRL_ARC_PULLDN_SHIFT 12
|
||||
#define UTMI_CTRL_PLL_PWR_UP_SHIFT 1
|
||||
#define UTMI_CTRL_PWR_UP_SHIFT 0
|
||||
/* For UTMI_PLL Register */
|
||||
#define UTMI_PLL_CLK_BLK_EN_SHIFT 24
|
||||
#define UTMI_PLL_FBDIV_SHIFT 4
|
||||
#define UTMI_PLL_REFDIV_SHIFT 0
|
||||
#define UTMI_PLL_FBDIV_MASK 0x00000FF0
|
||||
#define UTMI_PLL_REFDIV_MASK 0x0000000F
|
||||
#define UTMI_PLL_ICP_MASK 0x00007000
|
||||
#define UTMI_PLL_KVCO_MASK 0x00031000
|
||||
#define UTMI_PLL_PLLCALI12_SHIFT 29
|
||||
#define UTMI_PLL_PLLCALI12_MASK (0x3 << 29)
|
||||
#define UTMI_PLL_PLLVDD18_SHIFT 27
|
||||
#define UTMI_PLL_PLLVDD18_MASK (0x3 << 27)
|
||||
#define UTMI_PLL_PLLVDD12_SHIFT 25
|
||||
#define UTMI_PLL_PLLVDD12_MASK (0x3 << 25)
|
||||
#define UTMI_PLL_KVCO_SHIFT 15
|
||||
#define UTMI_PLL_ICP_SHIFT 12
|
||||
/* For UTMI_TX Register */
|
||||
#define UTMI_TX_REG_EXT_FS_RCAL_SHIFT 27
|
||||
#define UTMI_TX_REG_EXT_FS_RCAL_MASK (0xf << 27)
|
||||
#define UTMI_TX_REG_EXT_FS_RCAL_EN_MASK 26
|
||||
#define UTMI_TX_REG_EXT_FS_RCAL_EN (0x1 << 26)
|
||||
#define UTMI_TX_LOW_VDD_EN_SHIFT 11
|
||||
#define UTMI_TX_IMPCAL_VTH_SHIFT 14
|
||||
#define UTMI_TX_IMPCAL_VTH_MASK (0x7 << 14)
|
||||
#define UTMI_TX_CK60_PHSEL_SHIFT 17
|
||||
#define UTMI_TX_CK60_PHSEL_MASK (0xf << 17)
|
||||
#define UTMI_TX_TXVDD12_SHIFT 22
|
||||
#define UTMI_TX_TXVDD12_MASK (0x3 << 22)
|
||||
#define UTMI_TX_AMP_SHIFT 0
|
||||
#define UTMI_TX_AMP_MASK (0x7 << 0)
|
||||
/* For UTMI_RX Register */
|
||||
#define UTMI_RX_SQ_THRESH_SHIFT 4
|
||||
#define UTMI_RX_SQ_THRESH_MASK (0xf << 4)
|
||||
#define UTMI_REG_SQ_LENGTH_SHIFT 15
|
||||
#define UTMI_REG_SQ_LENGTH_MASK (0x3 << 15)
|
||||
|
||||
#define REG_RCAL_START 0x00001000
|
||||
#define VCOCAL_START 0x00200000
|
||||
#define KVCO_EXT 0x00400000
|
||||
#define PLL_READY 0x00800000
|
||||
#define CLK_BLK_EN 0x01000000
|
||||
#endif
|
||||
|
||||
static unsigned int u2o_read(unsigned int base, unsigned int offset)
|
||||
{
|
||||
return readl(base + offset);
|
||||
}
|
||||
|
||||
static void u2o_set(unsigned int base, unsigned int offset, unsigned int value)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
reg = readl(base + offset);
|
||||
reg |= value;
|
||||
writel(reg, base + offset);
|
||||
readl(base + offset);
|
||||
}
|
||||
|
||||
static void u2o_clear(unsigned int base, unsigned int offset,
|
||||
unsigned int value)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
reg = readl(base + offset);
|
||||
reg &= ~value;
|
||||
writel(reg, base + offset);
|
||||
readl(base + offset);
|
||||
}
|
||||
|
||||
static void u2o_write(unsigned int base, unsigned int offset,
|
||||
unsigned int value)
|
||||
{
|
||||
writel(value, base + offset);
|
||||
readl(base + offset);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_MMP
|
||||
int mv_udc_phy_init(unsigned int base)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
/* Initialize the USB PHY power */
|
||||
if (cpu_is_pxa910()) {
|
||||
u2o_set(base, UTMI_CTRL, (1 << UTMI_CTRL_INPKT_DELAY_SOF_SHIFT)
|
||||
| (1 << UTMI_CTRL_PU_REF_SHIFT));
|
||||
}
|
||||
|
||||
u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PLL_PWR_UP_SHIFT);
|
||||
u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PWR_UP_SHIFT);
|
||||
|
||||
/* UTMI_PLL settings */
|
||||
u2o_clear(base, UTMI_PLL, UTMI_PLL_PLLVDD18_MASK
|
||||
| UTMI_PLL_PLLVDD12_MASK | UTMI_PLL_PLLCALI12_MASK
|
||||
| UTMI_PLL_FBDIV_MASK | UTMI_PLL_REFDIV_MASK
|
||||
| UTMI_PLL_ICP_MASK | UTMI_PLL_KVCO_MASK);
|
||||
|
||||
u2o_set(base, UTMI_PLL, (0xee << UTMI_PLL_FBDIV_SHIFT)
|
||||
| (0xb << UTMI_PLL_REFDIV_SHIFT)
|
||||
| (3 << UTMI_PLL_PLLVDD18_SHIFT)
|
||||
| (3 << UTMI_PLL_PLLVDD12_SHIFT)
|
||||
| (3 << UTMI_PLL_PLLCALI12_SHIFT)
|
||||
| (1 << UTMI_PLL_ICP_SHIFT) | (3 << UTMI_PLL_KVCO_SHIFT));
|
||||
|
||||
/* UTMI_TX */
|
||||
u2o_clear(base, UTMI_TX, UTMI_TX_REG_EXT_FS_RCAL_EN_MASK
|
||||
| UTMI_TX_TXVDD12_MASK
|
||||
| UTMI_TX_CK60_PHSEL_MASK | UTMI_TX_IMPCAL_VTH_MASK
|
||||
| UTMI_TX_REG_EXT_FS_RCAL_MASK | UTMI_TX_AMP_MASK);
|
||||
u2o_set(base, UTMI_TX, (3 << UTMI_TX_TXVDD12_SHIFT)
|
||||
| (4 << UTMI_TX_CK60_PHSEL_SHIFT)
|
||||
| (4 << UTMI_TX_IMPCAL_VTH_SHIFT)
|
||||
| (8 << UTMI_TX_REG_EXT_FS_RCAL_SHIFT)
|
||||
| (3 << UTMI_TX_AMP_SHIFT));
|
||||
|
||||
/* UTMI_RX */
|
||||
u2o_clear(base, UTMI_RX, UTMI_RX_SQ_THRESH_MASK
|
||||
| UTMI_REG_SQ_LENGTH_MASK);
|
||||
if (cpu_is_pxa168())
|
||||
u2o_set(base, UTMI_RX, (7 << UTMI_RX_SQ_THRESH_SHIFT)
|
||||
| (2 << UTMI_REG_SQ_LENGTH_SHIFT));
|
||||
else
|
||||
u2o_set(base, UTMI_RX, (0x7 << UTMI_RX_SQ_THRESH_SHIFT)
|
||||
| (2 << UTMI_REG_SQ_LENGTH_SHIFT));
|
||||
|
||||
/* UTMI_IVREF */
|
||||
if (cpu_is_pxa168())
|
||||
/*
|
||||
* fixing Microsoft Altair board interface with NEC hub issue -
|
||||
* Set UTMI_IVREF from 0x4a3 to 0x4bf
|
||||
*/
|
||||
u2o_write(base, UTMI_IVREF, 0x4bf);
|
||||
|
||||
/* calibrate */
|
||||
timeout = jiffies + 100;
|
||||
while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) {
|
||||
if (time_after(jiffies, timeout))
|
||||
return -ETIME;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
/* toggle VCOCAL_START bit of UTMI_PLL */
|
||||
udelay(200);
|
||||
u2o_set(base, UTMI_PLL, VCOCAL_START);
|
||||
udelay(40);
|
||||
u2o_clear(base, UTMI_PLL, VCOCAL_START);
|
||||
|
||||
/* toggle REG_RCAL_START bit of UTMI_TX */
|
||||
udelay(200);
|
||||
u2o_set(base, UTMI_TX, REG_RCAL_START);
|
||||
udelay(40);
|
||||
u2o_clear(base, UTMI_TX, REG_RCAL_START);
|
||||
udelay(200);
|
||||
|
||||
/* make sure phy is ready */
|
||||
timeout = jiffies + 100;
|
||||
while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) {
|
||||
if (time_after(jiffies, timeout))
|
||||
return -ETIME;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
if (cpu_is_pxa168()) {
|
||||
u2o_set(base, UTMI_RESERVE, 1 << 5);
|
||||
/* Turn on UTMI PHY OTG extension */
|
||||
u2o_write(base, UTMI_OTG_ADDON, 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int mv_udc_phy_init(unsigned int base)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* ncm.c -- NCM gadget driver
|
||||
*
|
||||
* Copyright (C) 2010 Nokia Corporation
|
||||
* Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
|
||||
*
|
||||
* The driver borrows from ether.c which is:
|
||||
*
|
||||
* Copyright (C) 2003-2005,2008 David Brownell
|
||||
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* #define DEBUG */
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/utsname.h>
|
||||
|
||||
|
||||
#include "u_ether.h"
|
||||
|
||||
#define DRIVER_DESC "NCM Gadget"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* 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 "composite.c"
|
||||
#include "usbstring.c"
|
||||
#include "config.c"
|
||||
#include "epautoconf.c"
|
||||
|
||||
#include "f_ncm.c"
|
||||
#include "u_ether.c"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
|
||||
* Instead: allocate your own, using normal USB-IF procedures.
|
||||
*/
|
||||
|
||||
/* Thanks to NetChip Technologies for donating this product ID.
|
||||
* It's for devices with only CDC Ethernet configurations.
|
||||
*/
|
||||
#define CDC_VENDOR_NUM 0x0525 /* NetChip */
|
||||
#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16 (0x0200),
|
||||
|
||||
.bDeviceClass = USB_CLASS_COMM,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
/* .bMaxPacketSize0 = f(hardware) */
|
||||
|
||||
/* Vendor and product id defaults change according to what configs
|
||||
* we support. (As does bNumConfigurations.) These values can
|
||||
* also be overridden by module parameters.
|
||||
*/
|
||||
.idVendor = cpu_to_le16 (CDC_VENDOR_NUM),
|
||||
.idProduct = cpu_to_le16 (CDC_PRODUCT_NUM),
|
||||
/* .bcdDevice = f(hardware) */
|
||||
/* .iManufacturer = DYNAMIC */
|
||||
/* .iProduct = DYNAMIC */
|
||||
/* NO SERIAL NUMBER */
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
static struct usb_otg_descriptor otg_descriptor = {
|
||||
.bLength = sizeof otg_descriptor,
|
||||
.bDescriptorType = USB_DT_OTG,
|
||||
|
||||
/* REVISIT SRP-only hardware is possible, although
|
||||
* it would not be called "OTG" ...
|
||||
*/
|
||||
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
|
||||
};
|
||||
|
||||
static const struct usb_descriptor_header *otg_desc[] = {
|
||||
(struct usb_descriptor_header *) &otg_descriptor,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
#define STRING_MANUFACTURER_IDX 0
|
||||
#define STRING_PRODUCT_IDX 1
|
||||
|
||||
static char manufacturer[50];
|
||||
|
||||
static struct usb_string strings_dev[] = {
|
||||
[STRING_MANUFACTURER_IDX].s = manufacturer,
|
||||
[STRING_PRODUCT_IDX].s = DRIVER_DESC,
|
||||
{ } /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings stringtab_dev = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = strings_dev,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *dev_strings[] = {
|
||||
&stringtab_dev,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init ncm_do_config(struct usb_configuration *c)
|
||||
{
|
||||
/* FIXME alloc iConfiguration string, set it in c->strings */
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
c->descriptors = otg_desc;
|
||||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
return ncm_bind_config(c, hostaddr);
|
||||
}
|
||||
|
||||
static struct usb_configuration ncm_config_driver = {
|
||||
/* .label = f(hardware) */
|
||||
.label = "CDC Ethernet (NCM)",
|
||||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init gncm_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int gcnum;
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
int status;
|
||||
|
||||
/* set up network link layer */
|
||||
status = gether_setup(cdev->gadget, hostaddr);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
gcnum = usb_gadget_controller_number(gadget);
|
||||
if (gcnum >= 0)
|
||||
device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
|
||||
else {
|
||||
/* We assume that can_support_ecm() tells the truth;
|
||||
* but if the controller isn't recognized at all then
|
||||
* that assumption is a bit more likely to be wrong.
|
||||
*/
|
||||
dev_warn(&gadget->dev,
|
||||
"controller '%s' not recognized; trying %s\n",
|
||||
gadget->name,
|
||||
ncm_config_driver.label);
|
||||
device_desc.bcdDevice =
|
||||
cpu_to_le16(0x0300 | 0x0099);
|
||||
}
|
||||
|
||||
|
||||
/* Allocate string descriptor numbers ... note that string
|
||||
* contents can be overridden by the composite_dev glue.
|
||||
*/
|
||||
|
||||
/* device descriptor strings: manufacturer, product */
|
||||
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
|
||||
init_utsname()->sysname, init_utsname()->release,
|
||||
gadget->name);
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
strings_dev[STRING_MANUFACTURER_IDX].id = status;
|
||||
device_desc.iManufacturer = status;
|
||||
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
strings_dev[STRING_PRODUCT_IDX].id = status;
|
||||
device_desc.iProduct = status;
|
||||
|
||||
status = usb_add_config(cdev, &ncm_config_driver,
|
||||
ncm_do_config);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
dev_info(&gadget->dev, "%s\n", DRIVER_DESC);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
gether_cleanup();
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __exit gncm_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
gether_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_composite_driver ncm_driver = {
|
||||
.name = "g_ncm",
|
||||
.dev = &device_desc,
|
||||
.strings = dev_strings,
|
||||
.unbind = __exit_p(gncm_unbind),
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR("Yauheni Kaliuta");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int __init init(void)
|
||||
{
|
||||
return usb_composite_probe(&ncm_driver, gncm_bind);
|
||||
}
|
||||
module_init(init);
|
||||
|
||||
static void __exit cleanup(void)
|
||||
{
|
||||
usb_composite_unregister(&ncm_driver);
|
||||
}
|
||||
module_exit(cleanup);
|
File diff suppressed because it is too large
Load Diff
|
@ -255,6 +255,7 @@ static int gaudio_open_snd_dev(struct gaudio *card)
|
|||
ERROR(card, "No such PCM capture device: %s\n", fn_cap);
|
||||
snd->substream = NULL;
|
||||
snd->card = NULL;
|
||||
snd->filp = NULL;
|
||||
} else {
|
||||
pcm_file = snd->filp->private_data;
|
||||
snd->substream = pcm_file->substream;
|
||||
|
@ -273,17 +274,17 @@ static int gaudio_close_snd_dev(struct gaudio *gau)
|
|||
|
||||
/* Close control device */
|
||||
snd = &gau->control;
|
||||
if (!IS_ERR(snd->filp))
|
||||
if (snd->filp)
|
||||
filp_close(snd->filp, current->files);
|
||||
|
||||
/* Close PCM playback device and setup substream */
|
||||
snd = &gau->playback;
|
||||
if (!IS_ERR(snd->filp))
|
||||
if (snd->filp)
|
||||
filp_close(snd->filp, current->files);
|
||||
|
||||
/* Close PCM capture device and setup substream */
|
||||
snd = &gau->capture;
|
||||
if (!IS_ERR(snd->filp))
|
||||
if (snd->filp)
|
||||
filp_close(snd->filp, current->files);
|
||||
|
||||
return 0;
|
||||
|
@ -304,8 +305,7 @@ int __init gaudio_setup(struct gaudio *card)
|
|||
ret = gaudio_open_snd_dev(card);
|
||||
if (ret)
|
||||
ERROR(card, "we need at least one control device\n");
|
||||
|
||||
if (!the_card)
|
||||
else if (!the_card)
|
||||
the_card = card;
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -240,6 +240,9 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
|
|||
size += out->maxpacket - 1;
|
||||
size -= size % out->maxpacket;
|
||||
|
||||
if (dev->port_usb->is_fixed)
|
||||
size = max(size, dev->port_usb->fixed_out_len);
|
||||
|
||||
skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
|
||||
if (skb == NULL) {
|
||||
DBG(dev, "no rx skb\n");
|
||||
|
@ -578,12 +581,19 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
|||
req->context = skb;
|
||||
req->complete = tx_complete;
|
||||
|
||||
/* NCM requires no zlp if transfer is dwNtbInMaxSize */
|
||||
if (dev->port_usb->is_fixed &&
|
||||
length == dev->port_usb->fixed_in_len &&
|
||||
(length % in->maxpacket) == 0)
|
||||
req->zero = 0;
|
||||
else
|
||||
req->zero = 1;
|
||||
|
||||
/* use zlp framing on tx for strict CDC-Ether conformance,
|
||||
* though any robust network rx path ignores extra padding.
|
||||
* and some hardware doesn't like to write zlps.
|
||||
*/
|
||||
req->zero = 1;
|
||||
if (!dev->zlp && (length % in->maxpacket) == 0)
|
||||
if (req->zero && !dev->zlp && (length % in->maxpacket) == 0)
|
||||
length++;
|
||||
|
||||
req->length = length;
|
||||
|
|
|
@ -62,6 +62,10 @@ struct gether {
|
|||
|
||||
/* hooks for added framing, as needed for RNDIS and EEM. */
|
||||
u32 header_len;
|
||||
/* NCM requires fixed size bundles */
|
||||
bool is_fixed;
|
||||
u32 fixed_out_len;
|
||||
u32 fixed_in_len;
|
||||
struct sk_buff *(*wrap)(struct gether *port,
|
||||
struct sk_buff *skb);
|
||||
int (*unwrap)(struct gether *port,
|
||||
|
@ -103,6 +107,7 @@ static inline bool can_support_ecm(struct usb_gadget *gadget)
|
|||
/* each configuration may bind one instance of an ethernet link */
|
||||
int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
|
||||
int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
|
||||
int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
|
||||
int eem_bind_config(struct usb_configuration *c);
|
||||
|
||||
#ifdef USB_ETH_RNDIS
|
||||
|
|
|
@ -133,6 +133,25 @@ config USB_EHCI_MXC
|
|||
---help---
|
||||
Variation of ARC USB block used in some Freescale chips.
|
||||
|
||||
config USB_EHCI_HCD_OMAP
|
||||
bool "EHCI support for OMAP3 and later chips"
|
||||
depends on USB_EHCI_HCD && ARCH_OMAP
|
||||
default y
|
||||
--- help ---
|
||||
Enables support for the on-chip EHCI controller on
|
||||
OMAP3 and later chips.
|
||||
|
||||
config USB_EHCI_MSM
|
||||
bool "Support for MSM on-chip EHCI USB controller"
|
||||
depends on USB_EHCI_HCD && ARCH_MSM
|
||||
select USB_EHCI_ROOT_HUB_TT
|
||||
select USB_MSM_OTG_72K
|
||||
---help---
|
||||
Enables support for the USB Host controller present on the
|
||||
Qualcomm chipsets. Root Hub has inbuilt TT.
|
||||
This driver depends on OTG driver for PHY initialization,
|
||||
clock management, powering up VBUS, and power management.
|
||||
|
||||
config USB_EHCI_HCD_PPC_OF
|
||||
bool "EHCI support for PPC USB controller on OF platform bus"
|
||||
depends on USB_EHCI_HCD && PPC_OF
|
||||
|
|
|
@ -99,6 +99,7 @@ static const struct hc_driver ehci_atmel_hc_driver = {
|
|||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/* scheduling support */
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
@ -110,6 +111,8 @@ static const struct hc_driver ehci_atmel_hc_driver = {
|
|||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int __init ehci_atmel_drv_probe(struct platform_device *pdev)
|
||||
|
|
|
@ -879,7 +879,7 @@ static int fill_buffer(struct debug_buffer *buf)
|
|||
int ret = 0;
|
||||
|
||||
if (!buf->output_buf)
|
||||
buf->output_buf = (char *)vmalloc(buf->alloc_size);
|
||||
buf->output_buf = vmalloc(buf->alloc_size);
|
||||
|
||||
if (!buf->output_buf) {
|
||||
ret = -ENOMEM;
|
||||
|
|
|
@ -114,6 +114,9 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n");
|
|||
|
||||
#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
|
||||
|
||||
/* for ASPM quirk of ISOC on AMD SB800 */
|
||||
static struct pci_dev *amd_nb_dev;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#include "ehci.h"
|
||||
|
@ -529,6 +532,11 @@ static void ehci_stop (struct usb_hcd *hcd)
|
|||
spin_unlock_irq (&ehci->lock);
|
||||
ehci_mem_cleanup (ehci);
|
||||
|
||||
if (amd_nb_dev) {
|
||||
pci_dev_put(amd_nb_dev);
|
||||
amd_nb_dev = NULL;
|
||||
}
|
||||
|
||||
#ifdef EHCI_STATS
|
||||
ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
|
||||
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
|
||||
|
@ -1166,12 +1174,17 @@ MODULE_LICENSE ("GPL");
|
|||
#define PLATFORM_DRIVER ehci_mxc_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CPU_SUBTYPE_SH7786
|
||||
#include "ehci-sh.c"
|
||||
#define PLATFORM_DRIVER ehci_hcd_sh_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SOC_AU1200
|
||||
#include "ehci-au1xxx.c"
|
||||
#define PLATFORM_DRIVER ehci_hcd_au1xxx_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP3
|
||||
#ifdef CONFIG_USB_EHCI_HCD_OMAP
|
||||
#include "ehci-omap.c"
|
||||
#define PLATFORM_DRIVER ehci_hcd_omap_driver
|
||||
#endif
|
||||
|
@ -1221,6 +1234,21 @@ MODULE_LICENSE ("GPL");
|
|||
#define PLATFORM_DRIVER cns3xxx_ehci_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_VT8500
|
||||
#include "ehci-vt8500.c"
|
||||
#define PLATFORM_DRIVER vt8500_ehci_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PLAT_SPEAR
|
||||
#include "ehci-spear.c"
|
||||
#define PLATFORM_DRIVER spear_ehci_hcd_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_MSM
|
||||
#include "ehci-msm.c"
|
||||
#define PLATFORM_DRIVER ehci_msm_driver
|
||||
#endif
|
||||
|
||||
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
|
||||
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
|
||||
!defined(XILINX_OF_PLATFORM_DRIVER)
|
||||
|
|
|
@ -0,0 +1,345 @@
|
|||
/* ehci-msm.c - HSUSB Host Controller Driver Implementation
|
||||
*
|
||||
* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* Partly derived from ehci-fsl.c and ehci-hcd.c
|
||||
* Copyright (c) 2000-2004 by David Brownell
|
||||
* Copyright (c) 2005 MontaVista Software
|
||||
*
|
||||
* All source code in this file is licensed under the following license except
|
||||
* where indicated.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you can find it at http://www.fsf.org
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/msm_hsusb_hw.h>
|
||||
|
||||
#define MSM_USB_BASE (hcd->regs)
|
||||
|
||||
static struct otg_transceiver *otg;
|
||||
|
||||
/*
|
||||
* ehci_run defined in drivers/usb/host/ehci-hcd.c reset the controller and
|
||||
* the configuration settings in ehci_msm_reset vanish after controller is
|
||||
* reset. Resetting the controler in ehci_run seems to be un-necessary
|
||||
* provided HCD reset the controller before calling ehci_run. Most of the HCD
|
||||
* do but some are not. So this function is same as ehci_run but we don't
|
||||
* reset the controller here.
|
||||
*/
|
||||
static int ehci_msm_run(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
u32 temp;
|
||||
u32 hcc_params;
|
||||
|
||||
hcd->uses_new_polling = 1;
|
||||
|
||||
ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
|
||||
ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next);
|
||||
|
||||
/*
|
||||
* hcc_params controls whether ehci->regs->segment must (!!!)
|
||||
* be used; it constrains QH/ITD/SITD and QTD locations.
|
||||
* pci_pool consistent memory always uses segment zero.
|
||||
* streaming mappings for I/O buffers, like pci_map_single(),
|
||||
* can return segments above 4GB, if the device allows.
|
||||
*
|
||||
* NOTE: the dma mask is visible through dma_supported(), so
|
||||
* drivers can pass this info along ... like NETIF_F_HIGHDMA,
|
||||
* Scsi_Host.highmem_io, and so forth. It's readonly to all
|
||||
* host side drivers though.
|
||||
*/
|
||||
hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
|
||||
if (HCC_64BIT_ADDR(hcc_params))
|
||||
ehci_writel(ehci, 0, &ehci->regs->segment);
|
||||
|
||||
/*
|
||||
* Philips, Intel, and maybe others need CMD_RUN before the
|
||||
* root hub will detect new devices (why?); NEC doesn't
|
||||
*/
|
||||
ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
|
||||
ehci->command |= CMD_RUN;
|
||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
||||
dbg_cmd(ehci, "init", ehci->command);
|
||||
|
||||
/*
|
||||
* Start, enabling full USB 2.0 functionality ... usb 1.1 devices
|
||||
* are explicitly handed to companion controller(s), so no TT is
|
||||
* involved with the root hub. (Except where one is integrated,
|
||||
* and there's no companion controller unless maybe for USB OTG.)
|
||||
*
|
||||
* Turning on the CF flag will transfer ownership of all ports
|
||||
* from the companions to the EHCI controller. If any of the
|
||||
* companions are in the middle of a port reset at the time, it
|
||||
* could cause trouble. Write-locking ehci_cf_port_reset_rwsem
|
||||
* guarantees that no resets are in progress. After we set CF,
|
||||
* a short delay lets the hardware catch up; new resets shouldn't
|
||||
* be started before the port switching actions could complete.
|
||||
*/
|
||||
down_write(&ehci_cf_port_reset_rwsem);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
|
||||
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
|
||||
usleep_range(5000, 5500);
|
||||
up_write(&ehci_cf_port_reset_rwsem);
|
||||
ehci->last_periodic_enable = ktime_get_real();
|
||||
|
||||
temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
ehci_info(ehci,
|
||||
"USB %x.%x started, EHCI %x.%02x%s\n",
|
||||
((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f),
|
||||
temp >> 8, temp & 0xff,
|
||||
ignore_oc ? ", overcurrent ignored" : "");
|
||||
|
||||
ehci_writel(ehci, INTR_MASK,
|
||||
&ehci->regs->intr_enable); /* Turn On Interrupts */
|
||||
|
||||
/* GRR this is run-once init(), being done every time the HC starts.
|
||||
* So long as they're part of class devices, we can't do it init()
|
||||
* since the class device isn't created that early.
|
||||
*/
|
||||
create_debug_files(ehci);
|
||||
create_companion_file(ehci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_msm_reset(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int retval;
|
||||
|
||||
ehci->caps = USB_CAPLENGTH;
|
||||
ehci->regs = USB_CAPLENGTH +
|
||||
HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
|
||||
/* cache the data to minimize the chip reads*/
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
|
||||
hcd->has_tt = 1;
|
||||
ehci->sbrn = HCD_USB2;
|
||||
|
||||
/* data structure init */
|
||||
retval = ehci_init(hcd);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = ehci_reset(ehci);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* bursts of unspecified length. */
|
||||
writel(0, USB_AHBBURST);
|
||||
/* Use the AHB transactor */
|
||||
writel(0, USB_AHBMODE);
|
||||
/* Disable streaming mode and select host mode */
|
||||
writel(0x13, USB_USBMODE);
|
||||
|
||||
ehci_port_power(ehci, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hc_driver msm_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "Qualcomm On-Chip EHCI Host Controller",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_USB2 | HCD_MEMORY,
|
||||
|
||||
.reset = ehci_msm_reset,
|
||||
.start = ehci_msm_run,
|
||||
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
/*
|
||||
* PM support
|
||||
*/
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
};
|
||||
|
||||
static int ehci_msm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&pdev->dev, "ehci_msm proble\n");
|
||||
|
||||
hcd = usb_create_hcd(&msm_hc_driver, &pdev->dev, dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
dev_err(&pdev->dev, "Unable to create HCD\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hcd->irq = platform_get_irq(pdev, 0);
|
||||
if (hcd->irq < 0) {
|
||||
dev_err(&pdev->dev, "Unable to get IRQ resource\n");
|
||||
ret = hcd->irq;
|
||||
goto put_hcd;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Unable to get memory resource\n");
|
||||
ret = -ENODEV;
|
||||
goto put_hcd;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (!hcd->regs) {
|
||||
dev_err(&pdev->dev, "ioremap failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto put_hcd;
|
||||
}
|
||||
|
||||
/*
|
||||
* OTG driver takes care of PHY initialization, clock management,
|
||||
* powering up VBUS, mapping of registers address space and power
|
||||
* management.
|
||||
*/
|
||||
otg = otg_get_transceiver();
|
||||
if (!otg) {
|
||||
dev_err(&pdev->dev, "unable to find transceiver\n");
|
||||
ret = -ENODEV;
|
||||
goto unmap;
|
||||
}
|
||||
|
||||
ret = otg_set_host(otg, &hcd->self);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "unable to register with transceiver\n");
|
||||
goto put_transceiver;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
/*
|
||||
* OTG device parent of HCD takes care of putting
|
||||
* hardware into low power mode.
|
||||
*/
|
||||
pm_runtime_no_callbacks(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
put_transceiver:
|
||||
otg_put_transceiver(otg);
|
||||
unmap:
|
||||
iounmap(hcd->regs);
|
||||
put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ehci_msm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
|
||||
otg_set_host(otg, NULL);
|
||||
otg_put_transceiver(otg);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ehci_msm_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
bool wakeup = device_may_wakeup(dev);
|
||||
|
||||
dev_dbg(dev, "ehci-msm PM suspend\n");
|
||||
|
||||
/*
|
||||
* EHCI helper function has also the same check before manipulating
|
||||
* port wakeup flags. We do check here the same condition before
|
||||
* calling the same helper function to avoid bringing hardware
|
||||
* from Low power mode when there is no need for adjusting port
|
||||
* wakeup flags.
|
||||
*/
|
||||
if (hcd->self.root_hub->do_remote_wakeup && !wakeup) {
|
||||
pm_runtime_resume(dev);
|
||||
ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),
|
||||
wakeup);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_msm_pm_resume(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(dev, "ehci-msm PM resume\n");
|
||||
ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ehci_msm_pm_suspend NULL
|
||||
#define ehci_msm_pm_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
|
||||
.suspend = ehci_msm_pm_suspend,
|
||||
.resume = ehci_msm_pm_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver ehci_msm_driver = {
|
||||
.probe = ehci_msm_probe,
|
||||
.remove = __devexit_p(ehci_msm_remove),
|
||||
.driver = {
|
||||
.name = "msm_hsusb_host",
|
||||
.pm = &ehci_msm_dev_pm_ops,
|
||||
},
|
||||
};
|
|
@ -36,14 +36,8 @@ struct ehci_mxc_priv {
|
|||
static int ehci_mxc_setup(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
struct device *dev = hcd->self.controller;
|
||||
struct mxc_usbh_platform_data *pdata = dev_get_platdata(dev);
|
||||
int retval;
|
||||
|
||||
/* EHCI registers start at offset 0x100 */
|
||||
ehci->caps = hcd->regs + 0x100;
|
||||
ehci->regs = hcd->regs + 0x100 +
|
||||
HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
dbg_hcs_params(ehci, "reset");
|
||||
dbg_hcc_params(ehci, "reset");
|
||||
|
||||
|
@ -65,12 +59,6 @@ static int ehci_mxc_setup(struct usb_hcd *hcd)
|
|||
|
||||
ehci_reset(ehci);
|
||||
|
||||
/* set up the PORTSCx register */
|
||||
ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]);
|
||||
|
||||
/* is this really needed? */
|
||||
msleep(10);
|
||||
|
||||
ehci_port_power(ehci, 0);
|
||||
return 0;
|
||||
}
|
||||
|
@ -100,6 +88,7 @@ static const struct hc_driver ehci_mxc_hc_driver = {
|
|||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
|
@ -115,6 +104,8 @@ static const struct hc_driver ehci_mxc_hc_driver = {
|
|||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int ehci_mxc_drv_probe(struct platform_device *pdev)
|
||||
|
@ -125,6 +116,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
|
|||
int irq, ret;
|
||||
struct ehci_mxc_priv *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ehci_hcd *ehci;
|
||||
|
||||
dev_info(&pdev->dev, "initializing i.MX USB Controller\n");
|
||||
|
||||
|
@ -212,6 +204,19 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
goto err_init;
|
||||
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
|
||||
/* EHCI registers start at offset 0x100 */
|
||||
ehci->caps = hcd->regs + 0x100;
|
||||
ehci->regs = hcd->regs + 0x100 +
|
||||
HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
|
||||
/* set up the PORTSCx register */
|
||||
ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]);
|
||||
|
||||
/* is this really needed? */
|
||||
msleep(10);
|
||||
|
||||
/* Initialize the transceiver */
|
||||
if (pdata->otg) {
|
||||
pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET;
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
/*
|
||||
* ehci-omap.c - driver for USBHOST on OMAP 34xx processor
|
||||
* ehci-omap.c - driver for USBHOST on OMAP3/4 processors
|
||||
*
|
||||
* Bus Glue for OMAP34xx USBHOST 3 port EHCI controller
|
||||
* Tested on OMAP3430 ES2.0 SDP
|
||||
* Bus Glue for the EHCI controllers in OMAP3/4
|
||||
* Tested on several OMAP3 boards, and OMAP4 Pandaboard
|
||||
*
|
||||
* Copyright (C) 2007-2008 Texas Instruments, Inc.
|
||||
* Copyright (C) 2007-2010 Texas Instruments, Inc.
|
||||
* Author: Vikram Pandita <vikram.pandita@ti.com>
|
||||
* Author: Anand Gadiyar <gadiyar@ti.com>
|
||||
*
|
||||
* Copyright (C) 2009 Nokia Corporation
|
||||
* Contact: Felipe Balbi <felipe.balbi@nokia.com>
|
||||
|
@ -26,11 +27,14 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* TODO (last updated Feb 12, 2010):
|
||||
* TODO (last updated Nov 21, 2010):
|
||||
* - add kernel-doc
|
||||
* - enable AUTOIDLE
|
||||
* - add suspend/resume
|
||||
* - move workarounds to board-files
|
||||
* - factor out code common to OHCI
|
||||
* - add HSIC and TLL support
|
||||
* - convert to use hwmod and runtime PM
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -86,9 +90,9 @@
|
|||
#define OMAP_TLL_ULPI_SCRATCH_REGISTER(num) (0x816 + 0x100 * num)
|
||||
|
||||
#define OMAP_TLL_CHANNEL_COUNT 3
|
||||
#define OMAP_TLL_CHANNEL_1_EN_MASK (1 << 1)
|
||||
#define OMAP_TLL_CHANNEL_2_EN_MASK (1 << 2)
|
||||
#define OMAP_TLL_CHANNEL_3_EN_MASK (1 << 4)
|
||||
#define OMAP_TLL_CHANNEL_1_EN_MASK (1 << 0)
|
||||
#define OMAP_TLL_CHANNEL_2_EN_MASK (1 << 1)
|
||||
#define OMAP_TLL_CHANNEL_3_EN_MASK (1 << 2)
|
||||
|
||||
/* UHH Register Set */
|
||||
#define OMAP_UHH_REVISION (0x00)
|
||||
|
@ -114,6 +118,23 @@
|
|||
#define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9)
|
||||
#define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10)
|
||||
|
||||
/* OMAP4-specific defines */
|
||||
#define OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR (3 << 2)
|
||||
#define OMAP4_UHH_SYSCONFIG_NOIDLE (1 << 2)
|
||||
|
||||
#define OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR (3 << 4)
|
||||
#define OMAP4_UHH_SYSCONFIG_NOSTDBY (1 << 4)
|
||||
#define OMAP4_UHH_SYSCONFIG_SOFTRESET (1 << 0)
|
||||
|
||||
#define OMAP4_P1_MODE_CLEAR (3 << 16)
|
||||
#define OMAP4_P1_MODE_TLL (1 << 16)
|
||||
#define OMAP4_P1_MODE_HSIC (3 << 16)
|
||||
#define OMAP4_P2_MODE_CLEAR (3 << 18)
|
||||
#define OMAP4_P2_MODE_TLL (1 << 18)
|
||||
#define OMAP4_P2_MODE_HSIC (3 << 18)
|
||||
|
||||
#define OMAP_REV2_TLL_CHANNEL_COUNT 2
|
||||
|
||||
#define OMAP_UHH_DEBUG_CSR (0x44)
|
||||
|
||||
/* EHCI Register Set */
|
||||
|
@ -127,6 +148,17 @@
|
|||
#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8
|
||||
#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0
|
||||
|
||||
/* Values of UHH_REVISION - Note: these are not given in the TRM */
|
||||
#define OMAP_EHCI_REV1 0x00000010 /* OMAP3 */
|
||||
#define OMAP_EHCI_REV2 0x50700100 /* OMAP4 */
|
||||
|
||||
#define is_omap_ehci_rev1(x) (x->omap_ehci_rev == OMAP_EHCI_REV1)
|
||||
#define is_omap_ehci_rev2(x) (x->omap_ehci_rev == OMAP_EHCI_REV2)
|
||||
|
||||
#define is_ehci_phy_mode(x) (x == EHCI_HCD_OMAP_MODE_PHY)
|
||||
#define is_ehci_tll_mode(x) (x == EHCI_HCD_OMAP_MODE_TLL)
|
||||
#define is_ehci_hsic_mode(x) (x == EHCI_HCD_OMAP_MODE_HSIC)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static inline void ehci_omap_writel(void __iomem *base, u32 reg, u32 val)
|
||||
|
@ -156,10 +188,14 @@ struct ehci_hcd_omap {
|
|||
struct device *dev;
|
||||
|
||||
struct clk *usbhost_ick;
|
||||
struct clk *usbhost2_120m_fck;
|
||||
struct clk *usbhost1_48m_fck;
|
||||
struct clk *usbhost_hs_fck;
|
||||
struct clk *usbhost_fs_fck;
|
||||
struct clk *usbtll_fck;
|
||||
struct clk *usbtll_ick;
|
||||
struct clk *xclk60mhsp1_ck;
|
||||
struct clk *xclk60mhsp2_ck;
|
||||
struct clk *utmi_p1_fck;
|
||||
struct clk *utmi_p2_fck;
|
||||
|
||||
/* FIXME the following two workarounds are
|
||||
* board specific not silicon-specific so these
|
||||
|
@ -176,6 +212,9 @@ struct ehci_hcd_omap {
|
|||
/* phy reset workaround */
|
||||
int phy_reset;
|
||||
|
||||
/* IP revision */
|
||||
u32 omap_ehci_rev;
|
||||
|
||||
/* desired phy_mode: TLL, PHY */
|
||||
enum ehci_hcd_omap_mode port_mode[OMAP3_HS_USB_PORTS];
|
||||
|
||||
|
@ -191,13 +230,14 @@ struct ehci_hcd_omap {
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask)
|
||||
static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask,
|
||||
u8 tll_channel_count)
|
||||
{
|
||||
unsigned reg;
|
||||
int i;
|
||||
|
||||
/* Program the 3 TLL channels upfront */
|
||||
for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) {
|
||||
for (i = 0; i < tll_channel_count; i++) {
|
||||
reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i));
|
||||
|
||||
/* Disable AutoIdle, BitStuffing and use SDR Mode */
|
||||
|
@ -217,7 +257,7 @@ static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask)
|
|||
ehci_omap_writel(omap->tll_base, OMAP_TLL_SHARED_CONF, reg);
|
||||
|
||||
/* Enable channels now */
|
||||
for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) {
|
||||
for (i = 0; i < tll_channel_count; i++) {
|
||||
reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i));
|
||||
|
||||
/* Enable only the reg that is needed */
|
||||
|
@ -286,19 +326,19 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
}
|
||||
clk_enable(omap->usbhost_ick);
|
||||
|
||||
omap->usbhost2_120m_fck = clk_get(omap->dev, "usbhost_120m_fck");
|
||||
if (IS_ERR(omap->usbhost2_120m_fck)) {
|
||||
ret = PTR_ERR(omap->usbhost2_120m_fck);
|
||||
omap->usbhost_hs_fck = clk_get(omap->dev, "hs_fck");
|
||||
if (IS_ERR(omap->usbhost_hs_fck)) {
|
||||
ret = PTR_ERR(omap->usbhost_hs_fck);
|
||||
goto err_host_120m_fck;
|
||||
}
|
||||
clk_enable(omap->usbhost2_120m_fck);
|
||||
clk_enable(omap->usbhost_hs_fck);
|
||||
|
||||
omap->usbhost1_48m_fck = clk_get(omap->dev, "usbhost_48m_fck");
|
||||
if (IS_ERR(omap->usbhost1_48m_fck)) {
|
||||
ret = PTR_ERR(omap->usbhost1_48m_fck);
|
||||
omap->usbhost_fs_fck = clk_get(omap->dev, "fs_fck");
|
||||
if (IS_ERR(omap->usbhost_fs_fck)) {
|
||||
ret = PTR_ERR(omap->usbhost_fs_fck);
|
||||
goto err_host_48m_fck;
|
||||
}
|
||||
clk_enable(omap->usbhost1_48m_fck);
|
||||
clk_enable(omap->usbhost_fs_fck);
|
||||
|
||||
if (omap->phy_reset) {
|
||||
/* Refer: ISSUE1 */
|
||||
|
@ -333,6 +373,80 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
}
|
||||
clk_enable(omap->usbtll_ick);
|
||||
|
||||
omap->omap_ehci_rev = ehci_omap_readl(omap->uhh_base,
|
||||
OMAP_UHH_REVISION);
|
||||
dev_dbg(omap->dev, "OMAP UHH_REVISION 0x%x\n",
|
||||
omap->omap_ehci_rev);
|
||||
|
||||
/*
|
||||
* Enable per-port clocks as needed (newer controllers only).
|
||||
* - External ULPI clock for PHY mode
|
||||
* - Internal clocks for TLL and HSIC modes (TODO)
|
||||
*/
|
||||
if (is_omap_ehci_rev2(omap)) {
|
||||
switch (omap->port_mode[0]) {
|
||||
case EHCI_HCD_OMAP_MODE_PHY:
|
||||
omap->xclk60mhsp1_ck = clk_get(omap->dev,
|
||||
"xclk60mhsp1_ck");
|
||||
if (IS_ERR(omap->xclk60mhsp1_ck)) {
|
||||
ret = PTR_ERR(omap->xclk60mhsp1_ck);
|
||||
dev_err(omap->dev,
|
||||
"Unable to get Port1 ULPI clock\n");
|
||||
}
|
||||
|
||||
omap->utmi_p1_fck = clk_get(omap->dev,
|
||||
"utmi_p1_gfclk");
|
||||
if (IS_ERR(omap->utmi_p1_fck)) {
|
||||
ret = PTR_ERR(omap->utmi_p1_fck);
|
||||
dev_err(omap->dev,
|
||||
"Unable to get utmi_p1_fck\n");
|
||||
}
|
||||
|
||||
ret = clk_set_parent(omap->utmi_p1_fck,
|
||||
omap->xclk60mhsp1_ck);
|
||||
if (ret != 0) {
|
||||
dev_err(omap->dev,
|
||||
"Unable to set P1 f-clock\n");
|
||||
}
|
||||
break;
|
||||
case EHCI_HCD_OMAP_MODE_TLL:
|
||||
/* TODO */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (omap->port_mode[1]) {
|
||||
case EHCI_HCD_OMAP_MODE_PHY:
|
||||
omap->xclk60mhsp2_ck = clk_get(omap->dev,
|
||||
"xclk60mhsp2_ck");
|
||||
if (IS_ERR(omap->xclk60mhsp2_ck)) {
|
||||
ret = PTR_ERR(omap->xclk60mhsp2_ck);
|
||||
dev_err(omap->dev,
|
||||
"Unable to get Port2 ULPI clock\n");
|
||||
}
|
||||
|
||||
omap->utmi_p2_fck = clk_get(omap->dev,
|
||||
"utmi_p2_gfclk");
|
||||
if (IS_ERR(omap->utmi_p2_fck)) {
|
||||
ret = PTR_ERR(omap->utmi_p2_fck);
|
||||
dev_err(omap->dev,
|
||||
"Unable to get utmi_p2_fck\n");
|
||||
}
|
||||
|
||||
ret = clk_set_parent(omap->utmi_p2_fck,
|
||||
omap->xclk60mhsp2_ck);
|
||||
if (ret != 0) {
|
||||
dev_err(omap->dev,
|
||||
"Unable to set P2 f-clock\n");
|
||||
}
|
||||
break;
|
||||
case EHCI_HCD_OMAP_MODE_TLL:
|
||||
/* TODO */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* perform TLL soft reset, and wait until reset is complete */
|
||||
ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
|
||||
OMAP_USBTLL_SYSCONFIG_SOFTRESET);
|
||||
|
@ -360,12 +474,20 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
|
||||
/* Put UHH in NoIdle/NoStandby mode */
|
||||
reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG);
|
||||
if (is_omap_ehci_rev1(omap)) {
|
||||
reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP
|
||||
| OMAP_UHH_SYSCONFIG_SIDLEMODE
|
||||
| OMAP_UHH_SYSCONFIG_CACTIVITY
|
||||
| OMAP_UHH_SYSCONFIG_MIDLEMODE);
|
||||
reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE;
|
||||
|
||||
|
||||
} else if (is_omap_ehci_rev2(omap)) {
|
||||
reg &= ~OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR;
|
||||
reg |= OMAP4_UHH_SYSCONFIG_NOIDLE;
|
||||
reg &= ~OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR;
|
||||
reg |= OMAP4_UHH_SYSCONFIG_NOSTDBY;
|
||||
}
|
||||
ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg);
|
||||
|
||||
reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
|
||||
|
@ -376,6 +498,7 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
| OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN);
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
|
||||
|
||||
if (is_omap_ehci_rev1(omap)) {
|
||||
if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN)
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
|
||||
if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN)
|
||||
|
@ -386,30 +509,45 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
/* Bypass the TLL module for PHY mode operation */
|
||||
if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) {
|
||||
dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1\n");
|
||||
if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) ||
|
||||
(omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) ||
|
||||
(omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY))
|
||||
if (is_ehci_phy_mode(omap->port_mode[0]) ||
|
||||
is_ehci_phy_mode(omap->port_mode[1]) ||
|
||||
is_ehci_phy_mode(omap->port_mode[2]))
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
|
||||
else
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
|
||||
} else {
|
||||
dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n");
|
||||
if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY)
|
||||
if (is_ehci_phy_mode(omap->port_mode[0]))
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
|
||||
else if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
|
||||
else if (is_ehci_tll_mode(omap->port_mode[0]))
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
|
||||
|
||||
if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY)
|
||||
if (is_ehci_phy_mode(omap->port_mode[1]))
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
|
||||
else if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
|
||||
else if (is_ehci_tll_mode(omap->port_mode[1]))
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
|
||||
|
||||
if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)
|
||||
if (is_ehci_phy_mode(omap->port_mode[2]))
|
||||
reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
|
||||
else if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)
|
||||
else if (is_ehci_tll_mode(omap->port_mode[2]))
|
||||
reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
|
||||
|
||||
}
|
||||
} else if (is_omap_ehci_rev2(omap)) {
|
||||
/* Clear port mode fields for PHY mode*/
|
||||
reg &= ~OMAP4_P1_MODE_CLEAR;
|
||||
reg &= ~OMAP4_P2_MODE_CLEAR;
|
||||
|
||||
if (is_ehci_tll_mode(omap->port_mode[0]))
|
||||
reg |= OMAP4_P1_MODE_TLL;
|
||||
else if (is_ehci_hsic_mode(omap->port_mode[0]))
|
||||
reg |= OMAP4_P1_MODE_HSIC;
|
||||
|
||||
if (is_ehci_tll_mode(omap->port_mode[1]))
|
||||
reg |= OMAP4_P2_MODE_TLL;
|
||||
else if (is_ehci_hsic_mode(omap->port_mode[1]))
|
||||
reg |= OMAP4_P2_MODE_HSIC;
|
||||
}
|
||||
|
||||
ehci_omap_writel(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg);
|
||||
dev_dbg(omap->dev, "UHH setup done, uhh_hostconfig=%x\n", reg);
|
||||
|
||||
|
@ -438,7 +576,7 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
tll_ch_mask |= OMAP_TLL_CHANNEL_3_EN_MASK;
|
||||
|
||||
/* Enable UTMI mode for required TLL channels */
|
||||
omap_usb_utmi_init(omap, tll_ch_mask);
|
||||
omap_usb_utmi_init(omap, tll_ch_mask, OMAP_TLL_CHANNEL_COUNT);
|
||||
}
|
||||
|
||||
if (omap->phy_reset) {
|
||||
|
@ -464,6 +602,14 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
return 0;
|
||||
|
||||
err_sys_status:
|
||||
clk_disable(omap->utmi_p2_fck);
|
||||
clk_put(omap->utmi_p2_fck);
|
||||
clk_disable(omap->xclk60mhsp2_ck);
|
||||
clk_put(omap->xclk60mhsp2_ck);
|
||||
clk_disable(omap->utmi_p1_fck);
|
||||
clk_put(omap->utmi_p1_fck);
|
||||
clk_disable(omap->xclk60mhsp1_ck);
|
||||
clk_put(omap->xclk60mhsp1_ck);
|
||||
clk_disable(omap->usbtll_ick);
|
||||
clk_put(omap->usbtll_ick);
|
||||
|
||||
|
@ -472,8 +618,8 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
clk_put(omap->usbtll_fck);
|
||||
|
||||
err_tll_fck:
|
||||
clk_disable(omap->usbhost1_48m_fck);
|
||||
clk_put(omap->usbhost1_48m_fck);
|
||||
clk_disable(omap->usbhost_fs_fck);
|
||||
clk_put(omap->usbhost_fs_fck);
|
||||
|
||||
if (omap->phy_reset) {
|
||||
if (gpio_is_valid(omap->reset_gpio_port[0]))
|
||||
|
@ -484,8 +630,8 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
}
|
||||
|
||||
err_host_48m_fck:
|
||||
clk_disable(omap->usbhost2_120m_fck);
|
||||
clk_put(omap->usbhost2_120m_fck);
|
||||
clk_disable(omap->usbhost_hs_fck);
|
||||
clk_put(omap->usbhost_hs_fck);
|
||||
|
||||
err_host_120m_fck:
|
||||
clk_disable(omap->usbhost_ick);
|
||||
|
@ -503,6 +649,8 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
|
||||
/* Reset OMAP modules for insmod/rmmod to work */
|
||||
ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG,
|
||||
is_omap_ehci_rev2(omap) ?
|
||||
OMAP4_UHH_SYSCONFIG_SOFTRESET :
|
||||
OMAP_UHH_SYSCONFIG_SOFTRESET);
|
||||
while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
|
||||
& (1 << 0))) {
|
||||
|
@ -550,16 +698,16 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
omap->usbhost_ick = NULL;
|
||||
}
|
||||
|
||||
if (omap->usbhost1_48m_fck != NULL) {
|
||||
clk_disable(omap->usbhost1_48m_fck);
|
||||
clk_put(omap->usbhost1_48m_fck);
|
||||
omap->usbhost1_48m_fck = NULL;
|
||||
if (omap->usbhost_fs_fck != NULL) {
|
||||
clk_disable(omap->usbhost_fs_fck);
|
||||
clk_put(omap->usbhost_fs_fck);
|
||||
omap->usbhost_fs_fck = NULL;
|
||||
}
|
||||
|
||||
if (omap->usbhost2_120m_fck != NULL) {
|
||||
clk_disable(omap->usbhost2_120m_fck);
|
||||
clk_put(omap->usbhost2_120m_fck);
|
||||
omap->usbhost2_120m_fck = NULL;
|
||||
if (omap->usbhost_hs_fck != NULL) {
|
||||
clk_disable(omap->usbhost_hs_fck);
|
||||
clk_put(omap->usbhost_hs_fck);
|
||||
omap->usbhost_hs_fck = NULL;
|
||||
}
|
||||
|
||||
if (omap->usbtll_ick != NULL) {
|
||||
|
@ -568,6 +716,32 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
|
|||
omap->usbtll_ick = NULL;
|
||||
}
|
||||
|
||||
if (is_omap_ehci_rev2(omap)) {
|
||||
if (omap->xclk60mhsp1_ck != NULL) {
|
||||
clk_disable(omap->xclk60mhsp1_ck);
|
||||
clk_put(omap->xclk60mhsp1_ck);
|
||||
omap->xclk60mhsp1_ck = NULL;
|
||||
}
|
||||
|
||||
if (omap->utmi_p1_fck != NULL) {
|
||||
clk_disable(omap->utmi_p1_fck);
|
||||
clk_put(omap->utmi_p1_fck);
|
||||
omap->utmi_p1_fck = NULL;
|
||||
}
|
||||
|
||||
if (omap->xclk60mhsp2_ck != NULL) {
|
||||
clk_disable(omap->xclk60mhsp2_ck);
|
||||
clk_put(omap->xclk60mhsp2_ck);
|
||||
omap->xclk60mhsp2_ck = NULL;
|
||||
}
|
||||
|
||||
if (omap->utmi_p2_fck != NULL) {
|
||||
clk_disable(omap->utmi_p2_fck);
|
||||
clk_put(omap->utmi_p2_fck);
|
||||
omap->utmi_p2_fck = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (omap->phy_reset) {
|
||||
if (gpio_is_valid(omap->reset_gpio_port[0]))
|
||||
gpio_free(omap->reset_gpio_port[0]);
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
#error "This file is PCI bus glue. CONFIG_PCI must be defined."
|
||||
#endif
|
||||
|
||||
/* defined here to avoid adding to pci_ids.h for single instance use */
|
||||
#define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* called after powerup, by probe or system-pm "wakeup" */
|
||||
|
@ -41,6 +44,35 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_quirk_amd_SB800(struct ehci_hcd *ehci)
|
||||
{
|
||||
struct pci_dev *amd_smbus_dev;
|
||||
u8 rev = 0;
|
||||
|
||||
amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
|
||||
if (!amd_smbus_dev)
|
||||
return 0;
|
||||
|
||||
pci_read_config_byte(amd_smbus_dev, PCI_REVISION_ID, &rev);
|
||||
if (rev < 0x40) {
|
||||
pci_dev_put(amd_smbus_dev);
|
||||
amd_smbus_dev = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!amd_nb_dev)
|
||||
amd_nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1510, NULL);
|
||||
if (!amd_nb_dev)
|
||||
ehci_err(ehci, "QUIRK: unable to get AMD NB device\n");
|
||||
|
||||
ehci_info(ehci, "QUIRK: Enable AMD SB800 L1 fix\n");
|
||||
|
||||
pci_dev_put(amd_smbus_dev);
|
||||
amd_smbus_dev = NULL;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* called during probe() after chip reset completes */
|
||||
static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||
{
|
||||
|
@ -99,6 +131,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
|||
/* cache this readonly data; minimize chip reads */
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
|
||||
if (ehci_quirk_amd_SB800(ehci))
|
||||
ehci->amd_l1_fix = 1;
|
||||
|
||||
retval = ehci_halt(ehci);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
@ -137,6 +172,10 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
|||
ehci_info(ehci, "disable lpm for langwell/penwell\n");
|
||||
ehci->has_lpm = 0;
|
||||
}
|
||||
if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) {
|
||||
hcd->has_tt = 1;
|
||||
tdi_reset(ehci);
|
||||
}
|
||||
break;
|
||||
case PCI_VENDOR_ID_TDI:
|
||||
if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
|
||||
|
|
|
@ -1590,6 +1590,63 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
|
|||
*hw_p = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD);
|
||||
}
|
||||
|
||||
#define AB_REG_BAR_LOW 0xe0
|
||||
#define AB_REG_BAR_HIGH 0xe1
|
||||
#define AB_INDX(addr) ((addr) + 0x00)
|
||||
#define AB_DATA(addr) ((addr) + 0x04)
|
||||
#define NB_PCIE_INDX_ADDR 0xe0
|
||||
#define NB_PCIE_INDX_DATA 0xe4
|
||||
#define NB_PIF0_PWRDOWN_0 0x01100012
|
||||
#define NB_PIF0_PWRDOWN_1 0x01100013
|
||||
|
||||
static void ehci_quirk_amd_L1(struct ehci_hcd *ehci, int disable)
|
||||
{
|
||||
u32 addr, addr_low, addr_high, val;
|
||||
|
||||
outb_p(AB_REG_BAR_LOW, 0xcd6);
|
||||
addr_low = inb_p(0xcd7);
|
||||
outb_p(AB_REG_BAR_HIGH, 0xcd6);
|
||||
addr_high = inb_p(0xcd7);
|
||||
addr = addr_high << 8 | addr_low;
|
||||
outl_p(0x30, AB_INDX(addr));
|
||||
outl_p(0x40, AB_DATA(addr));
|
||||
outl_p(0x34, AB_INDX(addr));
|
||||
val = inl_p(AB_DATA(addr));
|
||||
|
||||
if (disable) {
|
||||
val &= ~0x8;
|
||||
val |= (1 << 4) | (1 << 9);
|
||||
} else {
|
||||
val |= 0x8;
|
||||
val &= ~((1 << 4) | (1 << 9));
|
||||
}
|
||||
outl_p(val, AB_DATA(addr));
|
||||
|
||||
if (amd_nb_dev) {
|
||||
addr = NB_PIF0_PWRDOWN_0;
|
||||
pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_ADDR, addr);
|
||||
pci_read_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, &val);
|
||||
if (disable)
|
||||
val &= ~(0x3f << 7);
|
||||
else
|
||||
val |= 0x3f << 7;
|
||||
|
||||
pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, val);
|
||||
|
||||
addr = NB_PIF0_PWRDOWN_1;
|
||||
pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_ADDR, addr);
|
||||
pci_read_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, &val);
|
||||
if (disable)
|
||||
val &= ~(0x3f << 7);
|
||||
else
|
||||
val |= 0x3f << 7;
|
||||
|
||||
pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, val);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* fit urb's itds into the selected schedule slot; activate as needed */
|
||||
static int
|
||||
itd_link_urb (
|
||||
|
@ -1616,6 +1673,12 @@ itd_link_urb (
|
|||
urb->interval,
|
||||
next_uframe >> 3, next_uframe & 0x7);
|
||||
}
|
||||
|
||||
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
|
||||
if (ehci->amd_l1_fix == 1)
|
||||
ehci_quirk_amd_L1(ehci, 1);
|
||||
}
|
||||
|
||||
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
|
||||
|
||||
/* fill iTDs uframe by uframe */
|
||||
|
@ -1740,6 +1803,11 @@ itd_complete (
|
|||
(void) disable_periodic(ehci);
|
||||
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
|
||||
|
||||
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
|
||||
if (ehci->amd_l1_fix == 1)
|
||||
ehci_quirk_amd_L1(ehci, 0);
|
||||
}
|
||||
|
||||
if (unlikely(list_is_singular(&stream->td_list))) {
|
||||
ehci_to_hcd(ehci)->self.bandwidth_allocated
|
||||
-= stream->bandwidth;
|
||||
|
@ -2025,6 +2093,12 @@ sitd_link_urb (
|
|||
(next_uframe >> 3) & (ehci->periodic_size - 1),
|
||||
stream->interval, hc32_to_cpu(ehci, stream->splits));
|
||||
}
|
||||
|
||||
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
|
||||
if (ehci->amd_l1_fix == 1)
|
||||
ehci_quirk_amd_L1(ehci, 1);
|
||||
}
|
||||
|
||||
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
|
||||
|
||||
/* fill sITDs frame by frame */
|
||||
|
@ -2125,6 +2199,11 @@ sitd_complete (
|
|||
(void) disable_periodic(ehci);
|
||||
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
|
||||
|
||||
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
|
||||
if (ehci->amd_l1_fix == 1)
|
||||
ehci_quirk_amd_L1(ehci, 0);
|
||||
}
|
||||
|
||||
if (list_is_singular(&stream->td_list)) {
|
||||
ehci_to_hcd(ehci)->self.bandwidth_allocated
|
||||
-= stream->bandwidth;
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* SuperH EHCI host controller driver
|
||||
*
|
||||
* Copyright (C) 2010 Paul Mundt
|
||||
*
|
||||
* Based on ohci-sh.c and ehci-atmel.c.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*/
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
struct ehci_sh_priv {
|
||||
struct clk *iclk, *fclk;
|
||||
struct usb_hcd *hcd;
|
||||
};
|
||||
|
||||
static int ehci_sh_reset(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int ret;
|
||||
|
||||
ehci->caps = hcd->regs;
|
||||
ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci,
|
||||
&ehci->caps->hc_capbase));
|
||||
|
||||
dbg_hcs_params(ehci, "reset");
|
||||
dbg_hcc_params(ehci, "reset");
|
||||
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
|
||||
ret = ehci_halt(ehci);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
ret = ehci_init(hcd);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
ehci->sbrn = 0x20;
|
||||
|
||||
ehci_reset(ehci);
|
||||
ehci_port_power(ehci, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct hc_driver ehci_sh_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "SuperH EHCI",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_USB2 | HCD_MEMORY,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = ehci_sh_reset,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
#endif
|
||||
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int ehci_hcd_sh_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct hc_driver *driver = &ehci_sh_hc_driver;
|
||||
struct resource *res;
|
||||
struct ehci_sh_priv *priv;
|
||||
struct usb_hcd *hcd;
|
||||
int irq, ret;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev,
|
||||
"Found HC with no register addr. Check %s setup!\n",
|
||||
dev_name(&pdev->dev));
|
||||
ret = -ENODEV;
|
||||
goto fail_create_hcd;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"Found HC with no IRQ. Check %s setup!\n",
|
||||
dev_name(&pdev->dev));
|
||||
ret = -ENODEV;
|
||||
goto fail_create_hcd;
|
||||
}
|
||||
|
||||
/* initialize hcd */
|
||||
hcd = usb_create_hcd(&ehci_sh_hc_driver, &pdev->dev,
|
||||
dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_create_hcd;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
|
||||
driver->description)) {
|
||||
dev_dbg(&pdev->dev, "controller already in use\n");
|
||||
ret = -EBUSY;
|
||||
goto fail_request_resource;
|
||||
}
|
||||
|
||||
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (hcd->regs == NULL) {
|
||||
dev_dbg(&pdev->dev, "error mapping memory\n");
|
||||
ret = -ENXIO;
|
||||
goto fail_ioremap;
|
||||
}
|
||||
|
||||
priv = kmalloc(sizeof(struct ehci_sh_priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_dbg(&pdev->dev, "error allocating priv data\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail_alloc;
|
||||
}
|
||||
|
||||
/* These are optional, we don't care if they fail */
|
||||
priv->fclk = clk_get(&pdev->dev, "usb_fck");
|
||||
if (IS_ERR(priv->fclk))
|
||||
priv->fclk = NULL;
|
||||
|
||||
priv->iclk = clk_get(&pdev->dev, "usb_ick");
|
||||
if (IS_ERR(priv->iclk))
|
||||
priv->iclk = NULL;
|
||||
|
||||
clk_enable(priv->fclk);
|
||||
clk_enable(priv->iclk);
|
||||
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "Failed to add hcd");
|
||||
goto fail_add_hcd;
|
||||
}
|
||||
|
||||
priv->hcd = hcd;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
return ret;
|
||||
|
||||
fail_add_hcd:
|
||||
clk_disable(priv->iclk);
|
||||
clk_disable(priv->fclk);
|
||||
|
||||
clk_put(priv->iclk);
|
||||
clk_put(priv->fclk);
|
||||
|
||||
kfree(priv);
|
||||
fail_alloc:
|
||||
iounmap(hcd->regs);
|
||||
fail_ioremap:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
fail_request_resource:
|
||||
usb_put_hcd(hcd);
|
||||
fail_create_hcd:
|
||||
dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit ehci_hcd_sh_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = priv->hcd;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
clk_disable(priv->fclk);
|
||||
clk_disable(priv->iclk);
|
||||
|
||||
clk_put(priv->fclk);
|
||||
clk_put(priv->iclk);
|
||||
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ehci_hcd_sh_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = priv->hcd;
|
||||
|
||||
if (hcd->driver->shutdown)
|
||||
hcd->driver->shutdown(hcd);
|
||||
}
|
||||
|
||||
static struct platform_driver ehci_hcd_sh_driver = {
|
||||
.probe = ehci_hcd_sh_probe,
|
||||
.remove = __exit_p(ehci_hcd_sh_remove),
|
||||
.shutdown = ehci_hcd_sh_shutdown,
|
||||
.driver = {
|
||||
.name = "sh_ehci",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:sh_ehci");
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Driver for EHCI HCD on SPEAR SOC
|
||||
*
|
||||
* Copyright (C) 2010 ST Micro Electronics,
|
||||
* Deepak Sikri <deepak.sikri@st.com>
|
||||
*
|
||||
* Based on various ehci-*.c drivers
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
struct spear_ehci {
|
||||
struct ehci_hcd ehci;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
#define to_spear_ehci(hcd) (struct spear_ehci *)hcd_to_ehci(hcd)
|
||||
|
||||
static void spear_start_ehci(struct spear_ehci *ehci)
|
||||
{
|
||||
clk_enable(ehci->clk);
|
||||
}
|
||||
|
||||
static void spear_stop_ehci(struct spear_ehci *ehci)
|
||||
{
|
||||
clk_disable(ehci->clk);
|
||||
}
|
||||
|
||||
static int ehci_spear_setup(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int retval = 0;
|
||||
|
||||
/* registers start at offset 0x0 */
|
||||
ehci->caps = hcd->regs;
|
||||
ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci,
|
||||
&ehci->caps->hc_capbase));
|
||||
/* cache this readonly data; minimize chip reads */
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
retval = ehci_halt(ehci);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = ehci_init(hcd);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
ehci_reset(ehci);
|
||||
ehci_port_power(ehci, 0);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const struct hc_driver ehci_spear_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "SPEAr EHCI",
|
||||
.hcd_priv_size = sizeof(struct spear_ehci),
|
||||
|
||||
/* generic hardware linkage */
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2,
|
||||
|
||||
/* basic lifecycle operations */
|
||||
.reset = ehci_spear_setup,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/* managing i/o requests and associated device resources */
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/* scheduling support */
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/* root hub support */
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int spear_ehci_hcd_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd ;
|
||||
struct spear_ehci *ehci;
|
||||
struct resource *res;
|
||||
struct clk *usbh_clk;
|
||||
const struct hc_driver *driver = &ehci_spear_hc_driver;
|
||||
int *pdata = pdev->dev.platform_data;
|
||||
int irq, retval;
|
||||
char clk_name[20] = "usbh_clk";
|
||||
|
||||
if (pdata == NULL)
|
||||
return -EFAULT;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
retval = irq;
|
||||
goto fail_irq_get;
|
||||
}
|
||||
|
||||
if (*pdata >= 0)
|
||||
sprintf(clk_name, "usbh.%01d_clk", *pdata);
|
||||
|
||||
usbh_clk = clk_get(NULL, clk_name);
|
||||
if (IS_ERR(usbh_clk)) {
|
||||
dev_err(&pdev->dev, "Error getting interface clock\n");
|
||||
retval = PTR_ERR(usbh_clk);
|
||||
goto fail_get_usbh_clk;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
retval = -ENOMEM;
|
||||
goto fail_create_hcd;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
retval = -ENODEV;
|
||||
goto fail_request_resource;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
|
||||
driver->description)) {
|
||||
retval = -EBUSY;
|
||||
goto fail_request_resource;
|
||||
}
|
||||
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (hcd->regs == NULL) {
|
||||
dev_dbg(&pdev->dev, "error mapping memory\n");
|
||||
retval = -ENOMEM;
|
||||
goto fail_ioremap;
|
||||
}
|
||||
|
||||
ehci = (struct spear_ehci *)hcd_to_ehci(hcd);
|
||||
ehci->clk = usbh_clk;
|
||||
|
||||
spear_start_ehci(ehci);
|
||||
retval = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED);
|
||||
if (retval)
|
||||
goto fail_add_hcd;
|
||||
|
||||
return retval;
|
||||
|
||||
fail_add_hcd:
|
||||
spear_stop_ehci(ehci);
|
||||
iounmap(hcd->regs);
|
||||
fail_ioremap:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
fail_request_resource:
|
||||
usb_put_hcd(hcd);
|
||||
fail_create_hcd:
|
||||
clk_put(usbh_clk);
|
||||
fail_get_usbh_clk:
|
||||
fail_irq_get:
|
||||
dev_err(&pdev->dev, "init fail, %d\n", retval);
|
||||
|
||||
return retval ;
|
||||
}
|
||||
|
||||
static int spear_ehci_hcd_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
struct spear_ehci *ehci_p = to_spear_ehci(hcd);
|
||||
|
||||
if (!hcd)
|
||||
return 0;
|
||||
if (in_interrupt())
|
||||
BUG();
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
if (ehci_p->clk)
|
||||
spear_stop_ehci(ehci_p);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
if (ehci_p->clk)
|
||||
clk_put(ehci_p->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver spear_ehci_hcd_driver = {
|
||||
.probe = spear_ehci_hcd_drv_probe,
|
||||
.remove = spear_ehci_hcd_drv_remove,
|
||||
.shutdown = usb_hcd_platform_shutdown,
|
||||
.driver = {
|
||||
.name = "spear-ehci",
|
||||
.bus = &platform_bus_type
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:spear-ehci");
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* drivers/usb/host/ehci-vt8500.c
|
||||
*
|
||||
* Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
|
||||
*
|
||||
* Based on ehci-au1xxx.c
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int rc = 0;
|
||||
|
||||
if (!udev->parent) /* udev is root hub itself, impossible */
|
||||
rc = -1;
|
||||
/* we only support lpm device connected to root hub yet */
|
||||
if (ehci->has_lpm && !udev->parent->parent) {
|
||||
rc = ehci_lpm_set_da(ehci, udev->devnum, udev->portnum);
|
||||
if (!rc)
|
||||
rc = ehci_lpm_check(ehci, udev->portnum);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct hc_driver vt8500_ehci_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "VT8500 EHCI",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = ehci_init,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
/*
|
||||
* call back when device connected and addressed
|
||||
*/
|
||||
.update_device = ehci_update_device,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int vt8500_ehci_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct ehci_hcd *ehci;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
if (pdev->resource[1].flags != IORESOURCE_IRQ) {
|
||||
pr_debug("resource[1] is not IORESOURCE_IRQ");
|
||||
return -ENOMEM;
|
||||
}
|
||||
hcd = usb_create_hcd(&vt8500_ehci_hc_driver, &pdev->dev, "VT8500");
|
||||
if (!hcd)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
|
||||
pr_debug("request_mem_region failed");
|
||||
ret = -EBUSY;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (!hcd->regs) {
|
||||
pr_debug("ioremap failed");
|
||||
ret = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
ehci->caps = hcd->regs;
|
||||
ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
|
||||
|
||||
dbg_hcs_params(ehci, "reset");
|
||||
dbg_hcc_params(ehci, "reset");
|
||||
|
||||
/* cache this readonly data; minimize chip reads */
|
||||
ehci->hcs_params = readl(&ehci->caps->hcs_params);
|
||||
|
||||
ehci_port_power(ehci, 1);
|
||||
|
||||
ret = usb_add_hcd(hcd, pdev->resource[1].start,
|
||||
IRQF_DISABLED | IRQF_SHARED);
|
||||
if (ret == 0) {
|
||||
platform_set_drvdata(pdev, hcd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
iounmap(hcd->regs);
|
||||
err2:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
err1:
|
||||
usb_put_hcd(hcd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vt8500_ehci_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver vt8500_ehci_driver = {
|
||||
.probe = vt8500_ehci_drv_probe,
|
||||
.remove = vt8500_ehci_drv_remove,
|
||||
.shutdown = usb_hcd_platform_shutdown,
|
||||
.driver = {
|
||||
.name = "vt8500-ehci",
|
||||
.owner = THIS_MODULE,
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:vt8500-ehci");
|
|
@ -130,6 +130,7 @@ static const struct hc_driver ehci_w90x900_hc_driver = {
|
|||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
|
@ -147,6 +148,8 @@ static const struct hc_driver ehci_w90x900_hc_driver = {
|
|||
#endif
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int __devinit ehci_w90x900_probe(struct platform_device *pdev)
|
||||
|
|
|
@ -117,6 +117,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
|
|||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
|
|
|
@ -131,6 +131,7 @@ struct ehci_hcd { /* one per controller */
|
|||
unsigned has_amcc_usb23:1;
|
||||
unsigned need_io_watchdog:1;
|
||||
unsigned broken_periodic:1;
|
||||
unsigned amd_l1_fix:1;
|
||||
unsigned fs_i_thresh:1; /* Intel iso scheduling */
|
||||
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
|
||||
|
||||
|
|
|
@ -1081,6 +1081,11 @@ MODULE_LICENSE ("GPL");
|
|||
#define OF_PLATFORM_DRIVER ohci_hcd_ppc_of_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PLAT_SPEAR
|
||||
#include "ohci-spear.c"
|
||||
#define PLATFORM_DRIVER spear_ohci_hcd_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PPC_PS3
|
||||
#include "ohci-ps3.c"
|
||||
#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver
|
||||
|
|
|
@ -648,7 +648,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev)
|
|||
|
||||
ret = omap3_start_ohci(omap, hcd);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "failed to start ehci\n");
|
||||
dev_dbg(&pdev->dev, "failed to start ohci\n");
|
||||
goto err_start;
|
||||
}
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ static int ohci_hcd_sh_probe(struct platform_device *pdev)
|
|||
hcd->regs = (void __iomem *)res->start;
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
|
||||
if (ret != 0) {
|
||||
err("Failed to add hcd");
|
||||
usb_put_hcd(hcd);
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* OHCI HCD (Host Controller Driver) for USB.
|
||||
*
|
||||
* Copyright (C) 2010 ST Microelectronics.
|
||||
* Deepak Sikri<deepak.sikri@st.com>
|
||||
*
|
||||
* Based on various ohci-*.c drivers
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/signal.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
struct spear_ohci {
|
||||
struct ohci_hcd ohci;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
#define to_spear_ohci(hcd) (struct spear_ohci *)hcd_to_ohci(hcd)
|
||||
|
||||
static void spear_start_ohci(struct spear_ohci *ohci)
|
||||
{
|
||||
clk_enable(ohci->clk);
|
||||
}
|
||||
|
||||
static void spear_stop_ohci(struct spear_ohci *ohci)
|
||||
{
|
||||
clk_disable(ohci->clk);
|
||||
}
|
||||
|
||||
static int __devinit ohci_spear_start(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
int ret;
|
||||
|
||||
ret = ohci_init(ohci);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ohci->regs = hcd->regs;
|
||||
|
||||
ret = ohci_run(ohci);
|
||||
if (ret < 0) {
|
||||
dev_err(hcd->self.controller, "can't start\n");
|
||||
ohci_stop(hcd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
create_debug_files(ohci);
|
||||
|
||||
#ifdef DEBUG
|
||||
ohci_dump(ohci, 1);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hc_driver ohci_spear_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "SPEAr OHCI",
|
||||
.hcd_priv_size = sizeof(struct spear_ohci),
|
||||
|
||||
/* generic hardware linkage */
|
||||
.irq = ohci_irq,
|
||||
.flags = HCD_USB11 | HCD_MEMORY,
|
||||
|
||||
/* basic lifecycle operations */
|
||||
.start = ohci_spear_start,
|
||||
.stop = ohci_stop,
|
||||
.shutdown = ohci_shutdown,
|
||||
#ifdef CONFIG_PM
|
||||
.bus_suspend = ohci_bus_suspend,
|
||||
.bus_resume = ohci_bus_resume,
|
||||
#endif
|
||||
|
||||
/* managing i/o requests and associated device resources */
|
||||
.urb_enqueue = ohci_urb_enqueue,
|
||||
.urb_dequeue = ohci_urb_dequeue,
|
||||
.endpoint_disable = ohci_endpoint_disable,
|
||||
|
||||
/* scheduling support */
|
||||
.get_frame_number = ohci_get_frame,
|
||||
|
||||
/* root hub support */
|
||||
.hub_status_data = ohci_hub_status_data,
|
||||
.hub_control = ohci_hub_control,
|
||||
|
||||
.start_port_reset = ohci_start_port_reset,
|
||||
};
|
||||
|
||||
static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct hc_driver *driver = &ohci_spear_hc_driver;
|
||||
struct usb_hcd *hcd = NULL;
|
||||
struct clk *usbh_clk;
|
||||
struct spear_ohci *ohci_p;
|
||||
struct resource *res;
|
||||
int retval, irq;
|
||||
int *pdata = pdev->dev.platform_data;
|
||||
char clk_name[20] = "usbh_clk";
|
||||
|
||||
if (pdata == NULL)
|
||||
return -EFAULT;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
retval = irq;
|
||||
goto fail_irq_get;
|
||||
}
|
||||
|
||||
if (*pdata >= 0)
|
||||
sprintf(clk_name, "usbh.%01d_clk", *pdata);
|
||||
|
||||
usbh_clk = clk_get(NULL, clk_name);
|
||||
if (IS_ERR(usbh_clk)) {
|
||||
dev_err(&pdev->dev, "Error getting interface clock\n");
|
||||
retval = PTR_ERR(usbh_clk);
|
||||
goto fail_get_usbh_clk;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
retval = -ENOMEM;
|
||||
goto fail_create_hcd;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
retval = -ENODEV;
|
||||
goto fail_request_resource;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = pdev->resource[0].start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
|
||||
dev_dbg(&pdev->dev, "request_mem_region failed\n");
|
||||
retval = -EBUSY;
|
||||
goto fail_request_resource;
|
||||
}
|
||||
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (!hcd->regs) {
|
||||
dev_dbg(&pdev->dev, "ioremap failed\n");
|
||||
retval = -ENOMEM;
|
||||
goto fail_ioremap;
|
||||
}
|
||||
|
||||
ohci_p = (struct spear_ohci *)hcd_to_ohci(hcd);
|
||||
ohci_p->clk = usbh_clk;
|
||||
spear_start_ohci(ohci_p);
|
||||
ohci_hcd_init(hcd_to_ohci(hcd));
|
||||
|
||||
retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), IRQF_DISABLED);
|
||||
if (retval == 0)
|
||||
return retval;
|
||||
|
||||
spear_stop_ohci(ohci_p);
|
||||
iounmap(hcd->regs);
|
||||
fail_ioremap:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
fail_request_resource:
|
||||
usb_put_hcd(hcd);
|
||||
fail_create_hcd:
|
||||
clk_put(usbh_clk);
|
||||
fail_get_usbh_clk:
|
||||
fail_irq_get:
|
||||
dev_err(&pdev->dev, "init fail, %d\n", retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int spear_ohci_hcd_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
struct spear_ohci *ohci_p = to_spear_ohci(hcd);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
if (ohci_p->clk)
|
||||
spear_stop_ohci(ohci_p);
|
||||
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
if (ohci_p->clk)
|
||||
clk_put(ohci_p->clk);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PM)
|
||||
static int spear_ohci_hcd_drv_suspend(struct platform_device *dev,
|
||||
pm_message_t message)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
struct spear_ohci *ohci_p = to_spear_ohci(hcd);
|
||||
|
||||
if (time_before(jiffies, ohci->next_statechange))
|
||||
msleep(5);
|
||||
ohci->next_statechange = jiffies;
|
||||
|
||||
spear_stop_ohci(ohci_p);
|
||||
ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spear_ohci_hcd_drv_resume(struct platform_device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
struct spear_ohci *ohci_p = to_spear_ohci(hcd);
|
||||
|
||||
if (time_before(jiffies, ohci->next_statechange))
|
||||
msleep(5);
|
||||
ohci->next_statechange = jiffies;
|
||||
|
||||
spear_start_ohci(ohci_p);
|
||||
ohci_finish_controller_resume(hcd);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Driver definition to register with the platform bus */
|
||||
static struct platform_driver spear_ohci_hcd_driver = {
|
||||
.probe = spear_ohci_hcd_drv_probe,
|
||||
.remove = spear_ohci_hcd_drv_remove,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = spear_ohci_hcd_drv_suspend,
|
||||
.resume = spear_ohci_hcd_drv_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "spear-ohci",
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:spear-ohci");
|
|
@ -569,7 +569,7 @@ static int uhci_init(struct usb_hcd *hcd)
|
|||
*/
|
||||
static void uhci_shutdown(struct pci_dev *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = (struct usb_hcd *) pci_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = pci_get_drvdata(pdev);
|
||||
|
||||
uhci_hc_died(hcd_to_uhci(hcd));
|
||||
}
|
||||
|
|
|
@ -195,7 +195,9 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci,
|
|||
} else {
|
||||
struct uhci_td *ntd;
|
||||
|
||||
ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
|
||||
ntd = list_entry(td->fl_list.next,
|
||||
struct uhci_td,
|
||||
fl_list);
|
||||
uhci->frame[td->frame] = LINK_TO_TD(ntd);
|
||||
uhci->frame_cpu[td->frame] = ntd;
|
||||
}
|
||||
|
|
|
@ -356,7 +356,7 @@ static void __exit whci_hc_driver_exit(void)
|
|||
module_exit(whci_hc_driver_exit);
|
||||
|
||||
/* PCI device ID's that we handle (so it gets loaded) */
|
||||
static struct pci_device_id whci_hcd_id_table[] = {
|
||||
static struct pci_device_id __used whci_hcd_id_table[] = {
|
||||
{ PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) },
|
||||
{ /* empty last entry */ }
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* USB LED driver - 1.1
|
||||
* USB LED driver
|
||||
*
|
||||
* Copyright (C) 2004 Greg Kroah-Hartman (greg@kroah.com)
|
||||
*
|
||||
|
@ -20,12 +20,17 @@
|
|||
#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com"
|
||||
#define DRIVER_DESC "USB LED Driver"
|
||||
|
||||
#define VENDOR_ID 0x0fc5
|
||||
#define PRODUCT_ID 0x1223
|
||||
enum led_type {
|
||||
DELCOM_VISUAL_SIGNAL_INDICATOR,
|
||||
DREAM_CHEEKY_WEBMAIL_NOTIFIER,
|
||||
};
|
||||
|
||||
/* table of devices that work with this driver */
|
||||
static const struct usb_device_id id_table[] = {
|
||||
{ USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
|
||||
{ USB_DEVICE(0x0fc5, 0x1223),
|
||||
.driver_info = DELCOM_VISUAL_SIGNAL_INDICATOR },
|
||||
{ USB_DEVICE(0x1d34, 0x0004),
|
||||
.driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE (usb, id_table);
|
||||
|
@ -35,15 +40,12 @@ struct usb_led {
|
|||
unsigned char blue;
|
||||
unsigned char red;
|
||||
unsigned char green;
|
||||
enum led_type type;
|
||||
};
|
||||
|
||||
#define BLUE 0x04
|
||||
#define RED 0x02
|
||||
#define GREEN 0x01
|
||||
static void change_color(struct usb_led *led)
|
||||
{
|
||||
int retval;
|
||||
unsigned char color = 0x07;
|
||||
unsigned char *buffer;
|
||||
|
||||
buffer = kmalloc(8, GFP_KERNEL);
|
||||
|
@ -52,12 +54,16 @@ static void change_color(struct usb_led *led)
|
|||
return;
|
||||
}
|
||||
|
||||
switch (led->type) {
|
||||
case DELCOM_VISUAL_SIGNAL_INDICATOR: {
|
||||
unsigned char color = 0x07;
|
||||
|
||||
if (led->blue)
|
||||
color &= ~(BLUE);
|
||||
color &= ~0x04;
|
||||
if (led->red)
|
||||
color &= ~(RED);
|
||||
color &= ~0x02;
|
||||
if (led->green)
|
||||
color &= ~(GREEN);
|
||||
color &= ~0x01;
|
||||
dev_dbg(&led->udev->dev,
|
||||
"blue = %d, red = %d, green = %d, color = %.2x\n",
|
||||
led->blue, led->red, led->green, color);
|
||||
|
@ -71,6 +77,36 @@ static void change_color(struct usb_led *led)
|
|||
buffer,
|
||||
8,
|
||||
2000);
|
||||
break;
|
||||
}
|
||||
|
||||
case DREAM_CHEEKY_WEBMAIL_NOTIFIER:
|
||||
dev_dbg(&led->udev->dev,
|
||||
"red = %d, green = %d, blue = %d\n",
|
||||
led->red, led->green, led->blue);
|
||||
|
||||
buffer[0] = led->red;
|
||||
buffer[1] = led->green;
|
||||
buffer[2] = led->blue;
|
||||
buffer[3] = buffer[4] = buffer[5] = 0;
|
||||
buffer[6] = 0x1a;
|
||||
buffer[7] = 0x05;
|
||||
|
||||
retval = usb_control_msg(led->udev,
|
||||
usb_sndctrlpipe(led->udev, 0),
|
||||
0x09,
|
||||
0x21,
|
||||
0x200,
|
||||
0,
|
||||
buffer,
|
||||
8,
|
||||
2000);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&led->udev->dev, "unknown device type %d\n", led->type);
|
||||
}
|
||||
|
||||
if (retval)
|
||||
dev_dbg(&led->udev->dev, "retval = %d\n", retval);
|
||||
kfree(buffer);
|
||||
|
@ -107,11 +143,12 @@ static int led_probe(struct usb_interface *interface, const struct usb_device_id
|
|||
|
||||
dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
|
||||
if (dev == NULL) {
|
||||
dev_err(&interface->dev, "Out of memory\n");
|
||||
dev_err(&interface->dev, "out of memory\n");
|
||||
goto error_mem;
|
||||
}
|
||||
|
||||
dev->udev = usb_get_dev(udev);
|
||||
dev->type = id->driver_info;
|
||||
|
||||
usb_set_intfdata (interface, dev);
|
||||
|
||||
|
@ -125,6 +162,31 @@ static int led_probe(struct usb_interface *interface, const struct usb_device_id
|
|||
if (retval)
|
||||
goto error;
|
||||
|
||||
if (dev->type == DREAM_CHEEKY_WEBMAIL_NOTIFIER) {
|
||||
unsigned char *enable;
|
||||
|
||||
enable = kmemdup("\x1f\x02\0\x5f\0\0\x1a\x03", 8, GFP_KERNEL);
|
||||
if (!enable) {
|
||||
dev_err(&interface->dev, "out of memory\n");
|
||||
retval = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
retval = usb_control_msg(udev,
|
||||
usb_sndctrlpipe(udev, 0),
|
||||
0x09,
|
||||
0x21,
|
||||
0x200,
|
||||
0,
|
||||
enable,
|
||||
8,
|
||||
2000);
|
||||
|
||||
kfree(enable);
|
||||
if (retval != 8)
|
||||
goto error;
|
||||
}
|
||||
|
||||
dev_info(&interface->dev, "USB LED device now attached\n");
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -436,6 +436,28 @@ static unsigned int mon_bin_get_data(const struct mon_reader_bin *rp,
|
|||
return length;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the look-ahead pass in case of 'C Zi', when actual_length cannot
|
||||
* be used to determine the length of the whole contiguous buffer.
|
||||
*/
|
||||
static unsigned int mon_bin_collate_isodesc(const struct mon_reader_bin *rp,
|
||||
struct urb *urb, unsigned int ndesc)
|
||||
{
|
||||
struct usb_iso_packet_descriptor *fp;
|
||||
unsigned int length;
|
||||
|
||||
length = 0;
|
||||
fp = urb->iso_frame_desc;
|
||||
while (ndesc-- != 0) {
|
||||
if (fp->actual_length != 0) {
|
||||
if (fp->offset + fp->actual_length > length)
|
||||
length = fp->offset + fp->actual_length;
|
||||
}
|
||||
fp++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static void mon_bin_get_isodesc(const struct mon_reader_bin *rp,
|
||||
unsigned int offset, struct urb *urb, char ev_type, unsigned int ndesc)
|
||||
{
|
||||
|
@ -478,6 +500,10 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
|
|||
/*
|
||||
* Find the maximum allowable length, then allocate space.
|
||||
*/
|
||||
urb_length = (ev_type == 'S') ?
|
||||
urb->transfer_buffer_length : urb->actual_length;
|
||||
length = urb_length;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(epd)) {
|
||||
if (urb->number_of_packets < 0) {
|
||||
ndesc = 0;
|
||||
|
@ -486,14 +512,16 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
|
|||
} else {
|
||||
ndesc = urb->number_of_packets;
|
||||
}
|
||||
if (ev_type == 'C' && usb_urb_dir_in(urb))
|
||||
length = mon_bin_collate_isodesc(rp, urb, ndesc);
|
||||
} else {
|
||||
ndesc = 0;
|
||||
}
|
||||
lendesc = ndesc*sizeof(struct mon_bin_isodesc);
|
||||
|
||||
urb_length = (ev_type == 'S') ?
|
||||
urb->transfer_buffer_length : urb->actual_length;
|
||||
length = urb_length;
|
||||
/* not an issue unless there's a subtle bug in a HCD somewhere */
|
||||
if (length >= urb->transfer_buffer_length)
|
||||
length = urb->transfer_buffer_length;
|
||||
|
||||
if (length >= rp->b_size/5)
|
||||
length = rp->b_size/5;
|
||||
|
|
|
@ -12,6 +12,7 @@ config USB_MUSB_HDRC
|
|||
depends on (ARM || (BF54x && !BF544) || (BF52x && !BF522 && !BF523))
|
||||
select NOP_USB_XCEIV if (ARCH_DAVINCI || MACH_OMAP3EVM || BLACKFIN)
|
||||
select TWL4030_USB if MACH_OMAP_3430SDP
|
||||
select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
|
||||
select USB_OTG_UTILS
|
||||
tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
|
||||
help
|
||||
|
@ -30,57 +31,41 @@ config USB_MUSB_HDRC
|
|||
If you do not know what this is, please say N.
|
||||
|
||||
To compile this driver as a module, choose M here; the
|
||||
module will be called "musb_hdrc".
|
||||
module will be called "musb-hdrc".
|
||||
|
||||
config USB_MUSB_SOC
|
||||
boolean
|
||||
choice
|
||||
prompt "Platform Glue Layer"
|
||||
depends on USB_MUSB_HDRC
|
||||
default y if ARCH_DAVINCI
|
||||
default y if ARCH_OMAP2430
|
||||
default y if ARCH_OMAP3
|
||||
default y if ARCH_OMAP4
|
||||
default y if (BF54x && !BF544)
|
||||
default y if (BF52x && !BF522 && !BF523)
|
||||
|
||||
comment "DaVinci 35x and 644x USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_DAVINCI_DMx
|
||||
config USB_MUSB_DAVINCI
|
||||
bool "DaVinci"
|
||||
depends on ARCH_DAVINCI_DMx
|
||||
|
||||
comment "DA8xx/OMAP-L1x USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_DAVINCI_DA8XX
|
||||
config USB_MUSB_DA8XX
|
||||
bool "DA8xx/OMAP-L1x"
|
||||
depends on ARCH_DAVINCI_DA8XX
|
||||
|
||||
comment "OMAP 243x high speed USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_OMAP2430
|
||||
config USB_MUSB_TUSB6010
|
||||
bool "TUSB6010"
|
||||
depends on ARCH_OMAP
|
||||
|
||||
comment "OMAP 343x high speed USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_OMAP3
|
||||
|
||||
comment "OMAP 44xx high speed USB support"
|
||||
depends on USB_MUSB_HDRC && ARCH_OMAP4
|
||||
|
||||
comment "Blackfin high speed USB Support"
|
||||
depends on USB_MUSB_HDRC && ((BF54x && !BF544) || (BF52x && !BF522 && !BF523))
|
||||
config USB_MUSB_OMAP2PLUS
|
||||
bool "OMAP2430 and onwards"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
|
||||
config USB_MUSB_AM35X
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && !ARCH_OMAP2430 && !ARCH_OMAP4
|
||||
select NOP_USB_XCEIV
|
||||
default MACH_OMAP3517EVM
|
||||
help
|
||||
Select this option if your platform is based on AM35x. As
|
||||
AM35x has an updated MUSB with CPPI4.1 DMA so this config
|
||||
is introduced to differentiate musb ip between OMAP3x and
|
||||
AM35x platforms.
|
||||
bool "AM35x"
|
||||
depends on ARCH_OMAP
|
||||
|
||||
config USB_TUSB6010
|
||||
boolean "TUSB 6010 support"
|
||||
depends on USB_MUSB_HDRC && !USB_MUSB_SOC
|
||||
select NOP_USB_XCEIV
|
||||
default y
|
||||
help
|
||||
The TUSB 6010 chip, from Texas Instruments, connects a discrete
|
||||
HDRC core using a 16-bit parallel bus (NOR flash style) or VLYNQ
|
||||
(a high speed serial link). It can use system-specific external
|
||||
DMA controllers.
|
||||
config USB_MUSB_BLACKFIN
|
||||
bool "Blackfin"
|
||||
depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523)
|
||||
|
||||
config USB_MUSB_UX500
|
||||
bool "U8500 and U5500"
|
||||
depends on (ARCH_U8500 && AB8500_USB) || (ARCH_U5500)
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Driver Mode"
|
||||
|
@ -158,7 +143,7 @@ config USB_MUSB_HDRC_HCD
|
|||
config MUSB_PIO_ONLY
|
||||
bool 'Disable DMA (always use PIO)'
|
||||
depends on USB_MUSB_HDRC
|
||||
default USB_TUSB6010 || ARCH_DAVINCI_DA8XX || USB_MUSB_AM35X
|
||||
default USB_MUSB_TUSB6010 || USB_MUSB_DA8XX || USB_MUSB_AM35X
|
||||
help
|
||||
All data is copied between memory and FIFO by the CPU.
|
||||
DMA controllers are ignored.
|
||||
|
@ -171,21 +156,21 @@ config MUSB_PIO_ONLY
|
|||
config USB_INVENTRA_DMA
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
|
||||
default ARCH_OMAP2430 || ARCH_OMAP3 || BLACKFIN || ARCH_OMAP4
|
||||
default USB_MUSB_OMAP2PLUS || USB_MUSB_BLACKFIN
|
||||
help
|
||||
Enable DMA transfers using Mentor's engine.
|
||||
|
||||
config USB_TI_CPPI_DMA
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
|
||||
default ARCH_DAVINCI
|
||||
default USB_MUSB_DAVINCI
|
||||
help
|
||||
Enable DMA transfers when TI CPPI DMA is available.
|
||||
|
||||
config USB_TUSB_OMAP_DMA
|
||||
bool
|
||||
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
|
||||
depends on USB_TUSB6010
|
||||
depends on USB_MUSB_TUSB6010
|
||||
depends on ARCH_OMAP
|
||||
default y
|
||||
help
|
||||
|
|
|
@ -8,22 +8,19 @@ obj-$(CONFIG_USB_MUSB_HDRC) += musb_hdrc.o
|
|||
|
||||
musb_hdrc-y := musb_core.o
|
||||
|
||||
musb_hdrc-$(CONFIG_ARCH_DAVINCI_DMx) += davinci.o
|
||||
musb_hdrc-$(CONFIG_ARCH_DAVINCI_DA8XX) += da8xx.o
|
||||
musb_hdrc-$(CONFIG_USB_TUSB6010) += tusb6010.o
|
||||
musb_hdrc-$(CONFIG_ARCH_OMAP2430) += omap2430.o
|
||||
ifeq ($(CONFIG_USB_MUSB_AM35X),y)
|
||||
musb_hdrc-$(CONFIG_ARCH_OMAP3430) += am35x.o
|
||||
else
|
||||
musb_hdrc-$(CONFIG_ARCH_OMAP3430) += omap2430.o
|
||||
endif
|
||||
musb_hdrc-$(CONFIG_ARCH_OMAP4) += omap2430.o
|
||||
musb_hdrc-$(CONFIG_BF54x) += blackfin.o
|
||||
musb_hdrc-$(CONFIG_BF52x) += blackfin.o
|
||||
musb_hdrc-$(CONFIG_USB_GADGET_MUSB_HDRC) += musb_gadget_ep0.o musb_gadget.o
|
||||
musb_hdrc-$(CONFIG_USB_MUSB_HDRC_HCD) += musb_virthub.o musb_host.o
|
||||
musb_hdrc-$(CONFIG_DEBUG_FS) += musb_debugfs.o
|
||||
|
||||
# Hardware Glue Layer
|
||||
obj-$(CONFIG_USB_MUSB_OMAP2PLUS) += omap2430.o
|
||||
obj-$(CONFIG_USB_MUSB_AM35X) += am35x.o
|
||||
obj-$(CONFIG_USB_MUSB_TUSB6010) += tusb6010.o
|
||||
obj-$(CONFIG_USB_MUSB_DAVINCI) += davinci.o
|
||||
obj-$(CONFIG_USB_MUSB_DA8XX) += da8xx.o
|
||||
obj-$(CONFIG_USB_MUSB_BLACKFIN) += blackfin.o
|
||||
obj-$(CONFIG_USB_MUSB_UX500) += ux500.o
|
||||
|
||||
# the kconfig must guarantee that only one of the
|
||||
# possible I/O schemes will be enabled at a time ...
|
||||
# PIO only, or DMA (several potential schemes).
|
||||
|
|
|
@ -29,8 +29,9 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <plat/control.h>
|
||||
#include <plat/usb.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
|
@ -80,51 +81,18 @@
|
|||
|
||||
#define USB_MENTOR_CORE_OFFSET 0x400
|
||||
|
||||
static inline void phy_on(void)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(100);
|
||||
u32 devconf2;
|
||||
|
||||
/*
|
||||
* Start the on-chip PHY and its PLL.
|
||||
*/
|
||||
devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
|
||||
|
||||
devconf2 &= ~(CONF2_RESET | CONF2_PHYPWRDN | CONF2_OTGPWRDN);
|
||||
devconf2 |= CONF2_PHY_PLLON;
|
||||
|
||||
omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
|
||||
|
||||
DBG(1, "Waiting for PHY clock good...\n");
|
||||
while (!(omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2)
|
||||
& CONF2_PHYCLKGD)) {
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
DBG(1, "musb PHY clock good timed out\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void phy_off(void)
|
||||
{
|
||||
u32 devconf2;
|
||||
|
||||
/*
|
||||
* Power down the on-chip PHY.
|
||||
*/
|
||||
devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
|
||||
|
||||
devconf2 &= ~CONF2_PHY_PLLON;
|
||||
devconf2 |= CONF2_PHYPWRDN | CONF2_OTGPWRDN;
|
||||
omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
|
||||
}
|
||||
struct am35x_glue {
|
||||
struct device *dev;
|
||||
struct platform_device *musb;
|
||||
struct clk *phy_clk;
|
||||
struct clk *clk;
|
||||
};
|
||||
#define glue_to_musb(g) platform_get_drvdata(g->musb)
|
||||
|
||||
/*
|
||||
* musb_platform_enable - enable interrupts
|
||||
* am35x_musb_enable - enable interrupts
|
||||
*/
|
||||
void musb_platform_enable(struct musb *musb)
|
||||
static void am35x_musb_enable(struct musb *musb)
|
||||
{
|
||||
void __iomem *reg_base = musb->ctrl_base;
|
||||
u32 epmask;
|
||||
|
@ -143,9 +111,9 @@ void musb_platform_enable(struct musb *musb)
|
|||
}
|
||||
|
||||
/*
|
||||
* musb_platform_disable - disable HDRC and flush interrupts
|
||||
* am35x_musb_disable - disable HDRC and flush interrupts
|
||||
*/
|
||||
void musb_platform_disable(struct musb *musb)
|
||||
static void am35x_musb_disable(struct musb *musb)
|
||||
{
|
||||
void __iomem *reg_base = musb->ctrl_base;
|
||||
|
||||
|
@ -162,7 +130,7 @@ void musb_platform_disable(struct musb *musb)
|
|||
#define portstate(stmt)
|
||||
#endif
|
||||
|
||||
static void am35x_set_vbus(struct musb *musb, int is_on)
|
||||
static void am35x_musb_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
WARN_ON(is_on && is_peripheral_active(musb));
|
||||
}
|
||||
|
@ -221,7 +189,7 @@ static void otg_timer(unsigned long _musb)
|
|||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
}
|
||||
|
||||
void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout)
|
||||
{
|
||||
static unsigned long last_timer;
|
||||
|
||||
|
@ -251,13 +219,16 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
|||
mod_timer(&otg_workaround, timeout);
|
||||
}
|
||||
|
||||
static irqreturn_t am35x_interrupt(int irq, void *hci)
|
||||
static irqreturn_t am35x_musb_interrupt(int irq, void *hci)
|
||||
{
|
||||
struct musb *musb = hci;
|
||||
void __iomem *reg_base = musb->ctrl_base;
|
||||
struct device *dev = musb->controller;
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct omap_musb_board_data *data = plat->board_data;
|
||||
unsigned long flags;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 epintr, usbintr, lvl_intr;
|
||||
u32 epintr, usbintr;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
|
@ -346,9 +317,8 @@ static irqreturn_t am35x_interrupt(int irq, void *hci)
|
|||
/* EOI needs to be written for the IRQ to be re-asserted. */
|
||||
if (ret == IRQ_HANDLED || epintr || usbintr) {
|
||||
/* clear level interrupt */
|
||||
lvl_intr = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR);
|
||||
lvl_intr |= AM35XX_USBOTGSS_INT_CLR;
|
||||
omap_ctrl_writel(lvl_intr, AM35XX_CONTROL_LVL_INTR_CLEAR);
|
||||
if (data->clear_irq)
|
||||
data->clear_irq();
|
||||
/* write EOI */
|
||||
musb_writel(reg_base, USB_END_OF_INTR_REG, 0);
|
||||
}
|
||||
|
@ -362,137 +332,85 @@ static irqreturn_t am35x_interrupt(int irq, void *hci)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
static int am35x_musb_set_mode(struct musb *musb, u8 musb_mode)
|
||||
{
|
||||
u32 devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
|
||||
struct device *dev = musb->controller;
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct omap_musb_board_data *data = plat->board_data;
|
||||
int retval = 0;
|
||||
|
||||
devconf2 &= ~CONF2_OTGMODE;
|
||||
switch (musb_mode) {
|
||||
#ifdef CONFIG_USB_MUSB_HDRC_HCD
|
||||
case MUSB_HOST: /* Force VBUS valid, ID = 0 */
|
||||
devconf2 |= CONF2_FORCE_HOST;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
|
||||
case MUSB_PERIPHERAL: /* Force VBUS valid, ID = 1 */
|
||||
devconf2 |= CONF2_FORCE_DEVICE;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_USB_MUSB_OTG
|
||||
case MUSB_OTG: /* Don't override the VBUS/ID comparators */
|
||||
devconf2 |= CONF2_NO_OVERRIDE;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
DBG(2, "Trying to set unsupported mode %u\n", musb_mode);
|
||||
}
|
||||
if (data->set_mode)
|
||||
data->set_mode(musb_mode);
|
||||
else
|
||||
retval = -EIO;
|
||||
|
||||
omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
|
||||
return 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
static int am35x_musb_init(struct musb *musb)
|
||||
{
|
||||
struct device *dev = musb->controller;
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct omap_musb_board_data *data = plat->board_data;
|
||||
void __iomem *reg_base = musb->ctrl_base;
|
||||
u32 rev, lvl_intr, sw_reset;
|
||||
int status;
|
||||
u32 rev;
|
||||
|
||||
musb->mregs += USB_MENTOR_CORE_OFFSET;
|
||||
|
||||
clk_enable(musb->clock);
|
||||
DBG(2, "musb->clock=%lud\n", clk_get_rate(musb->clock));
|
||||
|
||||
musb->phy_clock = clk_get(musb->controller, "fck");
|
||||
if (IS_ERR(musb->phy_clock)) {
|
||||
status = PTR_ERR(musb->phy_clock);
|
||||
goto exit0;
|
||||
}
|
||||
clk_enable(musb->phy_clock);
|
||||
DBG(2, "musb->phy_clock=%lud\n", clk_get_rate(musb->phy_clock));
|
||||
|
||||
/* Returns zero if e.g. not clocked */
|
||||
rev = musb_readl(reg_base, USB_REVISION_REG);
|
||||
if (!rev) {
|
||||
status = -ENODEV;
|
||||
goto exit1;
|
||||
}
|
||||
if (!rev)
|
||||
return -ENODEV;
|
||||
|
||||
usb_nop_xceiv_register();
|
||||
musb->xceiv = otg_get_transceiver();
|
||||
if (!musb->xceiv) {
|
||||
status = -ENODEV;
|
||||
goto exit1;
|
||||
}
|
||||
if (!musb->xceiv)
|
||||
return -ENODEV;
|
||||
|
||||
if (is_host_enabled(musb))
|
||||
setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
|
||||
|
||||
musb->board_set_vbus = am35x_set_vbus;
|
||||
|
||||
/* Global reset */
|
||||
sw_reset = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET);
|
||||
|
||||
sw_reset |= AM35XX_USBOTGSS_SW_RST;
|
||||
omap_ctrl_writel(sw_reset, AM35XX_CONTROL_IP_SW_RESET);
|
||||
|
||||
sw_reset &= ~AM35XX_USBOTGSS_SW_RST;
|
||||
omap_ctrl_writel(sw_reset, AM35XX_CONTROL_IP_SW_RESET);
|
||||
/* Reset the musb */
|
||||
if (data->reset)
|
||||
data->reset();
|
||||
|
||||
/* Reset the controller */
|
||||
musb_writel(reg_base, USB_CTRL_REG, AM35X_SOFT_RESET_MASK);
|
||||
|
||||
/* Start the on-chip PHY and its PLL. */
|
||||
phy_on();
|
||||
if (data->set_phy_power)
|
||||
data->set_phy_power(1);
|
||||
|
||||
msleep(5);
|
||||
|
||||
musb->isr = am35x_interrupt;
|
||||
musb->isr = am35x_musb_interrupt;
|
||||
|
||||
/* clear level interrupt */
|
||||
lvl_intr = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR);
|
||||
lvl_intr |= AM35XX_USBOTGSS_INT_CLR;
|
||||
omap_ctrl_writel(lvl_intr, AM35XX_CONTROL_LVL_INTR_CLEAR);
|
||||
if (data->clear_irq)
|
||||
data->clear_irq();
|
||||
|
||||
return 0;
|
||||
exit1:
|
||||
clk_disable(musb->phy_clock);
|
||||
clk_put(musb->phy_clock);
|
||||
exit0:
|
||||
clk_disable(musb->clock);
|
||||
return status;
|
||||
}
|
||||
|
||||
int musb_platform_exit(struct musb *musb)
|
||||
static int am35x_musb_exit(struct musb *musb)
|
||||
{
|
||||
struct device *dev = musb->controller;
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct omap_musb_board_data *data = plat->board_data;
|
||||
|
||||
if (is_host_enabled(musb))
|
||||
del_timer_sync(&otg_workaround);
|
||||
|
||||
phy_off();
|
||||
/* Shutdown the on-chip PHY and its PLL. */
|
||||
if (data->set_phy_power)
|
||||
data->set_phy_power(0);
|
||||
|
||||
otg_put_transceiver(musb->xceiv);
|
||||
usb_nop_xceiv_unregister();
|
||||
|
||||
clk_disable(musb->clock);
|
||||
|
||||
clk_disable(musb->phy_clock);
|
||||
clk_put(musb->phy_clock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
void musb_platform_save_context(struct musb *musb,
|
||||
struct musb_context_registers *musb_context)
|
||||
{
|
||||
phy_off();
|
||||
}
|
||||
|
||||
void musb_platform_restore_context(struct musb *musb,
|
||||
struct musb_context_registers *musb_context)
|
||||
{
|
||||
phy_on();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* AM35x supports only 32bit read operation */
|
||||
void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
|
||||
{
|
||||
|
@ -522,3 +440,215 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
|
|||
memcpy(dst, &val, len);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct musb_platform_ops am35x_ops = {
|
||||
.init = am35x_musb_init,
|
||||
.exit = am35x_musb_exit,
|
||||
|
||||
.enable = am35x_musb_enable,
|
||||
.disable = am35x_musb_disable,
|
||||
|
||||
.set_mode = am35x_musb_set_mode,
|
||||
.try_idle = am35x_musb_try_idle,
|
||||
|
||||
.set_vbus = am35x_musb_set_vbus,
|
||||
};
|
||||
|
||||
static u64 am35x_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int __init am35x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct am35x_glue *glue;
|
||||
|
||||
struct clk *phy_clk;
|
||||
struct clk *clk;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
||||
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue) {
|
||||
dev_err(&pdev->dev, "failed to allocate glue context\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
musb = platform_device_alloc("musb-hdrc", -1);
|
||||
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");
|
||||
ret = PTR_ERR(phy_clk);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
clk = clk_get(&pdev->dev, "ick");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock\n");
|
||||
ret = PTR_ERR(clk);
|
||||
goto err3;
|
||||
}
|
||||
|
||||
ret = clk_enable(phy_clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable PHY clock\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = clk_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable clock\n");
|
||||
goto err5;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
pdata->platform_ops = &am35x_ops;
|
||||
|
||||
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 err6;
|
||||
}
|
||||
|
||||
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add platform_data\n");
|
||||
goto err6;
|
||||
}
|
||||
|
||||
ret = platform_device_add(musb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
goto err6;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err6:
|
||||
clk_disable(clk);
|
||||
|
||||
err5:
|
||||
clk_disable(phy_clk);
|
||||
|
||||
err4:
|
||||
clk_put(clk);
|
||||
|
||||
err3:
|
||||
clk_put(phy_clk);
|
||||
|
||||
err2:
|
||||
platform_device_put(musb);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit am35x_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct am35x_glue *glue = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_del(glue->musb);
|
||||
platform_device_put(glue->musb);
|
||||
clk_disable(glue->clk);
|
||||
clk_disable(glue->phy_clk);
|
||||
clk_put(glue->clk);
|
||||
clk_put(glue->phy_clk);
|
||||
kfree(glue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int am35x_suspend(struct device *dev)
|
||||
{
|
||||
struct am35x_glue *glue = dev_get_drvdata(dev);
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct omap_musb_board_data *data = plat->board_data;
|
||||
|
||||
/* Shutdown the on-chip PHY and its PLL. */
|
||||
if (data->set_phy_power)
|
||||
data->set_phy_power(0);
|
||||
|
||||
clk_disable(glue->phy_clk);
|
||||
clk_disable(glue->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int am35x_resume(struct device *dev)
|
||||
{
|
||||
struct am35x_glue *glue = dev_get_drvdata(dev);
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct omap_musb_board_data *data = plat->board_data;
|
||||
int ret;
|
||||
|
||||
/* Start the on-chip PHY and its PLL. */
|
||||
if (data->set_phy_power)
|
||||
data->set_phy_power(1);
|
||||
|
||||
ret = clk_enable(glue->phy_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable PHY clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_enable(glue->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 struct platform_driver am35x_driver = {
|
||||
.remove = __exit_p(am35x_remove),
|
||||
.driver = {
|
||||
.name = "musb-am35x",
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION("AM35x MUSB Glue Layer");
|
||||
MODULE_AUTHOR("Ajay Kumar Gupta <ajay.gupta@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static int __init am35x_init(void)
|
||||
{
|
||||
return platform_driver_probe(&am35x_driver, am35x_probe);
|
||||
}
|
||||
subsys_initcall(am35x_init);
|
||||
|
||||
static void __exit am35x_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&am35x_driver);
|
||||
}
|
||||
module_exit(am35x_exit);
|
||||
|
|
|
@ -15,12 +15,20 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
#include "blackfin.h"
|
||||
|
||||
struct bfin_glue {
|
||||
struct device *dev;
|
||||
struct platform_device *musb;
|
||||
};
|
||||
#define glue_to_musb(g) platform_get_drvdata(g->musb)
|
||||
|
||||
/*
|
||||
* Load an endpoint's FIFO
|
||||
*/
|
||||
|
@ -278,7 +286,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
|
|||
DBG(4, "state is %s\n", otg_state_string(musb));
|
||||
}
|
||||
|
||||
void musb_platform_enable(struct musb *musb)
|
||||
static void bfin_musb_enable(struct musb *musb)
|
||||
{
|
||||
if (!is_otg_enabled(musb) && is_host_enabled(musb)) {
|
||||
mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
|
||||
|
@ -286,11 +294,11 @@ void musb_platform_enable(struct musb *musb)
|
|||
}
|
||||
}
|
||||
|
||||
void musb_platform_disable(struct musb *musb)
|
||||
static void bfin_musb_disable(struct musb *musb)
|
||||
{
|
||||
}
|
||||
|
||||
static void bfin_set_vbus(struct musb *musb, int is_on)
|
||||
static void bfin_musb_set_vbus(struct musb *musb, int is_on)
|
||||
{
|
||||
int value = musb->config->gpio_vrsel_active;
|
||||
if (!is_on)
|
||||
|
@ -303,28 +311,28 @@ static void bfin_set_vbus(struct musb *musb, int is_on)
|
|||
musb_readb(musb->mregs, MUSB_DEVCTL));
|
||||
}
|
||||
|
||||
static int bfin_set_power(struct otg_transceiver *x, unsigned mA)
|
||||
static int bfin_musb_set_power(struct otg_transceiver *x, unsigned mA)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
|
||||
static void bfin_musb_try_idle(struct musb *musb, unsigned long timeout)
|
||||
{
|
||||
if (!is_otg_enabled(musb) && is_host_enabled(musb))
|
||||
mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
|
||||
}
|
||||
|
||||
int musb_platform_get_vbus_status(struct musb *musb)
|
||||
static int bfin_musb_get_vbus_status(struct musb *musb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
|
||||
static int bfin_musb_set_mode(struct musb *musb, u8 musb_mode)
|
||||
{
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void musb_platform_reg_init(struct musb *musb)
|
||||
static void bfin_musb_reg_init(struct musb *musb)
|
||||
{
|
||||
if (ANOMALY_05000346) {
|
||||
bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value);
|
||||
|
@ -362,7 +370,7 @@ static void musb_platform_reg_init(struct musb *musb)
|
|||
SSYNC();
|
||||
}
|
||||
|
||||
int __init musb_platform_init(struct musb *musb, void *board_data)
|
||||
static int bfin_musb_init(struct musb *musb)
|
||||
{
|
||||
|
||||
/*
|
||||
|
@ -386,25 +394,124 @@ int __init musb_platform_init(struct musb *musb, void *board_data)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
musb_platform_reg_init(musb);
|
||||
bfin_musb_reg_init(musb);
|
||||
|
||||
if (is_host_enabled(musb)) {
|
||||
musb->board_set_vbus = bfin_set_vbus;
|
||||
setup_timer(&musb_conn_timer,
|
||||
musb_conn_timer_handler, (unsigned long) musb);
|
||||
}
|
||||
if (is_peripheral_enabled(musb))
|
||||
musb->xceiv->set_power = bfin_set_power;
|
||||
musb->xceiv->set_power = bfin_musb_set_power;
|
||||
|
||||
musb->isr = blackfin_interrupt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
void musb_platform_save_context(struct musb *musb,
|
||||
struct musb_context_registers *musb_context)
|
||||
static int bfin_musb_exit(struct musb *musb)
|
||||
{
|
||||
gpio_free(musb->config->gpio_vrsel);
|
||||
|
||||
otg_put_transceiver(musb->xceiv);
|
||||
usb_nop_xceiv_unregister();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct musb_platform_ops bfin_ops = {
|
||||
.init = bfin_musb_init,
|
||||
.exit = bfin_musb_exit,
|
||||
|
||||
.enable = bfin_musb_enable,
|
||||
.disable = bfin_musb_disable,
|
||||
|
||||
.set_mode = bfin_musb_set_mode,
|
||||
.try_idle = bfin_musb_try_idle,
|
||||
|
||||
.vbus_status = bfin_musb_vbus_status,
|
||||
.set_vbus = bfin_musb_set_vbus,
|
||||
};
|
||||
|
||||
static u64 bfin_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int __init bfin_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct bfin_glue *glue;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
||||
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue) {
|
||||
dev_err(&pdev->dev, "failed to allocate glue context\n");
|
||||
goto err0;
|
||||
}
|
||||
|
||||
musb = platform_device_alloc("musb-hdrc", -1);
|
||||
if (!musb) {
|
||||
dev_err(&pdev->dev, "failed to allocate musb device\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
musb->dev.parent = &pdev->dev;
|
||||
musb->dev.dma_mask = &bfin_dmamask;
|
||||
musb->dev.coherent_dma_mask = bfin_dmamask;
|
||||
|
||||
glue->dev = &pdev->dev;
|
||||
glue->musb = musb;
|
||||
|
||||
pdata->platform_ops = &bfin_ops;
|
||||
|
||||
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 err2;
|
||||
}
|
||||
|
||||
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add platform_data\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = platform_device_add(musb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
platform_device_put(musb);
|
||||
|
||||
err1:
|
||||
kfree(glue);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit bfin_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bfin_glue *glue = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_del(glue->musb);
|
||||
platform_device_put(glue->musb);
|
||||
kfree(glue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int bfin_suspend(struct device *dev)
|
||||
{
|
||||
struct bfin_glue *glue = dev_get_drvdata(dev);
|
||||
struct musb *musb = glue_to_musb(glue);
|
||||
|
||||
if (is_host_active(musb))
|
||||
/*
|
||||
* During hibernate gpio_vrsel will change from high to low
|
||||
|
@ -413,20 +520,50 @@ void musb_platform_save_context(struct musb *musb,
|
|||
* wakeup event.
|
||||
*/
|
||||
gpio_set_value(musb->config->gpio_vrsel, 0);
|
||||
}
|
||||
|
||||
void musb_platform_restore_context(struct musb *musb,
|
||||
struct musb_context_registers *musb_context)
|
||||
{
|
||||
musb_platform_reg_init(musb);
|
||||
}
|
||||
#endif
|
||||
|
||||
int musb_platform_exit(struct musb *musb)
|
||||
{
|
||||
gpio_free(musb->config->gpio_vrsel);
|
||||
|
||||
otg_put_transceiver(musb->xceiv);
|
||||
usb_nop_xceiv_unregister();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bfin_resume(struct device *dev)
|
||||
{
|
||||
struct bfin_glue *glue = dev_get_drvdata(dev);
|
||||
struct musb *musb = glue_to_musb(glue);
|
||||
|
||||
bfin_musb_reg_init(musb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_pm_ops bfin_pm_ops = {
|
||||
.suspend = bfin_suspend,
|
||||
.resume = bfin_resume,
|
||||
};
|
||||
|
||||
#define DEV_PM_OPS &bfin_pm_op,
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver bfin_driver = {
|
||||
.remove = __exit_p(bfin_remove),
|
||||
.driver = {
|
||||
.name = "musb-bfin",
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION("Blackfin MUSB Glue Layer");
|
||||
MODULE_AUTHOR("Bryan Wy <cooloney@kernel.org>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static int __init bfin_init(void)
|
||||
{
|
||||
return platform_driver_probe(&bfin_driver, bfin_probe);
|
||||
}
|
||||
subsys_initcall(bfin_init);
|
||||
|
||||
static void __exit bfin_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&bfin_driver);
|
||||
}
|
||||
module_exit(bfin_exit);
|
||||
|
|
|
@ -1308,7 +1308,7 @@ dma_controller_create(struct musb *musb, void __iomem *mregs)
|
|||
struct cppi *controller;
|
||||
struct device *dev = musb->controller;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int irq = platform_get_irq(pdev, 1);
|
||||
int irq = platform_get_irq_byname(pdev, "dma");
|
||||
|
||||
controller = kzalloc(sizeof *controller, GFP_KERNEL);
|
||||
if (!controller)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue