Merge tag 'usb-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB/PHY updates from Greg KH: "Here is the big USB/PHY driver patches for 4.20-rc1 Lots of USB changes in here, primarily in these areas: - typec updates and new drivers - new PHY drivers - dwc2 driver updates and additions (this old core keeps getting added to new devices.) - usbtmc major update based on the industry group coming together and working to add new features and performance to the driver. - USB gadget additions for new features - USB gadget configfs updates - chipidea driver updates - other USB gadget updates - USB serial driver updates - renesas driver updates - xhci driver updates - other tiny USB driver updates All of these have been in linux-next for a while with no reported issues" * tag 'usb-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (229 commits) usb: phy: ab8500: silence some uninitialized variable warnings usb: xhci: tegra: Add genpd support usb: xhci: tegra: Power-off power-domains on removal usbip:vudc: BUG kmalloc-2048 (Not tainted): Poison overwritten usbip: tools: fix atoi() on non-null terminated string USB: misc: appledisplay: fix backlight update_status return code phy: phy-pxa-usb: add a new driver usb: host: add DT bindings for faraday fotg2 usb: host: ohci-at91: fix request of irq for optional gpio usb/early: remove set but not used variable 'remain_length' usb: typec: Fix copy/paste on typec_set_vconn_role() kerneldoc usb: typec: tcpm: Report back negotiated PPS voltage and current USB: core: remove set but not used variable 'udev' usb: core: fix memory leak on port_dev_path allocation USB: net2280: Remove ->disconnect() callback from net2280_pullup() usb: dwc2: disable power_down on rockchip devices usb: gadget: udc: renesas_usb3: add support for r8a77990 dt-bindings: usb: renesas_usb3: add bindings for r8a77990 usb: gadget: udc: renesas_usb3: Add r8a774a1 support USB: serial: cypress_m8: remove set but not used variable 'iflag' ...
此提交包含在:
@@ -364,8 +364,7 @@ static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
|
||||
ci_hdrc_imx_remove(pdev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int imx_controller_suspend(struct device *dev)
|
||||
static int __maybe_unused imx_controller_suspend(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
|
||||
@@ -377,7 +376,7 @@ static int imx_controller_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_controller_resume(struct device *dev)
|
||||
static int __maybe_unused imx_controller_resume(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
@@ -408,8 +407,7 @@ clk_disable:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int ci_hdrc_imx_suspend(struct device *dev)
|
||||
static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -431,7 +429,7 @@ static int ci_hdrc_imx_suspend(struct device *dev)
|
||||
return imx_controller_suspend(dev);
|
||||
}
|
||||
|
||||
static int ci_hdrc_imx_resume(struct device *dev)
|
||||
static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@@ -445,9 +443,8 @@ static int ci_hdrc_imx_resume(struct device *dev)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static int ci_hdrc_imx_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@@ -466,13 +463,11 @@ static int ci_hdrc_imx_runtime_suspend(struct device *dev)
|
||||
return imx_controller_suspend(dev);
|
||||
}
|
||||
|
||||
static int ci_hdrc_imx_runtime_resume(struct device *dev)
|
||||
static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev)
|
||||
{
|
||||
return imx_controller_resume(dev);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
|
||||
SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
|
||||
@@ -492,7 +487,7 @@ static struct platform_driver ci_hdrc_imx_driver = {
|
||||
module_platform_driver(ci_hdrc_imx_driver);
|
||||
|
||||
MODULE_ALIAS("platform:imx-usb");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
|
||||
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
|
||||
MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
|
||||
|
@@ -53,6 +53,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
@@ -723,6 +724,24 @@ static int ci_get_platdata(struct device *dev,
|
||||
else
|
||||
cable->connected = false;
|
||||
}
|
||||
|
||||
platdata->pctl = devm_pinctrl_get(dev);
|
||||
if (!IS_ERR(platdata->pctl)) {
|
||||
struct pinctrl_state *p;
|
||||
|
||||
p = pinctrl_lookup_state(platdata->pctl, "default");
|
||||
if (!IS_ERR(p))
|
||||
platdata->pins_default = p;
|
||||
|
||||
p = pinctrl_lookup_state(platdata->pctl, "host");
|
||||
if (!IS_ERR(p))
|
||||
platdata->pins_host = p;
|
||||
|
||||
p = pinctrl_lookup_state(platdata->pctl, "device");
|
||||
if (!IS_ERR(p))
|
||||
platdata->pins_device = p;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
#include "../host/ehci.h"
|
||||
|
||||
@@ -153,6 +154,10 @@ static int host_start(struct ci_hdrc *ci)
|
||||
}
|
||||
}
|
||||
|
||||
if (ci->platdata->pins_host)
|
||||
pinctrl_select_state(ci->platdata->pctl,
|
||||
ci->platdata->pins_host);
|
||||
|
||||
ret = usb_add_hcd(hcd, 0, 0);
|
||||
if (ret) {
|
||||
goto disable_reg;
|
||||
@@ -197,6 +202,10 @@ static void host_stop(struct ci_hdrc *ci)
|
||||
}
|
||||
ci->hcd = NULL;
|
||||
ci->otg.host = NULL;
|
||||
|
||||
if (ci->platdata->pins_host && ci->platdata->pins_default)
|
||||
pinctrl_select_state(ci->platdata->pctl,
|
||||
ci->platdata->pins_default);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -203,14 +203,17 @@ static void ci_otg_work(struct work_struct *work)
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(ci->dev);
|
||||
|
||||
if (ci->id_event) {
|
||||
ci->id_event = false;
|
||||
ci_handle_id_switch(ci);
|
||||
} else if (ci->b_sess_valid_event) {
|
||||
}
|
||||
|
||||
if (ci->b_sess_valid_event) {
|
||||
ci->b_sess_valid_event = false;
|
||||
ci_handle_vbus_change(ci);
|
||||
} else
|
||||
dev_err(ci->dev, "unexpected event occurs at %s\n", __func__);
|
||||
}
|
||||
|
||||
pm_runtime_put_sync(ci->dev);
|
||||
|
||||
enable_irq(ci->irq);
|
||||
|
@@ -17,7 +17,8 @@ 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);
|
||||
if (queue_work(ci->wq, &ci->work) == false)
|
||||
enable_irq(ci->irq);
|
||||
}
|
||||
|
||||
#endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg-fsm.h>
|
||||
@@ -1965,6 +1966,10 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
|
||||
|
||||
static int udc_id_switch_for_device(struct ci_hdrc *ci)
|
||||
{
|
||||
if (ci->platdata->pins_device)
|
||||
pinctrl_select_state(ci->platdata->pctl,
|
||||
ci->platdata->pins_device);
|
||||
|
||||
if (ci->is_otg)
|
||||
/* Clear and enable BSV irq */
|
||||
hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE,
|
||||
@@ -1983,6 +1988,10 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
|
||||
hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);
|
||||
|
||||
ci->vbus_active = 0;
|
||||
|
||||
if (ci->platdata->pins_device && ci->platdata->pins_default)
|
||||
pinctrl_select_state(ci->platdata->pctl,
|
||||
ci->platdata->pins_default);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -343,6 +343,8 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
|
||||
} else if (data->oc_polarity == 1) {
|
||||
/* High active */
|
||||
reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY);
|
||||
} else {
|
||||
reg &= ~(MX6_BM_OVER_CUR_DIS);
|
||||
}
|
||||
writel(reg, usbmisc->base + data->index * 4);
|
||||
|
||||
@@ -633,6 +635,6 @@ static struct platform_driver usbmisc_imx_driver = {
|
||||
module_platform_driver(usbmisc_imx_driver);
|
||||
|
||||
MODULE_ALIAS("platform:usbmisc-imx");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("driver for imx usb non-core registers");
|
||||
MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
|
||||
|
檔案差異因為檔案過大而無法顯示
載入差異
@@ -101,12 +101,8 @@ void hcd_buffer_destroy(struct usb_hcd *hcd)
|
||||
return;
|
||||
|
||||
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
||||
struct dma_pool *pool = hcd->pool[i];
|
||||
|
||||
if (pool) {
|
||||
dma_pool_destroy(pool);
|
||||
hcd->pool[i] = NULL;
|
||||
}
|
||||
dma_pool_destroy(hcd->pool[i]);
|
||||
hcd->pool[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -510,7 +510,6 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
||||
struct usb_interface *iface, void *priv)
|
||||
{
|
||||
struct device *dev;
|
||||
struct usb_device *udev;
|
||||
int retval = 0;
|
||||
|
||||
if (!iface)
|
||||
@@ -524,8 +523,6 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
||||
if (!iface->authorized)
|
||||
return -ENODEV;
|
||||
|
||||
udev = interface_to_usbdev(iface);
|
||||
|
||||
dev->driver = &driver->drvwrap.driver;
|
||||
usb_set_intfdata(iface, priv);
|
||||
iface->needs_binding = 0;
|
||||
|
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <uapi/linux/usb/audio.h>
|
||||
#include "usb.h"
|
||||
|
||||
static inline const char *plural(int n)
|
||||
@@ -42,6 +43,16 @@ static int is_activesync(struct usb_interface_descriptor *desc)
|
||||
&& desc->bInterfaceProtocol == 1;
|
||||
}
|
||||
|
||||
static bool is_audio(struct usb_interface_descriptor *desc)
|
||||
{
|
||||
return desc->bInterfaceClass == USB_CLASS_AUDIO;
|
||||
}
|
||||
|
||||
static bool is_uac3_config(struct usb_interface_descriptor *desc)
|
||||
{
|
||||
return desc->bInterfaceProtocol == UAC_VERSION_3;
|
||||
}
|
||||
|
||||
int usb_choose_configuration(struct usb_device *udev)
|
||||
{
|
||||
int i;
|
||||
@@ -121,6 +132,22 @@ int usb_choose_configuration(struct usb_device *udev)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Select first configuration as default for audio so that
|
||||
* devices that don't comply with UAC3 protocol are supported.
|
||||
* But, still iterate through other configurations and
|
||||
* select UAC3 compliant config if present.
|
||||
*/
|
||||
if (i == 0 && num_configs > 1 && desc && is_audio(desc)) {
|
||||
best = c;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i > 0 && desc && is_audio(desc) && is_uac3_config(desc)) {
|
||||
best = c;
|
||||
break;
|
||||
}
|
||||
|
||||
/* From the remaining configs, choose the first one whose
|
||||
* first interface is for a non-vendor-specific class.
|
||||
* Reason: Linux is more likely to have a class driver
|
||||
|
@@ -1738,7 +1738,6 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
|
||||
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
|
||||
struct usb_anchor *anchor = urb->anchor;
|
||||
int status = urb->unlinked;
|
||||
unsigned long flags;
|
||||
|
||||
urb->hcpriv = NULL;
|
||||
if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
|
||||
@@ -1755,20 +1754,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
|
||||
|
||||
/* pass ownership to the completion handler */
|
||||
urb->status = status;
|
||||
|
||||
/*
|
||||
* We disable local IRQs here avoid possible deadlock because
|
||||
* drivers may call spin_lock() to hold lock which might be
|
||||
* acquired in one hard interrupt handler.
|
||||
*
|
||||
* The local_irq_save()/local_irq_restore() around complete()
|
||||
* will be removed if current USB drivers have been cleaned up
|
||||
* and no one may trigger the above deadlock situation when
|
||||
* running complete() in tasklet.
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
urb->complete(urb);
|
||||
local_irq_restore(flags);
|
||||
|
||||
usb_anchor_resume_wakeups(anchor);
|
||||
atomic_dec(&urb->use_count);
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/kobject.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/byteorder.h>
|
||||
@@ -2660,11 +2661,13 @@ static bool use_new_scheme(struct usb_device *udev, int retry,
|
||||
{
|
||||
int old_scheme_first_port =
|
||||
port_dev->quirks & USB_PORT_QUIRK_OLD_SCHEME;
|
||||
int quick_enumeration = (udev->speed == USB_SPEED_HIGH);
|
||||
|
||||
if (udev->speed >= USB_SPEED_SUPER)
|
||||
return false;
|
||||
|
||||
return USE_NEW_SCHEME(retry, old_scheme_first_port || old_scheme_first);
|
||||
return USE_NEW_SCHEME(retry, old_scheme_first_port || old_scheme_first
|
||||
|| quick_enumeration);
|
||||
}
|
||||
|
||||
/* Is a USB 3.0 port in the Inactive or Compliance Mode state?
|
||||
@@ -5147,6 +5150,42 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
||||
usb_lock_port(port_dev);
|
||||
}
|
||||
|
||||
/* Handle notifying userspace about hub over-current events */
|
||||
static void port_over_current_notify(struct usb_port *port_dev)
|
||||
{
|
||||
static char *envp[] = { NULL, NULL, NULL };
|
||||
struct device *hub_dev;
|
||||
char *port_dev_path;
|
||||
|
||||
sysfs_notify(&port_dev->dev.kobj, NULL, "over_current_count");
|
||||
|
||||
hub_dev = port_dev->dev.parent;
|
||||
|
||||
if (!hub_dev)
|
||||
return;
|
||||
|
||||
port_dev_path = kobject_get_path(&port_dev->dev.kobj, GFP_KERNEL);
|
||||
if (!port_dev_path)
|
||||
return;
|
||||
|
||||
envp[0] = kasprintf(GFP_KERNEL, "OVER_CURRENT_PORT=%s", port_dev_path);
|
||||
if (!envp[0])
|
||||
goto exit_path;
|
||||
|
||||
envp[1] = kasprintf(GFP_KERNEL, "OVER_CURRENT_COUNT=%u",
|
||||
port_dev->over_current_count);
|
||||
if (!envp[1])
|
||||
goto exit;
|
||||
|
||||
kobject_uevent_env(&hub_dev->kobj, KOBJ_CHANGE, envp);
|
||||
|
||||
kfree(envp[1]);
|
||||
exit:
|
||||
kfree(envp[0]);
|
||||
exit_path:
|
||||
kfree(port_dev_path);
|
||||
}
|
||||
|
||||
static void port_event(struct usb_hub *hub, int port1)
|
||||
__must_hold(&port_dev->status_lock)
|
||||
{
|
||||
@@ -5189,6 +5228,7 @@ static void port_event(struct usb_hub *hub, int port1)
|
||||
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
|
||||
u16 status = 0, unused;
|
||||
port_dev->over_current_count++;
|
||||
port_over_current_notify(port_dev);
|
||||
|
||||
dev_dbg(&port_dev->dev, "over-current change #%u\n",
|
||||
port_dev->over_current_count);
|
||||
|
@@ -23,10 +23,11 @@ static int usb_phy_roothub_add_phy(struct device *dev, int index,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct usb_phy_roothub *roothub_entry;
|
||||
struct phy *phy = devm_of_phy_get_by_index(dev, dev->of_node, index);
|
||||
struct phy *phy;
|
||||
|
||||
if (IS_ERR_OR_NULL(phy)) {
|
||||
if (!phy || PTR_ERR(phy) == -ENODEV)
|
||||
phy = devm_of_phy_get_by_index(dev, dev->of_node, index);
|
||||
if (IS_ERR(phy)) {
|
||||
if (PTR_ERR(phy) == -ENODEV)
|
||||
return 0;
|
||||
else
|
||||
return PTR_ERR(phy);
|
||||
|
@@ -16,6 +16,15 @@ static int usb_port_block_power_off;
|
||||
|
||||
static const struct attribute_group *port_dev_group[];
|
||||
|
||||
static ssize_t location_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct usb_port *port_dev = to_usb_port(dev);
|
||||
|
||||
return sprintf(buf, "0x%08x\n", port_dev->location);
|
||||
}
|
||||
static DEVICE_ATTR_RO(location);
|
||||
|
||||
static ssize_t connect_type_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
@@ -140,6 +149,7 @@ static DEVICE_ATTR_RW(usb3_lpm_permit);
|
||||
|
||||
static struct attribute *port_dev_attrs[] = {
|
||||
&dev_attr_connect_type.attr,
|
||||
&dev_attr_location.attr,
|
||||
&dev_attr_quirks.attr,
|
||||
&dev_attr_over_current_count.attr,
|
||||
NULL,
|
||||
|
@@ -393,6 +393,20 @@ enum dwc2_ep0_state {
|
||||
* 0 - No
|
||||
* 1 - Yes
|
||||
* @hird_threshold: Value of BESL or HIRD Threshold.
|
||||
* @ref_clk_per: Indicates in terms of pico seconds the period
|
||||
* of ref_clk.
|
||||
* 62500 - 16MHz
|
||||
* 58823 - 17MHz
|
||||
* 52083 - 19.2MHz
|
||||
* 50000 - 20MHz
|
||||
* 41666 - 24MHz
|
||||
* 33333 - 30MHz (default)
|
||||
* 25000 - 40MHz
|
||||
* @sof_cnt_wkup_alert: Indicates in term of number of SOF's after which
|
||||
* the controller should generate an interrupt if the
|
||||
* device had been in L1 state until that period.
|
||||
* This is used by SW to initiate Remote WakeUp in the
|
||||
* controller so as to sync to the uF number from the host.
|
||||
* @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO
|
||||
* register.
|
||||
* 0 - Deactivate the transceiver (default)
|
||||
@@ -416,6 +430,9 @@ enum dwc2_ep0_state {
|
||||
* back to DWC2_SPEED_PARAM_HIGH while device is gone.
|
||||
* 0 - No (default)
|
||||
* 1 - Yes
|
||||
* @service_interval: Enable service interval based scheduling.
|
||||
* 0 - No
|
||||
* 1 - Yes
|
||||
*
|
||||
* The following parameters may be specified when starting the module. These
|
||||
* parameters define how the DWC_otg controller should be configured. A
|
||||
@@ -461,6 +478,7 @@ struct dwc2_core_params {
|
||||
bool lpm_clock_gating;
|
||||
bool besl;
|
||||
bool hird_threshold_en;
|
||||
bool service_interval;
|
||||
u8 hird_threshold;
|
||||
bool activate_stm_fs_transceiver;
|
||||
bool ipg_isoc_en;
|
||||
@@ -468,6 +486,10 @@ struct dwc2_core_params {
|
||||
u32 max_transfer_size;
|
||||
u32 ahbcfg;
|
||||
|
||||
/* GREFCLK parameters */
|
||||
u32 ref_clk_per;
|
||||
u16 sof_cnt_wkup_alert;
|
||||
|
||||
/* Host parameters */
|
||||
bool host_dma;
|
||||
bool dma_desc_enable;
|
||||
@@ -605,6 +627,10 @@ struct dwc2_core_params {
|
||||
* FIFO sizing is enabled 16 to 32768
|
||||
* Actual maximum value is autodetected and also
|
||||
* the default.
|
||||
* @service_interval_mode: For enabling service interval based scheduling in the
|
||||
* controller.
|
||||
* 0 - Disable
|
||||
* 1 - Enable
|
||||
*/
|
||||
struct dwc2_hw_params {
|
||||
unsigned op_mode:3;
|
||||
@@ -635,6 +661,7 @@ struct dwc2_hw_params {
|
||||
unsigned utmi_phy_data_width:2;
|
||||
unsigned lpm_mode:1;
|
||||
unsigned ipg_isoc_en:1;
|
||||
unsigned service_interval_mode:1;
|
||||
u32 snpsid;
|
||||
u32 dev_ep_dirs;
|
||||
u32 g_tx_fifo_size[MAX_EPS_CHANNELS];
|
||||
@@ -1354,6 +1381,7 @@ int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg);
|
||||
void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg);
|
||||
void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg);
|
||||
#else
|
||||
static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2)
|
||||
{ return 0; }
|
||||
@@ -1388,6 +1416,7 @@ static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
|
||||
static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) {}
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
|
@@ -701,6 +701,7 @@ static int params_show(struct seq_file *seq, void *v)
|
||||
print_param(seq, p, besl);
|
||||
print_param(seq, p, hird_threshold_en);
|
||||
print_param(seq, p, hird_threshold);
|
||||
print_param(seq, p, service_interval);
|
||||
print_param(seq, p, host_dma);
|
||||
print_param(seq, p, g_dma);
|
||||
print_param(seq, p, g_dma_desc);
|
||||
|
@@ -122,6 +122,24 @@ static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_dec_frame_num_by_one - Decrements the targeted frame number
|
||||
* by one.
|
||||
* @hs_ep: The endpoint.
|
||||
*
|
||||
* This function used in service interval based scheduling flow to calculate
|
||||
* descriptor frame number filed value. For service interval mode frame
|
||||
* number in descriptor should point to last (u)frame in the interval.
|
||||
*
|
||||
*/
|
||||
static inline void dwc2_gadget_dec_frame_num_by_one(struct dwc2_hsotg_ep *hs_ep)
|
||||
{
|
||||
if (hs_ep->target_frame)
|
||||
hs_ep->target_frame -= 1;
|
||||
else
|
||||
hs_ep->target_frame = DSTS_SOFFN_LIMIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_en_gsint - enable one or more of the general interrupt
|
||||
* @hsotg: The device state
|
||||
@@ -227,6 +245,27 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
|
||||
return tx_addr_max - addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_wkup_alert_handler - Handler for WKUP_ALERT interrupt
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
*
|
||||
*/
|
||||
static void dwc2_gadget_wkup_alert_handler(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 gintsts2;
|
||||
u32 gintmsk2;
|
||||
|
||||
gintsts2 = dwc2_readl(hsotg, GINTSTS2);
|
||||
gintmsk2 = dwc2_readl(hsotg, GINTMSK2);
|
||||
|
||||
if (gintsts2 & GINTSTS2_WKUP_ALERT_INT) {
|
||||
dev_dbg(hsotg->dev, "%s: Wkup_Alert_Int\n", __func__);
|
||||
dwc2_clear_bit(hsotg, GINTSTS2, GINTSTS2_WKUP_ALERT_INT);
|
||||
dwc2_set_bit(hsotg, DCFG, DCTL_RMTWKUPSIG);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_tx_fifo_average_depth - returns average depth of device mode
|
||||
* TX FIFOs
|
||||
@@ -2812,6 +2851,23 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep)
|
||||
if (using_desc_dma(hsotg)) {
|
||||
hs_ep->target_frame = hsotg->frame_number;
|
||||
dwc2_gadget_incr_frame_num(hs_ep);
|
||||
|
||||
/* In service interval mode target_frame must
|
||||
* be set to last (u)frame of the service interval.
|
||||
*/
|
||||
if (hsotg->params.service_interval) {
|
||||
/* Set target_frame to the first (u)frame of
|
||||
* the service interval
|
||||
*/
|
||||
hs_ep->target_frame &= ~hs_ep->interval + 1;
|
||||
|
||||
/* Set target_frame to the last (u)frame of
|
||||
* the service interval
|
||||
*/
|
||||
dwc2_gadget_incr_frame_num(hs_ep);
|
||||
dwc2_gadget_dec_frame_num_by_one(hs_ep);
|
||||
}
|
||||
|
||||
dwc2_gadget_start_isoc_ddma(hs_ep);
|
||||
return;
|
||||
}
|
||||
@@ -3109,6 +3165,8 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg,
|
||||
dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index);
|
||||
}
|
||||
|
||||
static int dwc2_hsotg_ep_disable(struct usb_ep *ep);
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_disconnect - disconnect service
|
||||
* @hsotg: The device state.
|
||||
@@ -3127,13 +3185,12 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg)
|
||||
hsotg->connected = 0;
|
||||
hsotg->test_mode = 0;
|
||||
|
||||
/* all endpoints should be shutdown */
|
||||
for (ep = 0; ep < hsotg->num_of_eps; ep++) {
|
||||
if (hsotg->eps_in[ep])
|
||||
kill_all_requests(hsotg, hsotg->eps_in[ep],
|
||||
-ESHUTDOWN);
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
|
||||
if (hsotg->eps_out[ep])
|
||||
kill_all_requests(hsotg, hsotg->eps_out[ep],
|
||||
-ESHUTDOWN);
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
|
||||
}
|
||||
|
||||
call_gadget(hsotg, disconnect);
|
||||
@@ -3191,13 +3248,23 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
|
||||
u32 val;
|
||||
u32 usbcfg;
|
||||
u32 dcfg = 0;
|
||||
int ep;
|
||||
|
||||
/* Kill any ep0 requests as controller will be reinitialized */
|
||||
kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
|
||||
|
||||
if (!is_usb_reset)
|
||||
if (!is_usb_reset) {
|
||||
if (dwc2_core_reset(hsotg, true))
|
||||
return;
|
||||
} else {
|
||||
/* all endpoints should be shutdown */
|
||||
for (ep = 1; ep < hsotg->num_of_eps; ep++) {
|
||||
if (hsotg->eps_in[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
|
||||
if (hsotg->eps_out[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* we must now enable ep0 ready for host detection and then
|
||||
@@ -3312,6 +3379,10 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
|
||||
dwc2_set_bit(hsotg, DIEPMSK, DIEPMSK_BNAININTRMSK);
|
||||
}
|
||||
|
||||
/* Enable Service Interval mode if supported */
|
||||
if (using_desc_dma(hsotg) && hsotg->params.service_interval)
|
||||
dwc2_set_bit(hsotg, DCTL, DCTL_SERVICE_INTERVAL_SUPPORTED);
|
||||
|
||||
dwc2_writel(hsotg, 0, DAINTMSK);
|
||||
|
||||
dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
|
||||
@@ -3368,6 +3439,10 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
|
||||
/* configure the core to support LPM */
|
||||
dwc2_gadget_init_lpm(hsotg);
|
||||
|
||||
/* program GREFCLK register if needed */
|
||||
if (using_desc_dma(hsotg) && hsotg->params.service_interval)
|
||||
dwc2_gadget_program_ref_clk(hsotg);
|
||||
|
||||
/* must be at-least 3ms to allow bus to see disconnect */
|
||||
mdelay(3);
|
||||
|
||||
@@ -3676,6 +3751,10 @@ irq_retry:
|
||||
if (gintsts & IRQ_RETRY_MASK && --retry_count > 0)
|
||||
goto irq_retry;
|
||||
|
||||
/* Check WKUP_ALERT interrupt*/
|
||||
if (hsotg->params.service_interval)
|
||||
dwc2_gadget_wkup_alert_handler(hsotg);
|
||||
|
||||
spin_unlock(&hsotg->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@@ -3993,6 +4072,7 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
|
||||
unsigned long flags;
|
||||
u32 epctrl_reg;
|
||||
u32 ctrl;
|
||||
int locked;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);
|
||||
|
||||
@@ -4008,7 +4088,9 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
|
||||
|
||||
epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
locked = spin_is_locked(&hsotg->lock);
|
||||
if (!locked)
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
ctrl = dwc2_readl(hsotg, epctrl_reg);
|
||||
|
||||
@@ -4032,7 +4114,9 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
|
||||
hs_ep->fifo_index = 0;
|
||||
hs_ep->fifo_size = 0;
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
if (!locked)
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4944,6 +5028,29 @@ void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg)
|
||||
val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0;
|
||||
dwc2_writel(hsotg, val, GLPMCFG);
|
||||
dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG));
|
||||
|
||||
/* Unmask WKUP_ALERT Interrupt */
|
||||
if (hsotg->params.service_interval)
|
||||
dwc2_set_bit(hsotg, GINTMSK2, GINTMSK2_WKUP_ALERT_INT_MSK);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_gadget_program_ref_clk - Program GREFCLK register in device mode
|
||||
*
|
||||
* @hsotg: Programming view of DWC_otg controller
|
||||
*
|
||||
*/
|
||||
void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
val |= GREFCLK_REF_CLK_MODE;
|
||||
val |= hsotg->params.ref_clk_per << GREFCLK_REFCLKPER_SHIFT;
|
||||
val |= hsotg->params.sof_cnt_wkup_alert <<
|
||||
GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT;
|
||||
|
||||
dwc2_writel(hsotg, val, GREFCLK);
|
||||
dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -358,16 +358,10 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
|
||||
|
||||
static int dwc2_vbus_supply_init(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int ret;
|
||||
if (hsotg->vbus_supply)
|
||||
return regulator_enable(hsotg->vbus_supply);
|
||||
|
||||
hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");
|
||||
if (IS_ERR(hsotg->vbus_supply)) {
|
||||
ret = PTR_ERR(hsotg->vbus_supply);
|
||||
hsotg->vbus_supply = NULL;
|
||||
return ret == -ENODEV ? 0 : ret;
|
||||
}
|
||||
|
||||
return regulator_enable(hsotg->vbus_supply);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc2_vbus_supply_exit(struct dwc2_hsotg *hsotg)
|
||||
@@ -1328,14 +1322,11 @@ static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg,
|
||||
u32 remaining_count;
|
||||
u32 byte_count;
|
||||
u32 dword_count;
|
||||
u32 __iomem *data_fifo;
|
||||
u32 *data_buf = (u32 *)chan->xfer_buf;
|
||||
|
||||
if (dbg_hc(chan))
|
||||
dev_vdbg(hsotg->dev, "%s()\n", __func__);
|
||||
|
||||
data_fifo = (u32 __iomem *)(hsotg->regs + HCFIFO(chan->hc_num));
|
||||
|
||||
remaining_count = chan->xfer_len - chan->xfer_count;
|
||||
if (remaining_count > chan->max_packet)
|
||||
byte_count = chan->max_packet;
|
||||
@@ -3564,6 +3555,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||||
u32 port_status;
|
||||
u32 speed;
|
||||
u32 pcgctl;
|
||||
u32 pwr;
|
||||
|
||||
switch (typereq) {
|
||||
case ClearHubFeature:
|
||||
@@ -3612,8 +3604,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||||
dev_dbg(hsotg->dev,
|
||||
"ClearPortFeature USB_PORT_FEAT_POWER\n");
|
||||
hprt0 = dwc2_read_hprt0(hsotg);
|
||||
pwr = hprt0 & HPRT0_PWR;
|
||||
hprt0 &= ~HPRT0_PWR;
|
||||
dwc2_writel(hsotg, hprt0, HPRT0);
|
||||
if (pwr)
|
||||
dwc2_vbus_supply_exit(hsotg);
|
||||
break;
|
||||
|
||||
case USB_PORT_FEAT_INDICATOR:
|
||||
@@ -3823,8 +3818,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||||
dev_dbg(hsotg->dev,
|
||||
"SetPortFeature - USB_PORT_FEAT_POWER\n");
|
||||
hprt0 = dwc2_read_hprt0(hsotg);
|
||||
pwr = hprt0 & HPRT0_PWR;
|
||||
hprt0 |= HPRT0_PWR;
|
||||
dwc2_writel(hsotg, hprt0, HPRT0);
|
||||
if (!pwr)
|
||||
dwc2_vbus_supply_init(hsotg);
|
||||
break;
|
||||
|
||||
case USB_PORT_FEAT_RESET:
|
||||
@@ -3841,6 +3839,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||||
dwc2_writel(hsotg, 0, PCGCTL);
|
||||
|
||||
hprt0 = dwc2_read_hprt0(hsotg);
|
||||
pwr = hprt0 & HPRT0_PWR;
|
||||
/* Clear suspend bit if resetting from suspend state */
|
||||
hprt0 &= ~HPRT0_SUSP;
|
||||
|
||||
@@ -3854,6 +3853,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||||
dev_dbg(hsotg->dev,
|
||||
"In host mode, hprt0=%08x\n", hprt0);
|
||||
dwc2_writel(hsotg, hprt0, HPRT0);
|
||||
if (!pwr)
|
||||
dwc2_vbus_supply_init(hsotg);
|
||||
}
|
||||
|
||||
/* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
|
||||
@@ -4393,6 +4394,8 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
|
||||
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
|
||||
struct usb_bus *bus = hcd_to_bus(hcd);
|
||||
unsigned long flags;
|
||||
u32 hprt0;
|
||||
int ret;
|
||||
|
||||
dev_dbg(hsotg->dev, "DWC OTG HCD START\n");
|
||||
|
||||
@@ -4408,6 +4411,17 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
|
||||
|
||||
dwc2_hcd_reinit(hsotg);
|
||||
|
||||
hprt0 = dwc2_read_hprt0(hsotg);
|
||||
/* Has vbus power been turned on in dwc2_core_host_init ? */
|
||||
if (hprt0 & HPRT0_PWR) {
|
||||
/* Enable external vbus supply before resuming root hub */
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
ret = dwc2_vbus_supply_init(hsotg);
|
||||
if (ret)
|
||||
return ret;
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
/* Initialize and connect root hub if one is not already attached */
|
||||
if (bus->root_hub) {
|
||||
dev_dbg(hsotg->dev, "DWC OTG HCD Has Root Hub\n");
|
||||
@@ -4417,7 +4431,7 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
return dwc2_vbus_supply_init(hsotg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4428,6 +4442,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
|
||||
unsigned long flags;
|
||||
u32 hprt0;
|
||||
|
||||
/* Turn off all host-specific interrupts */
|
||||
dwc2_disable_host_interrupts(hsotg);
|
||||
@@ -4436,6 +4451,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
|
||||
synchronize_irq(hcd->irq);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
hprt0 = dwc2_read_hprt0(hsotg);
|
||||
/* Ensure hcd is disconnected */
|
||||
dwc2_hcd_disconnect(hsotg, true);
|
||||
dwc2_hcd_stop(hsotg);
|
||||
@@ -4444,7 +4460,9 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
|
||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
dwc2_vbus_supply_exit(hsotg);
|
||||
/* keep balanced supply init/exit by checking HPRT0_PWR */
|
||||
if (hprt0 & HPRT0_PWR)
|
||||
dwc2_vbus_supply_exit(hsotg);
|
||||
|
||||
usleep_range(1000, 3000);
|
||||
}
|
||||
@@ -4482,7 +4500,9 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
|
||||
hprt0 |= HPRT0_SUSP;
|
||||
hprt0 &= ~HPRT0_PWR;
|
||||
dwc2_writel(hsotg, hprt0, HPRT0);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
dwc2_vbus_supply_exit(hsotg);
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
/* Enter partial_power_down */
|
||||
|
@@ -312,6 +312,7 @@
|
||||
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14
|
||||
#define GHWCFG4_ACG_SUPPORTED BIT(12)
|
||||
#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11)
|
||||
#define GHWCFG4_SERVICE_INTERVAL_SUPPORTED BIT(10)
|
||||
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0
|
||||
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1
|
||||
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2
|
||||
@@ -404,6 +405,19 @@
|
||||
#define ADPCTL_PRB_DSCHRG_MASK (0x3 << 0)
|
||||
#define ADPCTL_PRB_DSCHRG_SHIFT 0
|
||||
|
||||
#define GREFCLK HSOTG_REG(0x0064)
|
||||
#define GREFCLK_REFCLKPER_MASK (0x1ffff << 15)
|
||||
#define GREFCLK_REFCLKPER_SHIFT 15
|
||||
#define GREFCLK_REF_CLK_MODE BIT(14)
|
||||
#define GREFCLK_SOF_CNT_WKUP_ALERT_MASK (0x3ff)
|
||||
#define GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT 0
|
||||
|
||||
#define GINTMSK2 HSOTG_REG(0x0068)
|
||||
#define GINTMSK2_WKUP_ALERT_INT_MSK BIT(0)
|
||||
|
||||
#define GINTSTS2 HSOTG_REG(0x006c)
|
||||
#define GINTSTS2_WKUP_ALERT_INT BIT(0)
|
||||
|
||||
#define HPTXFSIZ HSOTG_REG(0x100)
|
||||
/* Use FIFOSIZE_* constants to access this register */
|
||||
|
||||
@@ -443,6 +457,7 @@
|
||||
#define DCFG_DEVSPD_FS48 3
|
||||
|
||||
#define DCTL HSOTG_REG(0x804)
|
||||
#define DCTL_SERVICE_INTERVAL_SUPPORTED BIT(19)
|
||||
#define DCTL_PWRONPRGDONE BIT(11)
|
||||
#define DCTL_CGOUTNAK BIT(10)
|
||||
#define DCTL_SGOUTNAK BIT(9)
|
||||
|
@@ -81,6 +81,7 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
|
||||
p->host_perio_tx_fifo_size = 256;
|
||||
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
|
||||
GAHBCFG_HBSTLEN_SHIFT;
|
||||
p->power_down = 0;
|
||||
}
|
||||
|
||||
static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
|
||||
@@ -299,9 +300,12 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
|
||||
p->hird_threshold_en = true;
|
||||
p->hird_threshold = 4;
|
||||
p->ipg_isoc_en = false;
|
||||
p->service_interval = false;
|
||||
p->max_packet_count = hw->max_packet_count;
|
||||
p->max_transfer_size = hw->max_transfer_size;
|
||||
p->ahbcfg = GAHBCFG_HBSTLEN_INCR << GAHBCFG_HBSTLEN_SHIFT;
|
||||
p->ref_clk_per = 33333;
|
||||
p->sof_cnt_wkup_alert = 100;
|
||||
|
||||
if ((hsotg->dr_mode == USB_DR_MODE_HOST) ||
|
||||
(hsotg->dr_mode == USB_DR_MODE_OTG)) {
|
||||
@@ -592,6 +596,7 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg)
|
||||
CHECK_BOOL(besl, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a));
|
||||
CHECK_BOOL(hird_threshold_en, hsotg->params.lpm);
|
||||
CHECK_RANGE(hird_threshold, 0, hsotg->params.besl ? 12 : 7, 0);
|
||||
CHECK_BOOL(service_interval, hw->service_interval_mode);
|
||||
CHECK_RANGE(max_packet_count,
|
||||
15, hw->max_packet_count,
|
||||
hw->max_packet_count);
|
||||
@@ -780,6 +785,8 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
||||
GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT;
|
||||
hw->acg_enable = !!(hwcfg4 & GHWCFG4_ACG_SUPPORTED);
|
||||
hw->ipg_isoc_en = !!(hwcfg4 & GHWCFG4_IPG_ISOC_SUPPORTED);
|
||||
hw->service_interval_mode = !!(hwcfg4 &
|
||||
GHWCFG4_SERVICE_INTERVAL_SUPPORTED);
|
||||
|
||||
/* fifo sizes */
|
||||
hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
|
||||
|
@@ -432,6 +432,14 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");
|
||||
if (IS_ERR(hsotg->vbus_supply)) {
|
||||
retval = PTR_ERR(hsotg->vbus_supply);
|
||||
hsotg->vbus_supply = NULL;
|
||||
if (retval != -ENODEV)
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = dwc2_lowlevel_hw_enable(hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
@@ -113,7 +113,7 @@ config USB_DWC3_ST
|
||||
|
||||
config USB_DWC3_QCOM
|
||||
tristate "Qualcomm Platform"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
depends on EXTCON && (ARCH_QCOM || COMPILE_TEST)
|
||||
depends on OF
|
||||
default USB_DWC3
|
||||
help
|
||||
|
@@ -756,7 +756,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
|
||||
|
||||
/* check if current dwc3 is on simulation board */
|
||||
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
|
||||
dev_info(dwc->dev, "Running with FPGA optmizations\n");
|
||||
dev_info(dwc->dev, "Running with FPGA optimizations\n");
|
||||
dwc->is_fpga = true;
|
||||
}
|
||||
|
||||
|
@@ -13,80 +13,30 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/usb_phy_generic.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define DWC3_EXYNOS_MAX_CLOCKS 4
|
||||
|
||||
struct dwc3_exynos_driverdata {
|
||||
const char *clk_names[DWC3_EXYNOS_MAX_CLOCKS];
|
||||
int num_clks;
|
||||
int suspend_clk_idx;
|
||||
};
|
||||
|
||||
struct dwc3_exynos {
|
||||
struct platform_device *usb2_phy;
|
||||
struct platform_device *usb3_phy;
|
||||
struct device *dev;
|
||||
|
||||
struct clk *clk;
|
||||
struct clk *susp_clk;
|
||||
struct clk *axius_clk;
|
||||
const char **clk_names;
|
||||
struct clk *clks[DWC3_EXYNOS_MAX_CLOCKS];
|
||||
int num_clks;
|
||||
int suspend_clk_idx;
|
||||
|
||||
struct regulator *vdd33;
|
||||
struct regulator *vdd10;
|
||||
};
|
||||
|
||||
static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
|
||||
{
|
||||
struct usb_phy_generic_platform_data pdata;
|
||||
struct platform_device *pdev;
|
||||
int ret;
|
||||
|
||||
memset(&pdata, 0x00, sizeof(pdata));
|
||||
|
||||
pdev = platform_device_alloc("usb_phy_generic", PLATFORM_DEVID_AUTO);
|
||||
if (!pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
exynos->usb2_phy = pdev;
|
||||
pdata.type = USB_PHY_TYPE_USB2;
|
||||
pdata.gpio_reset = -1;
|
||||
|
||||
ret = platform_device_add_data(exynos->usb2_phy, &pdata, sizeof(pdata));
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
pdev = platform_device_alloc("usb_phy_generic", PLATFORM_DEVID_AUTO);
|
||||
if (!pdev) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
exynos->usb3_phy = pdev;
|
||||
pdata.type = USB_PHY_TYPE_USB3;
|
||||
|
||||
ret = platform_device_add_data(exynos->usb3_phy, &pdata, sizeof(pdata));
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = platform_device_add(exynos->usb2_phy);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = platform_device_add(exynos->usb3_phy);
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
platform_device_del(exynos->usb2_phy);
|
||||
|
||||
err2:
|
||||
platform_device_put(exynos->usb3_phy);
|
||||
|
||||
err1:
|
||||
platform_device_put(exynos->usb2_phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_exynos_remove_child(struct device *dev, void *unused)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
@@ -101,47 +51,42 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
||||
struct dwc3_exynos *exynos;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
|
||||
int ret;
|
||||
const struct dwc3_exynos_driverdata *driver_data;
|
||||
int i, ret;
|
||||
|
||||
exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
|
||||
if (!exynos)
|
||||
return -ENOMEM;
|
||||
|
||||
driver_data = of_device_get_match_data(dev);
|
||||
exynos->dev = dev;
|
||||
exynos->num_clks = driver_data->num_clks;
|
||||
exynos->clk_names = (const char **)driver_data->clk_names;
|
||||
exynos->suspend_clk_idx = driver_data->suspend_clk_idx;
|
||||
|
||||
platform_set_drvdata(pdev, exynos);
|
||||
|
||||
exynos->dev = dev;
|
||||
|
||||
exynos->clk = devm_clk_get(dev, "usbdrd30");
|
||||
if (IS_ERR(exynos->clk)) {
|
||||
dev_err(dev, "couldn't get clock\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = clk_prepare_enable(exynos->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
exynos->susp_clk = devm_clk_get(dev, "usbdrd30_susp_clk");
|
||||
if (IS_ERR(exynos->susp_clk))
|
||||
exynos->susp_clk = NULL;
|
||||
ret = clk_prepare_enable(exynos->susp_clk);
|
||||
if (ret)
|
||||
goto susp_clk_err;
|
||||
|
||||
if (of_device_is_compatible(node, "samsung,exynos7-dwusb3")) {
|
||||
exynos->axius_clk = devm_clk_get(dev, "usbdrd30_axius_clk");
|
||||
if (IS_ERR(exynos->axius_clk)) {
|
||||
dev_err(dev, "no AXI UpScaler clk specified\n");
|
||||
ret = -ENODEV;
|
||||
goto axius_clk_err;
|
||||
for (i = 0; i < exynos->num_clks; i++) {
|
||||
exynos->clks[i] = devm_clk_get(dev, exynos->clk_names[i]);
|
||||
if (IS_ERR(exynos->clks[i])) {
|
||||
dev_err(dev, "failed to get clock: %s\n",
|
||||
exynos->clk_names[i]);
|
||||
return PTR_ERR(exynos->clks[i]);
|
||||
}
|
||||
ret = clk_prepare_enable(exynos->axius_clk);
|
||||
if (ret)
|
||||
goto axius_clk_err;
|
||||
} else {
|
||||
exynos->axius_clk = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < exynos->num_clks; i++) {
|
||||
ret = clk_prepare_enable(exynos->clks[i]);
|
||||
if (ret) {
|
||||
while (--i > 0)
|
||||
clk_disable_unprepare(exynos->clks[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (exynos->suspend_clk_idx >= 0)
|
||||
clk_prepare_enable(exynos->clks[exynos->suspend_clk_idx]);
|
||||
|
||||
exynos->vdd33 = devm_regulator_get(dev, "vdd33");
|
||||
if (IS_ERR(exynos->vdd33)) {
|
||||
ret = PTR_ERR(exynos->vdd33);
|
||||
@@ -164,12 +109,6 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
||||
goto vdd10_err;
|
||||
}
|
||||
|
||||
ret = dwc3_exynos_register_phys(exynos);
|
||||
if (ret) {
|
||||
dev_err(dev, "couldn't register PHYs\n");
|
||||
goto phys_err;
|
||||
}
|
||||
|
||||
if (node) {
|
||||
ret = of_platform_populate(node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
@@ -185,32 +124,31 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
populate_err:
|
||||
platform_device_unregister(exynos->usb2_phy);
|
||||
platform_device_unregister(exynos->usb3_phy);
|
||||
phys_err:
|
||||
regulator_disable(exynos->vdd10);
|
||||
vdd10_err:
|
||||
regulator_disable(exynos->vdd33);
|
||||
vdd33_err:
|
||||
clk_disable_unprepare(exynos->axius_clk);
|
||||
axius_clk_err:
|
||||
clk_disable_unprepare(exynos->susp_clk);
|
||||
susp_clk_err:
|
||||
clk_disable_unprepare(exynos->clk);
|
||||
for (i = exynos->num_clks - 1; i >= 0; i--)
|
||||
clk_disable_unprepare(exynos->clks[i]);
|
||||
|
||||
if (exynos->suspend_clk_idx >= 0)
|
||||
clk_disable_unprepare(exynos->clks[exynos->suspend_clk_idx]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_exynos_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_exynos *exynos = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
device_for_each_child(&pdev->dev, NULL, dwc3_exynos_remove_child);
|
||||
platform_device_unregister(exynos->usb2_phy);
|
||||
platform_device_unregister(exynos->usb3_phy);
|
||||
|
||||
clk_disable_unprepare(exynos->axius_clk);
|
||||
clk_disable_unprepare(exynos->susp_clk);
|
||||
clk_disable_unprepare(exynos->clk);
|
||||
for (i = exynos->num_clks - 1; i >= 0; i--)
|
||||
clk_disable_unprepare(exynos->clks[i]);
|
||||
|
||||
if (exynos->suspend_clk_idx >= 0)
|
||||
clk_disable_unprepare(exynos->clks[exynos->suspend_clk_idx]);
|
||||
|
||||
regulator_disable(exynos->vdd33);
|
||||
regulator_disable(exynos->vdd10);
|
||||
@@ -218,10 +156,36 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dwc3_exynos_driverdata exynos5250_drvdata = {
|
||||
.clk_names = { "usbdrd30" },
|
||||
.num_clks = 1,
|
||||
.suspend_clk_idx = -1,
|
||||
};
|
||||
|
||||
static const struct dwc3_exynos_driverdata exynos5433_drvdata = {
|
||||
.clk_names = { "aclk", "susp_clk", "pipe_pclk", "phyclk" },
|
||||
.num_clks = 4,
|
||||
.suspend_clk_idx = 1,
|
||||
};
|
||||
|
||||
static const struct dwc3_exynos_driverdata exynos7_drvdata = {
|
||||
.clk_names = { "usbdrd30", "usbdrd30_susp_clk", "usbdrd30_axius_clk" },
|
||||
.num_clks = 3,
|
||||
.suspend_clk_idx = 1,
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos_dwc3_match[] = {
|
||||
{ .compatible = "samsung,exynos5250-dwusb3" },
|
||||
{ .compatible = "samsung,exynos7-dwusb3" },
|
||||
{},
|
||||
{
|
||||
.compatible = "samsung,exynos5250-dwusb3",
|
||||
.data = &exynos5250_drvdata,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-dwusb3",
|
||||
.data = &exynos5433_drvdata,
|
||||
}, {
|
||||
.compatible = "samsung,exynos7-dwusb3",
|
||||
.data = &exynos7_drvdata,
|
||||
}, {
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
|
||||
|
||||
@@ -229,9 +193,10 @@ MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
|
||||
static int dwc3_exynos_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_exynos *exynos = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
clk_disable(exynos->axius_clk);
|
||||
clk_disable(exynos->clk);
|
||||
for (i = exynos->num_clks - 1; i >= 0; i--)
|
||||
clk_disable_unprepare(exynos->clks[i]);
|
||||
|
||||
regulator_disable(exynos->vdd33);
|
||||
regulator_disable(exynos->vdd10);
|
||||
@@ -242,7 +207,7 @@ static int dwc3_exynos_suspend(struct device *dev)
|
||||
static int dwc3_exynos_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_exynos *exynos = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
ret = regulator_enable(exynos->vdd33);
|
||||
if (ret) {
|
||||
@@ -255,13 +220,14 @@ static int dwc3_exynos_resume(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_enable(exynos->clk);
|
||||
clk_enable(exynos->axius_clk);
|
||||
|
||||
/* runtime set active to reflect active state. */
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
for (i = 0; i < exynos->num_clks; i++) {
|
||||
ret = clk_prepare_enable(exynos->clks[i]);
|
||||
if (ret) {
|
||||
while (--i > 0)
|
||||
clk_disable_unprepare(exynos->clks[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -270,27 +270,36 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
||||
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 timeout = 1000;
|
||||
u32 saved_config = 0;
|
||||
u32 reg;
|
||||
|
||||
int cmd_status = 0;
|
||||
int susphy = false;
|
||||
int ret = -EINVAL;
|
||||
|
||||
/*
|
||||
* Synopsys Databook 2.60a states, on section 6.3.2.5.[1-8], that if
|
||||
* we're issuing an endpoint command, we must check if
|
||||
* GUSB2PHYCFG.SUSPHY bit is set. If it is, then we need to clear it.
|
||||
* When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or
|
||||
* GUSB2PHYCFG.SUSPHY is set, it must be cleared before issuing an
|
||||
* endpoint command.
|
||||
*
|
||||
* We will also set SUSPHY bit to what it was before returning as stated
|
||||
* by the same section on Synopsys databook.
|
||||
* Save and clear both GUSB2PHYCFG.ENBLSLPM and GUSB2PHYCFG.SUSPHY
|
||||
* settings. Restore them after the command is completed.
|
||||
*
|
||||
* DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2
|
||||
*/
|
||||
if (dwc->gadget.speed <= USB_SPEED_HIGH) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
|
||||
susphy = true;
|
||||
saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
}
|
||||
|
||||
if (reg & DWC3_GUSB2PHYCFG_ENBLSLPM) {
|
||||
saved_config |= DWC3_GUSB2PHYCFG_ENBLSLPM;
|
||||
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
|
||||
}
|
||||
|
||||
if (saved_config)
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
}
|
||||
|
||||
if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
|
||||
@@ -389,9 +398,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(susphy)) {
|
||||
if (saved_config) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
reg |= saved_config;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
}
|
||||
|
||||
|
@@ -717,17 +717,14 @@ static void xdbc_handle_port_status(struct xdbc_trb *evt_trb)
|
||||
|
||||
static void xdbc_handle_tx_event(struct xdbc_trb *evt_trb)
|
||||
{
|
||||
size_t remain_length;
|
||||
u32 comp_code;
|
||||
int ep_id;
|
||||
|
||||
comp_code = GET_COMP_CODE(le32_to_cpu(evt_trb->field[2]));
|
||||
remain_length = EVENT_TRB_LEN(le32_to_cpu(evt_trb->field[2]));
|
||||
ep_id = TRB_TO_EP_ID(le32_to_cpu(evt_trb->field[3]));
|
||||
|
||||
switch (comp_code) {
|
||||
case COMP_SUCCESS:
|
||||
remain_length = 0;
|
||||
case COMP_SHORT_PACKET:
|
||||
break;
|
||||
case COMP_TRB_ERROR:
|
||||
|
@@ -22,12 +22,8 @@
|
||||
* controlled by two clock sources :
|
||||
* CLK_5 := c_srate, and CLK_6 := p_srate
|
||||
*/
|
||||
#define USB_OUT_IT_ID 1
|
||||
#define IO_IN_IT_ID 2
|
||||
#define IO_OUT_OT_ID 3
|
||||
#define USB_IN_OT_ID 4
|
||||
#define USB_OUT_CLK_ID 5
|
||||
#define USB_IN_CLK_ID 6
|
||||
#define USB_OUT_CLK_ID (out_clk_src_desc.bClockID)
|
||||
#define USB_IN_CLK_ID (in_clk_src_desc.bClockID)
|
||||
|
||||
#define CONTROL_ABSENT 0
|
||||
#define CONTROL_RDONLY 1
|
||||
@@ -43,6 +39,9 @@
|
||||
#define UNFLW_CTRL 8
|
||||
#define OVFLW_CTRL 10
|
||||
|
||||
#define EPIN_EN(_opts) ((_opts)->p_chmask != 0)
|
||||
#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)
|
||||
|
||||
struct f_uac2 {
|
||||
struct g_audio g_audio;
|
||||
u8 ac_intf, as_in_intf, as_out_intf;
|
||||
@@ -135,7 +134,7 @@ static struct uac_clock_source_descriptor in_clk_src_desc = {
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
|
||||
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
|
||||
.bClockID = USB_IN_CLK_ID,
|
||||
/* .bClockID = DYNAMIC */
|
||||
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
|
||||
.bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
|
||||
.bAssocTerminal = 0,
|
||||
@@ -147,7 +146,7 @@ static struct uac_clock_source_descriptor out_clk_src_desc = {
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
|
||||
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
|
||||
.bClockID = USB_OUT_CLK_ID,
|
||||
/* .bClockID = DYNAMIC */
|
||||
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
|
||||
.bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
|
||||
.bAssocTerminal = 0,
|
||||
@@ -159,10 +158,10 @@ static struct uac2_input_terminal_descriptor usb_out_it_desc = {
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
|
||||
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
|
||||
.bTerminalID = USB_OUT_IT_ID,
|
||||
/* .bTerminalID = DYNAMIC */
|
||||
.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
|
||||
.bAssocTerminal = 0,
|
||||
.bCSourceID = USB_OUT_CLK_ID,
|
||||
/* .bCSourceID = DYNAMIC */
|
||||
.iChannelNames = 0,
|
||||
.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
|
||||
};
|
||||
@@ -173,10 +172,10 @@ static struct uac2_input_terminal_descriptor io_in_it_desc = {
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
|
||||
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
|
||||
.bTerminalID = IO_IN_IT_ID,
|
||||
/* .bTerminalID = DYNAMIC */
|
||||
.wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_UNDEFINED),
|
||||
.bAssocTerminal = 0,
|
||||
.bCSourceID = USB_IN_CLK_ID,
|
||||
/* .bCSourceID = DYNAMIC */
|
||||
.iChannelNames = 0,
|
||||
.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
|
||||
};
|
||||
@@ -187,11 +186,11 @@ static struct uac2_output_terminal_descriptor usb_in_ot_desc = {
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
|
||||
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
|
||||
.bTerminalID = USB_IN_OT_ID,
|
||||
/* .bTerminalID = DYNAMIC */
|
||||
.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
|
||||
.bAssocTerminal = 0,
|
||||
.bSourceID = IO_IN_IT_ID,
|
||||
.bCSourceID = USB_IN_CLK_ID,
|
||||
/* .bSourceID = DYNAMIC */
|
||||
/* .bCSourceID = DYNAMIC */
|
||||
.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
|
||||
};
|
||||
|
||||
@@ -201,11 +200,11 @@ static struct uac2_output_terminal_descriptor io_out_ot_desc = {
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
|
||||
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
|
||||
.bTerminalID = IO_OUT_OT_ID,
|
||||
/* .bTerminalID = DYNAMIC */
|
||||
.wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_UNDEFINED),
|
||||
.bAssocTerminal = 0,
|
||||
.bSourceID = USB_OUT_IT_ID,
|
||||
.bCSourceID = USB_OUT_CLK_ID,
|
||||
/* .bSourceID = DYNAMIC */
|
||||
/* .bCSourceID = DYNAMIC */
|
||||
.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
|
||||
};
|
||||
|
||||
@@ -253,7 +252,7 @@ static struct uac2_as_header_descriptor as_out_hdr_desc = {
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
|
||||
.bDescriptorSubtype = UAC_AS_GENERAL,
|
||||
.bTerminalLink = USB_OUT_IT_ID,
|
||||
/* .bTerminalLink = DYNAMIC */
|
||||
.bmControls = 0,
|
||||
.bFormatType = UAC_FORMAT_TYPE_I,
|
||||
.bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),
|
||||
@@ -330,7 +329,7 @@ static struct uac2_as_header_descriptor as_in_hdr_desc = {
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
|
||||
.bDescriptorSubtype = UAC_AS_GENERAL,
|
||||
.bTerminalLink = USB_IN_OT_ID,
|
||||
/* .bTerminalLink = DYNAMIC */
|
||||
.bmControls = 0,
|
||||
.bFormatType = UAC_FORMAT_TYPE_I,
|
||||
.bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),
|
||||
@@ -471,6 +470,125 @@ static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
|
||||
le16_to_cpu(ep_desc->wMaxPacketSize)));
|
||||
}
|
||||
|
||||
/* Use macro to overcome line length limitation */
|
||||
#define USBDHDR(p) (struct usb_descriptor_header *)(p)
|
||||
|
||||
static void setup_descriptor(struct f_uac2_opts *opts)
|
||||
{
|
||||
/* patch descriptors */
|
||||
int i = 1; /* ID's start with 1 */
|
||||
|
||||
if (EPOUT_EN(opts))
|
||||
usb_out_it_desc.bTerminalID = i++;
|
||||
if (EPIN_EN(opts))
|
||||
io_in_it_desc.bTerminalID = i++;
|
||||
if (EPOUT_EN(opts))
|
||||
io_out_ot_desc.bTerminalID = i++;
|
||||
if (EPIN_EN(opts))
|
||||
usb_in_ot_desc.bTerminalID = i++;
|
||||
if (EPOUT_EN(opts))
|
||||
out_clk_src_desc.bClockID = i++;
|
||||
if (EPIN_EN(opts))
|
||||
in_clk_src_desc.bClockID = i++;
|
||||
|
||||
usb_out_it_desc.bCSourceID = out_clk_src_desc.bClockID;
|
||||
usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID;
|
||||
usb_in_ot_desc.bCSourceID = in_clk_src_desc.bClockID;
|
||||
io_in_it_desc.bCSourceID = in_clk_src_desc.bClockID;
|
||||
io_out_ot_desc.bCSourceID = out_clk_src_desc.bClockID;
|
||||
io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID;
|
||||
as_out_hdr_desc.bTerminalLink = usb_out_it_desc.bTerminalID;
|
||||
as_in_hdr_desc.bTerminalLink = usb_in_ot_desc.bTerminalID;
|
||||
|
||||
iad_desc.bInterfaceCount = 1;
|
||||
ac_hdr_desc.wTotalLength = 0;
|
||||
|
||||
if (EPIN_EN(opts)) {
|
||||
u16 len = le16_to_cpu(ac_hdr_desc.wTotalLength);
|
||||
|
||||
len += sizeof(in_clk_src_desc);
|
||||
len += sizeof(usb_in_ot_desc);
|
||||
len += sizeof(io_in_it_desc);
|
||||
ac_hdr_desc.wTotalLength = cpu_to_le16(len);
|
||||
iad_desc.bInterfaceCount++;
|
||||
}
|
||||
if (EPOUT_EN(opts)) {
|
||||
u16 len = le16_to_cpu(ac_hdr_desc.wTotalLength);
|
||||
|
||||
len += sizeof(out_clk_src_desc);
|
||||
len += sizeof(usb_out_it_desc);
|
||||
len += sizeof(io_out_ot_desc);
|
||||
ac_hdr_desc.wTotalLength = cpu_to_le16(len);
|
||||
iad_desc.bInterfaceCount++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
fs_audio_desc[i++] = USBDHDR(&iad_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&std_ac_if_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&ac_hdr_desc);
|
||||
if (EPIN_EN(opts))
|
||||
fs_audio_desc[i++] = USBDHDR(&in_clk_src_desc);
|
||||
if (EPOUT_EN(opts)) {
|
||||
fs_audio_desc[i++] = USBDHDR(&out_clk_src_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&usb_out_it_desc);
|
||||
}
|
||||
if (EPIN_EN(opts)) {
|
||||
fs_audio_desc[i++] = USBDHDR(&io_in_it_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&usb_in_ot_desc);
|
||||
}
|
||||
if (EPOUT_EN(opts)) {
|
||||
fs_audio_desc[i++] = USBDHDR(&io_out_ot_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&std_as_out_if0_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&std_as_out_if1_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&as_out_hdr_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&as_out_fmt1_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&fs_epout_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&as_iso_out_desc);
|
||||
}
|
||||
if (EPIN_EN(opts)) {
|
||||
fs_audio_desc[i++] = USBDHDR(&std_as_in_if0_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&std_as_in_if1_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&as_in_hdr_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&as_in_fmt1_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&fs_epin_desc);
|
||||
fs_audio_desc[i++] = USBDHDR(&as_iso_in_desc);
|
||||
}
|
||||
fs_audio_desc[i] = NULL;
|
||||
|
||||
i = 0;
|
||||
hs_audio_desc[i++] = USBDHDR(&iad_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&std_ac_if_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&ac_hdr_desc);
|
||||
if (EPIN_EN(opts))
|
||||
hs_audio_desc[i++] = USBDHDR(&in_clk_src_desc);
|
||||
if (EPOUT_EN(opts)) {
|
||||
hs_audio_desc[i++] = USBDHDR(&out_clk_src_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&usb_out_it_desc);
|
||||
}
|
||||
if (EPIN_EN(opts)) {
|
||||
hs_audio_desc[i++] = USBDHDR(&io_in_it_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&usb_in_ot_desc);
|
||||
}
|
||||
if (EPOUT_EN(opts)) {
|
||||
hs_audio_desc[i++] = USBDHDR(&io_out_ot_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&std_as_out_if0_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&std_as_out_if1_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&as_out_hdr_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&as_out_fmt1_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&hs_epout_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&as_iso_out_desc);
|
||||
}
|
||||
if (EPIN_EN(opts)) {
|
||||
hs_audio_desc[i++] = USBDHDR(&std_as_in_if0_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&std_as_in_if1_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&as_in_hdr_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&as_in_fmt1_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&hs_epin_desc);
|
||||
hs_audio_desc[i++] = USBDHDR(&as_iso_in_desc);
|
||||
}
|
||||
hs_audio_desc[i] = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
||||
{
|
||||
@@ -530,25 +648,29 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
||||
uac2->ac_intf = ret;
|
||||
uac2->ac_alt = 0;
|
||||
|
||||
ret = usb_interface_id(cfg, fn);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
if (EPOUT_EN(uac2_opts)) {
|
||||
ret = usb_interface_id(cfg, fn);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
std_as_out_if0_desc.bInterfaceNumber = ret;
|
||||
std_as_out_if1_desc.bInterfaceNumber = ret;
|
||||
uac2->as_out_intf = ret;
|
||||
uac2->as_out_alt = 0;
|
||||
}
|
||||
std_as_out_if0_desc.bInterfaceNumber = ret;
|
||||
std_as_out_if1_desc.bInterfaceNumber = ret;
|
||||
uac2->as_out_intf = ret;
|
||||
uac2->as_out_alt = 0;
|
||||
|
||||
ret = usb_interface_id(cfg, fn);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
if (EPIN_EN(uac2_opts)) {
|
||||
ret = usb_interface_id(cfg, fn);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
std_as_in_if0_desc.bInterfaceNumber = ret;
|
||||
std_as_in_if1_desc.bInterfaceNumber = ret;
|
||||
uac2->as_in_intf = ret;
|
||||
uac2->as_in_alt = 0;
|
||||
}
|
||||
std_as_in_if0_desc.bInterfaceNumber = ret;
|
||||
std_as_in_if1_desc.bInterfaceNumber = ret;
|
||||
uac2->as_in_intf = ret;
|
||||
uac2->as_in_alt = 0;
|
||||
|
||||
/* Calculate wMaxPacketSize according to audio bandwidth */
|
||||
set_ep_max_packet_size(uac2_opts, &fs_epin_desc, 1000, true);
|
||||
@@ -556,16 +678,20 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
||||
set_ep_max_packet_size(uac2_opts, &hs_epin_desc, 8000, true);
|
||||
set_ep_max_packet_size(uac2_opts, &hs_epout_desc, 8000, false);
|
||||
|
||||
agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
|
||||
if (!agdev->out_ep) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return -ENODEV;
|
||||
if (EPOUT_EN(uac2_opts)) {
|
||||
agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
|
||||
if (!agdev->out_ep) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
|
||||
if (!agdev->in_ep) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return -ENODEV;
|
||||
if (EPIN_EN(uac2_opts)) {
|
||||
agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
|
||||
if (!agdev->in_ep) {
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
agdev->in_ep_maxpsize = max_t(u16,
|
||||
@@ -578,6 +704,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
||||
hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
|
||||
hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
|
||||
|
||||
setup_descriptor(uac2_opts);
|
||||
|
||||
ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL,
|
||||
NULL);
|
||||
if (ret)
|
||||
|
@@ -197,12 +197,6 @@ static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
void uvc_set_trace_param(unsigned int trace)
|
||||
{
|
||||
uvc_gadget_trace_param = trace;
|
||||
}
|
||||
EXPORT_SYMBOL(uvc_set_trace_param);
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Control requests
|
||||
*/
|
||||
@@ -232,13 +226,8 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
|
||||
struct v4l2_event v4l2_event;
|
||||
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
|
||||
|
||||
/* printk(KERN_INFO "setup request %02x %02x value %04x index %04x %04x\n",
|
||||
* ctrl->bRequestType, ctrl->bRequest, le16_to_cpu(ctrl->wValue),
|
||||
* le16_to_cpu(ctrl->wIndex), le16_to_cpu(ctrl->wLength));
|
||||
*/
|
||||
|
||||
if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) {
|
||||
INFO(f->config->cdev, "invalid request type\n");
|
||||
uvcg_info(f, "invalid request type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -272,7 +261,7 @@ uvc_function_get_alt(struct usb_function *f, unsigned interface)
|
||||
{
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
|
||||
INFO(f->config->cdev, "uvc_function_get_alt(%u)\n", interface);
|
||||
uvcg_info(f, "%s(%u)\n", __func__, interface);
|
||||
|
||||
if (interface == uvc->control_intf)
|
||||
return 0;
|
||||
@@ -291,13 +280,13 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
|
||||
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
|
||||
int ret;
|
||||
|
||||
INFO(cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);
|
||||
uvcg_info(f, "%s(%u, %u)\n", __func__, interface, alt);
|
||||
|
||||
if (interface == uvc->control_intf) {
|
||||
if (alt)
|
||||
return -EINVAL;
|
||||
|
||||
INFO(cdev, "reset UVC Control\n");
|
||||
uvcg_info(f, "reset UVC Control\n");
|
||||
usb_ep_disable(uvc->control_ep);
|
||||
|
||||
if (!uvc->control_ep->desc)
|
||||
@@ -348,7 +337,7 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
|
||||
if (!uvc->video.ep)
|
||||
return -EINVAL;
|
||||
|
||||
INFO(cdev, "reset UVC\n");
|
||||
uvcg_info(f, "reset UVC\n");
|
||||
usb_ep_disable(uvc->video.ep);
|
||||
|
||||
ret = config_ep_by_speed(f->config->cdev->gadget,
|
||||
@@ -373,7 +362,7 @@ uvc_function_disable(struct usb_function *f)
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
struct v4l2_event v4l2_event;
|
||||
|
||||
INFO(f->config->cdev, "uvc_function_disable\n");
|
||||
uvcg_info(f, "%s()\n", __func__);
|
||||
|
||||
memset(&v4l2_event, 0, sizeof(v4l2_event));
|
||||
v4l2_event.type = UVC_EVENT_DISCONNECT;
|
||||
@@ -392,21 +381,19 @@ uvc_function_disable(struct usb_function *f)
|
||||
void
|
||||
uvc_function_connect(struct uvc_device *uvc)
|
||||
{
|
||||
struct usb_composite_dev *cdev = uvc->func.config->cdev;
|
||||
int ret;
|
||||
|
||||
if ((ret = usb_function_activate(&uvc->func)) < 0)
|
||||
INFO(cdev, "UVC connect failed with %d\n", ret);
|
||||
uvcg_info(&uvc->func, "UVC connect failed with %d\n", ret);
|
||||
}
|
||||
|
||||
void
|
||||
uvc_function_disconnect(struct uvc_device *uvc)
|
||||
{
|
||||
struct usb_composite_dev *cdev = uvc->func.config->cdev;
|
||||
int ret;
|
||||
|
||||
if ((ret = usb_function_deactivate(&uvc->func)) < 0)
|
||||
INFO(cdev, "UVC disconnect failed with %d\n", ret);
|
||||
uvcg_info(&uvc->func, "UVC disconnect failed with %d\n", ret);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
@@ -605,7 +592,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
struct f_uvc_opts *opts;
|
||||
int ret = -EINVAL;
|
||||
|
||||
INFO(cdev, "uvc_function_bind\n");
|
||||
uvcg_info(f, "%s()\n", __func__);
|
||||
|
||||
opts = fi_to_f_uvc_opts(f->fi);
|
||||
/* Sanity check the streaming endpoint module parameters.
|
||||
@@ -618,8 +605,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
if (opts->streaming_maxburst &&
|
||||
(opts->streaming_maxpacket % 1024) != 0) {
|
||||
opts->streaming_maxpacket = roundup(opts->streaming_maxpacket, 1024);
|
||||
INFO(cdev, "overriding streaming_maxpacket to %d\n",
|
||||
opts->streaming_maxpacket);
|
||||
uvcg_info(f, "overriding streaming_maxpacket to %d\n",
|
||||
opts->streaming_maxpacket);
|
||||
}
|
||||
|
||||
/* Fill in the FS/HS/SS Video Streaming specific descriptors from the
|
||||
@@ -658,7 +645,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
/* Allocate endpoints. */
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
|
||||
if (!ep) {
|
||||
INFO(cdev, "Unable to allocate control EP\n");
|
||||
uvcg_info(f, "Unable to allocate control EP\n");
|
||||
goto error;
|
||||
}
|
||||
uvc->control_ep = ep;
|
||||
@@ -672,7 +659,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
|
||||
|
||||
if (!ep) {
|
||||
INFO(cdev, "Unable to allocate streaming EP\n");
|
||||
uvcg_info(f, "Unable to allocate streaming EP\n");
|
||||
goto error;
|
||||
}
|
||||
uvc->video.ep = ep;
|
||||
@@ -699,12 +686,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
uvc_iad.bFirstInterface = ret;
|
||||
uvc_control_intf.bInterfaceNumber = ret;
|
||||
uvc->control_intf = ret;
|
||||
opts->control_interface = ret;
|
||||
|
||||
if ((ret = usb_interface_id(c, f)) < 0)
|
||||
goto error;
|
||||
uvc_streaming_intf_alt0.bInterfaceNumber = ret;
|
||||
uvc_streaming_intf_alt1.bInterfaceNumber = ret;
|
||||
uvc->streaming_intf = ret;
|
||||
opts->streaming_interface = ret;
|
||||
|
||||
/* Copy descriptors */
|
||||
f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
|
||||
@@ -743,19 +732,19 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
uvc->control_req->context = uvc;
|
||||
|
||||
if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) {
|
||||
printk(KERN_INFO "v4l2_device_register failed\n");
|
||||
uvcg_err(f, "failed to register V4L2 device\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Initialise video. */
|
||||
ret = uvcg_video_init(&uvc->video);
|
||||
ret = uvcg_video_init(&uvc->video, uvc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/* Register a V4L2 device. */
|
||||
ret = uvc_register_video(uvc);
|
||||
if (ret < 0) {
|
||||
printk(KERN_INFO "Unable to register video device\n");
|
||||
uvcg_err(f, "failed to register video device\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -792,6 +781,7 @@ static struct usb_function_instance *uvc_alloc_inst(void)
|
||||
struct uvc_output_terminal_descriptor *od;
|
||||
struct uvc_color_matching_descriptor *md;
|
||||
struct uvc_descriptor_header **ctl_cls;
|
||||
int ret;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
@@ -868,7 +858,12 @@ static struct usb_function_instance *uvc_alloc_inst(void)
|
||||
opts->streaming_interval = 1;
|
||||
opts->streaming_maxpacket = 1024;
|
||||
|
||||
uvcg_attach_configfs(opts);
|
||||
ret = uvcg_attach_configfs(opts);
|
||||
if (ret < 0) {
|
||||
kfree(opts);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
@@ -886,7 +881,7 @@ static void uvc_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
|
||||
INFO(cdev, "%s\n", __func__);
|
||||
uvcg_info(f, "%s\n", __func__);
|
||||
|
||||
device_remove_file(&uvc->vdev.dev, &dev_attr_function_name);
|
||||
video_unregister_device(&uvc->vdev);
|
||||
|
@@ -25,6 +25,9 @@ struct f_uvc_opts {
|
||||
unsigned int streaming_maxpacket;
|
||||
unsigned int streaming_maxburst;
|
||||
|
||||
unsigned int control_interface;
|
||||
unsigned int streaming_interface;
|
||||
|
||||
/*
|
||||
* Control descriptors array pointers for full-/high-speed and
|
||||
* super-speed. They point by default to the uvc_fs_control_cls and
|
||||
|
@@ -24,6 +24,7 @@
|
||||
struct usb_ep;
|
||||
struct usb_request;
|
||||
struct uvc_descriptor_header;
|
||||
struct uvc_device;
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Debugging, printing and logging
|
||||
@@ -51,14 +52,12 @@ extern unsigned int uvc_gadget_trace_param;
|
||||
printk(KERN_DEBUG "uvcvideo: " msg); \
|
||||
} while (0)
|
||||
|
||||
#define uvc_warn_once(dev, warn, msg...) \
|
||||
do { \
|
||||
if (!test_and_set_bit(warn, &dev->warnings)) \
|
||||
printk(KERN_INFO "uvcvideo: " msg); \
|
||||
} while (0)
|
||||
|
||||
#define uvc_printk(level, msg...) \
|
||||
printk(level "uvcvideo: " msg)
|
||||
#define uvcg_dbg(f, fmt, args...) \
|
||||
dev_dbg(&(f)->config->cdev->gadget->dev, "%s: " fmt, (f)->name, ##args)
|
||||
#define uvcg_info(f, fmt, args...) \
|
||||
dev_info(&(f)->config->cdev->gadget->dev, "%s: " fmt, (f)->name, ##args)
|
||||
#define uvcg_err(f, fmt, args...) \
|
||||
dev_err(&(f)->config->cdev->gadget->dev, "%s: " fmt, (f)->name, ##args)
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Driver specific constants
|
||||
@@ -73,6 +72,7 @@ extern unsigned int uvc_gadget_trace_param;
|
||||
*/
|
||||
|
||||
struct uvc_video {
|
||||
struct uvc_device *uvc;
|
||||
struct usb_ep *ep;
|
||||
|
||||
/* Frame parameters */
|
||||
|
檔案差異因為檔案過大而無法顯示
載入差異
@@ -115,8 +115,8 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(uvc_formats)) {
|
||||
printk(KERN_INFO "Unsupported format 0x%08x.\n",
|
||||
fmt->fmt.pix.pixelformat);
|
||||
uvcg_info(&uvc->func, "Unsupported format 0x%08x.\n",
|
||||
fmt->fmt.pix.pixelformat);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@@ -125,6 +125,23 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
|
||||
* Request handling
|
||||
*/
|
||||
|
||||
static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
|
||||
if (ret < 0) {
|
||||
uvcg_err(&video->uvc->func, "Failed to queue request (%d).\n",
|
||||
ret);
|
||||
|
||||
/* Isochronous endpoints can't be halted. */
|
||||
if (usb_endpoint_xfer_bulk(video->ep->desc))
|
||||
usb_ep_set_halt(video->ep);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* I somehow feel that synchronisation won't be easy to achieve here. We have
|
||||
* three events that control USB requests submission:
|
||||
@@ -169,13 +186,14 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
break;
|
||||
|
||||
case -ESHUTDOWN: /* disconnect from host. */
|
||||
printk(KERN_DEBUG "VS request cancelled.\n");
|
||||
uvcg_dbg(&video->uvc->func, "VS request cancelled.\n");
|
||||
uvcg_queue_cancel(queue, 1);
|
||||
goto requeue;
|
||||
|
||||
default:
|
||||
printk(KERN_INFO "VS request completed with status %d.\n",
|
||||
req->status);
|
||||
uvcg_info(&video->uvc->func,
|
||||
"VS request completed with status %d.\n",
|
||||
req->status);
|
||||
uvcg_queue_cancel(queue, 0);
|
||||
goto requeue;
|
||||
}
|
||||
@@ -189,14 +207,13 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
|
||||
video->encode(req, video, buf);
|
||||
|
||||
if ((ret = usb_ep_queue(ep, req, GFP_ATOMIC)) < 0) {
|
||||
printk(KERN_INFO "Failed to queue request (%d).\n", ret);
|
||||
usb_ep_set_halt(ep);
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
ret = uvcg_video_ep_queue(video, req);
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
|
||||
if (ret < 0) {
|
||||
uvcg_queue_cancel(queue, 0);
|
||||
goto requeue;
|
||||
}
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
|
||||
return;
|
||||
|
||||
@@ -316,15 +333,13 @@ int uvcg_video_pump(struct uvc_video *video)
|
||||
video->encode(req, video, buf);
|
||||
|
||||
/* Queue the USB request */
|
||||
ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
|
||||
ret = uvcg_video_ep_queue(video, req);
|
||||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
|
||||
if (ret < 0) {
|
||||
printk(KERN_INFO "Failed to queue request (%d)\n", ret);
|
||||
usb_ep_set_halt(video->ep);
|
||||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
uvcg_queue_cancel(queue, 0);
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&video->req_lock, flags);
|
||||
@@ -342,8 +357,8 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
|
||||
int ret;
|
||||
|
||||
if (video->ep == NULL) {
|
||||
printk(KERN_INFO "Video enable failed, device is "
|
||||
"uninitialized.\n");
|
||||
uvcg_info(&video->uvc->func,
|
||||
"Video enable failed, device is uninitialized.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@@ -375,11 +390,12 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
|
||||
/*
|
||||
* Initialize the UVC video stream.
|
||||
*/
|
||||
int uvcg_video_init(struct uvc_video *video)
|
||||
int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
|
||||
{
|
||||
INIT_LIST_HEAD(&video->req_free);
|
||||
spin_lock_init(&video->req_lock);
|
||||
|
||||
video->uvc = uvc;
|
||||
video->fcc = V4L2_PIX_FMT_YUYV;
|
||||
video->bpp = 16;
|
||||
video->width = 320;
|
||||
|
@@ -18,6 +18,6 @@ int uvcg_video_pump(struct uvc_video *video);
|
||||
|
||||
int uvcg_video_enable(struct uvc_video *video, int enable);
|
||||
|
||||
int uvcg_video_init(struct uvc_video *video);
|
||||
int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc);
|
||||
|
||||
#endif /* __UVC_VIDEO_H__ */
|
||||
|
@@ -353,7 +353,7 @@ static int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req,
|
||||
/* Endpoint enabled ? */
|
||||
if (!ep->epn.enabled || !u_ep->desc || !ep->dev || !ep->d_idx ||
|
||||
!ep->dev->enabled || ep->dev->suspended) {
|
||||
EPDBG(ep,"Enqueing request on wrong or disabled EP\n");
|
||||
EPDBG(ep, "Enqueuing request on wrong or disabled EP\n");
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
|
@@ -2004,7 +2004,6 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
||||
struct usba_udc *udc)
|
||||
{
|
||||
u32 val;
|
||||
const char *name;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct of_device_id *match;
|
||||
struct device_node *pp;
|
||||
@@ -2017,6 +2016,8 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
||||
|
||||
udc->errata = match->data;
|
||||
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9g45-pmc");
|
||||
if (IS_ERR(udc->pmc))
|
||||
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9rl-pmc");
|
||||
if (IS_ERR(udc->pmc))
|
||||
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9x5-pmc");
|
||||
if (udc->errata && IS_ERR(udc->pmc))
|
||||
@@ -2094,11 +2095,6 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
||||
ep->can_dma = of_property_read_bool(pp, "atmel,can-dma");
|
||||
ep->can_isoc = of_property_read_bool(pp, "atmel,can-isoc");
|
||||
|
||||
ret = of_property_read_string(pp, "name", &name);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "of_probe: name error(%d)\n", ret);
|
||||
goto err;
|
||||
}
|
||||
sprintf(ep->name, "ep%d", ep->index);
|
||||
ep->ep.name = ep->name;
|
||||
|
||||
|
@@ -690,6 +690,9 @@ EXPORT_SYMBOL_GPL(usb_gadget_connect);
|
||||
* as a disconnect (when a VBUS session is active). Not all systems
|
||||
* support software pullup controls.
|
||||
*
|
||||
* Following a successful disconnect, invoke the ->disconnect() callback
|
||||
* for the current gadget driver so that UDC drivers don't need to.
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
int usb_gadget_disconnect(struct usb_gadget *gadget)
|
||||
@@ -711,8 +714,10 @@ int usb_gadget_disconnect(struct usb_gadget *gadget)
|
||||
}
|
||||
|
||||
ret = gadget->ops->pullup(gadget, 0);
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
gadget->connected = 0;
|
||||
gadget->udc->driver->disconnect(gadget);
|
||||
}
|
||||
|
||||
out:
|
||||
trace_usb_gadget_disconnect(gadget, ret);
|
||||
@@ -1281,7 +1286,6 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
|
||||
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
|
||||
|
||||
usb_gadget_disconnect(udc->gadget);
|
||||
udc->driver->disconnect(udc->gadget);
|
||||
udc->driver->unbind(udc->gadget);
|
||||
usb_gadget_udc_stop(udc);
|
||||
|
||||
@@ -1471,7 +1475,6 @@ static ssize_t soft_connect_store(struct device *dev,
|
||||
usb_gadget_connect(udc->gadget);
|
||||
} else if (sysfs_streq(buf, "disconnect")) {
|
||||
usb_gadget_disconnect(udc->gadget);
|
||||
udc->driver->disconnect(udc->gadget);
|
||||
usb_gadget_udc_stop(udc);
|
||||
} else {
|
||||
dev_err(dev, "unsupported command '%s'\n", buf);
|
||||
|
@@ -741,7 +741,7 @@ static void fotg210_get_status(struct fotg210_udc *fotg210,
|
||||
fotg210->ep0_req->length = 2;
|
||||
|
||||
spin_unlock(&fotg210->lock);
|
||||
fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_KERNEL);
|
||||
fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_ATOMIC);
|
||||
spin_lock(&fotg210->lock);
|
||||
}
|
||||
|
||||
|
@@ -2234,8 +2234,10 @@ static void fsl_udc_release(struct device *dev)
|
||||
Internal structure setup functions
|
||||
*******************************************************************/
|
||||
/*------------------------------------------------------------------
|
||||
* init resource for globle controller
|
||||
* Return the udc handle on success or NULL on failure
|
||||
* init resource for global controller called by fsl_udc_probe()
|
||||
* On success the udc handle is initialized, on failure it is
|
||||
* unchanged (reset).
|
||||
* Return 0 on success and -1 on allocation failure
|
||||
------------------------------------------------------------------*/
|
||||
static int struct_udc_setup(struct fsl_udc *udc,
|
||||
struct platform_device *pdev)
|
||||
@@ -2247,8 +2249,10 @@ static int struct_udc_setup(struct fsl_udc *udc,
|
||||
udc->phy_mode = pdata->phy_mode;
|
||||
|
||||
udc->eps = kcalloc(udc->max_ep, sizeof(struct fsl_ep), GFP_KERNEL);
|
||||
if (!udc->eps)
|
||||
return -1;
|
||||
if (!udc->eps) {
|
||||
ERR("kmalloc udc endpoint status failed\n");
|
||||
goto eps_alloc_failed;
|
||||
}
|
||||
|
||||
/* initialized QHs, take care of alignment */
|
||||
size = udc->max_ep * sizeof(struct ep_queue_head);
|
||||
@@ -2262,8 +2266,7 @@ static int struct_udc_setup(struct fsl_udc *udc,
|
||||
&udc->ep_qh_dma, GFP_KERNEL);
|
||||
if (!udc->ep_qh) {
|
||||
ERR("malloc QHs for udc failed\n");
|
||||
kfree(udc->eps);
|
||||
return -1;
|
||||
goto ep_queue_alloc_failed;
|
||||
}
|
||||
|
||||
udc->ep_qh_size = size;
|
||||
@@ -2272,8 +2275,17 @@ static int struct_udc_setup(struct fsl_udc *udc,
|
||||
/* FIXME: fsl_alloc_request() ignores ep argument */
|
||||
udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL),
|
||||
struct fsl_req, req);
|
||||
if (!udc->status_req) {
|
||||
ERR("kzalloc for udc status request failed\n");
|
||||
goto udc_status_alloc_failed;
|
||||
}
|
||||
|
||||
/* allocate a small amount of memory to get valid address */
|
||||
udc->status_req->req.buf = kmalloc(8, GFP_KERNEL);
|
||||
if (!udc->status_req->req.buf) {
|
||||
ERR("kzalloc for udc request buffer failed\n");
|
||||
goto udc_req_buf_alloc_failed;
|
||||
}
|
||||
|
||||
udc->resume_state = USB_STATE_NOTATTACHED;
|
||||
udc->usb_state = USB_STATE_POWERED;
|
||||
@@ -2281,6 +2293,18 @@ static int struct_udc_setup(struct fsl_udc *udc,
|
||||
udc->remote_wakeup = 0; /* default to 0 on reset */
|
||||
|
||||
return 0;
|
||||
|
||||
udc_req_buf_alloc_failed:
|
||||
kfree(udc->status_req);
|
||||
udc_status_alloc_failed:
|
||||
kfree(udc->ep_qh);
|
||||
udc->ep_qh_size = 0;
|
||||
ep_queue_alloc_failed:
|
||||
kfree(udc->eps);
|
||||
eps_alloc_failed:
|
||||
udc->phy_mode = 0;
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------
|
||||
|
@@ -185,7 +185,7 @@ static int process_ep_req(struct mv_udc *udc, int index,
|
||||
else
|
||||
bit_pos = 1 << (16 + curr_req->ep->ep_num);
|
||||
|
||||
while ((curr_dqh->curr_dtd_ptr == curr_dtd->td_dma)) {
|
||||
while (curr_dqh->curr_dtd_ptr == curr_dtd->td_dma) {
|
||||
if (curr_dtd->dtd_next == EP_QUEUE_HEAD_NEXT_TERMINATE) {
|
||||
while (readl(&udc->op_regs->epstatus) & bit_pos)
|
||||
udelay(1);
|
||||
|
@@ -1550,9 +1550,6 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on)
|
||||
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
if (!is_on && dev->driver)
|
||||
dev->driver->disconnect(&dev->gadget);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -2437,6 +2437,9 @@ static ssize_t renesas_usb3_b_device_write(struct file *file,
|
||||
else
|
||||
usb3->forced_b_device = false;
|
||||
|
||||
if (usb3->workaround_for_vbus)
|
||||
usb3_disconnect(usb3);
|
||||
|
||||
/* Let this driver call usb3_connect() anyway */
|
||||
usb3_check_id(usb3);
|
||||
|
||||
@@ -2600,6 +2603,13 @@ static const struct renesas_usb3_priv renesas_usb3_priv_gen3 = {
|
||||
.ramsize_per_pipe = SZ_4K,
|
||||
};
|
||||
|
||||
static const struct renesas_usb3_priv renesas_usb3_priv_r8a77990 = {
|
||||
.ramsize_per_ramif = SZ_16K,
|
||||
.num_ramif = 4,
|
||||
.ramsize_per_pipe = SZ_4K,
|
||||
.workaround_for_vbus = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id usb3_of_match[] = {
|
||||
{
|
||||
.compatible = "renesas,r8a7795-usb3-peri",
|
||||
@@ -2618,6 +2628,10 @@ static const struct soc_device_attribute renesas_usb3_quirks_match[] = {
|
||||
.soc_id = "r8a7795", .revision = "ES1.*",
|
||||
.data = &renesas_usb3_priv_r8a7795_es1,
|
||||
},
|
||||
{
|
||||
.soc_id = "r8a77990",
|
||||
.data = &renesas_usb3_priv_r8a77990,
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
|
@@ -1078,7 +1078,7 @@ static int xudc_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
|
||||
unsigned long flags;
|
||||
|
||||
if (!ep->desc) {
|
||||
dev_dbg(udc->dev, "%s:queing request to disabled %s\n",
|
||||
dev_dbg(udc->dev, "%s: queuing request to disabled %s\n",
|
||||
__func__, ep->name);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
@@ -276,7 +276,7 @@ config USB_EHCI_EXYNOS
|
||||
Enable support for the Samsung Exynos SOC's on-chip EHCI controller.
|
||||
|
||||
config USB_EHCI_MV
|
||||
bool "EHCI support for Marvell PXA/MMP USB controller"
|
||||
tristate "EHCI support for Marvell PXA/MMP USB controller"
|
||||
depends on (ARCH_PXA || ARCH_MMP)
|
||||
select USB_EHCI_ROOT_HUB_TT
|
||||
---help---
|
||||
|
@@ -87,6 +87,7 @@ obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o
|
||||
obj-$(CONFIG_USB_FSL_USB2) += fsl-mph-dr-of.o
|
||||
obj-$(CONFIG_USB_EHCI_FSL) += fsl-mph-dr-of.o
|
||||
obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o
|
||||
obj-$(CONFIG_USB_EHCI_MV) += ehci-mv.o
|
||||
obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o
|
||||
obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o
|
||||
obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o
|
||||
|
@@ -730,9 +730,9 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
|
||||
/* normal [4.15.1.2] or error [4.15.1.1] completion */
|
||||
if (likely ((status & (STS_INT|STS_ERR)) != 0)) {
|
||||
if (likely ((status & STS_ERR) == 0))
|
||||
COUNT (ehci->stats.normal);
|
||||
INCR(ehci->stats.normal);
|
||||
else
|
||||
COUNT (ehci->stats.error);
|
||||
INCR(ehci->stats.error);
|
||||
bh = 1;
|
||||
}
|
||||
|
||||
@@ -756,7 +756,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
|
||||
if (cmd & CMD_IAAD)
|
||||
ehci_dbg(ehci, "IAA with IAAD still set?\n");
|
||||
if (ehci->iaa_in_progress)
|
||||
COUNT(ehci->stats.iaa);
|
||||
INCR(ehci->stats.iaa);
|
||||
end_iaa_cycle(ehci);
|
||||
}
|
||||
|
||||
@@ -1286,11 +1286,6 @@ MODULE_LICENSE ("GPL");
|
||||
#define PLATFORM_DRIVER ehci_grlib_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_MV
|
||||
#include "ehci-mv.c"
|
||||
#define PLATFORM_DRIVER ehci_mv_driver
|
||||
#endif
|
||||
|
||||
static int __init ehci_hcd_init(void)
|
||||
{
|
||||
int retval = 0;
|
||||
|
@@ -12,24 +12,33 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/platform_data/mv_usb.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
#include "ehci.h"
|
||||
|
||||
/* registers */
|
||||
#define U2x_CAPREGS_OFFSET 0x100
|
||||
|
||||
#define CAPLENGTH_MASK (0xff)
|
||||
|
||||
struct ehci_hcd_mv {
|
||||
struct usb_hcd *hcd;
|
||||
#define hcd_to_ehci_hcd_mv(h) ((struct ehci_hcd_mv *)hcd_to_ehci(h)->priv)
|
||||
|
||||
struct ehci_hcd_mv {
|
||||
/* Which mode does this ehci running OTG/Host ? */
|
||||
int mode;
|
||||
|
||||
void __iomem *phy_regs;
|
||||
void __iomem *base;
|
||||
void __iomem *cap_regs;
|
||||
void __iomem *op_regs;
|
||||
|
||||
struct usb_phy *otg;
|
||||
|
||||
struct mv_usb_platform_data *pdata;
|
||||
|
||||
struct clk *clk;
|
||||
|
||||
struct phy *phy;
|
||||
|
||||
int (*set_vbus)(unsigned int vbus);
|
||||
};
|
||||
|
||||
static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv)
|
||||
@@ -44,29 +53,20 @@ static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv)
|
||||
|
||||
static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv)
|
||||
{
|
||||
int retval;
|
||||
|
||||
ehci_clock_enable(ehci_mv);
|
||||
if (ehci_mv->pdata->phy_init) {
|
||||
retval = ehci_mv->pdata->phy_init(ehci_mv->phy_regs);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return phy_init(ehci_mv->phy);
|
||||
}
|
||||
|
||||
static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv)
|
||||
{
|
||||
if (ehci_mv->pdata->phy_deinit)
|
||||
ehci_mv->pdata->phy_deinit(ehci_mv->phy_regs);
|
||||
phy_exit(ehci_mv->phy);
|
||||
ehci_clock_disable(ehci_mv);
|
||||
}
|
||||
|
||||
static int mv_ehci_reset(struct usb_hcd *hcd)
|
||||
{
|
||||
struct device *dev = hcd->self.controller;
|
||||
struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev);
|
||||
struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd);
|
||||
int retval;
|
||||
|
||||
if (ehci_mv == NULL) {
|
||||
@@ -83,46 +83,11 @@ static int mv_ehci_reset(struct usb_hcd *hcd)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const struct hc_driver mv_ehci_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "Marvell EHCI",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
static struct hc_driver __read_mostly ehci_platform_hc_driver;
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
.reset = mv_ehci_reset,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
static const struct ehci_driver_overrides platform_overrides __initconst = {
|
||||
.reset = mv_ehci_reset,
|
||||
.extra_priv_size = sizeof(struct ehci_hcd_mv),
|
||||
};
|
||||
|
||||
static int mv_ehci_probe(struct platform_device *pdev)
|
||||
@@ -135,27 +100,29 @@ static int mv_ehci_probe(struct platform_device *pdev)
|
||||
int retval = -ENODEV;
|
||||
u32 offset;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "missing platform_data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
hcd = usb_create_hcd(&mv_ehci_hc_driver, &pdev->dev, "mv ehci");
|
||||
hcd = usb_create_hcd(&ehci_platform_hc_driver, &pdev->dev, "mv ehci");
|
||||
if (!hcd)
|
||||
return -ENOMEM;
|
||||
|
||||
ehci_mv = devm_kzalloc(&pdev->dev, sizeof(*ehci_mv), GFP_KERNEL);
|
||||
if (ehci_mv == NULL) {
|
||||
retval = -ENOMEM;
|
||||
goto err_put_hcd;
|
||||
platform_set_drvdata(pdev, hcd);
|
||||
ehci_mv = hcd_to_ehci_hcd_mv(hcd);
|
||||
|
||||
ehci_mv->mode = MV_USB_MODE_HOST;
|
||||
if (pdata) {
|
||||
ehci_mv->mode = pdata->mode;
|
||||
ehci_mv->set_vbus = pdata->set_vbus;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ehci_mv);
|
||||
ehci_mv->pdata = pdata;
|
||||
ehci_mv->hcd = hcd;
|
||||
ehci_mv->phy = devm_phy_get(&pdev->dev, "usb");
|
||||
if (IS_ERR(ehci_mv->phy)) {
|
||||
retval = PTR_ERR(ehci_mv->phy);
|
||||
if (retval != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Failed to get phy.\n");
|
||||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
ehci_mv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(ehci_mv->clk)) {
|
||||
@@ -164,17 +131,12 @@ static int mv_ehci_probe(struct platform_device *pdev)
|
||||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs");
|
||||
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;
|
||||
}
|
||||
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs");
|
||||
ehci_mv->cap_regs = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(ehci_mv->cap_regs)) {
|
||||
retval = PTR_ERR(ehci_mv->cap_regs);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ehci_mv->base = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(ehci_mv->base)) {
|
||||
retval = PTR_ERR(ehci_mv->base);
|
||||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
@@ -184,6 +146,8 @@ static int mv_ehci_probe(struct platform_device *pdev)
|
||||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
ehci_mv->cap_regs =
|
||||
(void __iomem *) ((unsigned long) ehci_mv->base + U2x_CAPREGS_OFFSET);
|
||||
offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK;
|
||||
ehci_mv->op_regs =
|
||||
(void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset);
|
||||
@@ -202,7 +166,6 @@ static int mv_ehci_probe(struct platform_device *pdev)
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
|
||||
|
||||
ehci_mv->mode = pdata->mode;
|
||||
if (ehci_mv->mode == MV_USB_MODE_OTG) {
|
||||
ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR(ehci_mv->otg)) {
|
||||
@@ -227,8 +190,8 @@ static int mv_ehci_probe(struct platform_device *pdev)
|
||||
/* otg will enable clock before use as host */
|
||||
mv_ehci_disable(ehci_mv);
|
||||
} else {
|
||||
if (pdata->set_vbus)
|
||||
pdata->set_vbus(1);
|
||||
if (ehci_mv->set_vbus)
|
||||
ehci_mv->set_vbus(1);
|
||||
|
||||
retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
|
||||
if (retval) {
|
||||
@@ -239,9 +202,6 @@ static int mv_ehci_probe(struct platform_device *pdev)
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
}
|
||||
|
||||
if (pdata->private_init)
|
||||
pdata->private_init(ehci_mv->op_regs, ehci_mv->phy_regs);
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"successful find EHCI device with regs 0x%p irq %d"
|
||||
" working in %s mode\n", hcd->regs, hcd->irq,
|
||||
@@ -250,8 +210,8 @@ static int mv_ehci_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
err_set_vbus:
|
||||
if (pdata->set_vbus)
|
||||
pdata->set_vbus(0);
|
||||
if (ehci_mv->set_vbus)
|
||||
ehci_mv->set_vbus(0);
|
||||
err_disable_clk:
|
||||
mv_ehci_disable(ehci_mv);
|
||||
err_put_hcd:
|
||||
@@ -262,8 +222,8 @@ err_put_hcd:
|
||||
|
||||
static int mv_ehci_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = ehci_mv->hcd;
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd);
|
||||
|
||||
if (hcd->rh_registered)
|
||||
usb_remove_hcd(hcd);
|
||||
@@ -272,8 +232,8 @@ static int mv_ehci_remove(struct platform_device *pdev)
|
||||
otg_set_host(ehci_mv->otg->otg, NULL);
|
||||
|
||||
if (ehci_mv->mode == MV_USB_MODE_HOST) {
|
||||
if (ehci_mv->pdata->set_vbus)
|
||||
ehci_mv->pdata->set_vbus(0);
|
||||
if (ehci_mv->set_vbus)
|
||||
ehci_mv->set_vbus(0);
|
||||
|
||||
mv_ehci_disable(ehci_mv);
|
||||
}
|
||||
@@ -295,8 +255,7 @@ static const struct platform_device_id ehci_id_table[] = {
|
||||
|
||||
static void mv_ehci_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = ehci_mv->hcd;
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
|
||||
if (!hcd->rh_registered)
|
||||
return;
|
||||
@@ -305,13 +264,41 @@ static void mv_ehci_shutdown(struct platform_device *pdev)
|
||||
hcd->driver->shutdown(hcd);
|
||||
}
|
||||
|
||||
static const struct of_device_id ehci_mv_dt_ids[] = {
|
||||
{ .compatible = "marvell,pxau2o-ehci", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver ehci_mv_driver = {
|
||||
.probe = mv_ehci_probe,
|
||||
.remove = mv_ehci_remove,
|
||||
.shutdown = mv_ehci_shutdown,
|
||||
.driver = {
|
||||
.name = "mv-ehci",
|
||||
.bus = &platform_bus_type,
|
||||
},
|
||||
.name = "mv-ehci",
|
||||
.bus = &platform_bus_type,
|
||||
.of_match_table = ehci_mv_dt_ids,
|
||||
},
|
||||
.id_table = ehci_id_table,
|
||||
};
|
||||
|
||||
static int __init ehci_platform_init(void)
|
||||
{
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides);
|
||||
return platform_driver_register(&ehci_mv_driver);
|
||||
}
|
||||
module_init(ehci_platform_init);
|
||||
|
||||
static void __exit ehci_platform_cleanup(void)
|
||||
{
|
||||
platform_driver_unregister(&ehci_mv_driver);
|
||||
}
|
||||
module_exit(ehci_platform_cleanup);
|
||||
|
||||
MODULE_DESCRIPTION("Marvell EHCI driver");
|
||||
MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>");
|
||||
MODULE_AUTHOR("Neil Zhang <zhangwm@marvell.com>");
|
||||
MODULE_ALIAS("mv-ehci");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@@ -245,12 +245,12 @@ ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status)
|
||||
}
|
||||
|
||||
if (unlikely(urb->unlinked)) {
|
||||
COUNT(ehci->stats.unlink);
|
||||
INCR(ehci->stats.unlink);
|
||||
} else {
|
||||
/* report non-error and short read status as zero */
|
||||
if (status == -EINPROGRESS || status == -EREMOTEIO)
|
||||
status = 0;
|
||||
COUNT(ehci->stats.complete);
|
||||
INCR(ehci->stats.complete);
|
||||
}
|
||||
|
||||
#ifdef EHCI_URB_TRACE
|
||||
|
@@ -347,7 +347,7 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci)
|
||||
*/
|
||||
status = ehci_readl(ehci, &ehci->regs->status);
|
||||
if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
|
||||
COUNT(ehci->stats.lost_iaa);
|
||||
INCR(ehci->stats.lost_iaa);
|
||||
ehci_writel(ehci, STS_IAA, &ehci->regs->status);
|
||||
}
|
||||
|
||||
|
@@ -235,9 +235,9 @@ struct ehci_hcd { /* one per controller */
|
||||
/* irq statistics */
|
||||
#ifdef EHCI_STATS
|
||||
struct ehci_stats stats;
|
||||
# define COUNT(x) ((x)++)
|
||||
# define INCR(x) ((x)++)
|
||||
#else
|
||||
# define COUNT(x)
|
||||
# define INCR(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
/* debug files */
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/irq.h>
|
||||
@@ -1285,7 +1286,7 @@ static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210)
|
||||
*/
|
||||
status = fotg210_readl(fotg210, &fotg210->regs->status);
|
||||
if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
|
||||
COUNT(fotg210->stats.lost_iaa);
|
||||
INCR(fotg210->stats.lost_iaa);
|
||||
fotg210_writel(fotg210, STS_IAA,
|
||||
&fotg210->regs->status);
|
||||
}
|
||||
@@ -2204,12 +2205,12 @@ __acquires(fotg210->lock)
|
||||
}
|
||||
|
||||
if (unlikely(urb->unlinked)) {
|
||||
COUNT(fotg210->stats.unlink);
|
||||
INCR(fotg210->stats.unlink);
|
||||
} else {
|
||||
/* report non-error and short read status as zero */
|
||||
if (status == -EINPROGRESS || status == -EREMOTEIO)
|
||||
status = 0;
|
||||
COUNT(fotg210->stats.complete);
|
||||
INCR(fotg210->stats.complete);
|
||||
}
|
||||
|
||||
#ifdef FOTG210_URB_TRACE
|
||||
@@ -5153,9 +5154,9 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd)
|
||||
/* normal [4.15.1.2] or error [4.15.1.1] completion */
|
||||
if (likely((status & (STS_INT|STS_ERR)) != 0)) {
|
||||
if (likely((status & STS_ERR) == 0))
|
||||
COUNT(fotg210->stats.normal);
|
||||
INCR(fotg210->stats.normal);
|
||||
else
|
||||
COUNT(fotg210->stats.error);
|
||||
INCR(fotg210->stats.error);
|
||||
bh = 1;
|
||||
}
|
||||
|
||||
@@ -5180,7 +5181,7 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd)
|
||||
if (cmd & CMD_IAAD)
|
||||
fotg210_dbg(fotg210, "IAA with IAAD still set?\n");
|
||||
if (fotg210->async_iaa) {
|
||||
COUNT(fotg210->stats.iaa);
|
||||
INCR(fotg210->stats.iaa);
|
||||
end_unlink_async(fotg210);
|
||||
} else
|
||||
fotg210_dbg(fotg210, "IAA with nothing unlinked?\n");
|
||||
@@ -5596,7 +5597,7 @@ static int fotg210_hcd_probe(struct platform_device *pdev)
|
||||
hcd->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(hcd->regs)) {
|
||||
retval = PTR_ERR(hcd->regs);
|
||||
goto failed;
|
||||
goto failed_put_hcd;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = res->start;
|
||||
@@ -5606,22 +5607,43 @@ static int fotg210_hcd_probe(struct platform_device *pdev)
|
||||
|
||||
fotg210->caps = hcd->regs;
|
||||
|
||||
/* It's OK not to supply this clock */
|
||||
fotg210->pclk = clk_get(dev, "PCLK");
|
||||
if (!IS_ERR(fotg210->pclk)) {
|
||||
retval = clk_prepare_enable(fotg210->pclk);
|
||||
if (retval) {
|
||||
dev_err(dev, "failed to enable PCLK\n");
|
||||
goto failed_put_hcd;
|
||||
}
|
||||
} else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) {
|
||||
/*
|
||||
* Percolate deferrals, for anything else,
|
||||
* just live without the clocking.
|
||||
*/
|
||||
retval = PTR_ERR(fotg210->pclk);
|
||||
goto failed_dis_clk;
|
||||
}
|
||||
|
||||
retval = fotg210_setup(hcd);
|
||||
if (retval)
|
||||
goto failed;
|
||||
goto failed_dis_clk;
|
||||
|
||||
fotg210_init(fotg210);
|
||||
|
||||
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (retval) {
|
||||
dev_err(dev, "failed to add hcd with err %d\n", retval);
|
||||
goto failed;
|
||||
goto failed_dis_clk;
|
||||
}
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
platform_set_drvdata(pdev, hcd);
|
||||
|
||||
return retval;
|
||||
|
||||
failed:
|
||||
failed_dis_clk:
|
||||
if (!IS_ERR(fotg210->pclk))
|
||||
clk_disable_unprepare(fotg210->pclk);
|
||||
failed_put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
fail_create_hcd:
|
||||
dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval);
|
||||
@@ -5635,11 +5657,11 @@ fail_create_hcd:
|
||||
*/
|
||||
static int fotg210_hcd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
|
||||
|
||||
if (!hcd)
|
||||
return 0;
|
||||
if (!IS_ERR(fotg210->pclk))
|
||||
clk_disable_unprepare(fotg210->pclk);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
usb_put_hcd(hcd);
|
||||
|
@@ -177,11 +177,14 @@ struct fotg210_hcd { /* one per controller */
|
||||
/* irq statistics */
|
||||
#ifdef FOTG210_STATS
|
||||
struct fotg210_stats stats;
|
||||
# define COUNT(x) ((x)++)
|
||||
# define INCR(x) ((x)++)
|
||||
#else
|
||||
# define COUNT(x)
|
||||
# define INCR(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
/* silicon clock */
|
||||
struct clk *pclk;
|
||||
|
||||
/* debug files */
|
||||
struct dentry *debug_dir;
|
||||
};
|
||||
|
@@ -551,6 +551,8 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
|
||||
pdata->overcurrent_pin[i] =
|
||||
devm_gpiod_get_index_optional(&pdev->dev, "atmel,oc",
|
||||
i, GPIOD_IN);
|
||||
if (!pdata->overcurrent_pin[i])
|
||||
continue;
|
||||
if (IS_ERR(pdata->overcurrent_pin[i])) {
|
||||
err = PTR_ERR(pdata->overcurrent_pin[i]);
|
||||
dev_err(&pdev->dev, "unable to claim gpio \"overcurrent\": %d\n", err);
|
||||
|
@@ -783,15 +783,9 @@ static void quirk_usb_handoff_ohci(struct pci_dev *pdev)
|
||||
/* disable interrupts */
|
||||
writel((u32) ~0, base + OHCI_INTRDISABLE);
|
||||
|
||||
/* Reset the USB bus, if the controller isn't already in RESET */
|
||||
if (control & OHCI_HCFS) {
|
||||
/* Go into RESET, preserving RWC (and possibly IR) */
|
||||
writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL);
|
||||
readl(base + OHCI_CONTROL);
|
||||
|
||||
/* drive bus reset for at least 50 ms (7.1.7.5) */
|
||||
msleep(50);
|
||||
}
|
||||
/* Go into the USB_RESET state, preserving RWC (and possibly IR) */
|
||||
writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL);
|
||||
readl(base + OHCI_CONTROL);
|
||||
|
||||
/* software reset of the controller, preserving HcFmInterval */
|
||||
if (!no_fminterval)
|
||||
|
@@ -900,6 +900,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
|
||||
set_bit(wIndex, &bus_state->resuming_ports);
|
||||
bus_state->resume_done[wIndex] = timeout;
|
||||
mod_timer(&hcd->rh_timer, timeout);
|
||||
usb_hcd_start_port_resume(&hcd->self, wIndex);
|
||||
}
|
||||
/* Has resume been signalled for USB_RESUME_TIME yet? */
|
||||
} else if (time_after_eq(jiffies,
|
||||
@@ -940,6 +941,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
|
||||
clear_bit(wIndex, &bus_state->rexit_ports);
|
||||
}
|
||||
|
||||
usb_hcd_end_port_resume(&hcd->self, wIndex);
|
||||
bus_state->port_c_suspend |= 1 << wIndex;
|
||||
bus_state->suspended_ports &= ~(1 << wIndex);
|
||||
} else {
|
||||
@@ -962,6 +964,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
|
||||
(raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) {
|
||||
bus_state->resume_done[wIndex] = 0;
|
||||
clear_bit(wIndex, &bus_state->resuming_ports);
|
||||
usb_hcd_end_port_resume(&hcd->self, wIndex);
|
||||
}
|
||||
|
||||
|
||||
@@ -1337,6 +1340,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
goto error;
|
||||
|
||||
set_bit(wIndex, &bus_state->resuming_ports);
|
||||
usb_hcd_start_port_resume(&hcd->self, wIndex);
|
||||
xhci_set_link_state(xhci, ports[wIndex],
|
||||
XDEV_RESUME);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
@@ -1345,6 +1349,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
xhci_set_link_state(xhci, ports[wIndex],
|
||||
XDEV_U0);
|
||||
clear_bit(wIndex, &bus_state->resuming_ports);
|
||||
usb_hcd_end_port_resume(&hcd->self, wIndex);
|
||||
}
|
||||
bus_state->port_c_suspend |= 1 << wIndex;
|
||||
|
||||
|
@@ -13,14 +13,20 @@
|
||||
#include "xhci.h"
|
||||
#include "xhci-mtk.h"
|
||||
|
||||
#define SSP_BW_BOUNDARY 130000
|
||||
#define SS_BW_BOUNDARY 51000
|
||||
/* table 5-5. High-speed Isoc Transaction Limits in usb_20 spec */
|
||||
#define HS_BW_BOUNDARY 6144
|
||||
/* usb2 spec section11.18.1: at most 188 FS bytes per microframe */
|
||||
#define FS_PAYLOAD_MAX 188
|
||||
/*
|
||||
* max number of microframes for split transfer,
|
||||
* for fs isoc in : 1 ss + 1 idle + 7 cs
|
||||
*/
|
||||
#define TT_MICROFRAMES_MAX 9
|
||||
|
||||
/* mtk scheduler bitmasks */
|
||||
#define EP_BPKTS(p) ((p) & 0x3f)
|
||||
#define EP_BPKTS(p) ((p) & 0x7f)
|
||||
#define EP_BCSCOUNT(p) (((p) & 0x7) << 8)
|
||||
#define EP_BBM(p) ((p) << 11)
|
||||
#define EP_BOFFSET(p) ((p) & 0x3fff)
|
||||
@@ -51,7 +57,7 @@ static int get_bw_index(struct xhci_hcd *xhci, struct usb_device *udev,
|
||||
|
||||
virt_dev = xhci->devs[udev->slot_id];
|
||||
|
||||
if (udev->speed == USB_SPEED_SUPER) {
|
||||
if (udev->speed >= USB_SPEED_SUPER) {
|
||||
if (usb_endpoint_dir_out(&ep->desc))
|
||||
bw_index = (virt_dev->real_port - 1) * 2;
|
||||
else
|
||||
@@ -64,25 +70,167 @@ static int get_bw_index(struct xhci_hcd *xhci, struct usb_device *udev,
|
||||
return bw_index;
|
||||
}
|
||||
|
||||
static u32 get_esit(struct xhci_ep_ctx *ep_ctx)
|
||||
{
|
||||
u32 esit;
|
||||
|
||||
esit = 1 << CTX_TO_EP_INTERVAL(le32_to_cpu(ep_ctx->ep_info));
|
||||
if (esit > XHCI_MTK_MAX_ESIT)
|
||||
esit = XHCI_MTK_MAX_ESIT;
|
||||
|
||||
return esit;
|
||||
}
|
||||
|
||||
static struct mu3h_sch_tt *find_tt(struct usb_device *udev)
|
||||
{
|
||||
struct usb_tt *utt = udev->tt;
|
||||
struct mu3h_sch_tt *tt, **tt_index, **ptt;
|
||||
unsigned int port;
|
||||
bool allocated_index = false;
|
||||
|
||||
if (!utt)
|
||||
return NULL; /* Not below a TT */
|
||||
|
||||
/*
|
||||
* Find/create our data structure.
|
||||
* For hubs with a single TT, we get it directly.
|
||||
* For hubs with multiple TTs, there's an extra level of pointers.
|
||||
*/
|
||||
tt_index = NULL;
|
||||
if (utt->multi) {
|
||||
tt_index = utt->hcpriv;
|
||||
if (!tt_index) { /* Create the index array */
|
||||
tt_index = kcalloc(utt->hub->maxchild,
|
||||
sizeof(*tt_index), GFP_KERNEL);
|
||||
if (!tt_index)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
utt->hcpriv = tt_index;
|
||||
allocated_index = true;
|
||||
}
|
||||
port = udev->ttport - 1;
|
||||
ptt = &tt_index[port];
|
||||
} else {
|
||||
port = 0;
|
||||
ptt = (struct mu3h_sch_tt **) &utt->hcpriv;
|
||||
}
|
||||
|
||||
tt = *ptt;
|
||||
if (!tt) { /* Create the mu3h_sch_tt */
|
||||
tt = kzalloc(sizeof(*tt), GFP_KERNEL);
|
||||
if (!tt) {
|
||||
if (allocated_index) {
|
||||
utt->hcpriv = NULL;
|
||||
kfree(tt_index);
|
||||
}
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
INIT_LIST_HEAD(&tt->ep_list);
|
||||
tt->usb_tt = utt;
|
||||
tt->tt_port = port;
|
||||
*ptt = tt;
|
||||
}
|
||||
|
||||
return tt;
|
||||
}
|
||||
|
||||
/* Release the TT above udev, if it's not in use */
|
||||
static void drop_tt(struct usb_device *udev)
|
||||
{
|
||||
struct usb_tt *utt = udev->tt;
|
||||
struct mu3h_sch_tt *tt, **tt_index, **ptt;
|
||||
int i, cnt;
|
||||
|
||||
if (!utt || !utt->hcpriv)
|
||||
return; /* Not below a TT, or never allocated */
|
||||
|
||||
cnt = 0;
|
||||
if (utt->multi) {
|
||||
tt_index = utt->hcpriv;
|
||||
ptt = &tt_index[udev->ttport - 1];
|
||||
/* How many entries are left in tt_index? */
|
||||
for (i = 0; i < utt->hub->maxchild; ++i)
|
||||
cnt += !!tt_index[i];
|
||||
} else {
|
||||
tt_index = NULL;
|
||||
ptt = (struct mu3h_sch_tt **)&utt->hcpriv;
|
||||
}
|
||||
|
||||
tt = *ptt;
|
||||
if (!tt || !list_empty(&tt->ep_list))
|
||||
return; /* never allocated , or still in use*/
|
||||
|
||||
*ptt = NULL;
|
||||
kfree(tt);
|
||||
|
||||
if (cnt == 1) {
|
||||
utt->hcpriv = NULL;
|
||||
kfree(tt_index);
|
||||
}
|
||||
}
|
||||
|
||||
static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev,
|
||||
struct usb_host_endpoint *ep, struct xhci_ep_ctx *ep_ctx)
|
||||
{
|
||||
struct mu3h_sch_ep_info *sch_ep;
|
||||
struct mu3h_sch_tt *tt = NULL;
|
||||
u32 len_bw_budget_table;
|
||||
size_t mem_size;
|
||||
|
||||
if (is_fs_or_ls(udev->speed))
|
||||
len_bw_budget_table = TT_MICROFRAMES_MAX;
|
||||
else if ((udev->speed >= USB_SPEED_SUPER)
|
||||
&& usb_endpoint_xfer_isoc(&ep->desc))
|
||||
len_bw_budget_table = get_esit(ep_ctx);
|
||||
else
|
||||
len_bw_budget_table = 1;
|
||||
|
||||
mem_size = sizeof(struct mu3h_sch_ep_info) +
|
||||
len_bw_budget_table * sizeof(u32);
|
||||
sch_ep = kzalloc(mem_size, GFP_KERNEL);
|
||||
if (!sch_ep)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (is_fs_or_ls(udev->speed)) {
|
||||
tt = find_tt(udev);
|
||||
if (IS_ERR(tt)) {
|
||||
kfree(sch_ep);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
sch_ep->sch_tt = tt;
|
||||
sch_ep->ep = ep;
|
||||
|
||||
return sch_ep;
|
||||
}
|
||||
|
||||
static void setup_sch_info(struct usb_device *udev,
|
||||
struct xhci_ep_ctx *ep_ctx, struct mu3h_sch_ep_info *sch_ep)
|
||||
{
|
||||
u32 ep_type;
|
||||
u32 ep_interval;
|
||||
u32 max_packet_size;
|
||||
u32 maxpkt;
|
||||
u32 max_burst;
|
||||
u32 mult;
|
||||
u32 esit_pkts;
|
||||
u32 max_esit_payload;
|
||||
u32 *bwb_table = sch_ep->bw_budget_table;
|
||||
int i;
|
||||
|
||||
ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2));
|
||||
ep_interval = CTX_TO_EP_INTERVAL(le32_to_cpu(ep_ctx->ep_info));
|
||||
max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2));
|
||||
maxpkt = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2));
|
||||
max_burst = CTX_TO_MAX_BURST(le32_to_cpu(ep_ctx->ep_info2));
|
||||
mult = CTX_TO_EP_MULT(le32_to_cpu(ep_ctx->ep_info));
|
||||
max_esit_payload =
|
||||
(CTX_TO_MAX_ESIT_PAYLOAD_HI(
|
||||
le32_to_cpu(ep_ctx->ep_info)) << 16) |
|
||||
CTX_TO_MAX_ESIT_PAYLOAD(le32_to_cpu(ep_ctx->tx_info));
|
||||
|
||||
sch_ep->esit = 1 << ep_interval;
|
||||
sch_ep->esit = get_esit(ep_ctx);
|
||||
sch_ep->ep_type = ep_type;
|
||||
sch_ep->maxpkt = maxpkt;
|
||||
sch_ep->offset = 0;
|
||||
sch_ep->burst_mode = 0;
|
||||
sch_ep->repeat = 0;
|
||||
|
||||
if (udev->speed == USB_SPEED_HIGH) {
|
||||
sch_ep->cs_count = 0;
|
||||
@@ -93,7 +241,6 @@ static void setup_sch_info(struct usb_device *udev,
|
||||
* in a interval
|
||||
*/
|
||||
sch_ep->num_budget_microframes = 1;
|
||||
sch_ep->repeat = 0;
|
||||
|
||||
/*
|
||||
* xHCI spec section6.2.3.4
|
||||
@@ -101,19 +248,33 @@ static void setup_sch_info(struct usb_device *udev,
|
||||
* opportunities per microframe
|
||||
*/
|
||||
sch_ep->pkts = max_burst + 1;
|
||||
sch_ep->bw_cost_per_microframe = max_packet_size * sch_ep->pkts;
|
||||
} else if (udev->speed == USB_SPEED_SUPER) {
|
||||
sch_ep->bw_cost_per_microframe = maxpkt * sch_ep->pkts;
|
||||
bwb_table[0] = sch_ep->bw_cost_per_microframe;
|
||||
} else if (udev->speed >= USB_SPEED_SUPER) {
|
||||
/* usb3_r1 spec section4.4.7 & 4.4.8 */
|
||||
sch_ep->cs_count = 0;
|
||||
esit_pkts = (mult + 1) * (max_burst + 1);
|
||||
sch_ep->burst_mode = 1;
|
||||
/*
|
||||
* some device's (d)wBytesPerInterval is set as 0,
|
||||
* then max_esit_payload is 0, so evaluate esit_pkts from
|
||||
* mult and burst
|
||||
*/
|
||||
esit_pkts = DIV_ROUND_UP(max_esit_payload, maxpkt);
|
||||
if (esit_pkts == 0)
|
||||
esit_pkts = (mult + 1) * (max_burst + 1);
|
||||
|
||||
if (ep_type == INT_IN_EP || ep_type == INT_OUT_EP) {
|
||||
sch_ep->pkts = esit_pkts;
|
||||
sch_ep->num_budget_microframes = 1;
|
||||
sch_ep->repeat = 0;
|
||||
bwb_table[0] = maxpkt * sch_ep->pkts;
|
||||
}
|
||||
|
||||
if (ep_type == ISOC_IN_EP || ep_type == ISOC_OUT_EP) {
|
||||
if (esit_pkts <= sch_ep->esit)
|
||||
u32 remainder;
|
||||
|
||||
if (sch_ep->esit == 1)
|
||||
sch_ep->pkts = esit_pkts;
|
||||
else if (esit_pkts <= sch_ep->esit)
|
||||
sch_ep->pkts = 1;
|
||||
else
|
||||
sch_ep->pkts = roundup_pow_of_two(esit_pkts)
|
||||
@@ -122,43 +283,48 @@ static void setup_sch_info(struct usb_device *udev,
|
||||
sch_ep->num_budget_microframes =
|
||||
DIV_ROUND_UP(esit_pkts, sch_ep->pkts);
|
||||
|
||||
if (sch_ep->num_budget_microframes > 1)
|
||||
sch_ep->repeat = 1;
|
||||
else
|
||||
sch_ep->repeat = 0;
|
||||
sch_ep->repeat = !!(sch_ep->num_budget_microframes > 1);
|
||||
sch_ep->bw_cost_per_microframe = maxpkt * sch_ep->pkts;
|
||||
|
||||
remainder = sch_ep->bw_cost_per_microframe;
|
||||
remainder *= sch_ep->num_budget_microframes;
|
||||
remainder -= (maxpkt * esit_pkts);
|
||||
for (i = 0; i < sch_ep->num_budget_microframes - 1; i++)
|
||||
bwb_table[i] = sch_ep->bw_cost_per_microframe;
|
||||
|
||||
/* last one <= bw_cost_per_microframe */
|
||||
bwb_table[i] = remainder;
|
||||
}
|
||||
sch_ep->bw_cost_per_microframe = max_packet_size * sch_ep->pkts;
|
||||
} else if (is_fs_or_ls(udev->speed)) {
|
||||
sch_ep->pkts = 1; /* at most one packet for each microframe */
|
||||
|
||||
/*
|
||||
* usb_20 spec section11.18.4
|
||||
* assume worst cases
|
||||
* num_budget_microframes and cs_count will be updated when
|
||||
* check TT for INT_OUT_EP, ISOC/INT_IN_EP type
|
||||
*/
|
||||
sch_ep->repeat = 0;
|
||||
sch_ep->pkts = 1; /* at most one packet for each microframe */
|
||||
if (ep_type == INT_IN_EP || ep_type == INT_OUT_EP) {
|
||||
sch_ep->cs_count = 3; /* at most need 3 CS*/
|
||||
/* one for SS and one for budgeted transaction */
|
||||
sch_ep->num_budget_microframes = sch_ep->cs_count + 2;
|
||||
sch_ep->bw_cost_per_microframe = max_packet_size;
|
||||
}
|
||||
if (ep_type == ISOC_OUT_EP) {
|
||||
sch_ep->cs_count = DIV_ROUND_UP(maxpkt, FS_PAYLOAD_MAX);
|
||||
sch_ep->num_budget_microframes = sch_ep->cs_count;
|
||||
sch_ep->bw_cost_per_microframe =
|
||||
(maxpkt < FS_PAYLOAD_MAX) ? maxpkt : FS_PAYLOAD_MAX;
|
||||
|
||||
/* init budget table */
|
||||
if (ep_type == ISOC_OUT_EP) {
|
||||
for (i = 0; i < sch_ep->num_budget_microframes; i++)
|
||||
bwb_table[i] = sch_ep->bw_cost_per_microframe;
|
||||
} else if (ep_type == INT_OUT_EP) {
|
||||
/* only first one consumes bandwidth, others as zero */
|
||||
bwb_table[0] = sch_ep->bw_cost_per_microframe;
|
||||
} else { /* INT_IN_EP or ISOC_IN_EP */
|
||||
bwb_table[0] = 0; /* start split */
|
||||
bwb_table[1] = 0; /* idle */
|
||||
/*
|
||||
* the best case FS budget assumes that 188 FS bytes
|
||||
* occur in each microframe
|
||||
* due to cs_count will be updated according to cs
|
||||
* position, assign all remainder budget array
|
||||
* elements as @bw_cost_per_microframe, but only first
|
||||
* @num_budget_microframes elements will be used later
|
||||
*/
|
||||
sch_ep->num_budget_microframes = DIV_ROUND_UP(
|
||||
max_packet_size, FS_PAYLOAD_MAX);
|
||||
sch_ep->bw_cost_per_microframe = FS_PAYLOAD_MAX;
|
||||
sch_ep->cs_count = sch_ep->num_budget_microframes;
|
||||
}
|
||||
if (ep_type == ISOC_IN_EP) {
|
||||
/* at most need additional two CS. */
|
||||
sch_ep->cs_count = DIV_ROUND_UP(
|
||||
max_packet_size, FS_PAYLOAD_MAX) + 2;
|
||||
sch_ep->num_budget_microframes = sch_ep->cs_count + 2;
|
||||
sch_ep->bw_cost_per_microframe = FS_PAYLOAD_MAX;
|
||||
for (i = 2; i < TT_MICROFRAMES_MAX; i++)
|
||||
bwb_table[i] = sch_ep->bw_cost_per_microframe;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,6 +335,7 @@ static u32 get_max_bw(struct mu3h_sch_bw_info *sch_bw,
|
||||
{
|
||||
u32 num_esit;
|
||||
u32 max_bw = 0;
|
||||
u32 bw;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
@@ -177,15 +344,17 @@ static u32 get_max_bw(struct mu3h_sch_bw_info *sch_bw,
|
||||
u32 base = offset + i * sch_ep->esit;
|
||||
|
||||
for (j = 0; j < sch_ep->num_budget_microframes; j++) {
|
||||
if (sch_bw->bus_bw[base + j] > max_bw)
|
||||
max_bw = sch_bw->bus_bw[base + j];
|
||||
bw = sch_bw->bus_bw[base + j] +
|
||||
sch_ep->bw_budget_table[j];
|
||||
if (bw > max_bw)
|
||||
max_bw = bw;
|
||||
}
|
||||
}
|
||||
return max_bw;
|
||||
}
|
||||
|
||||
static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw,
|
||||
struct mu3h_sch_ep_info *sch_ep, int bw_cost)
|
||||
struct mu3h_sch_ep_info *sch_ep, bool used)
|
||||
{
|
||||
u32 num_esit;
|
||||
u32 base;
|
||||
@@ -195,27 +364,122 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw,
|
||||
num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
|
||||
for (i = 0; i < num_esit; i++) {
|
||||
base = sch_ep->offset + i * sch_ep->esit;
|
||||
for (j = 0; j < sch_ep->num_budget_microframes; j++)
|
||||
sch_bw->bus_bw[base + j] += bw_cost;
|
||||
for (j = 0; j < sch_ep->num_budget_microframes; j++) {
|
||||
if (used)
|
||||
sch_bw->bus_bw[base + j] +=
|
||||
sch_ep->bw_budget_table[j];
|
||||
else
|
||||
sch_bw->bus_bw[base + j] -=
|
||||
sch_ep->bw_budget_table[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int check_sch_tt(struct usb_device *udev,
|
||||
struct mu3h_sch_ep_info *sch_ep, u32 offset)
|
||||
{
|
||||
struct mu3h_sch_tt *tt = sch_ep->sch_tt;
|
||||
u32 extra_cs_count;
|
||||
u32 fs_budget_start;
|
||||
u32 start_ss, last_ss;
|
||||
u32 start_cs, last_cs;
|
||||
int i;
|
||||
|
||||
start_ss = offset % 8;
|
||||
fs_budget_start = (start_ss + 1) % 8;
|
||||
|
||||
if (sch_ep->ep_type == ISOC_OUT_EP) {
|
||||
last_ss = start_ss + sch_ep->cs_count - 1;
|
||||
|
||||
/*
|
||||
* usb_20 spec section11.18:
|
||||
* must never schedule Start-Split in Y6
|
||||
*/
|
||||
if (!(start_ss == 7 || last_ss < 6))
|
||||
return -ERANGE;
|
||||
|
||||
for (i = 0; i < sch_ep->cs_count; i++)
|
||||
if (test_bit(offset + i, tt->split_bit_map))
|
||||
return -ERANGE;
|
||||
|
||||
} else {
|
||||
u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX);
|
||||
|
||||
/*
|
||||
* usb_20 spec section11.18:
|
||||
* must never schedule Start-Split in Y6
|
||||
*/
|
||||
if (start_ss == 6)
|
||||
return -ERANGE;
|
||||
|
||||
/* one uframe for ss + one uframe for idle */
|
||||
start_cs = (start_ss + 2) % 8;
|
||||
last_cs = start_cs + cs_count - 1;
|
||||
|
||||
if (last_cs > 7)
|
||||
return -ERANGE;
|
||||
|
||||
if (sch_ep->ep_type == ISOC_IN_EP)
|
||||
extra_cs_count = (last_cs == 7) ? 1 : 2;
|
||||
else /* ep_type : INTR IN / INTR OUT */
|
||||
extra_cs_count = (fs_budget_start == 6) ? 1 : 2;
|
||||
|
||||
cs_count += extra_cs_count;
|
||||
if (cs_count > 7)
|
||||
cs_count = 7; /* HW limit */
|
||||
|
||||
for (i = 0; i < cs_count + 2; i++) {
|
||||
if (test_bit(offset + i, tt->split_bit_map))
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
sch_ep->cs_count = cs_count;
|
||||
/* one for ss, the other for idle */
|
||||
sch_ep->num_budget_microframes = cs_count + 2;
|
||||
|
||||
/*
|
||||
* if interval=1, maxp >752, num_budge_micoframe is larger
|
||||
* than sch_ep->esit, will overstep boundary
|
||||
*/
|
||||
if (sch_ep->num_budget_microframes > sch_ep->esit)
|
||||
sch_ep->num_budget_microframes = sch_ep->esit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_sch_tt(struct usb_device *udev,
|
||||
struct mu3h_sch_ep_info *sch_ep)
|
||||
{
|
||||
struct mu3h_sch_tt *tt = sch_ep->sch_tt;
|
||||
u32 base, num_esit;
|
||||
int i, j;
|
||||
|
||||
num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
|
||||
for (i = 0; i < num_esit; i++) {
|
||||
base = sch_ep->offset + i * sch_ep->esit;
|
||||
for (j = 0; j < sch_ep->num_budget_microframes; j++)
|
||||
set_bit(base + j, tt->split_bit_map);
|
||||
}
|
||||
|
||||
list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list);
|
||||
}
|
||||
|
||||
static int check_sch_bw(struct usb_device *udev,
|
||||
struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep)
|
||||
{
|
||||
u32 offset;
|
||||
u32 esit;
|
||||
u32 num_budget_microframes;
|
||||
u32 min_bw;
|
||||
u32 min_index;
|
||||
u32 worst_bw;
|
||||
u32 bw_boundary;
|
||||
|
||||
if (sch_ep->esit > XHCI_MTK_MAX_ESIT)
|
||||
sch_ep->esit = XHCI_MTK_MAX_ESIT;
|
||||
u32 min_num_budget;
|
||||
u32 min_cs_count;
|
||||
bool tt_offset_ok = false;
|
||||
int ret;
|
||||
|
||||
esit = sch_ep->esit;
|
||||
num_budget_microframes = sch_ep->num_budget_microframes;
|
||||
|
||||
/*
|
||||
* Search through all possible schedule microframes.
|
||||
@@ -223,36 +487,56 @@ static int check_sch_bw(struct usb_device *udev,
|
||||
*/
|
||||
min_bw = ~0;
|
||||
min_index = 0;
|
||||
min_cs_count = sch_ep->cs_count;
|
||||
min_num_budget = sch_ep->num_budget_microframes;
|
||||
for (offset = 0; offset < esit; offset++) {
|
||||
if ((offset + num_budget_microframes) > sch_ep->esit)
|
||||
break;
|
||||
if (is_fs_or_ls(udev->speed)) {
|
||||
ret = check_sch_tt(udev, sch_ep, offset);
|
||||
if (ret)
|
||||
continue;
|
||||
else
|
||||
tt_offset_ok = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* usb_20 spec section11.18:
|
||||
* must never schedule Start-Split in Y6
|
||||
*/
|
||||
if (is_fs_or_ls(udev->speed) && (offset % 8 == 6))
|
||||
continue;
|
||||
if ((offset + sch_ep->num_budget_microframes) > sch_ep->esit)
|
||||
break;
|
||||
|
||||
worst_bw = get_max_bw(sch_bw, sch_ep, offset);
|
||||
if (min_bw > worst_bw) {
|
||||
min_bw = worst_bw;
|
||||
min_index = offset;
|
||||
min_cs_count = sch_ep->cs_count;
|
||||
min_num_budget = sch_ep->num_budget_microframes;
|
||||
}
|
||||
if (min_bw == 0)
|
||||
break;
|
||||
}
|
||||
sch_ep->offset = min_index;
|
||||
|
||||
bw_boundary = (udev->speed == USB_SPEED_SUPER)
|
||||
? SS_BW_BOUNDARY : HS_BW_BOUNDARY;
|
||||
if (udev->speed == USB_SPEED_SUPER_PLUS)
|
||||
bw_boundary = SSP_BW_BOUNDARY;
|
||||
else if (udev->speed == USB_SPEED_SUPER)
|
||||
bw_boundary = SS_BW_BOUNDARY;
|
||||
else
|
||||
bw_boundary = HS_BW_BOUNDARY;
|
||||
|
||||
/* check bandwidth */
|
||||
if (min_bw + sch_ep->bw_cost_per_microframe > bw_boundary)
|
||||
if (min_bw > bw_boundary)
|
||||
return -ERANGE;
|
||||
|
||||
sch_ep->offset = min_index;
|
||||
sch_ep->cs_count = min_cs_count;
|
||||
sch_ep->num_budget_microframes = min_num_budget;
|
||||
|
||||
if (is_fs_or_ls(udev->speed)) {
|
||||
/* all offset for tt is not ok*/
|
||||
if (!tt_offset_ok)
|
||||
return -ERANGE;
|
||||
|
||||
update_sch_tt(udev, sch_ep);
|
||||
}
|
||||
|
||||
/* update bus bandwidth info */
|
||||
update_bus_bw(sch_bw, sch_ep, sch_ep->bw_cost_per_microframe);
|
||||
update_bus_bw(sch_bw, sch_ep, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -347,8 +631,8 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
bw_index = get_bw_index(xhci, udev, ep);
|
||||
sch_bw = &sch_array[bw_index];
|
||||
|
||||
sch_ep = kzalloc(sizeof(struct mu3h_sch_ep_info), GFP_NOIO);
|
||||
if (!sch_ep)
|
||||
sch_ep = create_sch_ep(udev, ep, ep_ctx);
|
||||
if (IS_ERR_OR_NULL(sch_ep))
|
||||
return -ENOMEM;
|
||||
|
||||
setup_sch_info(udev, ep_ctx, sch_ep);
|
||||
@@ -356,12 +640,14 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
ret = check_sch_bw(udev, sch_bw, sch_ep);
|
||||
if (ret) {
|
||||
xhci_err(xhci, "Not enough bandwidth!\n");
|
||||
if (is_fs_or_ls(udev->speed))
|
||||
drop_tt(udev);
|
||||
|
||||
kfree(sch_ep);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
list_add_tail(&sch_ep->endpoint, &sch_bw->bw_ep_list);
|
||||
sch_ep->ep = ep;
|
||||
|
||||
ep_ctx->reserved[0] |= cpu_to_le32(EP_BPKTS(sch_ep->pkts)
|
||||
| EP_BCSCOUNT(sch_ep->cs_count) | EP_BBM(sch_ep->burst_mode));
|
||||
@@ -406,9 +692,12 @@ void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
|
||||
list_for_each_entry(sch_ep, &sch_bw->bw_ep_list, endpoint) {
|
||||
if (sch_ep->ep == ep) {
|
||||
update_bus_bw(sch_bw, sch_ep,
|
||||
-sch_ep->bw_cost_per_microframe);
|
||||
update_bus_bw(sch_bw, sch_ep, 0);
|
||||
list_del(&sch_ep->endpoint);
|
||||
if (is_fs_or_ls(udev->speed)) {
|
||||
list_del(&sch_ep->tt_endpoint);
|
||||
drop_tt(udev);
|
||||
}
|
||||
kfree(sch_ep);
|
||||
break;
|
||||
}
|
||||
|
@@ -19,6 +19,19 @@
|
||||
*/
|
||||
#define XHCI_MTK_MAX_ESIT 64
|
||||
|
||||
/**
|
||||
* @split_bit_map: used to avoid split microframes overlay
|
||||
* @ep_list: Endpoints using this TT
|
||||
* @usb_tt: usb TT related
|
||||
* @tt_port: TT port number
|
||||
*/
|
||||
struct mu3h_sch_tt {
|
||||
DECLARE_BITMAP(split_bit_map, XHCI_MTK_MAX_ESIT);
|
||||
struct list_head ep_list;
|
||||
struct usb_tt *usb_tt;
|
||||
int tt_port;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mu3h_sch_bw_info: schedule information for bandwidth domain
|
||||
*
|
||||
@@ -41,6 +54,10 @@ struct mu3h_sch_bw_info {
|
||||
* (@repeat==1) scheduled within the interval
|
||||
* @bw_cost_per_microframe: bandwidth cost per microframe
|
||||
* @endpoint: linked into bandwidth domain which it belongs to
|
||||
* @tt_endpoint: linked into mu3h_sch_tt's list which it belongs to
|
||||
* @sch_tt: mu3h_sch_tt linked into
|
||||
* @ep_type: endpoint type
|
||||
* @maxpkt: max packet size of endpoint
|
||||
* @ep: address of usb_host_endpoint struct
|
||||
* @offset: which uframe of the interval that transfer should be
|
||||
* scheduled first time within the interval
|
||||
@@ -57,12 +74,17 @@ struct mu3h_sch_bw_info {
|
||||
* times; 1: distribute the (bMaxBurst+1)*(Mult+1) packets
|
||||
* according to @pkts and @repeat. normal mode is used by
|
||||
* default
|
||||
* @bw_budget_table: table to record bandwidth budget per microframe
|
||||
*/
|
||||
struct mu3h_sch_ep_info {
|
||||
u32 esit;
|
||||
u32 num_budget_microframes;
|
||||
u32 bw_cost_per_microframe;
|
||||
struct list_head endpoint;
|
||||
struct list_head tt_endpoint;
|
||||
struct mu3h_sch_tt *sch_tt;
|
||||
u32 ep_type;
|
||||
u32 maxpkt;
|
||||
void *ep;
|
||||
/*
|
||||
* mtk xHCI scheduling information put into reserved DWs
|
||||
@@ -73,6 +95,7 @@ struct mu3h_sch_ep_info {
|
||||
u32 pkts;
|
||||
u32 cs_count;
|
||||
u32 burst_mode;
|
||||
u32 bw_budget_table[0];
|
||||
};
|
||||
|
||||
#define MU3C_U3_PORT_MAX 4
|
||||
|
@@ -41,6 +41,13 @@
|
||||
#define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8
|
||||
#define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8
|
||||
#define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0
|
||||
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_XHCI 0x15b5
|
||||
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_XHCI 0x15b6
|
||||
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_XHCI 0x15db
|
||||
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_XHCI 0x15d4
|
||||
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI 0x15e9
|
||||
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_XHCI 0x15ec
|
||||
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI 0x15f0
|
||||
|
||||
#define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9
|
||||
#define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba
|
||||
@@ -193,6 +200,16 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI))
|
||||
xhci->quirks |= XHCI_MISSING_CAS;
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
(pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_XHCI ||
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI))
|
||||
xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
|
||||
pdev->device == PCI_DEVICE_ID_EJ168) {
|
||||
xhci->quirks |= XHCI_RESET_ON_RESUME;
|
||||
@@ -336,6 +353,9 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
/* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */
|
||||
pm_runtime_put_noidle(&dev->dev);
|
||||
|
||||
if (xhci->quirks & XHCI_DEFAULT_PM_RUNTIME_ALLOW)
|
||||
pm_runtime_allow(&dev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
put_usb3_hcd:
|
||||
@@ -353,6 +373,10 @@ static void xhci_pci_remove(struct pci_dev *dev)
|
||||
|
||||
xhci = hcd_to_xhci(pci_get_drvdata(dev));
|
||||
xhci->xhc_state |= XHCI_STATE_REMOVING;
|
||||
|
||||
if (xhci->quirks & XHCI_DEFAULT_PM_RUNTIME_ALLOW)
|
||||
pm_runtime_forbid(&dev->dev);
|
||||
|
||||
if (xhci->shared_hcd) {
|
||||
usb_remove_hcd(xhci->shared_hcd);
|
||||
usb_put_hcd(xhci->shared_hcd);
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/usb/of.h>
|
||||
|
||||
#include "xhci.h"
|
||||
#include "xhci-plat.h"
|
||||
@@ -305,6 +306,8 @@ static int xhci_plat_probe(struct platform_device *pdev)
|
||||
hcd->skip_phy_initialization = 1;
|
||||
}
|
||||
|
||||
hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
|
||||
xhci->shared_hcd->tpl_support = hcd->tpl_support;
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (ret)
|
||||
goto disable_usb_phy;
|
||||
|
@@ -1155,6 +1155,10 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
|
||||
/* Clear our internal halted state */
|
||||
xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED;
|
||||
}
|
||||
|
||||
/* if this was a soft reset, then restart */
|
||||
if ((le32_to_cpu(trb->generic.field[3])) & TRB_TSP)
|
||||
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
|
||||
}
|
||||
|
||||
static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id,
|
||||
@@ -1602,6 +1606,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
|
||||
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
mod_timer(&hcd->rh_timer,
|
||||
bus_state->resume_done[hcd_portnum]);
|
||||
usb_hcd_start_port_resume(&hcd->self, hcd_portnum);
|
||||
bogus_port_status = true;
|
||||
}
|
||||
}
|
||||
@@ -2132,10 +2137,16 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||
union xhci_trb *ep_trb, struct xhci_transfer_event *event,
|
||||
struct xhci_virt_ep *ep, int *status)
|
||||
{
|
||||
struct xhci_slot_ctx *slot_ctx;
|
||||
struct xhci_ring *ep_ring;
|
||||
u32 trb_comp_code;
|
||||
u32 remaining, requested, ep_trb_len;
|
||||
unsigned int slot_id;
|
||||
int ep_index;
|
||||
|
||||
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
|
||||
slot_ctx = xhci_get_slot_ctx(xhci, xhci->devs[slot_id]->out_ctx);
|
||||
ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
|
||||
ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
|
||||
trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
|
||||
remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
|
||||
@@ -2144,6 +2155,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||
|
||||
switch (trb_comp_code) {
|
||||
case COMP_SUCCESS:
|
||||
ep_ring->err_count = 0;
|
||||
/* handle success with untransferred data as short packet */
|
||||
if (ep_trb != td->last_trb || remaining) {
|
||||
xhci_warn(xhci, "WARN Successful completion on short TX\n");
|
||||
@@ -2167,6 +2179,14 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||
ep_trb_len = 0;
|
||||
remaining = 0;
|
||||
break;
|
||||
case COMP_USB_TRANSACTION_ERROR:
|
||||
if ((ep_ring->err_count++ > MAX_SOFT_RETRY) ||
|
||||
le32_to_cpu(slot_ctx->tt_info) & TT_SLOT)
|
||||
break;
|
||||
*status = 0;
|
||||
xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
|
||||
ep_ring->stream_id, td, EP_SOFT_RESET);
|
||||
return 0;
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include <linux/phy/tegra/xusb.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
@@ -107,35 +108,35 @@
|
||||
#define IMEM_BLOCK_SIZE 256
|
||||
|
||||
struct tegra_xusb_fw_header {
|
||||
u32 boot_loadaddr_in_imem;
|
||||
u32 boot_codedfi_offset;
|
||||
u32 boot_codetag;
|
||||
u32 boot_codesize;
|
||||
u32 phys_memaddr;
|
||||
u16 reqphys_memsize;
|
||||
u16 alloc_phys_memsize;
|
||||
u32 rodata_img_offset;
|
||||
u32 rodata_section_start;
|
||||
u32 rodata_section_end;
|
||||
u32 main_fnaddr;
|
||||
u32 fwimg_cksum;
|
||||
u32 fwimg_created_time;
|
||||
u32 imem_resident_start;
|
||||
u32 imem_resident_end;
|
||||
u32 idirect_start;
|
||||
u32 idirect_end;
|
||||
u32 l2_imem_start;
|
||||
u32 l2_imem_end;
|
||||
u32 version_id;
|
||||
__le32 boot_loadaddr_in_imem;
|
||||
__le32 boot_codedfi_offset;
|
||||
__le32 boot_codetag;
|
||||
__le32 boot_codesize;
|
||||
__le32 phys_memaddr;
|
||||
__le16 reqphys_memsize;
|
||||
__le16 alloc_phys_memsize;
|
||||
__le32 rodata_img_offset;
|
||||
__le32 rodata_section_start;
|
||||
__le32 rodata_section_end;
|
||||
__le32 main_fnaddr;
|
||||
__le32 fwimg_cksum;
|
||||
__le32 fwimg_created_time;
|
||||
__le32 imem_resident_start;
|
||||
__le32 imem_resident_end;
|
||||
__le32 idirect_start;
|
||||
__le32 idirect_end;
|
||||
__le32 l2_imem_start;
|
||||
__le32 l2_imem_end;
|
||||
__le32 version_id;
|
||||
u8 init_ddirect;
|
||||
u8 reserved[3];
|
||||
u32 phys_addr_log_buffer;
|
||||
u32 total_log_entries;
|
||||
u32 dequeue_ptr;
|
||||
u32 dummy_var[2];
|
||||
u32 fwimg_len;
|
||||
__le32 phys_addr_log_buffer;
|
||||
__le32 total_log_entries;
|
||||
__le32 dequeue_ptr;
|
||||
__le32 dummy_var[2];
|
||||
__le32 fwimg_len;
|
||||
u8 magic[8];
|
||||
u32 ss_low_power_entry_timeout;
|
||||
__le32 ss_low_power_entry_timeout;
|
||||
u8 num_hsic_port;
|
||||
u8 padding[139]; /* Pad to 256 bytes */
|
||||
};
|
||||
@@ -194,6 +195,11 @@ struct tegra_xusb {
|
||||
struct reset_control *host_rst;
|
||||
struct reset_control *ss_rst;
|
||||
|
||||
struct device *genpd_dev_host;
|
||||
struct device *genpd_dev_ss;
|
||||
struct device_link *genpd_dl_host;
|
||||
struct device_link *genpd_dl_ss;
|
||||
|
||||
struct phy **phys;
|
||||
unsigned int num_phys;
|
||||
|
||||
@@ -928,6 +934,57 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_xusb_powerdomain_remove(struct device *dev,
|
||||
struct tegra_xusb *tegra)
|
||||
{
|
||||
if (tegra->genpd_dl_ss)
|
||||
device_link_del(tegra->genpd_dl_ss);
|
||||
if (tegra->genpd_dl_host)
|
||||
device_link_del(tegra->genpd_dl_host);
|
||||
if (tegra->genpd_dev_ss)
|
||||
dev_pm_domain_detach(tegra->genpd_dev_ss, true);
|
||||
if (tegra->genpd_dev_host)
|
||||
dev_pm_domain_detach(tegra->genpd_dev_host, true);
|
||||
}
|
||||
|
||||
static int tegra_xusb_powerdomain_init(struct device *dev,
|
||||
struct tegra_xusb *tegra)
|
||||
{
|
||||
int err;
|
||||
|
||||
tegra->genpd_dev_host = dev_pm_domain_attach_by_name(dev, "xusb_host");
|
||||
if (IS_ERR(tegra->genpd_dev_host)) {
|
||||
err = PTR_ERR(tegra->genpd_dev_host);
|
||||
dev_err(dev, "failed to get host pm-domain: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
tegra->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "xusb_ss");
|
||||
if (IS_ERR(tegra->genpd_dev_ss)) {
|
||||
err = PTR_ERR(tegra->genpd_dev_ss);
|
||||
dev_err(dev, "failed to get superspeed pm-domain: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
tegra->genpd_dl_host = device_link_add(dev, tegra->genpd_dev_host,
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_STATELESS);
|
||||
if (!tegra->genpd_dl_host) {
|
||||
dev_err(dev, "adding host device link failed!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
tegra->genpd_dl_ss = device_link_add(dev, tegra->genpd_dev_ss,
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_STATELESS);
|
||||
if (!tegra->genpd_dl_ss) {
|
||||
dev_err(dev, "adding superspeed device link failed!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_xusb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_xusb_mbox_msg msg;
|
||||
@@ -1038,7 +1095,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
|
||||
goto put_padctl;
|
||||
}
|
||||
|
||||
if (!pdev->dev.pm_domain) {
|
||||
if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
|
||||
tegra->host_rst = devm_reset_control_get(&pdev->dev,
|
||||
"xusb_host");
|
||||
if (IS_ERR(tegra->host_rst)) {
|
||||
@@ -1069,17 +1126,22 @@ static int tegra_xusb_probe(struct platform_device *pdev)
|
||||
tegra->host_clk,
|
||||
tegra->host_rst);
|
||||
if (err) {
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
|
||||
dev_err(&pdev->dev,
|
||||
"failed to enable XUSBC domain: %d\n", err);
|
||||
goto disable_xusba;
|
||||
goto put_padctl;
|
||||
}
|
||||
} else {
|
||||
err = tegra_xusb_powerdomain_init(&pdev->dev, tegra);
|
||||
if (err)
|
||||
goto put_powerdomains;
|
||||
}
|
||||
|
||||
tegra->supplies = devm_kcalloc(&pdev->dev, tegra->soc->num_supplies,
|
||||
sizeof(*tegra->supplies), GFP_KERNEL);
|
||||
if (!tegra->supplies) {
|
||||
err = -ENOMEM;
|
||||
goto disable_xusbc;
|
||||
goto put_powerdomains;
|
||||
}
|
||||
|
||||
for (i = 0; i < tegra->soc->num_supplies; i++)
|
||||
@@ -1089,7 +1151,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
|
||||
tegra->supplies);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to get regulators: %d\n", err);
|
||||
goto disable_xusbc;
|
||||
goto put_powerdomains;
|
||||
}
|
||||
|
||||
for (i = 0; i < tegra->soc->num_types; i++)
|
||||
@@ -1099,7 +1161,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
|
||||
sizeof(*tegra->phys), GFP_KERNEL);
|
||||
if (!tegra->phys) {
|
||||
err = -ENOMEM;
|
||||
goto disable_xusbc;
|
||||
goto put_powerdomains;
|
||||
}
|
||||
|
||||
for (i = 0, k = 0; i < tegra->soc->num_types; i++) {
|
||||
@@ -1115,7 +1177,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
|
||||
"failed to get PHY %s: %ld\n", prop,
|
||||
PTR_ERR(phy));
|
||||
err = PTR_ERR(phy);
|
||||
goto disable_xusbc;
|
||||
goto put_powerdomains;
|
||||
}
|
||||
|
||||
tegra->phys[k++] = phy;
|
||||
@@ -1126,7 +1188,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
|
||||
dev_name(&pdev->dev));
|
||||
if (!tegra->hcd) {
|
||||
err = -ENOMEM;
|
||||
goto disable_xusbc;
|
||||
goto put_powerdomains;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1222,12 +1284,13 @@ put_rpm:
|
||||
disable_rpm:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
usb_put_hcd(tegra->hcd);
|
||||
disable_xusbc:
|
||||
if (!pdev->dev.pm_domain)
|
||||
put_powerdomains:
|
||||
if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
|
||||
disable_xusba:
|
||||
if (!pdev->dev.pm_domain)
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
|
||||
} else {
|
||||
tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
|
||||
}
|
||||
put_padctl:
|
||||
tegra_xusb_padctl_put(tegra->padctl);
|
||||
return err;
|
||||
@@ -1249,6 +1312,13 @@ static int tegra_xusb_remove(struct platform_device *pdev)
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
|
||||
} else {
|
||||
tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
|
||||
}
|
||||
|
||||
tegra_xusb_padctl_put(tegra->padctl);
|
||||
|
||||
return 0;
|
||||
|
@@ -1496,6 +1496,7 @@ static inline const char *xhci_trb_type_string(u8 type)
|
||||
/* How much data is left before the 64KB boundary? */
|
||||
#define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_BUFF_SIZE - \
|
||||
(addr & (TRB_MAX_BUFF_SIZE - 1)))
|
||||
#define MAX_SOFT_RETRY 3
|
||||
|
||||
struct xhci_segment {
|
||||
union xhci_trb *trbs;
|
||||
@@ -1583,6 +1584,7 @@ struct xhci_ring {
|
||||
* if we own the TRB (if we are the consumer). See section 4.9.1.
|
||||
*/
|
||||
u32 cycle_state;
|
||||
unsigned int err_count;
|
||||
unsigned int stream_id;
|
||||
unsigned int num_segs;
|
||||
unsigned int num_trbs_free;
|
||||
@@ -1846,6 +1848,7 @@ struct xhci_hcd {
|
||||
#define XHCI_SUSPEND_DELAY BIT_ULL(30)
|
||||
#define XHCI_INTEL_USB_ROLE_SW BIT_ULL(31)
|
||||
#define XHCI_ZERO_64B_REGS BIT_ULL(32)
|
||||
#define XHCI_DEFAULT_PM_RUNTIME_ALLOW BIT_ULL(33)
|
||||
|
||||
unsigned int num_active_eps;
|
||||
unsigned int limit_active_eps;
|
||||
|
@@ -146,8 +146,11 @@ static int appledisplay_bl_update_status(struct backlight_device *bd)
|
||||
pdata->msgdata, 2,
|
||||
ACD_USB_TIMEOUT);
|
||||
mutex_unlock(&pdata->sysfslock);
|
||||
|
||||
return retval;
|
||||
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int appledisplay_bl_get_brightness(struct backlight_device *bd)
|
||||
|
@@ -808,8 +808,8 @@ static int iowarrior_probe(struct usb_interface *interface,
|
||||
dev->int_in_endpoint->bInterval);
|
||||
/* create an internal buffer for interrupt data from the device */
|
||||
dev->read_queue =
|
||||
kmalloc(((dev->report_size + 1) * MAX_INTERRUPT_BUFFER),
|
||||
GFP_KERNEL);
|
||||
kmalloc_array(dev->report_size + 1, MAX_INTERRUPT_BUFFER,
|
||||
GFP_KERNEL);
|
||||
if (!dev->read_queue)
|
||||
goto error;
|
||||
/* Get the serial-number of the chip */
|
||||
|
@@ -46,7 +46,9 @@ static ssize_t speed_store(struct device *dev, struct device_attribute *attr,
|
||||
struct trancevibrator *tv = usb_get_intfdata(intf);
|
||||
int temp, retval, old;
|
||||
|
||||
temp = simple_strtoul(buf, NULL, 10);
|
||||
retval = kstrtoint(buf, 10, &temp);
|
||||
if (retval)
|
||||
return retval;
|
||||
if (temp > 255)
|
||||
temp = 255;
|
||||
else if (temp < 0)
|
||||
|
@@ -185,8 +185,8 @@ static void mtu3_intr_enable(struct mtu3 *mtu)
|
||||
|
||||
if (mtu->is_u3_ip) {
|
||||
/* Enable U3 LTSSM interrupts */
|
||||
value = HOT_RST_INTR | WARM_RST_INTR | VBUS_RISE_INTR |
|
||||
VBUS_FALL_INTR | ENTER_U3_INTR | EXIT_U3_INTR;
|
||||
value = HOT_RST_INTR | WARM_RST_INTR |
|
||||
ENTER_U3_INTR | EXIT_U3_INTR;
|
||||
mtu3_writel(mbase, U3D_LTSSM_INTR_ENABLE, value);
|
||||
}
|
||||
|
||||
|
@@ -585,6 +585,17 @@ static const struct usb_gadget_ops mtu3_gadget_ops = {
|
||||
.udc_stop = mtu3_gadget_stop,
|
||||
};
|
||||
|
||||
static void mtu3_state_reset(struct mtu3 *mtu)
|
||||
{
|
||||
mtu->address = 0;
|
||||
mtu->ep0_state = MU3D_EP0_STATE_SETUP;
|
||||
mtu->may_wakeup = 0;
|
||||
mtu->u1_enable = 0;
|
||||
mtu->u2_enable = 0;
|
||||
mtu->delayed_status = false;
|
||||
mtu->test_mode = false;
|
||||
}
|
||||
|
||||
static void init_hw_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
|
||||
u32 epnum, u32 is_in)
|
||||
{
|
||||
@@ -702,6 +713,7 @@ void mtu3_gadget_disconnect(struct mtu3 *mtu)
|
||||
spin_lock(&mtu->lock);
|
||||
}
|
||||
|
||||
mtu3_state_reset(mtu);
|
||||
usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
|
||||
}
|
||||
|
||||
@@ -712,12 +724,6 @@ void mtu3_gadget_reset(struct mtu3 *mtu)
|
||||
/* report disconnect, if we didn't flush EP state */
|
||||
if (mtu->g.speed != USB_SPEED_UNKNOWN)
|
||||
mtu3_gadget_disconnect(mtu);
|
||||
|
||||
mtu->address = 0;
|
||||
mtu->ep0_state = MU3D_EP0_STATE_SETUP;
|
||||
mtu->may_wakeup = 0;
|
||||
mtu->u1_enable = 0;
|
||||
mtu->u2_enable = 0;
|
||||
mtu->delayed_status = false;
|
||||
mtu->test_mode = false;
|
||||
else
|
||||
mtu3_state_reset(mtu);
|
||||
}
|
||||
|
@@ -505,15 +505,19 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab)
|
||||
if (is_ab8500(ab->ab8500)) {
|
||||
enum ab8500_usb_link_status lsts;
|
||||
|
||||
abx500_get_register_interruptible(ab->dev,
|
||||
ret = abx500_get_register_interruptible(ab->dev,
|
||||
AB8500_USB, AB8500_USB_LINE_STAT_REG, ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
lsts = (reg >> 3) & 0x0F;
|
||||
ret = ab8500_usb_link_status_update(ab, lsts);
|
||||
} else if (is_ab8505(ab->ab8500)) {
|
||||
enum ab8505_usb_link_status lsts;
|
||||
|
||||
abx500_get_register_interruptible(ab->dev,
|
||||
ret = abx500_get_register_interruptible(ab->dev,
|
||||
AB8500_USB, AB8505_USB_LINE_STAT_REG, ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
lsts = (reg >> 3) & 0x1F;
|
||||
ret = ab8505_usb_link_status_update(ab, lsts);
|
||||
}
|
||||
|
@@ -563,7 +563,7 @@ static enum usb_charger_type mxs_charger_primary_detection(struct mxs_phy *x)
|
||||
regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val);
|
||||
if (!(val & ANADIG_USB1_CHRG_DET_STAT_CHRG_DETECTED)) {
|
||||
chgr_type = SDP_TYPE;
|
||||
dev_dbg(x->phy.dev, "It is a stardard downstream port\n");
|
||||
dev_dbg(x->phy.dev, "It is a standard downstream port\n");
|
||||
}
|
||||
|
||||
/* Disable charger detector */
|
||||
|
@@ -5,6 +5,7 @@
|
||||
* Copyright (C) 2011 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
@@ -12,6 +13,7 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include "common.h"
|
||||
@@ -290,6 +292,79 @@ static void usbhsc_set_buswait(struct usbhs_priv *priv)
|
||||
usbhs_bset(priv, BUSWAIT, 0x000F, wait);
|
||||
}
|
||||
|
||||
static bool usbhsc_is_multi_clks(struct usbhs_priv *priv)
|
||||
{
|
||||
if (priv->dparam.type == USBHS_TYPE_RCAR_GEN3 ||
|
||||
priv->dparam.type == USBHS_TYPE_RCAR_GEN3_WITH_PLL)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int usbhsc_clk_get(struct device *dev, struct usbhs_priv *priv)
|
||||
{
|
||||
if (!usbhsc_is_multi_clks(priv))
|
||||
return 0;
|
||||
|
||||
/* The first clock should exist */
|
||||
priv->clks[0] = of_clk_get(dev->of_node, 0);
|
||||
if (IS_ERR(priv->clks[0]))
|
||||
return PTR_ERR(priv->clks[0]);
|
||||
|
||||
/*
|
||||
* To backward compatibility with old DT, this driver checks the return
|
||||
* value if it's -ENOENT or not.
|
||||
*/
|
||||
priv->clks[1] = of_clk_get(dev->of_node, 1);
|
||||
if (PTR_ERR(priv->clks[1]) == -ENOENT)
|
||||
priv->clks[1] = NULL;
|
||||
else if (IS_ERR(priv->clks[1]))
|
||||
return PTR_ERR(priv->clks[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usbhsc_clk_put(struct usbhs_priv *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!usbhsc_is_multi_clks(priv))
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(priv->clks); i++)
|
||||
clk_put(priv->clks[i]);
|
||||
}
|
||||
|
||||
static int usbhsc_clk_prepare_enable(struct usbhs_priv *priv)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
if (!usbhsc_is_multi_clks(priv))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(priv->clks); i++) {
|
||||
ret = clk_prepare_enable(priv->clks[i]);
|
||||
if (ret) {
|
||||
while (--i >= 0)
|
||||
clk_disable_unprepare(priv->clks[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usbhsc_clk_disable_unprepare(struct usbhs_priv *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!usbhsc_is_multi_clks(priv))
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(priv->clks); i++)
|
||||
clk_disable_unprepare(priv->clks[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* platform default param
|
||||
*/
|
||||
@@ -340,6 +415,10 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable)
|
||||
/* enable PM */
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
/* enable clks */
|
||||
if (usbhsc_clk_prepare_enable(priv))
|
||||
return;
|
||||
|
||||
/* enable platform power */
|
||||
usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable);
|
||||
|
||||
@@ -352,6 +431,9 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable)
|
||||
/* disable platform power */
|
||||
usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable);
|
||||
|
||||
/* disable clks */
|
||||
usbhsc_clk_disable_unprepare(priv);
|
||||
|
||||
/* disable PM */
|
||||
pm_runtime_put_sync(dev);
|
||||
}
|
||||
@@ -477,6 +559,10 @@ static const struct of_device_id usbhs_of_match[] = {
|
||||
.compatible = "renesas,usbhs-r8a7796",
|
||||
.data = (void *)USBHS_TYPE_RCAR_GEN3,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,usbhs-r8a77990",
|
||||
.data = (void *)USBHS_TYPE_RCAR_GEN3_WITH_PLL,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,usbhs-r8a77995",
|
||||
.data = (void *)USBHS_TYPE_RCAR_GEN3_WITH_PLL,
|
||||
@@ -574,6 +660,10 @@ static int usbhs_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(priv->edev);
|
||||
}
|
||||
|
||||
priv->rsts = devm_reset_control_array_get_optional_shared(&pdev->dev);
|
||||
if (IS_ERR(priv->rsts))
|
||||
return PTR_ERR(priv->rsts);
|
||||
|
||||
/*
|
||||
* care platform info
|
||||
*/
|
||||
@@ -591,15 +681,6 @@ static int usbhs_probe(struct platform_device *pdev)
|
||||
break;
|
||||
case USBHS_TYPE_RCAR_GEN3_WITH_PLL:
|
||||
priv->pfunc = usbhs_rcar3_with_pll_ops;
|
||||
if (!IS_ERR_OR_NULL(priv->edev)) {
|
||||
priv->nb.notifier_call = priv->pfunc.notifier;
|
||||
ret = devm_extcon_register_notifier(&pdev->dev,
|
||||
priv->edev,
|
||||
EXTCON_USB_HOST,
|
||||
&priv->nb);
|
||||
if (ret < 0)
|
||||
dev_err(&pdev->dev, "no notifier registered\n");
|
||||
}
|
||||
break;
|
||||
case USBHS_TYPE_RZA1:
|
||||
priv->pfunc = usbhs_rza1_ops;
|
||||
@@ -658,6 +739,14 @@ static int usbhs_probe(struct platform_device *pdev)
|
||||
/* dev_set_drvdata should be called after usbhs_mod_init */
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = reset_control_deassert(priv->rsts);
|
||||
if (ret)
|
||||
goto probe_fail_rst;
|
||||
|
||||
ret = usbhsc_clk_get(&pdev->dev, priv);
|
||||
if (ret)
|
||||
goto probe_fail_clks;
|
||||
|
||||
/*
|
||||
* deviece reset here because
|
||||
* USB device might be used in boot loader.
|
||||
@@ -711,6 +800,10 @@ static int usbhs_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
|
||||
probe_end_mod_exit:
|
||||
usbhsc_clk_put(priv);
|
||||
probe_fail_clks:
|
||||
reset_control_assert(priv->rsts);
|
||||
probe_fail_rst:
|
||||
usbhs_mod_remove(priv);
|
||||
probe_end_fifo_exit:
|
||||
usbhs_fifo_remove(priv);
|
||||
@@ -739,6 +832,8 @@ static int usbhs_remove(struct platform_device *pdev)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
usbhs_platform_call(priv, hardware_exit, pdev);
|
||||
usbhsc_clk_put(priv);
|
||||
reset_control_assert(priv->rsts);
|
||||
usbhs_mod_remove(priv);
|
||||
usbhs_fifo_remove(priv);
|
||||
usbhs_pipe_remove(priv);
|
||||
|
@@ -8,8 +8,10 @@
|
||||
#ifndef RENESAS_USB_DRIVER_H
|
||||
#define RENESAS_USB_DRIVER_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/usb/renesas_usbhs.h>
|
||||
|
||||
struct usbhs_priv;
|
||||
@@ -255,7 +257,6 @@ struct usbhs_priv {
|
||||
struct platform_device *pdev;
|
||||
|
||||
struct extcon_dev *edev;
|
||||
struct notifier_block nb;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
@@ -277,6 +278,8 @@ struct usbhs_priv {
|
||||
struct usbhs_fifo_info fifo_info;
|
||||
|
||||
struct phy *phy;
|
||||
struct reset_control *rsts;
|
||||
struct clk *clks[2];
|
||||
};
|
||||
|
||||
/*
|
||||
|
@@ -27,7 +27,6 @@
|
||||
* Remarks: bit[31:11] and bit[9:6] should be 0
|
||||
*/
|
||||
#define UGCTRL2_RESERVED_3 0x00000001 /* bit[3:0] should be B'0001 */
|
||||
#define UGCTRL2_USB0SEL_EHCI 0x00000010
|
||||
#define UGCTRL2_USB0SEL_HSUSB 0x00000020
|
||||
#define UGCTRL2_USB0SEL_OTG 0x00000030
|
||||
#define UGCTRL2_VBUSSEL 0x00000400
|
||||
@@ -50,14 +49,6 @@ static void usbhs_rcar3_set_ugctrl2(struct usbhs_priv *priv, u32 val)
|
||||
usbhs_write32(priv, UGCTRL2, val | UGCTRL2_RESERVED_3);
|
||||
}
|
||||
|
||||
static void usbhs_rcar3_set_usbsel(struct usbhs_priv *priv, bool ehci)
|
||||
{
|
||||
if (ehci)
|
||||
usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_EHCI);
|
||||
else
|
||||
usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_HSUSB);
|
||||
}
|
||||
|
||||
static int usbhs_rcar3_power_ctrl(struct platform_device *pdev,
|
||||
void __iomem *base, int enable)
|
||||
{
|
||||
@@ -83,14 +74,11 @@ static int usbhs_rcar3_power_and_pll_ctrl(struct platform_device *pdev,
|
||||
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
|
||||
u32 val;
|
||||
int timeout = 1000;
|
||||
bool is_host = false;
|
||||
|
||||
if (enable) {
|
||||
usbhs_write32(priv, UGCTRL, 0); /* release PLLRESET */
|
||||
if (priv->edev)
|
||||
is_host = extcon_get_state(priv->edev, EXTCON_USB_HOST);
|
||||
|
||||
usbhs_rcar3_set_usbsel(priv, is_host);
|
||||
usbhs_rcar3_set_ugctrl2(priv,
|
||||
UGCTRL2_USB0SEL_OTG | UGCTRL2_VBUSSEL);
|
||||
|
||||
usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM);
|
||||
do {
|
||||
@@ -112,16 +100,6 @@ static int usbhs_rcar3_get_id(struct platform_device *pdev)
|
||||
return USBHS_GADGET;
|
||||
}
|
||||
|
||||
static int usbhs_rcar3_notifier(struct notifier_block *nb, unsigned long event,
|
||||
void *data)
|
||||
{
|
||||
struct usbhs_priv *priv = container_of(nb, struct usbhs_priv, nb);
|
||||
|
||||
usbhs_rcar3_set_usbsel(priv, !!event);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = {
|
||||
.power_ctrl = usbhs_rcar3_power_ctrl,
|
||||
.get_id = usbhs_rcar3_get_id,
|
||||
@@ -130,5 +108,4 @@ const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = {
|
||||
const struct renesas_usbhs_platform_callback usbhs_rcar3_with_pll_ops = {
|
||||
.power_ctrl = usbhs_rcar3_power_and_pll_ctrl,
|
||||
.get_id = usbhs_rcar3_get_id,
|
||||
.notifier = usbhs_rcar3_notifier,
|
||||
};
|
||||
|
@@ -378,7 +378,7 @@ static int cypress_serial_control(struct tty_struct *tty,
|
||||
retval = -ENOTTY;
|
||||
goto out;
|
||||
}
|
||||
dev_dbg(dev, "%s - retreiving serial line settings\n", __func__);
|
||||
dev_dbg(dev, "%s - retrieving serial line settings\n", __func__);
|
||||
do {
|
||||
retval = usb_control_msg(port->serial->dev,
|
||||
usb_rcvctrlpipe(port->serial->dev, 0),
|
||||
@@ -769,7 +769,7 @@ send:
|
||||
|
||||
usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev,
|
||||
usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
|
||||
port->interrupt_out_buffer, port->interrupt_out_size,
|
||||
port->interrupt_out_buffer, actual_size,
|
||||
cypress_write_int_callback, port, priv->write_urb_interval);
|
||||
result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
|
||||
if (result) {
|
||||
@@ -863,7 +863,7 @@ static void cypress_set_termios(struct tty_struct *tty,
|
||||
struct cypress_private *priv = usb_get_serial_port_data(port);
|
||||
struct device *dev = &port->dev;
|
||||
int data_bits, stop_bits, parity_type, parity_enable;
|
||||
unsigned cflag, iflag;
|
||||
unsigned int cflag;
|
||||
unsigned long flags;
|
||||
__u8 oldlines;
|
||||
int linechange = 0;
|
||||
@@ -899,7 +899,6 @@ static void cypress_set_termios(struct tty_struct *tty,
|
||||
tty->termios.c_cflag &= ~(CMSPAR|CRTSCTS);
|
||||
|
||||
cflag = tty->termios.c_cflag;
|
||||
iflag = tty->termios.c_iflag;
|
||||
|
||||
/* check if there are new settings */
|
||||
if (old_termios) {
|
||||
|
@@ -39,6 +39,7 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/usb/serial.h>
|
||||
#include "ftdi_sio.h"
|
||||
#include "ftdi_sio_ids.h"
|
||||
@@ -72,6 +73,15 @@ struct ftdi_private {
|
||||
unsigned int latency; /* latency setting in use */
|
||||
unsigned short max_packet_size;
|
||||
struct mutex cfg_lock; /* Avoid mess by parallel calls of config ioctl() and change_speed() */
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
struct gpio_chip gc;
|
||||
struct mutex gpio_lock; /* protects GPIO state */
|
||||
bool gpio_registered; /* is the gpiochip in kernel registered */
|
||||
bool gpio_used; /* true if the user requested a gpio */
|
||||
u8 gpio_altfunc; /* which pins are in gpio mode */
|
||||
u8 gpio_output; /* pin directions cache */
|
||||
u8 gpio_value; /* pin value for outputs */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* struct ftdi_sio_quirk is used by devices requiring special attention. */
|
||||
@@ -1764,6 +1774,375 @@ static void remove_sysfs_attrs(struct usb_serial_port *port)
|
||||
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
|
||||
static int ftdi_set_bitmode(struct usb_serial_port *port, u8 mode)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
struct usb_serial *serial = port->serial;
|
||||
int result;
|
||||
u16 val;
|
||||
|
||||
val = (mode << 8) | (priv->gpio_output << 4) | priv->gpio_value;
|
||||
result = usb_control_msg(serial->dev,
|
||||
usb_sndctrlpipe(serial->dev, 0),
|
||||
FTDI_SIO_SET_BITMODE_REQUEST,
|
||||
FTDI_SIO_SET_BITMODE_REQUEST_TYPE, val,
|
||||
priv->interface, NULL, 0, WDR_TIMEOUT);
|
||||
if (result < 0) {
|
||||
dev_err(&serial->interface->dev,
|
||||
"bitmode request failed for value 0x%04x: %d\n",
|
||||
val, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ftdi_set_cbus_pins(struct usb_serial_port *port)
|
||||
{
|
||||
return ftdi_set_bitmode(port, FTDI_SIO_BITMODE_CBUS);
|
||||
}
|
||||
|
||||
static int ftdi_exit_cbus_mode(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
priv->gpio_output = 0;
|
||||
priv->gpio_value = 0;
|
||||
return ftdi_set_bitmode(port, FTDI_SIO_BITMODE_RESET);
|
||||
}
|
||||
|
||||
static int ftdi_gpio_request(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct usb_serial_port *port = gpiochip_get_data(gc);
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
int result;
|
||||
|
||||
if (priv->gpio_altfunc & BIT(offset))
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&priv->gpio_lock);
|
||||
if (!priv->gpio_used) {
|
||||
/* Set default pin states, as we cannot get them from device */
|
||||
priv->gpio_output = 0x00;
|
||||
priv->gpio_value = 0x00;
|
||||
result = ftdi_set_cbus_pins(port);
|
||||
if (result) {
|
||||
mutex_unlock(&priv->gpio_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
priv->gpio_used = true;
|
||||
}
|
||||
mutex_unlock(&priv->gpio_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftdi_read_cbus_pins(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
struct usb_serial *serial = port->serial;
|
||||
unsigned char *buf;
|
||||
int result;
|
||||
|
||||
buf = kmalloc(1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
result = usb_control_msg(serial->dev,
|
||||
usb_rcvctrlpipe(serial->dev, 0),
|
||||
FTDI_SIO_READ_PINS_REQUEST,
|
||||
FTDI_SIO_READ_PINS_REQUEST_TYPE, 0,
|
||||
priv->interface, buf, 1, WDR_TIMEOUT);
|
||||
if (result < 1) {
|
||||
if (result >= 0)
|
||||
result = -EIO;
|
||||
} else {
|
||||
result = buf[0];
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ftdi_gpio_get(struct gpio_chip *gc, unsigned int gpio)
|
||||
{
|
||||
struct usb_serial_port *port = gpiochip_get_data(gc);
|
||||
int result;
|
||||
|
||||
result = ftdi_read_cbus_pins(port);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
return !!(result & BIT(gpio));
|
||||
}
|
||||
|
||||
static void ftdi_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
|
||||
{
|
||||
struct usb_serial_port *port = gpiochip_get_data(gc);
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
mutex_lock(&priv->gpio_lock);
|
||||
|
||||
if (value)
|
||||
priv->gpio_value |= BIT(gpio);
|
||||
else
|
||||
priv->gpio_value &= ~BIT(gpio);
|
||||
|
||||
ftdi_set_cbus_pins(port);
|
||||
|
||||
mutex_unlock(&priv->gpio_lock);
|
||||
}
|
||||
|
||||
static int ftdi_gpio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
|
||||
unsigned long *bits)
|
||||
{
|
||||
struct usb_serial_port *port = gpiochip_get_data(gc);
|
||||
int result;
|
||||
|
||||
result = ftdi_read_cbus_pins(port);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
*bits = result & *mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ftdi_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
|
||||
unsigned long *bits)
|
||||
{
|
||||
struct usb_serial_port *port = gpiochip_get_data(gc);
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
mutex_lock(&priv->gpio_lock);
|
||||
|
||||
priv->gpio_value &= ~(*mask);
|
||||
priv->gpio_value |= *bits & *mask;
|
||||
ftdi_set_cbus_pins(port);
|
||||
|
||||
mutex_unlock(&priv->gpio_lock);
|
||||
}
|
||||
|
||||
static int ftdi_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio)
|
||||
{
|
||||
struct usb_serial_port *port = gpiochip_get_data(gc);
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
return !(priv->gpio_output & BIT(gpio));
|
||||
}
|
||||
|
||||
static int ftdi_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio)
|
||||
{
|
||||
struct usb_serial_port *port = gpiochip_get_data(gc);
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
int result;
|
||||
|
||||
mutex_lock(&priv->gpio_lock);
|
||||
|
||||
priv->gpio_output &= ~BIT(gpio);
|
||||
result = ftdi_set_cbus_pins(port);
|
||||
|
||||
mutex_unlock(&priv->gpio_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ftdi_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio,
|
||||
int value)
|
||||
{
|
||||
struct usb_serial_port *port = gpiochip_get_data(gc);
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
int result;
|
||||
|
||||
mutex_lock(&priv->gpio_lock);
|
||||
|
||||
priv->gpio_output |= BIT(gpio);
|
||||
if (value)
|
||||
priv->gpio_value |= BIT(gpio);
|
||||
else
|
||||
priv->gpio_value &= ~BIT(gpio);
|
||||
|
||||
result = ftdi_set_cbus_pins(port);
|
||||
|
||||
mutex_unlock(&priv->gpio_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ftdi_read_eeprom(struct usb_serial *serial, void *dst, u16 addr,
|
||||
u16 nbytes)
|
||||
{
|
||||
int read = 0;
|
||||
|
||||
if (addr % 2 != 0)
|
||||
return -EINVAL;
|
||||
if (nbytes % 2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Read EEPROM two bytes at a time */
|
||||
while (read < nbytes) {
|
||||
int rv;
|
||||
|
||||
rv = usb_control_msg(serial->dev,
|
||||
usb_rcvctrlpipe(serial->dev, 0),
|
||||
FTDI_SIO_READ_EEPROM_REQUEST,
|
||||
FTDI_SIO_READ_EEPROM_REQUEST_TYPE,
|
||||
0, (addr + read) / 2, dst + read, 2,
|
||||
WDR_TIMEOUT);
|
||||
if (rv < 2) {
|
||||
if (rv >= 0)
|
||||
return -EIO;
|
||||
else
|
||||
return rv;
|
||||
}
|
||||
|
||||
read += rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftdi_gpio_init_ft232r(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
u16 cbus_config;
|
||||
u8 *buf;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
buf = kmalloc(2, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = ftdi_read_eeprom(port->serial, buf, 0x14, 2);
|
||||
if (ret < 0)
|
||||
goto out_free;
|
||||
|
||||
cbus_config = le16_to_cpup((__le16 *)buf);
|
||||
dev_dbg(&port->dev, "cbus_config = 0x%04x\n", cbus_config);
|
||||
|
||||
priv->gc.ngpio = 4;
|
||||
|
||||
priv->gpio_altfunc = 0xff;
|
||||
for (i = 0; i < priv->gc.ngpio; ++i) {
|
||||
if ((cbus_config & 0xf) == FTDI_FT232R_CBUS_MUX_GPIO)
|
||||
priv->gpio_altfunc &= ~BIT(i);
|
||||
cbus_config >>= 4;
|
||||
}
|
||||
out_free:
|
||||
kfree(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ftdi_gpio_init_ftx(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
struct usb_serial *serial = port->serial;
|
||||
const u16 cbus_cfg_addr = 0x1a;
|
||||
const u16 cbus_cfg_size = 4;
|
||||
u8 *cbus_cfg_buf;
|
||||
int result;
|
||||
u8 i;
|
||||
|
||||
cbus_cfg_buf = kmalloc(cbus_cfg_size, GFP_KERNEL);
|
||||
if (!cbus_cfg_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
result = ftdi_read_eeprom(serial, cbus_cfg_buf,
|
||||
cbus_cfg_addr, cbus_cfg_size);
|
||||
if (result < 0)
|
||||
goto out_free;
|
||||
|
||||
/* FIXME: FT234XD alone has 1 GPIO, but how to recognize this IC? */
|
||||
priv->gc.ngpio = 4;
|
||||
|
||||
/* Determine which pins are configured for CBUS bitbanging */
|
||||
priv->gpio_altfunc = 0xff;
|
||||
for (i = 0; i < priv->gc.ngpio; ++i) {
|
||||
if (cbus_cfg_buf[i] == FTDI_FTX_CBUS_MUX_GPIO)
|
||||
priv->gpio_altfunc &= ~BIT(i);
|
||||
}
|
||||
|
||||
out_free:
|
||||
kfree(cbus_cfg_buf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ftdi_gpio_init(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
struct usb_serial *serial = port->serial;
|
||||
int result;
|
||||
|
||||
switch (priv->chip_type) {
|
||||
case FT232RL:
|
||||
result = ftdi_gpio_init_ft232r(port);
|
||||
break;
|
||||
case FTX:
|
||||
result = ftdi_gpio_init_ftx(port);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
mutex_init(&priv->gpio_lock);
|
||||
|
||||
priv->gc.label = "ftdi-cbus";
|
||||
priv->gc.request = ftdi_gpio_request;
|
||||
priv->gc.get_direction = ftdi_gpio_direction_get;
|
||||
priv->gc.direction_input = ftdi_gpio_direction_input;
|
||||
priv->gc.direction_output = ftdi_gpio_direction_output;
|
||||
priv->gc.get = ftdi_gpio_get;
|
||||
priv->gc.set = ftdi_gpio_set;
|
||||
priv->gc.get_multiple = ftdi_gpio_get_multiple;
|
||||
priv->gc.set_multiple = ftdi_gpio_set_multiple;
|
||||
priv->gc.owner = THIS_MODULE;
|
||||
priv->gc.parent = &serial->interface->dev;
|
||||
priv->gc.base = -1;
|
||||
priv->gc.can_sleep = true;
|
||||
|
||||
result = gpiochip_add_data(&priv->gc, port);
|
||||
if (!result)
|
||||
priv->gpio_registered = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ftdi_gpio_remove(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
if (priv->gpio_registered) {
|
||||
gpiochip_remove(&priv->gc);
|
||||
priv->gpio_registered = false;
|
||||
}
|
||||
|
||||
if (priv->gpio_used) {
|
||||
/* Exiting CBUS-mode does not reset pin states. */
|
||||
ftdi_exit_cbus_mode(port);
|
||||
priv->gpio_used = false;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int ftdi_gpio_init(struct usb_serial_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ftdi_gpio_remove(struct usb_serial_port *port) { }
|
||||
|
||||
#endif /* CONFIG_GPIOLIB */
|
||||
|
||||
/*
|
||||
* ***************************************************************************
|
||||
* FTDI driver specific functions
|
||||
@@ -1792,7 +2171,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv;
|
||||
const struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);
|
||||
|
||||
int result;
|
||||
|
||||
priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
|
||||
if (!priv)
|
||||
@@ -1811,6 +2190,14 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
|
||||
priv->latency = 16;
|
||||
write_latency_timer(port);
|
||||
create_sysfs_attrs(port);
|
||||
|
||||
result = ftdi_gpio_init(port);
|
||||
if (result < 0) {
|
||||
dev_err(&port->serial->interface->dev,
|
||||
"GPIO initialisation failed: %d\n",
|
||||
result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1928,6 +2315,8 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
ftdi_gpio_remove(port);
|
||||
|
||||
remove_sysfs_attrs(port);
|
||||
|
||||
kfree(priv);
|
||||
|
@@ -35,7 +35,10 @@
|
||||
#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */
|
||||
#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */
|
||||
#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */
|
||||
#define FTDI_SIO_GET_LATENCY_TIMER 10 /* Get the latency timer */
|
||||
#define FTDI_SIO_GET_LATENCY_TIMER 0x0a /* Get the latency timer */
|
||||
#define FTDI_SIO_SET_BITMODE 0x0b /* Set bitbang mode */
|
||||
#define FTDI_SIO_READ_PINS 0x0c /* Read immediate value of pins */
|
||||
#define FTDI_SIO_READ_EEPROM 0x90 /* Read EEPROM */
|
||||
|
||||
/* Interface indices for FT2232, FT2232H and FT4232H devices */
|
||||
#define INTERFACE_A 1
|
||||
@@ -433,6 +436,29 @@ enum ftdi_sio_baudrate {
|
||||
* 1 = active
|
||||
*/
|
||||
|
||||
/* FTDI_SIO_SET_BITMODE */
|
||||
#define FTDI_SIO_SET_BITMODE_REQUEST_TYPE 0x40
|
||||
#define FTDI_SIO_SET_BITMODE_REQUEST FTDI_SIO_SET_BITMODE
|
||||
|
||||
/* Possible bitmodes for FTDI_SIO_SET_BITMODE_REQUEST */
|
||||
#define FTDI_SIO_BITMODE_RESET 0x00
|
||||
#define FTDI_SIO_BITMODE_CBUS 0x20
|
||||
|
||||
/* FTDI_SIO_READ_PINS */
|
||||
#define FTDI_SIO_READ_PINS_REQUEST_TYPE 0xc0
|
||||
#define FTDI_SIO_READ_PINS_REQUEST FTDI_SIO_READ_PINS
|
||||
|
||||
/*
|
||||
* FTDI_SIO_READ_EEPROM
|
||||
*
|
||||
* EEPROM format found in FTDI AN_201, "FT-X MTP memory Configuration",
|
||||
* http://www.ftdichip.com/Support/Documents/AppNotes/AN_201_FT-X%20MTP%20Memory%20Configuration.pdf
|
||||
*/
|
||||
#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xc0
|
||||
#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM
|
||||
|
||||
#define FTDI_FTX_CBUS_MUX_GPIO 0x8
|
||||
#define FTDI_FT232R_CBUS_MUX_GPIO 0xa
|
||||
|
||||
|
||||
/* Descriptors returned by the device
|
||||
|
@@ -23,16 +23,16 @@ config USB_STORAGE
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usb-storage.
|
||||
|
||||
if USB_STORAGE
|
||||
|
||||
config USB_STORAGE_DEBUG
|
||||
bool "USB Mass Storage verbose debug"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here in order to have the USB Mass Storage code generate
|
||||
verbose debugging messages.
|
||||
|
||||
config USB_STORAGE_REALTEK
|
||||
tristate "Realtek Card Reader support"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here to include additional code to support the power-saving function
|
||||
for Realtek RTS51xx USB card readers.
|
||||
@@ -46,7 +46,6 @@ config REALTEK_AUTOPM
|
||||
|
||||
config USB_STORAGE_DATAFAB
|
||||
tristate "Datafab Compact Flash Reader support"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Support for certain Datafab CompactFlash readers.
|
||||
Datafab has a web page at <http://www.datafab.com/>.
|
||||
@@ -55,7 +54,6 @@ config USB_STORAGE_DATAFAB
|
||||
|
||||
config USB_STORAGE_FREECOM
|
||||
tristate "Freecom USB/ATAPI Bridge support"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Support for the Freecom USB to IDE/ATAPI adaptor.
|
||||
Freecom has a web page at <http://www.freecom.de/>.
|
||||
@@ -64,7 +62,6 @@ config USB_STORAGE_FREECOM
|
||||
|
||||
config USB_STORAGE_ISD200
|
||||
tristate "ISD-200 USB/ATA Bridge support"
|
||||
depends on USB_STORAGE
|
||||
---help---
|
||||
Say Y here if you want to use USB Mass Store devices based
|
||||
on the In-Systems Design ISD-200 USB/ATA bridge.
|
||||
@@ -82,7 +79,6 @@ config USB_STORAGE_ISD200
|
||||
|
||||
config USB_STORAGE_USBAT
|
||||
tristate "USBAT/USBAT02-based storage support"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here to include additional code to support storage devices
|
||||
based on the SCM/Shuttle USBAT/USBAT02 processors.
|
||||
@@ -105,7 +101,6 @@ config USB_STORAGE_USBAT
|
||||
|
||||
config USB_STORAGE_SDDR09
|
||||
tristate "SanDisk SDDR-09 (and other SmartMedia, including DPCM) support"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here to include additional code to support the Sandisk SDDR-09
|
||||
SmartMedia reader in the USB Mass Storage driver.
|
||||
@@ -115,7 +110,6 @@ config USB_STORAGE_SDDR09
|
||||
|
||||
config USB_STORAGE_SDDR55
|
||||
tristate "SanDisk SDDR-55 SmartMedia support"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here to include additional code to support the Sandisk SDDR-55
|
||||
SmartMedia reader in the USB Mass Storage driver.
|
||||
@@ -124,7 +118,6 @@ config USB_STORAGE_SDDR55
|
||||
|
||||
config USB_STORAGE_JUMPSHOT
|
||||
tristate "Lexar Jumpshot Compact Flash Reader"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here to include additional code to support the Lexar Jumpshot
|
||||
USB CompactFlash reader.
|
||||
@@ -133,7 +126,6 @@ config USB_STORAGE_JUMPSHOT
|
||||
|
||||
config USB_STORAGE_ALAUDA
|
||||
tristate "Olympus MAUSB-10/Fuji DPC-R1 support"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here to include additional code to support the Olympus MAUSB-10
|
||||
and Fujifilm DPC-R1 USB Card reader/writer devices.
|
||||
@@ -145,7 +137,6 @@ config USB_STORAGE_ALAUDA
|
||||
|
||||
config USB_STORAGE_ONETOUCH
|
||||
tristate "Support OneTouch Button on Maxtor Hard Drives"
|
||||
depends on USB_STORAGE
|
||||
depends on INPUT=y || INPUT=USB_STORAGE
|
||||
help
|
||||
Say Y here to include additional code to support the Maxtor OneTouch
|
||||
@@ -160,7 +151,6 @@ config USB_STORAGE_ONETOUCH
|
||||
|
||||
config USB_STORAGE_KARMA
|
||||
tristate "Support for Rio Karma music player"
|
||||
depends on USB_STORAGE
|
||||
help
|
||||
Say Y here to include additional code to support the Rio Karma
|
||||
USB interface.
|
||||
@@ -174,7 +164,6 @@ config USB_STORAGE_KARMA
|
||||
|
||||
config USB_STORAGE_CYPRESS_ATACB
|
||||
tristate "SAT emulation on Cypress USB/ATA Bridge with ATACB"
|
||||
depends on USB_STORAGE
|
||||
---help---
|
||||
Say Y here if you want to use SAT (ata pass through) on devices based
|
||||
on the Cypress USB/ATA bridge supporting ATACB. This will allow you
|
||||
@@ -187,19 +176,15 @@ config USB_STORAGE_CYPRESS_ATACB
|
||||
|
||||
config USB_STORAGE_ENE_UB6250
|
||||
tristate "USB ENE card reader support"
|
||||
depends on SCSI
|
||||
depends on USB_STORAGE
|
||||
---help---
|
||||
Say Y here if you wish to control a ENE SD/MS Card reader.
|
||||
Note that this driver does not support SM cards.
|
||||
|
||||
This option depends on 'SCSI' support being enabled, but you
|
||||
probably also need 'SCSI device support: SCSI disk support'
|
||||
(BLK_DEV_SD) for most USB storage devices.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ums-eneub6250.
|
||||
|
||||
endif # USB_STORAGE
|
||||
|
||||
config USB_UAS
|
||||
tristate "USB Attached SCSI"
|
||||
depends on SCSI && USB_STORAGE
|
||||
|
@@ -1153,7 +1153,7 @@ static int isd200_get_inquiry_data( struct us_data *us )
|
||||
/* Fill in vendor identification fields */
|
||||
src = (__be16 *)&id[ATA_ID_PROD];
|
||||
dest = (__u16*)info->InquiryData.VendorId;
|
||||
for (i=0;i<4;i++)
|
||||
for (i = 0; i < 4; i++)
|
||||
dest[i] = be16_to_cpu(src[i]);
|
||||
|
||||
src = (__be16 *)&id[ATA_ID_PROD + 8/2];
|
||||
|
@@ -45,50 +45,7 @@ menuconfig TYPEC
|
||||
|
||||
if TYPEC
|
||||
|
||||
config TYPEC_TCPM
|
||||
tristate "USB Type-C Port Controller Manager"
|
||||
depends on USB
|
||||
select USB_ROLE_SWITCH
|
||||
select POWER_SUPPLY
|
||||
help
|
||||
The Type-C Port Controller Manager provides a USB PD and USB Type-C
|
||||
state machine for use with Type-C Port Controllers.
|
||||
|
||||
if TYPEC_TCPM
|
||||
|
||||
config TYPEC_TCPCI
|
||||
tristate "Type-C Port Controller Interface driver"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Type-C Port Controller driver for TCPCI-compliant controller.
|
||||
|
||||
config TYPEC_RT1711H
|
||||
tristate "Richtek RT1711H Type-C chip driver"
|
||||
depends on I2C
|
||||
select TYPEC_TCPCI
|
||||
help
|
||||
Richtek RT1711H Type-C chip driver that works with
|
||||
Type-C Port Controller Manager to provide USB PD and USB
|
||||
Type-C functionalities.
|
||||
|
||||
source "drivers/usb/typec/fusb302/Kconfig"
|
||||
|
||||
config TYPEC_WCOVE
|
||||
tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
|
||||
depends on ACPI
|
||||
depends on INTEL_SOC_PMIC
|
||||
depends on INTEL_PMC_IPC
|
||||
depends on BXT_WC_PMIC_OPREGION
|
||||
help
|
||||
This driver adds support for USB Type-C detection on Intel Broxton
|
||||
platforms that have Intel Whiskey Cove PMIC. The driver can detect the
|
||||
role and cable orientation.
|
||||
|
||||
To compile this driver as module, choose M here: the module will be
|
||||
called typec_wcove
|
||||
|
||||
endif # TYPEC_TCPM
|
||||
source "drivers/usb/typec/tcpm/Kconfig"
|
||||
|
||||
source "drivers/usb/typec/ucsi/Kconfig"
|
||||
|
||||
|
@@ -2,11 +2,7 @@
|
||||
obj-$(CONFIG_TYPEC) += typec.o
|
||||
typec-y := class.o mux.o bus.o
|
||||
obj-$(CONFIG_TYPEC) += altmodes/
|
||||
obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
|
||||
obj-y += fusb302/
|
||||
obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
|
||||
obj-$(CONFIG_TYPEC_TCPM) += tcpm/
|
||||
obj-$(CONFIG_TYPEC_UCSI) += ucsi/
|
||||
obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o
|
||||
obj-$(CONFIG_TYPEC) += mux/
|
||||
obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o
|
||||
obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o
|
||||
|
@@ -1322,7 +1322,7 @@ void typec_set_pwr_role(struct typec_port *port, enum typec_role role)
|
||||
EXPORT_SYMBOL_GPL(typec_set_pwr_role);
|
||||
|
||||
/**
|
||||
* typec_set_pwr_role - Report VCONN source change
|
||||
* typec_set_vconn_role - Report VCONN source change
|
||||
* @port: The USB Type-C Port which VCONN role changed
|
||||
* @role: Source when @port is sourcing VCONN, or Sink when it's not
|
||||
*
|
||||
@@ -1500,7 +1500,7 @@ typec_port_register_altmode(struct typec_port *port,
|
||||
|
||||
sprintf(id, "id%04xm%02x", desc->svid, desc->mode);
|
||||
|
||||
mux = typec_mux_get(port->dev.parent, id);
|
||||
mux = typec_mux_get(&port->dev, id);
|
||||
if (IS_ERR(mux))
|
||||
return ERR_CAST(mux);
|
||||
|
||||
@@ -1540,18 +1540,6 @@ struct typec_port *typec_register_port(struct device *parent,
|
||||
return ERR_PTR(id);
|
||||
}
|
||||
|
||||
port->sw = typec_switch_get(cap->fwnode ? &port->dev : parent);
|
||||
if (IS_ERR(port->sw)) {
|
||||
ret = PTR_ERR(port->sw);
|
||||
goto err_switch;
|
||||
}
|
||||
|
||||
port->mux = typec_mux_get(parent, "typec-mux");
|
||||
if (IS_ERR(port->mux)) {
|
||||
ret = PTR_ERR(port->mux);
|
||||
goto err_mux;
|
||||
}
|
||||
|
||||
switch (cap->type) {
|
||||
case TYPEC_PORT_SRC:
|
||||
port->pwr_role = TYPEC_SOURCE;
|
||||
@@ -1592,13 +1580,26 @@ struct typec_port *typec_register_port(struct device *parent,
|
||||
port->port_type = cap->type;
|
||||
port->prefer_role = cap->prefer_role;
|
||||
|
||||
device_initialize(&port->dev);
|
||||
port->dev.class = typec_class;
|
||||
port->dev.parent = parent;
|
||||
port->dev.fwnode = cap->fwnode;
|
||||
port->dev.type = &typec_port_dev_type;
|
||||
dev_set_name(&port->dev, "port%d", id);
|
||||
|
||||
ret = device_register(&port->dev);
|
||||
port->sw = typec_switch_get(&port->dev);
|
||||
if (IS_ERR(port->sw)) {
|
||||
put_device(&port->dev);
|
||||
return ERR_CAST(port->sw);
|
||||
}
|
||||
|
||||
port->mux = typec_mux_get(&port->dev, "typec-mux");
|
||||
if (IS_ERR(port->mux)) {
|
||||
put_device(&port->dev);
|
||||
return ERR_CAST(port->mux);
|
||||
}
|
||||
|
||||
ret = device_add(&port->dev);
|
||||
if (ret) {
|
||||
dev_err(parent, "failed to register port (%d)\n", ret);
|
||||
put_device(&port->dev);
|
||||
@@ -1606,15 +1607,6 @@ struct typec_port *typec_register_port(struct device *parent,
|
||||
}
|
||||
|
||||
return port;
|
||||
|
||||
err_mux:
|
||||
typec_switch_put(port->sw);
|
||||
|
||||
err_switch:
|
||||
ida_simple_remove(&typec_index_ida, port->id);
|
||||
kfree(port);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_register_port);
|
||||
|
||||
|
@@ -1,7 +0,0 @@
|
||||
config TYPEC_FUSB302
|
||||
tristate "Fairchild FUSB302 Type-C chip driver"
|
||||
depends on I2C
|
||||
help
|
||||
The Fairchild FUSB302 Type-C chip driver that works with
|
||||
Type-C Port Controller Manager to provide USB PD and USB
|
||||
Type-C functionalities.
|
@@ -1,2 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_TYPEC_FUSB302) += fusb302.o
|
52
drivers/usb/typec/tcpm/Kconfig
一般檔案
52
drivers/usb/typec/tcpm/Kconfig
一般檔案
@@ -0,0 +1,52 @@
|
||||
config TYPEC_TCPM
|
||||
tristate "USB Type-C Port Controller Manager"
|
||||
depends on USB
|
||||
select USB_ROLE_SWITCH
|
||||
select POWER_SUPPLY
|
||||
help
|
||||
The Type-C Port Controller Manager provides a USB PD and USB Type-C
|
||||
state machine for use with Type-C Port Controllers.
|
||||
|
||||
if TYPEC_TCPM
|
||||
|
||||
config TYPEC_TCPCI
|
||||
tristate "Type-C Port Controller Interface driver"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Type-C Port Controller driver for TCPCI-compliant controller.
|
||||
|
||||
if TYPEC_TCPCI
|
||||
|
||||
config TYPEC_RT1711H
|
||||
tristate "Richtek RT1711H Type-C chip driver"
|
||||
help
|
||||
Richtek RT1711H Type-C chip driver that works with
|
||||
Type-C Port Controller Manager to provide USB PD and USB
|
||||
Type-C functionalities.
|
||||
|
||||
endif # TYPEC_TCPCI
|
||||
|
||||
config TYPEC_FUSB302
|
||||
tristate "Fairchild FUSB302 Type-C chip driver"
|
||||
depends on I2C
|
||||
help
|
||||
The Fairchild FUSB302 Type-C chip driver that works with
|
||||
Type-C Port Controller Manager to provide USB PD and USB
|
||||
Type-C functionalities.
|
||||
|
||||
config TYPEC_WCOVE
|
||||
tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
|
||||
depends on ACPI
|
||||
depends on INTEL_SOC_PMIC
|
||||
depends on INTEL_PMC_IPC
|
||||
depends on BXT_WC_PMIC_OPREGION
|
||||
help
|
||||
This driver adds support for USB Type-C on Intel Broxton platforms
|
||||
that have Intel Whiskey Cove PMIC. The driver works with USB Type-C
|
||||
Port Controller Manager to provide USB PD and Type-C functionalities.
|
||||
|
||||
To compile this driver as module, choose M here: the module will be
|
||||
called typec_wcove.ko
|
||||
|
||||
endif # TYPEC_TCPM
|
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
|
||||
obj-$(CONFIG_TYPEC_FUSB302) += fusb302.o
|
||||
obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
|
||||
typec_wcove-y := wcove.o
|
||||
obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o
|
||||
obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o
|
@@ -42,19 +42,12 @@
|
||||
#define T_BC_LVL_DEBOUNCE_DELAY_MS 30
|
||||
|
||||
enum toggling_mode {
|
||||
TOGGLINE_MODE_OFF,
|
||||
TOGGLING_MODE_OFF,
|
||||
TOGGLING_MODE_DRP,
|
||||
TOGGLING_MODE_SNK,
|
||||
TOGGLING_MODE_SRC,
|
||||
};
|
||||
|
||||
static const char * const toggling_mode_name[] = {
|
||||
[TOGGLINE_MODE_OFF] = "toggling_OFF",
|
||||
[TOGGLING_MODE_DRP] = "toggling_DRP",
|
||||
[TOGGLING_MODE_SNK] = "toggling_SNK",
|
||||
[TOGGLING_MODE_SRC] = "toggling_SRC",
|
||||
};
|
||||
|
||||
enum src_current_status {
|
||||
SRC_CURRENT_DEFAULT,
|
||||
SRC_CURRENT_MEDIUM,
|
||||
@@ -601,7 +594,7 @@ static int fusb302_set_toggling(struct fusb302_chip *chip,
|
||||
chip->intr_comp_chng = false;
|
||||
/* configure toggling mode: none/snk/src/drp */
|
||||
switch (mode) {
|
||||
case TOGGLINE_MODE_OFF:
|
||||
case TOGGLING_MODE_OFF:
|
||||
ret = fusb302_i2c_mask_write(chip, FUSB_REG_CONTROL2,
|
||||
FUSB_REG_CONTROL2_MODE_MASK,
|
||||
FUSB_REG_CONTROL2_MODE_NONE);
|
||||
@@ -633,7 +626,7 @@ static int fusb302_set_toggling(struct fusb302_chip *chip,
|
||||
break;
|
||||
}
|
||||
|
||||
if (mode == TOGGLINE_MODE_OFF) {
|
||||
if (mode == TOGGLING_MODE_OFF) {
|
||||
/* mask TOGDONE interrupt */
|
||||
ret = fusb302_i2c_set_bits(chip, FUSB_REG_MASKA,
|
||||
FUSB_REG_MASKA_TOGDONE);
|
||||
@@ -686,6 +679,7 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc)
|
||||
int ret = 0;
|
||||
bool pull_up, pull_down;
|
||||
u8 rd_mda;
|
||||
enum toggling_mode mode;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
switch (cc) {
|
||||
@@ -709,7 +703,7 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc)
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
ret = fusb302_set_toggling(chip, TOGGLINE_MODE_OFF);
|
||||
ret = fusb302_set_toggling(chip, TOGGLING_MODE_OFF);
|
||||
if (ret < 0) {
|
||||
fusb302_log(chip, "cannot stop toggling, ret=%d", ret);
|
||||
goto done;
|
||||
@@ -771,6 +765,29 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc)
|
||||
chip->intr_comp_chng = false;
|
||||
}
|
||||
fusb302_log(chip, "cc := %s", typec_cc_status_name[cc]);
|
||||
|
||||
/* Enable detection for fixed SNK or SRC only roles */
|
||||
switch (cc) {
|
||||
case TYPEC_CC_RD:
|
||||
mode = TOGGLING_MODE_SNK;
|
||||
break;
|
||||
case TYPEC_CC_RP_DEF:
|
||||
case TYPEC_CC_RP_1_5:
|
||||
case TYPEC_CC_RP_3_0:
|
||||
mode = TOGGLING_MODE_SRC;
|
||||
break;
|
||||
default:
|
||||
mode = TOGGLING_MODE_OFF;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mode != TOGGLING_MODE_OFF) {
|
||||
ret = fusb302_set_toggling(chip, mode);
|
||||
if (ret < 0)
|
||||
fusb302_log(chip,
|
||||
"cannot set fixed role toggling mode, ret=%d",
|
||||
ret);
|
||||
}
|
||||
done:
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
@@ -1178,10 +1195,6 @@ static const u32 src_pdo[] = {
|
||||
PDO_FIXED(5000, 400, PDO_FIXED_FLAGS),
|
||||
};
|
||||
|
||||
static const u32 snk_pdo[] = {
|
||||
PDO_FIXED(5000, 400, PDO_FIXED_FLAGS),
|
||||
};
|
||||
|
||||
static const struct tcpc_config fusb302_tcpc_config = {
|
||||
.src_pdo = src_pdo,
|
||||
.nr_src_pdo = ARRAY_SIZE(src_pdo),
|
||||
@@ -1303,7 +1316,7 @@ static int fusb302_handle_togdone_snk(struct fusb302_chip *chip,
|
||||
tcpm_cc_change(chip->tcpm_port);
|
||||
}
|
||||
/* turn off toggling */
|
||||
ret = fusb302_set_toggling(chip, TOGGLINE_MODE_OFF);
|
||||
ret = fusb302_set_toggling(chip, TOGGLING_MODE_OFF);
|
||||
if (ret < 0) {
|
||||
fusb302_log(chip,
|
||||
"cannot set toggling mode off, ret=%d", ret);
|
||||
@@ -1399,7 +1412,7 @@ static int fusb302_handle_togdone_src(struct fusb302_chip *chip,
|
||||
tcpm_cc_change(chip->tcpm_port);
|
||||
}
|
||||
/* turn off toggling */
|
||||
ret = fusb302_set_toggling(chip, TOGGLINE_MODE_OFF);
|
||||
ret = fusb302_set_toggling(chip, TOGGLING_MODE_OFF);
|
||||
if (ret < 0) {
|
||||
fusb302_log(chip,
|
||||
"cannot set toggling mode off, ret=%d", ret);
|
||||
@@ -1730,12 +1743,14 @@ static int fusb302_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
chip->i2c_client = client;
|
||||
i2c_set_clientdata(client, chip);
|
||||
chip->dev = &client->dev;
|
||||
chip->tcpc_config = fusb302_tcpc_config;
|
||||
chip->tcpc_dev.config = &chip->tcpc_config;
|
||||
mutex_init(&chip->lock);
|
||||
|
||||
chip->tcpc_dev.fwnode =
|
||||
device_get_named_child_node(dev, "connector");
|
||||
|
||||
if (!device_property_read_u32(dev, "fcs,operating-sink-microwatt", &v))
|
||||
chip->tcpc_config.operating_snk_mw = v / 1000;
|
||||
|
||||
@@ -1756,22 +1771,17 @@ static int fusb302_probe(struct i2c_client *client,
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
fusb302_debugfs_init(chip);
|
||||
chip->vbus = devm_regulator_get(chip->dev, "vbus");
|
||||
if (IS_ERR(chip->vbus))
|
||||
return PTR_ERR(chip->vbus);
|
||||
|
||||
chip->wq = create_singlethread_workqueue(dev_name(chip->dev));
|
||||
if (!chip->wq) {
|
||||
ret = -ENOMEM;
|
||||
goto clear_client_data;
|
||||
}
|
||||
if (!chip->wq)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_DELAYED_WORK(&chip->bc_lvl_handler, fusb302_bc_lvl_handler_work);
|
||||
init_tcpc_dev(&chip->tcpc_dev);
|
||||
|
||||
chip->vbus = devm_regulator_get(chip->dev, "vbus");
|
||||
if (IS_ERR(chip->vbus)) {
|
||||
ret = PTR_ERR(chip->vbus);
|
||||
goto destroy_workqueue;
|
||||
}
|
||||
|
||||
if (client->irq) {
|
||||
chip->gpio_int_n_irq = client->irq;
|
||||
} else {
|
||||
@@ -1797,15 +1807,15 @@ static int fusb302_probe(struct i2c_client *client,
|
||||
goto tcpm_unregister_port;
|
||||
}
|
||||
enable_irq_wake(chip->gpio_int_n_irq);
|
||||
fusb302_debugfs_init(chip);
|
||||
i2c_set_clientdata(client, chip);
|
||||
|
||||
return ret;
|
||||
|
||||
tcpm_unregister_port:
|
||||
tcpm_unregister_port(chip->tcpm_port);
|
||||
destroy_workqueue:
|
||||
destroy_workqueue(chip->wq);
|
||||
clear_client_data:
|
||||
i2c_set_clientdata(client, NULL);
|
||||
fusb302_debugfs_exit(chip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1816,7 +1826,6 @@ static int fusb302_remove(struct i2c_client *client)
|
||||
|
||||
tcpm_unregister_port(chip->tcpm_port);
|
||||
destroy_workqueue(chip->wq);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
fusb302_debugfs_exit(chip);
|
||||
|
||||
return 0;
|
@@ -1430,8 +1430,8 @@ static enum pdo_err tcpm_caps_err(struct tcpm_port *port, const u32 *pdo,
|
||||
if (pdo_apdo_type(pdo[i]) != APDO_TYPE_PPS)
|
||||
break;
|
||||
|
||||
if (pdo_pps_apdo_max_current(pdo[i]) <
|
||||
pdo_pps_apdo_max_current(pdo[i - 1]))
|
||||
if (pdo_pps_apdo_max_voltage(pdo[i]) <
|
||||
pdo_pps_apdo_max_voltage(pdo[i - 1]))
|
||||
return PDO_ERR_PPS_APDO_NOT_SORTED;
|
||||
else if (pdo_pps_apdo_min_voltage(pdo[i]) ==
|
||||
pdo_pps_apdo_min_voltage(pdo[i - 1]) &&
|
||||
@@ -2209,7 +2209,7 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port)
|
||||
{
|
||||
unsigned int i, j, max_mw = 0, max_mv = 0;
|
||||
unsigned int min_src_mv, max_src_mv, src_ma, src_mw;
|
||||
unsigned int min_snk_mv, max_snk_mv, snk_ma;
|
||||
unsigned int min_snk_mv, max_snk_mv;
|
||||
u32 pdo;
|
||||
unsigned int src_pdo = 0, snk_pdo = 0;
|
||||
|
||||
@@ -2253,8 +2253,6 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port)
|
||||
pdo_pps_apdo_min_voltage(pdo);
|
||||
max_snk_mv =
|
||||
pdo_pps_apdo_max_voltage(pdo);
|
||||
snk_ma =
|
||||
pdo_pps_apdo_max_current(pdo);
|
||||
break;
|
||||
default:
|
||||
tcpm_log(port,
|
||||
@@ -2402,7 +2400,7 @@ static int tcpm_pd_send_request(struct tcpm_port *port)
|
||||
|
||||
static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo)
|
||||
{
|
||||
unsigned int out_mv, op_ma, op_mw, min_mv, max_mv, max_ma, flags;
|
||||
unsigned int out_mv, op_ma, op_mw, max_mv, max_ma, flags;
|
||||
enum pd_pdo_type type;
|
||||
unsigned int src_pdo_index;
|
||||
u32 pdo;
|
||||
@@ -2420,7 +2418,6 @@ static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo)
|
||||
tcpm_log(port, "Invalid APDO selected!");
|
||||
return -EINVAL;
|
||||
}
|
||||
min_mv = port->pps_data.min_volt;
|
||||
max_mv = port->pps_data.max_volt;
|
||||
max_ma = port->pps_data.max_curr;
|
||||
out_mv = port->pps_data.out_volt;
|
||||
@@ -4116,6 +4113,9 @@ static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 op_curr)
|
||||
goto port_unlock;
|
||||
}
|
||||
|
||||
/* Round down operating current to align with PPS valid steps */
|
||||
op_curr = op_curr - (op_curr % RDO_PROG_CURR_MA_STEP);
|
||||
|
||||
reinit_completion(&port->pps_complete);
|
||||
port->pps_data.op_curr = op_curr;
|
||||
port->pps_status = 0;
|
||||
@@ -4169,6 +4169,9 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 out_volt)
|
||||
goto port_unlock;
|
||||
}
|
||||
|
||||
/* Round down output voltage to align with PPS valid steps */
|
||||
out_volt = out_volt - (out_volt % RDO_PROG_VOLT_MV_STEP);
|
||||
|
||||
reinit_completion(&port->pps_complete);
|
||||
port->pps_data.out_volt = out_volt;
|
||||
port->pps_status = 0;
|
@@ -73,6 +73,10 @@ static int __init init(void)
|
||||
cleanup:
|
||||
list_for_each_entry_safe(udc_dev, udc_dev2, &vudc_devices, dev_entry) {
|
||||
list_del(&udc_dev->dev_entry);
|
||||
/*
|
||||
* Just do platform_device_del() here, put_vudc_device()
|
||||
* calls the platform_device_put()
|
||||
*/
|
||||
platform_device_del(udc_dev->pdev);
|
||||
put_vudc_device(udc_dev);
|
||||
}
|
||||
@@ -89,7 +93,11 @@ static void __exit cleanup(void)
|
||||
|
||||
list_for_each_entry_safe(udc_dev, udc_dev2, &vudc_devices, dev_entry) {
|
||||
list_del(&udc_dev->dev_entry);
|
||||
platform_device_unregister(udc_dev->pdev);
|
||||
/*
|
||||
* Just do platform_device_del() here, put_vudc_device()
|
||||
* calls the platform_device_put()
|
||||
*/
|
||||
platform_device_del(udc_dev->pdev);
|
||||
put_vudc_device(udc_dev);
|
||||
}
|
||||
platform_driver_unregister(&vudc_driver);
|
||||
|
@@ -470,9 +470,7 @@ error:
|
||||
int wa_rpipes_create(struct wahc *wa)
|
||||
{
|
||||
wa->rpipes = le16_to_cpu(wa->wa_descr->wNumRPipes);
|
||||
wa->rpipe_bm = kcalloc(BITS_TO_LONGS(wa->rpipes),
|
||||
sizeof(unsigned long),
|
||||
GFP_KERNEL);
|
||||
wa->rpipe_bm = bitmap_zalloc(wa->rpipes, GFP_KERNEL);
|
||||
if (wa->rpipe_bm == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
@@ -487,7 +485,7 @@ void wa_rpipes_destroy(struct wahc *wa)
|
||||
dev_err(dev, "BUG: pipes not released on exit: %*pb\n",
|
||||
wa->rpipes, wa->rpipe_bm);
|
||||
}
|
||||
kfree(wa->rpipe_bm);
|
||||
bitmap_free(wa->rpipe_bm);
|
||||
}
|
||||
|
||||
/*
|
||||
|
新增問題並參考
封鎖使用者