mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
USB driver patches for 3.16-rc1
Here is the big USB driver pull request for 3.16-rc1. Nothing huge here, but lots of little things in the USB core, and in lots of drivers. Hopefully the USB power management will be work better now that it has been reworked to do per-port power control dynamically. There's also a raft of gadget driver updates and fixes, CONFIG_USB_DEBUG is finally gone now that everything has been converted over to the dynamic debug inteface, the last hold-out drivers were cleaned up and the config option removed. There were also other minor things all through the drivers/usb/ tree, the shortlog shows this pretty well. All have been in linux-next, including the very last patch, which came from linux-next to fix a build issue on some platforms. Signed-off-by: Greg Kroah-Hartman <[email protected]> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iEYEABECAAYFAlONYEMACgkQMUfUDdst+ynxvgCggMQBhN5icth8Y5hFglNNaISN c4AAoMHR2kb62U1plylLbPnboQTjfcl0 =fG6y -----END PGP SIGNATURE----- Merge tag 'usb-3.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb into next Pull USB driver updates from Greg KH: "Here is the big USB driver pull request for 3.16-rc1. Nothing huge here, but lots of little things in the USB core, and in lots of drivers. Hopefully the USB power management will be work better now that it has been reworked to do per-port power control dynamically. There's also a raft of gadget driver updates and fixes, CONFIG_USB_DEBUG is finally gone now that everything has been converted over to the dynamic debug inteface, the last hold-out drivers were cleaned up and the config option removed. There were also other minor things all through the drivers/usb/ tree, the shortlog shows this pretty well. All have been in linux-next, including the very last patch, which came from linux-next to fix a build issue on some platforms" * tag 'usb-3.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (314 commits) usb: hub_handle_remote_wakeup() only exists for CONFIG_PM=y USB: orinoco_usb: remove CONFIG_USB_DEBUG support USB: media: lirc: igorplugusb: remove CONFIG_USB_DEBUG support USB: media: streamzap: remove CONFIG_USB_DEBUG USB: media: redrat3: remove CONFIG_USB_DEBUG usage USB: media: redrat3: remove unneeded tracing macro usb: qcserial: add additional Sierra Wireless QMI devices usb: host: max3421-hcd: Use module_spi_driver usb: host: max3421-hcd: Allow platform-data to specify Vbus polarity usb: host: max3421-hcd: fix "spi_rd8" uses dynamic stack allocation warning usb: host: max3421-hcd: Fix missing unlock in max3421_urb_enqueue() usb: qcserial: add Netgear AirCard 341U Documentation: dt-bindings: update xhci-platform DT binding for R-Car H2 and M2 usb: host: xhci-plat: add xhci_plat_start() usb: host: max3421-hcd: Fix potential NULL urb dereference Revert "usb: gadget: net2280: Add support for PLX USB338X" USB: usbip: remove CONFIG_USB_DEBUG reference USB: remove CONFIG_USB_DEBUG from defconfig files usb: resume child device when port is powered on usb: hub_handle_remote_wakeup() depends on CONFIG_PM_RUNTIME=y ...
This commit is contained in:
commit
e5c4ecdc55
291 changed files with 14364 additions and 6800 deletions
|
|
@ -28,6 +28,7 @@ obj-$(CONFIG_USB_IMX21_HCD) += host/
|
|||
obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/
|
||||
obj-$(CONFIG_USB_FUSBH200_HCD) += host/
|
||||
obj-$(CONFIG_USB_FOTG210_HCD) += host/
|
||||
obj-$(CONFIG_USB_MAX3421_HCD) += host/
|
||||
|
||||
obj-$(CONFIG_USB_C67X00_HCD) += c67x00/
|
||||
|
||||
|
|
@ -58,4 +59,4 @@ obj-$(CONFIG_USB_CHIPIDEA) += chipidea/
|
|||
obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/
|
||||
obj-$(CONFIG_USB_GADGET) += gadget/
|
||||
|
||||
obj-$(CONFIG_USB_COMMON) += usb-common.o
|
||||
obj-$(CONFIG_USB_COMMON) += common/
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ ci_hdrc-y := core.o otg.o
|
|||
ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
|
||||
ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
|
||||
ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o
|
||||
ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
|
||||
|
||||
# Glue/Bridge layers go here
|
||||
|
||||
|
|
|
|||
|
|
@ -44,9 +44,14 @@
|
|||
#define DEVICEADDR_USBADR (0x7FUL << 25)
|
||||
|
||||
/* PORTSC */
|
||||
#define PORTSC_CCS BIT(0)
|
||||
#define PORTSC_CSC BIT(1)
|
||||
#define PORTSC_PEC BIT(3)
|
||||
#define PORTSC_OCC BIT(5)
|
||||
#define PORTSC_FPR BIT(6)
|
||||
#define PORTSC_SUSP BIT(7)
|
||||
#define PORTSC_HSP BIT(9)
|
||||
#define PORTSC_PP BIT(12)
|
||||
#define PORTSC_PTC (0x0FUL << 16)
|
||||
#define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23))
|
||||
/* PTS and PTW for non lpm version only */
|
||||
|
|
@ -56,6 +61,9 @@
|
|||
#define PORTSC_PTW BIT(28)
|
||||
#define PORTSC_STS BIT(29)
|
||||
|
||||
#define PORTSC_W1C_BITS \
|
||||
(PORTSC_CSC | PORTSC_PEC | PORTSC_OCC)
|
||||
|
||||
/* DEVLC */
|
||||
#define DEVLC_PFSC BIT(23)
|
||||
#define DEVLC_PSPD (0x03UL << 25)
|
||||
|
|
@ -72,6 +80,8 @@
|
|||
|
||||
/* OTGSC */
|
||||
#define OTGSC_IDPU BIT(5)
|
||||
#define OTGSC_HADP BIT(6)
|
||||
#define OTGSC_HABA BIT(7)
|
||||
#define OTGSC_ID BIT(8)
|
||||
#define OTGSC_AVV BIT(9)
|
||||
#define OTGSC_ASV BIT(10)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/irqreturn.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg-fsm.h>
|
||||
|
||||
/******************************************************************************
|
||||
* DEFINE
|
||||
|
|
@ -139,6 +140,8 @@ struct hw_bank {
|
|||
* @roles: array of supported roles for this controller
|
||||
* @role: current role
|
||||
* @is_otg: if the device is otg-capable
|
||||
* @fsm: otg finite state machine
|
||||
* @fsm_timer: pointer to timer list of otg fsm
|
||||
* @work: work for role changing
|
||||
* @wq: workqueue thread
|
||||
* @qh_pool: allocation pool for queue heads
|
||||
|
|
@ -174,6 +177,8 @@ struct ci_hdrc {
|
|||
struct ci_role_driver *roles[CI_ROLE_END];
|
||||
enum ci_role role;
|
||||
bool is_otg;
|
||||
struct otg_fsm fsm;
|
||||
struct ci_otg_fsm_timer_list *fsm_timer;
|
||||
struct work_struct work;
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
|
|
@ -319,6 +324,24 @@ static inline u32 hw_test_and_write(struct ci_hdrc *ci, enum ci_hw_regs reg,
|
|||
return (val & mask) >> __ffs(mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* ci_otg_is_fsm_mode: runtime check if otg controller
|
||||
* is in otg fsm mode.
|
||||
*/
|
||||
static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci)
|
||||
{
|
||||
#ifdef CONFIG_USB_OTG_FSM
|
||||
return ci->is_otg && ci->roles[CI_ROLE_HOST] &&
|
||||
ci->roles[CI_ROLE_GADGET];
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
u32 hw_read_intr_enable(struct ci_hdrc *ci);
|
||||
|
||||
u32 hw_read_intr_status(struct ci_hdrc *ci);
|
||||
|
||||
int hw_device_reset(struct ci_hdrc *ci, u32 mode);
|
||||
|
||||
int hw_port_test_set(struct ci_hdrc *ci, u8 mode);
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
|
|||
|
||||
static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
|
||||
.name = "ci_hdrc_msm",
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
.flags = CI_HDRC_REGS_SHARED |
|
||||
CI_HDRC_REQUIRE_TRANSCEIVER |
|
||||
CI_HDRC_DISABLE_STREAMING,
|
||||
|
|
@ -57,9 +58,21 @@ static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
|
|||
static int ci_hdrc_msm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct platform_device *plat_ci;
|
||||
struct usb_phy *phy;
|
||||
|
||||
dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n");
|
||||
|
||||
/*
|
||||
* OTG(PHY) driver takes care of PHY initialization, clock management,
|
||||
* powering up VBUS, mapping of registers address space and power
|
||||
* management.
|
||||
*/
|
||||
phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
|
||||
if (IS_ERR(phy))
|
||||
return PTR_ERR(phy);
|
||||
|
||||
ci_hdrc_msm_platdata.phy = phy;
|
||||
|
||||
plat_ci = ci_hdrc_add_device(&pdev->dev,
|
||||
pdev->resource, pdev->num_resources,
|
||||
&ci_hdrc_msm_platdata);
|
||||
|
|
@ -86,10 +99,19 @@ static int ci_hdrc_msm_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id msm_ci_dt_match[] = {
|
||||
{ .compatible = "qcom,ci-hdrc", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, msm_ci_dt_match);
|
||||
|
||||
static struct platform_driver ci_hdrc_msm_driver = {
|
||||
.probe = ci_hdrc_msm_probe,
|
||||
.remove = ci_hdrc_msm_remove,
|
||||
.driver = { .name = "msm_hsusb", },
|
||||
.driver = {
|
||||
.name = "msm_hsusb",
|
||||
.of_match_table = msm_ci_dt_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ci_hdrc_msm_driver);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
* - BUS: bus glue code, bus abstraction layer
|
||||
*
|
||||
* Compile Options
|
||||
* - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities
|
||||
* - CONFIG_USB_CHIPIDEA_DEBUG: enable debug facilities
|
||||
* - STALL_IN: non-empty bulk-in pipes cannot be halted
|
||||
* if defined mass storage compliance succeeds but with warnings
|
||||
* => case 4: Hi > Dn
|
||||
|
|
@ -42,10 +42,6 @@
|
|||
* - Not Supported: 15 & 16 (ISO)
|
||||
*
|
||||
* TODO List
|
||||
* - OTG
|
||||
* - Interrupt Traffic
|
||||
* - GET_STATUS(device) - always reports 0
|
||||
* - Gadget API (majority of optional features)
|
||||
* - Suspend & Remote Wakeup
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
|
|
@ -74,6 +70,7 @@
|
|||
#include "host.h"
|
||||
#include "debug.h"
|
||||
#include "otg.h"
|
||||
#include "otg_fsm.h"
|
||||
|
||||
/* Controller register map */
|
||||
static const u8 ci_regs_nolpm[] = {
|
||||
|
|
@ -139,6 +136,26 @@ static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_read_intr_enable: returns interrupt enable register
|
||||
*
|
||||
* This function returns register data
|
||||
*/
|
||||
u32 hw_read_intr_enable(struct ci_hdrc *ci)
|
||||
{
|
||||
return hw_read(ci, OP_USBINTR, ~0);
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_read_intr_status: returns interrupt status register
|
||||
*
|
||||
* This function returns register data
|
||||
*/
|
||||
u32 hw_read_intr_status(struct ci_hdrc *ci)
|
||||
{
|
||||
return hw_read(ci, OP_USBSTS, ~0);
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_port_test_set: writes port test mode (execute without interruption)
|
||||
* @mode: new value
|
||||
|
|
@ -179,11 +196,10 @@ static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
|
|||
hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
|
||||
0);
|
||||
/*
|
||||
* The controller needs at least 1ms to reflect
|
||||
* PHY's status, the PHY also needs some time (less
|
||||
* the PHY needs some time (less
|
||||
* than 1ms) to leave low power mode.
|
||||
*/
|
||||
usleep_range(1500, 2000);
|
||||
usleep_range(1000, 1100);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -392,8 +408,14 @@ static irqreturn_t ci_irq(int irq, void *data)
|
|||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 otgsc = 0;
|
||||
|
||||
if (ci->is_otg)
|
||||
otgsc = hw_read(ci, OP_OTGSC, ~0);
|
||||
if (ci->is_otg) {
|
||||
otgsc = hw_read_otgsc(ci, ~0);
|
||||
if (ci_otg_is_fsm_mode(ci)) {
|
||||
ret = ci_otg_fsm_irq(ci);
|
||||
if (ret == IRQ_HANDLED)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle id change interrupt, it indicates device/host function
|
||||
|
|
@ -401,9 +423,9 @@ static irqreturn_t ci_irq(int irq, void *data)
|
|||
*/
|
||||
if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
|
||||
ci->id_event = true;
|
||||
ci_clear_otg_interrupt(ci, OTGSC_IDIS);
|
||||
disable_irq_nosync(ci->irq);
|
||||
queue_work(ci->wq, &ci->work);
|
||||
/* Clear ID change irq status */
|
||||
hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
|
||||
ci_otg_queue_work(ci);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
@ -413,9 +435,9 @@ static irqreturn_t ci_irq(int irq, void *data)
|
|||
*/
|
||||
if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
|
||||
ci->b_sess_valid_event = true;
|
||||
ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
|
||||
disable_irq_nosync(ci->irq);
|
||||
queue_work(ci->wq, &ci->work);
|
||||
/* Clear BSV irq */
|
||||
hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
|
||||
ci_otg_queue_work(ci);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
@ -533,11 +555,8 @@ static void ci_get_otg_capable(struct ci_hdrc *ci)
|
|||
ci->is_otg = (hw_read(ci, CAP_DCCPARAMS,
|
||||
DCCPARAMS_DC | DCCPARAMS_HC)
|
||||
== (DCCPARAMS_DC | DCCPARAMS_HC));
|
||||
if (ci->is_otg) {
|
||||
if (ci->is_otg)
|
||||
dev_dbg(ci->dev, "It is OTG capable controller\n");
|
||||
ci_disable_otg_interrupt(ci, OTGSC_INT_EN_BITS);
|
||||
ci_clear_otg_interrupt(ci, OTGSC_INT_STATUS_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
|
|
@ -599,6 +618,13 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
if (ret) {
|
||||
dev_err(dev, "unable to init phy: %d\n", ret);
|
||||
return ret;
|
||||
} else {
|
||||
/*
|
||||
* The delay to sync PHY's status, the maximum delay is
|
||||
* 2ms since the otgsc uses 1ms timer to debounce the
|
||||
* PHY's input
|
||||
*/
|
||||
usleep_range(2000, 2500);
|
||||
}
|
||||
|
||||
ci->hw_bank.phys = res->start;
|
||||
|
|
@ -633,6 +659,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
if (ci->is_otg) {
|
||||
/* Disable and clear all OTG irq */
|
||||
hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
|
||||
OTGSC_INT_STATUS_BITS);
|
||||
ret = ci_hdrc_otg_init(ci);
|
||||
if (ret) {
|
||||
dev_err(dev, "init otg fails, ret = %d\n", ret);
|
||||
|
|
@ -642,13 +671,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
|
||||
if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
|
||||
if (ci->is_otg) {
|
||||
/*
|
||||
* ID pin needs 1ms debouce time,
|
||||
* we delay 2ms for safe.
|
||||
*/
|
||||
mdelay(2);
|
||||
ci->role = ci_otg_role(ci);
|
||||
ci_enable_otg_interrupt(ci, OTGSC_IDIE);
|
||||
/* Enable ID change irq */
|
||||
hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE);
|
||||
} else {
|
||||
/*
|
||||
* If the controller is not OTG capable, but support
|
||||
|
|
@ -667,10 +692,13 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
if (ci->role == CI_ROLE_GADGET)
|
||||
ci_handle_vbus_change(ci);
|
||||
|
||||
ret = ci_role_start(ci, ci->role);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't start %s role\n", ci_role(ci)->name);
|
||||
goto stop;
|
||||
if (!ci_otg_is_fsm_mode(ci)) {
|
||||
ret = ci_role_start(ci, ci->role);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't start %s role\n",
|
||||
ci_role(ci)->name);
|
||||
goto stop;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ci);
|
||||
|
|
@ -679,6 +707,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto stop;
|
||||
|
||||
if (ci_otg_is_fsm_mode(ci))
|
||||
ci_hdrc_otg_fsm_start(ci);
|
||||
|
||||
ret = dbg_create_files(ci);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
|
@ -711,6 +742,7 @@ static struct platform_driver ci_hdrc_driver = {
|
|||
.remove = ci_hdrc_remove,
|
||||
.driver = {
|
||||
.name = "ci_hdrc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,11 +7,15 @@
|
|||
#include <linux/uaccess.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/otg-fsm.h>
|
||||
|
||||
#include "ci.h"
|
||||
#include "udc.h"
|
||||
#include "bits.h"
|
||||
#include "debug.h"
|
||||
#include "otg.h"
|
||||
|
||||
/**
|
||||
* ci_device_show: prints information about device capabilities and status
|
||||
|
|
@ -204,6 +208,80 @@ static const struct file_operations ci_requests_fops = {
|
|||
.release = single_release,
|
||||
};
|
||||
|
||||
int ci_otg_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct ci_hdrc *ci = s->private;
|
||||
struct otg_fsm *fsm;
|
||||
|
||||
if (!ci || !ci_otg_is_fsm_mode(ci))
|
||||
return 0;
|
||||
|
||||
fsm = &ci->fsm;
|
||||
|
||||
/* ------ State ----- */
|
||||
seq_printf(s, "OTG state: %s\n\n",
|
||||
usb_otg_state_string(ci->transceiver->state));
|
||||
|
||||
/* ------ State Machine Variables ----- */
|
||||
seq_printf(s, "a_bus_drop: %d\n", fsm->a_bus_drop);
|
||||
|
||||
seq_printf(s, "a_bus_req: %d\n", fsm->a_bus_req);
|
||||
|
||||
seq_printf(s, "a_srp_det: %d\n", fsm->a_srp_det);
|
||||
|
||||
seq_printf(s, "a_vbus_vld: %d\n", fsm->a_vbus_vld);
|
||||
|
||||
seq_printf(s, "b_conn: %d\n", fsm->b_conn);
|
||||
|
||||
seq_printf(s, "adp_change: %d\n", fsm->adp_change);
|
||||
|
||||
seq_printf(s, "power_up: %d\n", fsm->power_up);
|
||||
|
||||
seq_printf(s, "a_bus_resume: %d\n", fsm->a_bus_resume);
|
||||
|
||||
seq_printf(s, "a_bus_suspend: %d\n", fsm->a_bus_suspend);
|
||||
|
||||
seq_printf(s, "a_conn: %d\n", fsm->a_conn);
|
||||
|
||||
seq_printf(s, "b_bus_req: %d\n", fsm->b_bus_req);
|
||||
|
||||
seq_printf(s, "b_bus_suspend: %d\n", fsm->b_bus_suspend);
|
||||
|
||||
seq_printf(s, "b_se0_srp: %d\n", fsm->b_se0_srp);
|
||||
|
||||
seq_printf(s, "b_ssend_srp: %d\n", fsm->b_ssend_srp);
|
||||
|
||||
seq_printf(s, "b_sess_vld: %d\n", fsm->b_sess_vld);
|
||||
|
||||
seq_printf(s, "b_srp_done: %d\n", fsm->b_srp_done);
|
||||
|
||||
seq_printf(s, "drv_vbus: %d\n", fsm->drv_vbus);
|
||||
|
||||
seq_printf(s, "loc_conn: %d\n", fsm->loc_conn);
|
||||
|
||||
seq_printf(s, "loc_sof: %d\n", fsm->loc_sof);
|
||||
|
||||
seq_printf(s, "adp_prb: %d\n", fsm->adp_prb);
|
||||
|
||||
seq_printf(s, "id: %d\n", fsm->id);
|
||||
|
||||
seq_printf(s, "protocol: %d\n", fsm->protocol);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ci_otg_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ci_otg_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations ci_otg_fops = {
|
||||
.open = ci_otg_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int ci_role_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct ci_hdrc *ci = s->private;
|
||||
|
|
@ -253,6 +331,50 @@ static const struct file_operations ci_role_fops = {
|
|||
.release = single_release,
|
||||
};
|
||||
|
||||
int ci_registers_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct ci_hdrc *ci = s->private;
|
||||
u32 tmp_reg;
|
||||
|
||||
if (!ci)
|
||||
return 0;
|
||||
|
||||
/* ------ Registers ----- */
|
||||
tmp_reg = hw_read_intr_enable(ci);
|
||||
seq_printf(s, "USBINTR reg: %08x\n", tmp_reg);
|
||||
|
||||
tmp_reg = hw_read_intr_status(ci);
|
||||
seq_printf(s, "USBSTS reg: %08x\n", tmp_reg);
|
||||
|
||||
tmp_reg = hw_read(ci, OP_USBMODE, ~0);
|
||||
seq_printf(s, "USBMODE reg: %08x\n", tmp_reg);
|
||||
|
||||
tmp_reg = hw_read(ci, OP_USBCMD, ~0);
|
||||
seq_printf(s, "USBCMD reg: %08x\n", tmp_reg);
|
||||
|
||||
tmp_reg = hw_read(ci, OP_PORTSC, ~0);
|
||||
seq_printf(s, "PORTSC reg: %08x\n", tmp_reg);
|
||||
|
||||
if (ci->is_otg) {
|
||||
tmp_reg = hw_read_otgsc(ci, ~0);
|
||||
seq_printf(s, "OTGSC reg: %08x\n", tmp_reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ci_registers_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ci_registers_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations ci_registers_fops = {
|
||||
.open = ci_registers_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* dbg_create_files: initializes the attribute interface
|
||||
* @ci: device
|
||||
|
|
@ -287,8 +409,21 @@ int dbg_create_files(struct ci_hdrc *ci)
|
|||
if (!dent)
|
||||
goto err;
|
||||
|
||||
if (ci_otg_is_fsm_mode(ci)) {
|
||||
dent = debugfs_create_file("otg", S_IRUGO, ci->debugfs, ci,
|
||||
&ci_otg_fops);
|
||||
if (!dent)
|
||||
goto err;
|
||||
}
|
||||
|
||||
dent = debugfs_create_file("role", S_IRUGO | S_IWUSR, ci->debugfs, ci,
|
||||
&ci_role_fops);
|
||||
if (!dent)
|
||||
goto err;
|
||||
|
||||
dent = debugfs_create_file("registers", S_IRUGO, ci->debugfs, ci,
|
||||
&ci_registers_fops);
|
||||
|
||||
if (dent)
|
||||
return 0;
|
||||
err:
|
||||
|
|
|
|||
|
|
@ -67,7 +67,11 @@ static int host_start(struct ci_hdrc *ci)
|
|||
ehci->has_tdi_phy_lpm = ci->hw_bank.lpm;
|
||||
ehci->imx28_write_fix = ci->imx28_write_fix;
|
||||
|
||||
if (ci->platdata->reg_vbus) {
|
||||
/*
|
||||
* vbus is always on if host is not in OTG FSM mode,
|
||||
* otherwise should be controlled by OTG FSM
|
||||
*/
|
||||
if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
|
||||
ret = regulator_enable(ci->platdata->reg_vbus);
|
||||
if (ret) {
|
||||
dev_err(ci->dev,
|
||||
|
|
@ -78,10 +82,17 @@ static int host_start(struct ci_hdrc *ci)
|
|||
}
|
||||
|
||||
ret = usb_add_hcd(hcd, 0, 0);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
goto disable_reg;
|
||||
else
|
||||
} else {
|
||||
struct usb_otg *otg = ci->transceiver->otg;
|
||||
|
||||
ci->hcd = hcd;
|
||||
if (otg) {
|
||||
otg->host = &hcd->self;
|
||||
hcd->self.otg_port = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING)
|
||||
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
|
||||
|
|
@ -89,7 +100,7 @@ static int host_start(struct ci_hdrc *ci)
|
|||
return ret;
|
||||
|
||||
disable_reg:
|
||||
if (ci->platdata->reg_vbus)
|
||||
if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci))
|
||||
regulator_disable(ci->platdata->reg_vbus);
|
||||
|
||||
put_hcd:
|
||||
|
|
@ -105,7 +116,7 @@ static void host_stop(struct ci_hdrc *ci)
|
|||
if (hcd) {
|
||||
usb_remove_hcd(hcd);
|
||||
usb_put_hcd(hcd);
|
||||
if (ci->platdata->reg_vbus)
|
||||
if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci))
|
||||
regulator_disable(ci->platdata->reg_vbus);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
*/
|
||||
|
||||
/*
|
||||
* This file mainly handles otgsc register, it may include OTG operation
|
||||
* in the future.
|
||||
* This file mainly handles otgsc register, OTG fsm operations for HNP and SRP
|
||||
* are also included.
|
||||
*/
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
|
|
@ -22,6 +22,26 @@
|
|||
#include "ci.h"
|
||||
#include "bits.h"
|
||||
#include "otg.h"
|
||||
#include "otg_fsm.h"
|
||||
|
||||
/**
|
||||
* hw_read_otgsc returns otgsc register bits value.
|
||||
* @mask: bitfield mask
|
||||
*/
|
||||
u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
|
||||
{
|
||||
return hw_read(ci, OP_OTGSC, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_write_otgsc updates target bits of OTGSC register.
|
||||
* @mask: bitfield mask
|
||||
* @data: to be written
|
||||
*/
|
||||
void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
|
||||
{
|
||||
hw_write(ci, OP_OTGSC, mask | OTGSC_INT_STATUS_BITS, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* ci_otg_role - pick role based on ID pin state
|
||||
|
|
@ -29,8 +49,7 @@
|
|||
*/
|
||||
enum ci_role ci_otg_role(struct ci_hdrc *ci)
|
||||
{
|
||||
u32 sts = hw_read(ci, OP_OTGSC, ~0);
|
||||
enum ci_role role = sts & OTGSC_ID
|
||||
enum ci_role role = hw_read_otgsc(ci, OTGSC_ID)
|
||||
? CI_ROLE_GADGET
|
||||
: CI_ROLE_HOST;
|
||||
|
||||
|
|
@ -39,14 +58,10 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci)
|
|||
|
||||
void ci_handle_vbus_change(struct ci_hdrc *ci)
|
||||
{
|
||||
u32 otgsc;
|
||||
|
||||
if (!ci->is_otg)
|
||||
return;
|
||||
|
||||
otgsc = hw_read(ci, OP_OTGSC, ~0);
|
||||
|
||||
if (otgsc & OTGSC_BSV)
|
||||
if (hw_read_otgsc(ci, OTGSC_BSV))
|
||||
usb_gadget_vbus_connect(&ci->gadget);
|
||||
else
|
||||
usb_gadget_vbus_disconnect(&ci->gadget);
|
||||
|
|
@ -76,6 +91,11 @@ static void ci_otg_work(struct work_struct *work)
|
|||
{
|
||||
struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work);
|
||||
|
||||
if (ci_otg_is_fsm_mode(ci) && !ci_otg_fsm_work(ci)) {
|
||||
enable_irq(ci->irq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ci->id_event) {
|
||||
ci->id_event = false;
|
||||
ci_handle_id_switch(ci);
|
||||
|
|
@ -102,6 +122,9 @@ int ci_hdrc_otg_init(struct ci_hdrc *ci)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (ci_otg_is_fsm_mode(ci))
|
||||
return ci_hdrc_otg_fsm_init(ci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -115,6 +138,9 @@ void ci_hdrc_otg_destroy(struct ci_hdrc *ci)
|
|||
flush_workqueue(ci->wq);
|
||||
destroy_workqueue(ci->wq);
|
||||
}
|
||||
ci_disable_otg_interrupt(ci, OTGSC_INT_EN_BITS);
|
||||
ci_clear_otg_interrupt(ci, OTGSC_INT_STATUS_BITS);
|
||||
/* Disable all OTG irq and clear status */
|
||||
hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
|
||||
OTGSC_INT_STATUS_BITS);
|
||||
if (ci_otg_is_fsm_mode(ci))
|
||||
ci_hdrc_otg_fsm_remove(ci);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,25 +11,16 @@
|
|||
#ifndef __DRIVERS_USB_CHIPIDEA_OTG_H
|
||||
#define __DRIVERS_USB_CHIPIDEA_OTG_H
|
||||
|
||||
static inline void ci_clear_otg_interrupt(struct ci_hdrc *ci, u32 bits)
|
||||
{
|
||||
/* Only clear request bits */
|
||||
hw_write(ci, OP_OTGSC, OTGSC_INT_STATUS_BITS, bits);
|
||||
}
|
||||
|
||||
static inline void ci_enable_otg_interrupt(struct ci_hdrc *ci, u32 bits)
|
||||
{
|
||||
hw_write(ci, OP_OTGSC, bits | OTGSC_INT_STATUS_BITS, bits);
|
||||
}
|
||||
|
||||
static inline void ci_disable_otg_interrupt(struct ci_hdrc *ci, u32 bits)
|
||||
{
|
||||
hw_write(ci, OP_OTGSC, bits | OTGSC_INT_STATUS_BITS, 0);
|
||||
}
|
||||
|
||||
u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask);
|
||||
void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data);
|
||||
int ci_hdrc_otg_init(struct ci_hdrc *ci);
|
||||
void ci_hdrc_otg_destroy(struct ci_hdrc *ci);
|
||||
enum ci_role ci_otg_role(struct ci_hdrc *ci);
|
||||
void ci_handle_vbus_change(struct ci_hdrc *ci);
|
||||
static inline void ci_otg_queue_work(struct ci_hdrc *ci)
|
||||
{
|
||||
disable_irq_nosync(ci->irq);
|
||||
queue_work(ci->wq, &ci->work);
|
||||
}
|
||||
|
||||
#endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */
|
||||
|
|
|
|||
842
drivers/usb/chipidea/otg_fsm.c
Normal file
842
drivers/usb/chipidea/otg_fsm.c
Normal file
|
|
@ -0,0 +1,842 @@
|
|||
/*
|
||||
* otg_fsm.c - ChipIdea USB IP core OTG FSM driver
|
||||
*
|
||||
* Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* Author: Jun Li
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file mainly handles OTG fsm, it includes OTG fsm operations
|
||||
* for HNP and SRP.
|
||||
*
|
||||
* TODO List
|
||||
* - ADP
|
||||
* - OTG test device
|
||||
*/
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "ci.h"
|
||||
#include "bits.h"
|
||||
#include "otg.h"
|
||||
#include "otg_fsm.h"
|
||||
|
||||
static struct ci_otg_fsm_timer *otg_timer_initializer
|
||||
(struct ci_hdrc *ci, void (*function)(void *, unsigned long),
|
||||
unsigned long expires, unsigned long data)
|
||||
{
|
||||
struct ci_otg_fsm_timer *timer;
|
||||
|
||||
timer = devm_kzalloc(ci->dev, sizeof(struct ci_otg_fsm_timer),
|
||||
GFP_KERNEL);
|
||||
if (!timer)
|
||||
return NULL;
|
||||
timer->function = function;
|
||||
timer->expires = expires;
|
||||
timer->data = data;
|
||||
return timer;
|
||||
}
|
||||
|
||||
/* Add for otg: interact with user space app */
|
||||
static ssize_t
|
||||
get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
char *next;
|
||||
unsigned size, t;
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
||||
next = buf;
|
||||
size = PAGE_SIZE;
|
||||
t = scnprintf(next, size, "%d\n", ci->fsm.a_bus_req);
|
||||
size -= t;
|
||||
next += t;
|
||||
|
||||
return PAGE_SIZE - size;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
set_a_bus_req(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
||||
if (count > 2)
|
||||
return -1;
|
||||
|
||||
mutex_lock(&ci->fsm.lock);
|
||||
if (buf[0] == '0') {
|
||||
ci->fsm.a_bus_req = 0;
|
||||
} else if (buf[0] == '1') {
|
||||
/* If a_bus_drop is TRUE, a_bus_req can't be set */
|
||||
if (ci->fsm.a_bus_drop) {
|
||||
mutex_unlock(&ci->fsm.lock);
|
||||
return count;
|
||||
}
|
||||
ci->fsm.a_bus_req = 1;
|
||||
}
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
mutex_unlock(&ci->fsm.lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, set_a_bus_req);
|
||||
|
||||
static ssize_t
|
||||
get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
char *next;
|
||||
unsigned size, t;
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
||||
next = buf;
|
||||
size = PAGE_SIZE;
|
||||
t = scnprintf(next, size, "%d\n", ci->fsm.a_bus_drop);
|
||||
size -= t;
|
||||
next += t;
|
||||
|
||||
return PAGE_SIZE - size;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
set_a_bus_drop(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
||||
if (count > 2)
|
||||
return -1;
|
||||
|
||||
mutex_lock(&ci->fsm.lock);
|
||||
if (buf[0] == '0') {
|
||||
ci->fsm.a_bus_drop = 0;
|
||||
} else if (buf[0] == '1') {
|
||||
ci->fsm.a_bus_drop = 1;
|
||||
ci->fsm.a_bus_req = 0;
|
||||
}
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
mutex_unlock(&ci->fsm.lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, get_a_bus_drop,
|
||||
set_a_bus_drop);
|
||||
|
||||
static ssize_t
|
||||
get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
char *next;
|
||||
unsigned size, t;
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
||||
next = buf;
|
||||
size = PAGE_SIZE;
|
||||
t = scnprintf(next, size, "%d\n", ci->fsm.b_bus_req);
|
||||
size -= t;
|
||||
next += t;
|
||||
|
||||
return PAGE_SIZE - size;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
set_b_bus_req(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
||||
if (count > 2)
|
||||
return -1;
|
||||
|
||||
mutex_lock(&ci->fsm.lock);
|
||||
if (buf[0] == '0')
|
||||
ci->fsm.b_bus_req = 0;
|
||||
else if (buf[0] == '1')
|
||||
ci->fsm.b_bus_req = 1;
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
mutex_unlock(&ci->fsm.lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUSR, get_b_bus_req, set_b_bus_req);
|
||||
|
||||
static ssize_t
|
||||
set_a_clr_err(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
||||
if (count > 2)
|
||||
return -1;
|
||||
|
||||
mutex_lock(&ci->fsm.lock);
|
||||
if (buf[0] == '1')
|
||||
ci->fsm.a_clr_err = 1;
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
mutex_unlock(&ci->fsm.lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err);
|
||||
|
||||
static struct attribute *inputs_attrs[] = {
|
||||
&dev_attr_a_bus_req.attr,
|
||||
&dev_attr_a_bus_drop.attr,
|
||||
&dev_attr_b_bus_req.attr,
|
||||
&dev_attr_a_clr_err.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group inputs_attr_group = {
|
||||
.name = "inputs",
|
||||
.attrs = inputs_attrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* Add timer to active timer list
|
||||
*/
|
||||
static void ci_otg_add_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t)
|
||||
{
|
||||
struct ci_otg_fsm_timer *tmp_timer;
|
||||
struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t];
|
||||
struct list_head *active_timers = &ci->fsm_timer->active_timers;
|
||||
|
||||
if (t >= NUM_CI_OTG_FSM_TIMERS)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Check if the timer is already in the active list,
|
||||
* if so update timer count
|
||||
*/
|
||||
list_for_each_entry(tmp_timer, active_timers, list)
|
||||
if (tmp_timer == timer) {
|
||||
timer->count = timer->expires;
|
||||
return;
|
||||
}
|
||||
|
||||
timer->count = timer->expires;
|
||||
list_add_tail(&timer->list, active_timers);
|
||||
|
||||
/* Enable 1ms irq */
|
||||
if (!(hw_read_otgsc(ci, OTGSC_1MSIE)))
|
||||
hw_write_otgsc(ci, OTGSC_1MSIE, OTGSC_1MSIE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove timer from active timer list
|
||||
*/
|
||||
static void ci_otg_del_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t)
|
||||
{
|
||||
struct ci_otg_fsm_timer *tmp_timer, *del_tmp;
|
||||
struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t];
|
||||
struct list_head *active_timers = &ci->fsm_timer->active_timers;
|
||||
|
||||
if (t >= NUM_CI_OTG_FSM_TIMERS)
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list)
|
||||
if (tmp_timer == timer)
|
||||
list_del(&timer->list);
|
||||
|
||||
/* Disable 1ms irq if there is no any active timer */
|
||||
if (list_empty(active_timers))
|
||||
hw_write_otgsc(ci, OTGSC_1MSIE, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reduce timer count by 1, and find timeout conditions.
|
||||
* Called by otg 1ms timer interrupt
|
||||
*/
|
||||
static inline int ci_otg_tick_timer(struct ci_hdrc *ci)
|
||||
{
|
||||
struct ci_otg_fsm_timer *tmp_timer, *del_tmp;
|
||||
struct list_head *active_timers = &ci->fsm_timer->active_timers;
|
||||
int expired = 0;
|
||||
|
||||
list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) {
|
||||
tmp_timer->count--;
|
||||
/* check if timer expires */
|
||||
if (!tmp_timer->count) {
|
||||
list_del(&tmp_timer->list);
|
||||
tmp_timer->function(ci, tmp_timer->data);
|
||||
expired = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* disable 1ms irq if there is no any timer active */
|
||||
if ((expired == 1) && list_empty(active_timers))
|
||||
hw_write_otgsc(ci, OTGSC_1MSIE, 0);
|
||||
|
||||
return expired;
|
||||
}
|
||||
|
||||
/* The timeout callback function to set time out bit */
|
||||
static void set_tmout(void *ptr, unsigned long indicator)
|
||||
{
|
||||
*(int *)indicator = 1;
|
||||
}
|
||||
|
||||
static void set_tmout_and_fsm(void *ptr, unsigned long indicator)
|
||||
{
|
||||
struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
|
||||
|
||||
set_tmout(ci, indicator);
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
|
||||
static void a_wait_vfall_tmout_func(void *ptr, unsigned long indicator)
|
||||
{
|
||||
struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
|
||||
|
||||
set_tmout(ci, indicator);
|
||||
/* Disable port power */
|
||||
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, 0);
|
||||
/* Clear exsiting DP irq */
|
||||
hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
|
||||
/* Enable data pulse irq */
|
||||
hw_write_otgsc(ci, OTGSC_DPIE, OTGSC_DPIE);
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
|
||||
static void b_ase0_brst_tmout_func(void *ptr, unsigned long indicator)
|
||||
{
|
||||
struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
|
||||
|
||||
set_tmout(ci, indicator);
|
||||
if (!hw_read_otgsc(ci, OTGSC_BSV))
|
||||
ci->fsm.b_sess_vld = 0;
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
|
||||
static void b_ssend_srp_tmout_func(void *ptr, unsigned long indicator)
|
||||
{
|
||||
struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
|
||||
|
||||
set_tmout(ci, indicator);
|
||||
|
||||
/* only vbus fall below B_sess_vld in b_idle state */
|
||||
if (ci->transceiver->state == OTG_STATE_B_IDLE)
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
|
||||
static void b_sess_vld_tmout_func(void *ptr, unsigned long indicator)
|
||||
{
|
||||
struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
|
||||
|
||||
/* Check if A detached */
|
||||
if (!(hw_read_otgsc(ci, OTGSC_BSV))) {
|
||||
ci->fsm.b_sess_vld = 0;
|
||||
ci_otg_add_timer(ci, B_SSEND_SRP);
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
}
|
||||
|
||||
static void b_data_pulse_end(void *ptr, unsigned long indicator)
|
||||
{
|
||||
struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
|
||||
|
||||
ci->fsm.b_srp_done = 1;
|
||||
ci->fsm.b_bus_req = 0;
|
||||
if (ci->fsm.power_up)
|
||||
ci->fsm.power_up = 0;
|
||||
|
||||
hw_write_otgsc(ci, OTGSC_HABA, 0);
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
|
||||
/* Initialize timers */
|
||||
static int ci_otg_init_timers(struct ci_hdrc *ci)
|
||||
{
|
||||
struct otg_fsm *fsm = &ci->fsm;
|
||||
|
||||
/* FSM used timers */
|
||||
ci->fsm_timer->timer_list[A_WAIT_VRISE] =
|
||||
otg_timer_initializer(ci, &set_tmout_and_fsm, TA_WAIT_VRISE,
|
||||
(unsigned long)&fsm->a_wait_vrise_tmout);
|
||||
if (ci->fsm_timer->timer_list[A_WAIT_VRISE] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[A_WAIT_VFALL] =
|
||||
otg_timer_initializer(ci, &a_wait_vfall_tmout_func,
|
||||
TA_WAIT_VFALL, (unsigned long)&fsm->a_wait_vfall_tmout);
|
||||
if (ci->fsm_timer->timer_list[A_WAIT_VFALL] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[A_WAIT_BCON] =
|
||||
otg_timer_initializer(ci, &set_tmout_and_fsm, TA_WAIT_BCON,
|
||||
(unsigned long)&fsm->a_wait_bcon_tmout);
|
||||
if (ci->fsm_timer->timer_list[A_WAIT_BCON] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[A_AIDL_BDIS] =
|
||||
otg_timer_initializer(ci, &set_tmout_and_fsm, TA_AIDL_BDIS,
|
||||
(unsigned long)&fsm->a_aidl_bdis_tmout);
|
||||
if (ci->fsm_timer->timer_list[A_AIDL_BDIS] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[A_BIDL_ADIS] =
|
||||
otg_timer_initializer(ci, &set_tmout_and_fsm, TA_BIDL_ADIS,
|
||||
(unsigned long)&fsm->a_bidl_adis_tmout);
|
||||
if (ci->fsm_timer->timer_list[A_BIDL_ADIS] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[B_ASE0_BRST] =
|
||||
otg_timer_initializer(ci, &b_ase0_brst_tmout_func, TB_ASE0_BRST,
|
||||
(unsigned long)&fsm->b_ase0_brst_tmout);
|
||||
if (ci->fsm_timer->timer_list[B_ASE0_BRST] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[B_SE0_SRP] =
|
||||
otg_timer_initializer(ci, &set_tmout_and_fsm, TB_SE0_SRP,
|
||||
(unsigned long)&fsm->b_se0_srp);
|
||||
if (ci->fsm_timer->timer_list[B_SE0_SRP] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[B_SSEND_SRP] =
|
||||
otg_timer_initializer(ci, &b_ssend_srp_tmout_func, TB_SSEND_SRP,
|
||||
(unsigned long)&fsm->b_ssend_srp);
|
||||
if (ci->fsm_timer->timer_list[B_SSEND_SRP] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[B_SRP_FAIL] =
|
||||
otg_timer_initializer(ci, &set_tmout, TB_SRP_FAIL,
|
||||
(unsigned long)&fsm->b_srp_done);
|
||||
if (ci->fsm_timer->timer_list[B_SRP_FAIL] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[B_DATA_PLS] =
|
||||
otg_timer_initializer(ci, &b_data_pulse_end, TB_DATA_PLS, 0);
|
||||
if (ci->fsm_timer->timer_list[B_DATA_PLS] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[B_SESS_VLD] = otg_timer_initializer(ci,
|
||||
&b_sess_vld_tmout_func, TB_SESS_VLD, 0);
|
||||
if (ci->fsm_timer->timer_list[B_SESS_VLD] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------*/
|
||||
/* Operations that will be called from OTG Finite State Machine */
|
||||
/* -------------------------------------------------------------*/
|
||||
static void ci_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
|
||||
|
||||
if (t < NUM_OTG_FSM_TIMERS)
|
||||
ci_otg_add_timer(ci, t);
|
||||
return;
|
||||
}
|
||||
|
||||
static void ci_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
|
||||
|
||||
if (t < NUM_OTG_FSM_TIMERS)
|
||||
ci_otg_del_timer(ci, t);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* A-device drive vbus: turn on vbus regulator and enable port power
|
||||
* Data pulse irq should be disabled while vbus is on.
|
||||
*/
|
||||
static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on)
|
||||
{
|
||||
int ret;
|
||||
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
|
||||
|
||||
if (on) {
|
||||
/* Enable power power */
|
||||
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP,
|
||||
PORTSC_PP);
|
||||
if (ci->platdata->reg_vbus) {
|
||||
ret = regulator_enable(ci->platdata->reg_vbus);
|
||||
if (ret) {
|
||||
dev_err(ci->dev,
|
||||
"Failed to enable vbus regulator, ret=%d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Disable data pulse irq */
|
||||
hw_write_otgsc(ci, OTGSC_DPIE, 0);
|
||||
|
||||
fsm->a_srp_det = 0;
|
||||
fsm->power_up = 0;
|
||||
} else {
|
||||
if (ci->platdata->reg_vbus)
|
||||
regulator_disable(ci->platdata->reg_vbus);
|
||||
|
||||
fsm->a_bus_drop = 1;
|
||||
fsm->a_bus_req = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Control data line by Run Stop bit.
|
||||
*/
|
||||
static void ci_otg_loc_conn(struct otg_fsm *fsm, int on)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
|
||||
|
||||
if (on)
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
|
||||
else
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate SOF by host.
|
||||
* This is controlled through suspend/resume the port.
|
||||
* In host mode, controller will automatically send SOF.
|
||||
* Suspend will block the data on the port.
|
||||
*/
|
||||
static void ci_otg_loc_sof(struct otg_fsm *fsm, int on)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
|
||||
|
||||
if (on)
|
||||
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_FPR,
|
||||
PORTSC_FPR);
|
||||
else
|
||||
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_SUSP,
|
||||
PORTSC_SUSP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start SRP pulsing by data-line pulsing,
|
||||
* no v-bus pulsing followed
|
||||
*/
|
||||
static void ci_otg_start_pulse(struct otg_fsm *fsm)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
|
||||
|
||||
/* Hardware Assistant Data pulse */
|
||||
hw_write_otgsc(ci, OTGSC_HADP, OTGSC_HADP);
|
||||
|
||||
ci_otg_add_timer(ci, B_DATA_PLS);
|
||||
}
|
||||
|
||||
static int ci_otg_start_host(struct otg_fsm *fsm, int on)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
|
||||
|
||||
mutex_unlock(&fsm->lock);
|
||||
if (on) {
|
||||
ci_role_stop(ci);
|
||||
ci_role_start(ci, CI_ROLE_HOST);
|
||||
} else {
|
||||
ci_role_stop(ci);
|
||||
hw_device_reset(ci, USBMODE_CM_DC);
|
||||
ci_role_start(ci, CI_ROLE_GADGET);
|
||||
}
|
||||
mutex_lock(&fsm->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ci_otg_start_gadget(struct otg_fsm *fsm, int on)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
|
||||
|
||||
mutex_unlock(&fsm->lock);
|
||||
if (on)
|
||||
usb_gadget_vbus_connect(&ci->gadget);
|
||||
else
|
||||
usb_gadget_vbus_disconnect(&ci->gadget);
|
||||
mutex_lock(&fsm->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct otg_fsm_ops ci_otg_ops = {
|
||||
.drv_vbus = ci_otg_drv_vbus,
|
||||
.loc_conn = ci_otg_loc_conn,
|
||||
.loc_sof = ci_otg_loc_sof,
|
||||
.start_pulse = ci_otg_start_pulse,
|
||||
.add_timer = ci_otg_fsm_add_timer,
|
||||
.del_timer = ci_otg_fsm_del_timer,
|
||||
.start_host = ci_otg_start_host,
|
||||
.start_gadget = ci_otg_start_gadget,
|
||||
};
|
||||
|
||||
int ci_otg_fsm_work(struct ci_hdrc *ci)
|
||||
{
|
||||
/*
|
||||
* Don't do fsm transition for B device
|
||||
* when there is no gadget class driver
|
||||
*/
|
||||
if (ci->fsm.id && !(ci->driver) &&
|
||||
ci->transceiver->state < OTG_STATE_A_IDLE)
|
||||
return 0;
|
||||
|
||||
if (otg_statemachine(&ci->fsm)) {
|
||||
if (ci->transceiver->state == OTG_STATE_A_IDLE) {
|
||||
/*
|
||||
* Further state change for cases:
|
||||
* a_idle to b_idle; or
|
||||
* a_idle to a_wait_vrise due to ID change(1->0), so
|
||||
* B-dev becomes A-dev can try to start new session
|
||||
* consequently; or
|
||||
* a_idle to a_wait_vrise when power up
|
||||
*/
|
||||
if ((ci->fsm.id) || (ci->id_event) ||
|
||||
(ci->fsm.power_up))
|
||||
ci_otg_queue_work(ci);
|
||||
if (ci->id_event)
|
||||
ci->id_event = false;
|
||||
} else if (ci->transceiver->state == OTG_STATE_B_IDLE) {
|
||||
if (ci->fsm.b_sess_vld) {
|
||||
ci->fsm.power_up = 0;
|
||||
/*
|
||||
* Further transite to b_periphearl state
|
||||
* when register gadget driver with vbus on
|
||||
*/
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update fsm variables in each state if catching expected interrupts,
|
||||
* called by otg fsm isr.
|
||||
*/
|
||||
static void ci_otg_fsm_event(struct ci_hdrc *ci)
|
||||
{
|
||||
u32 intr_sts, otg_bsess_vld, port_conn;
|
||||
struct otg_fsm *fsm = &ci->fsm;
|
||||
|
||||
intr_sts = hw_read_intr_status(ci);
|
||||
otg_bsess_vld = hw_read_otgsc(ci, OTGSC_BSV);
|
||||
port_conn = hw_read(ci, OP_PORTSC, PORTSC_CCS);
|
||||
|
||||
switch (ci->transceiver->state) {
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
if (port_conn) {
|
||||
fsm->b_conn = 1;
|
||||
fsm->a_bus_req = 1;
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_B_IDLE:
|
||||
if (otg_bsess_vld && (intr_sts & USBi_PCI) && port_conn) {
|
||||
fsm->b_sess_vld = 1;
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
if ((intr_sts & USBi_SLI) && port_conn && otg_bsess_vld) {
|
||||
fsm->a_bus_suspend = 1;
|
||||
ci_otg_queue_work(ci);
|
||||
} else if (intr_sts & USBi_PCI) {
|
||||
if (fsm->a_bus_suspend == 1)
|
||||
fsm->a_bus_suspend = 0;
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_B_HOST:
|
||||
if ((intr_sts & USBi_PCI) && !port_conn) {
|
||||
fsm->a_conn = 0;
|
||||
fsm->b_bus_req = 0;
|
||||
ci_otg_queue_work(ci);
|
||||
ci_otg_add_timer(ci, B_SESS_VLD);
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
if (intr_sts & USBi_SLI) {
|
||||
fsm->b_bus_suspend = 1;
|
||||
/*
|
||||
* Init a timer to know how long this suspend
|
||||
* will contine, if time out, indicates B no longer
|
||||
* wants to be host role
|
||||
*/
|
||||
ci_otg_add_timer(ci, A_BIDL_ADIS);
|
||||
}
|
||||
|
||||
if (intr_sts & USBi_URI)
|
||||
ci_otg_del_timer(ci, A_BIDL_ADIS);
|
||||
|
||||
if (intr_sts & USBi_PCI) {
|
||||
if (fsm->b_bus_suspend == 1) {
|
||||
ci_otg_del_timer(ci, A_BIDL_ADIS);
|
||||
fsm->b_bus_suspend = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
if ((intr_sts & USBi_PCI) && !port_conn) {
|
||||
fsm->b_conn = 0;
|
||||
|
||||
/* if gadget driver is binded */
|
||||
if (ci->driver) {
|
||||
/* A device to be peripheral mode */
|
||||
ci->gadget.is_a_peripheral = 1;
|
||||
}
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_A_HOST:
|
||||
if ((intr_sts & USBi_PCI) && !port_conn) {
|
||||
fsm->b_conn = 0;
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
if ((intr_sts & USBi_PCI) && port_conn) {
|
||||
fsm->a_conn = 1;
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ci_otg_irq - otg fsm related irq handling
|
||||
* and also update otg fsm variable by monitoring usb host and udc
|
||||
* state change interrupts.
|
||||
* @ci: ci_hdrc
|
||||
*/
|
||||
irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
|
||||
{
|
||||
irqreturn_t retval = IRQ_NONE;
|
||||
u32 otgsc, otg_int_src = 0;
|
||||
struct otg_fsm *fsm = &ci->fsm;
|
||||
|
||||
otgsc = hw_read_otgsc(ci, ~0);
|
||||
otg_int_src = otgsc & OTGSC_INT_STATUS_BITS & (otgsc >> 8);
|
||||
fsm->id = (otgsc & OTGSC_ID) ? 1 : 0;
|
||||
|
||||
if (otg_int_src) {
|
||||
if (otg_int_src & OTGSC_1MSIS) {
|
||||
hw_write_otgsc(ci, OTGSC_1MSIS, OTGSC_1MSIS);
|
||||
retval = ci_otg_tick_timer(ci);
|
||||
return IRQ_HANDLED;
|
||||
} else if (otg_int_src & OTGSC_DPIS) {
|
||||
hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
|
||||
fsm->a_srp_det = 1;
|
||||
fsm->a_bus_drop = 0;
|
||||
} else if (otg_int_src & OTGSC_IDIS) {
|
||||
hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
|
||||
if (fsm->id == 0) {
|
||||
fsm->a_bus_drop = 0;
|
||||
fsm->a_bus_req = 1;
|
||||
ci->id_event = true;
|
||||
}
|
||||
} else if (otg_int_src & OTGSC_BSVIS) {
|
||||
hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
|
||||
if (otgsc & OTGSC_BSV) {
|
||||
fsm->b_sess_vld = 1;
|
||||
ci_otg_del_timer(ci, B_SSEND_SRP);
|
||||
ci_otg_del_timer(ci, B_SRP_FAIL);
|
||||
fsm->b_ssend_srp = 0;
|
||||
} else {
|
||||
fsm->b_sess_vld = 0;
|
||||
if (fsm->id)
|
||||
ci_otg_add_timer(ci, B_SSEND_SRP);
|
||||
}
|
||||
} else if (otg_int_src & OTGSC_AVVIS) {
|
||||
hw_write_otgsc(ci, OTGSC_AVVIS, OTGSC_AVVIS);
|
||||
if (otgsc & OTGSC_AVV) {
|
||||
fsm->a_vbus_vld = 1;
|
||||
} else {
|
||||
fsm->a_vbus_vld = 0;
|
||||
fsm->b_conn = 0;
|
||||
}
|
||||
}
|
||||
ci_otg_queue_work(ci);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
ci_otg_fsm_event(ci);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
|
||||
{
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
|
||||
int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
|
||||
{
|
||||
int retval = 0;
|
||||
struct usb_otg *otg;
|
||||
|
||||
otg = devm_kzalloc(ci->dev,
|
||||
sizeof(struct usb_otg), GFP_KERNEL);
|
||||
if (!otg) {
|
||||
dev_err(ci->dev,
|
||||
"Failed to allocate usb_otg structure for ci hdrc otg!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
otg->phy = ci->transceiver;
|
||||
otg->gadget = &ci->gadget;
|
||||
ci->fsm.otg = otg;
|
||||
ci->transceiver->otg = ci->fsm.otg;
|
||||
ci->fsm.power_up = 1;
|
||||
ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
|
||||
ci->transceiver->state = OTG_STATE_UNDEFINED;
|
||||
ci->fsm.ops = &ci_otg_ops;
|
||||
|
||||
mutex_init(&ci->fsm.lock);
|
||||
|
||||
ci->fsm_timer = devm_kzalloc(ci->dev,
|
||||
sizeof(struct ci_otg_fsm_timer_list), GFP_KERNEL);
|
||||
if (!ci->fsm_timer) {
|
||||
dev_err(ci->dev,
|
||||
"Failed to allocate timer structure for ci hdrc otg!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&ci->fsm_timer->active_timers);
|
||||
retval = ci_otg_init_timers(ci);
|
||||
if (retval) {
|
||||
dev_err(ci->dev, "Couldn't init OTG timers\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = sysfs_create_group(&ci->dev->kobj, &inputs_attr_group);
|
||||
if (retval < 0) {
|
||||
dev_dbg(ci->dev,
|
||||
"Can't register sysfs attr group: %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Enable A vbus valid irq */
|
||||
hw_write_otgsc(ci, OTGSC_AVVIE, OTGSC_AVVIE);
|
||||
|
||||
if (ci->fsm.id) {
|
||||
ci->fsm.b_ssend_srp =
|
||||
hw_read_otgsc(ci, OTGSC_BSV) ? 0 : 1;
|
||||
ci->fsm.b_sess_vld =
|
||||
hw_read_otgsc(ci, OTGSC_BSV) ? 1 : 0;
|
||||
/* Enable BSV irq */
|
||||
hw_write_otgsc(ci, OTGSC_BSVIE, OTGSC_BSVIE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci)
|
||||
{
|
||||
sysfs_remove_group(&ci->dev->kobj, &inputs_attr_group);
|
||||
}
|
||||
129
drivers/usb/chipidea/otg_fsm.h
Normal file
129
drivers/usb/chipidea/otg_fsm.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* Author: Jun Li
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_USB_CHIPIDEA_OTG_FSM_H
|
||||
#define __DRIVERS_USB_CHIPIDEA_OTG_FSM_H
|
||||
|
||||
#include <linux/usb/otg-fsm.h>
|
||||
|
||||
/*
|
||||
* A-DEVICE timing constants
|
||||
*/
|
||||
|
||||
/* Wait for VBUS Rise */
|
||||
#define TA_WAIT_VRISE (100) /* a_wait_vrise: section 7.1.2
|
||||
* a_wait_vrise_tmr: section 7.4.5.1
|
||||
* TA_VBUS_RISE <= 100ms, section 4.4
|
||||
* Table 4-1: Electrical Characteristics
|
||||
* ->DC Electrical Timing
|
||||
*/
|
||||
/* Wait for VBUS Fall */
|
||||
#define TA_WAIT_VFALL (1000) /* a_wait_vfall: section 7.1.7
|
||||
* a_wait_vfall_tmr: section: 7.4.5.2
|
||||
*/
|
||||
/* Wait for B-Connect */
|
||||
#define TA_WAIT_BCON (10000) /* a_wait_bcon: section 7.1.3
|
||||
* TA_WAIT_BCON: should be between 1100
|
||||
* and 30000 ms, section 5.5, Table 5-1
|
||||
*/
|
||||
/* A-Idle to B-Disconnect */
|
||||
#define TA_AIDL_BDIS (5000) /* a_suspend min 200 ms, section 5.2.1
|
||||
* TA_AIDL_BDIS: section 5.5, Table 5-1
|
||||
*/
|
||||
/* B-Idle to A-Disconnect */
|
||||
#define TA_BIDL_ADIS (500) /* TA_BIDL_ADIS: section 5.2.1
|
||||
* 500ms is used for B switch to host
|
||||
* for safe
|
||||
*/
|
||||
|
||||
/*
|
||||
* B-device timing constants
|
||||
*/
|
||||
|
||||
/* Data-Line Pulse Time*/
|
||||
#define TB_DATA_PLS (10) /* b_srp_init,continue 5~10ms
|
||||
* section:5.1.3
|
||||
*/
|
||||
/* SRP Fail Time */
|
||||
#define TB_SRP_FAIL (6000) /* b_srp_init,fail time 5~6s
|
||||
* section:5.1.6
|
||||
*/
|
||||
/* A-SE0 to B-Reset */
|
||||
#define TB_ASE0_BRST (155) /* minimum 155 ms, section:5.3.1 */
|
||||
/* SE0 Time Before SRP */
|
||||
#define TB_SE0_SRP (1000) /* b_idle,minimum 1s, section:5.1.2 */
|
||||
/* SSEND time before SRP */
|
||||
#define TB_SSEND_SRP (1500) /* minimum 1.5 sec, section:5.1.2 */
|
||||
|
||||
#define TB_SESS_VLD (1000)
|
||||
|
||||
enum ci_otg_fsm_timer_index {
|
||||
/*
|
||||
* CI specific timers, start from the end
|
||||
* of standard and auxiliary OTG timers
|
||||
*/
|
||||
B_DATA_PLS = NUM_OTG_FSM_TIMERS,
|
||||
B_SSEND_SRP,
|
||||
B_SESS_VLD,
|
||||
|
||||
NUM_CI_OTG_FSM_TIMERS,
|
||||
};
|
||||
|
||||
struct ci_otg_fsm_timer {
|
||||
unsigned long expires; /* Number of count increase to timeout */
|
||||
unsigned long count; /* Tick counter */
|
||||
void (*function)(void *, unsigned long); /* Timeout function */
|
||||
unsigned long data; /* Data passed to function */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct ci_otg_fsm_timer_list {
|
||||
struct ci_otg_fsm_timer *timer_list[NUM_CI_OTG_FSM_TIMERS];
|
||||
struct list_head active_timers;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_USB_OTG_FSM
|
||||
|
||||
int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci);
|
||||
int ci_otg_fsm_work(struct ci_hdrc *ci);
|
||||
irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci);
|
||||
void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci);
|
||||
void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci);
|
||||
|
||||
#else
|
||||
|
||||
static inline int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ci_otg_fsm_work(struct ci_hdrc *ci)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
|
||||
{
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static inline void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static inline void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __DRIVERS_USB_CHIPIDEA_OTG_FSM_H */
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/pm_runtime.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg-fsm.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
|
||||
#include "ci.h"
|
||||
|
|
@ -27,6 +28,7 @@
|
|||
#include "bits.h"
|
||||
#include "debug.h"
|
||||
#include "otg.h"
|
||||
#include "otg_fsm.h"
|
||||
|
||||
/* control endpoint description */
|
||||
static const struct usb_endpoint_descriptor
|
||||
|
|
@ -241,26 +243,6 @@ static int hw_port_is_high_speed(struct ci_hdrc *ci)
|
|||
hw_read(ci, OP_PORTSC, PORTSC_HSP);
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_read_intr_enable: returns interrupt enable register
|
||||
*
|
||||
* This function returns register data
|
||||
*/
|
||||
static u32 hw_read_intr_enable(struct ci_hdrc *ci)
|
||||
{
|
||||
return hw_read(ci, OP_USBINTR, ~0);
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_read_intr_status: returns interrupt status register
|
||||
*
|
||||
* This function returns register data
|
||||
*/
|
||||
static u32 hw_read_intr_status(struct ci_hdrc *ci)
|
||||
{
|
||||
return hw_read(ci, OP_USBSTS, ~0);
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_test_and_clear_complete: test & clear complete status (execute without
|
||||
* interruption)
|
||||
|
|
@ -727,6 +709,8 @@ __acquires(ci->lock)
|
|||
if (ci->status == NULL)
|
||||
retval = -ENOMEM;
|
||||
|
||||
usb_gadget_set_state(&ci->gadget, USB_STATE_DEFAULT);
|
||||
|
||||
done:
|
||||
spin_lock(&ci->lock);
|
||||
|
||||
|
|
@ -841,7 +825,6 @@ __acquires(hwep->lock)
|
|||
if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
|
||||
/* Assume that device is bus powered for now. */
|
||||
*(u16 *)req->buf = ci->remote_wakeup << 1;
|
||||
retval = 0;
|
||||
} else if ((setup->bRequestType & USB_RECIP_MASK) \
|
||||
== USB_RECIP_ENDPOINT) {
|
||||
dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ?
|
||||
|
|
@ -883,6 +866,8 @@ isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
if (ci->setaddr) {
|
||||
hw_usb_set_address(ci, ci->address);
|
||||
ci->setaddr = false;
|
||||
if (ci->address)
|
||||
usb_gadget_set_state(&ci->gadget, USB_STATE_ADDRESS);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
|
|
@ -1072,6 +1057,14 @@ __acquires(ci->lock)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_B_HNP_ENABLE:
|
||||
if (ci_otg_is_fsm_mode(ci)) {
|
||||
ci->gadget.b_hnp_enable = 1;
|
||||
err = isr_setup_status_phase(
|
||||
ci);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto delegate;
|
||||
}
|
||||
|
|
@ -1477,7 +1470,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
|
|||
pm_runtime_get_sync(&_gadget->dev);
|
||||
hw_device_reset(ci, USBMODE_CM_DC);
|
||||
hw_device_state(ci, ci->ep0out->qh.dma);
|
||||
dev_dbg(ci->dev, "Connected to host\n");
|
||||
usb_gadget_set_state(_gadget, USB_STATE_POWERED);
|
||||
} else {
|
||||
if (ci->driver)
|
||||
ci->driver->disconnect(&ci->gadget);
|
||||
|
|
@ -1487,7 +1480,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
|
|||
CI_HDRC_CONTROLLER_STOPPED_EVENT);
|
||||
_gadget_stop_activity(&ci->gadget);
|
||||
pm_runtime_put_sync(&_gadget->dev);
|
||||
dev_dbg(ci->dev, "Disconnected from host\n");
|
||||
usb_gadget_set_state(_gadget, USB_STATE_NOTATTACHED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1655,6 +1648,13 @@ static int ci_udc_start(struct usb_gadget *gadget,
|
|||
return retval;
|
||||
|
||||
ci->driver = driver;
|
||||
|
||||
/* Start otg fsm for B-device */
|
||||
if (ci_otg_is_fsm_mode(ci) && ci->fsm.id) {
|
||||
ci_hdrc_otg_fsm_start(ci);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(&ci->gadget.dev);
|
||||
if (ci->vbus_active) {
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
|
|
@ -1753,6 +1753,8 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci)
|
|||
ci->suspended = 1;
|
||||
spin_unlock(&ci->lock);
|
||||
ci->driver->suspend(&ci->gadget);
|
||||
usb_gadget_set_state(&ci->gadget,
|
||||
USB_STATE_SUSPENDED);
|
||||
spin_lock(&ci->lock);
|
||||
}
|
||||
}
|
||||
|
|
@ -1779,7 +1781,7 @@ static int udc_start(struct ci_hdrc *ci)
|
|||
ci->gadget.ops = &usb_gadget_ops;
|
||||
ci->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
ci->gadget.max_speed = USB_SPEED_HIGH;
|
||||
ci->gadget.is_otg = 0;
|
||||
ci->gadget.is_otg = ci->is_otg ? 1 : 0;
|
||||
ci->gadget.name = ci->platdata->name;
|
||||
|
||||
INIT_LIST_HEAD(&ci->gadget.ep_list);
|
||||
|
|
@ -1843,21 +1845,22 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
|
|||
|
||||
static int udc_id_switch_for_device(struct ci_hdrc *ci)
|
||||
{
|
||||
if (ci->is_otg) {
|
||||
ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
|
||||
ci_enable_otg_interrupt(ci, OTGSC_BSVIE);
|
||||
}
|
||||
if (ci->is_otg)
|
||||
/* Clear and enable BSV irq */
|
||||
hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE,
|
||||
OTGSC_BSVIS | OTGSC_BSVIE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void udc_id_switch_for_host(struct ci_hdrc *ci)
|
||||
{
|
||||
if (ci->is_otg) {
|
||||
/* host doesn't care B_SESSION_VALID event */
|
||||
ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
|
||||
ci_disable_otg_interrupt(ci, OTGSC_BSVIE);
|
||||
}
|
||||
/*
|
||||
* host doesn't care B_SESSION_VALID event
|
||||
* so clear and disbale BSV irq
|
||||
*/
|
||||
if (ci->is_otg)
|
||||
hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,16 +21,39 @@
|
|||
#define MX25_USB_PHY_CTRL_OFFSET 0x08
|
||||
#define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23)
|
||||
|
||||
#define MX25_EHCI_INTERFACE_SINGLE_UNI (2 << 0)
|
||||
#define MX25_EHCI_INTERFACE_DIFF_UNI (0 << 0)
|
||||
#define MX25_EHCI_INTERFACE_MASK (0xf)
|
||||
|
||||
#define MX25_OTG_SIC_SHIFT 29
|
||||
#define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT)
|
||||
#define MX25_OTG_PM_BIT BIT(24)
|
||||
#define MX25_OTG_PP_BIT BIT(11)
|
||||
#define MX25_OTG_OCPOL_BIT BIT(3)
|
||||
|
||||
#define MX25_H1_SIC_SHIFT 21
|
||||
#define MX25_H1_SIC_MASK (0x3 << MX25_H1_SIC_SHIFT)
|
||||
#define MX25_H1_PP_BIT BIT(18)
|
||||
#define MX25_H1_PM_BIT BIT(16)
|
||||
#define MX25_H1_IPPUE_UP_BIT BIT(7)
|
||||
#define MX25_H1_IPPUE_DOWN_BIT BIT(6)
|
||||
#define MX25_H1_TLL_BIT BIT(5)
|
||||
#define MX25_H1_USBTE_BIT BIT(4)
|
||||
#define MX25_H1_OCPOL_BIT BIT(2)
|
||||
|
||||
#define MX27_H1_PM_BIT BIT(8)
|
||||
#define MX27_H2_PM_BIT BIT(16)
|
||||
#define MX27_OTG_PM_BIT BIT(24)
|
||||
|
||||
#define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08
|
||||
#define MX53_USB_OTG_PHY_CTRL_1_OFFSET 0x0c
|
||||
#define MX53_USB_UH2_CTRL_OFFSET 0x14
|
||||
#define MX53_USB_UH3_CTRL_OFFSET 0x18
|
||||
#define MX53_BM_OVER_CUR_DIS_H1 BIT(5)
|
||||
#define MX53_BM_OVER_CUR_DIS_OTG BIT(8)
|
||||
#define MX53_BM_OVER_CUR_DIS_UHx BIT(30)
|
||||
#define MX53_USB_PHYCTRL1_PLLDIV_MASK 0x3
|
||||
#define MX53_USB_PLL_DIV_24_MHZ 0x01
|
||||
|
||||
#define MX6_BM_OVER_CUR_DIS BIT(7)
|
||||
|
||||
|
|
@ -50,6 +73,39 @@ struct imx_usbmisc {
|
|||
|
||||
static struct imx_usbmisc *usbmisc;
|
||||
|
||||
static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 val = 0;
|
||||
|
||||
if (data->index > 1)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
switch (data->index) {
|
||||
case 0:
|
||||
val = readl(usbmisc->base);
|
||||
val &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PP_BIT);
|
||||
val |= (MX25_EHCI_INTERFACE_DIFF_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT;
|
||||
val |= (MX25_OTG_PM_BIT | MX25_OTG_OCPOL_BIT);
|
||||
writel(val, usbmisc->base);
|
||||
break;
|
||||
case 1:
|
||||
val = readl(usbmisc->base);
|
||||
val &= ~(MX25_H1_SIC_MASK | MX25_H1_PP_BIT | MX25_H1_IPPUE_UP_BIT);
|
||||
val |= (MX25_EHCI_INTERFACE_SINGLE_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT;
|
||||
val |= (MX25_H1_PM_BIT | MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT |
|
||||
MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT);
|
||||
|
||||
writel(val, usbmisc->base);
|
||||
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbmisc_imx25_post(struct imx_usbmisc_data *data)
|
||||
{
|
||||
void __iomem *reg;
|
||||
|
|
@ -111,6 +167,13 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
|
|||
if (data->index > 3)
|
||||
return -EINVAL;
|
||||
|
||||
/* Select a 24 MHz reference clock for the PHY */
|
||||
reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET;
|
||||
val = readl(reg);
|
||||
val &= ~MX53_USB_PHYCTRL1_PLLDIV_MASK;
|
||||
val |= MX53_USB_PLL_DIV_24_MHZ;
|
||||
writel(val, usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
|
||||
|
||||
if (data->disable_oc) {
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
switch (data->index) {
|
||||
|
|
@ -159,6 +222,7 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
|
|||
}
|
||||
|
||||
static const struct usbmisc_ops imx25_usbmisc_ops = {
|
||||
.init = usbmisc_imx25_init,
|
||||
.post = usbmisc_imx25_post,
|
||||
};
|
||||
|
||||
|
|
@ -199,6 +263,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
|
|||
.compatible = "fsl,imx25-usbmisc",
|
||||
.data = &imx25_usbmisc_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,imx35-usbmisc",
|
||||
.data = &imx25_usbmisc_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,imx27-usbmisc",
|
||||
.data = &imx27_usbmisc_ops,
|
||||
|
|
|
|||
|
|
@ -122,13 +122,23 @@ static void acm_release_minor(struct acm *acm)
|
|||
static int acm_ctrl_msg(struct acm *acm, int request, int value,
|
||||
void *buf, int len)
|
||||
{
|
||||
int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
|
||||
int retval;
|
||||
|
||||
retval = usb_autopm_get_interface(acm->control);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
|
||||
request, USB_RT_ACM, value,
|
||||
acm->control->altsetting[0].desc.bInterfaceNumber,
|
||||
buf, len, 5000);
|
||||
|
||||
dev_dbg(&acm->control->dev,
|
||||
"%s - rq 0x%02x, val %#x, len %#x, result %d\n",
|
||||
__func__, request, value, len, retval);
|
||||
|
||||
usb_autopm_put_interface(acm->control);
|
||||
|
||||
return retval < 0 ? retval : 0;
|
||||
}
|
||||
|
||||
|
|
@ -406,19 +416,21 @@ static void acm_read_bulk_callback(struct urb *urb)
|
|||
dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
|
||||
return;
|
||||
}
|
||||
usb_mark_last_busy(acm->dev);
|
||||
|
||||
if (urb->status) {
|
||||
dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
|
||||
__func__, urb->status);
|
||||
return;
|
||||
}
|
||||
|
||||
usb_mark_last_busy(acm->dev);
|
||||
|
||||
acm_process_read_urb(acm, urb);
|
||||
|
||||
/* throttle device if requested by tty */
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
acm->throttled = acm->throttle_req;
|
||||
if (!acm->throttled && !acm->susp_count) {
|
||||
if (!acm->throttled) {
|
||||
spin_unlock_irqrestore(&acm->read_lock, flags);
|
||||
acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
|
||||
} else {
|
||||
|
|
@ -492,10 +504,30 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
|
|||
return tty_port_open(&acm->port, tty, filp);
|
||||
}
|
||||
|
||||
static void acm_port_dtr_rts(struct tty_port *port, int raise)
|
||||
{
|
||||
struct acm *acm = container_of(port, struct acm, port);
|
||||
int val;
|
||||
int res;
|
||||
|
||||
if (raise)
|
||||
val = ACM_CTRL_DTR | ACM_CTRL_RTS;
|
||||
else
|
||||
val = 0;
|
||||
|
||||
/* FIXME: add missing ctrlout locking throughout driver */
|
||||
acm->ctrlout = val;
|
||||
|
||||
res = acm_set_control(acm, val);
|
||||
if (res && (acm->ctrl_caps & USB_CDC_CAP_LINE))
|
||||
dev_err(&acm->control->dev, "failed to set dtr/rts\n");
|
||||
}
|
||||
|
||||
static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = container_of(port, struct acm, port);
|
||||
int retval = -ENODEV;
|
||||
int i;
|
||||
|
||||
dev_dbg(&acm->control->dev, "%s\n", __func__);
|
||||
|
||||
|
|
@ -515,22 +547,13 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
|
|||
acm->control->needs_remote_wakeup = 1;
|
||||
|
||||
acm->ctrlurb->dev = acm->dev;
|
||||
if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
|
||||
retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL);
|
||||
if (retval) {
|
||||
dev_err(&acm->control->dev,
|
||||
"%s - usb_submit_urb(ctrl irq) failed\n", __func__);
|
||||
usb_autopm_put_interface(acm->control);
|
||||
goto error_submit_urb;
|
||||
}
|
||||
|
||||
acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS;
|
||||
if (acm_set_control(acm, acm->ctrlout) < 0 &&
|
||||
(acm->ctrl_caps & USB_CDC_CAP_LINE)) {
|
||||
usb_autopm_put_interface(acm->control);
|
||||
goto error_set_control;
|
||||
}
|
||||
|
||||
usb_autopm_put_interface(acm->control);
|
||||
|
||||
/*
|
||||
* Unthrottle device in case the TTY was closed while throttled.
|
||||
*/
|
||||
|
|
@ -539,23 +562,27 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
|
|||
acm->throttle_req = 0;
|
||||
spin_unlock_irq(&acm->read_lock);
|
||||
|
||||
if (acm_submit_read_urbs(acm, GFP_KERNEL))
|
||||
retval = acm_submit_read_urbs(acm, GFP_KERNEL);
|
||||
if (retval)
|
||||
goto error_submit_read_urbs;
|
||||
|
||||
usb_autopm_put_interface(acm->control);
|
||||
|
||||
mutex_unlock(&acm->mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
error_submit_read_urbs:
|
||||
acm->ctrlout = 0;
|
||||
acm_set_control(acm, acm->ctrlout);
|
||||
error_set_control:
|
||||
for (i = 0; i < acm->rx_buflimit; i++)
|
||||
usb_kill_urb(acm->read_urbs[i]);
|
||||
usb_kill_urb(acm->ctrlurb);
|
||||
error_submit_urb:
|
||||
usb_autopm_put_interface(acm->control);
|
||||
error_get_interface:
|
||||
disconnected:
|
||||
mutex_unlock(&acm->mutex);
|
||||
return retval;
|
||||
|
||||
return usb_translate_errors(retval);
|
||||
}
|
||||
|
||||
static void acm_port_destruct(struct tty_port *port)
|
||||
|
|
@ -573,23 +600,37 @@ static void acm_port_destruct(struct tty_port *port)
|
|||
static void acm_port_shutdown(struct tty_port *port)
|
||||
{
|
||||
struct acm *acm = container_of(port, struct acm, port);
|
||||
struct urb *urb;
|
||||
struct acm_wb *wb;
|
||||
int i;
|
||||
|
||||
dev_dbg(&acm->control->dev, "%s\n", __func__);
|
||||
|
||||
mutex_lock(&acm->mutex);
|
||||
if (!acm->disconnected) {
|
||||
usb_autopm_get_interface(acm->control);
|
||||
acm_set_control(acm, acm->ctrlout = 0);
|
||||
usb_kill_urb(acm->ctrlurb);
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
usb_kill_urb(acm->wb[i].urb);
|
||||
for (i = 0; i < acm->rx_buflimit; i++)
|
||||
usb_kill_urb(acm->read_urbs[i]);
|
||||
acm->control->needs_remote_wakeup = 0;
|
||||
usb_autopm_put_interface(acm->control);
|
||||
/*
|
||||
* Need to grab write_lock to prevent race with resume, but no need to
|
||||
* hold it due to the tty-port initialised flag.
|
||||
*/
|
||||
spin_lock_irq(&acm->write_lock);
|
||||
spin_unlock_irq(&acm->write_lock);
|
||||
|
||||
usb_autopm_get_interface_no_resume(acm->control);
|
||||
acm->control->needs_remote_wakeup = 0;
|
||||
usb_autopm_put_interface(acm->control);
|
||||
|
||||
for (;;) {
|
||||
urb = usb_get_from_anchor(&acm->delayed);
|
||||
if (!urb)
|
||||
break;
|
||||
wb = urb->context;
|
||||
wb->use = 0;
|
||||
usb_autopm_put_interface_async(acm->control);
|
||||
}
|
||||
mutex_unlock(&acm->mutex);
|
||||
|
||||
usb_kill_urb(acm->ctrlurb);
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
usb_kill_urb(acm->wb[i].urb);
|
||||
for (i = 0; i < acm->rx_buflimit; i++)
|
||||
usb_kill_urb(acm->read_urbs[i]);
|
||||
}
|
||||
|
||||
static void acm_tty_cleanup(struct tty_struct *tty)
|
||||
|
|
@ -646,16 +687,18 @@ static int acm_tty_write(struct tty_struct *tty,
|
|||
memcpy(wb->buf, buf, count);
|
||||
wb->len = count;
|
||||
|
||||
usb_autopm_get_interface_async(acm->control);
|
||||
if (acm->susp_count) {
|
||||
if (!acm->delayed_wb)
|
||||
acm->delayed_wb = wb;
|
||||
else
|
||||
usb_autopm_put_interface_async(acm->control);
|
||||
stat = usb_autopm_get_interface_async(acm->control);
|
||||
if (stat) {
|
||||
wb->use = 0;
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return count; /* A white lie */
|
||||
return stat;
|
||||
}
|
||||
|
||||
if (acm->susp_count) {
|
||||
usb_anchor_urb(wb->urb, &acm->delayed);
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return count;
|
||||
}
|
||||
usb_mark_last_busy(acm->dev);
|
||||
|
||||
stat = acm_start_wb(acm, wb);
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
|
|
@ -958,6 +1001,7 @@ static void acm_tty_set_termios(struct tty_struct *tty,
|
|||
}
|
||||
|
||||
static const struct tty_port_operations acm_port_ops = {
|
||||
.dtr_rts = acm_port_dtr_rts,
|
||||
.shutdown = acm_port_shutdown,
|
||||
.activate = acm_port_activate,
|
||||
.destruct = acm_port_destruct,
|
||||
|
|
@ -1269,6 +1313,7 @@ made_compressed_probe:
|
|||
acm->bInterval = epread->bInterval;
|
||||
tty_port_init(&acm->port);
|
||||
acm->port.ops = &acm_port_ops;
|
||||
init_usb_anchor(&acm->delayed);
|
||||
|
||||
buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
|
||||
if (!buf) {
|
||||
|
|
@ -1394,8 +1439,6 @@ skip_countries:
|
|||
|
||||
dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
|
||||
|
||||
acm_set_control(acm, acm->ctrlout);
|
||||
|
||||
acm->line.dwDTERate = cpu_to_le32(9600);
|
||||
acm->line.bDataBits = 8;
|
||||
acm_set_line(acm, &acm->line);
|
||||
|
|
@ -1514,27 +1557,20 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
struct acm *acm = usb_get_intfdata(intf);
|
||||
int cnt;
|
||||
|
||||
spin_lock_irq(&acm->write_lock);
|
||||
if (PMSG_IS_AUTO(message)) {
|
||||
int b;
|
||||
|
||||
spin_lock_irq(&acm->write_lock);
|
||||
b = acm->transmitting;
|
||||
spin_unlock_irq(&acm->write_lock);
|
||||
if (b)
|
||||
if (acm->transmitting) {
|
||||
spin_unlock_irq(&acm->write_lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
spin_lock(&acm->write_lock);
|
||||
cnt = acm->susp_count++;
|
||||
spin_unlock(&acm->write_lock);
|
||||
spin_unlock_irq(&acm->read_lock);
|
||||
spin_unlock_irq(&acm->write_lock);
|
||||
|
||||
if (cnt)
|
||||
return 0;
|
||||
|
||||
if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags))
|
||||
stop_data_traffic(acm);
|
||||
stop_data_traffic(acm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1542,29 +1578,23 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
static int acm_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct acm *acm = usb_get_intfdata(intf);
|
||||
struct acm_wb *wb;
|
||||
struct urb *urb;
|
||||
int rv = 0;
|
||||
int cnt;
|
||||
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
acm->susp_count -= 1;
|
||||
cnt = acm->susp_count;
|
||||
spin_unlock_irq(&acm->read_lock);
|
||||
spin_lock_irq(&acm->write_lock);
|
||||
|
||||
if (cnt)
|
||||
return 0;
|
||||
if (--acm->susp_count)
|
||||
goto out;
|
||||
|
||||
if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
|
||||
rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
|
||||
rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
|
||||
|
||||
spin_lock_irq(&acm->write_lock);
|
||||
if (acm->delayed_wb) {
|
||||
wb = acm->delayed_wb;
|
||||
acm->delayed_wb = NULL;
|
||||
spin_unlock_irq(&acm->write_lock);
|
||||
acm_start_wb(acm, wb);
|
||||
} else {
|
||||
spin_unlock_irq(&acm->write_lock);
|
||||
for (;;) {
|
||||
urb = usb_get_from_anchor(&acm->delayed);
|
||||
if (!urb)
|
||||
break;
|
||||
|
||||
acm_start_wb(acm, urb->context);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1572,12 +1602,13 @@ static int acm_resume(struct usb_interface *intf)
|
|||
* do the write path at all cost
|
||||
*/
|
||||
if (rv < 0)
|
||||
goto err_out;
|
||||
goto out;
|
||||
|
||||
rv = acm_submit_read_urbs(acm, GFP_NOIO);
|
||||
rv = acm_submit_read_urbs(acm, GFP_ATOMIC);
|
||||
}
|
||||
out:
|
||||
spin_unlock_irq(&acm->write_lock);
|
||||
|
||||
err_out:
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -120,15 +120,15 @@ struct acm {
|
|||
unsigned int throttled:1; /* actually throttled */
|
||||
unsigned int throttle_req:1; /* throttle requested */
|
||||
u8 bInterval;
|
||||
struct acm_wb *delayed_wb; /* write queued for a device about to be woken */
|
||||
struct usb_anchor delayed; /* writes queued for a device about to be woken */
|
||||
};
|
||||
|
||||
#define CDC_DATA_INTERFACE_TYPE 0x0a
|
||||
|
||||
/* constants describing various quirks and errors */
|
||||
#define NO_UNION_NORMAL 1
|
||||
#define SINGLE_RX_URB 2
|
||||
#define NO_CAP_LINE 4
|
||||
#define NOT_A_MODEM 8
|
||||
#define NO_DATA_INTERFACE 16
|
||||
#define IGNORE_DEVICE 32
|
||||
#define NO_UNION_NORMAL BIT(0)
|
||||
#define SINGLE_RX_URB BIT(1)
|
||||
#define NO_CAP_LINE BIT(2)
|
||||
#define NOT_A_MODEM BIT(3)
|
||||
#define NO_DATA_INTERFACE BIT(4)
|
||||
#define IGNORE_DEVICE BIT(5)
|
||||
|
|
|
|||
|
|
@ -383,9 +383,12 @@ exit:
|
|||
static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t transfer_size)
|
||||
{
|
||||
int retval;
|
||||
u8 buffer[USBTMC_HEADER_SIZE];
|
||||
u8 *buffer;
|
||||
int actual;
|
||||
|
||||
buffer = kmalloc(USBTMC_HEADER_SIZE, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
/* Setup IO buffer for REQUEST_DEV_DEP_MSG_IN message
|
||||
* Refer to class specs for details
|
||||
*/
|
||||
|
|
@ -417,6 +420,7 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t
|
|||
if (!data->bTag)
|
||||
data->bTag++;
|
||||
|
||||
kfree(buffer);
|
||||
if (retval < 0) {
|
||||
dev_err(&data->intf->dev, "usb_bulk_msg in send_request_dev_dep_msg_in() returned %d\n", retval);
|
||||
return retval;
|
||||
|
|
|
|||
6
drivers/usb/common/Makefile
Normal file
6
drivers/usb/common/Makefile
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# Makefile for the usb common parts.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_USB_COMMON) += usb-common.o
|
||||
obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
|
||||
|
|
@ -364,3 +364,4 @@ int otg_statemachine(struct otg_fsm *fsm)
|
|||
VDBG("quit statemachine, changed = %d\n", state_changed);
|
||||
return state_changed;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(otg_statemachine);
|
||||
|
|
@ -1,13 +1,6 @@
|
|||
#
|
||||
# USB Core configuration
|
||||
#
|
||||
config USB_DEBUG
|
||||
bool "USB verbose debug messages"
|
||||
help
|
||||
Say Y here if you want the USB core & hub drivers to produce a bunch
|
||||
of debug messages to the system log. Select this if you are having a
|
||||
problem with USB support and want to see more of what is going on.
|
||||
|
||||
config USB_ANNOUNCE_NEW_DEVICES
|
||||
bool "USB announce new devices"
|
||||
help
|
||||
|
|
@ -88,3 +81,12 @@ config USB_OTG_BLACKLIST_HUB
|
|||
and software costs by not supporting external hubs. So
|
||||
are "Embedded Hosts" that don't offer OTG support.
|
||||
|
||||
config USB_OTG_FSM
|
||||
tristate "USB 2.0 OTG FSM implementation"
|
||||
depends on USB
|
||||
select USB_OTG
|
||||
select USB_PHY
|
||||
help
|
||||
Implements OTG Finite State Machine as specified in On-The-Go
|
||||
and Embedded Host Supplement to the USB Revision 2.0 Specification.
|
||||
|
||||
|
|
|
|||
|
|
@ -192,7 +192,6 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
|
||||
if (pci_enable_device(dev) < 0)
|
||||
return -ENODEV;
|
||||
dev->current_state = PCI_D0;
|
||||
|
||||
/*
|
||||
* The xHCI driver has its own irq management
|
||||
|
|
|
|||
|
|
@ -918,6 +918,7 @@ static void usb_bus_init (struct usb_bus *bus)
|
|||
bus->bandwidth_allocated = 0;
|
||||
bus->bandwidth_int_reqs = 0;
|
||||
bus->bandwidth_isoc_reqs = 0;
|
||||
mutex_init(&bus->usb_address0_mutex);
|
||||
|
||||
INIT_LIST_HEAD (&bus->bus_list);
|
||||
}
|
||||
|
|
@ -1502,6 +1503,9 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
|||
ret = -EAGAIN;
|
||||
else
|
||||
urb->transfer_flags |= URB_DMA_MAP_PAGE;
|
||||
} else if (is_vmalloc_addr(urb->transfer_buffer)) {
|
||||
WARN_ONCE(1, "transfer buffer not dma capable\n");
|
||||
ret = -EAGAIN;
|
||||
} else {
|
||||
urb->transfer_dma = dma_map_single(
|
||||
hcd->self.controller,
|
||||
|
|
@ -2263,9 +2267,7 @@ static void hcd_resume_work(struct work_struct *work)
|
|||
struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work);
|
||||
struct usb_device *udev = hcd->self.root_hub;
|
||||
|
||||
usb_lock_device(udev);
|
||||
usb_remote_wakeup(udev);
|
||||
usb_unlock_device(udev);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2454,11 +2456,13 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
|
|||
mutex_init(hcd->bandwidth_mutex);
|
||||
dev_set_drvdata(dev, hcd);
|
||||
} else {
|
||||
mutex_lock(&usb_port_peer_mutex);
|
||||
hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex;
|
||||
hcd->primary_hcd = primary_hcd;
|
||||
primary_hcd->primary_hcd = primary_hcd;
|
||||
hcd->shared_hcd = primary_hcd;
|
||||
primary_hcd->shared_hcd = hcd;
|
||||
mutex_unlock(&usb_port_peer_mutex);
|
||||
}
|
||||
|
||||
kref_init(&hcd->kref);
|
||||
|
|
@ -2510,18 +2514,25 @@ EXPORT_SYMBOL_GPL(usb_create_hcd);
|
|||
* deallocated.
|
||||
*
|
||||
* Make sure to only deallocate the bandwidth_mutex when the primary HCD is
|
||||
* freed. When hcd_release() is called for the non-primary HCD, set the
|
||||
* primary_hcd's shared_hcd pointer to null (since the non-primary HCD will be
|
||||
* freed shortly).
|
||||
* freed. When hcd_release() is called for either hcd in a peer set
|
||||
* invalidate the peer's ->shared_hcd and ->primary_hcd pointers to
|
||||
* block new peering attempts
|
||||
*/
|
||||
static void hcd_release (struct kref *kref)
|
||||
static void hcd_release(struct kref *kref)
|
||||
{
|
||||
struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
|
||||
|
||||
mutex_lock(&usb_port_peer_mutex);
|
||||
if (usb_hcd_is_primary_hcd(hcd))
|
||||
kfree(hcd->bandwidth_mutex);
|
||||
else
|
||||
hcd->shared_hcd->shared_hcd = NULL;
|
||||
if (hcd->shared_hcd) {
|
||||
struct usb_hcd *peer = hcd->shared_hcd;
|
||||
|
||||
peer->shared_hcd = NULL;
|
||||
if (peer->primary_hcd == hcd)
|
||||
peer->primary_hcd = NULL;
|
||||
}
|
||||
mutex_unlock(&usb_port_peer_mutex);
|
||||
kfree(hcd);
|
||||
}
|
||||
|
||||
|
|
@ -2589,6 +2600,21 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Before we free this root hub, flush in-flight peering attempts
|
||||
* and disable peer lookups
|
||||
*/
|
||||
static void usb_put_invalidate_rhdev(struct usb_hcd *hcd)
|
||||
{
|
||||
struct usb_device *rhdev;
|
||||
|
||||
mutex_lock(&usb_port_peer_mutex);
|
||||
rhdev = hcd->self.root_hub;
|
||||
hcd->self.root_hub = NULL;
|
||||
mutex_unlock(&usb_port_peer_mutex);
|
||||
usb_put_dev(rhdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_add_hcd - finish generic HCD structure initialization and register
|
||||
* @hcd: the usb_hcd structure to initialize
|
||||
|
|
@ -2649,7 +2675,9 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
|||
retval = -ENOMEM;
|
||||
goto err_allocate_root_hub;
|
||||
}
|
||||
mutex_lock(&usb_port_peer_mutex);
|
||||
hcd->self.root_hub = rhdev;
|
||||
mutex_unlock(&usb_port_peer_mutex);
|
||||
|
||||
switch (hcd->speed) {
|
||||
case HCD_USB11:
|
||||
|
|
@ -2758,7 +2786,7 @@ err_hcd_driver_start:
|
|||
err_request_irq:
|
||||
err_hcd_driver_setup:
|
||||
err_set_rh_speed:
|
||||
usb_put_dev(hcd->self.root_hub);
|
||||
usb_put_invalidate_rhdev(hcd);
|
||||
err_allocate_root_hub:
|
||||
usb_deregister_bus(&hcd->self);
|
||||
err_register_bus:
|
||||
|
|
@ -2838,7 +2866,6 @@ void usb_remove_hcd(struct usb_hcd *hcd)
|
|||
free_irq(hcd->irq, hcd);
|
||||
}
|
||||
|
||||
usb_put_dev(hcd->self.root_hub);
|
||||
usb_deregister_bus(&hcd->self);
|
||||
hcd_buffer_destroy(hcd);
|
||||
if (hcd->remove_phy && hcd->phy) {
|
||||
|
|
@ -2846,6 +2873,8 @@ void usb_remove_hcd(struct usb_hcd *hcd)
|
|||
usb_put_phy(hcd->phy);
|
||||
hcd->phy = NULL;
|
||||
}
|
||||
|
||||
usb_put_invalidate_rhdev(hcd);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_remove_hcd);
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -45,12 +45,13 @@ struct usb_hub {
|
|||
unsigned long event_bits[1]; /* status change bitmask */
|
||||
unsigned long change_bits[1]; /* ports with logical connect
|
||||
status change */
|
||||
unsigned long busy_bits[1]; /* ports being reset or
|
||||
resumed */
|
||||
unsigned long removed_bits[1]; /* ports with a "removed"
|
||||
device present */
|
||||
unsigned long wakeup_bits[1]; /* ports that have signaled
|
||||
remote wakeup */
|
||||
unsigned long power_bits[1]; /* ports that are powered */
|
||||
unsigned long child_usage_bits[1]; /* ports powered on for
|
||||
children */
|
||||
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
|
||||
#error event_bits[] is too short!
|
||||
#endif
|
||||
|
|
@ -66,6 +67,7 @@ struct usb_hub {
|
|||
unsigned limited_power:1;
|
||||
unsigned quiescing:1;
|
||||
unsigned disconnected:1;
|
||||
unsigned in_reset:1;
|
||||
|
||||
unsigned quirk_check_port_auto_suspend:1;
|
||||
|
||||
|
|
@ -81,19 +83,23 @@ struct usb_hub {
|
|||
* @child: usb device attached to the port
|
||||
* @dev: generic device interface
|
||||
* @port_owner: port's owner
|
||||
* @peer: related usb2 and usb3 ports (share the same connector)
|
||||
* @connect_type: port's connect type
|
||||
* @location: opaque representation of platform connector location
|
||||
* @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
|
||||
* @portnum: port index num based one
|
||||
* @power_is_on: port's power state
|
||||
* @did_runtime_put: port has done pm_runtime_put().
|
||||
* @is_superspeed cache super-speed status
|
||||
*/
|
||||
struct usb_port {
|
||||
struct usb_device *child;
|
||||
struct device dev;
|
||||
struct usb_dev_state *port_owner;
|
||||
struct usb_port *peer;
|
||||
enum usb_port_connect_type connect_type;
|
||||
usb_port_location_t location;
|
||||
struct mutex status_lock;
|
||||
u8 portnum;
|
||||
unsigned power_is_on:1;
|
||||
unsigned did_runtime_put:1;
|
||||
unsigned int is_superspeed:1;
|
||||
};
|
||||
|
||||
#define to_usb_port(_dev) \
|
||||
|
|
@ -111,6 +117,29 @@ extern int hub_port_debounce(struct usb_hub *hub, int port1,
|
|||
extern int usb_clear_port_feature(struct usb_device *hdev,
|
||||
int port1, int feature);
|
||||
|
||||
static inline bool hub_is_port_power_switchable(struct usb_hub *hub)
|
||||
{
|
||||
__le16 hcs;
|
||||
|
||||
if (!hub)
|
||||
return false;
|
||||
hcs = hub->descriptor->wHubCharacteristics;
|
||||
return (le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM;
|
||||
}
|
||||
|
||||
static inline int hub_is_superspeed(struct usb_device *hdev)
|
||||
{
|
||||
return hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS;
|
||||
}
|
||||
|
||||
static inline unsigned hub_power_on_good_delay(struct usb_hub *hub)
|
||||
{
|
||||
unsigned delay = hub->descriptor->bPwrOn2PwrGood * 2;
|
||||
|
||||
/* Wait at least 100 msec for power to become stable */
|
||||
return max(delay, 100U);
|
||||
}
|
||||
|
||||
static inline int hub_port_debounce_be_connected(struct usb_hub *hub,
|
||||
int port1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -76,17 +76,29 @@ static int usb_port_runtime_resume(struct device *dev)
|
|||
struct usb_device *hdev = to_usb_device(dev->parent->parent);
|
||||
struct usb_interface *intf = to_usb_interface(dev->parent);
|
||||
struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
|
||||
struct usb_device *udev = port_dev->child;
|
||||
struct usb_port *peer = port_dev->peer;
|
||||
int port1 = port_dev->portnum;
|
||||
int retval;
|
||||
|
||||
if (!hub)
|
||||
return -EINVAL;
|
||||
if (hub->in_reset) {
|
||||
set_bit(port1, hub->power_bits);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Power on our usb3 peer before this usb2 port to prevent a usb3
|
||||
* device from degrading to its usb2 connection
|
||||
*/
|
||||
if (!port_dev->is_superspeed && peer)
|
||||
pm_runtime_get_sync(&peer->dev);
|
||||
|
||||
usb_autopm_get_interface(intf);
|
||||
set_bit(port1, hub->busy_bits);
|
||||
|
||||
retval = usb_hub_set_port_power(hdev, hub, port1, true);
|
||||
if (port_dev->child && !retval) {
|
||||
msleep(hub_power_on_good_delay(hub));
|
||||
if (udev && !retval) {
|
||||
/*
|
||||
* Attempt to wait for usb hub port to be reconnected in order
|
||||
* to make the resume procedure successful. The device may have
|
||||
|
|
@ -97,12 +109,17 @@ static int usb_port_runtime_resume(struct device *dev)
|
|||
if (retval < 0)
|
||||
dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n",
|
||||
retval);
|
||||
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
|
||||
retval = 0;
|
||||
|
||||
/* Force the child awake to revalidate after the power loss. */
|
||||
if (!test_and_set_bit(port1, hub->child_usage_bits)) {
|
||||
pm_runtime_get_noresume(&port_dev->dev);
|
||||
pm_request_resume(&udev->dev);
|
||||
}
|
||||
}
|
||||
|
||||
clear_bit(port1, hub->busy_bits);
|
||||
usb_autopm_put_interface(intf);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -112,23 +129,34 @@ static int usb_port_runtime_suspend(struct device *dev)
|
|||
struct usb_device *hdev = to_usb_device(dev->parent->parent);
|
||||
struct usb_interface *intf = to_usb_interface(dev->parent);
|
||||
struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
|
||||
struct usb_port *peer = port_dev->peer;
|
||||
int port1 = port_dev->portnum;
|
||||
int retval;
|
||||
|
||||
if (!hub)
|
||||
return -EINVAL;
|
||||
if (hub->in_reset)
|
||||
return -EBUSY;
|
||||
|
||||
if (dev_pm_qos_flags(&port_dev->dev, PM_QOS_FLAG_NO_POWER_OFF)
|
||||
== PM_QOS_FLAGS_ALL)
|
||||
return -EAGAIN;
|
||||
|
||||
usb_autopm_get_interface(intf);
|
||||
set_bit(port1, hub->busy_bits);
|
||||
retval = usb_hub_set_port_power(hdev, hub, port1, false);
|
||||
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
|
||||
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
|
||||
clear_bit(port1, hub->busy_bits);
|
||||
if (!port_dev->is_superspeed)
|
||||
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
|
||||
usb_autopm_put_interface(intf);
|
||||
|
||||
/*
|
||||
* Our peer usb3 port may now be able to suspend, so
|
||||
* asynchronously queue a suspend request to observe that this
|
||||
* usb2 port is now off.
|
||||
*/
|
||||
if (!port_dev->is_superspeed && peer)
|
||||
pm_runtime_put(&peer->dev);
|
||||
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -146,9 +174,215 @@ struct device_type usb_port_device_type = {
|
|||
.pm = &usb_port_pm_ops,
|
||||
};
|
||||
|
||||
static struct device_driver usb_port_driver = {
|
||||
.name = "usb",
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int link_peers(struct usb_port *left, struct usb_port *right)
|
||||
{
|
||||
struct usb_port *ss_port, *hs_port;
|
||||
int rc;
|
||||
|
||||
if (left->peer == right && right->peer == left)
|
||||
return 0;
|
||||
|
||||
if (left->peer || right->peer) {
|
||||
struct usb_port *lpeer = left->peer;
|
||||
struct usb_port *rpeer = right->peer;
|
||||
|
||||
WARN(1, "failed to peer %s and %s (%s -> %p) (%s -> %p)\n",
|
||||
dev_name(&left->dev), dev_name(&right->dev),
|
||||
dev_name(&left->dev), lpeer,
|
||||
dev_name(&right->dev), rpeer);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
rc = sysfs_create_link(&left->dev.kobj, &right->dev.kobj, "peer");
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = sysfs_create_link(&right->dev.kobj, &left->dev.kobj, "peer");
|
||||
if (rc) {
|
||||
sysfs_remove_link(&left->dev.kobj, "peer");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to wake the HiSpeed port to make sure we don't race
|
||||
* setting ->peer with usb_port_runtime_suspend(). Otherwise we
|
||||
* may miss a suspend event for the SuperSpeed port.
|
||||
*/
|
||||
if (left->is_superspeed) {
|
||||
ss_port = left;
|
||||
WARN_ON(right->is_superspeed);
|
||||
hs_port = right;
|
||||
} else {
|
||||
ss_port = right;
|
||||
WARN_ON(!right->is_superspeed);
|
||||
hs_port = left;
|
||||
}
|
||||
pm_runtime_get_sync(&hs_port->dev);
|
||||
|
||||
left->peer = right;
|
||||
right->peer = left;
|
||||
|
||||
/*
|
||||
* The SuperSpeed reference is dropped when the HiSpeed port in
|
||||
* this relationship suspends, i.e. when it is safe to allow a
|
||||
* SuperSpeed connection to drop since there is no risk of a
|
||||
* device degrading to its powered-off HiSpeed connection.
|
||||
*
|
||||
* Also, drop the HiSpeed ref taken above.
|
||||
*/
|
||||
pm_runtime_get_sync(&ss_port->dev);
|
||||
pm_runtime_put(&hs_port->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void link_peers_report(struct usb_port *left, struct usb_port *right)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = link_peers(left, right);
|
||||
if (rc == 0) {
|
||||
dev_dbg(&left->dev, "peered to %s\n", dev_name(&right->dev));
|
||||
} else {
|
||||
dev_warn(&left->dev, "failed to peer to %s (%d)\n",
|
||||
dev_name(&right->dev), rc);
|
||||
pr_warn_once("usb: port power management may be unreliable\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void unlink_peers(struct usb_port *left, struct usb_port *right)
|
||||
{
|
||||
struct usb_port *ss_port, *hs_port;
|
||||
|
||||
WARN(right->peer != left || left->peer != right,
|
||||
"%s and %s are not peers?\n",
|
||||
dev_name(&left->dev), dev_name(&right->dev));
|
||||
|
||||
/*
|
||||
* We wake the HiSpeed port to make sure we don't race its
|
||||
* usb_port_runtime_resume() event which takes a SuperSpeed ref
|
||||
* when ->peer is !NULL.
|
||||
*/
|
||||
if (left->is_superspeed) {
|
||||
ss_port = left;
|
||||
hs_port = right;
|
||||
} else {
|
||||
ss_port = right;
|
||||
hs_port = left;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(&hs_port->dev);
|
||||
|
||||
sysfs_remove_link(&left->dev.kobj, "peer");
|
||||
right->peer = NULL;
|
||||
sysfs_remove_link(&right->dev.kobj, "peer");
|
||||
left->peer = NULL;
|
||||
|
||||
/* Drop the SuperSpeed ref held on behalf of the active HiSpeed port */
|
||||
pm_runtime_put(&ss_port->dev);
|
||||
|
||||
/* Drop the ref taken above */
|
||||
pm_runtime_put(&hs_port->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* For each usb hub device in the system check to see if it is in the
|
||||
* peer domain of the given port_dev, and if it is check to see if it
|
||||
* has a port that matches the given port by location
|
||||
*/
|
||||
static int match_location(struct usb_device *peer_hdev, void *p)
|
||||
{
|
||||
int port1;
|
||||
struct usb_hcd *hcd, *peer_hcd;
|
||||
struct usb_port *port_dev = p, *peer;
|
||||
struct usb_hub *peer_hub = usb_hub_to_struct_hub(peer_hdev);
|
||||
struct usb_device *hdev = to_usb_device(port_dev->dev.parent->parent);
|
||||
|
||||
if (!peer_hub)
|
||||
return 0;
|
||||
|
||||
hcd = bus_to_hcd(hdev->bus);
|
||||
peer_hcd = bus_to_hcd(peer_hdev->bus);
|
||||
/* peer_hcd is provisional until we verify it against the known peer */
|
||||
if (peer_hcd != hcd->shared_hcd)
|
||||
return 0;
|
||||
|
||||
for (port1 = 1; port1 <= peer_hdev->maxchild; port1++) {
|
||||
peer = peer_hub->ports[port1 - 1];
|
||||
if (peer && peer->location == port_dev->location) {
|
||||
link_peers_report(port_dev, peer);
|
||||
return 1; /* done */
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the peer port either via explicit platform firmware "location"
|
||||
* data, the peer hcd for root hubs, or the upstream peer relationship
|
||||
* for all other hubs.
|
||||
*/
|
||||
static void find_and_link_peer(struct usb_hub *hub, int port1)
|
||||
{
|
||||
struct usb_port *port_dev = hub->ports[port1 - 1], *peer;
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
struct usb_device *peer_hdev;
|
||||
struct usb_hub *peer_hub;
|
||||
|
||||
/*
|
||||
* If location data is available then we can only peer this port
|
||||
* by a location match, not the default peer (lest we create a
|
||||
* situation where we need to go back and undo a default peering
|
||||
* when the port is later peered by location data)
|
||||
*/
|
||||
if (port_dev->location) {
|
||||
/* we link the peer in match_location() if found */
|
||||
usb_for_each_dev(port_dev, match_location);
|
||||
return;
|
||||
} else if (!hdev->parent) {
|
||||
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
|
||||
struct usb_hcd *peer_hcd = hcd->shared_hcd;
|
||||
|
||||
if (!peer_hcd)
|
||||
return;
|
||||
|
||||
peer_hdev = peer_hcd->self.root_hub;
|
||||
} else {
|
||||
struct usb_port *upstream;
|
||||
struct usb_device *parent = hdev->parent;
|
||||
struct usb_hub *parent_hub = usb_hub_to_struct_hub(parent);
|
||||
|
||||
if (!parent_hub)
|
||||
return;
|
||||
|
||||
upstream = parent_hub->ports[hdev->portnum - 1];
|
||||
if (!upstream || !upstream->peer)
|
||||
return;
|
||||
|
||||
peer_hdev = upstream->peer->child;
|
||||
}
|
||||
|
||||
peer_hub = usb_hub_to_struct_hub(peer_hdev);
|
||||
if (!peer_hub || port1 > peer_hdev->maxchild)
|
||||
return;
|
||||
|
||||
/*
|
||||
* we found a valid default peer, last check is to make sure it
|
||||
* does not have location data
|
||||
*/
|
||||
peer = peer_hub->ports[port1 - 1];
|
||||
if (peer && peer->location == 0)
|
||||
link_peers_report(port_dev, peer);
|
||||
}
|
||||
|
||||
int usb_hub_create_port_device(struct usb_hub *hub, int port1)
|
||||
{
|
||||
struct usb_port *port_dev = NULL;
|
||||
struct usb_port *port_dev;
|
||||
int retval;
|
||||
|
||||
port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
|
||||
|
|
@ -159,24 +393,33 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
|
|||
|
||||
hub->ports[port1 - 1] = port_dev;
|
||||
port_dev->portnum = port1;
|
||||
port_dev->power_is_on = true;
|
||||
set_bit(port1, hub->power_bits);
|
||||
port_dev->dev.parent = hub->intfdev;
|
||||
port_dev->dev.groups = port_dev_group;
|
||||
port_dev->dev.type = &usb_port_device_type;
|
||||
dev_set_name(&port_dev->dev, "port%d", port1);
|
||||
|
||||
port_dev->dev.driver = &usb_port_driver;
|
||||
if (hub_is_superspeed(hub->hdev))
|
||||
port_dev->is_superspeed = 1;
|
||||
dev_set_name(&port_dev->dev, "%s-port%d", dev_name(&hub->hdev->dev),
|
||||
port1);
|
||||
mutex_init(&port_dev->status_lock);
|
||||
retval = device_register(&port_dev->dev);
|
||||
if (retval)
|
||||
goto error_register;
|
||||
|
||||
find_and_link_peer(hub, port1);
|
||||
|
||||
pm_runtime_set_active(&port_dev->dev);
|
||||
|
||||
/* It would be dangerous if user space couldn't
|
||||
* prevent usb device from being powered off. So don't
|
||||
* enable port runtime pm if failed to expose port's pm qos.
|
||||
/*
|
||||
* Do not enable port runtime pm if the hub does not support
|
||||
* power switching. Also, userspace must have final say of
|
||||
* whether a port is permitted to power-off. Do not enable
|
||||
* runtime pm if we fail to expose pm_qos_no_power_off.
|
||||
*/
|
||||
if (!dev_pm_qos_expose_flags(&port_dev->dev,
|
||||
PM_QOS_FLAG_NO_POWER_OFF))
|
||||
if (hub_is_port_power_switchable(hub)
|
||||
&& dev_pm_qos_expose_flags(&port_dev->dev,
|
||||
PM_QOS_FLAG_NO_POWER_OFF) == 0)
|
||||
pm_runtime_enable(&port_dev->dev);
|
||||
|
||||
device_enable_async_suspend(&port_dev->dev);
|
||||
|
|
@ -188,9 +431,13 @@ exit:
|
|||
return retval;
|
||||
}
|
||||
|
||||
void usb_hub_remove_port_device(struct usb_hub *hub,
|
||||
int port1)
|
||||
void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
|
||||
{
|
||||
device_unregister(&hub->ports[port1 - 1]->dev);
|
||||
}
|
||||
struct usb_port *port_dev = hub->ports[port1 - 1];
|
||||
struct usb_port *peer;
|
||||
|
||||
peer = port_dev->peer;
|
||||
if (peer)
|
||||
unlink_peers(port_dev, peer);
|
||||
device_unregister(&port_dev->dev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "hub.h"
|
||||
|
||||
/**
|
||||
* usb_acpi_power_manageable - check whether usb port has
|
||||
|
|
@ -55,13 +55,18 @@ EXPORT_SYMBOL_GPL(usb_acpi_power_manageable);
|
|||
*/
|
||||
int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
|
||||
{
|
||||
struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
|
||||
struct usb_port *port_dev;
|
||||
acpi_handle port_handle;
|
||||
unsigned char state;
|
||||
int port1 = index + 1;
|
||||
int error = -EINVAL;
|
||||
|
||||
port_handle = (acpi_handle)usb_get_hub_port_acpi_handle(hdev,
|
||||
port1);
|
||||
if (!hub)
|
||||
return -ENODEV;
|
||||
port_dev = hub->ports[port1 - 1];
|
||||
|
||||
port_handle = (acpi_handle) usb_get_hub_port_acpi_handle(hdev, port1);
|
||||
if (!port_handle)
|
||||
return error;
|
||||
|
||||
|
|
@ -72,23 +77,21 @@ int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
|
|||
|
||||
error = acpi_bus_set_power(port_handle, state);
|
||||
if (!error)
|
||||
dev_dbg(&hdev->dev, "The power of hub port %d was set to %d\n",
|
||||
port1, enable);
|
||||
dev_dbg(&port_dev->dev, "acpi: power was set to %d\n", enable);
|
||||
else
|
||||
dev_dbg(&hdev->dev, "The power of hub port failed to be set\n");
|
||||
dev_dbg(&port_dev->dev, "acpi: power failed to be set\n");
|
||||
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
|
||||
|
||||
static int usb_acpi_check_port_connect_type(struct usb_device *hdev,
|
||||
acpi_handle handle, int port1)
|
||||
static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle,
|
||||
struct acpi_pld_info *pld)
|
||||
{
|
||||
acpi_status status;
|
||||
enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *upc;
|
||||
struct acpi_pld_info *pld;
|
||||
int ret = 0;
|
||||
acpi_status status;
|
||||
|
||||
/*
|
||||
* According to ACPI Spec 9.13. PLD indicates whether usb port is
|
||||
|
|
@ -98,39 +101,37 @@ static int usb_acpi_check_port_connect_type(struct usb_device *hdev,
|
|||
* a usb device is directly hard-wired to the port. If no visible and
|
||||
* no connectable, the port would be not used.
|
||||
*/
|
||||
status = acpi_get_physical_device_location(handle, &pld);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
|
||||
upc = buffer.pointer;
|
||||
if (!upc || (upc->type != ACPI_TYPE_PACKAGE)
|
||||
|| upc->package.count != 4) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (upc->package.elements[0].integer.value)
|
||||
if (pld->user_visible)
|
||||
usb_set_hub_port_connect_type(hdev, port1,
|
||||
USB_PORT_CONNECT_TYPE_HOT_PLUG);
|
||||
connect_type = USB_PORT_CONNECT_TYPE_HOT_PLUG;
|
||||
else
|
||||
usb_set_hub_port_connect_type(hdev, port1,
|
||||
USB_PORT_CONNECT_TYPE_HARD_WIRED);
|
||||
connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED;
|
||||
else if (!pld->user_visible)
|
||||
usb_set_hub_port_connect_type(hdev, port1, USB_PORT_NOT_USED);
|
||||
|
||||
connect_type = USB_PORT_NOT_USED;
|
||||
out:
|
||||
ACPI_FREE(pld);
|
||||
kfree(upc);
|
||||
return ret;
|
||||
return connect_type;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Private to usb-acpi, all the core needs to know is that
|
||||
* port_dev->location is non-zero when it has been set by the firmware.
|
||||
*/
|
||||
#define USB_ACPI_LOCATION_VALID (1 << 31)
|
||||
|
||||
static struct acpi_device *usb_acpi_find_companion(struct device *dev)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
struct acpi_device *adev;
|
||||
acpi_handle *parent_handle;
|
||||
int port_num;
|
||||
|
||||
/*
|
||||
* In the ACPI DSDT table, only usb root hub and usb ports are
|
||||
|
|
@ -147,37 +148,19 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev)
|
|||
*/
|
||||
if (is_usb_device(dev)) {
|
||||
udev = to_usb_device(dev);
|
||||
if (udev->parent) {
|
||||
enum usb_port_connect_type type;
|
||||
|
||||
/*
|
||||
* According usb port's connect type to set usb device's
|
||||
* removability.
|
||||
*/
|
||||
type = usb_get_hub_port_connect_type(udev->parent,
|
||||
udev->portnum);
|
||||
switch (type) {
|
||||
case USB_PORT_CONNECT_TYPE_HOT_PLUG:
|
||||
udev->removable = USB_DEVICE_REMOVABLE;
|
||||
break;
|
||||
case USB_PORT_CONNECT_TYPE_HARD_WIRED:
|
||||
udev->removable = USB_DEVICE_FIXED;
|
||||
break;
|
||||
default:
|
||||
udev->removable = USB_DEVICE_REMOVABLE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (udev->parent)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* root hub's parent is the usb hcd. */
|
||||
return acpi_find_child_device(ACPI_COMPANION(dev->parent),
|
||||
udev->portnum, false);
|
||||
/* root hub is only child (_ADR=0) under its parent, the HC */
|
||||
adev = ACPI_COMPANION(dev->parent);
|
||||
return acpi_find_child_device(adev, 0, false);
|
||||
} else if (is_usb_port(dev)) {
|
||||
struct acpi_device *adev = NULL;
|
||||
struct usb_port *port_dev = to_usb_port(dev);
|
||||
int port1 = port_dev->portnum;
|
||||
struct acpi_pld_info *pld;
|
||||
acpi_handle *handle;
|
||||
acpi_status status;
|
||||
|
||||
sscanf(dev_name(dev), "port%d", &port_num);
|
||||
/* Get the struct usb_device point of port's hub */
|
||||
udev = to_usb_device(dev->parent->parent);
|
||||
|
||||
|
|
@ -188,12 +171,11 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev)
|
|||
*/
|
||||
if (!udev->parent) {
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
int raw_port_num;
|
||||
int raw;
|
||||
|
||||
raw_port_num = usb_hcd_find_raw_port_number(hcd,
|
||||
port_num);
|
||||
raw = usb_hcd_find_raw_port_number(hcd, port1);
|
||||
adev = acpi_find_child_device(ACPI_COMPANION(&udev->dev),
|
||||
raw_port_num, false);
|
||||
raw, false);
|
||||
if (!adev)
|
||||
return NULL;
|
||||
} else {
|
||||
|
|
@ -204,11 +186,20 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev)
|
|||
return NULL;
|
||||
|
||||
acpi_bus_get_device(parent_handle, &adev);
|
||||
adev = acpi_find_child_device(adev, port_num, false);
|
||||
adev = acpi_find_child_device(adev, port1, false);
|
||||
if (!adev)
|
||||
return NULL;
|
||||
}
|
||||
usb_acpi_check_port_connect_type(udev, adev->handle, port_num);
|
||||
handle = adev->handle;
|
||||
status = acpi_get_physical_device_location(handle, &pld);
|
||||
if (ACPI_FAILURE(status) || !pld)
|
||||
return adev;
|
||||
|
||||
port_dev->location = USB_ACPI_LOCATION_VALID
|
||||
| pld->group_token << 8 | pld->group_position;
|
||||
port_dev->connect_type = usb_acpi_get_connect_type(handle, pld);
|
||||
ACPI_FREE(pld);
|
||||
|
||||
return adev;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,11 +107,6 @@ static inline int usb_autoresume_device(struct usb_device *udev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int usb_remote_wakeup(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
|
||||
{
|
||||
return 0;
|
||||
|
|
@ -119,6 +114,7 @@ static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
|
|||
#endif
|
||||
|
||||
extern struct bus_type usb_bus_type;
|
||||
extern struct mutex usb_port_peer_mutex;
|
||||
extern struct device_type usb_device_type;
|
||||
extern struct device_type usb_if_device_type;
|
||||
extern struct device_type usb_ep_device_type;
|
||||
|
|
@ -170,15 +166,17 @@ extern void usbfs_conn_disc_event(void);
|
|||
extern int usb_devio_init(void);
|
||||
extern void usb_devio_cleanup(void);
|
||||
|
||||
/*
|
||||
* Firmware specific cookie identifying a port's location. '0' == no location
|
||||
* data available
|
||||
*/
|
||||
typedef u32 usb_port_location_t;
|
||||
|
||||
/* internal notify stuff */
|
||||
extern void usb_notify_add_device(struct usb_device *udev);
|
||||
extern void usb_notify_remove_device(struct usb_device *udev);
|
||||
extern void usb_notify_add_bus(struct usb_bus *ubus);
|
||||
extern void usb_notify_remove_bus(struct usb_bus *ubus);
|
||||
extern enum usb_port_connect_type
|
||||
usb_get_hub_port_connect_type(struct usb_device *hdev, int port1);
|
||||
extern void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
|
||||
enum usb_port_connect_type type);
|
||||
extern void usb_hub_adjust_deviceremovable(struct usb_device *hdev,
|
||||
struct usb_hub_descriptor *desc);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,58 @@
|
|||
config USB_DWC2
|
||||
tristate "DesignWare USB2 DRD Core Support"
|
||||
bool "DesignWare USB2 DRD Core Support"
|
||||
depends on USB
|
||||
help
|
||||
Say Y or M here if your system has a Dual Role HighSpeed
|
||||
USB controller based on the DesignWare HSOTG IP Core.
|
||||
Say Y here if your system has a Dual Role Hi-Speed USB
|
||||
controller based on the DesignWare HSOTG IP Core.
|
||||
|
||||
If you choose to build this driver as dynamically linked
|
||||
modules, the core module will be called dwc2.ko, the
|
||||
PCI bus interface module (if you have a PCI bus system)
|
||||
will be called dwc2_pci.ko and the platform interface module
|
||||
(for controllers directly connected to the CPU) will be called
|
||||
dwc2_platform.ko.
|
||||
For host mode, if you choose to build the driver as dynamically
|
||||
linked modules, the core module will be called dwc2.ko, the PCI
|
||||
bus interface module (if you have a PCI bus system) will be
|
||||
called dwc2_pci.ko, and the platform interface module (for
|
||||
controllers directly connected to the CPU) will be called
|
||||
dwc2_platform.ko. For gadget mode, there will be a single
|
||||
module called dwc2_gadget.ko.
|
||||
|
||||
NOTE: This driver at present only implements the Host mode
|
||||
of the controller. The existing s3c-hsotg driver supports
|
||||
Peripheral mode, but only for the Samsung S3C platforms.
|
||||
There are plans to merge the s3c-hsotg driver with this
|
||||
driver in the near future to create a dual-role driver.
|
||||
NOTE: The s3c-hsotg driver is now renamed to dwc2_gadget. The
|
||||
host and gadget drivers are still currently separate drivers.
|
||||
There are plans to merge the dwc2_gadget driver with the dwc2
|
||||
host driver in the near future to create a dual-role driver.
|
||||
|
||||
if USB_DWC2
|
||||
|
||||
config USB_DWC2_HOST
|
||||
tristate "Host only mode"
|
||||
depends on USB
|
||||
help
|
||||
The Designware USB2.0 high-speed host controller
|
||||
integrated into many SoCs.
|
||||
|
||||
config USB_DWC2_PLATFORM
|
||||
bool "DWC2 Platform"
|
||||
depends on USB_DWC2_HOST
|
||||
default USB_DWC2_HOST
|
||||
help
|
||||
The Designware USB2.0 platform interface module for
|
||||
controllers directly connected to the CPU. This is only
|
||||
used for host mode.
|
||||
|
||||
config USB_DWC2_PCI
|
||||
bool "DWC2 PCI"
|
||||
depends on USB_DWC2_HOST && PCI
|
||||
default USB_DWC2_HOST
|
||||
help
|
||||
The Designware USB2.0 PCI interface module for controllers
|
||||
connected to a PCI bus. This is only used for host mode.
|
||||
|
||||
comment "Gadget mode requires USB Gadget support to be enabled"
|
||||
|
||||
config USB_DWC2_PERIPHERAL
|
||||
tristate "Gadget only mode"
|
||||
depends on USB_GADGET
|
||||
help
|
||||
The Designware USB2.0 high-speed gadget controller
|
||||
integrated into many SoCs.
|
||||
|
||||
config USB_DWC2_DEBUG
|
||||
bool "Enable Debugging Messages"
|
||||
help
|
||||
|
|
|
|||
|
|
@ -1,25 +1,28 @@
|
|||
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
|
||||
|
||||
# NOTE: This driver at present only implements the Host mode
|
||||
# of the controller. The existing s3c-hsotg driver supports
|
||||
# Peripheral mode, but only for the Samsung S3C platforms.
|
||||
# There are plans to merge the s3c-hsotg driver with this
|
||||
# driver in the near future to create a dual-role driver. Once
|
||||
# that is done, Host mode will become an optional feature that
|
||||
# is selected with a config option.
|
||||
|
||||
obj-$(CONFIG_USB_DWC2_HOST) += dwc2.o
|
||||
dwc2-y := core.o core_intr.o
|
||||
dwc2-y += hcd.o hcd_intr.o
|
||||
dwc2-y += hcd_queue.o hcd_ddma.o
|
||||
|
||||
ifneq ($(CONFIG_PCI),)
|
||||
obj-$(CONFIG_USB_DWC2) += dwc2_pci.o
|
||||
endif
|
||||
obj-$(CONFIG_USB_DWC2) += dwc2_platform.o
|
||||
# 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_gadget.ko will get built for peripheral mode. For host mode,
|
||||
# the core module will be dwc2.ko, the PCI bus interface module will called
|
||||
# dwc2_pci.ko and the platform interface module will be called dwc2_platform.ko.
|
||||
# At present the host and gadget driver will be separate drivers, but there
|
||||
# are plans in the near future to create a dual-role driver.
|
||||
|
||||
dwc2_pci-y += pci.o
|
||||
dwc2_platform-y += platform.o
|
||||
ifneq ($(CONFIG_USB_DWC2_PCI),)
|
||||
obj-$(CONFIG_USB_DWC2_HOST) += dwc2_pci.o
|
||||
dwc2_pci-y := pci.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_USB_DWC2_PLATFORM),)
|
||||
obj-$(CONFIG_USB_DWC2_HOST) += dwc2_platform.o
|
||||
dwc2_platform-y := platform.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB_DWC2_PERIPHERAL) += dwc2_gadget.o
|
||||
dwc2_gadget-y := gadget.o
|
||||
|
|
|
|||
|
|
@ -507,6 +507,72 @@ void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg)
|
|||
writel(intmsk, hsotg->regs + GINTMSK);
|
||||
}
|
||||
|
||||
/*
|
||||
* dwc2_calculate_dynamic_fifo() - Calculates the default fifo size
|
||||
* For system that have a total fifo depth that is smaller than the default
|
||||
* RX + TX fifo size.
|
||||
*
|
||||
* @hsotg: Programming view of DWC_otg controller
|
||||
*/
|
||||
static void dwc2_calculate_dynamic_fifo(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *params = hsotg->core_params;
|
||||
struct dwc2_hw_params *hw = &hsotg->hw_params;
|
||||
u32 rxfsiz, nptxfsiz, ptxfsiz, total_fifo_size;
|
||||
|
||||
total_fifo_size = hw->total_fifo_size;
|
||||
rxfsiz = params->host_rx_fifo_size;
|
||||
nptxfsiz = params->host_nperio_tx_fifo_size;
|
||||
ptxfsiz = params->host_perio_tx_fifo_size;
|
||||
|
||||
/*
|
||||
* Will use Method 2 defined in the DWC2 spec: minimum FIFO depth
|
||||
* allocation with support for high bandwidth endpoints. Synopsys
|
||||
* defines MPS(Max Packet size) for a periodic EP=1024, and for
|
||||
* non-periodic as 512.
|
||||
*/
|
||||
if (total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)) {
|
||||
/*
|
||||
* For Buffer DMA mode/Scatter Gather DMA mode
|
||||
* 2 * ((Largest Packet size / 4) + 1 + 1) + n
|
||||
* with n = number of host channel.
|
||||
* 2 * ((1024/4) + 2) = 516
|
||||
*/
|
||||
rxfsiz = 516 + hw->host_channels;
|
||||
|
||||
/*
|
||||
* min non-periodic tx fifo depth
|
||||
* 2 * (largest non-periodic USB packet used / 4)
|
||||
* 2 * (512/4) = 256
|
||||
*/
|
||||
nptxfsiz = 256;
|
||||
|
||||
/*
|
||||
* min periodic tx fifo depth
|
||||
* (largest packet size*MC)/4
|
||||
* (1024 * 3)/4 = 768
|
||||
*/
|
||||
ptxfsiz = 768;
|
||||
|
||||
params->host_rx_fifo_size = rxfsiz;
|
||||
params->host_nperio_tx_fifo_size = nptxfsiz;
|
||||
params->host_perio_tx_fifo_size = ptxfsiz;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the summation of RX, NPTX and PTX fifo sizes is still
|
||||
* bigger than the total_fifo_size, then we have a problem.
|
||||
*
|
||||
* We won't be able to allocate as many endpoints. Right now,
|
||||
* we're just printing an error message, but ideally this FIFO
|
||||
* allocation algorithm would be improved in the future.
|
||||
*
|
||||
* FIXME improve this FIFO allocation algorithm.
|
||||
*/
|
||||
if (unlikely(total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)))
|
||||
dev_err(hsotg->dev, "invalid fifo sizes\n");
|
||||
}
|
||||
|
||||
static void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *params = hsotg->core_params;
|
||||
|
|
@ -515,6 +581,8 @@ static void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
|
|||
if (!params->enable_dynamic_fifo)
|
||||
return;
|
||||
|
||||
dwc2_calculate_dynamic_fifo(hsotg);
|
||||
|
||||
/* Rx FIFO */
|
||||
grxfsiz = readl(hsotg->regs + GRXFSIZ);
|
||||
dev_dbg(hsotg->dev, "initial grxfsiz=%08x\n", grxfsiz);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@
|
|||
#ifndef __DWC2_CORE_H__
|
||||
#define __DWC2_CORE_H__
|
||||
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include "hw.h"
|
||||
|
||||
|
|
@ -54,6 +58,184 @@ static inline void do_write(u32 value, void *addr)
|
|||
/* Maximum number of Endpoints/HostChannels */
|
||||
#define MAX_EPS_CHANNELS 16
|
||||
|
||||
/* s3c-hsotg declarations */
|
||||
static const char * const s3c_hsotg_supply_names[] = {
|
||||
"vusb_d", /* digital USB supply, 1.2V */
|
||||
"vusb_a", /* analog USB supply, 1.1V */
|
||||
};
|
||||
|
||||
/*
|
||||
* EP0_MPS_LIMIT
|
||||
*
|
||||
* Unfortunately there seems to be a limit of the amount of data that can
|
||||
* be transferred by IN transactions on EP0. This is either 127 bytes or 3
|
||||
* packets (which practically means 1 packet and 63 bytes of data) when the
|
||||
* MPS is set to 64.
|
||||
*
|
||||
* This means if we are wanting to move >127 bytes of data, we need to
|
||||
* split the transactions up, but just doing one packet at a time does
|
||||
* not work (this may be an implicit DATA0 PID on first packet of the
|
||||
* transaction) and doing 2 packets is outside the controller's limits.
|
||||
*
|
||||
* If we try to lower the MPS size for EP0, then no transfers work properly
|
||||
* for EP0, and the system will fail basic enumeration. As no cause for this
|
||||
* has currently been found, we cannot support any large IN transfers for
|
||||
* EP0.
|
||||
*/
|
||||
#define EP0_MPS_LIMIT 64
|
||||
|
||||
struct s3c_hsotg;
|
||||
struct s3c_hsotg_req;
|
||||
|
||||
/**
|
||||
* struct s3c_hsotg_ep - driver endpoint definition.
|
||||
* @ep: The gadget layer representation of the endpoint.
|
||||
* @name: The driver generated name for the endpoint.
|
||||
* @queue: Queue of requests for this endpoint.
|
||||
* @parent: Reference back to the parent device structure.
|
||||
* @req: The current request that the endpoint is processing. This is
|
||||
* used to indicate an request has been loaded onto the endpoint
|
||||
* and has yet to be completed (maybe due to data move, or simply
|
||||
* awaiting an ack from the core all the data has been completed).
|
||||
* @debugfs: File entry for debugfs file for this endpoint.
|
||||
* @lock: State lock to protect contents of endpoint.
|
||||
* @dir_in: Set to true if this endpoint is of the IN direction, which
|
||||
* means that it is sending data to the Host.
|
||||
* @index: The index for the endpoint registers.
|
||||
* @mc: Multi Count - number of transactions per microframe
|
||||
* @interval - Interval for periodic endpoints
|
||||
* @name: The name array passed to the USB core.
|
||||
* @halted: Set if the endpoint has been halted.
|
||||
* @periodic: Set if this is a periodic ep, such as Interrupt
|
||||
* @isochronous: Set if this is a isochronous ep
|
||||
* @sent_zlp: Set if we've sent a zero-length packet.
|
||||
* @total_data: The total number of data bytes done.
|
||||
* @fifo_size: The size of the FIFO (for periodic IN endpoints)
|
||||
* @fifo_load: The amount of data loaded into the FIFO (periodic IN)
|
||||
* @last_load: The offset of data for the last start of request.
|
||||
* @size_loaded: The last loaded size for DxEPTSIZE for periodic IN
|
||||
*
|
||||
* This is the driver's state for each registered enpoint, allowing it
|
||||
* to keep track of transactions that need doing. Each endpoint has a
|
||||
* lock to protect the state, to try and avoid using an overall lock
|
||||
* for the host controller as much as possible.
|
||||
*
|
||||
* For periodic IN endpoints, we have fifo_size and fifo_load to try
|
||||
* and keep track of the amount of data in the periodic FIFO for each
|
||||
* of these as we don't have a status register that tells us how much
|
||||
* is in each of them. (note, this may actually be useless information
|
||||
* as in shared-fifo mode periodic in acts like a single-frame packet
|
||||
* buffer than a fifo)
|
||||
*/
|
||||
struct s3c_hsotg_ep {
|
||||
struct usb_ep ep;
|
||||
struct list_head queue;
|
||||
struct s3c_hsotg *parent;
|
||||
struct s3c_hsotg_req *req;
|
||||
struct dentry *debugfs;
|
||||
|
||||
unsigned long total_data;
|
||||
unsigned int size_loaded;
|
||||
unsigned int last_load;
|
||||
unsigned int fifo_load;
|
||||
unsigned short fifo_size;
|
||||
|
||||
unsigned char dir_in;
|
||||
unsigned char index;
|
||||
unsigned char mc;
|
||||
unsigned char interval;
|
||||
|
||||
unsigned int halted:1;
|
||||
unsigned int periodic:1;
|
||||
unsigned int isochronous:1;
|
||||
unsigned int sent_zlp:1;
|
||||
|
||||
char name[10];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct s3c_hsotg - driver state.
|
||||
* @dev: The parent device supplied to the probe function
|
||||
* @driver: USB gadget driver
|
||||
* @phy: The otg phy transceiver structure for phy control.
|
||||
* @uphy: The otg phy transceiver structure for old USB phy control.
|
||||
* @plat: The platform specific configuration data. This can be removed once
|
||||
* all SoCs support usb transceiver.
|
||||
* @regs: The memory area mapped for accessing registers.
|
||||
* @irq: The IRQ number we are using
|
||||
* @supplies: Definition of USB power supplies
|
||||
* @phyif: PHY interface width
|
||||
* @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
|
||||
* @num_of_eps: Number of available EPs (excluding EP0)
|
||||
* @debug_root: root directrory for debugfs.
|
||||
* @debug_file: main status file for debugfs.
|
||||
* @debug_fifo: FIFO status file for debugfs.
|
||||
* @ep0_reply: Request used for ep0 reply.
|
||||
* @ep0_buff: Buffer for EP0 reply data, if needed.
|
||||
* @ctrl_buff: Buffer for EP0 control requests.
|
||||
* @ctrl_req: Request for EP0 control packets.
|
||||
* @setup: NAK management for EP0 SETUP
|
||||
* @last_rst: Time of last reset
|
||||
* @eps: The endpoints being supplied to the gadget framework
|
||||
*/
|
||||
struct s3c_hsotg {
|
||||
struct device *dev;
|
||||
struct usb_gadget_driver *driver;
|
||||
struct phy *phy;
|
||||
struct usb_phy *uphy;
|
||||
struct s3c_hsotg_plat *plat;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
void __iomem *regs;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
|
||||
struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)];
|
||||
|
||||
u32 phyif;
|
||||
unsigned int dedicated_fifos:1;
|
||||
unsigned char num_of_eps;
|
||||
|
||||
struct dentry *debug_root;
|
||||
struct dentry *debug_file;
|
||||
struct dentry *debug_fifo;
|
||||
|
||||
struct usb_request *ep0_reply;
|
||||
struct usb_request *ctrl_req;
|
||||
u8 ep0_buff[8];
|
||||
u8 ctrl_buff[8];
|
||||
|
||||
struct usb_gadget gadget;
|
||||
unsigned int setup;
|
||||
unsigned long last_rst;
|
||||
struct s3c_hsotg_ep *eps;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct s3c_hsotg_req - data transfer request
|
||||
* @req: The USB gadget request
|
||||
* @queue: The list of requests for the endpoint this is queued for.
|
||||
* @in_progress: Has already had size/packets written to core
|
||||
* @mapped: DMA buffer for this request has been mapped via dma_map_single().
|
||||
*/
|
||||
struct s3c_hsotg_req {
|
||||
struct usb_request req;
|
||||
struct list_head queue;
|
||||
unsigned char in_progress;
|
||||
unsigned char mapped;
|
||||
};
|
||||
|
||||
#define call_gadget(_hs, _entry) \
|
||||
do { \
|
||||
if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
|
||||
(_hs)->driver && (_hs)->driver->_entry) { \
|
||||
spin_unlock(&_hs->lock); \
|
||||
(_hs)->driver->_entry(&(_hs)->gadget); \
|
||||
spin_lock(&_hs->lock); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct dwc2_hsotg;
|
||||
struct dwc2_host_chan;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -109,6 +109,7 @@
|
|||
#define GUSBCFG_FSINTF (1 << 5)
|
||||
#define GUSBCFG_ULPI_UTMI_SEL (1 << 4)
|
||||
#define GUSBCFG_PHYIF16 (1 << 3)
|
||||
#define GUSBCFG_PHYIF8 (0 << 3)
|
||||
#define GUSBCFG_TOUTCAL_MASK (0x7 << 0)
|
||||
#define GUSBCFG_TOUTCAL_SHIFT 0
|
||||
#define GUSBCFG_TOUTCAL_LIMIT 0x7
|
||||
|
|
@ -403,6 +404,7 @@
|
|||
#define FIFOSIZE_DEPTH_SHIFT 16
|
||||
#define FIFOSIZE_STARTADDR_MASK (0xffff << 0)
|
||||
#define FIFOSIZE_STARTADDR_SHIFT 0
|
||||
#define FIFOSIZE_DEPTH_GET(_x) (((_x) >> 16) & 0xffff)
|
||||
|
||||
/* Device mode registers */
|
||||
|
||||
|
|
@ -519,11 +521,11 @@
|
|||
#define DXEPCTL_STALL (1 << 21)
|
||||
#define DXEPCTL_SNP (1 << 20)
|
||||
#define DXEPCTL_EPTYPE_MASK (0x3 << 18)
|
||||
#define DXEPCTL_EPTYPE_SHIFT 18
|
||||
#define DXEPCTL_EPTYPE_CONTROL 0
|
||||
#define DXEPCTL_EPTYPE_ISO 1
|
||||
#define DXEPCTL_EPTYPE_BULK 2
|
||||
#define DXEPCTL_EPTYPE_INTTERUPT 3
|
||||
#define DXEPCTL_EPTYPE_CONTROL (0x0 << 18)
|
||||
#define DXEPCTL_EPTYPE_ISO (0x1 << 18)
|
||||
#define DXEPCTL_EPTYPE_BULK (0x2 << 18)
|
||||
#define DXEPCTL_EPTYPE_INTERRUPT (0x3 << 18)
|
||||
|
||||
#define DXEPCTL_NAKSTS (1 << 17)
|
||||
#define DXEPCTL_DPID (1 << 16)
|
||||
#define DXEPCTL_EOFRNUM (1 << 16)
|
||||
|
|
|
|||
|
|
@ -134,6 +134,12 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
|||
/* Default all params to autodetect */
|
||||
dwc2_set_all_params(&defparams, -1);
|
||||
params = &defparams;
|
||||
|
||||
/*
|
||||
* Disable descriptor dma mode by default as the HW can support
|
||||
* it, but does not support it for SPLIT transactions.
|
||||
*/
|
||||
defparams.dma_desc_enable = 0;
|
||||
}
|
||||
|
||||
hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ comment "Platform Glue Driver Support"
|
|||
|
||||
config USB_DWC3_OMAP
|
||||
tristate "Texas Instruments OMAP5 and similar Platforms"
|
||||
depends on EXTCON
|
||||
depends on EXTCON && (ARCH_OMAP2PLUS || COMPILE_TEST)
|
||||
default USB_DWC3
|
||||
help
|
||||
Some platforms from Texas Instruments like OMAP5, DRA7xxx and
|
||||
|
|
@ -54,6 +54,7 @@ config USB_DWC3_OMAP
|
|||
|
||||
config USB_DWC3_EXYNOS
|
||||
tristate "Samsung Exynos Platform"
|
||||
depends on ARCH_EXYNOS || COMPILE_TEST
|
||||
default USB_DWC3
|
||||
help
|
||||
Recent Exynos5 SoCs ship with one DesignWare Core USB3 IP inside,
|
||||
|
|
@ -72,6 +73,7 @@ config USB_DWC3_PCI
|
|||
|
||||
config USB_DWC3_KEYSTONE
|
||||
tristate "Texas Instruments Keystone2 Platforms"
|
||||
depends on ARCH_KEYSTONE || COMPILE_TEST
|
||||
default USB_DWC3
|
||||
help
|
||||
Support of USB2/3 functionality in TI Keystone2 platforms.
|
||||
|
|
|
|||
|
|
@ -486,70 +486,20 @@ static void dwc3_core_exit(struct dwc3 *dwc)
|
|||
phy_exit(dwc->usb3_generic_phy);
|
||||
}
|
||||
|
||||
#define DWC3_ALIGN_MASK (16 - 1)
|
||||
|
||||
static int dwc3_probe(struct platform_device *pdev)
|
||||
static int dwc3_core_get_phy(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dwc3_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct device *dev = dwc->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct resource *res;
|
||||
struct dwc3 *dwc;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
||||
void __iomem *regs;
|
||||
void *mem;
|
||||
|
||||
mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
|
||||
if (!mem) {
|
||||
dev_err(dev, "not enough memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
|
||||
dwc->mem = mem;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing IRQ\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dwc->xhci_resources[1].start = res->start;
|
||||
dwc->xhci_resources[1].end = res->end;
|
||||
dwc->xhci_resources[1].flags = res->flags;
|
||||
dwc->xhci_resources[1].name = res->name;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing memory resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
int ret;
|
||||
|
||||
if (node) {
|
||||
dwc->maximum_speed = of_usb_get_maximum_speed(node);
|
||||
|
||||
dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0);
|
||||
dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1);
|
||||
|
||||
dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize");
|
||||
dwc->dr_mode = of_usb_get_dr_mode(node);
|
||||
} else if (pdata) {
|
||||
dwc->maximum_speed = pdata->maximum_speed;
|
||||
|
||||
dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
|
||||
|
||||
dwc->needs_fifo_resize = pdata->tx_fifo_resize;
|
||||
dwc->dr_mode = pdata->dr_mode;
|
||||
} else {
|
||||
dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
|
||||
}
|
||||
|
||||
/* default to superspeed if no maximum_speed passed */
|
||||
if (dwc->maximum_speed == USB_SPEED_UNKNOWN)
|
||||
dwc->maximum_speed = USB_SPEED_SUPER;
|
||||
|
||||
if (IS_ERR(dwc->usb2_phy)) {
|
||||
ret = PTR_ERR(dwc->usb2_phy);
|
||||
if (ret == -ENXIO || ret == -ENODEV) {
|
||||
|
|
@ -600,6 +550,132 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = dwc->dev;
|
||||
int ret;
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_core_exit_mode(struct dwc3 *dwc)
|
||||
{
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
dwc3_gadget_exit(dwc);
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
dwc3_host_exit(dwc);
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_host_exit(dwc);
|
||||
dwc3_gadget_exit(dwc);
|
||||
break;
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define DWC3_ALIGN_MASK (16 - 1)
|
||||
|
||||
static int dwc3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dwc3_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct device_node *node = dev->of_node;
|
||||
struct resource *res;
|
||||
struct dwc3 *dwc;
|
||||
|
||||
int ret;
|
||||
|
||||
void __iomem *regs;
|
||||
void *mem;
|
||||
|
||||
mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
|
||||
if (!mem) {
|
||||
dev_err(dev, "not enough memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
|
||||
dwc->mem = mem;
|
||||
dwc->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing IRQ\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dwc->xhci_resources[1].start = res->start;
|
||||
dwc->xhci_resources[1].end = res->end;
|
||||
dwc->xhci_resources[1].flags = res->flags;
|
||||
dwc->xhci_resources[1].name = res->name;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing memory resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (node) {
|
||||
dwc->maximum_speed = of_usb_get_maximum_speed(node);
|
||||
|
||||
dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize");
|
||||
dwc->dr_mode = of_usb_get_dr_mode(node);
|
||||
} else if (pdata) {
|
||||
dwc->maximum_speed = pdata->maximum_speed;
|
||||
|
||||
dwc->needs_fifo_resize = pdata->tx_fifo_resize;
|
||||
dwc->dr_mode = pdata->dr_mode;
|
||||
}
|
||||
|
||||
/* default to superspeed if no maximum_speed passed */
|
||||
if (dwc->maximum_speed == USB_SPEED_UNKNOWN)
|
||||
dwc->maximum_speed = USB_SPEED_SUPER;
|
||||
|
||||
ret = dwc3_core_get_phy(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dwc->xhci_resources[0].start = res->start;
|
||||
dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
|
||||
DWC3_XHCI_REGS_END;
|
||||
|
|
@ -621,7 +697,6 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
|
||||
dwc->regs = regs;
|
||||
dwc->regs_size = resource_size(res);
|
||||
dwc->dev = dev;
|
||||
|
||||
dev->dma_mask = dev->parent->dma_mask;
|
||||
dev->dma_parms = dev->parent->dma_parms;
|
||||
|
|
@ -670,41 +745,9 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
goto err_usb3phy_power;
|
||||
}
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
goto err2;
|
||||
}
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
goto err2;
|
||||
}
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
goto err2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
|
||||
ret = dwc3_core_init_mode(dwc);
|
||||
if (ret)
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = dwc3_debugfs_init(dwc);
|
||||
if (ret) {
|
||||
|
|
@ -717,21 +760,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
err3:
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
dwc3_gadget_exit(dwc);
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
dwc3_host_exit(dwc);
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_host_exit(dwc);
|
||||
dwc3_gadget_exit(dwc);
|
||||
break;
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
dwc3_core_exit_mode(dwc);
|
||||
|
||||
err2:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
|
|
@ -766,23 +795,7 @@ static int dwc3_remove(struct platform_device *pdev)
|
|||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
dwc3_debugfs_exit(dwc);
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
dwc3_gadget_exit(dwc);
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
dwc3_host_exit(dwc);
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_host_exit(dwc);
|
||||
dwc3_gadget_exit(dwc);
|
||||
break;
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
dwc3_core_exit_mode(dwc);
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
dwc3_free_event_buffers(dwc);
|
||||
dwc3_core_exit(dwc);
|
||||
|
|
|
|||
|
|
@ -24,9 +24,10 @@
|
|||
#include <linux/dma-mapping.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/usb_phy_gen_xceiv.h>
|
||||
#include <linux/usb/usb_phy_generic.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
struct dwc3_exynos {
|
||||
struct platform_device *usb2_phy;
|
||||
|
|
@ -34,17 +35,19 @@ struct dwc3_exynos {
|
|||
struct device *dev;
|
||||
|
||||
struct clk *clk;
|
||||
struct regulator *vdd33;
|
||||
struct regulator *vdd10;
|
||||
};
|
||||
|
||||
static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
|
||||
{
|
||||
struct usb_phy_gen_xceiv_platform_data pdata;
|
||||
struct usb_phy_generic_platform_data pdata;
|
||||
struct platform_device *pdev;
|
||||
int ret;
|
||||
|
||||
memset(&pdata, 0x00, sizeof(pdata));
|
||||
|
||||
pdev = platform_device_alloc("usb_phy_gen_xceiv", PLATFORM_DEVID_AUTO);
|
||||
pdev = platform_device_alloc("usb_phy_generic", PLATFORM_DEVID_AUTO);
|
||||
if (!pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -56,7 +59,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
|
|||
if (ret)
|
||||
goto err1;
|
||||
|
||||
pdev = platform_device_alloc("usb_phy_gen_xceiv", PLATFORM_DEVID_AUTO);
|
||||
pdev = platform_device_alloc("usb_phy_generic", PLATFORM_DEVID_AUTO);
|
||||
if (!pdev) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
|
|
@ -107,12 +110,12 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
int ret;
|
||||
|
||||
exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
|
||||
if (!exynos) {
|
||||
dev_err(dev, "not enough memory\n");
|
||||
goto err1;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -122,21 +125,20 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
|||
*/
|
||||
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
goto err1;
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, exynos);
|
||||
|
||||
ret = dwc3_exynos_register_phys(exynos);
|
||||
if (ret) {
|
||||
dev_err(dev, "couldn't register PHYs\n");
|
||||
goto err1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk = devm_clk_get(dev, "usbdrd30");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "couldn't get clock\n");
|
||||
ret = -EINVAL;
|
||||
goto err1;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
exynos->dev = dev;
|
||||
|
|
@ -144,23 +146,48 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
|||
|
||||
clk_prepare_enable(exynos->clk);
|
||||
|
||||
exynos->vdd33 = devm_regulator_get(dev, "vdd33");
|
||||
if (IS_ERR(exynos->vdd33)) {
|
||||
ret = PTR_ERR(exynos->vdd33);
|
||||
goto err2;
|
||||
}
|
||||
ret = regulator_enable(exynos->vdd33);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable VDD33 supply\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
exynos->vdd10 = devm_regulator_get(dev, "vdd10");
|
||||
if (IS_ERR(exynos->vdd10)) {
|
||||
ret = PTR_ERR(exynos->vdd10);
|
||||
goto err3;
|
||||
}
|
||||
ret = regulator_enable(exynos->vdd10);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable VDD10 supply\n");
|
||||
goto err3;
|
||||
}
|
||||
|
||||
if (node) {
|
||||
ret = of_platform_populate(node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add dwc3 core\n");
|
||||
goto err2;
|
||||
goto err4;
|
||||
}
|
||||
} else {
|
||||
dev_err(dev, "no device node, failed to add dwc3 core\n");
|
||||
ret = -ENODEV;
|
||||
goto err2;
|
||||
goto err4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
regulator_disable(exynos->vdd10);
|
||||
err3:
|
||||
regulator_disable(exynos->vdd33);
|
||||
err2:
|
||||
clk_disable_unprepare(clk);
|
||||
err1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -174,6 +201,9 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
|
|||
|
||||
clk_disable_unprepare(exynos->clk);
|
||||
|
||||
regulator_disable(exynos->vdd33);
|
||||
regulator_disable(exynos->vdd10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -192,12 +222,27 @@ static int dwc3_exynos_suspend(struct device *dev)
|
|||
|
||||
clk_disable(exynos->clk);
|
||||
|
||||
regulator_disable(exynos->vdd33);
|
||||
regulator_disable(exynos->vdd10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_exynos_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_exynos *exynos = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(exynos->vdd33);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable VDD33 supply\n");
|
||||
return ret;
|
||||
}
|
||||
ret = regulator_enable(exynos->vdd10);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable VDD10 supply\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_enable(exynos->clk);
|
||||
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
|||
struct extcon_dev *edev;
|
||||
struct regulator *vbus_reg = NULL;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
int utmi_mode = 0;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/usb_phy_gen_xceiv.h>
|
||||
#include <linux/usb/usb_phy_generic.h>
|
||||
|
||||
/* FIXME define these in <linux/pci_ids.h> */
|
||||
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
|
||||
|
|
@ -40,13 +40,13 @@ struct dwc3_pci {
|
|||
|
||||
static int dwc3_pci_register_phys(struct dwc3_pci *glue)
|
||||
{
|
||||
struct usb_phy_gen_xceiv_platform_data pdata;
|
||||
struct usb_phy_generic_platform_data pdata;
|
||||
struct platform_device *pdev;
|
||||
int ret;
|
||||
|
||||
memset(&pdata, 0x00, sizeof(pdata));
|
||||
|
||||
pdev = platform_device_alloc("usb_phy_gen_xceiv", 0);
|
||||
pdev = platform_device_alloc("usb_phy_generic", 0);
|
||||
if (!pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ static int dwc3_pci_register_phys(struct dwc3_pci *glue)
|
|||
if (ret)
|
||||
goto err1;
|
||||
|
||||
pdev = platform_device_alloc("usb_phy_gen_xceiv", 1);
|
||||
pdev = platform_device_alloc("usb_phy_generic", 1);
|
||||
if (!pdev) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
|
|
@ -99,7 +99,7 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
|||
struct resource res[2];
|
||||
struct platform_device *dwc3;
|
||||
struct dwc3_pci *glue;
|
||||
int ret = -ENOMEM;
|
||||
int ret;
|
||||
struct device *dev = &pci->dev;
|
||||
|
||||
glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
|
||||
|
|
@ -110,7 +110,7 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
|||
|
||||
glue->dev = dev;
|
||||
|
||||
ret = pci_enable_device(pci);
|
||||
ret = pcim_enable_device(pci);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable pci device\n");
|
||||
return -ENODEV;
|
||||
|
|
@ -127,8 +127,7 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
|||
dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
|
||||
if (!dwc3) {
|
||||
dev_err(dev, "couldn't allocate dwc3 device\n");
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
|
||||
|
|
@ -145,7 +144,7 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
|||
ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
|
||||
if (ret) {
|
||||
dev_err(dev, "couldn't add resources to dwc3 device\n");
|
||||
goto err1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pci, glue);
|
||||
|
|
@ -167,9 +166,6 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
|||
|
||||
err3:
|
||||
platform_device_put(dwc3);
|
||||
err1:
|
||||
pci_disable_device(pci);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +176,6 @@ static void dwc3_pci_remove(struct pci_dev *pci)
|
|||
platform_device_unregister(glue->dwc3);
|
||||
platform_device_unregister(glue->usb2_phy);
|
||||
platform_device_unregister(glue->usb3_phy);
|
||||
pci_disable_device(pci);
|
||||
}
|
||||
|
||||
static const struct pci_device_id dwc3_pci_id_table[] = {
|
||||
|
|
|
|||
|
|
@ -298,11 +298,76 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd)
|
|||
}
|
||||
}
|
||||
|
||||
static const char *dwc3_gadget_generic_cmd_string(u8 cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case DWC3_DGCMD_SET_LMP:
|
||||
return "Set LMP";
|
||||
case DWC3_DGCMD_SET_PERIODIC_PAR:
|
||||
return "Set Periodic Parameters";
|
||||
case DWC3_DGCMD_XMIT_FUNCTION:
|
||||
return "Transmit Function Wake Device Notification";
|
||||
case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO:
|
||||
return "Set Scratchpad Buffer Array Address Lo";
|
||||
case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI:
|
||||
return "Set Scratchpad Buffer Array Address Hi";
|
||||
case DWC3_DGCMD_SELECTED_FIFO_FLUSH:
|
||||
return "Selected FIFO Flush";
|
||||
case DWC3_DGCMD_ALL_FIFO_FLUSH:
|
||||
return "All FIFO Flush";
|
||||
case DWC3_DGCMD_SET_ENDPOINT_NRDY:
|
||||
return "Set Endpoint NRDY";
|
||||
case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
|
||||
return "Run SoC Bus Loopback Test";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *dwc3_gadget_link_string(enum dwc3_link_state link_state)
|
||||
{
|
||||
switch (link_state) {
|
||||
case DWC3_LINK_STATE_U0:
|
||||
return "U0";
|
||||
case DWC3_LINK_STATE_U1:
|
||||
return "U1";
|
||||
case DWC3_LINK_STATE_U2:
|
||||
return "U2";
|
||||
case DWC3_LINK_STATE_U3:
|
||||
return "U3";
|
||||
case DWC3_LINK_STATE_SS_DIS:
|
||||
return "SS.Disabled";
|
||||
case DWC3_LINK_STATE_RX_DET:
|
||||
return "RX.Detect";
|
||||
case DWC3_LINK_STATE_SS_INACT:
|
||||
return "SS.Inactive";
|
||||
case DWC3_LINK_STATE_POLL:
|
||||
return "Polling";
|
||||
case DWC3_LINK_STATE_RECOV:
|
||||
return "Recovery";
|
||||
case DWC3_LINK_STATE_HRESET:
|
||||
return "Hot Reset";
|
||||
case DWC3_LINK_STATE_CMPLY:
|
||||
return "Compliance";
|
||||
case DWC3_LINK_STATE_LPBK:
|
||||
return "Loopback";
|
||||
case DWC3_LINK_STATE_RESET:
|
||||
return "Reset";
|
||||
case DWC3_LINK_STATE_RESUME:
|
||||
return "Resume";
|
||||
default:
|
||||
return "UNKNOWN link state\n";
|
||||
}
|
||||
}
|
||||
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param)
|
||||
{
|
||||
u32 timeout = 500;
|
||||
u32 reg;
|
||||
|
||||
dev_vdbg(dwc->dev, "generic cmd '%s' [%d] param %08x\n",
|
||||
dwc3_gadget_generic_cmd_string(cmd), cmd, param);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
|
||||
dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
|
||||
|
||||
|
|
@ -332,9 +397,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
|||
u32 timeout = 500;
|
||||
u32 reg;
|
||||
|
||||
dev_vdbg(dwc->dev, "%s: cmd '%s' params %08x %08x %08x\n",
|
||||
dev_vdbg(dwc->dev, "%s: cmd '%s' [%d] params %08x %08x %08x\n",
|
||||
dep->name,
|
||||
dwc3_gadget_ep_cmd_string(cmd), params->param0,
|
||||
dwc3_gadget_ep_cmd_string(cmd), cmd, params->param0,
|
||||
params->param1, params->param2);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0);
|
||||
|
|
@ -515,7 +580,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
|||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 reg;
|
||||
int ret = -ENOMEM;
|
||||
int ret;
|
||||
|
||||
dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
|
||||
|
||||
|
|
@ -604,6 +669,10 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
|
|||
|
||||
dwc3_remove_requests(dwc, dep);
|
||||
|
||||
/* make sure HW endpoint isn't stalled */
|
||||
if (dep->flags & DWC3_EP_STALL)
|
||||
__dwc3_gadget_ep_set_halt(dep, 0);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
|
||||
reg &= ~DWC3_DALEPENA_EP(dep->number);
|
||||
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
|
||||
|
|
@ -2441,8 +2510,6 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
|||
}
|
||||
}
|
||||
|
||||
dwc->link_state = next;
|
||||
|
||||
switch (next) {
|
||||
case DWC3_LINK_STATE_U1:
|
||||
if (dwc->speed == USB_SPEED_SUPER)
|
||||
|
|
@ -2460,7 +2527,11 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
|||
break;
|
||||
}
|
||||
|
||||
dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
|
||||
dev_vdbg(dwc->dev, "link change: %s [%d] -> %s [%d]\n",
|
||||
dwc3_gadget_link_string(dwc->link_state),
|
||||
dwc->link_state, dwc3_gadget_link_string(next), next);
|
||||
|
||||
dwc->link_state = next;
|
||||
}
|
||||
|
||||
static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ config USB_AT91
|
|||
|
||||
config USB_LPC32XX
|
||||
tristate "LPC32XX USB Peripheral Controller"
|
||||
depends on ARCH_LPC32XX
|
||||
depends on ARCH_LPC32XX && I2C
|
||||
select USB_ISP1301
|
||||
help
|
||||
This option selects the USB device controller in the LPC32xx SoC.
|
||||
|
|
@ -226,7 +226,7 @@ config USB_GR_UDC
|
|||
config USB_OMAP
|
||||
tristate "OMAP USB Device Controller"
|
||||
depends on ARCH_OMAP1
|
||||
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
|
||||
depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3)
|
||||
help
|
||||
Many Texas Instruments OMAP processors have flexible full
|
||||
speed USB device controllers, with support for up to 30
|
||||
|
|
@ -300,12 +300,6 @@ config USB_PXA27X
|
|||
dynamically linked module called "pxa27x_udc" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_S3C_HSOTG
|
||||
tristate "Designware/S3C HS/OtG USB Device controller"
|
||||
help
|
||||
The Designware USB2.0 high-speed gadget controller
|
||||
integrated into many SoCs.
|
||||
|
||||
config USB_S3C2410
|
||||
tristate "S3C2410 USB Device Controller"
|
||||
depends on ARCH_S3C24XX
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
|
|||
obj-$(CONFIG_USB_M66592) += m66592-udc.o
|
||||
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
|
||||
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
|
||||
obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o
|
||||
obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
|
||||
obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o
|
||||
obj-$(CONFIG_USB_EG20T) += pch_udc.o
|
||||
|
|
|
|||
|
|
@ -1686,7 +1686,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
|||
reset_all_endpoints(udc);
|
||||
|
||||
if (udc->gadget.speed != USB_SPEED_UNKNOWN
|
||||
&& udc->driver->disconnect) {
|
||||
&& udc->driver && udc->driver->disconnect) {
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
spin_unlock(&udc->lock);
|
||||
udc->driver->disconnect(&udc->gadget);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,24 @@
|
|||
#include <linux/usb/composite.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "u_os_desc.h"
|
||||
|
||||
/**
|
||||
* struct usb_os_string - represents OS String to be reported by a gadget
|
||||
* @bLength: total length of the entire descritor, always 0x12
|
||||
* @bDescriptorType: USB_DT_STRING
|
||||
* @qwSignature: the OS String proper
|
||||
* @bMS_VendorCode: code used by the host for subsequent requests
|
||||
* @bPad: not used, must be zero
|
||||
*/
|
||||
struct usb_os_string {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 qwSignature[OS_STRING_QW_SIGN_LEN];
|
||||
__u8 bMS_VendorCode;
|
||||
__u8 bPad;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* The code in this file is utility code, used to build a gadget driver
|
||||
* from one or more "function" drivers, one or more "configuration"
|
||||
|
|
@ -422,6 +440,7 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
|
|||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct usb_configuration *c;
|
||||
struct list_head *pos;
|
||||
u8 type = w_value >> 8;
|
||||
enum usb_device_speed speed = USB_SPEED_UNKNOWN;
|
||||
|
||||
|
|
@ -440,7 +459,20 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
|
|||
|
||||
/* This is a lookup by config *INDEX* */
|
||||
w_value &= 0xff;
|
||||
list_for_each_entry(c, &cdev->configs, list) {
|
||||
|
||||
pos = &cdev->configs;
|
||||
c = cdev->os_desc_config;
|
||||
if (c)
|
||||
goto check_config;
|
||||
|
||||
while ((pos = pos->next) != &cdev->configs) {
|
||||
c = list_entry(pos, typeof(*c), list);
|
||||
|
||||
/* skip OS Descriptors config which is handled separately */
|
||||
if (c == cdev->os_desc_config)
|
||||
continue;
|
||||
|
||||
check_config:
|
||||
/* ignore configs that won't work at this speed */
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
|
|
@ -634,6 +666,7 @@ static int set_config(struct usb_composite_dev *cdev,
|
|||
if (!c)
|
||||
goto done;
|
||||
|
||||
usb_gadget_set_state(gadget, USB_STATE_CONFIGURED);
|
||||
cdev->config = c;
|
||||
|
||||
/* Initialize all interfaces by setting them to altsetting zero. */
|
||||
|
|
@ -960,6 +993,19 @@ static int get_string(struct usb_composite_dev *cdev,
|
|||
return s->bLength;
|
||||
}
|
||||
|
||||
if (cdev->use_os_string && language == 0 && id == OS_STRING_IDX) {
|
||||
struct usb_os_string *b = buf;
|
||||
b->bLength = sizeof(*b);
|
||||
b->bDescriptorType = USB_DT_STRING;
|
||||
compiletime_assert(
|
||||
sizeof(b->qwSignature) == sizeof(cdev->qw_sign),
|
||||
"qwSignature size must be equal to qw_sign");
|
||||
memcpy(&b->qwSignature, cdev->qw_sign, sizeof(b->qwSignature));
|
||||
b->bMS_VendorCode = cdev->b_vendor_code;
|
||||
b->bPad = 0;
|
||||
return sizeof(*b);
|
||||
}
|
||||
|
||||
list_for_each_entry(uc, &cdev->gstrings, list) {
|
||||
struct usb_gadget_strings **sp;
|
||||
|
||||
|
|
@ -1206,6 +1252,156 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
req->status, req->actual, req->length);
|
||||
}
|
||||
|
||||
static int count_ext_compat(struct usb_configuration *c)
|
||||
{
|
||||
int i, res;
|
||||
|
||||
res = 0;
|
||||
for (i = 0; i < c->next_interface_id; ++i) {
|
||||
struct usb_function *f;
|
||||
int j;
|
||||
|
||||
f = c->interface[i];
|
||||
for (j = 0; j < f->os_desc_n; ++j) {
|
||||
struct usb_os_desc *d;
|
||||
|
||||
if (i != f->os_desc_table[j].if_id)
|
||||
continue;
|
||||
d = f->os_desc_table[j].os_desc;
|
||||
if (d && d->ext_compat_id)
|
||||
++res;
|
||||
}
|
||||
}
|
||||
BUG_ON(res > 255);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void fill_ext_compat(struct usb_configuration *c, u8 *buf)
|
||||
{
|
||||
int i, count;
|
||||
|
||||
count = 16;
|
||||
for (i = 0; i < c->next_interface_id; ++i) {
|
||||
struct usb_function *f;
|
||||
int j;
|
||||
|
||||
f = c->interface[i];
|
||||
for (j = 0; j < f->os_desc_n; ++j) {
|
||||
struct usb_os_desc *d;
|
||||
|
||||
if (i != f->os_desc_table[j].if_id)
|
||||
continue;
|
||||
d = f->os_desc_table[j].os_desc;
|
||||
if (d && d->ext_compat_id) {
|
||||
*buf++ = i;
|
||||
*buf++ = 0x01;
|
||||
memcpy(buf, d->ext_compat_id, 16);
|
||||
buf += 22;
|
||||
} else {
|
||||
++buf;
|
||||
*buf = 0x01;
|
||||
buf += 23;
|
||||
}
|
||||
count += 24;
|
||||
if (count >= 4096)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int count_ext_prop(struct usb_configuration *c, int interface)
|
||||
{
|
||||
struct usb_function *f;
|
||||
int j;
|
||||
|
||||
f = c->interface[interface];
|
||||
for (j = 0; j < f->os_desc_n; ++j) {
|
||||
struct usb_os_desc *d;
|
||||
|
||||
if (interface != f->os_desc_table[j].if_id)
|
||||
continue;
|
||||
d = f->os_desc_table[j].os_desc;
|
||||
if (d && d->ext_compat_id)
|
||||
return d->ext_prop_count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int len_ext_prop(struct usb_configuration *c, int interface)
|
||||
{
|
||||
struct usb_function *f;
|
||||
struct usb_os_desc *d;
|
||||
int j, res;
|
||||
|
||||
res = 10; /* header length */
|
||||
f = c->interface[interface];
|
||||
for (j = 0; j < f->os_desc_n; ++j) {
|
||||
if (interface != f->os_desc_table[j].if_id)
|
||||
continue;
|
||||
d = f->os_desc_table[j].os_desc;
|
||||
if (d)
|
||||
return min(res + d->ext_prop_len, 4096);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf)
|
||||
{
|
||||
struct usb_function *f;
|
||||
struct usb_os_desc *d;
|
||||
struct usb_os_desc_ext_prop *ext_prop;
|
||||
int j, count, n, ret;
|
||||
u8 *start = buf;
|
||||
|
||||
f = c->interface[interface];
|
||||
for (j = 0; j < f->os_desc_n; ++j) {
|
||||
if (interface != f->os_desc_table[j].if_id)
|
||||
continue;
|
||||
d = f->os_desc_table[j].os_desc;
|
||||
if (d)
|
||||
list_for_each_entry(ext_prop, &d->ext_prop, entry) {
|
||||
/* 4kB minus header length */
|
||||
n = buf - start;
|
||||
if (n >= 4086)
|
||||
return 0;
|
||||
|
||||
count = ext_prop->data_len +
|
||||
ext_prop->name_len + 14;
|
||||
if (count > 4086 - n)
|
||||
return -EINVAL;
|
||||
usb_ext_prop_put_size(buf, count);
|
||||
usb_ext_prop_put_type(buf, ext_prop->type);
|
||||
ret = usb_ext_prop_put_name(buf, ext_prop->name,
|
||||
ext_prop->name_len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
switch (ext_prop->type) {
|
||||
case USB_EXT_PROP_UNICODE:
|
||||
case USB_EXT_PROP_UNICODE_ENV:
|
||||
case USB_EXT_PROP_UNICODE_LINK:
|
||||
usb_ext_prop_put_unicode(buf, ret,
|
||||
ext_prop->data,
|
||||
ext_prop->data_len);
|
||||
break;
|
||||
case USB_EXT_PROP_BINARY:
|
||||
usb_ext_prop_put_binary(buf, ret,
|
||||
ext_prop->data,
|
||||
ext_prop->data_len);
|
||||
break;
|
||||
case USB_EXT_PROP_LE32:
|
||||
/* not implemented */
|
||||
case USB_EXT_PROP_BE32:
|
||||
/* not implemented */
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
buf += count;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The setup() callback implements all the ep0 functionality that's
|
||||
* not handled lower down, in hardware or the hardware driver(like
|
||||
|
|
@ -1415,6 +1611,91 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||
break;
|
||||
default:
|
||||
unknown:
|
||||
/*
|
||||
* OS descriptors handling
|
||||
*/
|
||||
if (cdev->use_os_string && cdev->os_desc_config &&
|
||||
(ctrl->bRequest & USB_TYPE_VENDOR) &&
|
||||
ctrl->bRequest == cdev->b_vendor_code) {
|
||||
struct usb_request *req;
|
||||
struct usb_configuration *os_desc_cfg;
|
||||
u8 *buf;
|
||||
int interface;
|
||||
int count = 0;
|
||||
|
||||
req = cdev->os_desc_req;
|
||||
req->complete = composite_setup_complete;
|
||||
buf = req->buf;
|
||||
os_desc_cfg = cdev->os_desc_config;
|
||||
memset(buf, 0, w_length);
|
||||
buf[5] = 0x01;
|
||||
switch (ctrl->bRequestType & USB_RECIP_MASK) {
|
||||
case USB_RECIP_DEVICE:
|
||||
if (w_index != 0x4 || (w_value >> 8))
|
||||
break;
|
||||
buf[6] = w_index;
|
||||
if (w_length == 0x10) {
|
||||
/* Number of ext compat interfaces */
|
||||
count = count_ext_compat(os_desc_cfg);
|
||||
buf[8] = count;
|
||||
count *= 24; /* 24 B/ext compat desc */
|
||||
count += 16; /* header */
|
||||
put_unaligned_le32(count, buf);
|
||||
value = w_length;
|
||||
} else {
|
||||
/* "extended compatibility ID"s */
|
||||
count = count_ext_compat(os_desc_cfg);
|
||||
buf[8] = count;
|
||||
count *= 24; /* 24 B/ext compat desc */
|
||||
count += 16; /* header */
|
||||
put_unaligned_le32(count, buf);
|
||||
buf += 16;
|
||||
fill_ext_compat(os_desc_cfg, buf);
|
||||
value = w_length;
|
||||
}
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
if (w_index != 0x5 || (w_value >> 8))
|
||||
break;
|
||||
interface = w_value & 0xFF;
|
||||
buf[6] = w_index;
|
||||
if (w_length == 0x0A) {
|
||||
count = count_ext_prop(os_desc_cfg,
|
||||
interface);
|
||||
put_unaligned_le16(count, buf + 8);
|
||||
count = len_ext_prop(os_desc_cfg,
|
||||
interface);
|
||||
put_unaligned_le32(count, buf);
|
||||
|
||||
value = w_length;
|
||||
} else {
|
||||
count = count_ext_prop(os_desc_cfg,
|
||||
interface);
|
||||
put_unaligned_le16(count, buf + 8);
|
||||
count = len_ext_prop(os_desc_cfg,
|
||||
interface);
|
||||
put_unaligned_le32(count, buf);
|
||||
buf += 10;
|
||||
value = fill_ext_prop(os_desc_cfg,
|
||||
interface, buf);
|
||||
if (value < 0)
|
||||
return value;
|
||||
|
||||
value = w_length;
|
||||
}
|
||||
break;
|
||||
}
|
||||
req->length = value;
|
||||
req->zero = value < w_length;
|
||||
value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
|
||||
if (value < 0) {
|
||||
DBG(cdev, "ep_queue --> %d\n", value);
|
||||
req->status = 0;
|
||||
composite_setup_complete(gadget->ep0, req);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
VDBG(cdev,
|
||||
"non-core control req%02x.%02x v%04x i%04x l%d\n",
|
||||
ctrl->bRequestType, ctrl->bRequest,
|
||||
|
|
@ -1638,6 +1919,29 @@ fail:
|
|||
return ret;
|
||||
}
|
||||
|
||||
int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
|
||||
struct usb_ep *ep0)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
cdev->os_desc_req = usb_ep_alloc_request(ep0, GFP_KERNEL);
|
||||
if (!cdev->os_desc_req) {
|
||||
ret = PTR_ERR(cdev->os_desc_req);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* OS feature descriptor length <= 4kB */
|
||||
cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL);
|
||||
if (!cdev->os_desc_req->buf) {
|
||||
ret = PTR_ERR(cdev->os_desc_req->buf);
|
||||
kfree(cdev->os_desc_req);
|
||||
goto end;
|
||||
}
|
||||
cdev->os_desc_req->complete = composite_setup_complete;
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void composite_dev_cleanup(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget_string_container *uc, *tmp;
|
||||
|
|
@ -1646,6 +1950,10 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
|
|||
list_del(&uc->list);
|
||||
kfree(uc);
|
||||
}
|
||||
if (cdev->os_desc_req) {
|
||||
kfree(cdev->os_desc_req->buf);
|
||||
usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req);
|
||||
}
|
||||
if (cdev->req) {
|
||||
kfree(cdev->req->buf);
|
||||
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
|
||||
|
|
@ -1683,6 +1991,12 @@ static int composite_bind(struct usb_gadget *gadget,
|
|||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
if (cdev->use_os_string) {
|
||||
status = composite_os_desc_req_prepare(cdev, gadget->ep0);
|
||||
if (status)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
update_unchanged_dev_desc(&cdev->desc, composite->dev);
|
||||
|
||||
/* has userspace failed to provide a serial number? */
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/gadget_configfs.h>
|
||||
#include "configfs.h"
|
||||
#include "u_f.h"
|
||||
#include "u_os_desc.h"
|
||||
|
||||
int check_user_usb_string(const char *name,
|
||||
struct usb_gadget_strings *stringtab_dev)
|
||||
|
|
@ -43,7 +46,8 @@ struct gadget_info {
|
|||
struct config_group functions_group;
|
||||
struct config_group configs_group;
|
||||
struct config_group strings_group;
|
||||
struct config_group *default_groups[4];
|
||||
struct config_group os_desc_group;
|
||||
struct config_group *default_groups[5];
|
||||
|
||||
struct mutex lock;
|
||||
struct usb_gadget_strings *gstrings[MAX_USB_STRING_LANGS + 1];
|
||||
|
|
@ -56,6 +60,9 @@ struct gadget_info {
|
|||
#endif
|
||||
struct usb_composite_driver composite;
|
||||
struct usb_composite_dev cdev;
|
||||
bool use_os_desc;
|
||||
char b_vendor_code;
|
||||
char qw_sign[OS_STRING_QW_SIGN_LEN];
|
||||
};
|
||||
|
||||
struct config_usb_cfg {
|
||||
|
|
@ -79,6 +86,10 @@ struct gadget_strings {
|
|||
struct list_head list;
|
||||
};
|
||||
|
||||
struct os_desc {
|
||||
struct config_group group;
|
||||
};
|
||||
|
||||
struct gadget_config_name {
|
||||
struct usb_gadget_strings stringtab_dev;
|
||||
struct usb_string strings;
|
||||
|
|
@ -736,6 +747,525 @@ static void gadget_strings_attr_release(struct config_item *item)
|
|||
USB_CONFIG_STRING_RW_OPS(gadget_strings);
|
||||
USB_CONFIG_STRINGS_LANG(gadget_strings, gadget_info);
|
||||
|
||||
static inline struct os_desc *to_os_desc(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct os_desc, group);
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR_STRUCT(os_desc);
|
||||
CONFIGFS_ATTR_OPS(os_desc);
|
||||
|
||||
static ssize_t os_desc_use_show(struct os_desc *os_desc, char *page)
|
||||
{
|
||||
struct gadget_info *gi;
|
||||
|
||||
gi = to_gadget_info(os_desc->group.cg_item.ci_parent);
|
||||
|
||||
return sprintf(page, "%d", gi->use_os_desc);
|
||||
}
|
||||
|
||||
static ssize_t os_desc_use_store(struct os_desc *os_desc, const char *page,
|
||||
size_t len)
|
||||
{
|
||||
struct gadget_info *gi;
|
||||
int ret;
|
||||
bool use;
|
||||
|
||||
gi = to_gadget_info(os_desc->group.cg_item.ci_parent);
|
||||
|
||||
mutex_lock(&gi->lock);
|
||||
ret = strtobool(page, &use);
|
||||
if (!ret) {
|
||||
gi->use_os_desc = use;
|
||||
ret = len;
|
||||
}
|
||||
mutex_unlock(&gi->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct os_desc_attribute os_desc_use =
|
||||
__CONFIGFS_ATTR(use, S_IRUGO | S_IWUSR,
|
||||
os_desc_use_show,
|
||||
os_desc_use_store);
|
||||
|
||||
static ssize_t os_desc_b_vendor_code_show(struct os_desc *os_desc, char *page)
|
||||
{
|
||||
struct gadget_info *gi;
|
||||
|
||||
gi = to_gadget_info(os_desc->group.cg_item.ci_parent);
|
||||
|
||||
return sprintf(page, "%d", gi->b_vendor_code);
|
||||
}
|
||||
|
||||
static ssize_t os_desc_b_vendor_code_store(struct os_desc *os_desc,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
struct gadget_info *gi;
|
||||
int ret;
|
||||
u8 b_vendor_code;
|
||||
|
||||
gi = to_gadget_info(os_desc->group.cg_item.ci_parent);
|
||||
|
||||
mutex_lock(&gi->lock);
|
||||
ret = kstrtou8(page, 0, &b_vendor_code);
|
||||
if (!ret) {
|
||||
gi->b_vendor_code = b_vendor_code;
|
||||
ret = len;
|
||||
}
|
||||
mutex_unlock(&gi->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct os_desc_attribute os_desc_b_vendor_code =
|
||||
__CONFIGFS_ATTR(b_vendor_code, S_IRUGO | S_IWUSR,
|
||||
os_desc_b_vendor_code_show,
|
||||
os_desc_b_vendor_code_store);
|
||||
|
||||
static ssize_t os_desc_qw_sign_show(struct os_desc *os_desc, char *page)
|
||||
{
|
||||
struct gadget_info *gi;
|
||||
|
||||
gi = to_gadget_info(os_desc->group.cg_item.ci_parent);
|
||||
|
||||
memcpy(page, gi->qw_sign, OS_STRING_QW_SIGN_LEN);
|
||||
|
||||
return OS_STRING_QW_SIGN_LEN;
|
||||
}
|
||||
|
||||
static ssize_t os_desc_qw_sign_store(struct os_desc *os_desc, const char *page,
|
||||
size_t len)
|
||||
{
|
||||
struct gadget_info *gi;
|
||||
int res, l;
|
||||
|
||||
gi = to_gadget_info(os_desc->group.cg_item.ci_parent);
|
||||
l = min((int)len, OS_STRING_QW_SIGN_LEN >> 1);
|
||||
if (page[l - 1] == '\n')
|
||||
--l;
|
||||
|
||||
mutex_lock(&gi->lock);
|
||||
res = utf8s_to_utf16s(page, l,
|
||||
UTF16_LITTLE_ENDIAN, (wchar_t *) gi->qw_sign,
|
||||
OS_STRING_QW_SIGN_LEN);
|
||||
if (res > 0)
|
||||
res = len;
|
||||
mutex_unlock(&gi->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct os_desc_attribute os_desc_qw_sign =
|
||||
__CONFIGFS_ATTR(qw_sign, S_IRUGO | S_IWUSR,
|
||||
os_desc_qw_sign_show,
|
||||
os_desc_qw_sign_store);
|
||||
|
||||
static struct configfs_attribute *os_desc_attrs[] = {
|
||||
&os_desc_use.attr,
|
||||
&os_desc_b_vendor_code.attr,
|
||||
&os_desc_qw_sign.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void os_desc_attr_release(struct config_item *item)
|
||||
{
|
||||
struct os_desc *os_desc = to_os_desc(item);
|
||||
kfree(os_desc);
|
||||
}
|
||||
|
||||
static int os_desc_link(struct config_item *os_desc_ci,
|
||||
struct config_item *usb_cfg_ci)
|
||||
{
|
||||
struct gadget_info *gi = container_of(to_config_group(os_desc_ci),
|
||||
struct gadget_info, os_desc_group);
|
||||
struct usb_composite_dev *cdev = &gi->cdev;
|
||||
struct config_usb_cfg *c_target =
|
||||
container_of(to_config_group(usb_cfg_ci),
|
||||
struct config_usb_cfg, group);
|
||||
struct usb_configuration *c;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&gi->lock);
|
||||
list_for_each_entry(c, &cdev->configs, list) {
|
||||
if (c == &c_target->c)
|
||||
break;
|
||||
}
|
||||
if (c != &c_target->c) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cdev->os_desc_config) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cdev->os_desc_config = &c_target->c;
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&gi->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int os_desc_unlink(struct config_item *os_desc_ci,
|
||||
struct config_item *usb_cfg_ci)
|
||||
{
|
||||
struct gadget_info *gi = container_of(to_config_group(os_desc_ci),
|
||||
struct gadget_info, os_desc_group);
|
||||
struct usb_composite_dev *cdev = &gi->cdev;
|
||||
|
||||
mutex_lock(&gi->lock);
|
||||
if (gi->udc_name)
|
||||
unregister_gadget(gi);
|
||||
cdev->os_desc_config = NULL;
|
||||
WARN_ON(gi->udc_name);
|
||||
mutex_unlock(&gi->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct configfs_item_operations os_desc_ops = {
|
||||
.release = os_desc_attr_release,
|
||||
.show_attribute = os_desc_attr_show,
|
||||
.store_attribute = os_desc_attr_store,
|
||||
.allow_link = os_desc_link,
|
||||
.drop_link = os_desc_unlink,
|
||||
};
|
||||
|
||||
static struct config_item_type os_desc_type = {
|
||||
.ct_item_ops = &os_desc_ops,
|
||||
.ct_attrs = os_desc_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
CONFIGFS_ATTR_STRUCT(usb_os_desc);
|
||||
CONFIGFS_ATTR_OPS(usb_os_desc);
|
||||
|
||||
|
||||
static inline struct usb_os_desc_ext_prop
|
||||
*to_usb_os_desc_ext_prop(struct config_item *item)
|
||||
{
|
||||
return container_of(item, struct usb_os_desc_ext_prop, item);
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR_STRUCT(usb_os_desc_ext_prop);
|
||||
CONFIGFS_ATTR_OPS(usb_os_desc_ext_prop);
|
||||
|
||||
static ssize_t ext_prop_type_show(struct usb_os_desc_ext_prop *ext_prop,
|
||||
char *page)
|
||||
{
|
||||
return sprintf(page, "%d", ext_prop->type);
|
||||
}
|
||||
|
||||
static ssize_t ext_prop_type_store(struct usb_os_desc_ext_prop *ext_prop,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent);
|
||||
u8 type;
|
||||
int ret;
|
||||
|
||||
if (desc->opts_mutex)
|
||||
mutex_lock(desc->opts_mutex);
|
||||
ret = kstrtou8(page, 0, &type);
|
||||
if (ret)
|
||||
goto end;
|
||||
if (type < USB_EXT_PROP_UNICODE || type > USB_EXT_PROP_UNICODE_MULTI) {
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((ext_prop->type == USB_EXT_PROP_BINARY ||
|
||||
ext_prop->type == USB_EXT_PROP_LE32 ||
|
||||
ext_prop->type == USB_EXT_PROP_BE32) &&
|
||||
(type == USB_EXT_PROP_UNICODE ||
|
||||
type == USB_EXT_PROP_UNICODE_ENV ||
|
||||
type == USB_EXT_PROP_UNICODE_LINK))
|
||||
ext_prop->data_len <<= 1;
|
||||
else if ((ext_prop->type == USB_EXT_PROP_UNICODE ||
|
||||
ext_prop->type == USB_EXT_PROP_UNICODE_ENV ||
|
||||
ext_prop->type == USB_EXT_PROP_UNICODE_LINK) &&
|
||||
(type == USB_EXT_PROP_BINARY ||
|
||||
type == USB_EXT_PROP_LE32 ||
|
||||
type == USB_EXT_PROP_BE32))
|
||||
ext_prop->data_len >>= 1;
|
||||
ext_prop->type = type;
|
||||
ret = len;
|
||||
|
||||
end:
|
||||
if (desc->opts_mutex)
|
||||
mutex_unlock(desc->opts_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ext_prop_data_show(struct usb_os_desc_ext_prop *ext_prop,
|
||||
char *page)
|
||||
{
|
||||
int len = ext_prop->data_len;
|
||||
|
||||
if (ext_prop->type == USB_EXT_PROP_UNICODE ||
|
||||
ext_prop->type == USB_EXT_PROP_UNICODE_ENV ||
|
||||
ext_prop->type == USB_EXT_PROP_UNICODE_LINK)
|
||||
len >>= 1;
|
||||
memcpy(page, ext_prop->data, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t ext_prop_data_store(struct usb_os_desc_ext_prop *ext_prop,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent);
|
||||
char *new_data;
|
||||
size_t ret_len = len;
|
||||
|
||||
if (page[len - 1] == '\n' || page[len - 1] == '\0')
|
||||
--len;
|
||||
new_data = kzalloc(len, GFP_KERNEL);
|
||||
if (!new_data)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(new_data, page, len);
|
||||
|
||||
if (desc->opts_mutex)
|
||||
mutex_lock(desc->opts_mutex);
|
||||
kfree(ext_prop->data);
|
||||
ext_prop->data = new_data;
|
||||
desc->ext_prop_len -= ext_prop->data_len;
|
||||
ext_prop->data_len = len;
|
||||
desc->ext_prop_len += ext_prop->data_len;
|
||||
if (ext_prop->type == USB_EXT_PROP_UNICODE ||
|
||||
ext_prop->type == USB_EXT_PROP_UNICODE_ENV ||
|
||||
ext_prop->type == USB_EXT_PROP_UNICODE_LINK) {
|
||||
desc->ext_prop_len -= ext_prop->data_len;
|
||||
ext_prop->data_len <<= 1;
|
||||
ext_prop->data_len += 2;
|
||||
desc->ext_prop_len += ext_prop->data_len;
|
||||
}
|
||||
if (desc->opts_mutex)
|
||||
mutex_unlock(desc->opts_mutex);
|
||||
return ret_len;
|
||||
}
|
||||
|
||||
static struct usb_os_desc_ext_prop_attribute ext_prop_type =
|
||||
__CONFIGFS_ATTR(type, S_IRUGO | S_IWUSR,
|
||||
ext_prop_type_show, ext_prop_type_store);
|
||||
|
||||
static struct usb_os_desc_ext_prop_attribute ext_prop_data =
|
||||
__CONFIGFS_ATTR(data, S_IRUGO | S_IWUSR,
|
||||
ext_prop_data_show, ext_prop_data_store);
|
||||
|
||||
static struct configfs_attribute *ext_prop_attrs[] = {
|
||||
&ext_prop_type.attr,
|
||||
&ext_prop_data.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void usb_os_desc_ext_prop_release(struct config_item *item)
|
||||
{
|
||||
struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item);
|
||||
|
||||
kfree(ext_prop); /* frees a whole chunk */
|
||||
}
|
||||
|
||||
static struct configfs_item_operations ext_prop_ops = {
|
||||
.release = usb_os_desc_ext_prop_release,
|
||||
.show_attribute = usb_os_desc_ext_prop_attr_show,
|
||||
.store_attribute = usb_os_desc_ext_prop_attr_store,
|
||||
};
|
||||
|
||||
static struct config_item *ext_prop_make(
|
||||
struct config_group *group,
|
||||
const char *name)
|
||||
{
|
||||
struct usb_os_desc_ext_prop *ext_prop;
|
||||
struct config_item_type *ext_prop_type;
|
||||
struct usb_os_desc *desc;
|
||||
char *vlabuf;
|
||||
|
||||
vla_group(data_chunk);
|
||||
vla_item(data_chunk, struct usb_os_desc_ext_prop, ext_prop, 1);
|
||||
vla_item(data_chunk, struct config_item_type, ext_prop_type, 1);
|
||||
|
||||
vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL);
|
||||
if (!vlabuf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ext_prop = vla_ptr(vlabuf, data_chunk, ext_prop);
|
||||
ext_prop_type = vla_ptr(vlabuf, data_chunk, ext_prop_type);
|
||||
|
||||
desc = container_of(group, struct usb_os_desc, group);
|
||||
ext_prop_type->ct_item_ops = &ext_prop_ops;
|
||||
ext_prop_type->ct_attrs = ext_prop_attrs;
|
||||
ext_prop_type->ct_owner = desc->owner;
|
||||
|
||||
config_item_init_type_name(&ext_prop->item, name, ext_prop_type);
|
||||
|
||||
ext_prop->name = kstrdup(name, GFP_KERNEL);
|
||||
if (!ext_prop->name) {
|
||||
kfree(vlabuf);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
desc->ext_prop_len += 14;
|
||||
ext_prop->name_len = 2 * strlen(ext_prop->name) + 2;
|
||||
if (desc->opts_mutex)
|
||||
mutex_lock(desc->opts_mutex);
|
||||
desc->ext_prop_len += ext_prop->name_len;
|
||||
list_add_tail(&ext_prop->entry, &desc->ext_prop);
|
||||
++desc->ext_prop_count;
|
||||
if (desc->opts_mutex)
|
||||
mutex_unlock(desc->opts_mutex);
|
||||
|
||||
return &ext_prop->item;
|
||||
}
|
||||
|
||||
static void ext_prop_drop(struct config_group *group, struct config_item *item)
|
||||
{
|
||||
struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item);
|
||||
struct usb_os_desc *desc = to_usb_os_desc(&group->cg_item);
|
||||
|
||||
if (desc->opts_mutex)
|
||||
mutex_lock(desc->opts_mutex);
|
||||
list_del(&ext_prop->entry);
|
||||
--desc->ext_prop_count;
|
||||
kfree(ext_prop->name);
|
||||
desc->ext_prop_len -= (ext_prop->name_len + ext_prop->data_len + 14);
|
||||
if (desc->opts_mutex)
|
||||
mutex_unlock(desc->opts_mutex);
|
||||
config_item_put(item);
|
||||
}
|
||||
|
||||
static struct configfs_group_operations interf_grp_ops = {
|
||||
.make_item = &ext_prop_make,
|
||||
.drop_item = &ext_prop_drop,
|
||||
};
|
||||
|
||||
static struct configfs_item_operations interf_item_ops = {
|
||||
.show_attribute = usb_os_desc_attr_show,
|
||||
.store_attribute = usb_os_desc_attr_store,
|
||||
};
|
||||
|
||||
static ssize_t rndis_grp_compatible_id_show(struct usb_os_desc *desc,
|
||||
char *page)
|
||||
{
|
||||
memcpy(page, desc->ext_compat_id, 8);
|
||||
return 8;
|
||||
}
|
||||
|
||||
static ssize_t rndis_grp_compatible_id_store(struct usb_os_desc *desc,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
int l;
|
||||
|
||||
l = min_t(int, 8, len);
|
||||
if (page[l - 1] == '\n')
|
||||
--l;
|
||||
if (desc->opts_mutex)
|
||||
mutex_lock(desc->opts_mutex);
|
||||
memcpy(desc->ext_compat_id, page, l);
|
||||
desc->ext_compat_id[l] = '\0';
|
||||
|
||||
if (desc->opts_mutex)
|
||||
mutex_unlock(desc->opts_mutex);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static struct usb_os_desc_attribute rndis_grp_attr_compatible_id =
|
||||
__CONFIGFS_ATTR(compatible_id, S_IRUGO | S_IWUSR,
|
||||
rndis_grp_compatible_id_show,
|
||||
rndis_grp_compatible_id_store);
|
||||
|
||||
static ssize_t rndis_grp_sub_compatible_id_show(struct usb_os_desc *desc,
|
||||
char *page)
|
||||
{
|
||||
memcpy(page, desc->ext_compat_id + 8, 8);
|
||||
return 8;
|
||||
}
|
||||
|
||||
static ssize_t rndis_grp_sub_compatible_id_store(struct usb_os_desc *desc,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
int l;
|
||||
|
||||
l = min_t(int, 8, len);
|
||||
if (page[l - 1] == '\n')
|
||||
--l;
|
||||
if (desc->opts_mutex)
|
||||
mutex_lock(desc->opts_mutex);
|
||||
memcpy(desc->ext_compat_id + 8, page, l);
|
||||
desc->ext_compat_id[l + 8] = '\0';
|
||||
|
||||
if (desc->opts_mutex)
|
||||
mutex_unlock(desc->opts_mutex);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static struct usb_os_desc_attribute rndis_grp_attr_sub_compatible_id =
|
||||
__CONFIGFS_ATTR(sub_compatible_id, S_IRUGO | S_IWUSR,
|
||||
rndis_grp_sub_compatible_id_show,
|
||||
rndis_grp_sub_compatible_id_store);
|
||||
|
||||
static struct configfs_attribute *interf_grp_attrs[] = {
|
||||
&rndis_grp_attr_compatible_id.attr,
|
||||
&rndis_grp_attr_sub_compatible_id.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
int usb_os_desc_prepare_interf_dir(struct config_group *parent,
|
||||
int n_interf,
|
||||
struct usb_os_desc **desc,
|
||||
struct module *owner)
|
||||
{
|
||||
struct config_group **f_default_groups, *os_desc_group,
|
||||
**interface_groups;
|
||||
struct config_item_type *os_desc_type, *interface_type;
|
||||
|
||||
vla_group(data_chunk);
|
||||
vla_item(data_chunk, struct config_group *, f_default_groups, 2);
|
||||
vla_item(data_chunk, struct config_group, os_desc_group, 1);
|
||||
vla_item(data_chunk, struct config_group *, interface_groups,
|
||||
n_interf + 1);
|
||||
vla_item(data_chunk, struct config_item_type, os_desc_type, 1);
|
||||
vla_item(data_chunk, struct config_item_type, interface_type, 1);
|
||||
|
||||
char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL);
|
||||
if (!vlabuf)
|
||||
return -ENOMEM;
|
||||
|
||||
f_default_groups = vla_ptr(vlabuf, data_chunk, f_default_groups);
|
||||
os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group);
|
||||
os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type);
|
||||
interface_groups = vla_ptr(vlabuf, data_chunk, interface_groups);
|
||||
interface_type = vla_ptr(vlabuf, data_chunk, interface_type);
|
||||
|
||||
parent->default_groups = f_default_groups;
|
||||
os_desc_type->ct_owner = owner;
|
||||
config_group_init_type_name(os_desc_group, "os_desc", os_desc_type);
|
||||
f_default_groups[0] = os_desc_group;
|
||||
|
||||
os_desc_group->default_groups = interface_groups;
|
||||
interface_type->ct_item_ops = &interf_item_ops;
|
||||
interface_type->ct_group_ops = &interf_grp_ops;
|
||||
interface_type->ct_attrs = interf_grp_attrs;
|
||||
interface_type->ct_owner = owner;
|
||||
|
||||
while (n_interf--) {
|
||||
struct usb_os_desc *d;
|
||||
|
||||
d = desc[n_interf];
|
||||
d->owner = owner;
|
||||
config_group_init_type_name(&d->group, "", interface_type);
|
||||
config_item_set_name(&d->group.cg_item, "interface.%d",
|
||||
n_interf);
|
||||
interface_groups[n_interf] = &d->group;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir);
|
||||
|
||||
static int configfs_do_nothing(struct usb_composite_dev *cdev)
|
||||
{
|
||||
WARN_ON(1);
|
||||
|
|
@ -745,6 +1275,9 @@ static int configfs_do_nothing(struct usb_composite_dev *cdev)
|
|||
int composite_dev_prepare(struct usb_composite_driver *composite,
|
||||
struct usb_composite_dev *dev);
|
||||
|
||||
int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
|
||||
struct usb_ep *ep0);
|
||||
|
||||
static void purge_configs_funcs(struct gadget_info *gi)
|
||||
{
|
||||
struct usb_configuration *c;
|
||||
|
|
@ -793,7 +1326,7 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
|
|||
ret = -EINVAL;
|
||||
|
||||
if (list_empty(&gi->cdev.configs)) {
|
||||
pr_err("Need atleast one configuration in %s.\n",
|
||||
pr_err("Need at least one configuration in %s.\n",
|
||||
gi->composite.name);
|
||||
goto err_comp_cleanup;
|
||||
}
|
||||
|
|
@ -804,7 +1337,7 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
|
|||
|
||||
cfg = container_of(c, struct config_usb_cfg, c);
|
||||
if (list_empty(&cfg->func_list)) {
|
||||
pr_err("Config %s/%d of %s needs atleast one function.\n",
|
||||
pr_err("Config %s/%d of %s needs at least one function.\n",
|
||||
c->label, c->bConfigurationValue,
|
||||
gi->composite.name);
|
||||
goto err_comp_cleanup;
|
||||
|
|
@ -839,6 +1372,12 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
|
|||
gi->cdev.desc.iSerialNumber = s[USB_GADGET_SERIAL_IDX].id;
|
||||
}
|
||||
|
||||
if (gi->use_os_desc) {
|
||||
cdev->use_os_string = true;
|
||||
cdev->b_vendor_code = gi->b_vendor_code;
|
||||
memcpy(cdev->qw_sign, gi->qw_sign, OS_STRING_QW_SIGN_LEN);
|
||||
}
|
||||
|
||||
/* Go through all configs, attach all functions */
|
||||
list_for_each_entry(c, &gi->cdev.configs, list) {
|
||||
struct config_usb_cfg *cfg;
|
||||
|
|
@ -874,6 +1413,12 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
|
|||
}
|
||||
usb_ep_autoconfig_reset(cdev->gadget);
|
||||
}
|
||||
if (cdev->use_os_string) {
|
||||
ret = composite_os_desc_req_prepare(cdev, gadget->ep0);
|
||||
if (ret)
|
||||
goto err_purge_funcs;
|
||||
}
|
||||
|
||||
usb_ep_autoconfig_reset(cdev->gadget);
|
||||
return 0;
|
||||
|
||||
|
|
@ -929,6 +1474,7 @@ static struct config_group *gadgets_make(
|
|||
gi->group.default_groups[0] = &gi->functions_group;
|
||||
gi->group.default_groups[1] = &gi->configs_group;
|
||||
gi->group.default_groups[2] = &gi->strings_group;
|
||||
gi->group.default_groups[3] = &gi->os_desc_group;
|
||||
|
||||
config_group_init_type_name(&gi->functions_group, "functions",
|
||||
&functions_type);
|
||||
|
|
@ -936,6 +1482,8 @@ static struct config_group *gadgets_make(
|
|||
&config_desc_type);
|
||||
config_group_init_type_name(&gi->strings_group, "strings",
|
||||
&gadget_strings_strings_type);
|
||||
config_group_init_type_name(&gi->os_desc_group, "os_desc",
|
||||
&os_desc_type);
|
||||
|
||||
gi->composite.bind = configfs_do_nothing;
|
||||
gi->composite.unbind = configfs_do_nothing;
|
||||
|
|
@ -1005,7 +1553,7 @@ void unregister_gadget_item(struct config_item *item)
|
|||
|
||||
unregister_gadget(gi);
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_gadget_item);
|
||||
EXPORT_SYMBOL_GPL(unregister_gadget_item);
|
||||
|
||||
static int __init gadget_cfs_init(void)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,18 @@
|
|||
#ifndef USB__GADGET__CONFIGFS__H
|
||||
#define USB__GADGET__CONFIGFS__H
|
||||
|
||||
#include <linux/configfs.h>
|
||||
|
||||
void unregister_gadget_item(struct config_item *item);
|
||||
|
||||
int usb_os_desc_prepare_interf_dir(struct config_group *parent,
|
||||
int n_interf,
|
||||
struct usb_os_desc **desc,
|
||||
struct module *owner);
|
||||
|
||||
static inline struct usb_os_desc *to_usb_os_desc(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct usb_os_desc, group);
|
||||
}
|
||||
|
||||
#endif /* USB__GADGET__CONFIGFS__H */
|
||||
|
|
|
|||
|
|
@ -561,7 +561,6 @@ static int dummy_disable(struct usb_ep *_ep)
|
|||
struct dummy_ep *ep;
|
||||
struct dummy *dum;
|
||||
unsigned long flags;
|
||||
int retval;
|
||||
|
||||
ep = usb_ep_to_dummy_ep(_ep);
|
||||
if (!_ep || !ep->desc || _ep->name == ep0name)
|
||||
|
|
@ -571,12 +570,11 @@ static int dummy_disable(struct usb_ep *_ep)
|
|||
spin_lock_irqsave(&dum->lock, flags);
|
||||
ep->desc = NULL;
|
||||
ep->stream_en = 0;
|
||||
retval = 0;
|
||||
nuke(dum, ep);
|
||||
spin_unlock_irqrestore(&dum->lock, flags);
|
||||
|
||||
dev_dbg(udc_dev(dum), "disabled %s\n", _ep->name);
|
||||
return retval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_request *dummy_alloc_request(struct usb_ep *_ep,
|
||||
|
|
|
|||
|
|
@ -33,36 +33,11 @@
|
|||
#include <linux/poll.h>
|
||||
|
||||
#include "u_fs.h"
|
||||
#include "u_f.h"
|
||||
#include "configfs.h"
|
||||
|
||||
#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
|
||||
|
||||
/* Variable Length Array Macros **********************************************/
|
||||
#define vla_group(groupname) size_t groupname##__next = 0
|
||||
#define vla_group_size(groupname) groupname##__next
|
||||
|
||||
#define vla_item(groupname, type, name, n) \
|
||||
size_t groupname##_##name##__offset = ({ \
|
||||
size_t align_mask = __alignof__(type) - 1; \
|
||||
size_t offset = (groupname##__next + align_mask) & ~align_mask;\
|
||||
size_t size = (n) * sizeof(type); \
|
||||
groupname##__next = offset + size; \
|
||||
offset; \
|
||||
})
|
||||
|
||||
#define vla_item_with_sz(groupname, type, name, n) \
|
||||
size_t groupname##_##name##__sz = (n) * sizeof(type); \
|
||||
size_t groupname##_##name##__offset = ({ \
|
||||
size_t align_mask = __alignof__(type) - 1; \
|
||||
size_t offset = (groupname##__next + align_mask) & ~align_mask;\
|
||||
size_t size = groupname##_##name##__sz; \
|
||||
groupname##__next = offset + size; \
|
||||
offset; \
|
||||
})
|
||||
|
||||
#define vla_ptr(ptr, groupname, name) \
|
||||
((void *) ((char *)ptr + groupname##_##name##__offset))
|
||||
|
||||
/* Reference counter handling */
|
||||
static void ffs_data_get(struct ffs_data *ffs);
|
||||
static void ffs_data_put(struct ffs_data *ffs);
|
||||
|
|
@ -190,7 +165,7 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
|
|||
/* Devices management *******************************************************/
|
||||
|
||||
DEFINE_MUTEX(ffs_lock);
|
||||
EXPORT_SYMBOL(ffs_lock);
|
||||
EXPORT_SYMBOL_GPL(ffs_lock);
|
||||
|
||||
static struct ffs_dev *_ffs_find_dev(const char *name);
|
||||
static struct ffs_dev *_ffs_alloc_dev(void);
|
||||
|
|
@ -2883,7 +2858,7 @@ int ffs_name_dev(struct ffs_dev *dev, const char *name)
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(ffs_name_dev);
|
||||
EXPORT_SYMBOL_GPL(ffs_name_dev);
|
||||
|
||||
int ffs_single_dev(struct ffs_dev *dev)
|
||||
{
|
||||
|
|
@ -2900,7 +2875,7 @@ int ffs_single_dev(struct ffs_dev *dev)
|
|||
ffs_dev_unlock();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(ffs_single_dev);
|
||||
EXPORT_SYMBOL_GPL(ffs_single_dev);
|
||||
|
||||
/*
|
||||
* ffs_lock must be taken by the caller of this function
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "u_ether_configfs.h"
|
||||
#include "u_rndis.h"
|
||||
#include "rndis.h"
|
||||
#include "configfs.h"
|
||||
|
||||
/*
|
||||
* This function is an RNDIS Ethernet port -- a Microsoft protocol that's
|
||||
|
|
@ -682,6 +683,15 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);
|
||||
|
||||
if (cdev->use_os_string) {
|
||||
f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
|
||||
GFP_KERNEL);
|
||||
if (!f->os_desc_table)
|
||||
return PTR_ERR(f->os_desc_table);
|
||||
f->os_desc_n = 1;
|
||||
f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc;
|
||||
}
|
||||
|
||||
/*
|
||||
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
|
||||
* configurations are bound in sequence with list_for_each_entry,
|
||||
|
|
@ -693,14 +703,16 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
gether_set_gadget(rndis_opts->net, cdev->gadget);
|
||||
status = gether_register_netdev(rndis_opts->net);
|
||||
if (status)
|
||||
return status;
|
||||
goto fail;
|
||||
rndis_opts->bound = true;
|
||||
}
|
||||
|
||||
us = usb_gstrings_attach(cdev, rndis_strings,
|
||||
ARRAY_SIZE(rndis_string_defs));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
if (IS_ERR(us)) {
|
||||
status = PTR_ERR(us);
|
||||
goto fail;
|
||||
}
|
||||
rndis_control_intf.iInterface = us[0].id;
|
||||
rndis_data_intf.iInterface = us[1].id;
|
||||
rndis_iad_descriptor.iFunction = us[2].id;
|
||||
|
|
@ -802,6 +814,8 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
return 0;
|
||||
|
||||
fail:
|
||||
kfree(f->os_desc_table);
|
||||
f->os_desc_n = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
if (rndis->notify_req) {
|
||||
|
|
@ -834,7 +848,7 @@ void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net)
|
|||
opts->borrowed_net = opts->bound = true;
|
||||
opts->net = net;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_borrow_net);
|
||||
EXPORT_SYMBOL_GPL(rndis_borrow_net);
|
||||
|
||||
static inline struct f_rndis_opts *to_f_rndis_opts(struct config_item *item)
|
||||
{
|
||||
|
|
@ -882,16 +896,21 @@ static void rndis_free_inst(struct usb_function_instance *f)
|
|||
else
|
||||
free_netdev(opts->net);
|
||||
}
|
||||
|
||||
kfree(opts->rndis_os_desc.group.default_groups); /* single VLA chunk */
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *rndis_alloc_inst(void)
|
||||
{
|
||||
struct f_rndis_opts *opts;
|
||||
struct usb_os_desc *descs[1];
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
opts->rndis_os_desc.ext_compat_id = opts->rndis_ext_compat_id;
|
||||
|
||||
mutex_init(&opts->lock);
|
||||
opts->func_inst.free_func_inst = rndis_free_inst;
|
||||
opts->net = gether_setup_default();
|
||||
|
|
@ -900,7 +919,11 @@ static struct usb_function_instance *rndis_alloc_inst(void)
|
|||
kfree(opts);
|
||||
return ERR_CAST(net);
|
||||
}
|
||||
INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop);
|
||||
|
||||
descs[0] = &opts->rndis_os_desc;
|
||||
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
|
||||
THIS_MODULE);
|
||||
config_group_init_type_name(&opts->func_inst.group, "",
|
||||
&rndis_func_type);
|
||||
|
||||
|
|
@ -925,6 +948,8 @@ static void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
|
|||
{
|
||||
struct f_rndis *rndis = func_to_rndis(f);
|
||||
|
||||
kfree(f->os_desc_table);
|
||||
f->os_desc_n = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
kfree(rndis->notify_req->buf);
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
}
|
||||
|
||||
net = gether_connect(&geth->port);
|
||||
return PTR_RET(net);
|
||||
return PTR_ERR_OR_ZERO(net);
|
||||
}
|
||||
|
||||
static void geth_disable(struct usb_function *f)
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
struct snd_uac2_chip *uac2 = prm->uac2;
|
||||
|
||||
/* i/f shutting down */
|
||||
if (!prm->ep_enabled)
|
||||
if (!prm->ep_enabled || req->status == -ESHUTDOWN)
|
||||
return;
|
||||
|
||||
/*
|
||||
|
|
@ -974,8 +974,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
|||
prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
|
||||
if (!prm->rbuf) {
|
||||
prm->max_psize = 0;
|
||||
dev_err(&uac2->pdev.dev,
|
||||
"%s:%d Error!\n", __func__, __LINE__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
|
@ -984,8 +982,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
|||
prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
|
||||
if (!prm->rbuf) {
|
||||
prm->max_psize = 0;
|
||||
dev_err(&uac2->pdev.dev,
|
||||
"%s:%d Error!\n", __func__, __LINE__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
|
@ -1298,10 +1294,8 @@ static int audio_bind_config(struct usb_configuration *cfg)
|
|||
int res;
|
||||
|
||||
agdev_g = kzalloc(sizeof *agdev_g, GFP_KERNEL);
|
||||
if (agdev_g == NULL) {
|
||||
printk(KERN_ERR "Unable to allocate audio gadget\n");
|
||||
if (agdev_g == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
res = usb_string_ids_tab(cfg->cdev, strings_fn);
|
||||
if (res)
|
||||
|
|
|
|||
|
|
@ -1112,17 +1112,13 @@ static int fotg210_udc_probe(struct platform_device *pdev)
|
|||
|
||||
/* initialize udc */
|
||||
fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL);
|
||||
if (fotg210 == NULL) {
|
||||
pr_err("kzalloc error\n");
|
||||
if (fotg210 == NULL)
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
for (i = 0; i < FOTG210_MAX_NUM_EP; i++) {
|
||||
_ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL);
|
||||
if (_ep[i] == NULL) {
|
||||
pr_err("_ep kzalloc error\n");
|
||||
if (_ep[i] == NULL)
|
||||
goto err_alloc;
|
||||
}
|
||||
fotg210->ep[i] = _ep[i];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2256,10 +2256,8 @@ static int __init struct_udc_setup(struct fsl_udc *udc,
|
|||
udc->phy_mode = pdata->phy_mode;
|
||||
|
||||
udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL);
|
||||
if (!udc->eps) {
|
||||
ERR("malloc fsl_ep failed\n");
|
||||
if (!udc->eps)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* initialized QHs, take care of alignment */
|
||||
size = udc->max_ep * sizeof(struct ep_queue_head);
|
||||
|
|
@ -2342,10 +2340,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
|||
u32 dccparams;
|
||||
|
||||
udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
|
||||
if (udc_controller == NULL) {
|
||||
ERR("malloc udc failed\n");
|
||||
if (udc_controller == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
udc_controller->pdata = pdata;
|
||||
|
|
|
|||
|
|
@ -1400,17 +1400,13 @@ static int __init fusb300_probe(struct platform_device *pdev)
|
|||
|
||||
/* initialize udc */
|
||||
fusb300 = kzalloc(sizeof(struct fusb300), GFP_KERNEL);
|
||||
if (fusb300 == NULL) {
|
||||
pr_err("kzalloc error\n");
|
||||
if (fusb300 == NULL)
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
for (i = 0; i < FUSB300_MAX_NUM_EP; i++) {
|
||||
_ep[i] = kzalloc(sizeof(struct fusb300_ep), GFP_KERNEL);
|
||||
if (_ep[i] == NULL) {
|
||||
pr_err("_ep kzalloc error\n");
|
||||
if (_ep[i] == NULL)
|
||||
goto clean_up;
|
||||
}
|
||||
fusb300->ep[i] = _ep[i];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ static void gr_seq_ep_show(struct seq_file *seq, struct gr_ep *ep)
|
|||
seq_printf(seq, " wedged = %d\n", ep->wedged);
|
||||
seq_printf(seq, " callback = %d\n", ep->callback);
|
||||
seq_printf(seq, " maxpacket = %d\n", ep->ep.maxpacket);
|
||||
seq_printf(seq, " maxpacket_limit = %d\n", ep->ep.maxpacket_limit);
|
||||
seq_printf(seq, " bytes_per_buffer = %d\n", ep->bytes_per_buffer);
|
||||
if (mode == 1 || mode == 3)
|
||||
seq_printf(seq, " nt = %d\n",
|
||||
|
|
@ -1541,6 +1542,10 @@ static int gr_ep_enable(struct usb_ep *_ep,
|
|||
} else if (max == 0) {
|
||||
dev_err(dev->dev, "Max payload cannot be set to 0\n");
|
||||
return -EINVAL;
|
||||
} else if (max > ep->ep.maxpacket_limit) {
|
||||
dev_err(dev->dev, "Requested max payload %d > limit %d\n",
|
||||
max, ep->ep.maxpacket_limit);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock(&ep->dev->lock);
|
||||
|
|
@ -1679,7 +1684,7 @@ static int gr_queue_ext(struct usb_ep *_ep, struct usb_request *_req,
|
|||
if (ep->is_in)
|
||||
gr_dbgprint_request("EXTERN", ep, req);
|
||||
|
||||
ret = gr_queue(ep, req, gfp_flags);
|
||||
ret = gr_queue(ep, req, GFP_ATOMIC);
|
||||
|
||||
spin_unlock(&ep->dev->lock);
|
||||
|
||||
|
|
@ -1985,8 +1990,8 @@ static int gr_ep_init(struct gr_udc *dev, int num, int is_in, u32 maxplimit)
|
|||
INIT_LIST_HEAD(&ep->queue);
|
||||
|
||||
if (num == 0) {
|
||||
_req = gr_alloc_request(&ep->ep, GFP_KERNEL);
|
||||
buf = devm_kzalloc(dev->dev, PAGE_SIZE, GFP_DMA | GFP_KERNEL);
|
||||
_req = gr_alloc_request(&ep->ep, GFP_ATOMIC);
|
||||
buf = devm_kzalloc(dev->dev, PAGE_SIZE, GFP_DMA | GFP_ATOMIC);
|
||||
if (!_req || !buf) {
|
||||
/* possible _req freed by gr_probe via gr_remove */
|
||||
return -ENOMEM;
|
||||
|
|
@ -2020,9 +2025,7 @@ static int gr_udc_init(struct gr_udc *dev)
|
|||
u32 dmactrl_val;
|
||||
int i;
|
||||
int ret = 0;
|
||||
u32 *bufsizes;
|
||||
u32 bufsize;
|
||||
int len;
|
||||
|
||||
gr_set_address(dev, 0);
|
||||
|
||||
|
|
@ -2033,19 +2036,17 @@ static int gr_udc_init(struct gr_udc *dev)
|
|||
INIT_LIST_HEAD(&dev->ep_list);
|
||||
gr_set_ep0state(dev, GR_EP0_DISCONNECT);
|
||||
|
||||
bufsizes = (u32 *)of_get_property(np, "epobufsizes", &len);
|
||||
len /= sizeof(u32);
|
||||
for (i = 0; i < dev->nepo; i++) {
|
||||
bufsize = (bufsizes && i < len) ? bufsizes[i] : 1024;
|
||||
if (of_property_read_u32_index(np, "epobufsizes", i, &bufsize))
|
||||
bufsize = 1024;
|
||||
ret = gr_ep_init(dev, i, 0, bufsize);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
bufsizes = (u32 *)of_get_property(np, "epibufsizes", &len);
|
||||
len /= sizeof(u32);
|
||||
for (i = 0; i < dev->nepi; i++) {
|
||||
bufsize = (bufsizes && i < len) ? bufsizes[i] : 1024;
|
||||
if (of_property_read_u32_index(np, "epibufsizes", i, &bufsize))
|
||||
bufsize = 1024;
|
||||
ret = gr_ep_init(dev, i, 1, bufsize);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -2065,9 +2066,9 @@ static int gr_udc_init(struct gr_udc *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int gr_remove(struct platform_device *ofdev)
|
||||
static int gr_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gr_udc *dev = dev_get_drvdata(&ofdev->dev);
|
||||
struct gr_udc *dev = platform_get_drvdata(pdev);
|
||||
|
||||
if (dev->added)
|
||||
usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */
|
||||
|
|
@ -2077,7 +2078,7 @@ static int gr_remove(struct platform_device *ofdev)
|
|||
gr_dfs_delete(dev);
|
||||
if (dev->desc_pool)
|
||||
dma_pool_destroy(dev->desc_pool);
|
||||
dev_set_drvdata(&ofdev->dev, NULL);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
gr_free_request(&dev->epi[0].ep, &dev->ep0reqi->req);
|
||||
gr_free_request(&dev->epo[0].ep, &dev->ep0reqo->req);
|
||||
|
|
@ -2090,7 +2091,7 @@ static int gr_request_irq(struct gr_udc *dev, int irq)
|
|||
IRQF_SHARED, driver_name, dev);
|
||||
}
|
||||
|
||||
static int gr_probe(struct platform_device *ofdev)
|
||||
static int gr_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gr_udc *dev;
|
||||
struct resource *res;
|
||||
|
|
@ -2098,30 +2099,32 @@ static int gr_probe(struct platform_device *ofdev)
|
|||
int retval;
|
||||
u32 status;
|
||||
|
||||
dev = devm_kzalloc(&ofdev->dev, sizeof(*dev), GFP_KERNEL);
|
||||
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
dev->dev = &ofdev->dev;
|
||||
dev->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
regs = devm_ioremap_resource(dev->dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
dev->irq = irq_of_parse_and_map(dev->dev->of_node, 0);
|
||||
if (!dev->irq) {
|
||||
dev->irq = platform_get_irq(pdev, 0);
|
||||
if (dev->irq <= 0) {
|
||||
dev_err(dev->dev, "No irq found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Some core configurations has separate irqs for IN and OUT events */
|
||||
dev->irqi = irq_of_parse_and_map(dev->dev->of_node, 1);
|
||||
if (dev->irqi) {
|
||||
dev->irqo = irq_of_parse_and_map(dev->dev->of_node, 2);
|
||||
if (!dev->irqo) {
|
||||
dev->irqi = platform_get_irq(pdev, 1);
|
||||
if (dev->irqi > 0) {
|
||||
dev->irqo = platform_get_irq(pdev, 2);
|
||||
if (dev->irqo <= 0) {
|
||||
dev_err(dev->dev, "Found irqi but not irqo\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
} else {
|
||||
dev->irqi = 0;
|
||||
}
|
||||
|
||||
dev->gadget.name = driver_name;
|
||||
|
|
@ -2132,7 +2135,7 @@ static int gr_probe(struct platform_device *ofdev)
|
|||
spin_lock_init(&dev->lock);
|
||||
dev->regs = regs;
|
||||
|
||||
dev_set_drvdata(&ofdev->dev, dev);
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
/* Determine number of endpoints and data interface mode */
|
||||
status = gr_read32(&dev->regs->status);
|
||||
|
|
@ -2204,7 +2207,7 @@ out:
|
|||
spin_unlock(&dev->lock);
|
||||
|
||||
if (retval)
|
||||
gr_remove(ofdev);
|
||||
gr_remove(pdev);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1494,6 +1494,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||
*/
|
||||
if (value == 0) {
|
||||
INFO (dev, "configuration #%d\n", dev->current_config);
|
||||
usb_gadget_set_state(gadget, USB_STATE_CONFIGURED);
|
||||
if (dev->usermode_setup) {
|
||||
dev->setup_can_stall = 0;
|
||||
goto delegate;
|
||||
|
|
@ -1501,7 +1502,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||
}
|
||||
break;
|
||||
|
||||
#ifndef CONFIG_USB_GADGET_PXA25X
|
||||
#ifndef CONFIG_USB_PXA25X
|
||||
/* PXA automagically handles this request too */
|
||||
case USB_REQ_GET_CONFIGURATION:
|
||||
if (ctrl->bRequestType != 0x80)
|
||||
|
|
|
|||
|
|
@ -1594,7 +1594,6 @@ static int __init m66592_probe(struct platform_device *pdev)
|
|||
m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL);
|
||||
if (m66592 == NULL) {
|
||||
ret = -ENOMEM;
|
||||
pr_err("kzalloc error\n");
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -297,10 +297,8 @@ static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req,
|
|||
u3d = req->ep->u3d;
|
||||
|
||||
trb = kzalloc(sizeof(*trb), GFP_ATOMIC);
|
||||
if (!trb) {
|
||||
dev_err(u3d->dev, "%s, trb alloc fail\n", __func__);
|
||||
if (!trb)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Be careful that no _GFP_HIGHMEM is set,
|
||||
|
|
@ -446,17 +444,12 @@ static int mv_u3d_req_to_trb(struct mv_u3d_req *req)
|
|||
trb_num++;
|
||||
|
||||
trb = kcalloc(trb_num, sizeof(*trb), GFP_ATOMIC);
|
||||
if (!trb) {
|
||||
dev_err(u3d->dev,
|
||||
"%s, trb alloc fail\n", __func__);
|
||||
if (!trb)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC);
|
||||
if (!trb_hw) {
|
||||
kfree(trb);
|
||||
dev_err(u3d->dev,
|
||||
"%s, trb_hw alloc fail\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
|
@ -1811,7 +1804,6 @@ static int mv_u3d_probe(struct platform_device *dev)
|
|||
|
||||
u3d = kzalloc(sizeof(*u3d), GFP_KERNEL);
|
||||
if (!u3d) {
|
||||
dev_err(&dev->dev, "failed to allocate memory for u3d\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_alloc_private;
|
||||
}
|
||||
|
|
@ -1905,7 +1897,6 @@ static int mv_u3d_probe(struct platform_device *dev)
|
|||
size = u3d->max_eps * sizeof(struct mv_u3d_ep) * 2;
|
||||
u3d->eps = kzalloc(size, GFP_KERNEL);
|
||||
if (!u3d->eps) {
|
||||
dev_err(&dev->dev, "allocate ep memory failed\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_alloc_eps;
|
||||
}
|
||||
|
|
@ -1913,7 +1904,6 @@ static int mv_u3d_probe(struct platform_device *dev)
|
|||
/* initialize ep0 status request structure */
|
||||
u3d->status_req = kzalloc(sizeof(struct mv_u3d_req) + 8, GFP_KERNEL);
|
||||
if (!u3d->status_req) {
|
||||
dev_err(&dev->dev, "allocate status_req memory failed\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_alloc_status_req;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1972,7 +1972,9 @@ static int net2280_stop(struct usb_gadget *_gadget,
|
|||
device_remove_file (&dev->pdev->dev, &dev_attr_function);
|
||||
device_remove_file (&dev->pdev->dev, &dev_attr_queues);
|
||||
|
||||
DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name);
|
||||
DEBUG(dev, "unregistered driver '%s'\n",
|
||||
driver ? driver->driver.name : "");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1904,7 +1904,6 @@ static int __init r8a66597_probe(struct platform_device *pdev)
|
|||
r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL);
|
||||
if (r8a66597 == NULL) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&pdev->dev, "kzalloc error\n");
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -761,7 +761,7 @@ int rndis_signal_connect(int configNr)
|
|||
return rndis_indicate_status_msg(configNr,
|
||||
RNDIS_STATUS_MEDIA_CONNECT);
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_signal_connect);
|
||||
EXPORT_SYMBOL_GPL(rndis_signal_connect);
|
||||
|
||||
int rndis_signal_disconnect(int configNr)
|
||||
{
|
||||
|
|
@ -770,7 +770,7 @@ int rndis_signal_disconnect(int configNr)
|
|||
return rndis_indicate_status_msg(configNr,
|
||||
RNDIS_STATUS_MEDIA_DISCONNECT);
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_signal_disconnect);
|
||||
EXPORT_SYMBOL_GPL(rndis_signal_disconnect);
|
||||
|
||||
void rndis_uninit(int configNr)
|
||||
{
|
||||
|
|
@ -785,13 +785,13 @@ void rndis_uninit(int configNr)
|
|||
while ((buf = rndis_get_next_response(configNr, &length)))
|
||||
rndis_free_response(configNr, buf);
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_uninit);
|
||||
EXPORT_SYMBOL_GPL(rndis_uninit);
|
||||
|
||||
void rndis_set_host_mac(int configNr, const u8 *addr)
|
||||
{
|
||||
rndis_per_dev_params[configNr].host_mac = addr;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_set_host_mac);
|
||||
EXPORT_SYMBOL_GPL(rndis_set_host_mac);
|
||||
|
||||
/*
|
||||
* Message Parser
|
||||
|
|
@ -874,7 +874,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
|
|||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_msg_parser);
|
||||
EXPORT_SYMBOL_GPL(rndis_msg_parser);
|
||||
|
||||
int rndis_register(void (*resp_avail)(void *v), void *v)
|
||||
{
|
||||
|
|
@ -896,7 +896,7 @@ int rndis_register(void (*resp_avail)(void *v), void *v)
|
|||
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_register);
|
||||
EXPORT_SYMBOL_GPL(rndis_register);
|
||||
|
||||
void rndis_deregister(int configNr)
|
||||
{
|
||||
|
|
@ -905,7 +905,7 @@ void rndis_deregister(int configNr)
|
|||
if (configNr >= RNDIS_MAX_CONFIGS) return;
|
||||
rndis_per_dev_params[configNr].used = 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_deregister);
|
||||
EXPORT_SYMBOL_GPL(rndis_deregister);
|
||||
|
||||
int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
|
||||
{
|
||||
|
|
@ -919,7 +919,7 @@ int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_set_param_dev);
|
||||
EXPORT_SYMBOL_GPL(rndis_set_param_dev);
|
||||
|
||||
int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr)
|
||||
{
|
||||
|
|
@ -932,7 +932,7 @@ int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_set_param_vendor);
|
||||
EXPORT_SYMBOL_GPL(rndis_set_param_vendor);
|
||||
|
||||
int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
|
||||
{
|
||||
|
|
@ -944,7 +944,7 @@ int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_set_param_medium);
|
||||
EXPORT_SYMBOL_GPL(rndis_set_param_medium);
|
||||
|
||||
void rndis_add_hdr(struct sk_buff *skb)
|
||||
{
|
||||
|
|
@ -959,7 +959,7 @@ void rndis_add_hdr(struct sk_buff *skb)
|
|||
header->DataOffset = cpu_to_le32(36);
|
||||
header->DataLength = cpu_to_le32(skb->len - sizeof(*header));
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_add_hdr);
|
||||
EXPORT_SYMBOL_GPL(rndis_add_hdr);
|
||||
|
||||
void rndis_free_response(int configNr, u8 *buf)
|
||||
{
|
||||
|
|
@ -976,7 +976,7 @@ void rndis_free_response(int configNr, u8 *buf)
|
|||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_free_response);
|
||||
EXPORT_SYMBOL_GPL(rndis_free_response);
|
||||
|
||||
u8 *rndis_get_next_response(int configNr, u32 *length)
|
||||
{
|
||||
|
|
@ -998,7 +998,7 @@ u8 *rndis_get_next_response(int configNr, u32 *length)
|
|||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_get_next_response);
|
||||
EXPORT_SYMBOL_GPL(rndis_get_next_response);
|
||||
|
||||
static rndis_resp_t *rndis_add_response(int configNr, u32 length)
|
||||
{
|
||||
|
|
@ -1042,7 +1042,7 @@ int rndis_rm_hdr(struct gether *port,
|
|||
skb_queue_tail(list, skb);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_rm_hdr);
|
||||
EXPORT_SYMBOL_GPL(rndis_rm_hdr);
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
|
||||
|
|
|
|||
|
|
@ -1,378 +0,0 @@
|
|||
/* drivers/usb/gadget/s3c-hsotg.h
|
||||
*
|
||||
* Copyright 2008 Openmoko, Inc.
|
||||
* Copyright 2008 Simtec Electronics
|
||||
* http://armlinux.simtec.co.uk/
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
* USB2.0 Highspeed/OtG Synopsis DWC2 device block registers
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __REGS_USB_HSOTG_H
|
||||
#define __REGS_USB_HSOTG_H __FILE__
|
||||
|
||||
#define HSOTG_REG(x) (x)
|
||||
|
||||
#define GOTGCTL HSOTG_REG(0x000)
|
||||
#define GOTGCTL_BSESVLD (1 << 19)
|
||||
#define GOTGCTL_ASESVLD (1 << 18)
|
||||
#define GOTGCTL_DBNC_SHORT (1 << 17)
|
||||
#define GOTGCTL_CONID_B (1 << 16)
|
||||
#define GOTGCTL_DEVHNPEN (1 << 11)
|
||||
#define GOTGCTL_HSSETHNPEN (1 << 10)
|
||||
#define GOTGCTL_HNPREQ (1 << 9)
|
||||
#define GOTGCTL_HSTNEGSCS (1 << 8)
|
||||
#define GOTGCTL_SESREQ (1 << 1)
|
||||
#define GOTGCTL_SESREQSCS (1 << 0)
|
||||
|
||||
#define GOTGINT HSOTG_REG(0x004)
|
||||
#define GOTGINT_DbnceDone (1 << 19)
|
||||
#define GOTGINT_ADevTOUTChg (1 << 18)
|
||||
#define GOTGINT_HstNegDet (1 << 17)
|
||||
#define GOTGINT_HstnegSucStsChng (1 << 9)
|
||||
#define GOTGINT_SesReqSucStsChng (1 << 8)
|
||||
#define GOTGINT_SesEndDet (1 << 2)
|
||||
|
||||
#define GAHBCFG HSOTG_REG(0x008)
|
||||
#define GAHBCFG_PTxFEmpLvl (1 << 8)
|
||||
#define GAHBCFG_NPTxFEmpLvl (1 << 7)
|
||||
#define GAHBCFG_DMAEn (1 << 5)
|
||||
#define GAHBCFG_HBstLen_MASK (0xf << 1)
|
||||
#define GAHBCFG_HBstLen_SHIFT (1)
|
||||
#define GAHBCFG_HBstLen_Single (0x0 << 1)
|
||||
#define GAHBCFG_HBstLen_Incr (0x1 << 1)
|
||||
#define GAHBCFG_HBstLen_Incr4 (0x3 << 1)
|
||||
#define GAHBCFG_HBstLen_Incr8 (0x5 << 1)
|
||||
#define GAHBCFG_HBstLen_Incr16 (0x7 << 1)
|
||||
#define GAHBCFG_GlblIntrEn (1 << 0)
|
||||
|
||||
#define GUSBCFG HSOTG_REG(0x00C)
|
||||
#define GUSBCFG_PHYLPClkSel (1 << 15)
|
||||
#define GUSBCFG_HNPCap (1 << 9)
|
||||
#define GUSBCFG_SRPCap (1 << 8)
|
||||
#define GUSBCFG_PHYIf16 (1 << 3)
|
||||
#define GUSBCFG_PHYIf8 (0 << 3)
|
||||
#define GUSBCFG_TOutCal_MASK (0x7 << 0)
|
||||
#define GUSBCFG_TOutCal_SHIFT (0)
|
||||
#define GUSBCFG_TOutCal_LIMIT (0x7)
|
||||
#define GUSBCFG_TOutCal(_x) ((_x) << 0)
|
||||
|
||||
#define GRSTCTL HSOTG_REG(0x010)
|
||||
|
||||
#define GRSTCTL_AHBIdle (1 << 31)
|
||||
#define GRSTCTL_DMAReq (1 << 30)
|
||||
#define GRSTCTL_TxFNum_MASK (0x1f << 6)
|
||||
#define GRSTCTL_TxFNum_SHIFT (6)
|
||||
#define GRSTCTL_TxFNum_LIMIT (0x1f)
|
||||
#define GRSTCTL_TxFNum(_x) ((_x) << 6)
|
||||
#define GRSTCTL_TxFFlsh (1 << 5)
|
||||
#define GRSTCTL_RxFFlsh (1 << 4)
|
||||
#define GRSTCTL_INTknQFlsh (1 << 3)
|
||||
#define GRSTCTL_FrmCntrRst (1 << 2)
|
||||
#define GRSTCTL_HSftRst (1 << 1)
|
||||
#define GRSTCTL_CSftRst (1 << 0)
|
||||
|
||||
#define GINTSTS HSOTG_REG(0x014)
|
||||
#define GINTMSK HSOTG_REG(0x018)
|
||||
|
||||
#define GINTSTS_WkUpInt (1 << 31)
|
||||
#define GINTSTS_SessReqInt (1 << 30)
|
||||
#define GINTSTS_DisconnInt (1 << 29)
|
||||
#define GINTSTS_ConIDStsChng (1 << 28)
|
||||
#define GINTSTS_PTxFEmp (1 << 26)
|
||||
#define GINTSTS_HChInt (1 << 25)
|
||||
#define GINTSTS_PrtInt (1 << 24)
|
||||
#define GINTSTS_FetSusp (1 << 22)
|
||||
#define GINTSTS_incompIP (1 << 21)
|
||||
#define GINTSTS_IncomplSOIN (1 << 20)
|
||||
#define GINTSTS_OEPInt (1 << 19)
|
||||
#define GINTSTS_IEPInt (1 << 18)
|
||||
#define GINTSTS_EPMis (1 << 17)
|
||||
#define GINTSTS_EOPF (1 << 15)
|
||||
#define GINTSTS_ISOutDrop (1 << 14)
|
||||
#define GINTSTS_EnumDone (1 << 13)
|
||||
#define GINTSTS_USBRst (1 << 12)
|
||||
#define GINTSTS_USBSusp (1 << 11)
|
||||
#define GINTSTS_ErlySusp (1 << 10)
|
||||
#define GINTSTS_GOUTNakEff (1 << 7)
|
||||
#define GINTSTS_GINNakEff (1 << 6)
|
||||
#define GINTSTS_NPTxFEmp (1 << 5)
|
||||
#define GINTSTS_RxFLvl (1 << 4)
|
||||
#define GINTSTS_SOF (1 << 3)
|
||||
#define GINTSTS_OTGInt (1 << 2)
|
||||
#define GINTSTS_ModeMis (1 << 1)
|
||||
#define GINTSTS_CurMod_Host (1 << 0)
|
||||
|
||||
#define GRXSTSR HSOTG_REG(0x01C)
|
||||
#define GRXSTSP HSOTG_REG(0x020)
|
||||
|
||||
#define GRXSTS_FN_MASK (0x7f << 25)
|
||||
#define GRXSTS_FN_SHIFT (25)
|
||||
|
||||
#define GRXSTS_PktSts_MASK (0xf << 17)
|
||||
#define GRXSTS_PktSts_SHIFT (17)
|
||||
#define GRXSTS_PktSts_GlobalOutNAK (0x1 << 17)
|
||||
#define GRXSTS_PktSts_OutRX (0x2 << 17)
|
||||
#define GRXSTS_PktSts_OutDone (0x3 << 17)
|
||||
#define GRXSTS_PktSts_SetupDone (0x4 << 17)
|
||||
#define GRXSTS_PktSts_SetupRX (0x6 << 17)
|
||||
|
||||
#define GRXSTS_DPID_MASK (0x3 << 15)
|
||||
#define GRXSTS_DPID_SHIFT (15)
|
||||
#define GRXSTS_ByteCnt_MASK (0x7ff << 4)
|
||||
#define GRXSTS_ByteCnt_SHIFT (4)
|
||||
#define GRXSTS_EPNum_MASK (0xf << 0)
|
||||
#define GRXSTS_EPNum_SHIFT (0)
|
||||
|
||||
#define GRXFSIZ HSOTG_REG(0x024)
|
||||
|
||||
#define GNPTXFSIZ HSOTG_REG(0x028)
|
||||
|
||||
#define GNPTXFSIZ_NPTxFDep_MASK (0xffff << 16)
|
||||
#define GNPTXFSIZ_NPTxFDep_SHIFT (16)
|
||||
#define GNPTXFSIZ_NPTxFDep_LIMIT (0xffff)
|
||||
#define GNPTXFSIZ_NPTxFDep(_x) ((_x) << 16)
|
||||
#define GNPTXFSIZ_NPTxFStAddr_MASK (0xffff << 0)
|
||||
#define GNPTXFSIZ_NPTxFStAddr_SHIFT (0)
|
||||
#define GNPTXFSIZ_NPTxFStAddr_LIMIT (0xffff)
|
||||
#define GNPTXFSIZ_NPTxFStAddr(_x) ((_x) << 0)
|
||||
|
||||
#define GNPTXSTS HSOTG_REG(0x02C)
|
||||
|
||||
#define GNPTXSTS_NPtxQTop_MASK (0x7f << 24)
|
||||
#define GNPTXSTS_NPtxQTop_SHIFT (24)
|
||||
|
||||
#define GNPTXSTS_NPTxQSpcAvail_MASK (0xff << 16)
|
||||
#define GNPTXSTS_NPTxQSpcAvail_SHIFT (16)
|
||||
#define GNPTXSTS_NPTxQSpcAvail_GET(_v) (((_v) >> 16) & 0xff)
|
||||
|
||||
#define GNPTXSTS_NPTxFSpcAvail_MASK (0xffff << 0)
|
||||
#define GNPTXSTS_NPTxFSpcAvail_SHIFT (0)
|
||||
#define GNPTXSTS_NPTxFSpcAvail_GET(_v) (((_v) >> 0) & 0xffff)
|
||||
|
||||
|
||||
#define HPTXFSIZ HSOTG_REG(0x100)
|
||||
|
||||
#define DPTXFSIZn(_a) HSOTG_REG(0x104 + (((_a) - 1) * 4))
|
||||
|
||||
#define DPTXFSIZn_DPTxFSize_MASK (0xffff << 16)
|
||||
#define DPTXFSIZn_DPTxFSize_SHIFT (16)
|
||||
#define DPTXFSIZn_DPTxFSize_GET(_v) (((_v) >> 16) & 0xffff)
|
||||
#define DPTXFSIZn_DPTxFSize_LIMIT (0xffff)
|
||||
#define DPTXFSIZn_DPTxFSize(_x) ((_x) << 16)
|
||||
|
||||
#define DPTXFSIZn_DPTxFStAddr_MASK (0xffff << 0)
|
||||
#define DPTXFSIZn_DPTxFStAddr_SHIFT (0)
|
||||
|
||||
/* Device mode registers */
|
||||
#define DCFG HSOTG_REG(0x800)
|
||||
|
||||
#define DCFG_EPMisCnt_MASK (0x1f << 18)
|
||||
#define DCFG_EPMisCnt_SHIFT (18)
|
||||
#define DCFG_EPMisCnt_LIMIT (0x1f)
|
||||
#define DCFG_EPMisCnt(_x) ((_x) << 18)
|
||||
|
||||
#define DCFG_PerFrInt_MASK (0x3 << 11)
|
||||
#define DCFG_PerFrInt_SHIFT (11)
|
||||
#define DCFG_PerFrInt_LIMIT (0x3)
|
||||
#define DCFG_PerFrInt(_x) ((_x) << 11)
|
||||
|
||||
#define DCFG_DevAddr_MASK (0x7f << 4)
|
||||
#define DCFG_DevAddr_SHIFT (4)
|
||||
#define DCFG_DevAddr_LIMIT (0x7f)
|
||||
#define DCFG_DevAddr(_x) ((_x) << 4)
|
||||
|
||||
#define DCFG_NZStsOUTHShk (1 << 2)
|
||||
|
||||
#define DCFG_DevSpd_MASK (0x3 << 0)
|
||||
#define DCFG_DevSpd_SHIFT (0)
|
||||
#define DCFG_DevSpd_HS (0x0 << 0)
|
||||
#define DCFG_DevSpd_FS (0x1 << 0)
|
||||
#define DCFG_DevSpd_LS (0x2 << 0)
|
||||
#define DCFG_DevSpd_FS48 (0x3 << 0)
|
||||
|
||||
#define DCTL HSOTG_REG(0x804)
|
||||
|
||||
#define DCTL_PWROnPrgDone (1 << 11)
|
||||
#define DCTL_CGOUTNak (1 << 10)
|
||||
#define DCTL_SGOUTNak (1 << 9)
|
||||
#define DCTL_CGNPInNAK (1 << 8)
|
||||
#define DCTL_SGNPInNAK (1 << 7)
|
||||
#define DCTL_TstCtl_MASK (0x7 << 4)
|
||||
#define DCTL_TstCtl_SHIFT (4)
|
||||
#define DCTL_GOUTNakSts (1 << 3)
|
||||
#define DCTL_GNPINNakSts (1 << 2)
|
||||
#define DCTL_SftDiscon (1 << 1)
|
||||
#define DCTL_RmtWkUpSig (1 << 0)
|
||||
|
||||
#define DSTS HSOTG_REG(0x808)
|
||||
|
||||
#define DSTS_SOFFN_MASK (0x3fff << 8)
|
||||
#define DSTS_SOFFN_SHIFT (8)
|
||||
#define DSTS_SOFFN_LIMIT (0x3fff)
|
||||
#define DSTS_SOFFN(_x) ((_x) << 8)
|
||||
#define DSTS_ErraticErr (1 << 3)
|
||||
#define DSTS_EnumSpd_MASK (0x3 << 1)
|
||||
#define DSTS_EnumSpd_SHIFT (1)
|
||||
#define DSTS_EnumSpd_HS (0x0 << 1)
|
||||
#define DSTS_EnumSpd_FS (0x1 << 1)
|
||||
#define DSTS_EnumSpd_LS (0x2 << 1)
|
||||
#define DSTS_EnumSpd_FS48 (0x3 << 1)
|
||||
|
||||
#define DSTS_SuspSts (1 << 0)
|
||||
|
||||
#define DIEPMSK HSOTG_REG(0x810)
|
||||
|
||||
#define DIEPMSK_TxFIFOEmpty (1 << 7)
|
||||
#define DIEPMSK_INEPNakEffMsk (1 << 6)
|
||||
#define DIEPMSK_INTknEPMisMsk (1 << 5)
|
||||
#define DIEPMSK_INTknTXFEmpMsk (1 << 4)
|
||||
#define DIEPMSK_TimeOUTMsk (1 << 3)
|
||||
#define DIEPMSK_AHBErrMsk (1 << 2)
|
||||
#define DIEPMSK_EPDisbldMsk (1 << 1)
|
||||
#define DIEPMSK_XferComplMsk (1 << 0)
|
||||
|
||||
#define DOEPMSK HSOTG_REG(0x814)
|
||||
|
||||
#define DOEPMSK_Back2BackSetup (1 << 6)
|
||||
#define DOEPMSK_OUTTknEPdisMsk (1 << 4)
|
||||
#define DOEPMSK_SetupMsk (1 << 3)
|
||||
#define DOEPMSK_AHBErrMsk (1 << 2)
|
||||
#define DOEPMSK_EPDisbldMsk (1 << 1)
|
||||
#define DOEPMSK_XferComplMsk (1 << 0)
|
||||
|
||||
#define DAINT HSOTG_REG(0x818)
|
||||
#define DAINTMSK HSOTG_REG(0x81C)
|
||||
|
||||
#define DAINT_OutEP_SHIFT (16)
|
||||
#define DAINT_OutEP(x) (1 << ((x) + 16))
|
||||
#define DAINT_InEP(x) (1 << (x))
|
||||
|
||||
#define DTKNQR1 HSOTG_REG(0x820)
|
||||
#define DTKNQR2 HSOTG_REG(0x824)
|
||||
#define DTKNQR3 HSOTG_REG(0x830)
|
||||
#define DTKNQR4 HSOTG_REG(0x834)
|
||||
|
||||
#define DVBUSDIS HSOTG_REG(0x828)
|
||||
#define DVBUSPULSE HSOTG_REG(0x82C)
|
||||
|
||||
#define DIEPCTL0 HSOTG_REG(0x900)
|
||||
#define DOEPCTL0 HSOTG_REG(0xB00)
|
||||
#define DIEPCTL(_a) HSOTG_REG(0x900 + ((_a) * 0x20))
|
||||
#define DOEPCTL(_a) HSOTG_REG(0xB00 + ((_a) * 0x20))
|
||||
|
||||
/* EP0 specialness:
|
||||
* bits[29..28] - reserved (no SetD0PID, SetD1PID)
|
||||
* bits[25..22] - should always be zero, this isn't a periodic endpoint
|
||||
* bits[10..0] - MPS setting differenct for EP0
|
||||
*/
|
||||
#define D0EPCTL_MPS_MASK (0x3 << 0)
|
||||
#define D0EPCTL_MPS_SHIFT (0)
|
||||
#define D0EPCTL_MPS_64 (0x0 << 0)
|
||||
#define D0EPCTL_MPS_32 (0x1 << 0)
|
||||
#define D0EPCTL_MPS_16 (0x2 << 0)
|
||||
#define D0EPCTL_MPS_8 (0x3 << 0)
|
||||
|
||||
#define DxEPCTL_EPEna (1 << 31)
|
||||
#define DxEPCTL_EPDis (1 << 30)
|
||||
#define DxEPCTL_SetD1PID (1 << 29)
|
||||
#define DxEPCTL_SetOddFr (1 << 29)
|
||||
#define DxEPCTL_SetD0PID (1 << 28)
|
||||
#define DxEPCTL_SetEvenFr (1 << 28)
|
||||
#define DxEPCTL_SNAK (1 << 27)
|
||||
#define DxEPCTL_CNAK (1 << 26)
|
||||
#define DxEPCTL_TxFNum_MASK (0xf << 22)
|
||||
#define DxEPCTL_TxFNum_SHIFT (22)
|
||||
#define DxEPCTL_TxFNum_LIMIT (0xf)
|
||||
#define DxEPCTL_TxFNum(_x) ((_x) << 22)
|
||||
|
||||
#define DxEPCTL_Stall (1 << 21)
|
||||
#define DxEPCTL_Snp (1 << 20)
|
||||
#define DxEPCTL_EPType_MASK (0x3 << 18)
|
||||
#define DxEPCTL_EPType_SHIFT (18)
|
||||
#define DxEPCTL_EPType_Control (0x0 << 18)
|
||||
#define DxEPCTL_EPType_Iso (0x1 << 18)
|
||||
#define DxEPCTL_EPType_Bulk (0x2 << 18)
|
||||
#define DxEPCTL_EPType_Intterupt (0x3 << 18)
|
||||
|
||||
#define DxEPCTL_NAKsts (1 << 17)
|
||||
#define DxEPCTL_DPID (1 << 16)
|
||||
#define DxEPCTL_EOFrNum (1 << 16)
|
||||
#define DxEPCTL_USBActEp (1 << 15)
|
||||
#define DxEPCTL_NextEp_MASK (0xf << 11)
|
||||
#define DxEPCTL_NextEp_SHIFT (11)
|
||||
#define DxEPCTL_NextEp_LIMIT (0xf)
|
||||
#define DxEPCTL_NextEp(_x) ((_x) << 11)
|
||||
|
||||
#define DxEPCTL_MPS_MASK (0x7ff << 0)
|
||||
#define DxEPCTL_MPS_SHIFT (0)
|
||||
#define DxEPCTL_MPS_LIMIT (0x7ff)
|
||||
#define DxEPCTL_MPS(_x) ((_x) << 0)
|
||||
|
||||
#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20))
|
||||
#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20))
|
||||
|
||||
#define DxEPINT_INEPNakEff (1 << 6)
|
||||
#define DxEPINT_Back2BackSetup (1 << 6)
|
||||
#define DxEPINT_INTknEPMis (1 << 5)
|
||||
#define DxEPINT_INTknTXFEmp (1 << 4)
|
||||
#define DxEPINT_OUTTknEPdis (1 << 4)
|
||||
#define DxEPINT_Timeout (1 << 3)
|
||||
#define DxEPINT_Setup (1 << 3)
|
||||
#define DxEPINT_AHBErr (1 << 2)
|
||||
#define DxEPINT_EPDisbld (1 << 1)
|
||||
#define DxEPINT_XferCompl (1 << 0)
|
||||
|
||||
#define DIEPTSIZ0 HSOTG_REG(0x910)
|
||||
|
||||
#define DIEPTSIZ0_PktCnt_MASK (0x3 << 19)
|
||||
#define DIEPTSIZ0_PktCnt_SHIFT (19)
|
||||
#define DIEPTSIZ0_PktCnt_LIMIT (0x3)
|
||||
#define DIEPTSIZ0_PktCnt(_x) ((_x) << 19)
|
||||
|
||||
#define DIEPTSIZ0_XferSize_MASK (0x7f << 0)
|
||||
#define DIEPTSIZ0_XferSize_SHIFT (0)
|
||||
#define DIEPTSIZ0_XferSize_LIMIT (0x7f)
|
||||
#define DIEPTSIZ0_XferSize(_x) ((_x) << 0)
|
||||
|
||||
#define DOEPTSIZ0 HSOTG_REG(0xB10)
|
||||
#define DOEPTSIZ0_SUPCnt_MASK (0x3 << 29)
|
||||
#define DOEPTSIZ0_SUPCnt_SHIFT (29)
|
||||
#define DOEPTSIZ0_SUPCnt_LIMIT (0x3)
|
||||
#define DOEPTSIZ0_SUPCnt(_x) ((_x) << 29)
|
||||
|
||||
#define DOEPTSIZ0_PktCnt (1 << 19)
|
||||
#define DOEPTSIZ0_XferSize_MASK (0x7f << 0)
|
||||
#define DOEPTSIZ0_XferSize_SHIFT (0)
|
||||
|
||||
#define DIEPTSIZ(_a) HSOTG_REG(0x910 + ((_a) * 0x20))
|
||||
#define DOEPTSIZ(_a) HSOTG_REG(0xB10 + ((_a) * 0x20))
|
||||
|
||||
#define DxEPTSIZ_MC_MASK (0x3 << 29)
|
||||
#define DxEPTSIZ_MC_SHIFT (29)
|
||||
#define DxEPTSIZ_MC_LIMIT (0x3)
|
||||
#define DxEPTSIZ_MC(_x) ((_x) << 29)
|
||||
|
||||
#define DxEPTSIZ_PktCnt_MASK (0x3ff << 19)
|
||||
#define DxEPTSIZ_PktCnt_SHIFT (19)
|
||||
#define DxEPTSIZ_PktCnt_GET(_v) (((_v) >> 19) & 0x3ff)
|
||||
#define DxEPTSIZ_PktCnt_LIMIT (0x3ff)
|
||||
#define DxEPTSIZ_PktCnt(_x) ((_x) << 19)
|
||||
|
||||
#define DxEPTSIZ_XferSize_MASK (0x7ffff << 0)
|
||||
#define DxEPTSIZ_XferSize_SHIFT (0)
|
||||
#define DxEPTSIZ_XferSize_GET(_v) (((_v) >> 0) & 0x7ffff)
|
||||
#define DxEPTSIZ_XferSize_LIMIT (0x7ffff)
|
||||
#define DxEPTSIZ_XferSize(_x) ((_x) << 0)
|
||||
|
||||
#define DIEPDMA(_a) HSOTG_REG(0x914 + ((_a) * 0x20))
|
||||
#define DOEPDMA(_a) HSOTG_REG(0xB14 + ((_a) * 0x20))
|
||||
#define DTXFSTS(_a) HSOTG_REG(0x918 + ((_a) * 0x20))
|
||||
|
||||
#define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000))
|
||||
|
||||
#endif /* __REGS_USB_HSOTG_H */
|
||||
|
|
@ -117,7 +117,8 @@ static int dprintk(int level, const char *fmt, ...)
|
|||
sizeof(printk_buf)-len, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return pr_debug("%s", printk_buf);
|
||||
pr_debug("%s", printk_buf);
|
||||
return len;
|
||||
}
|
||||
#else
|
||||
static int dprintk(int level, const char *fmt, ...)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ struct usb_interface_descriptor fsg_intf_desc = {
|
|||
.bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */
|
||||
.iInterface = FSG_STRING_INTERFACE,
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_intf_desc);
|
||||
EXPORT_SYMBOL_GPL(fsg_intf_desc);
|
||||
|
||||
/*
|
||||
* Three full-speed endpoint descriptors: bulk-in, bulk-out, and
|
||||
|
|
@ -58,7 +58,7 @@ struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
/* wMaxPacketSize set by autoconfiguration */
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_fs_bulk_in_desc);
|
||||
EXPORT_SYMBOL_GPL(fsg_fs_bulk_in_desc);
|
||||
|
||||
struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
|
|
@ -68,7 +68,7 @@ struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
/* wMaxPacketSize set by autoconfiguration */
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_fs_bulk_out_desc);
|
||||
EXPORT_SYMBOL_GPL(fsg_fs_bulk_out_desc);
|
||||
|
||||
struct usb_descriptor_header *fsg_fs_function[] = {
|
||||
(struct usb_descriptor_header *) &fsg_intf_desc,
|
||||
|
|
@ -76,7 +76,7 @@ struct usb_descriptor_header *fsg_fs_function[] = {
|
|||
(struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
|
||||
NULL,
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_fs_function);
|
||||
EXPORT_SYMBOL_GPL(fsg_fs_function);
|
||||
|
||||
|
||||
/*
|
||||
|
|
@ -95,7 +95,7 @@ struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_hs_bulk_in_desc);
|
||||
EXPORT_SYMBOL_GPL(fsg_hs_bulk_in_desc);
|
||||
|
||||
struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
|
|
@ -106,7 +106,7 @@ struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
|
|||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
.bInterval = 1, /* NAK every 1 uframe */
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_hs_bulk_out_desc);
|
||||
EXPORT_SYMBOL_GPL(fsg_hs_bulk_out_desc);
|
||||
|
||||
|
||||
struct usb_descriptor_header *fsg_hs_function[] = {
|
||||
|
|
@ -115,7 +115,7 @@ struct usb_descriptor_header *fsg_hs_function[] = {
|
|||
(struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
|
||||
NULL,
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_hs_function);
|
||||
EXPORT_SYMBOL_GPL(fsg_hs_function);
|
||||
|
||||
struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
|
|
@ -125,7 +125,7 @@ struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_ss_bulk_in_desc);
|
||||
EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_desc);
|
||||
|
||||
struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
|
||||
.bLength = sizeof(fsg_ss_bulk_in_comp_desc),
|
||||
|
|
@ -133,7 +133,7 @@ struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
|
|||
|
||||
/*.bMaxBurst = DYNAMIC, */
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_ss_bulk_in_comp_desc);
|
||||
EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_comp_desc);
|
||||
|
||||
struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
|
|
@ -143,7 +143,7 @@ struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_ss_bulk_out_desc);
|
||||
EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_desc);
|
||||
|
||||
struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
|
||||
.bLength = sizeof(fsg_ss_bulk_in_comp_desc),
|
||||
|
|
@ -151,7 +151,7 @@ struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
|
|||
|
||||
/*.bMaxBurst = DYNAMIC, */
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_ss_bulk_out_comp_desc);
|
||||
EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_comp_desc);
|
||||
|
||||
struct usb_descriptor_header *fsg_ss_function[] = {
|
||||
(struct usb_descriptor_header *) &fsg_intf_desc,
|
||||
|
|
@ -161,7 +161,7 @@ struct usb_descriptor_header *fsg_ss_function[] = {
|
|||
(struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
|
||||
NULL,
|
||||
};
|
||||
EXPORT_SYMBOL(fsg_ss_function);
|
||||
EXPORT_SYMBOL_GPL(fsg_ss_function);
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
@ -179,7 +179,7 @@ void fsg_lun_close(struct fsg_lun *curlun)
|
|||
curlun->filp = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_lun_close);
|
||||
EXPORT_SYMBOL_GPL(fsg_lun_close);
|
||||
|
||||
int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
|
||||
{
|
||||
|
|
@ -278,7 +278,7 @@ out:
|
|||
fput(filp);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_lun_open);
|
||||
EXPORT_SYMBOL_GPL(fsg_lun_open);
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
@ -295,7 +295,7 @@ int fsg_lun_fsync_sub(struct fsg_lun *curlun)
|
|||
return 0;
|
||||
return vfs_fsync(filp, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_lun_fsync_sub);
|
||||
EXPORT_SYMBOL_GPL(fsg_lun_fsync_sub);
|
||||
|
||||
void store_cdrom_address(u8 *dest, int msf, u32 addr)
|
||||
{
|
||||
|
|
@ -314,7 +314,7 @@ void store_cdrom_address(u8 *dest, int msf, u32 addr)
|
|||
put_unaligned_be32(addr, dest);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(store_cdrom_address);
|
||||
EXPORT_SYMBOL_GPL(store_cdrom_address);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
|
@ -325,13 +325,13 @@ ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf)
|
|||
? curlun->ro
|
||||
: curlun->initially_ro);
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_show_ro);
|
||||
EXPORT_SYMBOL_GPL(fsg_show_ro);
|
||||
|
||||
ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%u\n", curlun->nofua);
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_show_nofua);
|
||||
EXPORT_SYMBOL_GPL(fsg_show_nofua);
|
||||
|
||||
ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
|
||||
char *buf)
|
||||
|
|
@ -357,19 +357,19 @@ ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
|
|||
up_read(filesem);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_show_file);
|
||||
EXPORT_SYMBOL_GPL(fsg_show_file);
|
||||
|
||||
ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%u\n", curlun->cdrom);
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_show_cdrom);
|
||||
EXPORT_SYMBOL_GPL(fsg_show_cdrom);
|
||||
|
||||
ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%u\n", curlun->removable);
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_show_removable);
|
||||
EXPORT_SYMBOL_GPL(fsg_show_removable);
|
||||
|
||||
/*
|
||||
* The caller must hold fsg->filesem for reading when calling this function.
|
||||
|
|
@ -410,7 +410,7 @@ ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
|
|||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_store_ro);
|
||||
EXPORT_SYMBOL_GPL(fsg_store_ro);
|
||||
|
||||
ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
|
||||
{
|
||||
|
|
@ -429,7 +429,7 @@ ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
|
|||
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_store_nofua);
|
||||
EXPORT_SYMBOL_GPL(fsg_store_nofua);
|
||||
|
||||
ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
|
||||
const char *buf, size_t count)
|
||||
|
|
@ -460,7 +460,7 @@ ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
|
|||
up_write(filesem);
|
||||
return (rc < 0 ? rc : count);
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_store_file);
|
||||
EXPORT_SYMBOL_GPL(fsg_store_file);
|
||||
|
||||
ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
|
||||
const char *buf, size_t count)
|
||||
|
|
@ -483,7 +483,7 @@ ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_store_cdrom);
|
||||
EXPORT_SYMBOL_GPL(fsg_store_cdrom);
|
||||
|
||||
ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
|
||||
size_t count)
|
||||
|
|
@ -499,6 +499,6 @@ ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
|
|||
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(fsg_store_removable);
|
||||
EXPORT_SYMBOL_GPL(fsg_store_removable);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -1383,10 +1383,8 @@ static struct se_node_acl *usbg_alloc_fabric_acl(struct se_portal_group *se_tpg)
|
|||
struct usbg_nacl *nacl;
|
||||
|
||||
nacl = kzalloc(sizeof(struct usbg_nacl), GFP_KERNEL);
|
||||
if (!nacl) {
|
||||
printk(KERN_ERR "Unable to allocate struct usbg_nacl\n");
|
||||
if (!nacl)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &nacl->se_node_acl;
|
||||
}
|
||||
|
|
@ -1561,10 +1559,8 @@ static struct se_portal_group *usbg_make_tpg(
|
|||
}
|
||||
|
||||
tpg = kzalloc(sizeof(struct usbg_tpg), GFP_KERNEL);
|
||||
if (!tpg) {
|
||||
printk(KERN_ERR "Unable to allocate struct usbg_tpg");
|
||||
if (!tpg)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
mutex_init(&tpg->tpg_mutex);
|
||||
atomic_set(&tpg->tpg_port_count, 0);
|
||||
tpg->workqueue = alloc_workqueue("tcm_usb_gadget", 0, 1);
|
||||
|
|
@ -1613,10 +1609,8 @@ static struct se_wwn *usbg_make_tport(
|
|||
return ERR_PTR(-EINVAL);
|
||||
|
||||
tport = kzalloc(sizeof(struct usbg_tport), GFP_KERNEL);
|
||||
if (!(tport)) {
|
||||
printk(KERN_ERR "Unable to allocate struct usbg_tport");
|
||||
if (!(tport))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
tport->tport_wwpn = wwpn;
|
||||
snprintf(tport->tport_name, sizeof(tport->tport_name), "%s", wnn_name);
|
||||
return &tport->tport_wwn;
|
||||
|
|
@ -1727,10 +1721,8 @@ static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name)
|
|||
|
||||
ret = -ENOMEM;
|
||||
tv_nexus = kzalloc(sizeof(*tv_nexus), GFP_KERNEL);
|
||||
if (!tv_nexus) {
|
||||
pr_err("Unable to allocate struct tcm_vhost_nexus\n");
|
||||
if (!tv_nexus)
|
||||
goto err_unlock;
|
||||
}
|
||||
tv_nexus->tvn_se_sess = transport_init_session(TARGET_PROT_NORMAL);
|
||||
if (IS_ERR(tv_nexus->tvn_se_sess))
|
||||
goto err_free;
|
||||
|
|
|
|||
|
|
@ -818,7 +818,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
|
|||
|
||||
return dev;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_setup_name);
|
||||
EXPORT_SYMBOL_GPL(gether_setup_name);
|
||||
|
||||
struct net_device *gether_setup_name_default(const char *netname)
|
||||
{
|
||||
|
|
@ -855,7 +855,7 @@ struct net_device *gether_setup_name_default(const char *netname)
|
|||
|
||||
return net;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_setup_name_default);
|
||||
EXPORT_SYMBOL_GPL(gether_setup_name_default);
|
||||
|
||||
int gether_register_netdev(struct net_device *net)
|
||||
{
|
||||
|
|
@ -893,7 +893,7 @@ int gether_register_netdev(struct net_device *net)
|
|||
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_register_netdev);
|
||||
EXPORT_SYMBOL_GPL(gether_register_netdev);
|
||||
|
||||
void gether_set_gadget(struct net_device *net, struct usb_gadget *g)
|
||||
{
|
||||
|
|
@ -903,7 +903,7 @@ void gether_set_gadget(struct net_device *net, struct usb_gadget *g)
|
|||
dev->gadget = g;
|
||||
SET_NETDEV_DEV(net, &g->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_set_gadget);
|
||||
EXPORT_SYMBOL_GPL(gether_set_gadget);
|
||||
|
||||
int gether_set_dev_addr(struct net_device *net, const char *dev_addr)
|
||||
{
|
||||
|
|
@ -916,7 +916,7 @@ int gether_set_dev_addr(struct net_device *net, const char *dev_addr)
|
|||
memcpy(dev->dev_mac, new_addr, ETH_ALEN);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_set_dev_addr);
|
||||
EXPORT_SYMBOL_GPL(gether_set_dev_addr);
|
||||
|
||||
int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len)
|
||||
{
|
||||
|
|
@ -925,7 +925,7 @@ int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len)
|
|||
dev = netdev_priv(net);
|
||||
return get_ether_addr_str(dev->dev_mac, dev_addr, len);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_get_dev_addr);
|
||||
EXPORT_SYMBOL_GPL(gether_get_dev_addr);
|
||||
|
||||
int gether_set_host_addr(struct net_device *net, const char *host_addr)
|
||||
{
|
||||
|
|
@ -938,7 +938,7 @@ int gether_set_host_addr(struct net_device *net, const char *host_addr)
|
|||
memcpy(dev->host_mac, new_addr, ETH_ALEN);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_set_host_addr);
|
||||
EXPORT_SYMBOL_GPL(gether_set_host_addr);
|
||||
|
||||
int gether_get_host_addr(struct net_device *net, char *host_addr, int len)
|
||||
{
|
||||
|
|
@ -947,7 +947,7 @@ int gether_get_host_addr(struct net_device *net, char *host_addr, int len)
|
|||
dev = netdev_priv(net);
|
||||
return get_ether_addr_str(dev->host_mac, host_addr, len);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_get_host_addr);
|
||||
EXPORT_SYMBOL_GPL(gether_get_host_addr);
|
||||
|
||||
int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len)
|
||||
{
|
||||
|
|
@ -961,7 +961,7 @@ int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len)
|
|||
|
||||
return strlen(host_addr);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_get_host_addr_cdc);
|
||||
EXPORT_SYMBOL_GPL(gether_get_host_addr_cdc);
|
||||
|
||||
void gether_get_host_addr_u8(struct net_device *net, u8 host_mac[ETH_ALEN])
|
||||
{
|
||||
|
|
@ -970,7 +970,7 @@ void gether_get_host_addr_u8(struct net_device *net, u8 host_mac[ETH_ALEN])
|
|||
dev = netdev_priv(net);
|
||||
memcpy(host_mac, dev->host_mac, ETH_ALEN);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_get_host_addr_u8);
|
||||
EXPORT_SYMBOL_GPL(gether_get_host_addr_u8);
|
||||
|
||||
void gether_set_qmult(struct net_device *net, unsigned qmult)
|
||||
{
|
||||
|
|
@ -979,7 +979,7 @@ void gether_set_qmult(struct net_device *net, unsigned qmult)
|
|||
dev = netdev_priv(net);
|
||||
dev->qmult = qmult;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_set_qmult);
|
||||
EXPORT_SYMBOL_GPL(gether_set_qmult);
|
||||
|
||||
unsigned gether_get_qmult(struct net_device *net)
|
||||
{
|
||||
|
|
@ -988,7 +988,7 @@ unsigned gether_get_qmult(struct net_device *net)
|
|||
dev = netdev_priv(net);
|
||||
return dev->qmult;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_get_qmult);
|
||||
EXPORT_SYMBOL_GPL(gether_get_qmult);
|
||||
|
||||
int gether_get_ifname(struct net_device *net, char *name, int len)
|
||||
{
|
||||
|
|
@ -997,7 +997,7 @@ int gether_get_ifname(struct net_device *net, char *name, int len)
|
|||
rtnl_unlock();
|
||||
return strlen(name);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_get_ifname);
|
||||
EXPORT_SYMBOL_GPL(gether_get_ifname);
|
||||
|
||||
/**
|
||||
* gether_cleanup - remove Ethernet-over-USB device
|
||||
|
|
@ -1014,7 +1014,7 @@ void gether_cleanup(struct eth_dev *dev)
|
|||
flush_work(&dev->work);
|
||||
free_netdev(dev->net);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_cleanup);
|
||||
EXPORT_SYMBOL_GPL(gether_cleanup);
|
||||
|
||||
/**
|
||||
* gether_connect - notify network layer that USB link is active
|
||||
|
|
@ -1095,7 +1095,7 @@ fail0:
|
|||
return ERR_PTR(result);
|
||||
return dev->net;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_connect);
|
||||
EXPORT_SYMBOL_GPL(gether_connect);
|
||||
|
||||
/**
|
||||
* gether_disconnect - notify network layer that USB link is inactive
|
||||
|
|
@ -1166,7 +1166,7 @@ void gether_disconnect(struct gether *link)
|
|||
dev->port_usb = NULL;
|
||||
spin_unlock(&dev->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_disconnect);
|
||||
EXPORT_SYMBOL_GPL(gether_disconnect);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
|
|
|
|||
|
|
@ -29,4 +29,4 @@ struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len)
|
|||
}
|
||||
return req;
|
||||
}
|
||||
EXPORT_SYMBOL(alloc_ep_req);
|
||||
EXPORT_SYMBOL_GPL(alloc_ep_req);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,32 @@
|
|||
#ifndef __U_F_H__
|
||||
#define __U_F_H__
|
||||
|
||||
/* Variable Length Array Macros **********************************************/
|
||||
#define vla_group(groupname) size_t groupname##__next = 0
|
||||
#define vla_group_size(groupname) groupname##__next
|
||||
|
||||
#define vla_item(groupname, type, name, n) \
|
||||
size_t groupname##_##name##__offset = ({ \
|
||||
size_t align_mask = __alignof__(type) - 1; \
|
||||
size_t offset = (groupname##__next + align_mask) & ~align_mask;\
|
||||
size_t size = (n) * sizeof(type); \
|
||||
groupname##__next = offset + size; \
|
||||
offset; \
|
||||
})
|
||||
|
||||
#define vla_item_with_sz(groupname, type, name, n) \
|
||||
size_t groupname##_##name##__sz = (n) * sizeof(type); \
|
||||
size_t groupname##_##name##__offset = ({ \
|
||||
size_t align_mask = __alignof__(type) - 1; \
|
||||
size_t offset = (groupname##__next + align_mask) & ~align_mask;\
|
||||
size_t size = groupname##_##name##__sz; \
|
||||
groupname##__next = offset + size; \
|
||||
offset; \
|
||||
})
|
||||
|
||||
#define vla_ptr(ptr, groupname, name) \
|
||||
((void *) ((char *)ptr + groupname##_##name##__offset))
|
||||
|
||||
struct usb_ep;
|
||||
struct usb_request;
|
||||
|
||||
|
|
|
|||
90
drivers/usb/gadget/u_os_desc.h
Normal file
90
drivers/usb/gadget/u_os_desc.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* u_os_desc.h
|
||||
*
|
||||
* Utility definitions for "OS Descriptors" support
|
||||
*
|
||||
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.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.
|
||||
*/
|
||||
|
||||
#ifndef __U_OS_DESC_H__
|
||||
#define __U_OS_DESC_H__
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/nls.h>
|
||||
|
||||
#define USB_EXT_PROP_DW_SIZE 0
|
||||
#define USB_EXT_PROP_DW_PROPERTY_DATA_TYPE 4
|
||||
#define USB_EXT_PROP_W_PROPERTY_NAME_LENGTH 8
|
||||
#define USB_EXT_PROP_B_PROPERTY_NAME 10
|
||||
#define USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH 10
|
||||
#define USB_EXT_PROP_B_PROPERTY_DATA 14
|
||||
|
||||
#define USB_EXT_PROP_RESERVED 0
|
||||
#define USB_EXT_PROP_UNICODE 1
|
||||
#define USB_EXT_PROP_UNICODE_ENV 2
|
||||
#define USB_EXT_PROP_BINARY 3
|
||||
#define USB_EXT_PROP_LE32 4
|
||||
#define USB_EXT_PROP_BE32 5
|
||||
#define USB_EXT_PROP_UNICODE_LINK 6
|
||||
#define USB_EXT_PROP_UNICODE_MULTI 7
|
||||
|
||||
static inline void usb_ext_prop_put_size(u8 *buf, int dw_size)
|
||||
{
|
||||
put_unaligned_le32(dw_size, &buf[USB_EXT_PROP_DW_SIZE]);
|
||||
}
|
||||
|
||||
static inline void usb_ext_prop_put_type(u8 *buf, int type)
|
||||
{
|
||||
put_unaligned_le32(type, &buf[USB_EXT_PROP_DW_PROPERTY_DATA_TYPE]);
|
||||
}
|
||||
|
||||
static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl)
|
||||
{
|
||||
int result;
|
||||
|
||||
put_unaligned_le16(pnl, &buf[USB_EXT_PROP_W_PROPERTY_NAME_LENGTH]);
|
||||
result = utf8s_to_utf16s(name, strlen(name), UTF16_LITTLE_ENDIAN,
|
||||
(wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_NAME], pnl - 2);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl]);
|
||||
|
||||
return pnl;
|
||||
}
|
||||
|
||||
static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const u8 *data,
|
||||
int data_len)
|
||||
{
|
||||
put_unaligned_le32(data_len,
|
||||
&buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]);
|
||||
memcpy(&buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl], data, data_len);
|
||||
}
|
||||
|
||||
static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string,
|
||||
int data_len)
|
||||
{
|
||||
int result;
|
||||
put_unaligned_le32(data_len,
|
||||
&buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]);
|
||||
|
||||
result = utf8s_to_utf16s(string, data_len >> 1, UTF16_LITTLE_ENDIAN,
|
||||
(wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl],
|
||||
data_len - 2);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
put_unaligned_le16(0,
|
||||
&buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len]);
|
||||
|
||||
return data_len;
|
||||
}
|
||||
|
||||
#endif /* __U_OS_DESC_H__ */
|
||||
|
|
@ -26,6 +26,9 @@ struct f_rndis_opts {
|
|||
bool bound;
|
||||
bool borrowed_net;
|
||||
|
||||
struct usb_os_desc rndis_os_desc;
|
||||
char rndis_ext_compat_id[16];
|
||||
|
||||
/*
|
||||
* Read/write access to configfs attributes is handled by configfs.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -428,6 +428,8 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
|
|||
list_for_each_entry(udc, &udc_list, list)
|
||||
if (udc->driver == driver) {
|
||||
usb_gadget_remove_driver(udc);
|
||||
usb_gadget_set_state(udc->gadget,
|
||||
USB_STATE_NOTATTACHED);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/vmalloc.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/videobuf2-vmalloc.h>
|
||||
|
||||
#include "uvc.h"
|
||||
|
|
@ -136,6 +137,8 @@ static int uvc_queue_init(struct uvc_video_queue *queue,
|
|||
queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
|
||||
queue->queue.ops = &uvc_queue_qops;
|
||||
queue->queue.mem_ops = &vb2_vmalloc_memops;
|
||||
queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
|
||||
| V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
|
||||
ret = vb2_queue_init(&queue->queue);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -379,14 +382,9 @@ static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
|
|||
else
|
||||
nextbuf = NULL;
|
||||
|
||||
/*
|
||||
* FIXME: with videobuf2, the sequence number or timestamp fields
|
||||
* are valid only for video capture devices and the UVC gadget usually
|
||||
* is a video output device. Keeping these until the specs are clear on
|
||||
* this aspect.
|
||||
*/
|
||||
buf->buf.v4l2_buf.field = V4L2_FIELD_NONE;
|
||||
buf->buf.v4l2_buf.sequence = queue->sequence++;
|
||||
do_gettimeofday(&buf->buf.v4l2_buf.timestamp);
|
||||
v4l2_get_timestamp(&buf->buf.v4l2_buf.timestamp);
|
||||
|
||||
vb2_set_plane_payload(&buf->buf, 0, buf->bytesused);
|
||||
vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,14 @@ if USB_XHCI_HCD
|
|||
config USB_XHCI_PLATFORM
|
||||
tristate
|
||||
|
||||
config USB_XHCI_MVEBU
|
||||
tristate "xHCI support for Marvell Armada 375/38x"
|
||||
select USB_XHCI_PLATFORM
|
||||
depends on ARCH_MVEBU || COMPILE_TEST
|
||||
---help---
|
||||
Say 'Y' to enable the support for the xHCI host controller
|
||||
found in Marvell Armada 375/38x ARM SOCs.
|
||||
|
||||
endif # USB_XHCI_HCD
|
||||
|
||||
config USB_EHCI_HCD
|
||||
|
|
@ -170,7 +178,6 @@ config USB_EHCI_MSM
|
|||
tristate "Support for Qualcomm QSD/MSM on-chip EHCI USB controller"
|
||||
depends on ARCH_MSM
|
||||
select USB_EHCI_ROOT_HUB_TT
|
||||
select USB_MSM_OTG
|
||||
---help---
|
||||
Enables support for the USB Host controller present on the
|
||||
Qualcomm chipsets. Root Hub has inbuilt TT.
|
||||
|
|
@ -343,10 +350,19 @@ config USB_FOTG210_HCD
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called fotg210-hcd.
|
||||
|
||||
config USB_MAX3421_HCD
|
||||
tristate "MAX3421 HCD (USB-over-SPI) support"
|
||||
depends on USB && SPI
|
||||
---help---
|
||||
The Maxim MAX3421E chip supports standard USB 2.0-compliant
|
||||
full-speed devices either in host or peripheral mode. This
|
||||
driver supports the host-mode of the MAX3421E only.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called max3421-hcd.
|
||||
|
||||
config USB_OHCI_HCD
|
||||
tristate "OHCI HCD (USB 1.1) support"
|
||||
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
|
||||
depends on USB_ISP1301 || !ARCH_LPC32XX
|
||||
---help---
|
||||
The Open Host Controller Interface (OHCI) is a standard for accessing
|
||||
USB 1.1 host controller hardware. It does more in hardware than Intel's
|
||||
|
|
@ -365,6 +381,7 @@ if USB_OHCI_HCD
|
|||
config USB_OHCI_HCD_OMAP1
|
||||
tristate "OHCI support for OMAP1/2 chips"
|
||||
depends on ARCH_OMAP1
|
||||
depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3)
|
||||
default y
|
||||
---help---
|
||||
Enables support for the OHCI controller on OMAP1/2 chips.
|
||||
|
|
@ -388,6 +405,7 @@ config USB_OHCI_HCD_S3C2410
|
|||
config USB_OHCI_HCD_LPC32XX
|
||||
tristate "Support for LPC on-chip OHCI USB controller"
|
||||
depends on USB_OHCI_HCD && ARCH_LPC32XX
|
||||
depends on USB_ISP1301
|
||||
default y
|
||||
---help---
|
||||
Enables support for the on-chip OHCI controller on
|
||||
|
|
@ -417,6 +435,16 @@ config USB_OHCI_HCD_OMAP3
|
|||
Enables support for the on-chip OHCI controller on
|
||||
OMAP3 and later chips.
|
||||
|
||||
config USB_OHCI_HCD_DAVINCI
|
||||
bool "OHCI support for TI DaVinci DA8xx"
|
||||
depends on ARCH_DAVINCI_DA8XX
|
||||
depends on USB_OHCI_HCD=y
|
||||
default y
|
||||
help
|
||||
Enables support for the DaVinci DA8xx integrated OHCI
|
||||
controller. This driver cannot currently be a loadable
|
||||
module because it lacks a proper PHY abstraction.
|
||||
|
||||
config USB_OHCI_ATH79
|
||||
bool "USB OHCI support for the Atheros AR71XX/AR7240 SoCs (DEPRECATED)"
|
||||
depends on (SOC_AR71XX || SOC_AR724X)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ xhci-hcd-$(CONFIG_PCI) += xhci-pci.o
|
|||
|
||||
ifneq ($(CONFIG_USB_XHCI_PLATFORM), )
|
||||
xhci-hcd-y += xhci-plat.o
|
||||
ifneq ($(CONFIG_USB_XHCI_MVEBU), )
|
||||
xhci-hcd-y += xhci-mvebu.o
|
||||
endif
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB_WHCI_HCD) += whci/
|
||||
|
|
@ -70,3 +73,4 @@ obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o
|
|||
obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o
|
||||
obj-$(CONFIG_USB_FUSBH200_HCD) += fusbh200-hcd.o
|
||||
obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o
|
||||
obj-$(CONFIG_USB_MAX3421_HCD) += max3421-hcd.o
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/usb/samsung_usb_phy.h>
|
||||
|
|
@ -42,17 +43,106 @@
|
|||
static const char hcd_name[] = "ehci-exynos";
|
||||
static struct hc_driver __read_mostly exynos_ehci_hc_driver;
|
||||
|
||||
#define PHY_NUMBER 3
|
||||
|
||||
struct exynos_ehci_hcd {
|
||||
struct clk *clk;
|
||||
struct usb_phy *phy;
|
||||
struct usb_otg *otg;
|
||||
struct phy *phy_g[PHY_NUMBER];
|
||||
};
|
||||
|
||||
#define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
|
||||
|
||||
static void exynos_setup_vbus_gpio(struct platform_device *pdev)
|
||||
static int exynos_ehci_get_phy(struct device *dev,
|
||||
struct exynos_ehci_hcd *exynos_ehci)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct phy *phy;
|
||||
int phy_number;
|
||||
int ret = 0;
|
||||
|
||||
exynos_ehci->phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR(exynos_ehci->phy)) {
|
||||
ret = PTR_ERR(exynos_ehci->phy);
|
||||
if (ret != -ENXIO && ret != -ENODEV) {
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "Failed to get usb2 phy\n");
|
||||
} else {
|
||||
exynos_ehci->otg = exynos_ehci->phy->otg;
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(dev->of_node, child) {
|
||||
ret = of_property_read_u32(child, "reg", &phy_number);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to parse device tree\n");
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (phy_number >= PHY_NUMBER) {
|
||||
dev_err(dev, "Invalid number of PHYs\n");
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phy = devm_of_phy_get(dev, child, 0);
|
||||
of_node_put(child);
|
||||
if (IS_ERR(phy)) {
|
||||
ret = PTR_ERR(phy);
|
||||
if (ret != -ENOSYS && ret != -ENODEV) {
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "Failed to get usb2 phy\n");
|
||||
}
|
||||
exynos_ehci->phy_g[phy_number] = phy;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_ehci_phy_enable(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
if (!IS_ERR(exynos_ehci->phy))
|
||||
return usb_phy_init(exynos_ehci->phy);
|
||||
|
||||
for (i = 0; ret == 0 && i < PHY_NUMBER; i++)
|
||||
if (!IS_ERR(exynos_ehci->phy_g[i]))
|
||||
ret = phy_power_on(exynos_ehci->phy_g[i]);
|
||||
if (ret)
|
||||
for (i--; i >= 0; i--)
|
||||
if (!IS_ERR(exynos_ehci->phy_g[i]))
|
||||
phy_power_off(exynos_ehci->phy_g[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exynos_ehci_phy_disable(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
|
||||
int i;
|
||||
|
||||
if (!IS_ERR(exynos_ehci->phy)) {
|
||||
usb_phy_shutdown(exynos_ehci->phy);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < PHY_NUMBER; i++)
|
||||
if (!IS_ERR(exynos_ehci->phy_g[i]))
|
||||
phy_power_off(exynos_ehci->phy_g[i]);
|
||||
}
|
||||
|
||||
static void exynos_setup_vbus_gpio(struct device *dev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int err;
|
||||
int gpio;
|
||||
|
||||
|
|
@ -75,7 +165,6 @@ static int exynos_ehci_probe(struct platform_device *pdev)
|
|||
struct usb_hcd *hcd;
|
||||
struct ehci_hcd *ehci;
|
||||
struct resource *res;
|
||||
struct usb_phy *phy;
|
||||
int irq;
|
||||
int err;
|
||||
|
||||
|
|
@ -88,7 +177,7 @@ static int exynos_ehci_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
exynos_setup_vbus_gpio(pdev);
|
||||
exynos_setup_vbus_gpio(&pdev->dev);
|
||||
|
||||
hcd = usb_create_hcd(&exynos_ehci_hc_driver,
|
||||
&pdev->dev, dev_name(&pdev->dev));
|
||||
|
|
@ -102,15 +191,9 @@ static int exynos_ehci_probe(struct platform_device *pdev)
|
|||
"samsung,exynos5440-ehci"))
|
||||
goto skip_phy;
|
||||
|
||||
phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR(phy)) {
|
||||
usb_put_hcd(hcd);
|
||||
dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
|
||||
return -EPROBE_DEFER;
|
||||
} else {
|
||||
exynos_ehci->phy = phy;
|
||||
exynos_ehci->otg = phy->otg;
|
||||
}
|
||||
err = exynos_ehci_get_phy(&pdev->dev, exynos_ehci);
|
||||
if (err)
|
||||
goto fail_clk;
|
||||
|
||||
skip_phy:
|
||||
|
||||
|
|
@ -135,10 +218,9 @@ skip_phy:
|
|||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len);
|
||||
if (!hcd->regs) {
|
||||
dev_err(&pdev->dev, "Failed to remap I/O memory\n");
|
||||
err = -ENOMEM;
|
||||
hcd->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(hcd->regs)) {
|
||||
err = PTR_ERR(hcd->regs);
|
||||
goto fail_io;
|
||||
}
|
||||
|
||||
|
|
@ -152,8 +234,11 @@ skip_phy:
|
|||
if (exynos_ehci->otg)
|
||||
exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
|
||||
|
||||
if (exynos_ehci->phy)
|
||||
usb_phy_init(exynos_ehci->phy);
|
||||
err = exynos_ehci_phy_enable(&pdev->dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to enable USB phy\n");
|
||||
goto fail_io;
|
||||
}
|
||||
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
ehci->caps = hcd->regs;
|
||||
|
|
@ -173,8 +258,7 @@ skip_phy:
|
|||
return 0;
|
||||
|
||||
fail_add_hcd:
|
||||
if (exynos_ehci->phy)
|
||||
usb_phy_shutdown(exynos_ehci->phy);
|
||||
exynos_ehci_phy_disable(&pdev->dev);
|
||||
fail_io:
|
||||
clk_disable_unprepare(exynos_ehci->clk);
|
||||
fail_clk:
|
||||
|
|
@ -192,8 +276,7 @@ static int exynos_ehci_remove(struct platform_device *pdev)
|
|||
if (exynos_ehci->otg)
|
||||
exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
|
||||
|
||||
if (exynos_ehci->phy)
|
||||
usb_phy_shutdown(exynos_ehci->phy);
|
||||
exynos_ehci_phy_disable(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(exynos_ehci->clk);
|
||||
|
||||
|
|
@ -218,8 +301,7 @@ static int exynos_ehci_suspend(struct device *dev)
|
|||
if (exynos_ehci->otg)
|
||||
exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
|
||||
|
||||
if (exynos_ehci->phy)
|
||||
usb_phy_shutdown(exynos_ehci->phy);
|
||||
exynos_ehci_phy_disable(dev);
|
||||
|
||||
clk_disable_unprepare(exynos_ehci->clk);
|
||||
|
||||
|
|
@ -230,14 +312,19 @@ static int exynos_ehci_resume(struct device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
|
||||
int ret;
|
||||
|
||||
clk_prepare_enable(exynos_ehci->clk);
|
||||
|
||||
if (exynos_ehci->otg)
|
||||
exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
|
||||
|
||||
if (exynos_ehci->phy)
|
||||
usb_phy_init(exynos_ehci->phy);
|
||||
ret = exynos_ehci_phy_enable(dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable USB phy\n");
|
||||
clk_disable_unprepare(exynos_ehci->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* DMA burst Enable */
|
||||
writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
|
||||
|
|
|
|||
|
|
@ -33,15 +33,6 @@
|
|||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int ehci_hub_control(
|
||||
struct usb_hcd *hcd,
|
||||
u16 typeReq,
|
||||
u16 wValue,
|
||||
u16 wIndex,
|
||||
char *buf,
|
||||
u16 wLength
|
||||
);
|
||||
|
||||
static int persist_enabled_on_companion(struct usb_device *udev, void *unused)
|
||||
{
|
||||
return !udev->maxchild && udev->persist_enabled &&
|
||||
|
|
@ -865,7 +856,7 @@ cleanup:
|
|||
#endif /* CONFIG_USB_HCD_TEST_MODE */
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int ehci_hub_control (
|
||||
int ehci_hub_control(
|
||||
struct usb_hcd *hcd,
|
||||
u16 typeReq,
|
||||
u16 wValue,
|
||||
|
|
@ -1285,6 +1276,7 @@ error_exit:
|
|||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ehci_hub_control);
|
||||
|
||||
static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -96,10 +96,9 @@ static int ehci_msm_probe(struct platform_device *pdev)
|
|||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
hcd->regs = devm_ioremap(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (!hcd->regs) {
|
||||
dev_err(&pdev->dev, "ioremap failed\n");
|
||||
ret = -ENOMEM;
|
||||
hcd->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(hcd->regs)) {
|
||||
ret = PTR_ERR(hcd->regs);
|
||||
goto put_hcd;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -176,11 +176,9 @@ static int mv_ehci_probe(struct platform_device *pdev)
|
|||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
ehci_mv->phy_regs = devm_ioremap(&pdev->dev, r->start,
|
||||
resource_size(r));
|
||||
if (!ehci_mv->phy_regs) {
|
||||
dev_err(&pdev->dev, "failed to map phy I/O memory\n");
|
||||
retval = -EFAULT;
|
||||
ehci_mv->phy_regs = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(ehci_mv->phy_regs)) {
|
||||
retval = PTR_ERR(ehci_mv->phy_regs);
|
||||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
|
|
@ -191,11 +189,9 @@ static int mv_ehci_probe(struct platform_device *pdev)
|
|||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
ehci_mv->cap_regs = devm_ioremap(&pdev->dev, r->start,
|
||||
resource_size(r));
|
||||
if (ehci_mv->cap_regs == NULL) {
|
||||
dev_err(&pdev->dev, "failed to map I/O memory\n");
|
||||
retval = -EFAULT;
|
||||
ehci_mv->cap_regs = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(ehci_mv->cap_regs)) {
|
||||
retval = PTR_ERR(ehci_mv->cap_regs);
|
||||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/platform_data/usb-ehci-orion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/usb.h>
|
||||
|
|
@ -42,6 +43,13 @@
|
|||
|
||||
#define DRIVER_DESC "EHCI orion driver"
|
||||
|
||||
#define hcd_to_orion_priv(h) ((struct orion_ehci_hcd *)hcd_to_ehci(h)->priv)
|
||||
|
||||
struct orion_ehci_hcd {
|
||||
struct clk *clk;
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
static const char hcd_name[] = "ehci-orion";
|
||||
|
||||
static struct hc_driver __read_mostly ehci_orion_hc_driver;
|
||||
|
|
@ -137,6 +145,10 @@ ehci_orion_conf_mbus_windows(struct usb_hcd *hcd,
|
|||
}
|
||||
}
|
||||
|
||||
static const struct ehci_driver_overrides orion_overrides __initconst = {
|
||||
.extra_priv_size = sizeof(struct orion_ehci_hcd),
|
||||
};
|
||||
|
||||
static int ehci_orion_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct orion_ehci_data *pd = dev_get_platdata(&pdev->dev);
|
||||
|
|
@ -144,26 +156,23 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
|
|||
struct resource *res;
|
||||
struct usb_hcd *hcd;
|
||||
struct ehci_hcd *ehci;
|
||||
struct clk *clk;
|
||||
void __iomem *regs;
|
||||
int irq, err;
|
||||
enum orion_ehci_phy_ver phy_version;
|
||||
struct orion_ehci_hcd *priv;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
pr_debug("Initializing Orion-SoC USB Host Controller\n");
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
||||
else
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"Found HC with no IRQ. Check %s setup!\n",
|
||||
dev_name(&pdev->dev));
|
||||
err = -ENODEV;
|
||||
goto err1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
|
@ -172,7 +181,7 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
|
|||
"Found HC with no register addr. Check %s setup!\n",
|
||||
dev_name(&pdev->dev));
|
||||
err = -ENODEV;
|
||||
goto err1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -182,25 +191,19 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
|
|||
*/
|
||||
err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
||||
if (err)
|
||||
goto err1;
|
||||
goto err;
|
||||
|
||||
regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(regs)) {
|
||||
err = PTR_ERR(regs);
|
||||
goto err1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Not all platforms can gate the clock, so it is not
|
||||
an error if the clock does not exists. */
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (!IS_ERR(clk))
|
||||
clk_prepare_enable(clk);
|
||||
|
||||
hcd = usb_create_hcd(&ehci_orion_hc_driver,
|
||||
&pdev->dev, dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
err = -ENOMEM;
|
||||
goto err2;
|
||||
goto err;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = res->start;
|
||||
|
|
@ -211,6 +214,29 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
|
|||
ehci->caps = hcd->regs + 0x100;
|
||||
hcd->has_tt = 1;
|
||||
|
||||
priv = hcd_to_orion_priv(hcd);
|
||||
/*
|
||||
* Not all platforms can gate the clock, so it is not an error if
|
||||
* the clock does not exists.
|
||||
*/
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_prepare_enable(priv->clk);
|
||||
|
||||
priv->phy = devm_phy_optional_get(&pdev->dev, "usb");
|
||||
if (IS_ERR(priv->phy)) {
|
||||
err = PTR_ERR(priv->phy);
|
||||
goto err_phy_get;
|
||||
} else {
|
||||
err = phy_init(priv->phy);
|
||||
if (err)
|
||||
goto err_phy_init;
|
||||
|
||||
err = phy_power_on(priv->phy);
|
||||
if (err)
|
||||
goto err_phy_power_on;
|
||||
}
|
||||
|
||||
/*
|
||||
* (Re-)program MBUS remapping windows if we are asked to.
|
||||
*/
|
||||
|
|
@ -240,17 +266,23 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
|
|||
|
||||
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (err)
|
||||
goto err3;
|
||||
goto err_add_hcd;
|
||||
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
err_add_hcd:
|
||||
if (!IS_ERR(priv->phy))
|
||||
phy_power_off(priv->phy);
|
||||
err_phy_power_on:
|
||||
if (!IS_ERR(priv->phy))
|
||||
phy_exit(priv->phy);
|
||||
err_phy_init:
|
||||
err_phy_get:
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_disable_unprepare(priv->clk);
|
||||
usb_put_hcd(hcd);
|
||||
err2:
|
||||
if (!IS_ERR(clk))
|
||||
clk_disable_unprepare(clk);
|
||||
err1:
|
||||
err:
|
||||
dev_err(&pdev->dev, "init %s fail, %d\n",
|
||||
dev_name(&pdev->dev), err);
|
||||
|
||||
|
|
@ -260,14 +292,20 @@ err1:
|
|||
static int ehci_orion_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
struct clk *clk;
|
||||
struct orion_ehci_hcd *priv = hcd_to_orion_priv(hcd);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
if (!IS_ERR(priv->phy)) {
|
||||
phy_power_off(priv->phy);
|
||||
phy_exit(priv->phy);
|
||||
}
|
||||
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (!IS_ERR(clk))
|
||||
clk_disable_unprepare(clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -295,7 +333,7 @@ static int __init ehci_orion_init(void)
|
|||
|
||||
pr_info("%s: " DRIVER_DESC "\n", hcd_name);
|
||||
|
||||
ehci_init_driver(&ehci_orion_hc_driver, NULL);
|
||||
ehci_init_driver(&ehci_orion_hc_driver, &orion_overrides);
|
||||
return platform_driver_register(&ehci_orion_driver);
|
||||
}
|
||||
module_init(ehci_orion_init);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/ehci_pdriver.h>
|
||||
|
|
@ -41,6 +42,7 @@
|
|||
|
||||
struct ehci_platform_priv {
|
||||
struct clk *clks[EHCI_MAX_CLKS];
|
||||
struct reset_control *rst;
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
|
|
@ -208,6 +210,18 @@ static int ehci_platform_probe(struct platform_device *dev)
|
|||
}
|
||||
}
|
||||
|
||||
priv->rst = devm_reset_control_get_optional(&dev->dev, NULL);
|
||||
if (IS_ERR(priv->rst)) {
|
||||
err = PTR_ERR(priv->rst);
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_put_clks;
|
||||
priv->rst = NULL;
|
||||
} else {
|
||||
err = reset_control_deassert(priv->rst);
|
||||
if (err)
|
||||
goto err_put_clks;
|
||||
}
|
||||
|
||||
if (pdata->big_endian_desc)
|
||||
ehci->big_endian_desc = 1;
|
||||
if (pdata->big_endian_mmio)
|
||||
|
|
@ -218,7 +232,7 @@ static int ehci_platform_probe(struct platform_device *dev)
|
|||
dev_err(&dev->dev,
|
||||
"Error: CONFIG_USB_EHCI_BIG_ENDIAN_MMIO not set\n");
|
||||
err = -EINVAL;
|
||||
goto err_put_clks;
|
||||
goto err_reset;
|
||||
}
|
||||
#endif
|
||||
#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
|
||||
|
|
@ -226,14 +240,14 @@ static int ehci_platform_probe(struct platform_device *dev)
|
|||
dev_err(&dev->dev,
|
||||
"Error: CONFIG_USB_EHCI_BIG_ENDIAN_DESC not set\n");
|
||||
err = -EINVAL;
|
||||
goto err_put_clks;
|
||||
goto err_reset;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pdata->power_on) {
|
||||
err = pdata->power_on(dev);
|
||||
if (err < 0)
|
||||
goto err_put_clks;
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = res_mem->start;
|
||||
|
|
@ -256,6 +270,9 @@ static int ehci_platform_probe(struct platform_device *dev)
|
|||
err_power:
|
||||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
err_reset:
|
||||
if (priv->rst)
|
||||
reset_control_assert(priv->rst);
|
||||
err_put_clks:
|
||||
while (--clk >= 0)
|
||||
clk_put(priv->clks[clk]);
|
||||
|
|
@ -280,6 +297,9 @@ static int ehci_platform_remove(struct platform_device *dev)
|
|||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
|
||||
if (priv->rst)
|
||||
reset_control_assert(priv->rst);
|
||||
|
||||
for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++)
|
||||
clk_put(priv->clks[clk]);
|
||||
|
||||
|
|
|
|||
|
|
@ -106,16 +106,9 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev)
|
|||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
if (!devm_request_mem_region(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len,
|
||||
driver->description)) {
|
||||
retval = -EBUSY;
|
||||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
hcd->regs = devm_ioremap(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (hcd->regs == NULL) {
|
||||
dev_dbg(&pdev->dev, "error mapping memory\n");
|
||||
retval = -ENOMEM;
|
||||
hcd->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(hcd->regs)) {
|
||||
retval = PTR_ERR(hcd->regs);
|
||||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,10 +51,6 @@ struct tegra_ehci_soc_config {
|
|||
bool has_hostpc;
|
||||
};
|
||||
|
||||
static int (*orig_hub_control)(struct usb_hcd *hcd,
|
||||
u16 typeReq, u16 wValue, u16 wIndex,
|
||||
char *buf, u16 wLength);
|
||||
|
||||
struct tegra_ehci_hcd {
|
||||
struct tegra_usb_phy *phy;
|
||||
struct clk *clk;
|
||||
|
|
@ -236,7 +232,7 @@ static int tegra_ehci_hub_control(
|
|||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
|
||||
/* Handle the hub control events here */
|
||||
return orig_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
|
||||
return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
|
|
@ -415,10 +411,9 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
|||
}
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
hcd->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!hcd->regs) {
|
||||
dev_err(&pdev->dev, "Failed to remap I/O memory\n");
|
||||
err = -ENOMEM;
|
||||
hcd->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(hcd->regs)) {
|
||||
err = PTR_ERR(hcd->regs);
|
||||
goto cleanup_clk_en;
|
||||
}
|
||||
ehci->caps = hcd->regs + 0x100;
|
||||
|
|
@ -554,8 +549,6 @@ static int __init ehci_tegra_init(void)
|
|||
* too easy.
|
||||
*/
|
||||
|
||||
orig_hub_control = tegra_ehci_hc_driver.hub_control;
|
||||
|
||||
tegra_ehci_hc_driver.map_urb_for_dma = tegra_ehci_map_urb_for_dma;
|
||||
tegra_ehci_hc_driver.unmap_urb_for_dma = tegra_ehci_unmap_urb_for_dma;
|
||||
tegra_ehci_hc_driver.hub_control = tegra_ehci_hub_control;
|
||||
|
|
|
|||
|
|
@ -872,4 +872,7 @@ extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup);
|
|||
extern int ehci_resume(struct usb_hcd *hcd, bool hibernated);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
u16 wIndex, char *buf, u16 wLength);
|
||||
|
||||
#endif /* __LINUX_EHCI_HCD_H */
|
||||
|
|
|
|||
1957
drivers/usb/host/max3421-hcd.c
Normal file
1957
drivers/usb/host/max3421-hcd.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -46,9 +46,6 @@ static const char hcd_name[] = "ohci-atmel";
|
|||
|
||||
static struct hc_driver __read_mostly ohci_at91_hc_driver;
|
||||
static int clocked;
|
||||
static int (*orig_ohci_hub_control)(struct usb_hcd *hcd, u16 typeReq,
|
||||
u16 wValue, u16 wIndex, char *buf, u16 wLength);
|
||||
static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf);
|
||||
|
||||
extern int usb_disabled(void);
|
||||
|
||||
|
|
@ -262,7 +259,7 @@ static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port)
|
|||
static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf)
|
||||
{
|
||||
struct at91_usbh_data *pdata = hcd->self.controller->platform_data;
|
||||
int length = orig_ohci_hub_status_data(hcd, buf);
|
||||
int length = ohci_hub_status_data(hcd, buf);
|
||||
int port;
|
||||
|
||||
at91_for_each_port(port) {
|
||||
|
|
@ -340,8 +337,7 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|||
break;
|
||||
}
|
||||
|
||||
ret = orig_ohci_hub_control(hcd, typeReq, wValue, wIndex + 1,
|
||||
buf, wLength);
|
||||
ret = ohci_hub_control(hcd, typeReq, wValue, wIndex + 1, buf, wLength);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
|
@ -690,9 +686,6 @@ static int __init ohci_at91_init(void)
|
|||
* too easy.
|
||||
*/
|
||||
|
||||
orig_ohci_hub_control = ohci_at91_hc_driver.hub_control;
|
||||
orig_ohci_hub_status_data = ohci_at91_hc_driver.hub_status_data;
|
||||
|
||||
ohci_at91_hc_driver.hub_status_data = ohci_at91_hub_status_data;
|
||||
ohci_at91_hc_driver.hub_control = ohci_at91_hub_control;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/usb/samsung_usb_phy.h>
|
||||
#include <linux/usb.h>
|
||||
|
|
@ -33,28 +34,110 @@ static struct hc_driver __read_mostly exynos_ohci_hc_driver;
|
|||
|
||||
#define to_exynos_ohci(hcd) (struct exynos_ohci_hcd *)(hcd_to_ohci(hcd)->priv)
|
||||
|
||||
#define PHY_NUMBER 3
|
||||
|
||||
struct exynos_ohci_hcd {
|
||||
struct clk *clk;
|
||||
struct usb_phy *phy;
|
||||
struct usb_otg *otg;
|
||||
struct phy *phy_g[PHY_NUMBER];
|
||||
};
|
||||
|
||||
static void exynos_ohci_phy_enable(struct platform_device *pdev)
|
||||
static int exynos_ohci_get_phy(struct device *dev,
|
||||
struct exynos_ohci_hcd *exynos_ohci)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
|
||||
struct device_node *child;
|
||||
struct phy *phy;
|
||||
int phy_number;
|
||||
int ret = 0;
|
||||
|
||||
if (exynos_ohci->phy)
|
||||
usb_phy_init(exynos_ohci->phy);
|
||||
exynos_ohci->phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR(exynos_ohci->phy)) {
|
||||
ret = PTR_ERR(exynos_ohci->phy);
|
||||
if (ret != -ENXIO && ret != -ENODEV) {
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "Failed to get usb2 phy\n");
|
||||
} else {
|
||||
exynos_ohci->otg = exynos_ohci->phy->otg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Getting generic phy:
|
||||
* We are keeping both types of phys as a part of transiting OHCI
|
||||
* to generic phy framework, so as to maintain backward compatibilty
|
||||
* with old DTB.
|
||||
* If there are existing devices using DTB files built from them,
|
||||
* to remove the support for old bindings in this driver,
|
||||
* we need to make sure that such devices have their DTBs
|
||||
* updated to ones built from new DTS.
|
||||
*/
|
||||
for_each_available_child_of_node(dev->of_node, child) {
|
||||
ret = of_property_read_u32(child, "reg", &phy_number);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to parse device tree\n");
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (phy_number >= PHY_NUMBER) {
|
||||
dev_err(dev, "Invalid number of PHYs\n");
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phy = devm_of_phy_get(dev, child, 0);
|
||||
of_node_put(child);
|
||||
if (IS_ERR(phy)) {
|
||||
ret = PTR_ERR(phy);
|
||||
if (ret != -ENOSYS && ret != -ENODEV) {
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "Failed to get usb2 phy\n");
|
||||
}
|
||||
exynos_ohci->phy_g[phy_number] = phy;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exynos_ohci_phy_disable(struct platform_device *pdev)
|
||||
static int exynos_ohci_phy_enable(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
if (exynos_ohci->phy)
|
||||
if (!IS_ERR(exynos_ohci->phy))
|
||||
return usb_phy_init(exynos_ohci->phy);
|
||||
|
||||
for (i = 0; ret == 0 && i < PHY_NUMBER; i++)
|
||||
if (!IS_ERR(exynos_ohci->phy_g[i]))
|
||||
ret = phy_power_on(exynos_ohci->phy_g[i]);
|
||||
if (ret)
|
||||
for (i--; i >= 0; i--)
|
||||
if (!IS_ERR(exynos_ohci->phy_g[i]))
|
||||
phy_power_off(exynos_ohci->phy_g[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exynos_ohci_phy_disable(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
|
||||
int i;
|
||||
|
||||
if (!IS_ERR(exynos_ohci->phy)) {
|
||||
usb_phy_shutdown(exynos_ohci->phy);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < PHY_NUMBER; i++)
|
||||
if (!IS_ERR(exynos_ohci->phy_g[i]))
|
||||
phy_power_off(exynos_ohci->phy_g[i]);
|
||||
}
|
||||
|
||||
static int exynos_ohci_probe(struct platform_device *pdev)
|
||||
|
|
@ -62,7 +145,6 @@ static int exynos_ohci_probe(struct platform_device *pdev)
|
|||
struct exynos_ohci_hcd *exynos_ohci;
|
||||
struct usb_hcd *hcd;
|
||||
struct resource *res;
|
||||
struct usb_phy *phy;
|
||||
int irq;
|
||||
int err;
|
||||
|
||||
|
|
@ -88,15 +170,9 @@ static int exynos_ohci_probe(struct platform_device *pdev)
|
|||
"samsung,exynos5440-ohci"))
|
||||
goto skip_phy;
|
||||
|
||||
phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR(phy)) {
|
||||
usb_put_hcd(hcd);
|
||||
dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
|
||||
return -EPROBE_DEFER;
|
||||
} else {
|
||||
exynos_ohci->phy = phy;
|
||||
exynos_ohci->otg = phy->otg;
|
||||
}
|
||||
err = exynos_ohci_get_phy(&pdev->dev, exynos_ohci);
|
||||
if (err)
|
||||
goto fail_clk;
|
||||
|
||||
skip_phy:
|
||||
exynos_ohci->clk = devm_clk_get(&pdev->dev, "usbhost");
|
||||
|
|
@ -120,10 +196,9 @@ skip_phy:
|
|||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len);
|
||||
if (!hcd->regs) {
|
||||
dev_err(&pdev->dev, "Failed to remap I/O memory\n");
|
||||
err = -ENOMEM;
|
||||
hcd->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(hcd->regs)) {
|
||||
err = PTR_ERR(hcd->regs);
|
||||
goto fail_io;
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +214,11 @@ skip_phy:
|
|||
|
||||
platform_set_drvdata(pdev, hcd);
|
||||
|
||||
exynos_ohci_phy_enable(pdev);
|
||||
err = exynos_ohci_phy_enable(&pdev->dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to enable USB phy\n");
|
||||
goto fail_io;
|
||||
}
|
||||
|
||||
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (err) {
|
||||
|
|
@ -150,7 +229,7 @@ skip_phy:
|
|||
return 0;
|
||||
|
||||
fail_add_hcd:
|
||||
exynos_ohci_phy_disable(pdev);
|
||||
exynos_ohci_phy_disable(&pdev->dev);
|
||||
fail_io:
|
||||
clk_disable_unprepare(exynos_ohci->clk);
|
||||
fail_clk:
|
||||
|
|
@ -168,7 +247,7 @@ static int exynos_ohci_remove(struct platform_device *pdev)
|
|||
if (exynos_ohci->otg)
|
||||
exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self);
|
||||
|
||||
exynos_ohci_phy_disable(pdev);
|
||||
exynos_ohci_phy_disable(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(exynos_ohci->clk);
|
||||
|
||||
|
|
@ -190,26 +269,19 @@ static int exynos_ohci_suspend(struct device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
bool do_wakeup = device_may_wakeup(dev);
|
||||
unsigned long flags;
|
||||
int rc = ohci_suspend(hcd, do_wakeup);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
spin_lock_irqsave(&ohci->lock, flags);
|
||||
|
||||
if (exynos_ohci->otg)
|
||||
exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self);
|
||||
|
||||
exynos_ohci_phy_disable(pdev);
|
||||
exynos_ohci_phy_disable(dev);
|
||||
|
||||
clk_disable_unprepare(exynos_ohci->clk);
|
||||
|
||||
spin_unlock_irqrestore(&ohci->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -217,14 +289,19 @@ static int exynos_ohci_resume(struct device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int ret;
|
||||
|
||||
clk_prepare_enable(exynos_ohci->clk);
|
||||
|
||||
if (exynos_ohci->otg)
|
||||
exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self);
|
||||
|
||||
exynos_ohci_phy_enable(pdev);
|
||||
ret = exynos_ohci_phy_enable(dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable USB phy\n");
|
||||
clk_disable_unprepare(exynos_ohci->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ohci_resume(hcd, false);
|
||||
|
||||
|
|
|
|||
|
|
@ -1178,7 +1178,7 @@ MODULE_LICENSE ("GPL");
|
|||
#define SA1111_DRIVER ohci_hcd_sa1111_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_DAVINCI_DA8XX
|
||||
#ifdef CONFIG_USB_OHCI_HCD_DAVINCI
|
||||
#include "ohci-da8xx.c"
|
||||
#define DAVINCI_PLATFORM_DRIVER ohci_hcd_da8xx_driver
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -456,8 +456,7 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
|
|||
|
||||
/* build "status change" packet (one or two bytes) from HC registers */
|
||||
|
||||
static int
|
||||
ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
|
||||
int ohci_hub_status_data(struct usb_hcd *hcd, char *buf)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
int i, changed = 0, length = 1;
|
||||
|
|
@ -522,6 +521,7 @@ done:
|
|||
|
||||
return changed ? length : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ohci_hub_status_data);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
|
@ -664,7 +664,7 @@ static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ohci_hub_control (
|
||||
int ohci_hub_control(
|
||||
struct usb_hcd *hcd,
|
||||
u16 typeReq,
|
||||
u16 wValue,
|
||||
|
|
@ -790,4 +790,4 @@ error:
|
|||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(ohci_hub_control);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/usb/ohci_pdriver.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
|
@ -36,6 +37,7 @@
|
|||
|
||||
struct ohci_platform_priv {
|
||||
struct clk *clks[OHCI_MAX_CLKS];
|
||||
struct reset_control *rst;
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
|
|
@ -191,6 +193,19 @@ static int ohci_platform_probe(struct platform_device *dev)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
priv->rst = devm_reset_control_get_optional(&dev->dev, NULL);
|
||||
if (IS_ERR(priv->rst)) {
|
||||
err = PTR_ERR(priv->rst);
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_put_clks;
|
||||
priv->rst = NULL;
|
||||
} else {
|
||||
err = reset_control_deassert(priv->rst);
|
||||
if (err)
|
||||
goto err_put_clks;
|
||||
}
|
||||
|
||||
if (pdata->big_endian_desc)
|
||||
|
|
@ -203,7 +218,7 @@ static int ohci_platform_probe(struct platform_device *dev)
|
|||
dev_err(&dev->dev,
|
||||
"Error: CONFIG_USB_OHCI_BIG_ENDIAN_MMIO not set\n");
|
||||
err = -EINVAL;
|
||||
goto err_put_clks;
|
||||
goto err_reset;
|
||||
}
|
||||
#endif
|
||||
#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_DESC
|
||||
|
|
@ -211,14 +226,14 @@ static int ohci_platform_probe(struct platform_device *dev)
|
|||
dev_err(&dev->dev,
|
||||
"Error: CONFIG_USB_OHCI_BIG_ENDIAN_DESC not set\n");
|
||||
err = -EINVAL;
|
||||
goto err_put_clks;
|
||||
goto err_reset;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pdata->power_on) {
|
||||
err = pdata->power_on(dev);
|
||||
if (err < 0)
|
||||
goto err_put_clks;
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = res_mem->start;
|
||||
|
|
@ -242,6 +257,9 @@ static int ohci_platform_probe(struct platform_device *dev)
|
|||
err_power:
|
||||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
err_reset:
|
||||
if (priv->rst)
|
||||
reset_control_assert(priv->rst);
|
||||
err_put_clks:
|
||||
while (--clk >= 0)
|
||||
clk_put(priv->clks[clk]);
|
||||
|
|
@ -266,6 +284,9 @@ static int ohci_platform_remove(struct platform_device *dev)
|
|||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
|
||||
if (priv->rst)
|
||||
reset_control_assert(priv->rst);
|
||||
|
||||
for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++)
|
||||
clk_put(priv->clks[clk]);
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <linux/platform_data/usb-ohci-pxa27x.h>
|
||||
#include <linux/platform_data/usb-pxa3xx-ulpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
|
@ -120,6 +121,8 @@ static struct hc_driver __read_mostly ohci_pxa27x_hc_driver;
|
|||
struct pxa27x_ohci {
|
||||
struct clk *clk;
|
||||
void __iomem *mmio_base;
|
||||
struct regulator *vbus[3];
|
||||
bool vbus_enabled[3];
|
||||
};
|
||||
|
||||
#define to_pxa27x_ohci(hcd) (struct pxa27x_ohci *)(hcd_to_ohci(hcd)->priv)
|
||||
|
|
@ -166,6 +169,52 @@ static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *pxa_ohci, int mode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pxa27x_ohci_set_vbus_power(struct pxa27x_ohci *pxa_ohci,
|
||||
unsigned int port, bool enable)
|
||||
{
|
||||
struct regulator *vbus = pxa_ohci->vbus[port];
|
||||
int ret = 0;
|
||||
|
||||
if (IS_ERR_OR_NULL(vbus))
|
||||
return 0;
|
||||
|
||||
if (enable && !pxa_ohci->vbus_enabled[port])
|
||||
ret = regulator_enable(vbus);
|
||||
else if (!enable && pxa_ohci->vbus_enabled[port])
|
||||
ret = regulator_disable(vbus);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pxa_ohci->vbus_enabled[port] = enable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pxa27x_ohci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
u16 wIndex, char *buf, u16 wLength)
|
||||
{
|
||||
struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
|
||||
int ret;
|
||||
|
||||
switch (typeReq) {
|
||||
case SetPortFeature:
|
||||
case ClearPortFeature:
|
||||
if (!wIndex || wIndex > 3)
|
||||
return -EPIPE;
|
||||
|
||||
if (wValue != USB_PORT_FEAT_POWER)
|
||||
break;
|
||||
|
||||
ret = pxa27x_ohci_set_vbus_power(pxa_ohci, wIndex - 1,
|
||||
typeReq == SetPortFeature);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
|
||||
return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static inline void pxa27x_setup_hc(struct pxa27x_ohci *pxa_ohci,
|
||||
|
|
@ -372,6 +421,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
|
|||
struct ohci_hcd *ohci;
|
||||
struct resource *r;
|
||||
struct clk *usb_clk;
|
||||
unsigned int i;
|
||||
|
||||
retval = ohci_pxa_of_init(pdev);
|
||||
if (retval)
|
||||
|
|
@ -417,6 +467,16 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
|
|||
pxa_ohci->clk = usb_clk;
|
||||
pxa_ohci->mmio_base = (void __iomem *)hcd->regs;
|
||||
|
||||
for (i = 0; i < 3; ++i) {
|
||||
char name[6];
|
||||
|
||||
if (!(inf->flags & (ENABLE_PORT1 << i)))
|
||||
continue;
|
||||
|
||||
sprintf(name, "vbus%u", i + 1);
|
||||
pxa_ohci->vbus[i] = devm_regulator_get(&pdev->dev, name);
|
||||
}
|
||||
|
||||
retval = pxa27x_start_hc(pxa_ohci, &pdev->dev);
|
||||
if (retval < 0) {
|
||||
pr_debug("pxa27x_start_hc failed");
|
||||
|
|
@ -462,9 +522,14 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
|
|||
void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev)
|
||||
{
|
||||
struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
|
||||
unsigned int i;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
pxa27x_stop_hc(pxa_ohci, &pdev->dev);
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
pxa27x_ohci_set_vbus_power(pxa_ohci, i, false);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
}
|
||||
|
||||
|
|
@ -563,7 +628,10 @@ static int __init ohci_pxa27x_init(void)
|
|||
return -ENODEV;
|
||||
|
||||
pr_info("%s: " DRIVER_DESC "\n", hcd_name);
|
||||
|
||||
ohci_init_driver(&ohci_pxa27x_hc_driver, &pxa27x_overrides);
|
||||
ohci_pxa27x_hc_driver.hub_control = pxa27x_ohci_hub_control;
|
||||
|
||||
return platform_driver_register(&ohci_hcd_pxa27x_driver);
|
||||
}
|
||||
module_init(ohci_pxa27x_init);
|
||||
|
|
|
|||
|
|
@ -45,10 +45,6 @@ static struct clk *usb_clk;
|
|||
|
||||
/* forward definitions */
|
||||
|
||||
static int (*orig_ohci_hub_control)(struct usb_hcd *hcd, u16 typeReq,
|
||||
u16 wValue, u16 wIndex, char *buf, u16 wLength);
|
||||
static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf);
|
||||
|
||||
static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc);
|
||||
|
||||
/* conversion functions */
|
||||
|
|
@ -110,7 +106,7 @@ ohci_s3c2410_hub_status_data(struct usb_hcd *hcd, char *buf)
|
|||
int orig;
|
||||
int portno;
|
||||
|
||||
orig = orig_ohci_hub_status_data(hcd, buf);
|
||||
orig = ohci_hub_status_data(hcd, buf);
|
||||
|
||||
if (info == NULL)
|
||||
return orig;
|
||||
|
|
@ -181,7 +177,7 @@ static int ohci_s3c2410_hub_control(
|
|||
* process the request straight away and exit */
|
||||
|
||||
if (info == NULL) {
|
||||
ret = orig_ohci_hub_control(hcd, typeReq, wValue,
|
||||
ret = ohci_hub_control(hcd, typeReq, wValue,
|
||||
wIndex, buf, wLength);
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -231,7 +227,7 @@ static int ohci_s3c2410_hub_control(
|
|||
break;
|
||||
}
|
||||
|
||||
ret = orig_ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
|
||||
ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
|
@ -489,9 +485,6 @@ static int __init ohci_s3c2410_init(void)
|
|||
* override these functions by making it too easy.
|
||||
*/
|
||||
|
||||
orig_ohci_hub_control = ohci_s3c2410_hc_driver.hub_control;
|
||||
orig_ohci_hub_status_data = ohci_s3c2410_hc_driver.hub_status_data;
|
||||
|
||||
ohci_s3c2410_hc_driver.hub_status_data = ohci_s3c2410_hub_status_data;
|
||||
ohci_s3c2410_hc_driver.hub_control = ohci_s3c2410_hub_control;
|
||||
|
||||
|
|
|
|||
|
|
@ -729,3 +729,6 @@ extern int ohci_setup(struct usb_hcd *hcd);
|
|||
extern int ohci_suspend(struct usb_hcd *hcd, bool do_wakeup);
|
||||
extern int ohci_resume(struct usb_hcd *hcd, bool hibernated);
|
||||
#endif
|
||||
extern int ohci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
u16 wIndex, char *buf, u16 wLength);
|
||||
extern int ohci_hub_status_data(struct usb_hcd *hcd, char *buf);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
|
||||
int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base);
|
||||
int usb_amd_find_chipset_info(void);
|
||||
int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev);
|
||||
bool usb_amd_hang_symptom_quirk(void);
|
||||
bool usb_amd_prefetch_quirk(void);
|
||||
void usb_amd_dev_put(void);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@
|
|||
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/gfp.h>
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "xhci.h"
|
||||
|
|
@ -270,7 +271,6 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
|
|||
struct xhci_virt_device *virt_dev;
|
||||
struct xhci_command *cmd;
|
||||
unsigned long flags;
|
||||
int timeleft;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
|
|
@ -284,34 +284,31 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
|
|||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
for (i = LAST_EP_INDEX; i > 0; i--) {
|
||||
if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue)
|
||||
xhci_queue_stop_endpoint(xhci, slot_id, i, suspend);
|
||||
if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) {
|
||||
struct xhci_command *command;
|
||||
command = xhci_alloc_command(xhci, false, false,
|
||||
GFP_NOIO);
|
||||
if (!command) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
xhci_free_command(xhci, cmd);
|
||||
return -ENOMEM;
|
||||
|
||||
}
|
||||
xhci_queue_stop_endpoint(xhci, command, slot_id, i,
|
||||
suspend);
|
||||
}
|
||||
}
|
||||
cmd->command_trb = xhci_find_next_enqueue(xhci->cmd_ring);
|
||||
list_add_tail(&cmd->cmd_list, &virt_dev->cmd_list);
|
||||
xhci_queue_stop_endpoint(xhci, slot_id, 0, suspend);
|
||||
xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0, suspend);
|
||||
xhci_ring_cmd_db(xhci);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
||||
/* Wait for last stop endpoint command to finish */
|
||||
timeleft = wait_for_completion_interruptible_timeout(
|
||||
cmd->completion,
|
||||
XHCI_CMD_DEFAULT_TIMEOUT);
|
||||
if (timeleft <= 0) {
|
||||
xhci_warn(xhci, "%s while waiting for stop endpoint command\n",
|
||||
timeleft == 0 ? "Timeout" : "Signal");
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
/* The timeout might have raced with the event ring handler, so
|
||||
* only delete from the list if the item isn't poisoned.
|
||||
*/
|
||||
if (cmd->cmd_list.next != LIST_POISON1)
|
||||
list_del(&cmd->cmd_list);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
ret = -ETIME;
|
||||
goto command_cleanup;
|
||||
}
|
||||
wait_for_completion(cmd->completion);
|
||||
|
||||
command_cleanup:
|
||||
if (cmd->status == COMP_CMD_ABORT || cmd->status == COMP_CMD_STOP) {
|
||||
xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n");
|
||||
ret = -ETIME;
|
||||
}
|
||||
xhci_free_command(xhci, cmd);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1020,7 +1020,6 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
|
|||
dev->num_rings_cached = 0;
|
||||
|
||||
init_completion(&dev->cmd_completion);
|
||||
INIT_LIST_HEAD(&dev->cmd_list);
|
||||
dev->udev = udev;
|
||||
|
||||
/* Point to output device context in dcbaa. */
|
||||
|
|
@ -1794,10 +1793,11 @@ void xhci_free_command(struct xhci_hcd *xhci,
|
|||
void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.controller;
|
||||
struct xhci_cd *cur_cd, *next_cd;
|
||||
int size;
|
||||
int i, j, num_ports;
|
||||
|
||||
del_timer_sync(&xhci->cmd_timer);
|
||||
|
||||
/* Free the Event Ring Segment Table and the actual Event Ring */
|
||||
size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries);
|
||||
if (xhci->erst.entries)
|
||||
|
|
@ -1816,11 +1816,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
|||
xhci_ring_free(xhci, xhci->cmd_ring);
|
||||
xhci->cmd_ring = NULL;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring");
|
||||
list_for_each_entry_safe(cur_cd, next_cd,
|
||||
&xhci->cancel_cmd_list, cancel_cmd_list) {
|
||||
list_del(&cur_cd->cancel_cmd_list);
|
||||
kfree(cur_cd);
|
||||
}
|
||||
xhci_cleanup_command_queue(xhci);
|
||||
|
||||
for (i = 1; i < MAX_HC_SLOTS; ++i)
|
||||
xhci_free_virt_device(xhci, i);
|
||||
|
|
@ -2323,7 +2319,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
u32 page_size, temp;
|
||||
int i;
|
||||
|
||||
INIT_LIST_HEAD(&xhci->cancel_cmd_list);
|
||||
INIT_LIST_HEAD(&xhci->cmd_list);
|
||||
|
||||
page_size = readl(&xhci->op_regs->page_size);
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
|
|
@ -2509,6 +2505,11 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
"Wrote ERST address to ir_set 0.");
|
||||
xhci_print_ir_set(xhci, 0);
|
||||
|
||||
/* init command timeout timer */
|
||||
init_timer(&xhci->cmd_timer);
|
||||
xhci->cmd_timer.data = (unsigned long) xhci;
|
||||
xhci->cmd_timer.function = xhci_handle_command_timeout;
|
||||
|
||||
/*
|
||||
* XXX: Might need to set the Interrupter Moderation Register to
|
||||
* something other than the default (~1ms minimum between interrupts).
|
||||
|
|
|
|||
72
drivers/usb/host/xhci-mvebu.c
Normal file
72
drivers/usb/host/xhci-mvebu.c
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Marvell
|
||||
* Author: Gregory CLEMENT <gregory.clement@free-electrons.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/io.h>
|
||||
#include <linux/mbus.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "xhci-mvebu.h"
|
||||
|
||||
#define USB3_MAX_WINDOWS 4
|
||||
#define USB3_WIN_CTRL(w) (0x0 + ((w) * 8))
|
||||
#define USB3_WIN_BASE(w) (0x4 + ((w) * 8))
|
||||
|
||||
static void xhci_mvebu_mbus_config(void __iomem *base,
|
||||
const struct mbus_dram_target_info *dram)
|
||||
{
|
||||
int win;
|
||||
|
||||
/* Clear all existing windows */
|
||||
for (win = 0; win < USB3_MAX_WINDOWS; win++) {
|
||||
writel(0, base + USB3_WIN_CTRL(win));
|
||||
writel(0, base + USB3_WIN_BASE(win));
|
||||
}
|
||||
|
||||
/* Program each DRAM CS in a seperate window */
|
||||
for (win = 0; win < dram->num_cs; win++) {
|
||||
const struct mbus_dram_window *cs = dram->cs + win;
|
||||
|
||||
writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) |
|
||||
(dram->mbus_dram_target_id << 4) | 1,
|
||||
base + USB3_WIN_CTRL(win));
|
||||
|
||||
writel((cs->base & 0xffff0000), base + USB3_WIN_BASE(win));
|
||||
}
|
||||
}
|
||||
|
||||
int xhci_mvebu_mbus_init_quirk(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
const struct mbus_dram_target_info *dram;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* We don't use devm_ioremap() because this mapping should
|
||||
* only exists for the duration of this probe function.
|
||||
*/
|
||||
base = ioremap(res->start, resource_size(res));
|
||||
if (!base)
|
||||
return -ENODEV;
|
||||
|
||||
dram = mv_mbus_dram_info();
|
||||
xhci_mvebu_mbus_config(base, dram);
|
||||
|
||||
/*
|
||||
* This memory area was only needed to configure the MBus
|
||||
* windows, and is therefore no longer useful.
|
||||
*/
|
||||
iounmap(base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
21
drivers/usb/host/xhci-mvebu.h
Normal file
21
drivers/usb/host/xhci-mvebu.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Marvell
|
||||
*
|
||||
* Gregory Clement <gregory.clement@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_XHCI_MVEBU_H
|
||||
#define __LINUX_XHCI_MVEBU_H
|
||||
#if IS_ENABLED(CONFIG_USB_XHCI_MVEBU)
|
||||
int xhci_mvebu_mbus_init_quirk(struct platform_device *pdev);
|
||||
#else
|
||||
static inline int xhci_mvebu_mbus_init_quirk(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif /* __LINUX_XHCI_MVEBU_H */
|
||||
|
|
@ -134,14 +134,14 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
|||
*/
|
||||
if (pdev->subsystem_vendor == PCI_VENDOR_ID_HP)
|
||||
xhci->quirks |= XHCI_SPURIOUS_WAKEUP;
|
||||
|
||||
}
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) {
|
||||
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
|
||||
}
|
||||
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
|
||||
pdev->device == PCI_DEVICE_ID_ASROCK_P67) {
|
||||
xhci->quirks |= XHCI_RESET_ON_RESUME;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
|
||||
"QUIRK: Resetting on resume");
|
||||
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
|
||||
}
|
||||
if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
|
||||
|
|
@ -149,6 +149,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
|||
xhci->quirks |= XHCI_RESET_ON_RESUME;
|
||||
if (pdev->vendor == PCI_VENDOR_ID_VIA)
|
||||
xhci->quirks |= XHCI_RESET_ON_RESUME;
|
||||
|
||||
if (xhci->quirks & XHCI_RESET_ON_RESUME)
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
|
||||
"QUIRK: Resetting on resume");
|
||||
}
|
||||
|
||||
/* called during probe() after chip reset completes */
|
||||
|
|
|
|||
|
|
@ -11,13 +11,15 @@
|
|||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "xhci.h"
|
||||
#include "xhci-mvebu.h"
|
||||
|
||||
static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
{
|
||||
|
|
@ -35,6 +37,11 @@ static int xhci_plat_setup(struct usb_hcd *hcd)
|
|||
return xhci_gen_setup(hcd, xhci_plat_quirks);
|
||||
}
|
||||
|
||||
static int xhci_plat_start(struct usb_hcd *hcd)
|
||||
{
|
||||
return xhci_run(hcd);
|
||||
}
|
||||
|
||||
static const struct hc_driver xhci_plat_xhci_driver = {
|
||||
.description = "xhci-hcd",
|
||||
.product_desc = "xHCI Host Controller",
|
||||
|
|
@ -50,7 +57,7 @@ static const struct hc_driver xhci_plat_xhci_driver = {
|
|||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = xhci_plat_setup,
|
||||
.start = xhci_run,
|
||||
.start = xhci_plat_start,
|
||||
.stop = xhci_stop,
|
||||
.shutdown = xhci_shutdown,
|
||||
|
||||
|
|
@ -91,6 +98,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
|
|||
struct xhci_hcd *xhci;
|
||||
struct resource *res;
|
||||
struct usb_hcd *hcd;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
|
|
@ -107,6 +115,15 @@ static int xhci_plat_probe(struct platform_device *pdev)
|
|||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node,
|
||||
"marvell,armada-375-xhci") ||
|
||||
of_device_is_compatible(pdev->dev.of_node,
|
||||
"marvell,armada-380-xhci")) {
|
||||
ret = xhci_mvebu_mbus_init_quirk(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialize dma_mask and coherent_dma_mask to 32-bits */
|
||||
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
|
|
@ -137,14 +154,27 @@ static int xhci_plat_probe(struct platform_device *pdev)
|
|||
goto release_mem_region;
|
||||
}
|
||||
|
||||
/*
|
||||
* Not all platforms have a clk so it is not an error if the
|
||||
* clock does not exists.
|
||||
*/
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (!IS_ERR(clk)) {
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
goto unmap_registers;
|
||||
}
|
||||
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (ret)
|
||||
goto unmap_registers;
|
||||
goto disable_clk;
|
||||
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
|
||||
/* USB 2.0 roothub is stored in the platform_device now. */
|
||||
hcd = platform_get_drvdata(pdev);
|
||||
xhci = hcd_to_xhci(hcd);
|
||||
xhci->clk = clk;
|
||||
xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev,
|
||||
dev_name(&pdev->dev), hcd);
|
||||
if (!xhci->shared_hcd) {
|
||||
|
|
@ -173,6 +203,10 @@ put_usb3_hcd:
|
|||
dealloc_usb2_hcd:
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
disable_clk:
|
||||
if (!IS_ERR(clk))
|
||||
clk_disable_unprepare(clk);
|
||||
|
||||
unmap_registers:
|
||||
iounmap(hcd->regs);
|
||||
|
||||
|
|
@ -189,11 +223,14 @@ static int xhci_plat_remove(struct platform_device *dev)
|
|||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct clk *clk = xhci->clk;
|
||||
|
||||
usb_remove_hcd(xhci->shared_hcd);
|
||||
usb_put_hcd(xhci->shared_hcd);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
if (!IS_ERR(clk))
|
||||
clk_disable_unprepare(clk);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
|
|
@ -202,7 +239,7 @@ static int xhci_plat_remove(struct platform_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int xhci_plat_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
|
|
@ -231,6 +268,8 @@ static const struct dev_pm_ops xhci_plat_pm_ops = {
|
|||
static const struct of_device_id usb_xhci_of_match[] = {
|
||||
{ .compatible = "generic-xhci" },
|
||||
{ .compatible = "xhci-platform" },
|
||||
{ .compatible = "marvell,armada-375-xhci"},
|
||||
{ .compatible = "marvell,armada-380-xhci"},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue