1
0
Fork 0

usb: patches for v4.2 merge window

- dwc2 adds hibernation support
 - preparation for sunxi glue to musb driver
 - new ULPI bus
 - new ULPI PHY driver for TUSB1210
 - musb patches to support multiple DMA engines on same binary
 - support for R-Car E2 on renesas_usbhs
 
 Signed-off-by: Felipe Balbi <balbi@ti.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJVaIsMAAoJEIaOsuA1yqREQXQP/2tYG5H2f71mpYAuOKu1KcYi
 DnFH5bKgVQZwl+Dafa41B7P40qub7goCULQSmAgErKboHQvzf7GhzbH22jG3cUg+
 icowa+pEstXxzcwQGOMVIOZ3jAIQCZFFOizolfAaCHrnYNGwaCKq+5809EdMyXHc
 IdYnkYFYdOVGjTPypzoHf8fjpMoCfMx+TLh2ZV4ICj4o5cVXkr9oXNAA8/B/dn+T
 MLK02IPmMOVNJs2gYFQSsGYy03YVR+y62jrYXa+bIa1lzYr4v2yx5SymYTcCWMsH
 c6szhMxCOoL3CGzONCT52b+ogfpCzefl0HV6yvpAQmRcgbAiRuVt3caq1TBpvMq0
 NJVChEfFtOaUR+UGNkBUzh7/CjcVlH8516NQlCIdPemB8HzqBfRMv9MjHsGt1rXC
 SHQMQSTOBw2fbdG5zvi06BOO2hCStqXLNCHARJ2Z5YjP7mLrwteO+wYg2FG9zhTp
 ShGZlHPqP/U+50WuzorHEpr/pRTJh3LhDlYJ6CBwyd4+vg17PrYYbkf82d9WDEDL
 BFH9qe7jfOwZiWHJvNFeHLriKzj/k3i9/DoRaqtm7QwJgafzBz7l+319jvruupnG
 8oxhr+gt74uDPIzxWWv+eFOtO+Uu/q8C3dSp0H/0sY4soBELIgf+QtOkwn596rFp
 mBLSkru+oEN62IQbTKDK
 =Jqzf
 -----END PGP SIGNATURE-----

Merge tag 'usb-for-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next

Felipe writes:

usb: patches for v4.2 merge window

- dwc2 adds hibernation support
- preparation for sunxi glue to musb driver
- new ULPI bus
- new ULPI PHY driver for TUSB1210
- musb patches to support multiple DMA engines on same binary
- support for R-Car E2 on renesas_usbhs

Signed-off-by: Felipe Balbi <balbi@ti.com>
steinar/wifi_calib_4_9_kernel
Greg Kroah-Hartman 2015-06-02 10:47:03 +09:00
commit e152813ff5
93 changed files with 3969 additions and 1433 deletions

View File

@ -1,5 +1,15 @@
TWL BCI (Battery Charger Interface)
The battery charger needs to interact with the USB phy in order
to know when charging is permissible, and when there is a connection
or disconnection.
The choice of phy cannot be configured at a hardware level, so there
is no value in explicit configuration in device-tree. Rather
if there is a sibling of the BCI node which is compatible with
"ti,twl4030-usb", then that is used to determine when and how
use USB power for charging.
Required properties:
- compatible:
- "ti,twl4030-bci"

View File

@ -49,8 +49,7 @@ st_dwc3: dwc3@8f94000 {
st,syscfg = <&syscfg_core>;
resets = <&powerdown STIH407_USB3_POWERDOWN>,
<&softreset STIH407_MIPHY2_SOFTRESET>;
reset-names = "powerdown",
"softreset";
reset-names = "powerdown", "softreset";
#address-cells = <1>;
#size-cells = <1>;
pinctrl-names = "default";
@ -62,7 +61,7 @@ st_dwc3: dwc3@8f94000 {
reg = <0x09900000 0x100000>;
interrupts = <GIC_SPI 155 IRQ_TYPE_NONE>;
dr_mode = "host";
phys-names = "usb2-phy", "usb3-phy";
phys = <&usb2_picophy2>, <&phy_port2 MIPHY_TYPE_USB>;
phy-names = "usb2-phy", "usb3-phy";
phys = <&usb2_picophy2>, <&phy_port2 PHY_TYPE_USB3>;
};
};

View File

@ -38,6 +38,8 @@ Optional properties:
- snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
utmi_l1_suspend_n, false when asserts utmi_sleep_n
- snps,hird-threshold: HIRD threshold
- snps,hsphy_interface: High-Speed PHY interface selection between "utmi" for
UTMI+ and "ulpi" for ULPI when the DWC_USB3_HSPHY_INTERFACE has value 3.
This is usually a subnode to DWC3 glue to which it is connected.

View File

@ -69,6 +69,17 @@ Optional properties:
(no, min, max) where each value represents either a voltage
in microvolts or a value corresponding to voltage corner.
- qcom,manual-pullup: If present, vbus is not routed to USB controller/phy
and controller driver therefore enables pull-up explicitly
before starting controller using usbcmd run/stop bit.
- extcon: phandles to external connector devices. First phandle
should point to external connector, which provide "USB"
cable events, the second should point to external connector
device, which provide "USB-HOST" cable events. If one of
the external connector devices is not required empty <0>
phandle should be specified.
Example HSUSB OTG controller device node:
usb@f9a55000 {

View File

@ -4,6 +4,7 @@ Required properties:
- compatible: Must contain one of the following:
- "renesas,usbhs-r8a7790"
- "renesas,usbhs-r8a7791"
- "renesas,usbhs-r8a7794"
- reg: Base address and length of the register for the USBHS
- interrupts: Interrupt specifier for the USBHS
- clocks: A list of phandle + clock specifier pairs
@ -15,10 +16,8 @@ Optional properties:
- phys: phandle + phy specifier pair
- phy-names: must be "usb"
- dmas: Must contain a list of references to DMA specifiers.
- dma-names : Must contain a list of DMA names:
- tx0 ... tx<n>
- rx0 ... rx<n>
- This <n> means DnFIFO in USBHS module.
- dma-names : named "ch%d", where %d is the channel number ranging from zero
to the number of channels (DnFIFOs) minus one.
Example:
usbhs: usb@e6590000 {

View File

@ -30,6 +30,9 @@ TWL4030 USB PHY AND COMPARATOR
- usb_mode : The mode used by the phy to connect to the controller. "1"
specifies "ULPI" mode and "2" specifies "CEA2011_3PIN" mode.
If a sibling node is compatible "ti,twl4030-bci", then it will find
this device and query it for USB power status.
twl4030-usb {
compatible = "ti,twl4030-usb";
interrupts = < 10 4 >;

View File

@ -526,8 +526,6 @@ Except for ifname they can be written to until the function is linked to a
configuration. The ifname is read-only and contains the name of the interface
which was assigned by the net core, e. g. usb0.
By default there can be only 1 RNDIS interface in the system.
Testing the RNDIS function
--------------------------
@ -629,7 +627,7 @@ Function-specific configfs interface
The function name to use when creating the function directory is "uac2".
The uac2 function provides these attributes in its function directory:
chmask - capture channel mask
c_chmask - capture channel mask
c_srate - capture sampling rate
c_ssize - capture sample size (bytes)
p_chmask - playback channel mask

View File

@ -10489,6 +10489,13 @@ S: Maintained
F: Documentation/video4linux/zr364xx.txt
F: drivers/media/usb/zr364xx/
ULPI BUS
M: Heikki Krogerus <heikki.krogerus@linux.intel.com>
L: linux-usb@vger.kernel.org
S: Maintained
F: drivers/usb/common/ulpi.c
F: include/linux/ulpi/
USER-MODE LINUX (UML)
M: Jeff Dike <jdike@addtoit.com>
M: Richard Weinberger <richard@nod.at>

View File

@ -309,4 +309,11 @@ config PHY_QCOM_UFS
help
Support for UFS PHY on QCOM chipsets.
config PHY_TUSB1210
tristate "TI TUSB1210 ULPI PHY module"
depends on USB_ULPI_BUS
select GENERIC_PHY
help
Support for TI TUSB1210 USB ULPI PHY.
endmenu

View File

@ -40,3 +40,4 @@ obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o

View File

@ -30,6 +30,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-sun4i-usb.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
@ -58,6 +59,7 @@
#define PHY_OTG_FUNC_EN 0x28
#define PHY_VBUS_DET_EN 0x29
#define PHY_DISCON_TH_SEL 0x2a
#define PHY_SQUELCH_DETECT 0x3c
#define MAX_PHYS 3
@ -204,6 +206,13 @@ static int sun4i_usb_phy_power_off(struct phy *_phy)
return 0;
}
void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled)
{
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2);
}
static struct phy_ops sun4i_usb_phy_ops = {
.init = sun4i_usb_phy_init,
.exit = sun4i_usb_phy_exit,

View File

@ -0,0 +1,153 @@
/**
* tusb1210.c - TUSB1210 USB ULPI PHY driver
*
* Copyright (C) 2015 Intel Corporation
*
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
*
* 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/ulpi/driver.h>
#include <linux/gpio/consumer.h>
#include "ulpi_phy.h"
#define TUSB1210_VENDOR_SPECIFIC2 0x80
#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0
#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4
#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6
struct tusb1210 {
struct ulpi *ulpi;
struct phy *phy;
struct gpio_desc *gpio_reset;
struct gpio_desc *gpio_cs;
u8 vendor_specific2;
};
static int tusb1210_power_on(struct phy *phy)
{
struct tusb1210 *tusb = phy_get_drvdata(phy);
gpiod_set_value_cansleep(tusb->gpio_reset, 1);
gpiod_set_value_cansleep(tusb->gpio_cs, 1);
/* Restore the optional eye diagram optimization value */
if (tusb->vendor_specific2)
ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2,
tusb->vendor_specific2);
return 0;
}
static int tusb1210_power_off(struct phy *phy)
{
struct tusb1210 *tusb = phy_get_drvdata(phy);
gpiod_set_value_cansleep(tusb->gpio_reset, 0);
gpiod_set_value_cansleep(tusb->gpio_cs, 0);
return 0;
}
static struct phy_ops phy_ops = {
.power_on = tusb1210_power_on,
.power_off = tusb1210_power_off,
.owner = THIS_MODULE,
};
static int tusb1210_probe(struct ulpi *ulpi)
{
struct gpio_desc *gpio;
struct tusb1210 *tusb;
u8 val, reg;
int ret;
tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL);
if (!tusb)
return -ENOMEM;
gpio = devm_gpiod_get(&ulpi->dev, "reset");
if (!IS_ERR(gpio)) {
ret = gpiod_direction_output(gpio, 0);
if (ret)
return ret;
gpiod_set_value_cansleep(gpio, 1);
tusb->gpio_reset = gpio;
}
gpio = devm_gpiod_get(&ulpi->dev, "cs");
if (!IS_ERR(gpio)) {
ret = gpiod_direction_output(gpio, 0);
if (ret)
return ret;
gpiod_set_value_cansleep(gpio, 1);
tusb->gpio_cs = gpio;
}
/*
* VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye
* diagram optimization and DP/DM swap.
*/
/* High speed output drive strength configuration */
device_property_read_u8(&ulpi->dev, "ihstx", &val);
reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT;
/* High speed output impedance configuration */
device_property_read_u8(&ulpi->dev, "zhsdrv", &val);
reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT;
/* DP/DM swap control */
device_property_read_u8(&ulpi->dev, "datapolarity", &val);
reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT;
if (reg) {
ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg);
tusb->vendor_specific2 = reg;
}
tusb->phy = ulpi_phy_create(ulpi, &phy_ops);
if (IS_ERR(tusb->phy))
return PTR_ERR(tusb->phy);
tusb->ulpi = ulpi;
phy_set_drvdata(tusb->phy, tusb);
ulpi_set_drvdata(ulpi, tusb);
return 0;
}
static void tusb1210_remove(struct ulpi *ulpi)
{
struct tusb1210 *tusb = ulpi_get_drvdata(ulpi);
ulpi_phy_destroy(ulpi, tusb->phy);
}
#define TI_VENDOR_ID 0x0451
static const struct ulpi_device_id tusb1210_ulpi_id[] = {
{ TI_VENDOR_ID, 0x1507, },
{ },
};
MODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id);
static struct ulpi_driver tusb1210_driver = {
.id_table = tusb1210_ulpi_id,
.probe = tusb1210_probe,
.remove = tusb1210_remove,
.driver = {
.name = "tusb1210",
.owner = THIS_MODULE,
},
};
module_ulpi_driver(tusb1210_driver);
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("TUSB1210 ULPI PHY driver");

View File

@ -0,0 +1,31 @@
#include <linux/phy/phy.h>
/**
* Helper that registers PHY for a ULPI device and adds a lookup for binding it
* and it's controller, which is always the parent.
*/
static inline struct phy
*ulpi_phy_create(struct ulpi *ulpi, struct phy_ops *ops)
{
struct phy *phy;
int ret;
phy = phy_create(&ulpi->dev, NULL, ops);
if (IS_ERR(phy))
return phy;
ret = phy_create_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent));
if (ret) {
phy_destroy(phy);
return ERR_PTR(ret);
}
return phy;
}
/* Remove a PHY that was created with ulpi_phy_create() and it's lookup. */
static inline void ulpi_phy_destroy(struct ulpi *ulpi, struct phy *phy)
{
phy_remove_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent));
phy_destroy(phy);
}

View File

@ -638,10 +638,15 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
INIT_WORK(&bci->work, twl4030_bci_usb_work);
bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
if (!IS_ERR_OR_NULL(bci->transceiver)) {
bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
usb_register_notifier(bci->transceiver, &bci->usb_nb);
bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
if (bci->dev->of_node) {
struct device_node *phynode;
phynode = of_find_compatible_node(bci->dev->of_node->parent,
NULL, "ti,twl4030-usb");
if (phynode)
bci->transceiver = devm_usb_get_phy_by_node(
bci->dev, phynode, &bci->usb_nb);
}
/* Enable interrupts now. */
@ -671,10 +676,6 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
return 0;
fail_unmask_interrupts:
if (!IS_ERR_OR_NULL(bci->transceiver)) {
usb_unregister_notifier(bci->transceiver, &bci->usb_nb);
usb_put_phy(bci->transceiver);
}
free_irq(bci->irq_bci, bci);
fail_bci_irq:
free_irq(bci->irq_chg, bci);
@ -703,10 +704,6 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev)
twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
TWL4030_INTERRUPTS_BCIIMR2A);
if (!IS_ERR_OR_NULL(bci->transceiver)) {
usb_unregister_notifier(bci->transceiver, &bci->usb_nb);
usb_put_phy(bci->transceiver);
}
free_irq(bci->irq_bci, bci);
free_irq(bci->irq_chg, bci);
power_supply_unregister(bci->usb);

View File

@ -7,3 +7,4 @@ usb-common-y += common.o
usb-common-$(CONFIG_USB_LED_TRIG) += led.o
obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o

View File

@ -0,0 +1,255 @@
/**
* ulpi.c - USB ULPI PHY bus
*
* Copyright (C) 2015 Intel Corporation
*
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
*
* 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/ulpi/interface.h>
#include <linux/ulpi/driver.h>
#include <linux/ulpi/regs.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/acpi.h>
/* -------------------------------------------------------------------------- */
int ulpi_read(struct ulpi *ulpi, u8 addr)
{
return ulpi->ops->read(ulpi->ops, addr);
}
EXPORT_SYMBOL_GPL(ulpi_read);
int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val)
{
return ulpi->ops->write(ulpi->ops, addr, val);
}
EXPORT_SYMBOL_GPL(ulpi_write);
/* -------------------------------------------------------------------------- */
static int ulpi_match(struct device *dev, struct device_driver *driver)
{
struct ulpi_driver *drv = to_ulpi_driver(driver);
struct ulpi *ulpi = to_ulpi_dev(dev);
const struct ulpi_device_id *id;
for (id = drv->id_table; id->vendor; id++)
if (id->vendor == ulpi->id.vendor &&
id->product == ulpi->id.product)
return 1;
return 0;
}
static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct ulpi *ulpi = to_ulpi_dev(dev);
if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x",
ulpi->id.vendor, ulpi->id.product))
return -ENOMEM;
return 0;
}
static int ulpi_probe(struct device *dev)
{
struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
return drv->probe(to_ulpi_dev(dev));
}
static int ulpi_remove(struct device *dev)
{
struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
if (drv->remove)
drv->remove(to_ulpi_dev(dev));
return 0;
}
static struct bus_type ulpi_bus = {
.name = "ulpi",
.match = ulpi_match,
.uevent = ulpi_uevent,
.probe = ulpi_probe,
.remove = ulpi_remove,
};
/* -------------------------------------------------------------------------- */
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ulpi *ulpi = to_ulpi_dev(dev);
return sprintf(buf, "ulpi:v%04xp%04x\n",
ulpi->id.vendor, ulpi->id.product);
}
static DEVICE_ATTR_RO(modalias);
static struct attribute *ulpi_dev_attrs[] = {
&dev_attr_modalias.attr,
NULL
};
static struct attribute_group ulpi_dev_attr_group = {
.attrs = ulpi_dev_attrs,
};
static const struct attribute_group *ulpi_dev_attr_groups[] = {
&ulpi_dev_attr_group,
NULL
};
static void ulpi_dev_release(struct device *dev)
{
kfree(to_ulpi_dev(dev));
}
static struct device_type ulpi_dev_type = {
.name = "ulpi_device",
.groups = ulpi_dev_attr_groups,
.release = ulpi_dev_release,
};
/* -------------------------------------------------------------------------- */
/**
* ulpi_register_driver - register a driver with the ULPI bus
* @drv: driver being registered
*
* Registers a driver with the ULPI bus.
*/
int ulpi_register_driver(struct ulpi_driver *drv)
{
if (!drv->probe)
return -EINVAL;
drv->driver.bus = &ulpi_bus;
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(ulpi_register_driver);
/**
* ulpi_unregister_driver - unregister a driver with the ULPI bus
* @drv: driver to unregister
*
* Unregisters a driver with the ULPI bus.
*/
void ulpi_unregister_driver(struct ulpi_driver *drv)
{
driver_unregister(&drv->driver);
}
EXPORT_SYMBOL_GPL(ulpi_unregister_driver);
/* -------------------------------------------------------------------------- */
static int ulpi_register(struct device *dev, struct ulpi *ulpi)
{
int ret;
/* Test the interface */
ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
if (ret < 0)
return ret;
ret = ulpi_read(ulpi, ULPI_SCRATCH);
if (ret < 0)
return ret;
if (ret != 0xaa)
return -ENODEV;
ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW);
ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8;
ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW);
ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8;
ulpi->dev.parent = dev;
ulpi->dev.bus = &ulpi_bus;
ulpi->dev.type = &ulpi_dev_type;
dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product);
ret = device_register(&ulpi->dev);
if (ret)
return ret;
dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
ulpi->id.vendor, ulpi->id.product);
return 0;
}
/**
* ulpi_register_interface - instantiate new ULPI device
* @dev: USB controller's device interface
* @ops: ULPI register access
*
* Allocates and registers a ULPI device and an interface for it. Called from
* the USB controller that provides the ULPI interface.
*/
struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi_ops *ops)
{
struct ulpi *ulpi;
int ret;
ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
if (!ulpi)
return ERR_PTR(-ENOMEM);
ulpi->ops = ops;
ops->dev = dev;
ret = ulpi_register(dev, ulpi);
if (ret) {
kfree(ulpi);
return ERR_PTR(ret);
}
return ulpi;
}
EXPORT_SYMBOL_GPL(ulpi_register_interface);
/**
* ulpi_unregister_interface - unregister ULPI interface
* @intrf: struct ulpi_interface
*
* Unregisters a ULPI device and it's interface that was created with
* ulpi_create_interface().
*/
void ulpi_unregister_interface(struct ulpi *ulpi)
{
device_unregister(&ulpi->dev);
}
EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
/* -------------------------------------------------------------------------- */
static int __init ulpi_init(void)
{
return bus_register(&ulpi_bus);
}
module_init(ulpi_init);
static void __exit ulpi_exit(void)
{
bus_unregister(&ulpi_bus);
}
module_exit(ulpi_exit);
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("USB ULPI PHY bus");

View File

@ -84,3 +84,23 @@ config USB_OTG_FSM
Implements OTG Finite State Machine as specified in On-The-Go
and Embedded Host Supplement to the USB Revision 2.0 Specification.
config USB_ULPI_BUS
tristate "USB ULPI PHY interface support"
depends on USB_SUPPORT
help
UTMI+ Low Pin Interface (ULPI) is specification for a commonly used
USB 2.0 PHY interface. The ULPI specification defines a standard set
of registers that can be used to detect the vendor and product which
allows ULPI to be handled as a bus. This module is the driver for that
bus.
The ULPI interfaces (the buses) are registered by the drivers for USB
controllers which support ULPI register access and have ULPI PHY
attached to them. The ULPI PHY drivers themselves are normal PHY
drivers.
ULPI PHYs provide often functions such as ADP sensing/probing (OTG
protocol) and USB charger detection.
To compile this driver as a module, choose M here: the module will
be called ulpi.

View File

@ -50,18 +50,10 @@ config USB_DWC2_DUAL_ROLE
option requires USB_GADGET to be enabled.
endchoice
config USB_DWC2_PLATFORM
tristate "DWC2 Platform"
default USB_DWC2_HOST || USB_DWC2_PERIPHERAL
help
The Designware USB2.0 platform interface module for
controllers directly connected to the CPU.
config USB_DWC2_PCI
tristate "DWC2 PCI"
depends on PCI
default n
select USB_DWC2_PLATFORM
select NOP_USB_XCEIV
help
The Designware USB2.0 PCI interface module for controllers

View File

@ -2,7 +2,7 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG
ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG
obj-$(CONFIG_USB_DWC2) += dwc2.o
dwc2-y := core.o core_intr.o
dwc2-y := core.o core_intr.o platform.o
ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
dwc2-y += hcd.o hcd_intr.o
@ -13,6 +13,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC2_PERIPHERAL) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
dwc2-y += gadget.o
endif
ifneq ($(CONFIG_DEBUG_FS),)
dwc2-y += debugfs.o
endif
# NOTE: The previous s3c-hsotg peripheral mode only driver has been moved to
# this location and renamed gadget.c. When building for dynamically linked
# modules, dwc2.ko will get built for host mode, peripheral mode, and dual-role
@ -21,6 +25,3 @@ endif
obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o
dwc2_pci-y := pci.o
obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o
dwc2_platform-y := platform.o

View File

@ -56,6 +56,389 @@
#include "core.h"
#include "hcd.h"
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
/**
* dwc2_backup_host_registers() - Backup controller host registers.
* When suspending usb bus, registers needs to be backuped
* if controller power is disabled once suspended.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_hregs_backup *hr;
int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
/* Backup Host regs */
hr = hsotg->hr_backup;
if (!hr) {
hr = devm_kzalloc(hsotg->dev, sizeof(*hr), GFP_KERNEL);
if (!hr) {
dev_err(hsotg->dev, "%s: can't allocate host regs\n",
__func__);
return -ENOMEM;
}
hsotg->hr_backup = hr;
}
hr->hcfg = readl(hsotg->regs + HCFG);
hr->haintmsk = readl(hsotg->regs + HAINTMSK);
for (i = 0; i < hsotg->core_params->host_channels; ++i)
hr->hcintmsk[i] = readl(hsotg->regs + HCINTMSK(i));
hr->hprt0 = readl(hsotg->regs + HPRT0);
hr->hfir = readl(hsotg->regs + HFIR);
return 0;
}
/**
* dwc2_restore_host_registers() - Restore controller host registers.
* When resuming usb bus, device registers needs to be restored
* if controller power were disabled.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_hregs_backup *hr;
int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
/* Restore host regs */
hr = hsotg->hr_backup;
if (!hr) {
dev_err(hsotg->dev, "%s: no host registers to restore\n",
__func__);
return -EINVAL;
}
writel(hr->hcfg, hsotg->regs + HCFG);
writel(hr->haintmsk, hsotg->regs + HAINTMSK);
for (i = 0; i < hsotg->core_params->host_channels; ++i)
writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i));
writel(hr->hprt0, hsotg->regs + HPRT0);
writel(hr->hfir, hsotg->regs + HFIR);
return 0;
}
#else
static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
#endif
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
/**
* dwc2_backup_device_registers() - Backup controller device registers.
* When suspending usb bus, registers needs to be backuped
* if controller power is disabled once suspended.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_dregs_backup *dr;
int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
/* Backup dev regs */
dr = hsotg->dr_backup;
if (!dr) {
dr = devm_kzalloc(hsotg->dev, sizeof(*dr), GFP_KERNEL);
if (!dr) {
dev_err(hsotg->dev, "%s: can't allocate device regs\n",
__func__);
return -ENOMEM;
}
hsotg->dr_backup = dr;
}
dr->dcfg = readl(hsotg->regs + DCFG);
dr->dctl = readl(hsotg->regs + DCTL);
dr->daintmsk = readl(hsotg->regs + DAINTMSK);
dr->diepmsk = readl(hsotg->regs + DIEPMSK);
dr->doepmsk = readl(hsotg->regs + DOEPMSK);
for (i = 0; i < hsotg->num_of_eps; i++) {
/* Backup IN EPs */
dr->diepctl[i] = readl(hsotg->regs + DIEPCTL(i));
/* Ensure DATA PID is correctly configured */
if (dr->diepctl[i] & DXEPCTL_DPID)
dr->diepctl[i] |= DXEPCTL_SETD1PID;
else
dr->diepctl[i] |= DXEPCTL_SETD0PID;
dr->dieptsiz[i] = readl(hsotg->regs + DIEPTSIZ(i));
dr->diepdma[i] = readl(hsotg->regs + DIEPDMA(i));
/* Backup OUT EPs */
dr->doepctl[i] = readl(hsotg->regs + DOEPCTL(i));
/* Ensure DATA PID is correctly configured */
if (dr->doepctl[i] & DXEPCTL_DPID)
dr->doepctl[i] |= DXEPCTL_SETD1PID;
else
dr->doepctl[i] |= DXEPCTL_SETD0PID;
dr->doeptsiz[i] = readl(hsotg->regs + DOEPTSIZ(i));
dr->doepdma[i] = readl(hsotg->regs + DOEPDMA(i));
}
return 0;
}
/**
* dwc2_restore_device_registers() - Restore controller device registers.
* When resuming usb bus, device registers needs to be restored
* if controller power were disabled.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_dregs_backup *dr;
u32 dctl;
int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
/* Restore dev regs */
dr = hsotg->dr_backup;
if (!dr) {
dev_err(hsotg->dev, "%s: no device registers to restore\n",
__func__);
return -EINVAL;
}
writel(dr->dcfg, hsotg->regs + DCFG);
writel(dr->dctl, hsotg->regs + DCTL);
writel(dr->daintmsk, hsotg->regs + DAINTMSK);
writel(dr->diepmsk, hsotg->regs + DIEPMSK);
writel(dr->doepmsk, hsotg->regs + DOEPMSK);
for (i = 0; i < hsotg->num_of_eps; i++) {
/* Restore IN EPs */
writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i));
writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i));
writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i));
/* Restore OUT EPs */
writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i));
writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i));
writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i));
}
/* Set the Power-On Programming done bit */
dctl = readl(hsotg->regs + DCTL);
dctl |= DCTL_PWRONPRGDONE;
writel(dctl, hsotg->regs + DCTL);
return 0;
}
#else
static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
#endif
/**
* dwc2_backup_global_registers() - Backup global controller registers.
* When suspending usb bus, registers needs to be backuped
* if controller power is disabled once suspended.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_gregs_backup *gr;
int i;
/* Backup global regs */
gr = hsotg->gr_backup;
if (!gr) {
gr = devm_kzalloc(hsotg->dev, sizeof(*gr), GFP_KERNEL);
if (!gr) {
dev_err(hsotg->dev, "%s: can't allocate global regs\n",
__func__);
return -ENOMEM;
}
hsotg->gr_backup = gr;
}
gr->gotgctl = readl(hsotg->regs + GOTGCTL);
gr->gintmsk = readl(hsotg->regs + GINTMSK);
gr->gahbcfg = readl(hsotg->regs + GAHBCFG);
gr->gusbcfg = readl(hsotg->regs + GUSBCFG);
gr->grxfsiz = readl(hsotg->regs + GRXFSIZ);
gr->gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ);
gr->hptxfsiz = readl(hsotg->regs + HPTXFSIZ);
gr->gdfifocfg = readl(hsotg->regs + GDFIFOCFG);
for (i = 0; i < MAX_EPS_CHANNELS; i++)
gr->dtxfsiz[i] = readl(hsotg->regs + DPTXFSIZN(i));
return 0;
}
/**
* dwc2_restore_global_registers() - Restore controller global registers.
* When resuming usb bus, device registers needs to be restored
* if controller power were disabled.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_gregs_backup *gr;
int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
/* Restore global regs */
gr = hsotg->gr_backup;
if (!gr) {
dev_err(hsotg->dev, "%s: no global registers to restore\n",
__func__);
return -EINVAL;
}
writel(0xffffffff, hsotg->regs + GINTSTS);
writel(gr->gotgctl, hsotg->regs + GOTGCTL);
writel(gr->gintmsk, hsotg->regs + GINTMSK);
writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
writel(gr->gahbcfg, hsotg->regs + GAHBCFG);
writel(gr->grxfsiz, hsotg->regs + GRXFSIZ);
writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ);
writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ);
writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG);
for (i = 0; i < MAX_EPS_CHANNELS; i++)
writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i));
return 0;
}
/**
* dwc2_exit_hibernation() - Exit controller from Partial Power Down.
*
* @hsotg: Programming view of the DWC_otg controller
* @restore: Controller registers need to be restored
*/
int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
{
u32 pcgcctl;
int ret = 0;
if (!hsotg->core_params->hibernation)
return -ENOTSUPP;
pcgcctl = readl(hsotg->regs + PCGCTL);
pcgcctl &= ~PCGCTL_STOPPCLK;
writel(pcgcctl, hsotg->regs + PCGCTL);
pcgcctl = readl(hsotg->regs + PCGCTL);
pcgcctl &= ~PCGCTL_PWRCLMP;
writel(pcgcctl, hsotg->regs + PCGCTL);
pcgcctl = readl(hsotg->regs + PCGCTL);
pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
writel(pcgcctl, hsotg->regs + PCGCTL);
udelay(100);
if (restore) {
ret = dwc2_restore_global_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore registers\n",
__func__);
return ret;
}
if (dwc2_is_host_mode(hsotg)) {
ret = dwc2_restore_host_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore host registers\n",
__func__);
return ret;
}
} else {
ret = dwc2_restore_device_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore device registers\n",
__func__);
return ret;
}
}
}
return ret;
}
/**
* dwc2_enter_hibernation() - Put controller in Partial Power Down.
*
* @hsotg: Programming view of the DWC_otg controller
*/
int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
{
u32 pcgcctl;
int ret = 0;
if (!hsotg->core_params->hibernation)
return -ENOTSUPP;
/* Backup all registers */
ret = dwc2_backup_global_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to backup global registers\n",
__func__);
return ret;
}
if (dwc2_is_host_mode(hsotg)) {
ret = dwc2_backup_host_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to backup host registers\n",
__func__);
return ret;
}
} else {
ret = dwc2_backup_device_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to backup device registers\n",
__func__);
return ret;
}
}
/* Put the controller in low power state */
pcgcctl = readl(hsotg->regs + PCGCTL);
pcgcctl |= PCGCTL_PWRCLMP;
writel(pcgcctl, hsotg->regs + PCGCTL);
ndelay(20);
pcgcctl |= PCGCTL_RSTPDWNMODULE;
writel(pcgcctl, hsotg->regs + PCGCTL);
ndelay(20);
pcgcctl |= PCGCTL_STOPPCLK;
writel(pcgcctl, hsotg->regs + PCGCTL);
return ret;
}
/**
* dwc2_enable_common_interrupts() - Initializes the commmon interrupts,
* used in both device and host modes
@ -77,8 +460,10 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
if (hsotg->core_params->dma_enable <= 0)
intmsk |= GINTSTS_RXFLVL;
if (hsotg->core_params->external_id_pin_ctl <= 0)
intmsk |= GINTSTS_CONIDSTSCHNG;
intmsk |= GINTSTS_CONIDSTSCHNG | GINTSTS_WKUPINT | GINTSTS_USBSUSP |
intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP |
GINTSTS_SESSREQINT;
writel(intmsk, hsotg->regs + GINTMSK);
@ -2602,6 +2987,40 @@ static void dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val)
hsotg->core_params->uframe_sched = val;
}
static void dwc2_set_param_external_id_pin_ctl(struct dwc2_hsotg *hsotg,
int val)
{
if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
if (val >= 0) {
dev_err(hsotg->dev,
"'%d' invalid for parameter external_id_pin_ctl\n",
val);
dev_err(hsotg->dev, "external_id_pin_ctl must be 0 or 1\n");
}
val = 0;
dev_dbg(hsotg->dev, "Setting external_id_pin_ctl to %d\n", val);
}
hsotg->core_params->external_id_pin_ctl = val;
}
static void dwc2_set_param_hibernation(struct dwc2_hsotg *hsotg,
int val)
{
if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
if (val >= 0) {
dev_err(hsotg->dev,
"'%d' invalid for parameter hibernation\n",
val);
dev_err(hsotg->dev, "hibernation must be 0 or 1\n");
}
val = 0;
dev_dbg(hsotg->dev, "Setting hibernation to %d\n", val);
}
hsotg->core_params->hibernation = val;
}
/*
* This function is called during module intialization to pass module parameters
* for the DWC_otg core.
@ -2646,6 +3065,8 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
dwc2_set_param_ahbcfg(hsotg, params->ahbcfg);
dwc2_set_param_otg_ver(hsotg, params->otg_ver);
dwc2_set_param_uframe_sched(hsotg, params->uframe_sched);
dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl);
dwc2_set_param_hibernation(hsotg, params->hibernation);
}
/**
@ -2814,6 +3235,22 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
return 0;
}
/*
* Sets all parameters to the given value.
*
* Assumes that the dwc2_core_params struct contains only integers.
*/
void dwc2_set_all_params(struct dwc2_core_params *params, int value)
{
int *p = (int *)params;
size_t size = sizeof(*params) / sizeof(*p);
int i;
for (i = 0; i < size; i++)
p[i] = value;
}
u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg)
{
return hsotg->core_params->otg_ver == 1 ? 0x0200 : 0x0103;

View File

@ -331,6 +331,17 @@ enum dwc2_ep0_state {
* by the driver and are ignored in this
* configuration value.
* @uframe_sched: True to enable the microframe scheduler
* @external_id_pin_ctl: Specifies whether ID pin is handled externally.
* Disable CONIDSTSCHNG controller interrupt in such
* case.
* 0 - No (default)
* 1 - Yes
* @hibernation: Specifies whether the controller support hibernation.
* If hibernation is enabled, the controller will enter
* hibernation in both peripheral and host mode when
* needed.
* 0 - No (default)
* 1 - Yes
*
* The following parameters may be specified when starting the module. These
* parameters define how the DWC_otg controller should be configured. A
@ -368,6 +379,8 @@ struct dwc2_core_params {
int reload_ctl;
int ahbcfg;
int uframe_sched;
int external_id_pin_ctl;
int hibernation;
};
/**
@ -451,6 +464,82 @@ struct dwc2_hw_params {
/* Size of control and EP0 buffers */
#define DWC2_CTRL_BUFF_SIZE 8
/**
* struct dwc2_gregs_backup - Holds global registers state before entering partial
* power down
* @gotgctl: Backup of GOTGCTL register
* @gintmsk: Backup of GINTMSK register
* @gahbcfg: Backup of GAHBCFG register
* @gusbcfg: Backup of GUSBCFG register
* @grxfsiz: Backup of GRXFSIZ register
* @gnptxfsiz: Backup of GNPTXFSIZ register
* @gi2cctl: Backup of GI2CCTL register
* @hptxfsiz: Backup of HPTXFSIZ register
* @gdfifocfg: Backup of GDFIFOCFG register
* @dtxfsiz: Backup of DTXFSIZ registers for each endpoint
* @gpwrdn: Backup of GPWRDN register
*/
struct dwc2_gregs_backup {
u32 gotgctl;
u32 gintmsk;
u32 gahbcfg;
u32 gusbcfg;
u32 grxfsiz;
u32 gnptxfsiz;
u32 gi2cctl;
u32 hptxfsiz;
u32 pcgcctl;
u32 gdfifocfg;
u32 dtxfsiz[MAX_EPS_CHANNELS];
u32 gpwrdn;
};
/**
* struct dwc2_dregs_backup - Holds device registers state before entering partial
* power down
* @dcfg: Backup of DCFG register
* @dctl: Backup of DCTL register
* @daintmsk: Backup of DAINTMSK register
* @diepmsk: Backup of DIEPMSK register
* @doepmsk: Backup of DOEPMSK register
* @diepctl: Backup of DIEPCTL register
* @dieptsiz: Backup of DIEPTSIZ register
* @diepdma: Backup of DIEPDMA register
* @doepctl: Backup of DOEPCTL register
* @doeptsiz: Backup of DOEPTSIZ register
* @doepdma: Backup of DOEPDMA register
*/
struct dwc2_dregs_backup {
u32 dcfg;
u32 dctl;
u32 daintmsk;
u32 diepmsk;
u32 doepmsk;
u32 diepctl[MAX_EPS_CHANNELS];
u32 dieptsiz[MAX_EPS_CHANNELS];
u32 diepdma[MAX_EPS_CHANNELS];
u32 doepctl[MAX_EPS_CHANNELS];
u32 doeptsiz[MAX_EPS_CHANNELS];
u32 doepdma[MAX_EPS_CHANNELS];
};
/**
* struct dwc2_hregs_backup - Holds host registers state before entering partial
* power down
* @hcfg: Backup of HCFG register
* @haintmsk: Backup of HAINTMSK register
* @hcintmsk: Backup of HCINTMSK register
* @hptr0: Backup of HPTR0 register
* @hfir: Backup of HFIR register
*/
struct dwc2_hregs_backup {
u32 hcfg;
u32 haintmsk;
u32 hcintmsk[MAX_EPS_CHANNELS];
u32 hprt0;
u32 hfir;
};
/**
* struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
* and periodic schedules
@ -481,6 +570,9 @@ struct dwc2_hw_params {
* interrupt
* @wkp_timer: Timer object for handling Wakeup Detected interrupt
* @lx_state: Lx state of connected device
* @gregs_backup: Backup of global registers during suspend
* @dregs_backup: Backup of device registers during suspend
* @hregs_backup: Backup of host registers during suspend
*
* These are for host mode:
*
@ -613,11 +705,12 @@ struct dwc2_hsotg {
struct work_struct wf_otg;
struct timer_list wkp_timer;
enum dwc2_lx_state lx_state;
struct dwc2_gregs_backup *gr_backup;
struct dwc2_dregs_backup *dr_backup;
struct dwc2_hregs_backup *hr_backup;
struct dentry *debug_root;
struct dentry *debug_file;
struct dentry *debug_testmode;
struct dentry *debug_fifo;
struct debugfs_regset32 *regset;
/* DWC OTG HW Release versions */
#define DWC2_CORE_REV_2_71a 0x4f54271a
@ -751,6 +844,8 @@ enum dwc2_halt_status {
* and the DWC_otg controller
*/
extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg);
extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
/*
* Host core Functions.
@ -983,6 +1078,15 @@ extern void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val);
extern void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val);
extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
const struct dwc2_core_params *params);
extern void dwc2_set_all_params(struct dwc2_core_params *params, int value);
extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
/*
* Dump core registers and SPRAM
*/
@ -1005,6 +1109,8 @@ extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
bool reset);
extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg);
extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2);
extern int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
#else
static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2)
{ return 0; }
@ -1018,6 +1124,10 @@ static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
bool reset) {}
static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
static inline int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
int testmode)
{ return 0; }
#define dwc2_is_device_connected(hsotg) (0)
#endif
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
@ -1025,14 +1135,12 @@ extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
#else
static inline void dwc2_set_all_params(struct dwc2_core_params *params, int value) {}
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
const struct dwc2_core_params *params)
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
{ return 0; }
#endif

View File

@ -334,6 +334,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
*/
static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
{
int ret;
dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
@ -345,6 +346,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
/* Clear Remote Wakeup Signaling */
dctl &= ~DCTL_RMTWKUPSIG;
writel(dctl, hsotg->regs + DCTL);
ret = dwc2_exit_hibernation(hsotg, true);
if (ret && (ret != -ENOTSUPP))
dev_err(hsotg->dev, "exit hibernation failed\n");
call_gadget(hsotg, resume);
}
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
@ -397,6 +403,7 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
{
u32 dsts;
int ret;
dev_dbg(hsotg->dev, "USB SUSPEND\n");
@ -411,10 +418,43 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
"DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n",
!!(dsts & DSTS_SUSPSTS),
hsotg->hw_params.power_optimized);
if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) {
/* Ignore suspend request before enumeration */
if (!dwc2_is_device_connected(hsotg)) {
dev_dbg(hsotg->dev,
"ignore suspend request before enumeration\n");
goto clear_int;
}
ret = dwc2_enter_hibernation(hsotg);
if (ret) {
if (ret != -ENOTSUPP)
dev_err(hsotg->dev,
"enter hibernation failed\n");
goto skip_power_saving;
}
udelay(100);
/* Ask phy to be suspended */
if (!IS_ERR_OR_NULL(hsotg->uphy))
usb_phy_set_suspend(hsotg->uphy, true);
skip_power_saving:
/*
* Change to L2 (suspend) state before releasing
* spinlock
*/
hsotg->lx_state = DWC2_L2;
/* Call gadget suspend callback */
call_gadget(hsotg, suspend);
}
} else {
if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) {
dev_dbg(hsotg->dev, "a_peripheral->a_host\n");
/* Change to L2 (suspend) state */
hsotg->lx_state = DWC2_L2;
/* Clear the a_peripheral flag, back to a_host */
spin_unlock(&hsotg->lock);
dwc2_hcd_start(hsotg);
@ -423,9 +463,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
}
}
/* Change to L2 (suspend) state */
hsotg->lx_state = DWC2_L2;
clear_int:
/* Clear interrupt */
writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
}
@ -522,4 +560,3 @@ out:
spin_unlock(&hsotg->lock);
return retval;
}
EXPORT_SYMBOL_GPL(dwc2_handle_common_intr);

View File

@ -0,0 +1,27 @@
/**
* debug.h - Designware USB2 DRD controller debug header
*
* Copyright (C) 2015 Intel Corporation
* Mian Yousaf Kaukab <yousaf.kaukab@intel.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 of
* the License 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.
*/
#include "core.h"
#ifdef CONFIG_DEBUG_FS
extern int dwc2_debugfs_init(struct dwc2_hsotg *);
extern void dwc2_debugfs_exit(struct dwc2_hsotg *);
#else
static inline int dwc2_debugfs_init(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg)
{ }
#endif

View File

@ -0,0 +1,771 @@
/**
* debugfs.c - Designware USB2 DRD controller debugfs
*
* Copyright (C) 2015 Intel Corporation
* Mian Yousaf Kaukab <yousaf.kaukab@intel.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 of
* the License 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.
*/
#include <linux/spinlock.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include "core.h"
#include "debug.h"
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
/**
* testmode_write - debugfs: change usb test mode
* @seq: The seq file to write to.
* @v: Unused parameter.
*
* This debugfs entry modify the current usb test mode.
*/
static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t
count, loff_t *ppos)
{
struct seq_file *s = file->private_data;
struct dwc2_hsotg *hsotg = s->private;
unsigned long flags;
u32 testmode = 0;
char buf[32];
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (!strncmp(buf, "test_j", 6))
testmode = TEST_J;
else if (!strncmp(buf, "test_k", 6))
testmode = TEST_K;
else if (!strncmp(buf, "test_se0_nak", 12))
testmode = TEST_SE0_NAK;
else if (!strncmp(buf, "test_packet", 11))
testmode = TEST_PACKET;
else if (!strncmp(buf, "test_force_enable", 17))
testmode = TEST_FORCE_EN;
else
testmode = 0;
spin_lock_irqsave(&hsotg->lock, flags);
s3c_hsotg_set_test_mode(hsotg, testmode);
spin_unlock_irqrestore(&hsotg->lock, flags);
return count;
}
/**
* testmode_show - debugfs: show usb test mode state
* @seq: The seq file to write to.
* @v: Unused parameter.
*
* This debugfs entry shows which usb test mode is currently enabled.
*/
static int testmode_show(struct seq_file *s, void *unused)
{
struct dwc2_hsotg *hsotg = s->private;
unsigned long flags;
int dctl;
spin_lock_irqsave(&hsotg->lock, flags);
dctl = readl(hsotg->regs + DCTL);
dctl &= DCTL_TSTCTL_MASK;
dctl >>= DCTL_TSTCTL_SHIFT;
spin_unlock_irqrestore(&hsotg->lock, flags);
switch (dctl) {
case 0:
seq_puts(s, "no test\n");
break;
case TEST_J:
seq_puts(s, "test_j\n");
break;
case TEST_K:
seq_puts(s, "test_k\n");
break;
case TEST_SE0_NAK:
seq_puts(s, "test_se0_nak\n");
break;
case TEST_PACKET:
seq_puts(s, "test_packet\n");
break;
case TEST_FORCE_EN:
seq_puts(s, "test_force_enable\n");
break;
default:
seq_printf(s, "UNKNOWN %d\n", dctl);
}
return 0;
}
static int testmode_open(struct inode *inode, struct file *file)
{
return single_open(file, testmode_show, inode->i_private);
}
static const struct file_operations testmode_fops = {
.owner = THIS_MODULE,
.open = testmode_open,
.write = testmode_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* state_show - debugfs: show overall driver and device state.
* @seq: The seq file to write to.
* @v: Unused parameter.
*
* This debugfs entry shows the overall state of the hardware and
* some general information about each of the endpoints available
* to the system.
*/
static int state_show(struct seq_file *seq, void *v)
{
struct dwc2_hsotg *hsotg = seq->private;
void __iomem *regs = hsotg->regs;
int idx;
seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n",
readl(regs + DCFG),
readl(regs + DCTL),
readl(regs + DSTS));
seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n",
readl(regs + DIEPMSK), readl(regs + DOEPMSK));
seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n",
readl(regs + GINTMSK),
readl(regs + GINTSTS));
seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n",
readl(regs + DAINTMSK),
readl(regs + DAINT));
seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n",
readl(regs + GNPTXSTS),
readl(regs + GRXSTSR));
seq_puts(seq, "\nEndpoint status:\n");
for (idx = 0; idx < hsotg->num_of_eps; idx++) {
u32 in, out;
in = readl(regs + DIEPCTL(idx));
out = readl(regs + DOEPCTL(idx));
seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x",
idx, in, out);
in = readl(regs + DIEPTSIZ(idx));
out = readl(regs + DOEPTSIZ(idx));
seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x",
in, out);
seq_puts(seq, "\n");
}
return 0;
}
static int state_open(struct inode *inode, struct file *file)
{
return single_open(file, state_show, inode->i_private);
}
static const struct file_operations state_fops = {
.owner = THIS_MODULE,
.open = state_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* fifo_show - debugfs: show the fifo information
* @seq: The seq_file to write data to.
* @v: Unused parameter.
*
* Show the FIFO information for the overall fifo and all the
* periodic transmission FIFOs.
*/
static int fifo_show(struct seq_file *seq, void *v)
{
struct dwc2_hsotg *hsotg = seq->private;
void __iomem *regs = hsotg->regs;
u32 val;
int idx;
seq_puts(seq, "Non-periodic FIFOs:\n");
seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ));
val = readl(regs + GNPTXFSIZ);
seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n",
val >> FIFOSIZE_DEPTH_SHIFT,
val & FIFOSIZE_DEPTH_MASK);
seq_puts(seq, "\nPeriodic TXFIFOs:\n");
for (idx = 1; idx < hsotg->num_of_eps; idx++) {
val = readl(regs + DPTXFSIZN(idx));
seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx,
val >> FIFOSIZE_DEPTH_SHIFT,
val & FIFOSIZE_STARTADDR_MASK);
}
return 0;
}
static int fifo_open(struct inode *inode, struct file *file)
{
return single_open(file, fifo_show, inode->i_private);
}
static const struct file_operations fifo_fops = {
.owner = THIS_MODULE,
.open = fifo_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const char *decode_direction(int is_in)
{
return is_in ? "in" : "out";
}
/**
* ep_show - debugfs: show the state of an endpoint.
* @seq: The seq_file to write data to.
* @v: Unused parameter.
*
* This debugfs entry shows the state of the given endpoint (one is
* registered for each available).
*/
static int ep_show(struct seq_file *seq, void *v)
{
struct s3c_hsotg_ep *ep = seq->private;
struct dwc2_hsotg *hsotg = ep->parent;
struct s3c_hsotg_req *req;
void __iomem *regs = hsotg->regs;
int index = ep->index;
int show_limit = 15;
unsigned long flags;
seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n",
ep->index, ep->ep.name, decode_direction(ep->dir_in));
/* first show the register state */
seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n",
readl(regs + DIEPCTL(index)),
readl(regs + DOEPCTL(index)));
seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n",
readl(regs + DIEPDMA(index)),
readl(regs + DOEPDMA(index)));
seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n",
readl(regs + DIEPINT(index)),
readl(regs + DOEPINT(index)));
seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n",
readl(regs + DIEPTSIZ(index)),
readl(regs + DOEPTSIZ(index)));
seq_puts(seq, "\n");
seq_printf(seq, "mps %d\n", ep->ep.maxpacket);
seq_printf(seq, "total_data=%ld\n", ep->total_data);
seq_printf(seq, "request list (%p,%p):\n",
ep->queue.next, ep->queue.prev);
spin_lock_irqsave(&hsotg->lock, flags);
list_for_each_entry(req, &ep->queue, queue) {
if (--show_limit < 0) {
seq_puts(seq, "not showing more requests...\n");
break;
}
seq_printf(seq, "%c req %p: %d bytes @%p, ",
req == ep->req ? '*' : ' ',
req, req->req.length, req->req.buf);
seq_printf(seq, "%d done, res %d\n",
req->req.actual, req->req.status);
}
spin_unlock_irqrestore(&hsotg->lock, flags);
return 0;
}
static int ep_open(struct inode *inode, struct file *file)
{
return single_open(file, ep_show, inode->i_private);
}
static const struct file_operations ep_fops = {
.owner = THIS_MODULE,
.open = ep_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* s3c_hsotg_create_debug - create debugfs directory and files
* @hsotg: The driver state
*
* Create the debugfs files to allow the user to get information
* about the state of the system. The directory name is created
* with the same name as the device itself, in case we end up
* with multiple blocks in future systems.
*/
static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg)
{
struct dentry *root;
struct dentry *file;
unsigned epidx;
root = hsotg->debug_root;
/* create general state file */
file = debugfs_create_file("state", S_IRUGO, root, hsotg, &state_fops);
if (IS_ERR(file))
dev_err(hsotg->dev, "%s: failed to create state\n", __func__);
file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, hsotg,
&testmode_fops);
if (IS_ERR(file))
dev_err(hsotg->dev, "%s: failed to create testmode\n",
__func__);
file = debugfs_create_file("fifo", S_IRUGO, root, hsotg, &fifo_fops);
if (IS_ERR(file))
dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__);
/* Create one file for each out endpoint */
for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
struct s3c_hsotg_ep *ep;
ep = hsotg->eps_out[epidx];
if (ep) {
file = debugfs_create_file(ep->name, S_IRUGO,
root, ep, &ep_fops);
if (IS_ERR(file))
dev_err(hsotg->dev, "failed to create %s debug file\n",
ep->name);
}
}
/* Create one file for each in endpoint. EP0 is handled with out eps */
for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) {
struct s3c_hsotg_ep *ep;
ep = hsotg->eps_in[epidx];
if (ep) {
file = debugfs_create_file(ep->name, S_IRUGO,
root, ep, &ep_fops);
if (IS_ERR(file))
dev_err(hsotg->dev, "failed to create %s debug file\n",
ep->name);
}
}
}
#else
static inline void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) {}
#endif
/* s3c_hsotg_delete_debug is removed as cleanup in done in dwc2_debugfs_exit */
#define dump_register(nm) \
{ \
.name = #nm, \
.offset = nm, \
}
static const struct debugfs_reg32 dwc2_regs[] = {
/*
* Accessing registers like this can trigger mode mismatch interrupt.
* However, according to dwc2 databook, the register access, in this
* case, is completed on the processor bus but is ignored by the core
* and does not affect its operation.
*/
dump_register(GOTGCTL),
dump_register(GOTGINT),
dump_register(GAHBCFG),
dump_register(GUSBCFG),
dump_register(GRSTCTL),
dump_register(GINTSTS),
dump_register(GINTMSK),
dump_register(GRXSTSR),
dump_register(GRXSTSP),
dump_register(GRXFSIZ),
dump_register(GNPTXFSIZ),
dump_register(GNPTXSTS),
dump_register(GI2CCTL),
dump_register(GPVNDCTL),
dump_register(GGPIO),
dump_register(GUID),
dump_register(GSNPSID),
dump_register(GHWCFG1),
dump_register(GHWCFG2),
dump_register(GHWCFG3),
dump_register(GHWCFG4),
dump_register(GLPMCFG),
dump_register(GPWRDN),
dump_register(GDFIFOCFG),
dump_register(ADPCTL),
dump_register(HPTXFSIZ),
dump_register(DPTXFSIZN(1)),
dump_register(DPTXFSIZN(2)),
dump_register(DPTXFSIZN(3)),
dump_register(DPTXFSIZN(4)),
dump_register(DPTXFSIZN(5)),
dump_register(DPTXFSIZN(6)),
dump_register(DPTXFSIZN(7)),
dump_register(DPTXFSIZN(8)),
dump_register(DPTXFSIZN(9)),
dump_register(DPTXFSIZN(10)),
dump_register(DPTXFSIZN(11)),
dump_register(DPTXFSIZN(12)),
dump_register(DPTXFSIZN(13)),
dump_register(DPTXFSIZN(14)),
dump_register(DPTXFSIZN(15)),
dump_register(DCFG),
dump_register(DCTL),
dump_register(DSTS),
dump_register(DIEPMSK),
dump_register(DOEPMSK),
dump_register(DAINT),
dump_register(DAINTMSK),
dump_register(DTKNQR1),
dump_register(DTKNQR2),
dump_register(DTKNQR3),
dump_register(DTKNQR4),
dump_register(DVBUSDIS),
dump_register(DVBUSPULSE),
dump_register(DIEPCTL(0)),
dump_register(DIEPCTL(1)),
dump_register(DIEPCTL(2)),
dump_register(DIEPCTL(3)),
dump_register(DIEPCTL(4)),
dump_register(DIEPCTL(5)),
dump_register(DIEPCTL(6)),
dump_register(DIEPCTL(7)),
dump_register(DIEPCTL(8)),
dump_register(DIEPCTL(9)),
dump_register(DIEPCTL(10)),
dump_register(DIEPCTL(11)),
dump_register(DIEPCTL(12)),
dump_register(DIEPCTL(13)),
dump_register(DIEPCTL(14)),
dump_register(DIEPCTL(15)),
dump_register(DOEPCTL(0)),
dump_register(DOEPCTL(1)),
dump_register(DOEPCTL(2)),
dump_register(DOEPCTL(3)),
dump_register(DOEPCTL(4)),
dump_register(DOEPCTL(5)),
dump_register(DOEPCTL(6)),
dump_register(DOEPCTL(7)),
dump_register(DOEPCTL(8)),
dump_register(DOEPCTL(9)),
dump_register(DOEPCTL(10)),
dump_register(DOEPCTL(11)),
dump_register(DOEPCTL(12)),
dump_register(DOEPCTL(13)),
dump_register(DOEPCTL(14)),
dump_register(DOEPCTL(15)),
dump_register(DIEPINT(0)),
dump_register(DIEPINT(1)),
dump_register(DIEPINT(2)),
dump_register(DIEPINT(3)),
dump_register(DIEPINT(4)),
dump_register(DIEPINT(5)),
dump_register(DIEPINT(6)),
dump_register(DIEPINT(7)),
dump_register(DIEPINT(8)),
dump_register(DIEPINT(9)),
dump_register(DIEPINT(10)),
dump_register(DIEPINT(11)),
dump_register(DIEPINT(12)),
dump_register(DIEPINT(13)),
dump_register(DIEPINT(14)),
dump_register(DIEPINT(15)),
dump_register(DOEPINT(0)),
dump_register(DOEPINT(1)),
dump_register(DOEPINT(2)),
dump_register(DOEPINT(3)),
dump_register(DOEPINT(4)),
dump_register(DOEPINT(5)),
dump_register(DOEPINT(6)),
dump_register(DOEPINT(7)),
dump_register(DOEPINT(8)),
dump_register(DOEPINT(9)),
dump_register(DOEPINT(10)),
dump_register(DOEPINT(11)),
dump_register(DOEPINT(12)),
dump_register(DOEPINT(13)),
dump_register(DOEPINT(14)),
dump_register(DOEPINT(15)),
dump_register(DIEPTSIZ(0)),
dump_register(DIEPTSIZ(1)),
dump_register(DIEPTSIZ(2)),
dump_register(DIEPTSIZ(3)),
dump_register(DIEPTSIZ(4)),
dump_register(DIEPTSIZ(5)),
dump_register(DIEPTSIZ(6)),
dump_register(DIEPTSIZ(7)),
dump_register(DIEPTSIZ(8)),
dump_register(DIEPTSIZ(9)),
dump_register(DIEPTSIZ(10)),
dump_register(DIEPTSIZ(11)),
dump_register(DIEPTSIZ(12)),
dump_register(DIEPTSIZ(13)),
dump_register(DIEPTSIZ(14)),
dump_register(DIEPTSIZ(15)),
dump_register(DOEPTSIZ(0)),
dump_register(DOEPTSIZ(1)),
dump_register(DOEPTSIZ(2)),
dump_register(DOEPTSIZ(3)),
dump_register(DOEPTSIZ(4)),
dump_register(DOEPTSIZ(5)),
dump_register(DOEPTSIZ(6)),
dump_register(DOEPTSIZ(7)),
dump_register(DOEPTSIZ(8)),
dump_register(DOEPTSIZ(9)),
dump_register(DOEPTSIZ(10)),
dump_register(DOEPTSIZ(11)),
dump_register(DOEPTSIZ(12)),
dump_register(DOEPTSIZ(13)),
dump_register(DOEPTSIZ(14)),
dump_register(DOEPTSIZ(15)),
dump_register(DIEPDMA(0)),
dump_register(DIEPDMA(1)),
dump_register(DIEPDMA(2)),
dump_register(DIEPDMA(3)),
dump_register(DIEPDMA(4)),
dump_register(DIEPDMA(5)),
dump_register(DIEPDMA(6)),
dump_register(DIEPDMA(7)),
dump_register(DIEPDMA(8)),
dump_register(DIEPDMA(9)),
dump_register(DIEPDMA(10)),
dump_register(DIEPDMA(11)),
dump_register(DIEPDMA(12)),
dump_register(DIEPDMA(13)),
dump_register(DIEPDMA(14)),
dump_register(DIEPDMA(15)),
dump_register(DOEPDMA(0)),
dump_register(DOEPDMA(1)),
dump_register(DOEPDMA(2)),
dump_register(DOEPDMA(3)),
dump_register(DOEPDMA(4)),
dump_register(DOEPDMA(5)),
dump_register(DOEPDMA(6)),
dump_register(DOEPDMA(7)),
dump_register(DOEPDMA(8)),
dump_register(DOEPDMA(9)),
dump_register(DOEPDMA(10)),
dump_register(DOEPDMA(11)),
dump_register(DOEPDMA(12)),
dump_register(DOEPDMA(13)),
dump_register(DOEPDMA(14)),
dump_register(DOEPDMA(15)),
dump_register(DTXFSTS(0)),
dump_register(DTXFSTS(1)),
dump_register(DTXFSTS(2)),
dump_register(DTXFSTS(3)),
dump_register(DTXFSTS(4)),
dump_register(DTXFSTS(5)),
dump_register(DTXFSTS(6)),
dump_register(DTXFSTS(7)),
dump_register(DTXFSTS(8)),
dump_register(DTXFSTS(9)),
dump_register(DTXFSTS(10)),
dump_register(DTXFSTS(11)),
dump_register(DTXFSTS(12)),
dump_register(DTXFSTS(13)),
dump_register(DTXFSTS(14)),
dump_register(DTXFSTS(15)),
dump_register(PCGCTL),
dump_register(HCFG),
dump_register(HFIR),
dump_register(HFNUM),
dump_register(HPTXSTS),
dump_register(HAINT),
dump_register(HAINTMSK),
dump_register(HFLBADDR),
dump_register(HPRT0),
dump_register(HCCHAR(0)),
dump_register(HCCHAR(1)),
dump_register(HCCHAR(2)),
dump_register(HCCHAR(3)),
dump_register(HCCHAR(4)),
dump_register(HCCHAR(5)),
dump_register(HCCHAR(6)),
dump_register(HCCHAR(7)),
dump_register(HCCHAR(8)),
dump_register(HCCHAR(9)),
dump_register(HCCHAR(10)),
dump_register(HCCHAR(11)),
dump_register(HCCHAR(12)),
dump_register(HCCHAR(13)),
dump_register(HCCHAR(14)),
dump_register(HCCHAR(15)),
dump_register(HCSPLT(0)),
dump_register(HCSPLT(1)),
dump_register(HCSPLT(2)),
dump_register(HCSPLT(3)),
dump_register(HCSPLT(4)),
dump_register(HCSPLT(5)),
dump_register(HCSPLT(6)),
dump_register(HCSPLT(7)),
dump_register(HCSPLT(8)),
dump_register(HCSPLT(9)),
dump_register(HCSPLT(10)),
dump_register(HCSPLT(11)),
dump_register(HCSPLT(12)),
dump_register(HCSPLT(13)),
dump_register(HCSPLT(14)),
dump_register(HCSPLT(15)),
dump_register(HCINT(0)),
dump_register(HCINT(1)),
dump_register(HCINT(2)),
dump_register(HCINT(3)),
dump_register(HCINT(4)),
dump_register(HCINT(5)),
dump_register(HCINT(6)),
dump_register(HCINT(7)),
dump_register(HCINT(8)),
dump_register(HCINT(9)),
dump_register(HCINT(10)),
dump_register(HCINT(11)),
dump_register(HCINT(12)),
dump_register(HCINT(13)),
dump_register(HCINT(14)),
dump_register(HCINT(15)),
dump_register(HCINTMSK(0)),
dump_register(HCINTMSK(1)),
dump_register(HCINTMSK(2)),
dump_register(HCINTMSK(3)),
dump_register(HCINTMSK(4)),
dump_register(HCINTMSK(5)),
dump_register(HCINTMSK(6)),
dump_register(HCINTMSK(7)),
dump_register(HCINTMSK(8)),
dump_register(HCINTMSK(9)),
dump_register(HCINTMSK(10)),
dump_register(HCINTMSK(11)),
dump_register(HCINTMSK(12)),
dump_register(HCINTMSK(13)),
dump_register(HCINTMSK(14)),
dump_register(HCINTMSK(15)),
dump_register(HCTSIZ(0)),
dump_register(HCTSIZ(1)),
dump_register(HCTSIZ(2)),
dump_register(HCTSIZ(3)),
dump_register(HCTSIZ(4)),
dump_register(HCTSIZ(5)),
dump_register(HCTSIZ(6)),
dump_register(HCTSIZ(7)),
dump_register(HCTSIZ(8)),
dump_register(HCTSIZ(9)),
dump_register(HCTSIZ(10)),
dump_register(HCTSIZ(11)),
dump_register(HCTSIZ(12)),
dump_register(HCTSIZ(13)),
dump_register(HCTSIZ(14)),
dump_register(HCTSIZ(15)),
dump_register(HCDMA(0)),
dump_register(HCDMA(1)),
dump_register(HCDMA(2)),
dump_register(HCDMA(3)),
dump_register(HCDMA(4)),
dump_register(HCDMA(5)),
dump_register(HCDMA(6)),
dump_register(HCDMA(7)),
dump_register(HCDMA(8)),
dump_register(HCDMA(9)),
dump_register(HCDMA(10)),
dump_register(HCDMA(11)),
dump_register(HCDMA(12)),
dump_register(HCDMA(13)),
dump_register(HCDMA(14)),
dump_register(HCDMA(15)),
dump_register(HCDMAB(0)),
dump_register(HCDMAB(1)),
dump_register(HCDMAB(2)),
dump_register(HCDMAB(3)),
dump_register(HCDMAB(4)),
dump_register(HCDMAB(5)),
dump_register(HCDMAB(6)),
dump_register(HCDMAB(7)),
dump_register(HCDMAB(8)),
dump_register(HCDMAB(9)),
dump_register(HCDMAB(10)),
dump_register(HCDMAB(11)),
dump_register(HCDMAB(12)),
dump_register(HCDMAB(13)),
dump_register(HCDMAB(14)),
dump_register(HCDMAB(15)),
};
int dwc2_debugfs_init(struct dwc2_hsotg *hsotg)
{
int ret;
struct dentry *file;
hsotg->debug_root = debugfs_create_dir(dev_name(hsotg->dev), NULL);
if (!hsotg->debug_root) {
ret = -ENOMEM;
goto err0;
}
/* Add gadget debugfs nodes */
s3c_hsotg_create_debug(hsotg);
hsotg->regset = devm_kzalloc(hsotg->dev, sizeof(*hsotg->regset),
GFP_KERNEL);
if (!hsotg->regset) {
ret = -ENOMEM;
goto err1;
}
hsotg->regset->regs = dwc2_regs;
hsotg->regset->nregs = ARRAY_SIZE(dwc2_regs);
hsotg->regset->base = hsotg->regs;
file = debugfs_create_regset32("regdump", S_IRUGO, hsotg->debug_root,
hsotg->regset);
if (!file) {
ret = -ENOMEM;
goto err1;
}
return 0;
err1:
debugfs_remove_recursive(hsotg->debug_root);
err0:
return ret;
}
void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg)
{
debugfs_remove_recursive(hsotg->debug_root);
hsotg->debug_root = NULL;
}

View File

@ -20,7 +20,6 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/debugfs.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/delay.h>
@ -35,7 +34,6 @@
#include <linux/usb/gadget.h>
#include <linux/usb/phy.h>
#include <linux/platform_data/s3c-hsotg.h>
#include <linux/uaccess.h>
#include "core.h"
#include "hw.h"
@ -792,6 +790,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
ep->name, req, req->length, req->buf, req->no_interrupt,
req->zero, req->short_not_ok);
/* Prevent new request submission when controller is suspended */
if (hs->lx_state == DWC2_L2) {
dev_dbg(hs->dev, "%s: don't submit request while suspended\n",
__func__);
return -EAGAIN;
}
/* initialise status of the request */
INIT_LIST_HEAD(&hs_req->queue);
req->actual = 0;
@ -894,7 +899,7 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
* @testmode: requested usb test mode
* Enable usb Test Mode requested by the Host.
*/
static int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode)
int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode)
{
int dctl = readl(hsotg->regs + DCTL);
@ -2185,7 +2190,6 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg)
call_gadget(hsotg, disconnect);
}
EXPORT_SYMBOL_GPL(s3c_hsotg_disconnect);
/**
* s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler
@ -2310,8 +2314,9 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
writel(GINTSTS_ERLYSUSP | GINTSTS_SESSREQINT |
GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF |
GINTSTS_CONIDSTSCHNG | GINTSTS_USBRST |
GINTSTS_ENUMDONE | GINTSTS_OTGINT |
GINTSTS_USBSUSP | GINTSTS_WKUPINT,
GINTSTS_RESETDET | GINTSTS_ENUMDONE |
GINTSTS_OTGINT | GINTSTS_USBSUSP |
GINTSTS_WKUPINT,
hsotg->regs + GINTMSK);
if (using_dma(hsotg))
@ -2477,7 +2482,19 @@ irq_retry:
}
}
if (gintsts & GINTSTS_USBRST) {
if (gintsts & GINTSTS_RESETDET) {
dev_dbg(hsotg->dev, "%s: USBRstDet\n", __func__);
writel(GINTSTS_RESETDET, hsotg->regs + GINTSTS);
/* This event must be used only if controller is suspended */
if (hsotg->lx_state == DWC2_L2) {
dwc2_exit_hibernation(hsotg, true);
hsotg->lx_state = DWC2_L0;
}
}
if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) {
u32 usb_status = readl(hsotg->regs + GOTGCTL);
@ -2497,6 +2514,7 @@ irq_retry:
kill_all_requests(hsotg, hsotg->eps_out[0],
-ECONNRESET);
hsotg->lx_state = DWC2_L0;
s3c_hsotg_core_init_disconnected(hsotg, true);
}
}
@ -2745,7 +2763,7 @@ error:
* s3c_hsotg_ep_disable - disable given endpoint
* @ep: The endpoint to disable.
*/
static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force)
static int s3c_hsotg_ep_disable(struct usb_ep *ep)
{
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hsotg = hs_ep->parent;
@ -2788,10 +2806,6 @@ static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force)
return 0;
}
static int s3c_hsotg_ep_disable(struct usb_ep *ep)
{
return s3c_hsotg_ep_disable_force(ep, false);
}
/**
* on_list - check request is on the given endpoint
* @ep: The endpoint to check.
@ -3187,6 +3201,14 @@ static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active)
spin_lock_irqsave(&hsotg->lock, flags);
if (is_active) {
/*
* If controller is hibernated, it must exit from hibernation
* before being initialized
*/
if (hsotg->lx_state == DWC2_L2) {
dwc2_exit_hibernation(hsotg, false);
hsotg->lx_state = DWC2_L0;
}
/* Kill any ep0 requests as controller will be reinitialized */
kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
s3c_hsotg_core_init_disconnected(hsotg, false);
@ -3391,404 +3413,6 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg)
#endif
}
/**
* testmode_write - debugfs: change usb test mode
* @seq: The seq file to write to.
* @v: Unused parameter.
*
* This debugfs entry modify the current usb test mode.
*/
static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t
count, loff_t *ppos)
{
struct seq_file *s = file->private_data;
struct dwc2_hsotg *hsotg = s->private;
unsigned long flags;
u32 testmode = 0;
char buf[32];
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (!strncmp(buf, "test_j", 6))
testmode = TEST_J;
else if (!strncmp(buf, "test_k", 6))
testmode = TEST_K;
else if (!strncmp(buf, "test_se0_nak", 12))
testmode = TEST_SE0_NAK;
else if (!strncmp(buf, "test_packet", 11))
testmode = TEST_PACKET;
else if (!strncmp(buf, "test_force_enable", 17))
testmode = TEST_FORCE_EN;
else
testmode = 0;
spin_lock_irqsave(&hsotg->lock, flags);
s3c_hsotg_set_test_mode(hsotg, testmode);
spin_unlock_irqrestore(&hsotg->lock, flags);
return count;
}
/**
* testmode_show - debugfs: show usb test mode state
* @seq: The seq file to write to.
* @v: Unused parameter.
*
* This debugfs entry shows which usb test mode is currently enabled.
*/
static int testmode_show(struct seq_file *s, void *unused)
{
struct dwc2_hsotg *hsotg = s->private;
unsigned long flags;
int dctl;
spin_lock_irqsave(&hsotg->lock, flags);
dctl = readl(hsotg->regs + DCTL);
dctl &= DCTL_TSTCTL_MASK;
dctl >>= DCTL_TSTCTL_SHIFT;
spin_unlock_irqrestore(&hsotg->lock, flags);
switch (dctl) {
case 0:
seq_puts(s, "no test\n");
break;
case TEST_J:
seq_puts(s, "test_j\n");
break;
case TEST_K:
seq_puts(s, "test_k\n");
break;
case TEST_SE0_NAK:
seq_puts(s, "test_se0_nak\n");
break;
case TEST_PACKET:
seq_puts(s, "test_packet\n");
break;
case TEST_FORCE_EN:
seq_puts(s, "test_force_enable\n");
break;
default:
seq_printf(s, "UNKNOWN %d\n", dctl);
}
return 0;
}
static int testmode_open(struct inode *inode, struct file *file)
{
return single_open(file, testmode_show, inode->i_private);
}
static const struct file_operations testmode_fops = {
.owner = THIS_MODULE,
.open = testmode_open,
.write = testmode_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* state_show - debugfs: show overall driver and device state.
* @seq: The seq file to write to.
* @v: Unused parameter.
*
* This debugfs entry shows the overall state of the hardware and
* some general information about each of the endpoints available
* to the system.
*/
static int state_show(struct seq_file *seq, void *v)
{
struct dwc2_hsotg *hsotg = seq->private;
void __iomem *regs = hsotg->regs;
int idx;
seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n",
readl(regs + DCFG),
readl(regs + DCTL),
readl(regs + DSTS));
seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n",
readl(regs + DIEPMSK), readl(regs + DOEPMSK));
seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n",
readl(regs + GINTMSK),
readl(regs + GINTSTS));
seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n",
readl(regs + DAINTMSK),
readl(regs + DAINT));
seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n",
readl(regs + GNPTXSTS),
readl(regs + GRXSTSR));
seq_puts(seq, "\nEndpoint status:\n");
for (idx = 0; idx < hsotg->num_of_eps; idx++) {
u32 in, out;
in = readl(regs + DIEPCTL(idx));
out = readl(regs + DOEPCTL(idx));
seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x",
idx, in, out);
in = readl(regs + DIEPTSIZ(idx));
out = readl(regs + DOEPTSIZ(idx));
seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x",
in, out);
seq_puts(seq, "\n");
}
return 0;
}
static int state_open(struct inode *inode, struct file *file)
{
return single_open(file, state_show, inode->i_private);
}
static const struct file_operations state_fops = {
.owner = THIS_MODULE,
.open = state_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* fifo_show - debugfs: show the fifo information
* @seq: The seq_file to write data to.
* @v: Unused parameter.
*
* Show the FIFO information for the overall fifo and all the
* periodic transmission FIFOs.
*/
static int fifo_show(struct seq_file *seq, void *v)
{
struct dwc2_hsotg *hsotg = seq->private;
void __iomem *regs = hsotg->regs;
u32 val;
int idx;
seq_puts(seq, "Non-periodic FIFOs:\n");
seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ));
val = readl(regs + GNPTXFSIZ);
seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n",
val >> FIFOSIZE_DEPTH_SHIFT,
val & FIFOSIZE_DEPTH_MASK);
seq_puts(seq, "\nPeriodic TXFIFOs:\n");
for (idx = 1; idx < hsotg->num_of_eps; idx++) {
val = readl(regs + DPTXFSIZN(idx));
seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx,
val >> FIFOSIZE_DEPTH_SHIFT,
val & FIFOSIZE_STARTADDR_MASK);
}
return 0;
}
static int fifo_open(struct inode *inode, struct file *file)
{
return single_open(file, fifo_show, inode->i_private);
}
static const struct file_operations fifo_fops = {
.owner = THIS_MODULE,
.open = fifo_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const char *decode_direction(int is_in)
{
return is_in ? "in" : "out";
}
/**
* ep_show - debugfs: show the state of an endpoint.
* @seq: The seq_file to write data to.
* @v: Unused parameter.
*
* This debugfs entry shows the state of the given endpoint (one is
* registered for each available).
*/
static int ep_show(struct seq_file *seq, void *v)
{
struct s3c_hsotg_ep *ep = seq->private;
struct dwc2_hsotg *hsotg = ep->parent;
struct s3c_hsotg_req *req;
void __iomem *regs = hsotg->regs;
int index = ep->index;
int show_limit = 15;
unsigned long flags;
seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n",
ep->index, ep->ep.name, decode_direction(ep->dir_in));
/* first show the register state */
seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n",
readl(regs + DIEPCTL(index)),
readl(regs + DOEPCTL(index)));
seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n",
readl(regs + DIEPDMA(index)),
readl(regs + DOEPDMA(index)));
seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n",
readl(regs + DIEPINT(index)),
readl(regs + DOEPINT(index)));
seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n",
readl(regs + DIEPTSIZ(index)),
readl(regs + DOEPTSIZ(index)));
seq_puts(seq, "\n");
seq_printf(seq, "mps %d\n", ep->ep.maxpacket);
seq_printf(seq, "total_data=%ld\n", ep->total_data);
seq_printf(seq, "request list (%p,%p):\n",
ep->queue.next, ep->queue.prev);
spin_lock_irqsave(&hsotg->lock, flags);
list_for_each_entry(req, &ep->queue, queue) {
if (--show_limit < 0) {
seq_puts(seq, "not showing more requests...\n");
break;
}
seq_printf(seq, "%c req %p: %d bytes @%p, ",
req == ep->req ? '*' : ' ',
req, req->req.length, req->req.buf);
seq_printf(seq, "%d done, res %d\n",
req->req.actual, req->req.status);
}
spin_unlock_irqrestore(&hsotg->lock, flags);
return 0;
}
static int ep_open(struct inode *inode, struct file *file)
{
return single_open(file, ep_show, inode->i_private);
}
static const struct file_operations ep_fops = {
.owner = THIS_MODULE,
.open = ep_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* s3c_hsotg_create_debug - create debugfs directory and files
* @hsotg: The driver state
*
* Create the debugfs files to allow the user to get information
* about the state of the system. The directory name is created
* with the same name as the device itself, in case we end up
* with multiple blocks in future systems.
*/
static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg)
{
struct dentry *root;
unsigned epidx;
root = debugfs_create_dir(dev_name(hsotg->dev), NULL);
hsotg->debug_root = root;
if (IS_ERR(root)) {
dev_err(hsotg->dev, "cannot create debug root\n");
return;
}
/* create general state file */
hsotg->debug_file = debugfs_create_file("state", S_IRUGO, root,
hsotg, &state_fops);
if (IS_ERR(hsotg->debug_file))
dev_err(hsotg->dev, "%s: failed to create state\n", __func__);
hsotg->debug_testmode = debugfs_create_file("testmode",
S_IRUGO | S_IWUSR, root,
hsotg, &testmode_fops);
if (IS_ERR(hsotg->debug_testmode))
dev_err(hsotg->dev, "%s: failed to create testmode\n",
__func__);
hsotg->debug_fifo = debugfs_create_file("fifo", S_IRUGO, root,
hsotg, &fifo_fops);
if (IS_ERR(hsotg->debug_fifo))
dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__);
/* Create one file for each out endpoint */
for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
struct s3c_hsotg_ep *ep;
ep = hsotg->eps_out[epidx];
if (ep) {
ep->debugfs = debugfs_create_file(ep->name, S_IRUGO,
root, ep, &ep_fops);
if (IS_ERR(ep->debugfs))
dev_err(hsotg->dev, "failed to create %s debug file\n",
ep->name);
}
}
/* Create one file for each in endpoint. EP0 is handled with out eps */
for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) {
struct s3c_hsotg_ep *ep;
ep = hsotg->eps_in[epidx];
if (ep) {
ep->debugfs = debugfs_create_file(ep->name, S_IRUGO,
root, ep, &ep_fops);
if (IS_ERR(ep->debugfs))
dev_err(hsotg->dev, "failed to create %s debug file\n",
ep->name);
}
}
}
/**
* s3c_hsotg_delete_debug - cleanup debugfs entries
* @hsotg: The driver state
*
* Cleanup (remove) the debugfs files for use on module exit.
*/
static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg)
{
unsigned epidx;
for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
if (hsotg->eps_in[epidx])
debugfs_remove(hsotg->eps_in[epidx]->debugfs);
if (hsotg->eps_out[epidx])
debugfs_remove(hsotg->eps_out[epidx]->debugfs);
}
debugfs_remove(hsotg->debug_file);
debugfs_remove(hsotg->debug_testmode);
debugfs_remove(hsotg->debug_fifo);
debugfs_remove(hsotg->debug_root);
}
#ifdef CONFIG_OF
static void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg)
{
@ -3896,6 +3520,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
hsotg->gadget.max_speed = USB_SPEED_HIGH;
hsotg->gadget.ops = &s3c_hsotg_gadget_ops;
hsotg->gadget.name = dev_name(dev);
if (hsotg->dr_mode == USB_DR_MODE_OTG)
hsotg->gadget.is_otg = 1;
/* reset the system */
@ -4028,8 +3654,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
if (ret)
goto err_supplies;
s3c_hsotg_create_debug(hsotg);
s3c_hsotg_dump(hsotg);
return 0;
@ -4041,7 +3665,6 @@ err_clk:
return ret;
}
EXPORT_SYMBOL_GPL(dwc2_gadget_init);
/**
* s3c_hsotg_remove - remove function for hsotg driver
@ -4050,18 +3673,19 @@ EXPORT_SYMBOL_GPL(dwc2_gadget_init);
int s3c_hsotg_remove(struct dwc2_hsotg *hsotg)
{
usb_del_gadget_udc(&hsotg->gadget);
s3c_hsotg_delete_debug(hsotg);
clk_disable_unprepare(hsotg->clk);
return 0;
}
EXPORT_SYMBOL_GPL(s3c_hsotg_remove);
int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg)
{
unsigned long flags;
int ret = 0;
if (hsotg->lx_state != DWC2_L0)
return ret;
mutex_lock(&hsotg->init_mutex);
if (hsotg->driver) {
@ -4095,13 +3719,15 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg)
return ret;
}
EXPORT_SYMBOL_GPL(s3c_hsotg_suspend);
int s3c_hsotg_resume(struct dwc2_hsotg *hsotg)
{
unsigned long flags;
int ret = 0;
if (hsotg->lx_state == DWC2_L2)
return ret;
mutex_lock(&hsotg->init_mutex);
if (hsotg->driver) {
@ -4124,4 +3750,3 @@ int s3c_hsotg_resume(struct dwc2_hsotg *hsotg)
return ret;
}
EXPORT_SYMBOL_GPL(s3c_hsotg_resume);

View File

@ -357,12 +357,12 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)
writel(0, hsotg->regs + HPRT0);
}
/* Caller must hold driver lock */
static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
struct dwc2_hcd_urb *urb, void **ep_handle,
gfp_t mem_flags)
{
struct dwc2_qtd *qtd;
unsigned long flags;
u32 intr_mask;
int retval;
int dev_speed;
@ -413,11 +413,9 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
*/
return 0;
spin_lock_irqsave(&hsotg->lock, flags);
tr_type = dwc2_hcd_select_transactions(hsotg);
if (tr_type != DWC2_TRANSACTION_NONE)
dwc2_hcd_queue_transactions(hsotg, tr_type);
spin_unlock_irqrestore(&hsotg->lock, flags);
}
return 0;
@ -721,9 +719,7 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
/* 3072 = 3 max-size Isoc packets */
buf_size = 3072;
qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size,
&qh->dw_align_buf_dma,
GFP_ATOMIC);
qh->dw_align_buf = kmalloc(buf_size, GFP_ATOMIC | GFP_DMA);
if (!qh->dw_align_buf)
return -ENOMEM;
qh->dw_align_buf_size = buf_size;
@ -748,6 +744,15 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
}
}
qh->dw_align_buf_dma = dma_map_single(hsotg->dev,
qh->dw_align_buf, qh->dw_align_buf_size,
chan->ep_is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) {
dev_err(hsotg->dev, "can't map align_buf\n");
chan->align_buf = 0;
return -EINVAL;
}
chan->align_buf = qh->dw_align_buf_dma;
return 0;
}
@ -1774,6 +1779,15 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
/* Not supported */
break;
case USB_PORT_FEAT_TEST:
hprt0 = dwc2_read_hprt0(hsotg);
dev_dbg(hsotg->dev,
"SetPortFeature - USB_PORT_FEAT_TEST\n");
hprt0 &= ~HPRT0_TSTCTL_MASK;
hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT;
writel(hprt0, hsotg->regs + HPRT0);
break;
default:
retval = -EINVAL;
dev_err(hsotg->dev,
@ -2313,6 +2327,22 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
usleep_range(1000, 3000);
}
static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
{
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
hsotg->lx_state = DWC2_L2;
return 0;
}
static int _dwc2_hcd_resume(struct usb_hcd *hcd)
{
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
hsotg->lx_state = DWC2_L0;
return 0;
}
/* Returns the current frame number */
static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd)
{
@ -2468,7 +2498,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
"%s: unaligned transfer with no transfer_buffer",
__func__);
retval = -EINVAL;
goto fail1;
goto fail0;
}
}
@ -2496,7 +2526,6 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
spin_lock_irqsave(&hsotg->lock, flags);
retval = usb_hcd_link_urb_to_ep(hcd, urb);
spin_unlock_irqrestore(&hsotg->lock, flags);
if (retval)
goto fail1;
@ -2505,22 +2534,22 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
goto fail2;
if (alloc_bandwidth) {
spin_lock_irqsave(&hsotg->lock, flags);
dwc2_allocate_bus_bandwidth(hcd,
dwc2_hcd_get_ep_bandwidth(hsotg, ep),
urb);
spin_unlock_irqrestore(&hsotg->lock, flags);
}
spin_unlock_irqrestore(&hsotg->lock, flags);
return 0;
fail2:
spin_lock_irqsave(&hsotg->lock, flags);
dwc2_urb->priv = NULL;
usb_hcd_unlink_urb_from_ep(hcd, urb);
spin_unlock_irqrestore(&hsotg->lock, flags);
fail1:
spin_unlock_irqrestore(&hsotg->lock, flags);
urb->hcpriv = NULL;
fail0:
kfree(dwc2_urb);
return retval;
@ -2683,6 +2712,9 @@ static struct hc_driver dwc2_hc_driver = {
.hub_status_data = _dwc2_hcd_hub_status_data,
.hub_control = _dwc2_hcd_hub_control,
.clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete,
.bus_suspend = _dwc2_hcd_suspend,
.bus_resume = _dwc2_hcd_resume,
};
/*
@ -2748,8 +2780,6 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
destroy_workqueue(hsotg->wq_otg);
}
kfree(hsotg->core_params);
hsotg->core_params = NULL;
del_timer(&hsotg->wkp_timer);
}
@ -2761,30 +2791,13 @@ static void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
dwc2_hcd_free(hsotg);
}
/*
* Sets all parameters to the given value.
*
* Assumes that the dwc2_core_params struct contains only integers.
*/
void dwc2_set_all_params(struct dwc2_core_params *params, int value)
{
int *p = (int *)params;
size_t size = sizeof(*params) / sizeof(*p);
int i;
for (i = 0; i < size; i++)
p[i] = value;
}
EXPORT_SYMBOL_GPL(dwc2_set_all_params);
/*
* Initializes the HCD. This function allocates memory for and initializes the
* static parts of the usb_hcd and dwc2_hsotg structures. It also registers the
* USB bus with the core and calls the hc_driver->start() function. It returns
* a negative error on failure.
*/
int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
const struct dwc2_core_params *params)
int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
{
struct usb_hcd *hcd;
struct dwc2_host_chan *channel;
@ -2797,12 +2810,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n");
/* Detect config values from hardware */
retval = dwc2_get_hwparams(hsotg);
if (retval)
return retval;
retval = -ENOMEM;
hcfg = readl(hsotg->regs + HCFG);
@ -2821,15 +2828,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
hsotg->last_frame_num = HFNUM_MAX_FRNUM;
#endif
hsotg->core_params = kzalloc(sizeof(*hsotg->core_params), GFP_KERNEL);
if (!hsotg->core_params)
goto error1;
dwc2_set_all_params(hsotg->core_params, -1);
/* Validate parameter values */
dwc2_set_parameters(hsotg, params);
/* Check if the bus driver or platform code has setup a dma_mask */
if (hsotg->core_params->dma_enable > 0 &&
hsotg->dev->dma_mask == NULL) {
@ -2947,6 +2945,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
/* Don't support SG list at this point */
hcd->self.sg_tablesize = 0;
if (!IS_ERR_OR_NULL(hsotg->uphy))
otg_set_host(hsotg->uphy->otg, &hcd->self);
/*
* Finish generic HCD initialization and start the HCD. This function
* allocates the DMA buffer pool, registers the USB bus, requests the
@ -2979,7 +2980,6 @@ error1:
dev_err(hsotg->dev, "%s() FAILED, returning %d\n", __func__, retval);
return retval;
}
EXPORT_SYMBOL_GPL(dwc2_hcd_init);
/*
* Removes the HCD.
@ -3000,6 +3000,9 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg)
return;
}
if (!IS_ERR_OR_NULL(hsotg->uphy))
otg_set_host(hsotg->uphy->otg, NULL);
usb_remove_hcd(hcd);
hsotg->priv = NULL;
dwc2_hcd_release(hsotg);
@ -3010,4 +3013,3 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg)
kfree(hsotg->frame_num_array);
#endif
}
EXPORT_SYMBOL_GPL(dwc2_hcd_remove);

View File

@ -451,13 +451,8 @@ static inline u8 dwc2_hcd_is_pipe_out(struct dwc2_hcd_pipe_info *pipe)
return !dwc2_hcd_is_pipe_in(pipe);
}
extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
const struct dwc2_core_params *params);
extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq);
extern void dwc2_hcd_remove(struct dwc2_hsotg *hsotg);
extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
const struct dwc2_core_params *params);
extern void dwc2_set_all_params(struct dwc2_core_params *params, int value);
extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
/* Transaction Execution Functions */
extern enum dwc2_transaction_type dwc2_hcd_select_transactions(

View File

@ -350,6 +350,9 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
dev_vdbg(hsotg->dev,
"--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n",
hprt0);
if (hsotg->lx_state != DWC2_L0)
usb_hcd_resume_root_hub(hsotg->priv);
hsotg->flags.b.port_connect_status_change = 1;
hsotg->flags.b.port_connect_status = 1;
hprt0_modify |= HPRT0_CONNDET;
@ -463,10 +466,15 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
}
/* Non DWORD-aligned buffer case handling */
if (chan->align_buf && xfer_length && chan->ep_is_in) {
if (chan->align_buf && xfer_length) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf,
xfer_length);
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
chan->qh->dw_align_buf_size,
chan->ep_is_in ?
DMA_FROM_DEVICE : DMA_TO_DEVICE);
if (chan->ep_is_in)
memcpy(urb->buf + urb->actual_length,
chan->qh->dw_align_buf, xfer_length);
}
dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n",
@ -552,13 +560,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
chan, chnum, qtd, halt_status, NULL);
/* Non DWORD-aligned buffer case handling */
if (chan->align_buf && frame_desc->actual_length &&
chan->ep_is_in) {
if (chan->align_buf && frame_desc->actual_length) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
__func__);
memcpy(urb->buf + frame_desc->offset +
qtd->isoc_split_offset, chan->qh->dw_align_buf,
frame_desc->actual_length);
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
chan->qh->dw_align_buf_size,
chan->ep_is_in ?
DMA_FROM_DEVICE : DMA_TO_DEVICE);
if (chan->ep_is_in)
memcpy(urb->buf + frame_desc->offset +
qtd->isoc_split_offset,
chan->qh->dw_align_buf,
frame_desc->actual_length);
}
break;
case DWC2_HC_XFER_FRAME_OVERRUN:
@ -581,13 +594,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
chan, chnum, qtd, halt_status, NULL);
/* Non DWORD-aligned buffer case handling */
if (chan->align_buf && frame_desc->actual_length &&
chan->ep_is_in) {
if (chan->align_buf && frame_desc->actual_length) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
__func__);
memcpy(urb->buf + frame_desc->offset +
qtd->isoc_split_offset, chan->qh->dw_align_buf,
frame_desc->actual_length);
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
chan->qh->dw_align_buf_size,
chan->ep_is_in ?
DMA_FROM_DEVICE : DMA_TO_DEVICE);
if (chan->ep_is_in)
memcpy(urb->buf + frame_desc->offset +
qtd->isoc_split_offset,
chan->qh->dw_align_buf,
frame_desc->actual_length);
}
/* Skip whole frame */
@ -923,6 +941,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
if (chan->align_buf) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
chan->qh->dw_align_buf_size, DMA_FROM_DEVICE);
memcpy(qtd->urb->buf + frame_desc->offset +
qtd->isoc_split_offset, chan->qh->dw_align_buf, len);
}
@ -1152,8 +1172,14 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
/* Non DWORD-aligned buffer case handling */
if (chan->align_buf && xfer_length && chan->ep_is_in) {
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf,
xfer_length);
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
chan->qh->dw_align_buf_size,
chan->ep_is_in ?
DMA_FROM_DEVICE : DMA_TO_DEVICE);
if (chan->ep_is_in)
memcpy(urb->buf + urb->actual_length,
chan->qh->dw_align_buf,
xfer_length);
}
urb->actual_length += xfer_length;
@ -1182,6 +1208,16 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
struct dwc2_host_chan *chan, int chnum,
struct dwc2_qtd *qtd)
{
if (!qtd) {
dev_dbg(hsotg->dev, "%s: qtd is NULL\n", __func__);
return;
}
if (!qtd->urb) {
dev_dbg(hsotg->dev, "%s: qtd->urb is NULL\n", __func__);
return;
}
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: NAK Received--\n",
chnum);

View File

@ -229,11 +229,13 @@ static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
*/
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
if (hsotg->core_params->dma_desc_enable > 0)
if (hsotg->core_params->dma_desc_enable > 0) {
dwc2_hcd_qh_free_ddma(hsotg, qh);
else if (qh->dw_align_buf)
dma_free_coherent(hsotg->dev, qh->dw_align_buf_size,
qh->dw_align_buf, qh->dw_align_buf_dma);
} else {
/* kfree(NULL) is safe */
kfree(qh->dw_align_buf);
qh->dw_align_buf_dma = (dma_addr_t)0;
}
kfree(qh);
}
@ -761,6 +763,7 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
/**
* dwc2_hcd_qtd_add() - Adds a QTD to the QTD-list of a QH
* Caller must hold driver lock.
*
* @hsotg: The DWC HCD structure
* @qtd: The QTD to add
@ -777,7 +780,6 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
struct dwc2_qh **qh, gfp_t mem_flags)
{
struct dwc2_hcd_urb *urb = qtd->urb;
unsigned long flags;
int allocated = 0;
int retval;
@ -792,15 +794,12 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
allocated = 1;
}
spin_lock_irqsave(&hsotg->lock, flags);
retval = dwc2_hcd_qh_add(hsotg, *qh);
if (retval)
goto fail;
qtd->qh = *qh;
list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list);
spin_unlock_irqrestore(&hsotg->lock, flags);
return 0;
@ -817,10 +816,7 @@ fail:
qtd_list_entry)
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp);
spin_unlock_irqrestore(&hsotg->lock, flags);
dwc2_hcd_qh_free(hsotg, qh_tmp);
} else {
spin_unlock_irqrestore(&hsotg->lock, flags);
}
return retval;

View File

@ -47,6 +47,7 @@
#include "core.h"
#include "hcd.h"
#include "debug.h"
static const char dwc2_driver_name[] = "dwc2";
@ -76,6 +77,8 @@ static const struct dwc2_core_params params_bcm2835 = {
.reload_ctl = 0,
.ahbcfg = 0x10,
.uframe_sched = 0,
.external_id_pin_ctl = -1,
.hibernation = -1,
};
static const struct dwc2_core_params params_rk3066 = {
@ -104,6 +107,8 @@ static const struct dwc2_core_params params_rk3066 = {
.reload_ctl = -1,
.ahbcfg = 0x7, /* INCR16 */
.uframe_sched = -1,
.external_id_pin_ctl = -1,
.hibernation = -1,
};
/**
@ -121,6 +126,7 @@ static int dwc2_driver_remove(struct platform_device *dev)
{
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
dwc2_debugfs_exit(hsotg);
if (hsotg->hcd_enabled)
dwc2_hcd_remove(hsotg);
if (hsotg->gadget_enabled)
@ -237,6 +243,21 @@ static int dwc2_driver_probe(struct platform_device *dev)
spin_lock_init(&hsotg->lock);
mutex_init(&hsotg->init_mutex);
/* Detect config values from hardware */
retval = dwc2_get_hwparams(hsotg);
if (retval)
return retval;
hsotg->core_params = devm_kzalloc(&dev->dev,
sizeof(*hsotg->core_params), GFP_KERNEL);
if (!hsotg->core_params)
return -ENOMEM;
dwc2_set_all_params(hsotg->core_params, -1);
/* Validate parameter values */
dwc2_set_parameters(hsotg, params);
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
retval = dwc2_gadget_init(hsotg, irq);
if (retval)
@ -245,7 +266,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
}
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
retval = dwc2_hcd_init(hsotg, irq, params);
retval = dwc2_hcd_init(hsotg, irq);
if (retval) {
if (hsotg->gadget_enabled)
s3c_hsotg_remove(hsotg);
@ -256,6 +277,8 @@ static int dwc2_driver_probe(struct platform_device *dev)
platform_set_drvdata(dev, hsotg);
dwc2_debugfs_init(hsotg);
return retval;
}

View File

@ -11,6 +11,13 @@ config USB_DWC3
if USB_DWC3
config USB_DWC3_ULPI
bool "Register ULPI PHY Interface"
depends on USB_ULPI_BUS=y || USB_ULPI_BUS=USB_DWC3
help
Select this if you have ULPI type PHY attached to your DWC3
controller.
choice
bool "DWC3 Mode Selection"
default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET)

View File

@ -15,6 +15,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
dwc3-y += gadget.o ep0.o
endif
ifneq ($(CONFIG_USB_DWC3_ULPI),)
dwc3-y += ulpi.o
endif
ifneq ($(CONFIG_DEBUG_FS),)
dwc3-y += debugfs.o
endif

View File

@ -116,6 +116,33 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
return 0;
}
/**
* dwc3_soft_reset - Issue soft reset
* @dwc: Pointer to our controller context structure
*/
static int dwc3_soft_reset(struct dwc3 *dwc)
{
unsigned long timeout;
u32 reg;
timeout = jiffies + msecs_to_jiffies(500);
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
do {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST))
break;
if (time_after(jiffies, timeout)) {
dev_err(dwc->dev, "Reset Timed Out\n");
return -ETIMEDOUT;
}
cpu_relax();
} while (true);
return 0;
}
/**
* dwc3_free_one_event_buffer - Frees one event buffer
* @dwc: Pointer to our controller context structure
@ -367,10 +394,15 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
/**
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
* @dwc: Pointer to our controller context structure
*
* Returns 0 on success. The USB PHY interfaces are configured but not
* initialized. The PHY interfaces and the PHYs get initialized together with
* the core in dwc3_core_init.
*/
static void dwc3_phy_setup(struct dwc3 *dwc)
static int dwc3_phy_setup(struct dwc3 *dwc)
{
u32 reg;
int ret;
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
@ -409,10 +441,41 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
mdelay(100);
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
/* Select the HS PHY interface */
switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) {
case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI:
if (!strncmp(dwc->hsphy_interface, "utmi", 4)) {
reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI;
break;
} else if (!strncmp(dwc->hsphy_interface, "ulpi", 4)) {
reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
} else {
dev_warn(dwc->dev, "HSPHY Interface not defined\n");
/* Relying on default value. */
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
break;
}
/* FALLTHROUGH */
case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI:
/* Making sure the interface and PHY are operational */
ret = dwc3_soft_reset(dwc);
if (ret)
return ret;
udelay(1);
ret = dwc3_ulpi_init(dwc);
if (ret)
return ret;
/* FALLTHROUGH */
default:
break;
}
/*
* Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to
* '0' during coreConsultant configuration. So default value will
@ -427,7 +490,7 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
mdelay(100);
return 0;
}
/**
@ -438,7 +501,6 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
*/
static int dwc3_core_init(struct dwc3 *dwc)
{
unsigned long timeout;
u32 hwparams4 = dwc->hwparams.hwparams4;
u32 reg;
int ret;
@ -466,21 +528,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
}
/* issue device SoftReset too */
timeout = jiffies + msecs_to_jiffies(500);
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
do {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST))
break;
if (time_after(jiffies, timeout)) {
dev_err(dwc->dev, "Reset Timed Out\n");
ret = -ETIMEDOUT;
goto err0;
}
cpu_relax();
} while (true);
ret = dwc3_soft_reset(dwc);
if (ret)
goto err0;
ret = dwc3_core_soft_reset(dwc);
if (ret)
@ -555,8 +605,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
dwc3_phy_setup(dwc);
ret = dwc3_alloc_scratch_buffers(dwc);
if (ret)
goto err1;
@ -836,6 +884,8 @@ static int dwc3_probe(struct platform_device *pdev)
"snps,tx_de_emphasis_quirk");
of_property_read_u8(node, "snps,tx_de_emphasis",
&tx_de_emphasis);
of_property_read_string(node, "snps,hsphy_interface",
&dwc->hsphy_interface);
} else if (pdata) {
dwc->maximum_speed = pdata->maximum_speed;
dwc->has_lpm_erratum = pdata->has_lpm_erratum;
@ -863,6 +913,8 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk;
if (pdata->tx_de_emphasis)
tx_de_emphasis = pdata->tx_de_emphasis;
dwc->hsphy_interface = pdata->hsphy_interface;
}
/* default to superspeed if no maximum_speed passed */
@ -875,12 +927,18 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->hird_threshold = hird_threshold
| (dwc->is_utmi_l1_suspend << 4);
platform_set_drvdata(pdev, dwc);
dwc3_cache_hwparams(dwc);
ret = dwc3_phy_setup(dwc);
if (ret)
goto err0;
ret = dwc3_core_get_phy(dwc);
if (ret)
goto err0;
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
if (!dev->dma_mask) {
dev->dma_mask = dev->parent->dma_mask;
@ -892,8 +950,6 @@ static int dwc3_probe(struct platform_device *pdev)
pm_runtime_get_sync(dev);
pm_runtime_forbid(dev);
dwc3_cache_hwparams(dwc);
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n");
@ -964,6 +1020,7 @@ err2:
err1:
dwc3_free_event_buffers(dwc);
dwc3_ulpi_exit(dwc);
err0:
/*
@ -999,6 +1056,7 @@ static int dwc3_remove(struct platform_device *pdev)
phy_power_off(dwc->usb3_generic_phy);
dwc3_core_exit(dwc);
dwc3_ulpi_exit(dwc);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);

View File

@ -30,6 +30,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/ulpi/interface.h>
#include <linux/phy/phy.h>
@ -173,6 +174,15 @@
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4)
/* Global USB2 PHY Vendor Control Register */
#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25)
#define DWC3_GUSB2PHYACC_BUSY (1 << 23)
#define DWC3_GUSB2PHYACC_WRITE (1 << 22)
#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16)
#define DWC3_GUSB2PHYACC_EXTEND_ADDR(n) (n << 8)
#define DWC3_GUSB2PHYACC_DATA(n) (n & 0xff)
/* Global USB3 PIPE Control Register */
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
@ -652,6 +662,7 @@ struct dwc3_scratchpad_array {
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY
* @usb3_generic_phy: pointer to USB3 PHY
* @ulpi: pointer to ulpi interface
* @dcfg: saved contents of DCFG register
* @gctl: saved contents of GCTL register
* @isoch_delay: wValue from Set Isochronous Delay request;
@ -673,6 +684,7 @@ struct dwc3_scratchpad_array {
* @test_mode_nr: test feature selector
* @lpm_nyet_threshold: LPM NYET response threshold
* @hird_threshold: HIRD threshold
* @hsphy_interface: "utmi" or "ulpi"
* @delayed_status: true when gadget driver asks for delayed status
* @ep0_bounced: true when we used bounce buffer
* @ep0_expect_in: true when we expect a DATA IN transfer
@ -739,6 +751,8 @@ struct dwc3 {
struct phy *usb2_generic_phy;
struct phy *usb3_generic_phy;
struct ulpi *ulpi;
void __iomem *regs;
size_t regs_size;
@ -800,6 +814,8 @@ struct dwc3 {
u8 lpm_nyet_threshold;
u8 hird_threshold;
const char *hsphy_interface;
unsigned delayed_status:1;
unsigned ep0_bounced:1;
unsigned ep0_expect_in:1;
@ -1035,4 +1051,14 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc)
}
#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */
#if IS_ENABLED(CONFIG_USB_DWC3_ULPI)
int dwc3_ulpi_init(struct dwc3 *dwc);
void dwc3_ulpi_exit(struct dwc3 *dwc);
#else
static inline int dwc3_ulpi_init(struct dwc3 *dwc)
{ return 0; }
static inline void dwc3_ulpi_exit(struct dwc3 *dwc)
{ }
#endif
#endif /* __DRIVERS_USB_DWC3_CORE_H */

View File

@ -21,6 +21,8 @@
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/acpi.h>
#include "platform_data.h"
@ -31,6 +33,15 @@
#define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30
#define PCI_DEVICE_ID_INTEL_SPTH 0xa130
static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
static const struct acpi_gpio_params cs_gpios = { 1, 0, false };
static const struct acpi_gpio_mapping acpi_dwc3_byt_gpios[] = {
{ "reset-gpios", &reset_gpios, 1 },
{ "cs-gpios", &cs_gpios, 1 },
{ },
};
static int dwc3_pci_quirks(struct pci_dev *pdev)
{
if (pdev->vendor == PCI_VENDOR_ID_AMD &&
@ -65,6 +76,30 @@ static int dwc3_pci_quirks(struct pci_dev *pdev)
sizeof(pdata));
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
pdev->device == PCI_DEVICE_ID_INTEL_BYT) {
struct gpio_desc *gpio;
acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
acpi_dwc3_byt_gpios);
/* These GPIOs will turn on the USB2 PHY */
gpio = gpiod_get(&pdev->dev, "cs");
if (!IS_ERR(gpio)) {
gpiod_direction_output(gpio, 0);
gpiod_set_value_cansleep(gpio, 1);
gpiod_put(gpio);
}
gpio = gpiod_get(&pdev->dev, "reset");
if (!IS_ERR(gpio)) {
gpiod_direction_output(gpio, 0);
gpiod_set_value_cansleep(gpio, 1);
gpiod_put(gpio);
usleep_range(10000, 11000);
}
}
return 0;
}
@ -128,6 +163,7 @@ err:
static void dwc3_pci_remove(struct pci_dev *pci)
{
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pci->dev));
platform_device_unregister(pci_get_drvdata(pci));
}

View File

@ -291,6 +291,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
dwc3_trace(trace_dwc3_gadget,
"Command Complete --> %d",
DWC3_DGCMD_STATUS(reg));
if (DWC3_DGCMD_STATUS(reg))
return -EINVAL;
return 0;
}
@ -328,6 +330,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
dwc3_trace(trace_dwc3_gadget,
"Command Complete --> %d",
DWC3_DEPCMD_STATUS(reg));
if (DWC3_DEPCMD_STATUS(reg))
return -EINVAL;
return 0;
}
@ -1902,12 +1906,16 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
{
unsigned status = 0;
int clean_busy;
u32 is_xfer_complete;
is_xfer_complete = (event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE);
if (event->status & DEPEVT_STATUS_BUSERR)
status = -ECONNRESET;
clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
if (clean_busy)
if (clean_busy && (is_xfer_complete ||
usb_endpoint_xfer_isoc(dep->endpoint.desc)))
dep->flags &= ~DWC3_EP_BUSY;
/*

View File

@ -45,4 +45,6 @@ struct dwc3_platform_data {
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
const char *hsphy_interface;
};

View File

@ -0,0 +1,91 @@
/**
* ulpi.c - DesignWare USB3 Controller's ULPI PHY interface
*
* Copyright (C) 2015 Intel Corporation
*
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
*
* 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/ulpi/regs.h>
#include "core.h"
#include "io.h"
#define DWC3_ULPI_ADDR(a) \
((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \
DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \
DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a))
static int dwc3_ulpi_busyloop(struct dwc3 *dwc)
{
unsigned count = 1000;
u32 reg;
while (count--) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
if (!(reg & DWC3_GUSB2PHYACC_BUSY))
return 0;
cpu_relax();
}
return -ETIMEDOUT;
}
static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr)
{
struct dwc3 *dwc = dev_get_drvdata(ops->dev);
u32 reg;
int ret;
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
ret = dwc3_ulpi_busyloop(dwc);
if (ret)
return ret;
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
return DWC3_GUSB2PHYACC_DATA(reg);
}
static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val)
{
struct dwc3 *dwc = dev_get_drvdata(ops->dev);
u32 reg;
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
reg |= DWC3_GUSB2PHYACC_WRITE | val;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
return dwc3_ulpi_busyloop(dwc);
}
static struct ulpi_ops dwc3_ulpi_ops = {
.read = dwc3_ulpi_read,
.write = dwc3_ulpi_write,
};
int dwc3_ulpi_init(struct dwc3 *dwc)
{
/* Register the interface */
dwc->ulpi = ulpi_register_interface(dwc->dev, &dwc3_ulpi_ops);
if (IS_ERR(dwc->ulpi)) {
dev_err(dwc->dev, "failed to register ULPI interface");
return PTR_ERR(dwc->ulpi);
}
return 0;
}
void dwc3_ulpi_exit(struct dwc3 *dwc)
{
if (dwc->ulpi) {
ulpi_unregister_interface(dwc->ulpi);
dwc->ulpi = NULL;
}
}

View File

@ -258,15 +258,25 @@ struct usb_ep *usb_ep_autoconfig_ss(
/* First, apply chip-specific "best usage" knowledge.
* This might make a good usb_gadget_ops hook ...
*/
if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
/* ep-e, ep-f are PIO with only 64 byte fifos */
ep = find_ep (gadget, "ep-e");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
ep = find_ep (gadget, "ep-f");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
if (gadget_is_net2280(gadget)) {
char name[8];
if (type == USB_ENDPOINT_XFER_INT) {
/* ep-e, ep-f are PIO with only 64 byte fifos */
ep = find_ep(gadget, "ep-e");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
ep = find_ep(gadget, "ep-f");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
}
/* USB3380: use same address for usb and hardware endpoints */
snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc),
usb_endpoint_dir_in(desc) ? "in" : "out");
ep = find_ep(gadget, name);
if (ep && ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
} else if (gadget_is_goku (gadget)) {
if (USB_ENDPOINT_XFER_INT == type) {
/* single buffering is enough */

View File

@ -3433,6 +3433,7 @@ done:
static void ffs_closed(struct ffs_data *ffs)
{
struct ffs_dev *ffs_obj;
struct f_fs_opts *opts;
ENTER();
ffs_dev_lock();
@ -3446,8 +3447,13 @@ static void ffs_closed(struct ffs_data *ffs)
if (ffs_obj->ffs_closed_callback)
ffs_obj->ffs_closed_callback(ffs);
if (!ffs_obj->opts || ffs_obj->opts->no_configfs
|| !ffs_obj->opts->func_inst.group.cg_item.ci_parent)
if (ffs_obj->opts)
opts = ffs_obj->opts;
else
goto done;
if (opts->no_configfs || !opts->func_inst.group.cg_item.ci_parent
|| !atomic_read(&opts->func_inst.group.cg_item.ci_kref.refcount))
goto done;
unregister_gadget_item(ffs_obj->opts->

View File

@ -76,7 +76,7 @@ struct f_rndis {
u8 ethaddr[ETH_ALEN];
u32 vendorID;
const char *manufacturer;
int config;
struct rndis_params *params;
struct usb_ep *notify;
struct usb_request *notify_req;
@ -453,7 +453,7 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
// spin_lock(&dev->lock);
status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
if (status < 0)
pr_err("RNDIS command error %d, %d/%d\n",
status, req->actual, req->length);
@ -499,12 +499,12 @@ rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
u32 n;
/* return the result */
buf = rndis_get_next_response(rndis->config, &n);
buf = rndis_get_next_response(rndis->params, &n);
if (buf) {
memcpy(req->buf, buf, n);
req->complete = rndis_response_complete;
req->context = rndis;
rndis_free_response(rndis->config, buf);
rndis_free_response(rndis->params, buf);
value = n;
}
/* else stalls ... spec says to avoid that */
@ -597,7 +597,7 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (IS_ERR(net))
return PTR_ERR(net);
rndis_set_param_dev(rndis->config, net,
rndis_set_param_dev(rndis->params, net,
&rndis->port.cdc_filter);
} else
goto fail;
@ -617,7 +617,7 @@ static void rndis_disable(struct usb_function *f)
DBG(cdev, "rndis deactivated\n");
rndis_uninit(rndis->config);
rndis_uninit(rndis->params);
gether_disconnect(&rndis->port);
usb_ep_disable(rndis->notify);
@ -640,9 +640,9 @@ static void rndis_open(struct gether *geth)
DBG(cdev, "%s\n", __func__);
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3,
rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3,
bitrate(cdev->gadget) / 100);
rndis_signal_connect(rndis->config);
rndis_signal_connect(rndis->params);
}
static void rndis_close(struct gether *geth)
@ -651,8 +651,8 @@ static void rndis_close(struct gether *geth)
DBG(geth->func.config->cdev, "%s\n", __func__);
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
rndis_signal_disconnect(rndis->config);
rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0);
rndis_signal_disconnect(rndis->params);
}
/*-------------------------------------------------------------------------*/
@ -796,11 +796,11 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis->port.open = rndis_open;
rndis->port.close = rndis_close;
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
rndis_set_host_mac(rndis->config, rndis->ethaddr);
rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0);
rndis_set_host_mac(rndis->params, rndis->ethaddr);
if (rndis->manufacturer && rndis->vendorID &&
rndis_set_param_vendor(rndis->config, rndis->vendorID,
rndis_set_param_vendor(rndis->params, rndis->vendorID,
rndis->manufacturer)) {
status = -EINVAL;
goto fail_free_descs;
@ -944,7 +944,7 @@ static void rndis_free(struct usb_function *f)
struct f_rndis_opts *opts;
rndis = func_to_rndis(f);
rndis_deregister(rndis->config);
rndis_deregister(rndis->params);
opts = container_of(f->fi, struct f_rndis_opts, func_inst);
kfree(rndis);
mutex_lock(&opts->lock);
@ -968,7 +968,7 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
{
struct f_rndis *rndis;
struct f_rndis_opts *opts;
int status;
struct rndis_params *params;
/* allocate and initialize one new instance */
rndis = kzalloc(sizeof(*rndis), GFP_KERNEL);
@ -1002,36 +1002,16 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
rndis->port.func.disable = rndis_disable;
rndis->port.func.free_func = rndis_free;
status = rndis_register(rndis_response_available, rndis);
if (status < 0) {
params = rndis_register(rndis_response_available, rndis);
if (IS_ERR(params)) {
kfree(rndis);
return ERR_PTR(status);
return ERR_CAST(params);
}
rndis->config = status;
rndis->params = params;
return &rndis->port.func;
}
DECLARE_USB_FUNCTION(rndis, rndis_alloc_inst, rndis_alloc);
static int __init rndis_mod_init(void)
{
int ret;
ret = rndis_init();
if (ret)
return ret;
return usb_function_register(&rndisusb_func);
}
module_init(rndis_mod_init);
static void __exit rndis_mod_exit(void)
{
usb_function_unregister(&rndisusb_func);
rndis_exit();
}
module_exit(rndis_mod_exit);
DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");

View File

@ -25,6 +25,7 @@
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/idr.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
@ -57,17 +58,26 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging");
#define rndis_debug 0
#endif
#define RNDIS_MAX_CONFIGS 1
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
#define NAME_TEMPLATE "driver/rndis-%03d"
static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS];
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
static DEFINE_IDA(rndis_ida);
/* Driver Version */
static const __le32 rndis_driver_version = cpu_to_le32(1);
/* Function Prototypes */
static rndis_resp_t *rndis_add_response(int configNr, u32 length);
static rndis_resp_t *rndis_add_response(struct rndis_params *params,
u32 length);
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static const struct file_operations rndis_proc_fops;
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/* supported OIDs */
static const u32 oid_supported_list[] =
@ -160,7 +170,7 @@ static const u32 oid_supported_list[] =
/* NDIS Functions */
static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf,
unsigned buf_len, rndis_resp_t *r)
{
int retval = -ENOTSUPP;
@ -192,7 +202,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
outbuf = (__le32 *)&resp[1];
resp->InformationBufferOffset = cpu_to_le32(16);
net = rndis_per_dev_params[configNr].dev;
net = params->dev;
stats = dev_get_stats(net, &temp);
switch (OID) {
@ -225,7 +235,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
/* mandatory */
case RNDIS_OID_GEN_MEDIA_SUPPORTED:
pr_debug("%s: RNDIS_OID_GEN_MEDIA_SUPPORTED\n", __func__);
*outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium);
*outbuf = cpu_to_le32(params->medium);
retval = 0;
break;
@ -233,16 +243,15 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
case RNDIS_OID_GEN_MEDIA_IN_USE:
pr_debug("%s: RNDIS_OID_GEN_MEDIA_IN_USE\n", __func__);
/* one medium, one transport... (maybe you do it better) */
*outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium);
*outbuf = cpu_to_le32(params->medium);
retval = 0;
break;
/* mandatory */
case RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE:
pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__);
if (rndis_per_dev_params[configNr].dev) {
*outbuf = cpu_to_le32(
rndis_per_dev_params[configNr].dev->mtu);
if (params->dev) {
*outbuf = cpu_to_le32(params->dev->mtu);
retval = 0;
}
break;
@ -251,21 +260,18 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
case RNDIS_OID_GEN_LINK_SPEED:
if (rndis_debug > 1)
pr_debug("%s: RNDIS_OID_GEN_LINK_SPEED\n", __func__);
if (rndis_per_dev_params[configNr].media_state
== RNDIS_MEDIA_STATE_DISCONNECTED)
if (params->media_state == RNDIS_MEDIA_STATE_DISCONNECTED)
*outbuf = cpu_to_le32(0);
else
*outbuf = cpu_to_le32(
rndis_per_dev_params[configNr].speed);
*outbuf = cpu_to_le32(params->speed);
retval = 0;
break;
/* mandatory */
case RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE:
pr_debug("%s: RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__);
if (rndis_per_dev_params[configNr].dev) {
*outbuf = cpu_to_le32(
rndis_per_dev_params[configNr].dev->mtu);
if (params->dev) {
*outbuf = cpu_to_le32(params->dev->mtu);
retval = 0;
}
break;
@ -273,9 +279,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
/* mandatory */
case RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE:
pr_debug("%s: RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__);
if (rndis_per_dev_params[configNr].dev) {
*outbuf = cpu_to_le32(
rndis_per_dev_params[configNr].dev->mtu);
if (params->dev) {
*outbuf = cpu_to_le32(params->dev->mtu);
retval = 0;
}
break;
@ -283,20 +288,16 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
/* mandatory */
case RNDIS_OID_GEN_VENDOR_ID:
pr_debug("%s: RNDIS_OID_GEN_VENDOR_ID\n", __func__);
*outbuf = cpu_to_le32(
rndis_per_dev_params[configNr].vendorID);
*outbuf = cpu_to_le32(params->vendorID);
retval = 0;
break;
/* mandatory */
case RNDIS_OID_GEN_VENDOR_DESCRIPTION:
pr_debug("%s: RNDIS_OID_GEN_VENDOR_DESCRIPTION\n", __func__);
if (rndis_per_dev_params[configNr].vendorDescr) {
length = strlen(rndis_per_dev_params[configNr].
vendorDescr);
memcpy(outbuf,
rndis_per_dev_params[configNr].vendorDescr,
length);
if (params->vendorDescr) {
length = strlen(params->vendorDescr);
memcpy(outbuf, params->vendorDescr, length);
} else {
outbuf[0] = 0;
}
@ -313,7 +314,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
/* mandatory */
case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER\n", __func__);
*outbuf = cpu_to_le32(*rndis_per_dev_params[configNr].filter);
*outbuf = cpu_to_le32(*params->filter);
retval = 0;
break;
@ -328,8 +329,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS:
if (rndis_debug > 1)
pr_debug("%s: RNDIS_OID_GEN_MEDIA_CONNECT_STATUS\n", __func__);
*outbuf = cpu_to_le32(rndis_per_dev_params[configNr]
.media_state);
*outbuf = cpu_to_le32(params->media_state);
retval = 0;
break;
@ -409,11 +409,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
/* mandatory */
case RNDIS_OID_802_3_PERMANENT_ADDRESS:
pr_debug("%s: RNDIS_OID_802_3_PERMANENT_ADDRESS\n", __func__);
if (rndis_per_dev_params[configNr].dev) {
if (params->dev) {
length = ETH_ALEN;
memcpy(outbuf,
rndis_per_dev_params[configNr].host_mac,
length);
memcpy(outbuf, params->host_mac, length);
retval = 0;
}
break;
@ -421,11 +419,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
/* mandatory */
case RNDIS_OID_802_3_CURRENT_ADDRESS:
pr_debug("%s: RNDIS_OID_802_3_CURRENT_ADDRESS\n", __func__);
if (rndis_per_dev_params[configNr].dev) {
if (params->dev) {
length = ETH_ALEN;
memcpy(outbuf,
rndis_per_dev_params [configNr].host_mac,
length);
memcpy(outbuf, params->host_mac, length);
retval = 0;
}
break;
@ -490,12 +486,11 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
return retval;
}
static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len,
rndis_resp_t *r)
static int gen_ndis_set_resp(struct rndis_params *params, u32 OID,
u8 *buf, u32 buf_len, rndis_resp_t *r)
{
rndis_set_cmplt_type *resp;
int i, retval = -ENOTSUPP;
struct rndis_params *params;
if (!r)
return -ENOMEM;
@ -514,7 +509,6 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len,
}
}
params = &rndis_per_dev_params[configNr];
switch (OID) {
case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
@ -563,16 +557,16 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len,
* Response Functions
*/
static int rndis_init_response(int configNr, rndis_init_msg_type *buf)
static int rndis_init_response(struct rndis_params *params,
rndis_init_msg_type *buf)
{
rndis_init_cmplt_type *resp;
rndis_resp_t *r;
struct rndis_params *params = rndis_per_dev_params + configNr;
if (!params->dev)
return -ENOTSUPP;
r = rndis_add_response(configNr, sizeof(rndis_init_cmplt_type));
r = rndis_add_response(params, sizeof(rndis_init_cmplt_type));
if (!r)
return -ENOMEM;
resp = (rndis_init_cmplt_type *)r->buf;
@ -599,11 +593,11 @@ static int rndis_init_response(int configNr, rndis_init_msg_type *buf)
return 0;
}
static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
static int rndis_query_response(struct rndis_params *params,
rndis_query_msg_type *buf)
{
rndis_query_cmplt_type *resp;
rndis_resp_t *r;
struct rndis_params *params = rndis_per_dev_params + configNr;
/* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */
if (!params->dev)
@ -615,7 +609,7 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
* rndis_query_cmplt_type followed by data.
* oid_supported_list is the largest data reply
*/
r = rndis_add_response(configNr,
r = rndis_add_response(params,
sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type));
if (!r)
return -ENOMEM;
@ -624,7 +618,7 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C);
resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
if (gen_ndis_query_resp(configNr, le32_to_cpu(buf->OID),
if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID),
le32_to_cpu(buf->InformationBufferOffset)
+ 8 + (u8 *)buf,
le32_to_cpu(buf->InformationBufferLength),
@ -641,14 +635,14 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
return 0;
}
static int rndis_set_response(int configNr, rndis_set_msg_type *buf)
static int rndis_set_response(struct rndis_params *params,
rndis_set_msg_type *buf)
{
u32 BufLength, BufOffset;
rndis_set_cmplt_type *resp;
rndis_resp_t *r;
struct rndis_params *params = rndis_per_dev_params + configNr;
r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type));
r = rndis_add_response(params, sizeof(rndis_set_cmplt_type));
if (!r)
return -ENOMEM;
resp = (rndis_set_cmplt_type *)r->buf;
@ -671,7 +665,7 @@ static int rndis_set_response(int configNr, rndis_set_msg_type *buf)
resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C);
resp->MessageLength = cpu_to_le32(16);
resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
if (gen_ndis_set_resp(configNr, le32_to_cpu(buf->OID),
if (gen_ndis_set_resp(params, le32_to_cpu(buf->OID),
((u8 *)buf) + 8 + BufOffset, BufLength, r))
resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
else
@ -681,13 +675,13 @@ static int rndis_set_response(int configNr, rndis_set_msg_type *buf)
return 0;
}
static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf)
static int rndis_reset_response(struct rndis_params *params,
rndis_reset_msg_type *buf)
{
rndis_reset_cmplt_type *resp;
rndis_resp_t *r;
struct rndis_params *params = rndis_per_dev_params + configNr;
r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type));
r = rndis_add_response(params, sizeof(rndis_reset_cmplt_type));
if (!r)
return -ENOMEM;
resp = (rndis_reset_cmplt_type *)r->buf;
@ -702,16 +696,15 @@ static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf)
return 0;
}
static int rndis_keepalive_response(int configNr,
static int rndis_keepalive_response(struct rndis_params *params,
rndis_keepalive_msg_type *buf)
{
rndis_keepalive_cmplt_type *resp;
rndis_resp_t *r;
struct rndis_params *params = rndis_per_dev_params + configNr;
/* host "should" check only in RNDIS_DATA_INITIALIZED state */
r = rndis_add_response(configNr, sizeof(rndis_keepalive_cmplt_type));
r = rndis_add_response(params, sizeof(rndis_keepalive_cmplt_type));
if (!r)
return -ENOMEM;
resp = (rndis_keepalive_cmplt_type *)r->buf;
@ -729,17 +722,15 @@ static int rndis_keepalive_response(int configNr,
/*
* Device to Host Comunication
*/
static int rndis_indicate_status_msg(int configNr, u32 status)
static int rndis_indicate_status_msg(struct rndis_params *params, u32 status)
{
rndis_indicate_status_msg_type *resp;
rndis_resp_t *r;
struct rndis_params *params = rndis_per_dev_params + configNr;
if (params->state == RNDIS_UNINITIALIZED)
return -ENOTSUPP;
r = rndis_add_response(configNr,
sizeof(rndis_indicate_status_msg_type));
r = rndis_add_response(params, sizeof(rndis_indicate_status_msg_type));
if (!r)
return -ENOMEM;
resp = (rndis_indicate_status_msg_type *)r->buf;
@ -754,53 +745,48 @@ static int rndis_indicate_status_msg(int configNr, u32 status)
return 0;
}
int rndis_signal_connect(int configNr)
int rndis_signal_connect(struct rndis_params *params)
{
rndis_per_dev_params[configNr].media_state
= RNDIS_MEDIA_STATE_CONNECTED;
return rndis_indicate_status_msg(configNr,
RNDIS_STATUS_MEDIA_CONNECT);
params->media_state = RNDIS_MEDIA_STATE_CONNECTED;
return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_CONNECT);
}
EXPORT_SYMBOL_GPL(rndis_signal_connect);
int rndis_signal_disconnect(int configNr)
int rndis_signal_disconnect(struct rndis_params *params)
{
rndis_per_dev_params[configNr].media_state
= RNDIS_MEDIA_STATE_DISCONNECTED;
return rndis_indicate_status_msg(configNr,
RNDIS_STATUS_MEDIA_DISCONNECT);
params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED;
return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_DISCONNECT);
}
EXPORT_SYMBOL_GPL(rndis_signal_disconnect);
void rndis_uninit(int configNr)
void rndis_uninit(struct rndis_params *params)
{
u8 *buf;
u32 length;
if (configNr >= RNDIS_MAX_CONFIGS)
if (!params)
return;
rndis_per_dev_params[configNr].state = RNDIS_UNINITIALIZED;
params->state = RNDIS_UNINITIALIZED;
/* drain the response queue */
while ((buf = rndis_get_next_response(configNr, &length)))
rndis_free_response(configNr, buf);
while ((buf = rndis_get_next_response(params, &length)))
rndis_free_response(params, buf);
}
EXPORT_SYMBOL_GPL(rndis_uninit);
void rndis_set_host_mac(int configNr, const u8 *addr)
void rndis_set_host_mac(struct rndis_params *params, const u8 *addr)
{
rndis_per_dev_params[configNr].host_mac = addr;
params->host_mac = addr;
}
EXPORT_SYMBOL_GPL(rndis_set_host_mac);
/*
* Message Parser
*/
int rndis_msg_parser(u8 configNr, u8 *buf)
int rndis_msg_parser(struct rndis_params *params, u8 *buf)
{
u32 MsgType, MsgLength;
__le32 *tmp;
struct rndis_params *params;
if (!buf)
return -ENOMEM;
@ -809,9 +795,8 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
MsgType = get_unaligned_le32(tmp++);
MsgLength = get_unaligned_le32(tmp++);
if (configNr >= RNDIS_MAX_CONFIGS)
if (!params)
return -ENOTSUPP;
params = &rndis_per_dev_params[configNr];
/* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
* rx/tx statistics and link status, in addition to KEEPALIVE traffic
@ -824,8 +809,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
pr_debug("%s: RNDIS_MSG_INIT\n",
__func__);
params->state = RNDIS_INITIALIZED;
return rndis_init_response(configNr,
(rndis_init_msg_type *)buf);
return rndis_init_response(params, (rndis_init_msg_type *)buf);
case RNDIS_MSG_HALT:
pr_debug("%s: RNDIS_MSG_HALT\n",
@ -838,17 +822,16 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
return 0;
case RNDIS_MSG_QUERY:
return rndis_query_response(configNr,
return rndis_query_response(params,
(rndis_query_msg_type *)buf);
case RNDIS_MSG_SET:
return rndis_set_response(configNr,
(rndis_set_msg_type *)buf);
return rndis_set_response(params, (rndis_set_msg_type *)buf);
case RNDIS_MSG_RESET:
pr_debug("%s: RNDIS_MSG_RESET\n",
__func__);
return rndis_reset_response(configNr,
return rndis_reset_response(params,
(rndis_reset_msg_type *)buf);
case RNDIS_MSG_KEEPALIVE:
@ -856,7 +839,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
if (rndis_debug > 1)
pr_debug("%s: RNDIS_MSG_KEEPALIVE\n",
__func__);
return rndis_keepalive_response(configNr,
return rndis_keepalive_response(params,
(rndis_keepalive_msg_type *)
buf);
@ -876,71 +859,131 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
}
EXPORT_SYMBOL_GPL(rndis_msg_parser);
int rndis_register(void (*resp_avail)(void *v), void *v)
static inline int rndis_get_nr(void)
{
u8 i;
return ida_simple_get(&rndis_ida, 0, 0, GFP_KERNEL);
}
static inline void rndis_put_nr(int nr)
{
ida_simple_remove(&rndis_ida, nr);
}
struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v)
{
struct rndis_params *params;
int i;
if (!resp_avail)
return -EINVAL;
return ERR_PTR(-EINVAL);
for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
if (!rndis_per_dev_params[i].used) {
rndis_per_dev_params[i].used = 1;
rndis_per_dev_params[i].resp_avail = resp_avail;
rndis_per_dev_params[i].v = v;
pr_debug("%s: configNr = %d\n", __func__, i);
return i;
i = rndis_get_nr();
if (i < 0) {
pr_debug("failed\n");
return ERR_PTR(-ENODEV);
}
params = kzalloc(sizeof(*params), GFP_KERNEL);
if (!params) {
rndis_put_nr(i);
return ERR_PTR(-ENOMEM);
}
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
{
struct proc_dir_entry *proc_entry;
char name[20];
sprintf(name, NAME_TEMPLATE, i);
proc_entry = proc_create_data(name, 0660, NULL,
&rndis_proc_fops, params);
if (!proc_entry) {
kfree(params);
rndis_put_nr(i);
return ERR_PTR(-EIO);
}
}
pr_debug("failed\n");
#endif
return -ENODEV;
params->confignr = i;
params->used = 1;
params->state = RNDIS_UNINITIALIZED;
params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED;
params->resp_avail = resp_avail;
params->v = v;
INIT_LIST_HEAD(&(params->resp_queue));
pr_debug("%s: configNr = %d\n", __func__, i);
return params;
}
EXPORT_SYMBOL_GPL(rndis_register);
void rndis_deregister(int configNr)
void rndis_deregister(struct rndis_params *params)
{
int i;
pr_debug("%s:\n", __func__);
if (configNr >= RNDIS_MAX_CONFIGS) return;
rndis_per_dev_params[configNr].used = 0;
if (!params)
return;
i = params->confignr;
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
{
char name[20];
sprintf(name, NAME_TEMPLATE, i);
remove_proc_entry(name, NULL);
}
#endif
kfree(params);
rndis_put_nr(i);
}
EXPORT_SYMBOL_GPL(rndis_deregister);
int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev,
u16 *cdc_filter)
{
pr_debug("%s:\n", __func__);
if (!dev)
return -EINVAL;
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
if (!params)
return -1;
rndis_per_dev_params[configNr].dev = dev;
rndis_per_dev_params[configNr].filter = cdc_filter;
params->dev = dev;
params->filter = cdc_filter;
return 0;
}
EXPORT_SYMBOL_GPL(rndis_set_param_dev);
int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr)
int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID,
const char *vendorDescr)
{
pr_debug("%s:\n", __func__);
if (!vendorDescr) return -1;
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
if (!params)
return -1;
rndis_per_dev_params[configNr].vendorID = vendorID;
rndis_per_dev_params[configNr].vendorDescr = vendorDescr;
params->vendorID = vendorID;
params->vendorDescr = vendorDescr;
return 0;
}
EXPORT_SYMBOL_GPL(rndis_set_param_vendor);
int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed)
{
pr_debug("%s: %u %u\n", __func__, medium, speed);
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
if (!params)
return -1;
rndis_per_dev_params[configNr].medium = medium;
rndis_per_dev_params[configNr].speed = speed;
params->medium = medium;
params->speed = speed;
return 0;
}
@ -961,13 +1004,12 @@ void rndis_add_hdr(struct sk_buff *skb)
}
EXPORT_SYMBOL_GPL(rndis_add_hdr);
void rndis_free_response(int configNr, u8 *buf)
void rndis_free_response(struct rndis_params *params, u8 *buf)
{
rndis_resp_t *r;
struct list_head *act, *tmp;
list_for_each_safe(act, tmp,
&(rndis_per_dev_params[configNr].resp_queue))
list_for_each_safe(act, tmp, &(params->resp_queue))
{
r = list_entry(act, rndis_resp_t, list);
if (r && r->buf == buf) {
@ -978,15 +1020,14 @@ void rndis_free_response(int configNr, u8 *buf)
}
EXPORT_SYMBOL_GPL(rndis_free_response);
u8 *rndis_get_next_response(int configNr, u32 *length)
u8 *rndis_get_next_response(struct rndis_params *params, u32 *length)
{
rndis_resp_t *r;
struct list_head *act, *tmp;
if (!length) return NULL;
list_for_each_safe(act, tmp,
&(rndis_per_dev_params[configNr].resp_queue))
list_for_each_safe(act, tmp, &(params->resp_queue))
{
r = list_entry(act, rndis_resp_t, list);
if (!r->send) {
@ -1000,7 +1041,7 @@ u8 *rndis_get_next_response(int configNr, u32 *length)
}
EXPORT_SYMBOL_GPL(rndis_get_next_response);
static rndis_resp_t *rndis_add_response(int configNr, u32 length)
static rndis_resp_t *rndis_add_response(struct rndis_params *params, u32 length)
{
rndis_resp_t *r;
@ -1012,8 +1053,7 @@ static rndis_resp_t *rndis_add_response(int configNr, u32 length)
r->length = length;
r->send = 0;
list_add_tail(&r->list,
&(rndis_per_dev_params[configNr].resp_queue));
list_add_tail(&r->list, &(params->resp_queue));
return r;
}
@ -1103,11 +1143,11 @@ static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
break;
case 'C':
case 'c':
rndis_signal_connect(p->confignr);
rndis_signal_connect(p);
break;
case 'D':
case 'd':
rndis_signal_disconnect(p->confignr);
rndis_signal_disconnect(p);
break;
default:
if (fl_speed) p->speed = speed;
@ -1137,54 +1177,4 @@ static const struct file_operations rndis_proc_fops = {
#define NAME_TEMPLATE "driver/rndis-%03d"
static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
int rndis_init(void)
{
u8 i;
for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
char name [20];
sprintf(name, NAME_TEMPLATE, i);
rndis_connect_state[i] = proc_create_data(name, 0660, NULL,
&rndis_proc_fops,
(void *)(rndis_per_dev_params + i));
if (!rndis_connect_state[i]) {
pr_debug("%s: remove entries", __func__);
while (i) {
sprintf(name, NAME_TEMPLATE, --i);
remove_proc_entry(name, NULL);
}
pr_debug("\n");
return -EIO;
}
#endif
rndis_per_dev_params[i].confignr = i;
rndis_per_dev_params[i].used = 0;
rndis_per_dev_params[i].state = RNDIS_UNINITIALIZED;
rndis_per_dev_params[i].media_state
= RNDIS_MEDIA_STATE_DISCONNECTED;
INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue));
}
return 0;
}
void rndis_exit(void)
{
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
u8 i;
char name[20];
for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
sprintf(name, NAME_TEMPLATE, i);
remove_proc_entry(name, NULL);
}
#endif
}

View File

@ -177,7 +177,7 @@ typedef struct rndis_resp_t
typedef struct rndis_params
{
u8 confignr;
int confignr;
u8 used;
u16 saved_filter;
enum rndis_state state;
@ -197,24 +197,25 @@ typedef struct rndis_params
} rndis_params;
/* RNDIS Message parser and other useless functions */
int rndis_msg_parser (u8 configNr, u8 *buf);
int rndis_register(void (*resp_avail)(void *v), void *v);
void rndis_deregister (int configNr);
int rndis_set_param_dev (u8 configNr, struct net_device *dev,
int rndis_msg_parser(struct rndis_params *params, u8 *buf);
struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v);
void rndis_deregister(struct rndis_params *params);
int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev,
u16 *cdc_filter);
int rndis_set_param_vendor (u8 configNr, u32 vendorID,
int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID,
const char *vendorDescr);
int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
void rndis_add_hdr (struct sk_buff *skb);
int rndis_set_param_medium(struct rndis_params *params, u32 medium,
u32 speed);
void rndis_add_hdr(struct sk_buff *skb);
int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
struct sk_buff_head *list);
u8 *rndis_get_next_response (int configNr, u32 *length);
void rndis_free_response (int configNr, u8 *buf);
u8 *rndis_get_next_response(struct rndis_params *params, u32 *length);
void rndis_free_response(struct rndis_params *params, u8 *buf);
void rndis_uninit (int configNr);
int rndis_signal_connect (int configNr);
int rndis_signal_disconnect (int configNr);
int rndis_state (int configNr);
extern void rndis_set_host_mac (int configNr, const u8 *addr);
void rndis_uninit(struct rndis_params *params);
int rndis_signal_connect(struct rndis_params *params);
int rndis_signal_disconnect(struct rndis_params *params);
int rndis_state(struct rndis_params *params);
extern void rndis_set_host_mac(struct rndis_params *params, const u8 *addr);
#endif /* _LINUX_RNDIS_H */

View File

@ -39,8 +39,6 @@ struct f_rndis_opts {
int refcnt;
};
int rndis_init(void);
void rndis_exit(void);
void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net);
#endif /* U_RNDIS_H */

View File

@ -56,7 +56,6 @@ struct uvc_event
#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include <linux/videodev2.h>
#include <linux/version.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-device.h>

View File

@ -704,8 +704,8 @@ static int queue_dma(struct usba_udc *udc, struct usba_ep *ep,
unsigned long flags;
int ret;
DBG(DBG_DMA, "%s: req l/%u d/%08x %c%c%c\n",
ep->ep.name, req->req.length, req->req.dma,
DBG(DBG_DMA, "%s: req l/%u d/%pad %c%c%c\n",
ep->ep.name, req->req.length, &req->req.dma,
req->req.zero ? 'Z' : 'z',
req->req.short_not_ok ? 'S' : 's',
req->req.no_interrupt ? 'I' : 'i');
@ -2203,7 +2203,7 @@ static int usba_udc_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int usba_udc_suspend(struct device *dev)
{
struct usba_udc *udc = dev_get_drvdata(dev);

View File

@ -123,6 +123,11 @@ static char *type_string(u8 bmAttributes)
#define valid_bit cpu_to_le32(BIT(VALID_BIT))
#define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE))
static void ep_clear_seqnum(struct net2280_ep *ep);
static void stop_activity(struct net2280 *dev,
struct usb_gadget_driver *driver);
static void ep0_start(struct net2280 *dev);
/*-------------------------------------------------------------------------*/
static inline void enable_pciirqenb(struct net2280_ep *ep)
{
@ -142,7 +147,9 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
{
struct net2280 *dev;
struct net2280_ep *ep;
u32 max, tmp;
u32 max;
u32 tmp = 0;
u32 type;
unsigned long flags;
static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 };
int ret = 0;
@ -198,15 +205,29 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
/* set type, direction, address; reset fifo counters */
writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
if (tmp == USB_ENDPOINT_XFER_INT) {
if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) {
tmp = readl(&ep->cfg->ep_cfg);
/* If USB ep number doesn't match hardware ep number */
if ((tmp & 0xf) != usb_endpoint_num(desc)) {
ret = -EINVAL;
spin_unlock_irqrestore(&dev->lock, flags);
goto print_err;
}
if (ep->is_in)
tmp &= ~USB3380_EP_CFG_MASK_IN;
else
tmp &= ~USB3380_EP_CFG_MASK_OUT;
}
type = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
if (type == USB_ENDPOINT_XFER_INT) {
/* erratum 0105 workaround prevents hs NYET */
if (dev->chiprev == 0100 &&
dev->gadget.speed == USB_SPEED_HIGH &&
!(desc->bEndpointAddress & USB_DIR_IN))
writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE),
&ep->regs->ep_rsp);
} else if (tmp == USB_ENDPOINT_XFER_BULK) {
} else if (type == USB_ENDPOINT_XFER_BULK) {
/* catch some particularly blatant driver bugs */
if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) ||
(dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
@ -216,10 +237,10 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
goto print_err;
}
}
ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
ep->is_iso = (type == USB_ENDPOINT_XFER_ISOC);
/* Enable this endpoint */
if (dev->quirks & PLX_LEGACY) {
tmp <<= ENDPOINT_TYPE;
tmp |= type << ENDPOINT_TYPE;
tmp |= desc->bEndpointAddress;
/* default full fifo lines */
tmp |= (4 << ENDPOINT_BYTE_COUNT);
@ -228,17 +249,17 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
} else {
/* In Legacy mode, only OUT endpoints are used */
if (dev->enhanced_mode && ep->is_in) {
tmp <<= IN_ENDPOINT_TYPE;
tmp |= type << IN_ENDPOINT_TYPE;
tmp |= BIT(IN_ENDPOINT_ENABLE);
/* Not applicable to Legacy */
tmp |= BIT(ENDPOINT_DIRECTION);
} else {
tmp <<= OUT_ENDPOINT_TYPE;
tmp |= type << OUT_ENDPOINT_TYPE;
tmp |= BIT(OUT_ENDPOINT_ENABLE);
tmp |= (ep->is_in << ENDPOINT_DIRECTION);
}
tmp |= usb_endpoint_num(desc);
tmp |= (4 << ENDPOINT_BYTE_COUNT);
if (!dev->enhanced_mode)
tmp |= usb_endpoint_num(desc);
tmp |= (ep->ep.maxburst << MAX_BURST_SIZE);
}
@ -256,6 +277,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
}
if (dev->quirks & PLX_SUPERSPEED)
ep_clear_seqnum(ep);
writel(tmp, &ep->cfg->ep_cfg);
/* enable irqs */
@ -441,6 +464,13 @@ static void ep_reset_338x(struct net2280_regs __iomem *regs,
BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat);
tmp = readl(&ep->cfg->ep_cfg);
if (ep->is_in)
tmp &= ~USB3380_EP_CFG_MASK_IN;
else
tmp &= ~USB3380_EP_CFG_MASK_OUT;
writel(tmp, &ep->cfg->ep_cfg);
}
static void nuke(struct net2280_ep *);
@ -1468,11 +1498,14 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on)
spin_lock_irqsave(&dev->lock, flags);
tmp = readl(&dev->usb->usbctl);
dev->softconnect = (is_on != 0);
if (is_on)
tmp |= BIT(USB_DETECT_ENABLE);
else
tmp &= ~BIT(USB_DETECT_ENABLE);
writel(tmp, &dev->usb->usbctl);
if (is_on) {
ep0_start(dev);
writel(tmp | BIT(USB_DETECT_ENABLE), &dev->usb->usbctl);
} else {
writel(tmp & ~BIT(USB_DETECT_ENABLE), &dev->usb->usbctl);
stop_activity(dev, dev->driver);
}
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
@ -1860,8 +1893,8 @@ static void defect7374_enable_data_eps_zero(struct net2280 *dev)
tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) |
(2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) |
((dev->enhanced_mode) ?
BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) |
BIT(IN_ENDPOINT_ENABLE));
BIT(OUT_ENDPOINT_ENABLE) | BIT(IN_ENDPOINT_ENABLE) :
BIT(ENDPOINT_ENABLE)));
for (i = 1; i < 5; i++)
writel(tmp, &dev->ep[i].cfg->ep_cfg);
@ -1975,9 +2008,15 @@ static void usb_reset_338x(struct net2280 *dev)
/* clear old dma and irq state */
for (tmp = 0; tmp < 4; tmp++) {
struct net2280_ep *ep = &dev->ep[tmp + 1];
struct net2280_dma_regs __iomem *dma;
if (ep->dma)
if (ep->dma) {
abort_dma(ep);
} else {
dma = &dev->dma[tmp];
writel(BIT(DMA_ABORT), &dma->dmastat);
writel(0, &dma->dmactl);
}
}
writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1);
@ -2065,6 +2104,12 @@ static void usb_reinit_338x(struct net2280 *dev)
if (dev->enhanced_mode) {
ep->cfg = &dev->epregs[ne[i]];
/*
* Set USB endpoint number, hardware allows same number
* in both directions.
*/
if (i > 0 && i < 5)
writel(ne[i], &ep->cfg->ep_cfg);
ep->regs = (struct net2280_ep_regs __iomem *)
(((void __iomem *)&dev->epregs[ne[i]]) +
ep_reg_addr[i]);
@ -2874,6 +2919,26 @@ next_endpoints3:
return;
}
static void usb338x_handle_ep_intr(struct net2280 *dev, u32 stat0)
{
u32 index;
u32 bit;
for (index = 0; index < ARRAY_SIZE(ep_bit); index++) {
bit = BIT(ep_bit[index]);
if (!stat0)
break;
if (!(stat0 & bit))
continue;
stat0 &= ~bit;
handle_ep_small(&dev->ep[index]);
}
}
static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
{
struct net2280_ep *ep;
@ -3098,20 +3163,31 @@ do_stall:
#undef w_length
next_endpoints:
/* endpoint data irq ? */
scratch = stat & 0x7f;
stat &= ~0x7f;
for (num = 0; scratch; num++) {
u32 t;
if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) {
u32 mask = (BIT(ENDPOINT_0_INTERRUPT) |
USB3380_IRQSTAT0_EP_INTR_MASK_IN |
USB3380_IRQSTAT0_EP_INTR_MASK_OUT);
/* do this endpoint's FIFO and queue need tending? */
t = BIT(num);
if ((scratch & t) == 0)
continue;
scratch ^= t;
if (stat & mask) {
usb338x_handle_ep_intr(dev, stat & mask);
stat &= ~mask;
}
} else {
/* endpoint data irq ? */
scratch = stat & 0x7f;
stat &= ~0x7f;
for (num = 0; scratch; num++) {
u32 t;
ep = &dev->ep[num];
handle_ep_small(ep);
/* do this endpoint's FIFO and queue need tending? */
t = BIT(num);
if ((scratch & t) == 0)
continue;
scratch ^= t;
ep = &dev->ep[num];
handle_ep_small(ep);
}
}
if (stat)

View File

@ -92,40 +92,38 @@ static struct s3c2410_udc_mach_info *udc_info;
static uint32_t s3c2410_ticks = 0;
static int dprintk(int level, const char *fmt, ...)
__printf(2, 3)
static void dprintk(int level, const char *fmt, ...)
{
static char printk_buf[1024];
static long prevticks;
static int invocation;
struct va_format vaf;
va_list args;
int len;
if (level > USB_S3C2410_DEBUG_LEVEL)
return 0;
return;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
if (s3c2410_ticks != prevticks) {
prevticks = s3c2410_ticks;
invocation = 0;
}
len = scnprintf(printk_buf,
sizeof(printk_buf), "%1lu.%02d USB: ",
prevticks, invocation++);
pr_debug("%1lu.%02d USB: %pV", prevticks, invocation++, &vaf);
va_start(args, fmt);
len = vscnprintf(printk_buf+len,
sizeof(printk_buf)-len, fmt, args);
va_end(args);
pr_debug("%s", printk_buf);
return len;
}
#else
static int dprintk(int level, const char *fmt, ...)
__printf(2, 3)
static void dprintk(int level, const char *fmt, ...)
{
return 0;
}
#endif
static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
{
u32 addr_reg, pwr_reg, ep_int_reg, usb_int_reg;

View File

@ -438,11 +438,15 @@ static void am35x_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
}
static const struct musb_platform_ops am35x_ops = {
.quirks = MUSB_INDEXED_EP,
.quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP,
.init = am35x_musb_init,
.exit = am35x_musb_exit,
.read_fifo = am35x_read_fifo,
#ifdef CONFIG_USB_INVENTRA_DMA
.dma_init = musbhs_dma_controller_create,
.dma_exit = musbhs_dma_controller_destroy,
#endif
.enable = am35x_musb_enable,
.disable = am35x_musb_disable,
@ -565,7 +569,7 @@ static int am35x_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int am35x_suspend(struct device *dev)
{
struct am35x_glue *glue = dev_get_drvdata(dev);

View File

@ -465,6 +465,7 @@ static int bfin_musb_exit(struct musb *musb)
}
static const struct musb_platform_ops bfin_ops = {
.quirks = MUSB_DMA_INVENTRA,
.init = bfin_musb_init,
.exit = bfin_musb_exit,
@ -477,6 +478,10 @@ static const struct musb_platform_ops bfin_ops = {
.fifo_mode = 2,
.read_fifo = bfin_read_fifo,
.write_fifo = bfin_write_fifo,
#ifdef CONFIG_USB_INVENTRA_DMA
.dma_init = musbhs_dma_controller_create,
.dma_exit = musbhs_dma_controller_destroy,
#endif
.enable = bfin_musb_enable,
.disable = bfin_musb_disable,

View File

@ -1297,7 +1297,8 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id)
EXPORT_SYMBOL_GPL(cppi_interrupt);
/* Instantiate a software object representing a DMA controller. */
struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mregs)
struct dma_controller *
cppi_dma_controller_create(struct musb *musb, void __iomem *mregs)
{
struct cppi *controller;
struct device *dev = musb->controller;
@ -1334,7 +1335,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mr
if (irq > 0) {
if (request_irq(irq, cppi_interrupt, 0, "cppi-dma", musb)) {
dev_err(dev, "request_irq %d failed!\n", irq);
dma_controller_destroy(&controller->controller);
musb_dma_controller_destroy(&controller->controller);
return NULL;
}
controller->irq = irq;
@ -1343,11 +1344,12 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mr
cppi_controller_start(controller);
return &controller->controller;
}
EXPORT_SYMBOL_GPL(cppi_dma_controller_create);
/*
* Destroy a previously-instantiated DMA controller.
*/
void dma_controller_destroy(struct dma_controller *c)
void cppi_dma_controller_destroy(struct dma_controller *c)
{
struct cppi *cppi;
@ -1363,6 +1365,7 @@ void dma_controller_destroy(struct dma_controller *c)
kfree(cppi);
}
EXPORT_SYMBOL_GPL(cppi_dma_controller_destroy);
/*
* Context: controller irqlocked, endpoint selected

View File

@ -458,11 +458,15 @@ static int da8xx_musb_exit(struct musb *musb)
}
static const struct musb_platform_ops da8xx_ops = {
.quirks = MUSB_INDEXED_EP,
.quirks = MUSB_DMA_CPPI | MUSB_INDEXED_EP,
.init = da8xx_musb_init,
.exit = da8xx_musb_exit,
.fifo_mode = 2,
#ifdef CONFIG_USB_TI_CPPI_DMA
.dma_init = cppi_dma_controller_create,
.dma_exit = cppi_dma_controller_destroy,
#endif
.enable = da8xx_musb_enable,
.disable = da8xx_musb_disable,

View File

@ -284,7 +284,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci)
* mask, state, "vector", and EOI registers.
*/
cppi = container_of(musb->dma_controller, struct cppi, controller);
if (is_cppi_enabled() && musb->dma_controller && !cppi->irq)
if (is_cppi_enabled(musb) && musb->dma_controller && !cppi->irq)
retval = cppi_interrupt(irq, __hci);
/* ack and handle non-CPPI interrupts */
@ -491,9 +491,14 @@ static int davinci_musb_exit(struct musb *musb)
}
static const struct musb_platform_ops davinci_ops = {
.quirks = MUSB_DMA_CPPI,
.init = davinci_musb_init,
.exit = davinci_musb_exit,
#ifdef CONFIG_USB_TI_CPPI_DMA
.dma_init = cppi_dma_controller_create,
.dma_exit = cppi_dma_controller_destroy,
#endif
.enable = davinci_musb_enable,
.disable = davinci_musb_disable,

View File

@ -105,8 +105,12 @@ static int jz4740_musb_exit(struct musb *musb)
return 0;
}
/*
* DMA has not been confirmed to work with CONFIG_USB_INVENTRA_DMA,
* so let's not set up the dma function pointers yet.
*/
static const struct musb_platform_ops jz4740_musb_ops = {
.quirks = MUSB_INDEXED_EP,
.quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP,
.fifo_mode = 2,
.init = jz4740_musb_init,
.exit = jz4740_musb_exit,

View File

@ -251,6 +251,11 @@ static u32 musb_indexed_ep_offset(u8 epnum, u16 offset)
return 0x10 + offset;
}
static u32 musb_default_busctl_offset(u8 epnum, u16 offset)
{
return 0x80 + (0x08 * epnum) + offset;
}
static u8 musb_default_readb(const void __iomem *addr, unsigned offset)
{
return __raw_readb(addr + offset);
@ -309,7 +314,7 @@ static void musb_default_write_fifo(struct musb_hw_ep *hw_ep, u16 len,
index += len & ~0x03;
}
if (len & 0x02) {
musb_writew(fifo, 0, *(u16 *)&src[index]);
__raw_writew(*(u16 *)&src[index], fifo);
index += 2;
}
} else {
@ -319,7 +324,7 @@ static void musb_default_write_fifo(struct musb_hw_ep *hw_ep, u16 len,
}
}
if (len & 0x01)
musb_writeb(fifo, 0, src[index]);
__raw_writeb(src[index], fifo);
} else {
/* byte aligned */
iowrite8_rep(fifo, src, len);
@ -351,7 +356,7 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
index = len & ~0x03;
}
if (len & 0x02) {
*(u16 *)&dst[index] = musb_readw(fifo, 0);
*(u16 *)&dst[index] = __raw_readw(fifo);
index += 2;
}
} else {
@ -361,7 +366,7 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
}
}
if (len & 0x01)
dst[index] = musb_readb(fifo, 0);
dst[index] = __raw_readb(fifo);
} else {
/* byte aligned */
ioread8_rep(fifo, dst, len);
@ -389,6 +394,15 @@ EXPORT_SYMBOL_GPL(musb_readl);
void (*musb_writel)(void __iomem *addr, unsigned offset, u32 data);
EXPORT_SYMBOL_GPL(musb_writel);
#ifndef CONFIG_MUSB_PIO_ONLY
struct dma_controller *
(*musb_dma_controller_create)(struct musb *musb, void __iomem *base);
EXPORT_SYMBOL(musb_dma_controller_create);
void (*musb_dma_controller_destroy)(struct dma_controller *c);
EXPORT_SYMBOL(musb_dma_controller_destroy);
#endif
/*
* New style IO functions
*/
@ -1535,7 +1549,6 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
#endif
hw_ep->regs = musb->io.ep_offset(i, 0) + mbase;
hw_ep->target_regs = musb_read_target_reg_base(i, mbase);
hw_ep->rx_reinit = 1;
hw_ep->tx_reinit = 1;
@ -1658,15 +1671,13 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
/* called with controller lock already held */
if (!epnum) {
#ifndef CONFIG_USB_TUSB_OMAP_DMA
if (!is_cppi_enabled()) {
if (!is_cppi_enabled(musb)) {
/* endpoint 0 */
if (is_host_active(musb))
musb_h_ep0_irq(musb);
else
musb_g_ep0_irq(musb);
}
#endif
} else {
/* endpoints 1..15 */
if (transmit) {
@ -2021,13 +2032,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
if (musb->ops->quirks)
musb->io.quirks = musb->ops->quirks;
/* At least tusb6010 has it's own offsets.. */
if (musb->ops->ep_offset)
musb->io.ep_offset = musb->ops->ep_offset;
if (musb->ops->ep_select)
musb->io.ep_select = musb->ops->ep_select;
/* ..and some devices use indexed offset or flat offset */
/* Set default ep access to indexed offset or flat offset ops */
if (musb->io.quirks & MUSB_INDEXED_EP) {
musb->io.ep_offset = musb_indexed_ep_offset;
musb->io.ep_select = musb_indexed_ep_select;
@ -2035,6 +2040,11 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
musb->io.ep_offset = musb_flat_ep_offset;
musb->io.ep_select = musb_flat_ep_select;
}
/* And override them with platform specific ops if specified. */
if (musb->ops->ep_offset)
musb->io.ep_offset = musb->ops->ep_offset;
if (musb->ops->ep_select)
musb->io.ep_select = musb->ops->ep_select;
if (musb->ops->fifo_mode)
fifo_mode = musb->ops->fifo_mode;
@ -2046,6 +2056,11 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
else
musb->io.fifo_offset = musb_default_fifo_offset;
if (musb->ops->busctl_offset)
musb->io.busctl_offset = musb->ops->busctl_offset;
else
musb->io.busctl_offset = musb_default_busctl_offset;
if (musb->ops->readb)
musb_readb = musb->ops->readb;
if (musb->ops->writeb)
@ -2059,6 +2074,15 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
if (musb->ops->writel)
musb_writel = musb->ops->writel;
#ifndef CONFIG_MUSB_PIO_ONLY
if (!musb->ops->dma_init || !musb->ops->dma_exit) {
dev_err(dev, "DMA controller not set\n");
goto fail2;
}
musb_dma_controller_create = musb->ops->dma_init;
musb_dma_controller_destroy = musb->ops->dma_exit;
#endif
if (musb->ops->read_fifo)
musb->io.read_fifo = musb->ops->read_fifo;
else
@ -2078,7 +2102,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
pm_runtime_get_sync(musb->controller);
if (use_dma && dev->dma_mask) {
musb->dma_controller = dma_controller_create(musb, musb->mregs);
musb->dma_controller =
musb_dma_controller_create(musb, musb->mregs);
if (IS_ERR(musb->dma_controller)) {
status = PTR_ERR(musb->dma_controller);
goto fail2_5;
@ -2189,7 +2214,7 @@ fail3:
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
if (musb->dma_controller)
dma_controller_destroy(musb->dma_controller);
musb_dma_controller_destroy(musb->dma_controller);
fail2_5:
pm_runtime_put_sync(musb->controller);
@ -2248,7 +2273,7 @@ static int musb_remove(struct platform_device *pdev)
musb_shutdown(pdev);
if (musb->dma_controller)
dma_controller_destroy(musb->dma_controller);
musb_dma_controller_destroy(musb->dma_controller);
cancel_work_sync(&musb->irq_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
@ -2316,18 +2341,18 @@ static void musb_save_context(struct musb *musb)
musb_readb(epio, MUSB_RXINTERVAL);
musb->context.index_regs[i].txfunaddr =
musb_read_txfunaddr(musb_base, i);
musb_read_txfunaddr(musb, i);
musb->context.index_regs[i].txhubaddr =
musb_read_txhubaddr(musb_base, i);
musb_read_txhubaddr(musb, i);
musb->context.index_regs[i].txhubport =
musb_read_txhubport(musb_base, i);
musb_read_txhubport(musb, i);
musb->context.index_regs[i].rxfunaddr =
musb_read_rxfunaddr(musb_base, i);
musb_read_rxfunaddr(musb, i);
musb->context.index_regs[i].rxhubaddr =
musb_read_rxhubaddr(musb_base, i);
musb_read_rxhubaddr(musb, i);
musb->context.index_regs[i].rxhubport =
musb_read_rxhubport(musb_base, i);
musb_read_rxhubport(musb, i);
}
}
@ -2335,7 +2360,6 @@ static void musb_restore_context(struct musb *musb)
{
int i;
void __iomem *musb_base = musb->mregs;
void __iomem *ep_target_regs;
void __iomem *epio;
u8 power;
@ -2396,21 +2420,18 @@ static void musb_restore_context(struct musb *musb)
musb_writeb(epio, MUSB_RXINTERVAL,
musb->context.index_regs[i].rxinterval);
musb_write_txfunaddr(musb_base, i,
musb_write_txfunaddr(musb, i,
musb->context.index_regs[i].txfunaddr);
musb_write_txhubaddr(musb_base, i,
musb_write_txhubaddr(musb, i,
musb->context.index_regs[i].txhubaddr);
musb_write_txhubport(musb_base, i,
musb_write_txhubport(musb, i,
musb->context.index_regs[i].txhubport);
ep_target_regs =
musb_read_target_reg_base(i, musb_base);
musb_write_rxfunaddr(ep_target_regs,
musb_write_rxfunaddr(musb, i,
musb->context.index_regs[i].rxfunaddr);
musb_write_rxhubaddr(ep_target_regs,
musb_write_rxhubaddr(musb, i,
musb->context.index_regs[i].rxhubaddr);
musb_write_rxhubport(ep_target_regs,
musb_write_rxhubport(musb, i,
musb->context.index_regs[i].rxhubport);
}
musb_writeb(musb_base, MUSB_INDEX, musb->context.index);

View File

@ -67,7 +67,6 @@ struct musb_ep;
#include "musb_dma.h"
#include "musb_io.h"
#include "musb_regs.h"
#include "musb_gadget.h"
#include <linux/usb/hcd.h>
@ -157,6 +156,8 @@ struct musb_io;
* @writel: write 32 bits
* @read_fifo: reads the fifo
* @write_fifo: writes to fifo
* @dma_init: platform specific dma init function
* @dma_exit: platform specific dma exit function
* @init: turns on clocks, sets up platform-specific registers, etc
* @exit: undoes @init
* @set_mode: forcefully changes operating mode
@ -165,6 +166,8 @@ struct musb_io;
* @vbus_status: returns vbus status if possible
* @set_vbus: forces vbus status
* @adjust_channel_params: pre check for standard dma channel_program func
* @pre_root_reset_end: called before the root usb port reset flag gets cleared
* @post_root_reset_end: called after the root usb port reset flag gets cleared
*/
struct musb_platform_ops {
@ -187,6 +190,7 @@ struct musb_platform_ops {
void (*ep_select)(void __iomem *mbase, u8 epnum);
u16 fifo_mode;
u32 (*fifo_offset)(u8 epnum);
u32 (*busctl_offset)(u8 epnum, u16 offset);
u8 (*readb)(const void __iomem *addr, unsigned offset);
void (*writeb)(void __iomem *addr, unsigned offset, u8 data);
u16 (*readw)(const void __iomem *addr, unsigned offset);
@ -195,6 +199,9 @@ struct musb_platform_ops {
void (*writel)(void __iomem *addr, unsigned offset, u32 data);
void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf);
void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
struct dma_controller *
(*dma_init) (struct musb *musb, void __iomem *base);
void (*dma_exit)(struct dma_controller *c);
int (*set_mode)(struct musb *musb, u8 mode);
void (*try_idle)(struct musb *musb, unsigned long timeout);
int (*recover)(struct musb *musb);
@ -205,6 +212,8 @@ struct musb_platform_ops {
int (*adjust_channel_params)(struct dma_channel *channel,
u16 packet_sz, u8 *mode,
dma_addr_t *dma_addr, u32 *len);
void (*pre_root_reset_end)(struct musb *musb);
void (*post_root_reset_end)(struct musb *musb);
};
/*
@ -241,8 +250,6 @@ struct musb_hw_ep {
void __iomem *fifo_sync_va;
#endif
void __iomem *target_regs;
/* currently scheduled peripheral endpoint */
struct musb_qh *in_qh;
struct musb_qh *out_qh;
@ -437,6 +444,9 @@ struct musb {
#endif
};
/* This must be included after struct musb is defined */
#include "musb_regs.h"
static inline struct musb *gadget_to_musb(struct usb_gadget *g)
{
return container_of(g, struct musb, g);
@ -590,4 +600,16 @@ static inline int musb_platform_exit(struct musb *musb)
return musb->ops->exit(musb);
}
static inline void musb_platform_pre_root_reset_end(struct musb *musb)
{
if (musb->ops->pre_root_reset_end)
musb->ops->pre_root_reset_end(musb);
}
static inline void musb_platform_post_root_reset_end(struct musb *musb)
{
if (musb->ops->post_root_reset_end)
musb->ops->post_root_reset_end(musb);
}
#endif /* __MUSB_CORE_H__ */

View File

@ -678,7 +678,7 @@ err:
return ret;
}
void dma_controller_destroy(struct dma_controller *c)
void cppi41_dma_controller_destroy(struct dma_controller *c)
{
struct cppi41_dma_controller *controller = container_of(c,
struct cppi41_dma_controller, controller);
@ -687,9 +687,10 @@ void dma_controller_destroy(struct dma_controller *c)
cppi41_dma_controller_stop(controller);
kfree(controller);
}
EXPORT_SYMBOL_GPL(cppi41_dma_controller_destroy);
struct dma_controller *dma_controller_create(struct musb *musb,
void __iomem *base)
struct dma_controller *
cppi41_dma_controller_create(struct musb *musb, void __iomem *base)
{
struct cppi41_dma_controller *controller;
int ret = 0;
@ -726,3 +727,4 @@ kzalloc_fail:
return ERR_PTR(ret);
return NULL;
}
EXPORT_SYMBOL_GPL(cppi41_dma_controller_create);

View File

@ -191,9 +191,16 @@ static ssize_t musb_test_mode_write(struct file *file,
{
struct seq_file *s = file->private_data;
struct musb *musb = s->private;
u8 test = 0;
u8 test;
char buf[18];
test = musb_readb(musb->mregs, MUSB_TESTMODE);
if (test) {
dev_err(musb->controller, "Error: test mode is already set. "
"Please do USB Bus Reset to start a new test.\n");
return count;
}
memset(buf, 0x00, sizeof(buf));
if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
@ -238,6 +245,90 @@ static const struct file_operations musb_test_mode_fops = {
.release = single_release,
};
static int musb_softconnect_show(struct seq_file *s, void *unused)
{
struct musb *musb = s->private;
u8 reg;
int connect;
switch (musb->xceiv->otg->state) {
case OTG_STATE_A_HOST:
case OTG_STATE_A_WAIT_BCON:
reg = musb_readb(musb->mregs, MUSB_DEVCTL);
connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0;
break;
default:
connect = -1;
}
seq_printf(s, "%d\n", connect);
return 0;
}
static int musb_softconnect_open(struct inode *inode, struct file *file)
{
return single_open(file, musb_softconnect_show, inode->i_private);
}
static ssize_t musb_softconnect_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *ppos)
{
struct seq_file *s = file->private_data;
struct musb *musb = s->private;
char buf[2];
u8 reg;
memset(buf, 0x00, sizeof(buf));
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (!strncmp(buf, "0", 1)) {
switch (musb->xceiv->otg->state) {
case OTG_STATE_A_HOST:
musb_root_disconnect(musb);
reg = musb_readb(musb->mregs, MUSB_DEVCTL);
reg &= ~MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, reg);
break;
default:
break;
}
} else if (!strncmp(buf, "1", 1)) {
switch (musb->xceiv->otg->state) {
case OTG_STATE_A_WAIT_BCON:
/*
* musb_save_context() called in musb_runtime_suspend()
* might cache devctl with SESSION bit cleared during
* soft-disconnect, so specifically set SESSION bit
* here to preserve it for musb_runtime_resume().
*/
musb->context.devctl |= MUSB_DEVCTL_SESSION;
reg = musb_readb(musb->mregs, MUSB_DEVCTL);
reg |= MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, reg);
break;
default:
break;
}
}
return count;
}
/*
* In host mode, connect/disconnect the bus without physically
* remove the devices.
*/
static const struct file_operations musb_softconnect_fops = {
.open = musb_softconnect_open,
.write = musb_softconnect_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
int musb_init_debugfs(struct musb *musb)
{
struct dentry *root;
@ -264,6 +355,13 @@ int musb_init_debugfs(struct musb *musb)
goto err1;
}
file = debugfs_create_file("softconnect", S_IRUGO | S_IWUSR,
root, musb, &musb_softconnect_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
musb->debugfs_root = root;
return 0;

View File

@ -68,16 +68,41 @@ struct musb_hw_ep;
#define is_dma_capable() (1)
#endif
#if defined(CONFIG_USB_TI_CPPI_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA)
#define is_cppi_enabled() 1
#ifdef CONFIG_USB_UX500_DMA
#define musb_dma_ux500(musb) (musb->io.quirks & MUSB_DMA_UX500)
#else
#define is_cppi_enabled() 0
#define musb_dma_ux500(musb) 0
#endif
#ifdef CONFIG_USB_TI_CPPI41_DMA
#define musb_dma_cppi41(musb) (musb->io.quirks & MUSB_DMA_CPPI41)
#else
#define musb_dma_cppi41(musb) 0
#endif
#ifdef CONFIG_USB_TI_CPPI_DMA
#define musb_dma_cppi(musb) (musb->io.quirks & MUSB_DMA_CPPI)
#else
#define musb_dma_cppi(musb) 0
#endif
#ifdef CONFIG_USB_TUSB_OMAP_DMA
#define tusb_dma_omap() 1
#define tusb_dma_omap(musb) (musb->io.quirks & MUSB_DMA_TUSB_OMAP)
#else
#define tusb_dma_omap() 0
#define tusb_dma_omap(musb) 0
#endif
#ifdef CONFIG_USB_INVENTRA_DMA
#define musb_dma_inventra(musb) (musb->io.quirks & MUSB_DMA_INVENTRA)
#else
#define musb_dma_inventra(musb) 0
#endif
#if defined(CONFIG_USB_TI_CPPI_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA)
#define is_cppi_enabled(musb) \
(musb_dma_cppi(musb) || musb_dma_cppi41(musb))
#else
#define is_cppi_enabled(musb) 0
#endif
/* Anomaly 05000456 - USB Receive Interrupt Is Not Generated in DMA Mode 1
@ -177,19 +202,41 @@ struct dma_controller {
extern void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit);
#ifdef CONFIG_MUSB_PIO_ONLY
static inline struct dma_controller *dma_controller_create(struct musb *m,
void __iomem *io)
static inline struct dma_controller *
musb_dma_controller_create(struct musb *m, void __iomem *io)
{
return NULL;
}
static inline void dma_controller_destroy(struct dma_controller *d) { }
static inline void musb_dma_controller_destroy(struct dma_controller *d) { }
#else
extern struct dma_controller *dma_controller_create(struct musb *, void __iomem *);
extern struct dma_controller *
(*musb_dma_controller_create)(struct musb *, void __iomem *);
extern void dma_controller_destroy(struct dma_controller *);
extern void (*musb_dma_controller_destroy)(struct dma_controller *);
#endif
/* Platform specific DMA functions */
extern struct dma_controller *
musbhs_dma_controller_create(struct musb *musb, void __iomem *base);
extern void musbhs_dma_controller_destroy(struct dma_controller *c);
extern struct dma_controller *
tusb_dma_controller_create(struct musb *musb, void __iomem *base);
extern void tusb_dma_controller_destroy(struct dma_controller *c);
extern struct dma_controller *
cppi_dma_controller_create(struct musb *musb, void __iomem *base);
extern void cppi_dma_controller_destroy(struct dma_controller *c);
extern struct dma_controller *
cppi41_dma_controller_create(struct musb *musb, void __iomem *base);
extern void cppi41_dma_controller_destroy(struct dma_controller *c);
extern struct dma_controller *
ux500_dma_controller_create(struct musb *musb, void __iomem *base);
extern void ux500_dma_controller_destroy(struct dma_controller *c);
#endif /* __MUSB_DMA_H__ */

View File

@ -634,10 +634,14 @@ static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
}
static struct musb_platform_ops dsps_ops = {
.quirks = MUSB_INDEXED_EP,
.quirks = MUSB_DMA_CPPI41 | MUSB_INDEXED_EP,
.init = dsps_musb_init,
.exit = dsps_musb_exit,
#ifdef CONFIG_USB_TI_CPPI41_DMA
.dma_init = cppi41_dma_controller_create,
.dma_exit = cppi41_dma_controller_destroy,
#endif
.enable = dsps_musb_enable,
.disable = dsps_musb_disable,

View File

@ -366,7 +366,7 @@ static void txstate(struct musb *musb, struct musb_request *req)
}
#endif
if (is_cppi_enabled()) {
if (is_cppi_enabled(musb)) {
/* program endpoint CSR first, then setup DMA */
csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY);
csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE |
@ -402,7 +402,7 @@ static void txstate(struct musb *musb, struct musb_request *req)
musb_writew(epio, MUSB_TXCSR, csr);
/* invariant: prequest->buf is non-null */
}
} else if (tusb_dma_omap())
} else if (tusb_dma_omap(musb))
use_dma = use_dma && c->channel_program(
musb_ep->dma, musb_ep->packet_sz,
request->zero,
@ -489,6 +489,7 @@ void musb_g_tx(struct musb *musb, u8 epnum)
if (request) {
u8 is_dma = 0;
bool short_packet = false;
if (dma && (csr & MUSB_TXCSR_DMAENAB)) {
is_dma = 1;
@ -507,15 +508,18 @@ void musb_g_tx(struct musb *musb, u8 epnum)
* First, maybe a terminating short packet. Some DMA
* engines might handle this by themselves.
*/
if ((request->zero && request->length
if ((request->zero && request->length)
&& (request->length % musb_ep->packet_sz == 0)
&& (request->actual == request->length))
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
|| (is_dma && (!dma->desired_mode ||
short_packet = true;
if ((musb_dma_inventra(musb) || musb_dma_ux500(musb)) &&
(is_dma && (!dma->desired_mode ||
(request->actual &
(musb_ep->packet_sz - 1))))
#endif
) {
(musb_ep->packet_sz - 1)))))
short_packet = true;
if (short_packet) {
/*
* On DMA completion, FIFO may not be
* available yet...
@ -595,7 +599,7 @@ static void rxstate(struct musb *musb, struct musb_request *req)
return;
}
if (is_cppi_enabled() && is_buffer_mapped(req)) {
if (is_cppi_enabled(musb) && is_buffer_mapped(req)) {
struct dma_controller *c = musb->dma_controller;
struct dma_channel *channel = musb_ep->dma;
@ -772,7 +776,7 @@ static void rxstate(struct musb *musb, struct musb_request *req)
fifo_count = min_t(unsigned, len, fifo_count);
#ifdef CONFIG_USB_TUSB_OMAP_DMA
if (tusb_dma_omap() && is_buffer_mapped(req)) {
if (tusb_dma_omap(musb) && is_buffer_mapped(req)) {
struct dma_controller *c = musb->dma_controller;
struct dma_channel *channel = musb_ep->dma;
u32 dma_addr = request->dma + request->actual;

View File

@ -181,7 +181,7 @@ static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep)
/* NOTE: no locks here; caller should lock and select EP */
txcsr = musb_readw(ep->regs, MUSB_TXCSR);
txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS;
if (is_cppi_enabled())
if (is_cppi_enabled(ep->musb))
txcsr |= MUSB_TXCSR_DMAMODE;
musb_writew(ep->regs, MUSB_TXCSR, txcsr);
}
@ -294,7 +294,7 @@ start:
if (!hw_ep->tx_channel)
musb_h_tx_start(hw_ep);
else if (is_cppi_enabled() || tusb_dma_omap())
else if (is_cppi_enabled(musb) || tusb_dma_omap(musb))
musb_h_tx_dma_start(hw_ep);
}
}
@ -555,8 +555,9 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err)
* the busy/not-empty tests are basically paranoia.
*/
static void
musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep)
musb_rx_reinit(struct musb *musb, struct musb_qh *qh, u8 epnum)
{
struct musb_hw_ep *ep = musb->endpoints + epnum;
u16 csr;
/* NOTE: we know the "rx" fifo reinit never triggers for ep0.
@ -594,10 +595,9 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep)
/* target addr and (for multipoint) hub addr/port */
if (musb->is_multipoint) {
musb_write_rxfunaddr(ep->target_regs, qh->addr_reg);
musb_write_rxhubaddr(ep->target_regs, qh->h_addr_reg);
musb_write_rxhubport(ep->target_regs, qh->h_port_reg);
musb_write_rxfunaddr(musb, epnum, qh->addr_reg);
musb_write_rxhubaddr(musb, epnum, qh->h_addr_reg);
musb_write_rxhubport(musb, epnum, qh->h_port_reg);
} else
musb_writeb(musb->mregs, MUSB_FADDR, qh->addr_reg);
@ -617,23 +617,22 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep)
ep->rx_reinit = 0;
}
static bool musb_tx_dma_program(struct dma_controller *dma,
static int musb_tx_dma_set_mode_mentor(struct dma_controller *dma,
struct musb_hw_ep *hw_ep, struct musb_qh *qh,
struct urb *urb, u32 offset, u32 length)
struct urb *urb, u32 offset,
u32 *length, u8 *mode)
{
struct dma_channel *channel = hw_ep->tx_channel;
void __iomem *epio = hw_ep->regs;
u16 pkt_size = qh->maxpacket;
u16 csr;
u8 mode;
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
if (length > channel->max_len)
length = channel->max_len;
if (*length > channel->max_len)
*length = channel->max_len;
csr = musb_readw(epio, MUSB_TXCSR);
if (length > pkt_size) {
mode = 1;
if (*length > pkt_size) {
*mode = 1;
csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB;
/* autoset shouldn't be set in high bandwidth */
/*
@ -649,15 +648,28 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
can_bulk_split(hw_ep->musb, qh->type)))
csr |= MUSB_TXCSR_AUTOSET;
} else {
mode = 0;
*mode = 0;
csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE);
csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */
}
channel->desired_mode = mode;
musb_writew(epio, MUSB_TXCSR, csr);
#else
if (!is_cppi_enabled() && !tusb_dma_omap())
return false;
return 0;
}
static int musb_tx_dma_set_mode_cppi_tusb(struct dma_controller *dma,
struct musb_hw_ep *hw_ep,
struct musb_qh *qh,
struct urb *urb,
u32 offset,
u32 *length,
u8 *mode)
{
struct dma_channel *channel = hw_ep->tx_channel;
if (!is_cppi_enabled(hw_ep->musb) && !tusb_dma_omap(hw_ep->musb))
return -ENODEV;
channel->actual_len = 0;
@ -665,8 +677,28 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
* TX uses "RNDIS" mode automatically but needs help
* to identify the zero-length-final-packet case.
*/
mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0;
#endif
*mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0;
return 0;
}
static bool musb_tx_dma_program(struct dma_controller *dma,
struct musb_hw_ep *hw_ep, struct musb_qh *qh,
struct urb *urb, u32 offset, u32 length)
{
struct dma_channel *channel = hw_ep->tx_channel;
u16 pkt_size = qh->maxpacket;
u8 mode;
int res;
if (musb_dma_inventra(hw_ep->musb) || musb_dma_ux500(hw_ep->musb))
res = musb_tx_dma_set_mode_mentor(dma, hw_ep, qh, urb,
offset, &length, &mode);
else
res = musb_tx_dma_set_mode_cppi_tusb(dma, hw_ep, qh, urb,
offset, &length, &mode);
if (res)
return false;
qh->segsize = length;
@ -678,6 +710,9 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
if (!dma->channel_program(channel, pkt_size, mode,
urb->transfer_dma + offset, length)) {
void __iomem *epio = hw_ep->regs;
u16 csr;
dma->channel_release(channel);
hw_ep->tx_channel = NULL;
@ -801,9 +836,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
/* target addr and (for multipoint) hub addr/port */
if (musb->is_multipoint) {
musb_write_txfunaddr(mbase, epnum, qh->addr_reg);
musb_write_txhubaddr(mbase, epnum, qh->h_addr_reg);
musb_write_txhubport(mbase, epnum, qh->h_port_reg);
musb_write_txfunaddr(musb, epnum, qh->addr_reg);
musb_write_txhubaddr(musb, epnum, qh->h_addr_reg);
musb_write_txhubport(musb, epnum, qh->h_port_reg);
/* FIXME if !epnum, do the same for RX ... */
} else
musb_writeb(mbase, MUSB_FADDR, qh->addr_reg);
@ -875,7 +910,7 @@ finish:
u16 csr;
if (hw_ep->rx_reinit) {
musb_rx_reinit(musb, qh, hw_ep);
musb_rx_reinit(musb, qh, epnum);
/* init new state: toggle and NYET, maybe DMA later */
if (usb_gettoggle(urb->dev, qh->epnum, 0))
@ -901,7 +936,7 @@ finish:
/* kick things off */
if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) {
if ((is_cppi_enabled(musb) || tusb_dma_omap(musb)) && dma_channel) {
/* Candidate for DMA */
dma_channel->actual_len = 0L;
qh->segsize = len;
@ -1441,7 +1476,7 @@ done:
} else if ((usb_pipeisoc(pipe) || transfer_pending) && dma) {
if (musb_tx_dma_program(musb->dma_controller, hw_ep, qh, urb,
offset, length)) {
if (is_cppi_enabled() || tusb_dma_omap())
if (is_cppi_enabled(musb) || tusb_dma_omap(musb))
musb_h_tx_dma_start(hw_ep);
return;
}
@ -1498,9 +1533,47 @@ done:
MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY);
}
#ifdef CONFIG_USB_TI_CPPI41_DMA
/* Seems to set up ISO for cppi41 and not advance len. See commit c57c41d */
static int musb_rx_dma_iso_cppi41(struct dma_controller *dma,
struct musb_hw_ep *hw_ep,
struct musb_qh *qh,
struct urb *urb,
size_t len)
{
struct dma_channel *channel = hw_ep->tx_channel;
void __iomem *epio = hw_ep->regs;
dma_addr_t *buf;
u32 length, res;
u16 val;
#ifdef CONFIG_USB_INVENTRA_DMA
buf = (void *)urb->iso_frame_desc[qh->iso_idx].offset +
(u32)urb->transfer_dma;
length = urb->iso_frame_desc[qh->iso_idx].length;
val = musb_readw(epio, MUSB_RXCSR);
val |= MUSB_RXCSR_DMAENAB;
musb_writew(hw_ep->regs, MUSB_RXCSR, val);
res = dma->channel_program(channel, qh->maxpacket, 0,
(u32)buf, length);
return res;
}
#else
static inline int musb_rx_dma_iso_cppi41(struct dma_controller *dma,
struct musb_hw_ep *hw_ep,
struct musb_qh *qh,
struct urb *urb,
size_t len)
{
return false;
}
#endif
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \
defined(CONFIG_USB_TI_CPPI41_DMA)
/* Host side RX (IN) using Mentor DMA works as follows:
submit_urb ->
- if queue was empty, ProgramEndpoint
@ -1535,7 +1608,194 @@ done:
* thus be a great candidate for using mode 1 ... for all but the
* last packet of one URB's transfer.
*/
static int musb_rx_dma_inventra_cppi41(struct dma_controller *dma,
struct musb_hw_ep *hw_ep,
struct musb_qh *qh,
struct urb *urb,
size_t len)
{
struct dma_channel *channel = hw_ep->rx_channel;
void __iomem *epio = hw_ep->regs;
u16 val;
int pipe;
bool done;
pipe = urb->pipe;
if (usb_pipeisoc(pipe)) {
struct usb_iso_packet_descriptor *d;
d = urb->iso_frame_desc + qh->iso_idx;
d->actual_length = len;
/* even if there was an error, we did the dma
* for iso_frame_desc->length
*/
if (d->status != -EILSEQ && d->status != -EOVERFLOW)
d->status = 0;
if (++qh->iso_idx >= urb->number_of_packets) {
done = true;
} else {
/* REVISIT: Why ignore return value here? */
if (musb_dma_cppi41(hw_ep->musb))
done = musb_rx_dma_iso_cppi41(dma, hw_ep, qh,
urb, len);
done = false;
}
} else {
/* done if urb buffer is full or short packet is recd */
done = (urb->actual_length + len >=
urb->transfer_buffer_length
|| channel->actual_len < qh->maxpacket
|| channel->rx_packet_done);
}
/* send IN token for next packet, without AUTOREQ */
if (!done) {
val = musb_readw(epio, MUSB_RXCSR);
val |= MUSB_RXCSR_H_REQPKT;
musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | val);
}
return done;
}
/* Disadvantage of using mode 1:
* It's basically usable only for mass storage class; essentially all
* other protocols also terminate transfers on short packets.
*
* Details:
* An extra IN token is sent at the end of the transfer (due to AUTOREQ)
* If you try to use mode 1 for (transfer_buffer_length - 512), and try
* to use the extra IN token to grab the last packet using mode 0, then
* the problem is that you cannot be sure when the device will send the
* last packet and RxPktRdy set. Sometimes the packet is recd too soon
* such that it gets lost when RxCSR is re-set at the end of the mode 1
* transfer, while sometimes it is recd just a little late so that if you
* try to configure for mode 0 soon after the mode 1 transfer is
* completed, you will find rxcount 0. Okay, so you might think why not
* wait for an interrupt when the pkt is recd. Well, you won't get any!
*/
static int musb_rx_dma_in_inventra_cppi41(struct dma_controller *dma,
struct musb_hw_ep *hw_ep,
struct musb_qh *qh,
struct urb *urb,
size_t len,
u8 iso_err)
{
struct musb *musb = hw_ep->musb;
void __iomem *epio = hw_ep->regs;
struct dma_channel *channel = hw_ep->rx_channel;
u16 rx_count, val;
int length, pipe, done;
dma_addr_t buf;
rx_count = musb_readw(epio, MUSB_RXCOUNT);
pipe = urb->pipe;
if (usb_pipeisoc(pipe)) {
int d_status = 0;
struct usb_iso_packet_descriptor *d;
d = urb->iso_frame_desc + qh->iso_idx;
if (iso_err) {
d_status = -EILSEQ;
urb->error_count++;
}
if (rx_count > d->length) {
if (d_status == 0) {
d_status = -EOVERFLOW;
urb->error_count++;
}
dev_dbg(musb->controller, "** OVERFLOW %d into %d\n",
rx_count, d->length);
length = d->length;
} else
length = rx_count;
d->status = d_status;
buf = urb->transfer_dma + d->offset;
} else {
length = rx_count;
buf = urb->transfer_dma + urb->actual_length;
}
channel->desired_mode = 0;
#ifdef USE_MODE1
/* because of the issue below, mode 1 will
* only rarely behave with correct semantics.
*/
if ((urb->transfer_flags & URB_SHORT_NOT_OK)
&& (urb->transfer_buffer_length - urb->actual_length)
> qh->maxpacket)
channel->desired_mode = 1;
if (rx_count < hw_ep->max_packet_sz_rx) {
length = rx_count;
channel->desired_mode = 0;
} else {
length = urb->transfer_buffer_length;
}
#endif
/* See comments above on disadvantages of using mode 1 */
val = musb_readw(epio, MUSB_RXCSR);
val &= ~MUSB_RXCSR_H_REQPKT;
if (channel->desired_mode == 0)
val &= ~MUSB_RXCSR_H_AUTOREQ;
else
val |= MUSB_RXCSR_H_AUTOREQ;
val |= MUSB_RXCSR_DMAENAB;
/* autoclear shouldn't be set in high bandwidth */
if (qh->hb_mult == 1)
val |= MUSB_RXCSR_AUTOCLEAR;
musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | val);
/* REVISIT if when actual_length != 0,
* transfer_buffer_length needs to be
* adjusted first...
*/
done = dma->channel_program(channel, qh->maxpacket,
channel->desired_mode,
buf, length);
if (!done) {
dma->channel_release(channel);
hw_ep->rx_channel = NULL;
channel = NULL;
val = musb_readw(epio, MUSB_RXCSR);
val &= ~(MUSB_RXCSR_DMAENAB
| MUSB_RXCSR_H_AUTOREQ
| MUSB_RXCSR_AUTOCLEAR);
musb_writew(epio, MUSB_RXCSR, val);
}
return done;
}
#else
static inline int musb_rx_dma_inventra_cppi41(struct dma_controller *dma,
struct musb_hw_ep *hw_ep,
struct musb_qh *qh,
struct urb *urb,
size_t len)
{
return false;
}
static inline int musb_rx_dma_in_inventra_cppi41(struct dma_controller *dma,
struct musb_hw_ep *hw_ep,
struct musb_qh *qh,
struct urb *urb,
size_t len,
u8 iso_err)
{
return false;
}
#endif
/*
@ -1546,6 +1806,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
{
struct urb *urb;
struct musb_hw_ep *hw_ep = musb->endpoints + epnum;
struct dma_controller *c = musb->dma_controller;
void __iomem *epio = hw_ep->regs;
struct musb_qh *qh = hw_ep->in_qh;
size_t xfer_len;
@ -1661,9 +1922,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)
*/
/* FIXME this is _way_ too much in-line logic for Mentor DMA */
#if !defined(CONFIG_USB_INVENTRA_DMA) && !defined(CONFIG_USB_UX500_DMA)
if (rx_csr & MUSB_RXCSR_H_REQPKT) {
if (!musb_dma_inventra(musb) && !musb_dma_ux500(musb) &&
(rx_csr & MUSB_RXCSR_H_REQPKT)) {
/* REVISIT this happened for a while on some short reads...
* the cleanup still needs investigation... looks bad...
* and also duplicates dma cleanup code above ... plus,
@ -1684,7 +1944,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
musb_writew(epio, MUSB_RXCSR,
MUSB_RXCSR_H_WZC_BITS | rx_csr);
}
#endif
if (dma && (rx_csr & MUSB_RXCSR_DMAENAB)) {
xfer_len = dma->actual_len;
@ -1694,67 +1954,18 @@ void musb_host_rx(struct musb *musb, u8 epnum)
| MUSB_RXCSR_RXPKTRDY);
musb_writew(hw_ep->regs, MUSB_RXCSR, val);
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \
defined(CONFIG_USB_TI_CPPI41_DMA)
if (usb_pipeisoc(pipe)) {
struct usb_iso_packet_descriptor *d;
d = urb->iso_frame_desc + qh->iso_idx;
d->actual_length = xfer_len;
/* even if there was an error, we did the dma
* for iso_frame_desc->length
*/
if (d->status != -EILSEQ && d->status != -EOVERFLOW)
d->status = 0;
if (++qh->iso_idx >= urb->number_of_packets) {
done = true;
} else {
#if defined(CONFIG_USB_TI_CPPI41_DMA)
struct dma_controller *c;
dma_addr_t *buf;
u32 length, ret;
c = musb->dma_controller;
buf = (void *)
urb->iso_frame_desc[qh->iso_idx].offset
+ (u32)urb->transfer_dma;
length =
urb->iso_frame_desc[qh->iso_idx].length;
val |= MUSB_RXCSR_DMAENAB;
musb_writew(hw_ep->regs, MUSB_RXCSR, val);
ret = c->channel_program(dma, qh->maxpacket,
0, (u32) buf, length);
#endif
done = false;
}
} else {
/* done if urb buffer is full or short packet is recd */
done = (urb->actual_length + xfer_len >=
urb->transfer_buffer_length
|| dma->actual_len < qh->maxpacket
|| dma->rx_packet_done);
if (musb_dma_inventra(musb) || musb_dma_ux500(musb) ||
musb_dma_cppi41(musb)) {
done = musb_rx_dma_inventra_cppi41(c, hw_ep, qh, urb, xfer_len);
dev_dbg(hw_ep->musb->controller,
"ep %d dma %s, rxcsr %04x, rxcount %d\n",
epnum, done ? "off" : "reset",
musb_readw(epio, MUSB_RXCSR),
musb_readw(epio, MUSB_RXCOUNT));
} else {
done = true;
}
/* send IN token for next packet, without AUTOREQ */
if (!done) {
val |= MUSB_RXCSR_H_REQPKT;
musb_writew(epio, MUSB_RXCSR,
MUSB_RXCSR_H_WZC_BITS | val);
}
dev_dbg(musb->controller, "ep %d dma %s, rxcsr %04x, rxcount %d\n", epnum,
done ? "off" : "reset",
musb_readw(epio, MUSB_RXCSR),
musb_readw(epio, MUSB_RXCOUNT));
#else
done = true;
#endif
} else if (urb->status == -EINPROGRESS) {
/* if no errors, be sure a packet is ready for unloading */
if (unlikely(!(rx_csr & MUSB_RXCSR_RXPKTRDY))) {
@ -1772,126 +1983,24 @@ void musb_host_rx(struct musb *musb, u8 epnum)
}
/* we are expecting IN packets */
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \
defined(CONFIG_USB_TI_CPPI41_DMA)
if (dma) {
struct dma_controller *c;
u16 rx_count;
int ret, length;
dma_addr_t buf;
if ((musb_dma_inventra(musb) || musb_dma_ux500(musb) ||
musb_dma_cppi41(musb)) && dma) {
dev_dbg(hw_ep->musb->controller,
"RX%d count %d, buffer 0x%llx len %d/%d\n",
epnum, musb_readw(epio, MUSB_RXCOUNT),
(unsigned long long) urb->transfer_dma
+ urb->actual_length,
qh->offset,
urb->transfer_buffer_length);
rx_count = musb_readw(epio, MUSB_RXCOUNT);
dev_dbg(musb->controller, "RX%d count %d, buffer 0x%llx len %d/%d\n",
epnum, rx_count,
(unsigned long long) urb->transfer_dma
+ urb->actual_length,
qh->offset,
urb->transfer_buffer_length);
c = musb->dma_controller;
if (usb_pipeisoc(pipe)) {
int d_status = 0;
struct usb_iso_packet_descriptor *d;
d = urb->iso_frame_desc + qh->iso_idx;
if (iso_err) {
d_status = -EILSEQ;
urb->error_count++;
}
if (rx_count > d->length) {
if (d_status == 0) {
d_status = -EOVERFLOW;
urb->error_count++;
}
dev_dbg(musb->controller, "** OVERFLOW %d into %d\n",\
rx_count, d->length);
length = d->length;
} else
length = rx_count;
d->status = d_status;
buf = urb->transfer_dma + d->offset;
} else {
length = rx_count;
buf = urb->transfer_dma +
urb->actual_length;
}
dma->desired_mode = 0;
#ifdef USE_MODE1
/* because of the issue below, mode 1 will
* only rarely behave with correct semantics.
*/
if ((urb->transfer_flags &
URB_SHORT_NOT_OK)
&& (urb->transfer_buffer_length -
urb->actual_length)
> qh->maxpacket)
dma->desired_mode = 1;
if (rx_count < hw_ep->max_packet_sz_rx) {
length = rx_count;
dma->desired_mode = 0;
} else {
length = urb->transfer_buffer_length;
}
#endif
/* Disadvantage of using mode 1:
* It's basically usable only for mass storage class; essentially all
* other protocols also terminate transfers on short packets.
*
* Details:
* An extra IN token is sent at the end of the transfer (due to AUTOREQ)
* If you try to use mode 1 for (transfer_buffer_length - 512), and try
* to use the extra IN token to grab the last packet using mode 0, then
* the problem is that you cannot be sure when the device will send the
* last packet and RxPktRdy set. Sometimes the packet is recd too soon
* such that it gets lost when RxCSR is re-set at the end of the mode 1
* transfer, while sometimes it is recd just a little late so that if you
* try to configure for mode 0 soon after the mode 1 transfer is
* completed, you will find rxcount 0. Okay, so you might think why not
* wait for an interrupt when the pkt is recd. Well, you won't get any!
*/
val = musb_readw(epio, MUSB_RXCSR);
val &= ~MUSB_RXCSR_H_REQPKT;
if (dma->desired_mode == 0)
val &= ~MUSB_RXCSR_H_AUTOREQ;
done = musb_rx_dma_in_inventra_cppi41(c, hw_ep, qh,
urb, xfer_len,
iso_err);
if (done)
goto finish;
else
val |= MUSB_RXCSR_H_AUTOREQ;
val |= MUSB_RXCSR_DMAENAB;
/* autoclear shouldn't be set in high bandwidth */
if (qh->hb_mult == 1)
val |= MUSB_RXCSR_AUTOCLEAR;
musb_writew(epio, MUSB_RXCSR,
MUSB_RXCSR_H_WZC_BITS | val);
/* REVISIT if when actual_length != 0,
* transfer_buffer_length needs to be
* adjusted first...
*/
ret = c->channel_program(
dma, qh->maxpacket,
dma->desired_mode, buf, length);
if (!ret) {
c->channel_release(dma);
hw_ep->rx_channel = NULL;
dma = NULL;
val = musb_readw(epio, MUSB_RXCSR);
val &= ~(MUSB_RXCSR_DMAENAB
| MUSB_RXCSR_H_AUTOREQ
| MUSB_RXCSR_AUTOCLEAR);
musb_writew(epio, MUSB_RXCSR, val);
}
dev_err(musb->controller, "error: rx_dma failed\n");
}
#endif /* Mentor DMA */
if (!dma) {
unsigned int received_len;

View File

@ -47,6 +47,7 @@
* @fifo_offset: platform specific function to get fifo offset
* @read_fifo: platform specific function to read fifo
* @write_fifo: platform specific function to write fifo
* @busctl_offset: platform specific function to get busctl offset
*/
struct musb_io {
u32 quirks;
@ -55,6 +56,7 @@ struct musb_io {
u32 (*fifo_offset)(u8 epnum);
void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf);
void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
u32 (*busctl_offset)(u8 epnum, u16 offset);
};
/* Do not add new entries here, add them the struct musb_io instead */

View File

@ -300,9 +300,6 @@
#define MUSB_RXHUBADDR 0x06
#define MUSB_RXHUBPORT 0x07
#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \
(0x80 + (8*(_epnum)) + (_offset))
static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size)
{
musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
@ -364,78 +361,84 @@ static inline u16 musb_read_hwvers(void __iomem *mbase)
return musb_readw(mbase, MUSB_HWVERS);
}
static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase)
{
return (MUSB_BUSCTL_OFFSET(i, 0) + mbase);
}
static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs,
static inline void musb_write_rxfunaddr(struct musb *musb, u8 epnum,
u8 qh_addr_reg)
{
musb_writeb(ep_target_regs, MUSB_RXFUNCADDR, qh_addr_reg);
musb_writeb(musb->mregs,
musb->io.busctl_offset(epnum, MUSB_RXFUNCADDR),
qh_addr_reg);
}
static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs,
static inline void musb_write_rxhubaddr(struct musb *musb, u8 epnum,
u8 qh_h_addr_reg)
{
musb_writeb(ep_target_regs, MUSB_RXHUBADDR, qh_h_addr_reg);
musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_RXHUBADDR),
qh_h_addr_reg);
}
static inline void musb_write_rxhubport(void __iomem *ep_target_regs,
static inline void musb_write_rxhubport(struct musb *musb, u8 epnum,
u8 qh_h_port_reg)
{
musb_writeb(ep_target_regs, MUSB_RXHUBPORT, qh_h_port_reg);
}
static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum,
u8 qh_addr_reg)
{
musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR),
qh_addr_reg);
}
static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum,
u8 qh_addr_reg)
{
musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR),
qh_addr_reg);
}
static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum,
u8 qh_h_port_reg)
{
musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT),
musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_RXHUBPORT),
qh_h_port_reg);
}
static inline u8 musb_read_rxfunaddr(void __iomem *mbase, u8 epnum)
static inline void musb_write_txfunaddr(struct musb *musb, u8 epnum,
u8 qh_addr_reg)
{
return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXFUNCADDR));
musb_writeb(musb->mregs,
musb->io.busctl_offset(epnum, MUSB_TXFUNCADDR),
qh_addr_reg);
}
static inline u8 musb_read_rxhubaddr(void __iomem *mbase, u8 epnum)
static inline void musb_write_txhubaddr(struct musb *musb, u8 epnum,
u8 qh_addr_reg)
{
return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBADDR));
musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_TXHUBADDR),
qh_addr_reg);
}
static inline u8 musb_read_rxhubport(void __iomem *mbase, u8 epnum)
static inline void musb_write_txhubport(struct musb *musb, u8 epnum,
u8 qh_h_port_reg)
{
return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBPORT));
musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_TXHUBPORT),
qh_h_port_reg);
}
static inline u8 musb_read_txfunaddr(void __iomem *mbase, u8 epnum)
static inline u8 musb_read_rxfunaddr(struct musb *musb, u8 epnum)
{
return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR));
return musb_readb(musb->mregs,
musb->io.busctl_offset(epnum, MUSB_RXFUNCADDR));
}
static inline u8 musb_read_txhubaddr(void __iomem *mbase, u8 epnum)
static inline u8 musb_read_rxhubaddr(struct musb *musb, u8 epnum)
{
return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR));
return musb_readb(musb->mregs,
musb->io.busctl_offset(epnum, MUSB_RXHUBADDR));
}
static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum)
static inline u8 musb_read_rxhubport(struct musb *musb, u8 epnum)
{
return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT));
return musb_readb(musb->mregs,
musb->io.busctl_offset(epnum, MUSB_RXHUBPORT));
}
static inline u8 musb_read_txfunaddr(struct musb *musb, u8 epnum)
{
return musb_readb(musb->mregs,
musb->io.busctl_offset(epnum, MUSB_TXFUNCADDR));
}
static inline u8 musb_read_txhubaddr(struct musb *musb, u8 epnum)
{
return musb_readb(musb->mregs,
musb->io.busctl_offset(epnum, MUSB_TXHUBADDR));
}
static inline u8 musb_read_txhubport(struct musb *musb, u8 epnum)
{
return musb_readb(musb->mregs,
musb->io.busctl_offset(epnum, MUSB_TXHUBPORT));
}
#else /* CONFIG_BLACKFIN */
@ -556,22 +559,17 @@ static inline u16 musb_read_hwvers(void __iomem *mbase)
return MUSB_HWVERS_1900;
}
static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase)
{
return NULL;
}
static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs,
static inline void musb_write_rxfunaddr(void __iomem *mbase, u8 epnum,
u8 qh_addr_req)
{
}
static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs,
static inline void musb_write_rxhubaddr(void __iomem *mbase, u8 epnum,
u8 qh_h_addr_reg)
{
}
static inline void musb_write_rxhubport(void __iomem *ep_target_regs,
static inline void musb_write_rxhubport(void __iomem *mbase, u8 epnum,
u8 qh_h_port_reg)
{
}

View File

@ -195,8 +195,10 @@ void musb_port_reset(struct musb *musb, bool do_reset)
msecs_to_jiffies(50));
} else {
dev_dbg(musb->controller, "root port reset stopped\n");
musb_platform_pre_root_reset_end(musb);
musb_writeb(mbase, MUSB_POWER,
power & ~MUSB_POWER_RESET);
musb_platform_post_root_reset_end(musb);
power = musb_readb(mbase, MUSB_POWER);
if (power & MUSB_POWER_HSMODE) {

View File

@ -357,7 +357,7 @@ done:
return retval;
}
void dma_controller_destroy(struct dma_controller *c)
void musbhs_dma_controller_destroy(struct dma_controller *c)
{
struct musb_dma_controller *controller = container_of(c,
struct musb_dma_controller, controller);
@ -369,8 +369,10 @@ void dma_controller_destroy(struct dma_controller *c)
kfree(controller);
}
EXPORT_SYMBOL_GPL(musbhs_dma_controller_destroy);
struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *base)
struct dma_controller *musbhs_dma_controller_create(struct musb *musb,
void __iomem *base)
{
struct musb_dma_controller *controller;
struct device *dev = musb->controller;
@ -398,7 +400,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba
if (request_irq(irq, dma_controller_irq, 0,
dev_name(musb->controller), &controller->controller)) {
dev_err(dev, "request_irq %d failed!\n", irq);
dma_controller_destroy(&controller->controller);
musb_dma_controller_destroy(&controller->controller);
return NULL;
}
@ -407,3 +409,4 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba
return &controller->controller;
}
EXPORT_SYMBOL_GPL(musbhs_dma_controller_create);

View File

@ -493,6 +493,11 @@ static int omap2430_musb_exit(struct musb *musb)
}
static const struct musb_platform_ops omap2430_ops = {
.quirks = MUSB_DMA_INVENTRA,
#ifdef CONFIG_USB_INVENTRA_DMA
.dma_init = musbhs_dma_controller_create,
.dma_exit = musbhs_dma_controller_destroy,
#endif
.init = omap2430_musb_init,
.exit = omap2430_musb_exit,

View File

@ -890,7 +890,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci)
dev_dbg(musb->controller, "DMA IRQ %08x\n", dma_src);
real_dma_src = ~real_dma_src & dma_src;
if (tusb_dma_omap() && real_dma_src) {
if (tusb_dma_omap(musb) && real_dma_src) {
int tx_source = (real_dma_src & 0xffff);
int i;
@ -1181,7 +1181,7 @@ static int tusb_musb_exit(struct musb *musb)
}
static const struct musb_platform_ops tusb_ops = {
.quirks = MUSB_IN_TUSB,
.quirks = MUSB_DMA_TUSB_OMAP | MUSB_IN_TUSB,
.init = tusb_musb_init,
.exit = tusb_musb_exit,
@ -1192,6 +1192,10 @@ static const struct musb_platform_ops tusb_ops = {
.writeb = tusb_writeb,
.read_fifo = tusb_read_fifo,
.write_fifo = tusb_write_fifo,
#ifdef CONFIG_USB_TUSB_OMAP_DMA
.dma_init = tusb_dma_controller_create,
.dma_exit = tusb_dma_controller_destroy,
#endif
.enable = tusb_musb_enable,
.disable = tusb_musb_disable,

View File

@ -12,12 +12,6 @@
#ifndef __TUSB6010_H__
#define __TUSB6010_H__
#ifdef CONFIG_USB_TUSB_OMAP_DMA
#define tusb_dma_omap() 1
#else
#define tusb_dma_omap() 0
#endif
/* VLYNQ control register. 32-bit at offset 0x000 */
#define TUSB_VLYNQ_CTRL 0x004

View File

@ -625,7 +625,7 @@ static void tusb_omap_dma_release(struct dma_channel *channel)
channel = NULL;
}
void dma_controller_destroy(struct dma_controller *c)
void tusb_dma_controller_destroy(struct dma_controller *c)
{
struct tusb_omap_dma *tusb_dma;
int i;
@ -644,8 +644,10 @@ void dma_controller_destroy(struct dma_controller *c)
kfree(tusb_dma);
}
EXPORT_SYMBOL_GPL(tusb_dma_controller_destroy);
struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *base)
struct dma_controller *
tusb_dma_controller_create(struct musb *musb, void __iomem *base)
{
void __iomem *tbase = musb->ctrl_base;
struct tusb_omap_dma *tusb_dma;
@ -701,7 +703,8 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba
return &tusb_dma->controller;
cleanup:
dma_controller_destroy(&tusb_dma->controller);
musb_dma_controller_destroy(&tusb_dma->controller);
out:
return NULL;
}
EXPORT_SYMBOL_GPL(tusb_dma_controller_create);

View File

@ -188,7 +188,11 @@ static int ux500_musb_exit(struct musb *musb)
}
static const struct musb_platform_ops ux500_ops = {
.quirks = MUSB_INDEXED_EP,
.quirks = MUSB_DMA_UX500 | MUSB_INDEXED_EP,
#ifdef CONFIG_USB_UX500_DMA
.dma_init = ux500_dma_controller_create,
.dma_exit = ux500_dma_controller_destroy,
#endif
.init = ux500_musb_init,
.exit = ux500_musb_exit,
.fifo_mode = 5,
@ -338,7 +342,7 @@ static int ux500_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int ux500_suspend(struct device *dev)
{
struct ux500_glue *glue = dev_get_drvdata(dev);

View File

@ -359,7 +359,7 @@ static int ux500_dma_controller_start(struct ux500_dma_controller *controller)
return 0;
}
void dma_controller_destroy(struct dma_controller *c)
void ux500_dma_controller_destroy(struct dma_controller *c)
{
struct ux500_dma_controller *controller = container_of(c,
struct ux500_dma_controller, controller);
@ -367,9 +367,10 @@ void dma_controller_destroy(struct dma_controller *c)
ux500_dma_controller_stop(controller);
kfree(controller);
}
EXPORT_SYMBOL_GPL(ux500_dma_controller_destroy);
struct dma_controller *dma_controller_create(struct musb *musb,
void __iomem *base)
struct dma_controller *
ux500_dma_controller_create(struct musb *musb, void __iomem *base)
{
struct ux500_dma_controller *controller;
struct platform_device *pdev = to_platform_device(musb->controller);
@ -407,3 +408,4 @@ plat_get_fail:
kzalloc_fail:
return NULL;
}
EXPORT_SYMBOL_GPL(ux500_dma_controller_create);

View File

@ -91,7 +91,7 @@ config TWL6030_USB
config USB_GPIO_VBUS
tristate "GPIO based peripheral-only VBUS sensing 'transceiver'"
depends on GPIOLIB
depends on GPIOLIB || COMPILE_TEST
select USB_PHY
help
Provides simple GPIO VBUS sensing for controllers with an
@ -141,6 +141,7 @@ config USB_MSM_OTG
tristate "Qualcomm on-chip USB OTG controller support"
depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST)
depends on RESET_CONTROLLER
depends on EXTCON
select USB_PHY
help
Enable this to support the USB OTG transceiver on Qualcomm chips. It

View File

@ -240,8 +240,14 @@ static void ulpi_init(struct msm_otg *motg)
static int msm_phy_notify_disconnect(struct usb_phy *phy,
enum usb_device_speed speed)
{
struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
int val;
if (motg->manual_pullup) {
val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL;
usb_phy_io_write(phy, val, ULPI_CLR(ULPI_MISC_A));
}
/*
* Put the transceiver in non-driving mode. Otherwise host
* may not detect soft-disconnection.
@ -422,6 +428,24 @@ static int msm_phy_init(struct usb_phy *phy)
ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL);
}
if (motg->manual_pullup) {
val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT;
ulpi_write(phy, val, ULPI_SET(ULPI_MISC_A));
val = readl(USB_GENCONFIG_2);
val |= GENCONFIG_2_SESS_VLD_CTRL_EN;
writel(val, USB_GENCONFIG_2);
val = readl(USB_USBCMD);
val |= USBCMD_SESS_VLD_CTRL;
writel(val, USB_USBCMD);
val = ulpi_read(phy, ULPI_FUNC_CTRL);
val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
val |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
ulpi_write(phy, val, ULPI_FUNC_CTRL);
}
if (motg->phy_number)
writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2);
@ -1436,9 +1460,42 @@ static const struct of_device_id msm_otg_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, msm_otg_dt_match);
static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event,
void *ptr)
{
struct msm_usb_cable *vbus = container_of(nb, struct msm_usb_cable, nb);
struct msm_otg *motg = container_of(vbus, struct msm_otg, vbus);
if (event)
set_bit(B_SESS_VLD, &motg->inputs);
else
clear_bit(B_SESS_VLD, &motg->inputs);
schedule_work(&motg->sm_work);
return NOTIFY_DONE;
}
static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event,
void *ptr)
{
struct msm_usb_cable *id = container_of(nb, struct msm_usb_cable, nb);
struct msm_otg *motg = container_of(id, struct msm_otg, id);
if (event)
clear_bit(ID, &motg->inputs);
else
set_bit(ID, &motg->inputs);
schedule_work(&motg->sm_work);
return NOTIFY_DONE;
}
static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
{
struct msm_otg_platform_data *pdata;
struct extcon_dev *ext_id, *ext_vbus;
const struct of_device_id *id;
struct device_node *node = pdev->dev.of_node;
struct property *prop;
@ -1487,6 +1544,54 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX];
}
motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup");
ext_id = ERR_PTR(-ENODEV);
ext_vbus = ERR_PTR(-ENODEV);
if (of_property_read_bool(node, "extcon")) {
/* Each one of them is not mandatory */
ext_vbus = extcon_get_edev_by_phandle(&pdev->dev, 0);
if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV)
return PTR_ERR(ext_vbus);
ext_id = extcon_get_edev_by_phandle(&pdev->dev, 1);
if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV)
return PTR_ERR(ext_id);
}
if (!IS_ERR(ext_vbus)) {
motg->vbus.nb.notifier_call = msm_otg_vbus_notifier;
ret = extcon_register_interest(&motg->vbus.conn, ext_vbus->name,
"USB", &motg->vbus.nb);
if (ret < 0) {
dev_err(&pdev->dev, "register VBUS notifier failed\n");
return ret;
}
ret = extcon_get_cable_state(ext_vbus, "USB");
if (ret)
set_bit(B_SESS_VLD, &motg->inputs);
else
clear_bit(B_SESS_VLD, &motg->inputs);
}
if (!IS_ERR(ext_id)) {
motg->id.nb.notifier_call = msm_otg_id_notifier;
ret = extcon_register_interest(&motg->id.conn, ext_id->name,
"USB-HOST", &motg->id.nb);
if (ret < 0) {
dev_err(&pdev->dev, "register ID notifier failed\n");
return ret;
}
ret = extcon_get_cable_state(ext_id, "USB-HOST");
if (ret)
clear_bit(ID, &motg->inputs);
else
set_bit(ID, &motg->inputs);
}
prop = of_find_property(node, "qcom,phy-init-sequence", &len);
if (!prop || !len)
return 0;
@ -1700,6 +1805,11 @@ static int msm_otg_remove(struct platform_device *pdev)
if (phy->otg->host || phy->otg->gadget)
return -EBUSY;
if (motg->id.conn.edev)
extcon_unregister_interest(&motg->id.conn);
if (motg->vbus.conn.edev)
extcon_unregister_interest(&motg->vbus.conn);
msm_otg_debugfs_cleanup();
cancel_delayed_work_sync(&motg->chg_work);
cancel_work_sync(&motg->sm_work);

View File

@ -22,6 +22,11 @@ static LIST_HEAD(phy_list);
static LIST_HEAD(phy_bind_list);
static DEFINE_SPINLOCK(phy_lock);
struct phy_devm {
struct usb_phy *phy;
struct notifier_block *nb;
};
static struct usb_phy *__usb_find_phy(struct list_head *list,
enum usb_phy_type type)
{
@ -79,6 +84,15 @@ static void devm_usb_phy_release(struct device *dev, void *res)
usb_put_phy(phy);
}
static void devm_usb_phy_release2(struct device *dev, void *_res)
{
struct phy_devm *res = _res;
if (res->nb)
usb_unregister_notifier(res->phy, res->nb);
usb_put_phy(res->phy);
}
static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
{
struct usb_phy **phy = res;
@ -153,40 +167,30 @@ err0:
EXPORT_SYMBOL_GPL(usb_get_phy);
/**
* devm_usb_get_phy_by_phandle - find the USB PHY by phandle
* devm_usb_get_phy_by_node - find the USB PHY by device_node
* @dev - device that requests this phy
* @phandle - name of the property holding the phy phandle value
* @index - the index of the phy
* @node - the device_node for the phy device.
* @nb - a notifier_block to register with the phy.
*
* Returns the phy driver associated with the given phandle value,
* Returns the phy driver associated with the given device_node,
* after getting a refcount to it, -ENODEV if there is no such phy or
* -EPROBE_DEFER if there is a phandle to the phy, but the device is
* not yet loaded. While at that, it also associates the device with
* -EPROBE_DEFER if the device is not yet loaded. While at that, it
* also associates the device with
* the phy using devres. On driver detach, release function is invoked
* on the devres data, then, devres data is freed.
*
* For use by USB host and peripheral drivers.
* For use by peripheral drivers for devices related to a phy,
* such as a charger.
*/
struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
const char *phandle, u8 index)
struct usb_phy *devm_usb_get_phy_by_node(struct device *dev,
struct device_node *node,
struct notifier_block *nb)
{
struct usb_phy *phy = ERR_PTR(-ENOMEM), **ptr;
struct usb_phy *phy = ERR_PTR(-ENOMEM);
struct phy_devm *ptr;
unsigned long flags;
struct device_node *node;
if (!dev->of_node) {
dev_dbg(dev, "device does not have a device node entry\n");
return ERR_PTR(-EINVAL);
}
node = of_parse_phandle(dev->of_node, phandle, index);
if (!node) {
dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle,
dev->of_node->full_name);
return ERR_PTR(-ENODEV);
}
ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL);
ptr = devres_alloc(devm_usb_phy_release2, sizeof(*ptr), GFP_KERNEL);
if (!ptr) {
dev_dbg(dev, "failed to allocate memory for devres\n");
goto err0;
@ -205,8 +209,10 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
devres_free(ptr);
goto err1;
}
*ptr = phy;
if (nb)
usb_register_notifier(phy, nb);
ptr->phy = phy;
ptr->nb = nb;
devres_add(dev, ptr);
get_device(phy->dev);
@ -215,10 +221,47 @@ err1:
spin_unlock_irqrestore(&phy_lock, flags);
err0:
of_node_put(node);
return phy;
}
EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_node);
/**
* devm_usb_get_phy_by_phandle - find the USB PHY by phandle
* @dev - device that requests this phy
* @phandle - name of the property holding the phy phandle value
* @index - the index of the phy
*
* Returns the phy driver associated with the given phandle value,
* after getting a refcount to it, -ENODEV if there is no such phy or
* -EPROBE_DEFER if there is a phandle to the phy, but the device is
* not yet loaded. While at that, it also associates the device with
* the phy using devres. On driver detach, release function is invoked
* on the devres data, then, devres data is freed.
*
* For use by USB host and peripheral drivers.
*/
struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
const char *phandle, u8 index)
{
struct device_node *node;
struct usb_phy *phy;
if (!dev->of_node) {
dev_dbg(dev, "device does not have a device node entry\n");
return ERR_PTR(-EINVAL);
}
node = of_parse_phandle(dev->of_node, phandle, index);
if (!node) {
dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle,
dev->of_node->full_name);
return ERR_PTR(-ENODEV);
}
phy = devm_usb_get_phy_by_node(dev, node, NULL);
of_node_put(node);
return phy;
}
EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle);
/**

View File

@ -466,11 +466,15 @@ static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev)
static const struct of_device_id usbhs_of_match[] = {
{
.compatible = "renesas,usbhs-r8a7790",
.data = (void *)USBHS_TYPE_R8A7790,
.data = (void *)USBHS_TYPE_RCAR_GEN2,
},
{
.compatible = "renesas,usbhs-r8a7791",
.data = (void *)USBHS_TYPE_R8A7791,
.data = (void *)USBHS_TYPE_RCAR_GEN2,
},
{
.compatible = "renesas,usbhs-r8a7794",
.data = (void *)USBHS_TYPE_RCAR_GEN2,
},
{ },
};
@ -497,14 +501,8 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev)
if (gpio > 0)
dparam->enable_gpio = gpio;
switch (dparam->type) {
case USBHS_TYPE_R8A7790:
case USBHS_TYPE_R8A7791:
if (dparam->type == USBHS_TYPE_RCAR_GEN2)
dparam->has_usb_dmac = 1;
break;
default:
break;
}
return info;
}
@ -559,8 +557,7 @@ static int usbhs_probe(struct platform_device *pdev)
sizeof(struct renesas_usbhs_driver_param));
switch (priv->dparam.type) {
case USBHS_TYPE_R8A7790:
case USBHS_TYPE_R8A7791:
case USBHS_TYPE_RCAR_GEN2:
priv->pfunc = usbhs_rcar2_ops;
if (!priv->dparam.pipe_type) {
priv->dparam.pipe_type = usbhsc_new_pipe_type;

View File

@ -1239,15 +1239,21 @@ static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo,
{
char name[16];
snprintf(name, sizeof(name), "tx%d", channel);
fifo->tx_chan = dma_request_slave_channel_reason(dev, name);
if (IS_ERR(fifo->tx_chan))
fifo->tx_chan = NULL;
snprintf(name, sizeof(name), "rx%d", channel);
fifo->rx_chan = dma_request_slave_channel_reason(dev, name);
if (IS_ERR(fifo->rx_chan))
fifo->rx_chan = NULL;
/*
* To avoid complex handing for DnFIFOs, the driver uses each
* DnFIFO as TX or RX direction (not bi-direction).
* So, the driver uses odd channels for TX, even channels for RX.
*/
snprintf(name, sizeof(name), "ch%d", channel);
if (channel & 1) {
fifo->tx_chan = dma_request_slave_channel_reason(dev, name);
if (IS_ERR(fifo->tx_chan))
fifo->tx_chan = NULL;
} else {
fifo->rx_chan = dma_request_slave_channel_reason(dev, name);
if (IS_ERR(fifo->rx_chan))
fifo->rx_chan = NULL;
}
}
static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo,

View File

@ -44,10 +44,11 @@ struct usbhs_fifo_info {
struct usbhs_fifo dfifo[USBHS_MAX_NUM_DFIFO];
};
#define usbhsf_get_dnfifo(p, n) (&((p)->fifo_info.dfifo[n]))
#define usbhs_for_each_dfifo(priv, dfifo, i) \
for ((i) = 0, dfifo = usbhsf_get_dnfifo(priv, (i)); \
((i) < USBHS_MAX_NUM_DFIFO); \
(i)++, dfifo = usbhsf_get_dnfifo(priv, (i)))
#define usbhs_for_each_dfifo(priv, dfifo, i) \
for ((i) = 0; \
((i) < USBHS_MAX_NUM_DFIFO) && \
((dfifo) = usbhsf_get_dnfifo(priv, (i))); \
(i)++)
struct usbhs_pkt_handle;
struct usbhs_pkt {

View File

@ -218,10 +218,14 @@ static int usbhs_status_get_each_irq(struct usbhs_priv *priv,
/******************** spin lock ********************/
usbhs_lock(priv, flags);
state->intsts0 = usbhs_read(priv, INTSTS0);
state->intsts1 = usbhs_read(priv, INTSTS1);
intenb0 = usbhs_read(priv, INTENB0);
intenb1 = usbhs_read(priv, INTENB1);
if (usbhs_mod_is_host(priv)) {
state->intsts1 = usbhs_read(priv, INTSTS1);
intenb1 = usbhs_read(priv, INTENB1);
} else {
state->intsts1 = intenb1 = 0;
}
/* mask */
if (mod) {
@ -275,7 +279,8 @@ static irqreturn_t usbhs_interrupt(int irq, void *data)
* - Function :: VALID bit should 0
*/
usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC);
usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC);
if (usbhs_mod_is_host(priv))
usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC);
usbhs_write(priv, BRDYSTS, ~irq_state.brdysts);
usbhs_write(priv, NRDYSTS, ~irq_state.nrdysts);
@ -303,19 +308,20 @@ static irqreturn_t usbhs_interrupt(int irq, void *data)
if (irq_state.intsts0 & BRDY)
usbhs_mod_call(priv, irq_ready, priv, &irq_state);
/* INTSTS1 */
if (irq_state.intsts1 & ATTCH)
usbhs_mod_call(priv, irq_attch, priv, &irq_state);
if (usbhs_mod_is_host(priv)) {
/* INTSTS1 */
if (irq_state.intsts1 & ATTCH)
usbhs_mod_call(priv, irq_attch, priv, &irq_state);
if (irq_state.intsts1 & DTCH)
usbhs_mod_call(priv, irq_dtch, priv, &irq_state);
if (irq_state.intsts1 & DTCH)
usbhs_mod_call(priv, irq_dtch, priv, &irq_state);
if (irq_state.intsts1 & SIGN)
usbhs_mod_call(priv, irq_sign, priv, &irq_state);
if (irq_state.intsts1 & SACK)
usbhs_mod_call(priv, irq_sack, priv, &irq_state);
if (irq_state.intsts1 & SIGN)
usbhs_mod_call(priv, irq_sign, priv, &irq_state);
if (irq_state.intsts1 & SACK)
usbhs_mod_call(priv, irq_sack, priv, &irq_state);
}
return IRQ_HANDLED;
}
@ -334,7 +340,8 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
* - update INTSTS0
*/
usbhs_write(priv, INTENB0, 0);
usbhs_write(priv, INTENB1, 0);
if (usbhs_mod_is_host(priv))
usbhs_write(priv, INTENB1, 0);
usbhs_write(priv, BEMPENB, 0);
usbhs_write(priv, BRDYENB, 0);
@ -368,25 +375,27 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
intenb0 |= BRDYE;
}
/*
* INTSTS1
*/
if (mod->irq_attch)
intenb1 |= ATTCHE;
if (usbhs_mod_is_host(priv)) {
/*
* INTSTS1
*/
if (mod->irq_attch)
intenb1 |= ATTCHE;
if (mod->irq_dtch)
intenb1 |= DTCHE;
if (mod->irq_dtch)
intenb1 |= DTCHE;
if (mod->irq_sign)
intenb1 |= SIGNE;
if (mod->irq_sign)
intenb1 |= SIGNE;
if (mod->irq_sack)
intenb1 |= SACKE;
if (mod->irq_sack)
intenb1 |= SACKE;
}
}
if (intenb0)
usbhs_write(priv, INTENB0, intenb0);
if (intenb1)
if (usbhs_mod_is_host(priv) && intenb1)
usbhs_write(priv, INTENB1, intenb1);
}

View File

@ -629,4 +629,10 @@ struct mcb_device_id {
kernel_ulong_t driver_data;
};
struct ulpi_device_id {
__u16 vendor;
__u16 product;
kernel_ulong_t driver_data;
};
#endif /* LINUX_MOD_DEVICETABLE_H */

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2015 Hans de Goede <hdegoede@redhat.com>
*
* 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.
*/
#ifndef PHY_SUN4I_USB_H_
#define PHY_SUN4I_USB_H_
#include "phy.h"
/**
* sun4i_usb_phy_set_squelch_detect() - Enable/disable squelch detect
* @phy: reference to a sun4i usb phy
* @enabled: wether to enable or disable squelch detect
*/
void sun4i_usb_phy_set_squelch_detect(struct phy *phy, bool enabled);
#endif

View File

@ -0,0 +1,60 @@
#ifndef __LINUX_ULPI_DRIVER_H
#define __LINUX_ULPI_DRIVER_H
#include <linux/mod_devicetable.h>
#include <linux/device.h>
struct ulpi_ops;
/**
* struct ulpi - describes ULPI PHY device
* @id: vendor and product ids for ULPI device
* @ops: I/O access
* @dev: device interface
*/
struct ulpi {
struct ulpi_device_id id;
struct ulpi_ops *ops;
struct device dev;
};
#define to_ulpi_dev(d) container_of(d, struct ulpi, dev)
static inline void ulpi_set_drvdata(struct ulpi *ulpi, void *data)
{
dev_set_drvdata(&ulpi->dev, data);
}
static inline void *ulpi_get_drvdata(struct ulpi *ulpi)
{
return dev_get_drvdata(&ulpi->dev);
}
/**
* struct ulpi_driver - describes a ULPI PHY driver
* @id_table: array of device identifiers supported by this driver
* @probe: binds this driver to ULPI device
* @remove: unbinds this driver from ULPI device
* @driver: the name and owner members must be initialized by the drivers
*/
struct ulpi_driver {
const struct ulpi_device_id *id_table;
int (*probe)(struct ulpi *ulpi);
void (*remove)(struct ulpi *ulpi);
struct device_driver driver;
};
#define to_ulpi_driver(d) container_of(d, struct ulpi_driver, driver)
int ulpi_register_driver(struct ulpi_driver *drv);
void ulpi_unregister_driver(struct ulpi_driver *drv);
#define module_ulpi_driver(__ulpi_driver) \
module_driver(__ulpi_driver, ulpi_register_driver, \
ulpi_unregister_driver)
int ulpi_read(struct ulpi *ulpi, u8 addr);
int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val);
#endif /* __LINUX_ULPI_DRIVER_H */

View File

@ -0,0 +1,23 @@
#ifndef __LINUX_ULPI_INTERFACE_H
#define __LINUX_ULPI_INTERFACE_H
#include <linux/types.h>
struct ulpi;
/**
* struct ulpi_ops - ULPI register access
* @dev: the interface provider
* @read: read operation for ULPI register access
* @write: write operation for ULPI register access
*/
struct ulpi_ops {
struct device *dev;
int (*read)(struct ulpi_ops *ops, u8 addr);
int (*write)(struct ulpi_ops *ops, u8 addr, u8 val);
};
struct ulpi *ulpi_register_interface(struct device *, struct ulpi_ops *);
void ulpi_unregister_interface(struct ulpi *);
#endif /* __LINUX_ULPI_INTERFACE_H */

View File

@ -0,0 +1,130 @@
#ifndef __LINUX_ULPI_REGS_H
#define __LINUX_ULPI_REGS_H
/*
* Macros for Set and Clear
* See ULPI 1.1 specification to find the registers with Set and Clear offsets
*/
#define ULPI_SET(a) (a + 1)
#define ULPI_CLR(a) (a + 2)
/*
* Register Map
*/
#define ULPI_VENDOR_ID_LOW 0x00
#define ULPI_VENDOR_ID_HIGH 0x01
#define ULPI_PRODUCT_ID_LOW 0x02
#define ULPI_PRODUCT_ID_HIGH 0x03
#define ULPI_FUNC_CTRL 0x04
#define ULPI_IFC_CTRL 0x07
#define ULPI_OTG_CTRL 0x0a
#define ULPI_USB_INT_EN_RISE 0x0d
#define ULPI_USB_INT_EN_FALL 0x10
#define ULPI_USB_INT_STS 0x13
#define ULPI_USB_INT_LATCH 0x14
#define ULPI_DEBUG 0x15
#define ULPI_SCRATCH 0x16
/* Optional Carkit Registers */
#define ULPI_CARKIT_CTRL 0x19
#define ULPI_CARKIT_INT_DELAY 0x1c
#define ULPI_CARKIT_INT_EN 0x1d
#define ULPI_CARKIT_INT_STS 0x20
#define ULPI_CARKIT_INT_LATCH 0x21
#define ULPI_CARKIT_PLS_CTRL 0x22
/* Other Optional Registers */
#define ULPI_TX_POS_WIDTH 0x25
#define ULPI_TX_NEG_WIDTH 0x26
#define ULPI_POLARITY_RECOVERY 0x27
/* Access Extended Register Set */
#define ULPI_ACCESS_EXTENDED 0x2f
/* Vendor Specific */
#define ULPI_VENDOR_SPECIFIC 0x30
/* Extended Registers */
#define ULPI_EXT_VENDOR_SPECIFIC 0x80
/*
* Register Bits
*/
/* Function Control */
#define ULPI_FUNC_CTRL_XCVRSEL BIT(0)
#define ULPI_FUNC_CTRL_XCVRSEL_MASK 0x3
#define ULPI_FUNC_CTRL_HIGH_SPEED 0x0
#define ULPI_FUNC_CTRL_FULL_SPEED 0x1
#define ULPI_FUNC_CTRL_LOW_SPEED 0x2
#define ULPI_FUNC_CTRL_FS4LS 0x3
#define ULPI_FUNC_CTRL_TERMSELECT BIT(2)
#define ULPI_FUNC_CTRL_OPMODE BIT(3)
#define ULPI_FUNC_CTRL_OPMODE_MASK (0x3 << 3)
#define ULPI_FUNC_CTRL_OPMODE_NORMAL (0x0 << 3)
#define ULPI_FUNC_CTRL_OPMODE_NONDRIVING (0x1 << 3)
#define ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI (0x2 << 3)
#define ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP (0x3 << 3)
#define ULPI_FUNC_CTRL_RESET BIT(5)
#define ULPI_FUNC_CTRL_SUSPENDM BIT(6)
/* Interface Control */
#define ULPI_IFC_CTRL_6_PIN_SERIAL_MODE BIT(0)
#define ULPI_IFC_CTRL_3_PIN_SERIAL_MODE BIT(1)
#define ULPI_IFC_CTRL_CARKITMODE BIT(2)
#define ULPI_IFC_CTRL_CLOCKSUSPENDM BIT(3)
#define ULPI_IFC_CTRL_AUTORESUME BIT(4)
#define ULPI_IFC_CTRL_EXTERNAL_VBUS BIT(5)
#define ULPI_IFC_CTRL_PASSTHRU BIT(6)
#define ULPI_IFC_CTRL_PROTECT_IFC_DISABLE BIT(7)
/* OTG Control */
#define ULPI_OTG_CTRL_ID_PULLUP BIT(0)
#define ULPI_OTG_CTRL_DP_PULLDOWN BIT(1)
#define ULPI_OTG_CTRL_DM_PULLDOWN BIT(2)
#define ULPI_OTG_CTRL_DISCHRGVBUS BIT(3)
#define ULPI_OTG_CTRL_CHRGVBUS BIT(4)
#define ULPI_OTG_CTRL_DRVVBUS BIT(5)
#define ULPI_OTG_CTRL_DRVVBUS_EXT BIT(6)
#define ULPI_OTG_CTRL_EXTVBUSIND BIT(7)
/* USB Interrupt Enable Rising,
* USB Interrupt Enable Falling,
* USB Interrupt Status and
* USB Interrupt Latch
*/
#define ULPI_INT_HOST_DISCONNECT BIT(0)
#define ULPI_INT_VBUS_VALID BIT(1)
#define ULPI_INT_SESS_VALID BIT(2)
#define ULPI_INT_SESS_END BIT(3)
#define ULPI_INT_IDGRD BIT(4)
/* Debug */
#define ULPI_DEBUG_LINESTATE0 BIT(0)
#define ULPI_DEBUG_LINESTATE1 BIT(1)
/* Carkit Control */
#define ULPI_CARKIT_CTRL_CARKITPWR BIT(0)
#define ULPI_CARKIT_CTRL_IDGNDDRV BIT(1)
#define ULPI_CARKIT_CTRL_TXDEN BIT(2)
#define ULPI_CARKIT_CTRL_RXDEN BIT(3)
#define ULPI_CARKIT_CTRL_SPKLEFTEN BIT(4)
#define ULPI_CARKIT_CTRL_SPKRIGHTEN BIT(5)
#define ULPI_CARKIT_CTRL_MICEN BIT(6)
/* Carkit Interrupt Enable */
#define ULPI_CARKIT_INT_EN_IDFLOAT_RISE BIT(0)
#define ULPI_CARKIT_INT_EN_IDFLOAT_FALL BIT(1)
#define ULPI_CARKIT_INT_EN_CARINTDET BIT(2)
#define ULPI_CARKIT_INT_EN_DP_RISE BIT(3)
#define ULPI_CARKIT_INT_EN_DP_FALL BIT(4)
/* Carkit Interrupt Status and
* Carkit Interrupt Latch
*/
#define ULPI_CARKIT_INT_IDFLOAT BIT(0)
#define ULPI_CARKIT_INT_CARINTDET BIT(1)
#define ULPI_CARKIT_INT_DP BIT(2)
/* Carkit Pulse Control*/
#define ULPI_CARKIT_PLS_CTRL_TXPLSEN BIT(0)
#define ULPI_CARKIT_PLS_CTRL_RXPLSEN BIT(1)
#define ULPI_CARKIT_PLS_CTRL_SPKRLEFT_BIASEN BIT(2)
#define ULPI_CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN BIT(3)
#endif /* __LINUX_ULPI_REGS_H */

View File

@ -18,6 +18,7 @@
#ifndef __ASM_ARCH_MSM_HSUSB_H
#define __ASM_ARCH_MSM_HSUSB_H
#include <linux/extcon.h>
#include <linux/types.h>
#include <linux/usb/otg.h>
#include <linux/clk.h>
@ -119,6 +120,17 @@ struct msm_otg_platform_data {
void (*setup_gpio)(enum usb_otg_state state);
};
/**
* struct msm_usb_cable - structure for exteternal connector cable
* state tracking
* @nb: hold event notification callback
* @conn: used for notification registration
*/
struct msm_usb_cable {
struct notifier_block nb;
struct extcon_specific_cable_nb conn;
};
/**
* struct msm_otg: OTG driver data. Shared by HCD and DCD.
* @otg: USB OTG Transceiver structure.
@ -138,6 +150,11 @@ struct msm_otg_platform_data {
* @chg_type: The type of charger attached.
* @dcd_retires: The retry count used to track Data contact
* detection process.
* @manual_pullup: true if VBUS is not routed to USB controller/phy
* and controller driver therefore enables pull-up explicitly before
* starting controller using usbcmd run/stop bit.
* @vbus: VBUS signal state trakining, using extcon framework
* @id: ID signal state trakining, using extcon framework
*/
struct msm_otg {
struct usb_phy phy;
@ -166,6 +183,11 @@ struct msm_otg {
struct reset_control *phy_rst;
struct reset_control *link_rst;
int vdd_levels[3];
bool manual_pullup;
struct msm_usb_cable vbus;
struct msm_usb_cable id;
};
#endif

View File

@ -21,6 +21,8 @@
#define USB_AHBBURST (MSM_USB_BASE + 0x0090)
#define USB_AHBMODE (MSM_USB_BASE + 0x0098)
#define USB_GENCONFIG_2 (MSM_USB_BASE + 0x00a0)
#define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */
#define USB_USBCMD (MSM_USB_BASE + 0x0140)
@ -30,6 +32,9 @@
#define USB_PHY_CTRL (MSM_USB_BASE + 0x0240)
#define USB_PHY_CTRL2 (MSM_USB_BASE + 0x0278)
#define GENCONFIG_2_SESS_VLD_CTRL_EN BIT(7)
#define USBCMD_SESS_VLD_CTRL BIT(25)
#define USBCMD_RESET 2
#define USB_USBINTR (MSM_USB_BASE + 0x0148)
@ -50,6 +55,10 @@
#define ULPI_PWR_CLK_MNG_REG 0x88
#define OTG_COMP_DISABLE BIT(0)
#define ULPI_MISC_A 0x96
#define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1)
#define ULPI_MISC_A_VBUSVLDEXT BIT(0)
#define ASYNC_INTR_CTRL (1 << 29) /* Enable async interrupt */
#define ULPI_STP_CTRL (1 << 30) /* Block communication with PHY */
#define PHY_RETEN (1 << 1) /* PHY retention enable/disable */

View File

@ -168,6 +168,9 @@ struct net2280_regs {
#define ENDPOINT_B_INTERRUPT 2
#define ENDPOINT_A_INTERRUPT 1
#define ENDPOINT_0_INTERRUPT 0
#define USB3380_IRQSTAT0_EP_INTR_MASK_IN (0xF << 17)
#define USB3380_IRQSTAT0_EP_INTR_MASK_OUT (0xF << 1)
u32 irqstat1;
#define POWER_STATE_CHANGE_INTERRUPT 27
#define PCI_ARBITER_TIMEOUT_INTERRUPT 26

View File

@ -205,6 +205,8 @@ extern struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index);
extern struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index);
extern struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
const char *phandle, u8 index);
extern struct usb_phy *devm_usb_get_phy_by_node(struct device *dev,
struct device_node *node, struct notifier_block *nb);
extern void usb_put_phy(struct usb_phy *);
extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x);
extern int usb_bind_phy(const char *dev_name, u8 index,
@ -238,6 +240,12 @@ static inline struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
return ERR_PTR(-ENXIO);
}
static inline struct usb_phy *devm_usb_get_phy_by_node(struct device *dev,
struct device_node *node, struct notifier_block *nb)
{
return ERR_PTR(-ENXIO);
}
static inline void usb_put_phy(struct usb_phy *x)
{
}

View File

@ -169,8 +169,7 @@ struct renesas_usbhs_driver_param {
#define USBHS_USB_DMAC_XFER_SIZE 32 /* hardcode the xfer size */
};
#define USBHS_TYPE_R8A7790 1
#define USBHS_TYPE_R8A7791 2
#define USBHS_TYPE_RCAR_GEN2 1
/*
* option:

View File

@ -12,6 +12,8 @@
#define __LINUX_USB_ULPI_H
#include <linux/usb/otg.h>
#include <linux/ulpi/regs.h>
/*-------------------------------------------------------------------------*/
/*
@ -49,138 +51,6 @@
/*-------------------------------------------------------------------------*/
/*
* Macros for Set and Clear
* See ULPI 1.1 specification to find the registers with Set and Clear offsets
*/
#define ULPI_SET(a) (a + 1)
#define ULPI_CLR(a) (a + 2)
/*-------------------------------------------------------------------------*/
/*
* Register Map
*/
#define ULPI_VENDOR_ID_LOW 0x00
#define ULPI_VENDOR_ID_HIGH 0x01
#define ULPI_PRODUCT_ID_LOW 0x02
#define ULPI_PRODUCT_ID_HIGH 0x03
#define ULPI_FUNC_CTRL 0x04
#define ULPI_IFC_CTRL 0x07
#define ULPI_OTG_CTRL 0x0a
#define ULPI_USB_INT_EN_RISE 0x0d
#define ULPI_USB_INT_EN_FALL 0x10
#define ULPI_USB_INT_STS 0x13
#define ULPI_USB_INT_LATCH 0x14
#define ULPI_DEBUG 0x15
#define ULPI_SCRATCH 0x16
/* Optional Carkit Registers */
#define ULPI_CARCIT_CTRL 0x19
#define ULPI_CARCIT_INT_DELAY 0x1c
#define ULPI_CARCIT_INT_EN 0x1d
#define ULPI_CARCIT_INT_STS 0x20
#define ULPI_CARCIT_INT_LATCH 0x21
#define ULPI_CARCIT_PLS_CTRL 0x22
/* Other Optional Registers */
#define ULPI_TX_POS_WIDTH 0x25
#define ULPI_TX_NEG_WIDTH 0x26
#define ULPI_POLARITY_RECOVERY 0x27
/* Access Extended Register Set */
#define ULPI_ACCESS_EXTENDED 0x2f
/* Vendor Specific */
#define ULPI_VENDOR_SPECIFIC 0x30
/* Extended Registers */
#define ULPI_EXT_VENDOR_SPECIFIC 0x80
/*-------------------------------------------------------------------------*/
/*
* Register Bits
*/
/* Function Control */
#define ULPI_FUNC_CTRL_XCVRSEL (1 << 0)
#define ULPI_FUNC_CTRL_XCVRSEL_MASK (3 << 0)
#define ULPI_FUNC_CTRL_HIGH_SPEED (0 << 0)
#define ULPI_FUNC_CTRL_FULL_SPEED (1 << 0)
#define ULPI_FUNC_CTRL_LOW_SPEED (2 << 0)
#define ULPI_FUNC_CTRL_FS4LS (3 << 0)
#define ULPI_FUNC_CTRL_TERMSELECT (1 << 2)
#define ULPI_FUNC_CTRL_OPMODE (1 << 3)
#define ULPI_FUNC_CTRL_OPMODE_MASK (3 << 3)
#define ULPI_FUNC_CTRL_OPMODE_NORMAL (0 << 3)
#define ULPI_FUNC_CTRL_OPMODE_NONDRIVING (1 << 3)
#define ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI (2 << 3)
#define ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP (3 << 3)
#define ULPI_FUNC_CTRL_RESET (1 << 5)
#define ULPI_FUNC_CTRL_SUSPENDM (1 << 6)
/* Interface Control */
#define ULPI_IFC_CTRL_6_PIN_SERIAL_MODE (1 << 0)
#define ULPI_IFC_CTRL_3_PIN_SERIAL_MODE (1 << 1)
#define ULPI_IFC_CTRL_CARKITMODE (1 << 2)
#define ULPI_IFC_CTRL_CLOCKSUSPENDM (1 << 3)
#define ULPI_IFC_CTRL_AUTORESUME (1 << 4)
#define ULPI_IFC_CTRL_EXTERNAL_VBUS (1 << 5)
#define ULPI_IFC_CTRL_PASSTHRU (1 << 6)
#define ULPI_IFC_CTRL_PROTECT_IFC_DISABLE (1 << 7)
/* OTG Control */
#define ULPI_OTG_CTRL_ID_PULLUP (1 << 0)
#define ULPI_OTG_CTRL_DP_PULLDOWN (1 << 1)
#define ULPI_OTG_CTRL_DM_PULLDOWN (1 << 2)
#define ULPI_OTG_CTRL_DISCHRGVBUS (1 << 3)
#define ULPI_OTG_CTRL_CHRGVBUS (1 << 4)
#define ULPI_OTG_CTRL_DRVVBUS (1 << 5)
#define ULPI_OTG_CTRL_DRVVBUS_EXT (1 << 6)
#define ULPI_OTG_CTRL_EXTVBUSIND (1 << 7)
/* USB Interrupt Enable Rising,
* USB Interrupt Enable Falling,
* USB Interrupt Status and
* USB Interrupt Latch
*/
#define ULPI_INT_HOST_DISCONNECT (1 << 0)
#define ULPI_INT_VBUS_VALID (1 << 1)
#define ULPI_INT_SESS_VALID (1 << 2)
#define ULPI_INT_SESS_END (1 << 3)
#define ULPI_INT_IDGRD (1 << 4)
/* Debug */
#define ULPI_DEBUG_LINESTATE0 (1 << 0)
#define ULPI_DEBUG_LINESTATE1 (1 << 1)
/* Carkit Control */
#define ULPI_CARKIT_CTRL_CARKITPWR (1 << 0)
#define ULPI_CARKIT_CTRL_IDGNDDRV (1 << 1)
#define ULPI_CARKIT_CTRL_TXDEN (1 << 2)
#define ULPI_CARKIT_CTRL_RXDEN (1 << 3)
#define ULPI_CARKIT_CTRL_SPKLEFTEN (1 << 4)
#define ULPI_CARKIT_CTRL_SPKRIGHTEN (1 << 5)
#define ULPI_CARKIT_CTRL_MICEN (1 << 6)
/* Carkit Interrupt Enable */
#define ULPI_CARKIT_INT_EN_IDFLOAT_RISE (1 << 0)
#define ULPI_CARKIT_INT_EN_IDFLOAT_FALL (1 << 1)
#define ULPI_CARKIT_INT_EN_CARINTDET (1 << 2)
#define ULPI_CARKIT_INT_EN_DP_RISE (1 << 3)
#define ULPI_CARKIT_INT_EN_DP_FALL (1 << 4)
/* Carkit Interrupt Status and
* Carkit Interrupt Latch
*/
#define ULPI_CARKIT_INT_IDFLOAT (1 << 0)
#define ULPI_CARKIT_INT_CARINTDET (1 << 1)
#define ULPI_CARKIT_INT_DP (1 << 2)
/* Carkit Pulse Control*/
#define ULPI_CARKIT_PLS_CTRL_TXPLSEN (1 << 0)
#define ULPI_CARKIT_PLS_CTRL_RXPLSEN (1 << 1)
#define ULPI_CARKIT_PLS_CTRL_SPKRLEFT_BIASEN (1 << 2)
#define ULPI_CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN (1 << 3)
/*-------------------------------------------------------------------------*/
#if IS_ENABLED(CONFIG_USB_ULPI)
struct usb_phy *otg_ulpi_create(struct usb_phy_io_ops *ops,
unsigned int flags);

View File

@ -43,6 +43,10 @@
#define IN_ENDPOINT_TYPE 12
#define OUT_ENDPOINT_ENABLE 10
#define OUT_ENDPOINT_TYPE 8
#define USB3380_EP_CFG_MASK_IN ((0x3 << IN_ENDPOINT_TYPE) | \
BIT(IN_ENDPOINT_ENABLE))
#define USB3380_EP_CFG_MASK_OUT ((0x3 << OUT_ENDPOINT_TYPE) | \
BIT(OUT_ENDPOINT_ENABLE))
struct usb338x_usb_ext_regs {
u32 usbclass;

View File

@ -189,5 +189,9 @@ int main(void)
DEVID_FIELD(rio_device_id, asm_did);
DEVID_FIELD(rio_device_id, asm_vid);
DEVID(ulpi_device_id);
DEVID_FIELD(ulpi_device_id, vendor);
DEVID_FIELD(ulpi_device_id, product);
return 0;
}

View File

@ -1192,6 +1192,19 @@ static int do_rio_entry(const char *filename,
}
ADD_TO_DEVTABLE("rapidio", rio_device_id, do_rio_entry);
/* Looks like: ulpi:vNpN */
static int do_ulpi_entry(const char *filename, void *symval,
char *alias)
{
DEF_FIELD(symval, ulpi_device_id, vendor);
DEF_FIELD(symval, ulpi_device_id, product);
sprintf(alias, "ulpi:v%04xp%04x", vendor, product);
return 1;
}
ADD_TO_DEVTABLE("ulpi", ulpi_device_id, do_ulpi_entry);
/* Does namelen bytes of name exactly match the symbol? */
static bool sym_is(const char *name, unsigned namelen, const char *symbol)
{