forked from Minki/linux
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>
This commit is contained in:
commit
e152813ff5
@ -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"
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 >;
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
153
drivers/phy/phy-tusb1210.c
Normal file
153
drivers/phy/phy-tusb1210.c
Normal 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");
|
31
drivers/phy/ulpi_phy.h
Normal file
31
drivers/phy/ulpi_phy.h
Normal 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);
|
||||
}
|
@ -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);
|
||||
|
@ -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
|
||||
|
255
drivers/usb/common/ulpi.c
Normal file
255
drivers/usb/common/ulpi.c
Normal 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");
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
27
drivers/usb/dwc2/debug.h
Normal file
27
drivers/usb/dwc2/debug.h
Normal 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
|
771
drivers/usb/dwc2/debugfs.c
Normal file
771
drivers/usb/dwc2/debugfs.c
Normal 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;
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
/*
|
||||
|
@ -45,4 +45,6 @@ struct dwc3_platform_data {
|
||||
|
||||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
|
||||
const char *hsphy_interface;
|
||||
};
|
||||
|
91
drivers/usb/dwc3/ulpi.c
Normal file
91
drivers/usb/dwc3/ulpi.c
Normal 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;
|
||||
}
|
||||
}
|
@ -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 */
|
||||
|
@ -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->
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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__ */
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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__ */
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
26
include/linux/phy/phy-sun4i-usb.h
Normal file
26
include/linux/phy/phy-sun4i-usb.h
Normal 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
|
60
include/linux/ulpi/driver.h
Normal file
60
include/linux/ulpi/driver.h
Normal 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 */
|
23
include/linux/ulpi/interface.h
Normal file
23
include/linux/ulpi/interface.h
Normal 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 */
|
130
include/linux/ulpi/regs.h
Normal file
130
include/linux/ulpi/regs.h
Normal 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 */
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user