提交 530b7a3e 编写于 作者: G Greg Kroah-Hartman

Merge tag 'usb-ci-v4.11-rc1' of...

Merge tag 'usb-ci-v4.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb into usb-next

Peter writes:

Hi Greg,

In this series, it adds qualcomm USB2 support. The review process takes
more than half of year, thanks for Stephen Boyd's great work.

Most of patches at linux-next more than ten days, and the last two small
chipidea patches at my tree about one day, no warning is reported from
autobuild robot.

Thanks.
ULPI bus binding
----------------
Phys that are behind a ULPI connection can be described with the following
binding. The host controller shall have a "ulpi" named node as a child, and
that node shall have one enabled node underneath it representing the ulpi
device on the bus.
EXAMPLE
-------
usb {
compatible = "vendor,usb-controller";
ulpi {
phy {
compatible = "vendor,phy";
};
};
};
...@@ -225,6 +225,30 @@ ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len) ...@@ -225,6 +225,30 @@ ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len)
return tsize; return tsize;
} }
EXPORT_SYMBOL_GPL(of_device_get_modalias);
int of_device_request_module(struct device *dev)
{
char *str;
ssize_t size;
int ret;
size = of_device_get_modalias(dev, NULL, 0);
if (size < 0)
return size;
str = kmalloc(size + 1, GFP_KERNEL);
if (!str)
return -ENOMEM;
of_device_get_modalias(dev, str, size);
str[size] = '\0';
ret = request_module(str);
kfree(str);
return ret;
}
EXPORT_SYMBOL_GPL(of_device_request_module);
/** /**
* of_device_uevent - Display OF related uevent information * of_device_uevent - Display OF related uevent information
...@@ -287,3 +311,4 @@ int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) ...@@ -287,3 +311,4 @@ int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(of_device_uevent_modalias);
...@@ -2,6 +2,7 @@ config USB_CHIPIDEA ...@@ -2,6 +2,7 @@ config USB_CHIPIDEA
tristate "ChipIdea Highspeed Dual Role Controller" tristate "ChipIdea Highspeed Dual Role Controller"
depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA
select EXTCON select EXTCON
select RESET_CONTROLLER
help help
Say Y here if your system has a dual role high speed USB Say Y here if your system has a dual role high speed USB
controller based on ChipIdea silicon IP. It supports: controller based on ChipIdea silicon IP. It supports:
...@@ -38,4 +39,11 @@ config USB_CHIPIDEA_HOST ...@@ -38,4 +39,11 @@ config USB_CHIPIDEA_HOST
Say Y here to enable host controller functionality of the Say Y here to enable host controller functionality of the
ChipIdea driver. ChipIdea driver.
config USB_CHIPIDEA_ULPI
bool "ChipIdea ULPI PHY support"
depends on USB_ULPI_BUS=y || USB_ULPI_BUS=USB_CHIPIDEA
help
Say Y here if you have a ULPI PHY attached to your ChipIdea
controller.
endif endif
...@@ -4,6 +4,7 @@ ci_hdrc-y := core.o otg.o debug.o ...@@ -4,6 +4,7 @@ ci_hdrc-y := core.o otg.o debug.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_ULPI) += ulpi.o
# Glue/Bridge layers go here # Glue/Bridge layers go here
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/usb/otg-fsm.h> #include <linux/usb/otg-fsm.h>
#include <linux/usb/otg.h>
#include <linux/ulpi/interface.h>
/****************************************************************************** /******************************************************************************
* DEFINE * DEFINE
...@@ -52,6 +54,7 @@ enum ci_hw_regs { ...@@ -52,6 +54,7 @@ enum ci_hw_regs {
OP_ENDPTLISTADDR, OP_ENDPTLISTADDR,
OP_TTCTRL, OP_TTCTRL,
OP_BURSTSIZE, OP_BURSTSIZE,
OP_ULPI_VIEWPORT,
OP_PORTSC, OP_PORTSC,
OP_DEVLC, OP_DEVLC,
OP_OTGSC, OP_OTGSC,
...@@ -187,6 +190,8 @@ struct hw_bank { ...@@ -187,6 +190,8 @@ struct hw_bank {
* @test_mode: the selected test mode * @test_mode: the selected test mode
* @platdata: platform specific information supplied by parent device * @platdata: platform specific information supplied by parent device
* @vbus_active: is VBUS active * @vbus_active: is VBUS active
* @ulpi: pointer to ULPI device, if any
* @ulpi_ops: ULPI read/write ops for this device
* @phy: pointer to PHY, if any * @phy: pointer to PHY, if any
* @usb_phy: pointer to USB PHY, if any and if using the USB PHY framework * @usb_phy: pointer to USB PHY, if any and if using the USB PHY framework
* @hcd: pointer to usb_hcd for ehci host driver * @hcd: pointer to usb_hcd for ehci host driver
...@@ -236,6 +241,10 @@ struct ci_hdrc { ...@@ -236,6 +241,10 @@ struct ci_hdrc {
struct ci_hdrc_platform_data *platdata; struct ci_hdrc_platform_data *platdata;
int vbus_active; int vbus_active;
#ifdef CONFIG_USB_CHIPIDEA_ULPI
struct ulpi *ulpi;
struct ulpi_ops ulpi_ops;
#endif
struct phy *phy; struct phy *phy;
/* old usb_phy interface */ /* old usb_phy interface */
struct usb_phy *usb_phy; struct usb_phy *usb_phy;
...@@ -418,6 +427,16 @@ static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci) ...@@ -418,6 +427,16 @@ static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci)
#endif #endif
} }
#if IS_ENABLED(CONFIG_USB_CHIPIDEA_ULPI)
int ci_ulpi_init(struct ci_hdrc *ci);
void ci_ulpi_exit(struct ci_hdrc *ci);
int ci_ulpi_resume(struct ci_hdrc *ci);
#else
static inline int ci_ulpi_init(struct ci_hdrc *ci) { return 0; }
static inline void ci_ulpi_exit(struct ci_hdrc *ci) { }
static inline int ci_ulpi_resume(struct ci_hdrc *ci) { return 0; }
#endif
u32 hw_read_intr_enable(struct ci_hdrc *ci); u32 hw_read_intr_enable(struct ci_hdrc *ci);
u32 hw_read_intr_status(struct ci_hdrc *ci); u32 hw_read_intr_status(struct ci_hdrc *ci);
...@@ -428,8 +447,7 @@ int hw_port_test_set(struct ci_hdrc *ci, u8 mode); ...@@ -428,8 +447,7 @@ int hw_port_test_set(struct ci_hdrc *ci, u8 mode);
u8 hw_port_test_get(struct ci_hdrc *ci); u8 hw_port_test_get(struct ci_hdrc *ci);
int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask, void hw_phymode_configure(struct ci_hdrc *ci);
u32 value, unsigned int timeout_ms);
void ci_platform_configure(struct ci_hdrc *ci); void ci_platform_configure(struct ci_hdrc *ci);
......
...@@ -8,90 +8,292 @@ ...@@ -8,90 +8,292 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/usb/msm_hsusb_hw.h>
#include <linux/usb/ulpi.h>
#include <linux/usb/gadget.h>
#include <linux/usb/chipidea.h> #include <linux/usb/chipidea.h>
#include <linux/clk.h>
#include <linux/reset.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/io.h>
#include <linux/reset-controller.h>
#include <linux/extcon.h>
#include <linux/of.h>
#include "ci.h" #include "ci.h"
#define MSM_USB_BASE (ci->hw_bank.abs) #define HS_PHY_AHB_MODE 0x0098
static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event) #define HS_PHY_GENCONFIG 0x009c
#define HS_PHY_TXFIFO_IDLE_FORCE_DIS BIT(4)
#define HS_PHY_GENCONFIG_2 0x00a0
#define HS_PHY_SESS_VLD_CTRL_EN BIT(7)
#define HS_PHY_ULPI_TX_PKT_EN_CLR_FIX BIT(19)
#define HSPHY_SESS_VLD_CTRL BIT(25)
/* Vendor base starts at 0x200 beyond CI base */
#define HS_PHY_CTRL 0x0040
#define HS_PHY_SEC_CTRL 0x0078
#define HS_PHY_DIG_CLAMP_N BIT(16)
#define HS_PHY_POR_ASSERT BIT(0)
struct ci_hdrc_msm {
struct platform_device *ci;
struct clk *core_clk;
struct clk *iface_clk;
struct clk *fs_clk;
struct ci_hdrc_platform_data pdata;
struct reset_controller_dev rcdev;
bool secondary_phy;
bool hsic;
void __iomem *base;
};
static int
ci_hdrc_msm_por_reset(struct reset_controller_dev *r, unsigned long id)
{
struct ci_hdrc_msm *ci_msm = container_of(r, struct ci_hdrc_msm, rcdev);
void __iomem *addr = ci_msm->base;
u32 val;
if (id)
addr += HS_PHY_SEC_CTRL;
else
addr += HS_PHY_CTRL;
val = readl_relaxed(addr);
val |= HS_PHY_POR_ASSERT;
writel(val, addr);
/*
* wait for minimum 10 microseconds as suggested by manual.
* Use a slightly larger value since the exact value didn't
* work 100% of the time.
*/
udelay(12);
val &= ~HS_PHY_POR_ASSERT;
writel(val, addr);
return 0;
}
static const struct reset_control_ops ci_hdrc_msm_reset_ops = {
.reset = ci_hdrc_msm_por_reset,
};
static int ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
{ {
struct device *dev = ci->gadget.dev.parent; struct device *dev = ci->dev->parent;
struct ci_hdrc_msm *msm_ci = dev_get_drvdata(dev);
int ret;
switch (event) { switch (event) {
case CI_HDRC_CONTROLLER_RESET_EVENT: case CI_HDRC_CONTROLLER_RESET_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n"); dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
writel(0, USB_AHBBURST);
hw_phymode_configure(ci);
if (msm_ci->secondary_phy) {
u32 val = readl_relaxed(msm_ci->base + HS_PHY_SEC_CTRL);
val |= HS_PHY_DIG_CLAMP_N;
writel_relaxed(val, msm_ci->base + HS_PHY_SEC_CTRL);
}
ret = phy_init(ci->phy);
if (ret)
return ret;
ret = phy_power_on(ci->phy);
if (ret) {
phy_exit(ci->phy);
return ret;
}
/* use AHB transactor, allow posted data writes */ /* use AHB transactor, allow posted data writes */
writel(0x8, USB_AHBMODE); hw_write_id_reg(ci, HS_PHY_AHB_MODE, 0xffffffff, 0x8);
usb_phy_init(ci->usb_phy);
/* workaround for rx buffer collision issue */
hw_write_id_reg(ci, HS_PHY_GENCONFIG,
HS_PHY_TXFIFO_IDLE_FORCE_DIS, 0);
if (!msm_ci->hsic)
hw_write_id_reg(ci, HS_PHY_GENCONFIG_2,
HS_PHY_ULPI_TX_PKT_EN_CLR_FIX, 0);
if (!IS_ERR(ci->platdata->vbus_extcon.edev)) {
hw_write_id_reg(ci, HS_PHY_GENCONFIG_2,
HS_PHY_SESS_VLD_CTRL_EN,
HS_PHY_SESS_VLD_CTRL_EN);
hw_write(ci, OP_USBCMD, HSPHY_SESS_VLD_CTRL,
HSPHY_SESS_VLD_CTRL);
}
break; break;
case CI_HDRC_CONTROLLER_STOPPED_EVENT: case CI_HDRC_CONTROLLER_STOPPED_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n"); dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n");
/* phy_power_off(ci->phy);
* Put the phy in non-driving mode. Otherwise host phy_exit(ci->phy);
* may not detect soft-disconnection.
*/
usb_phy_notify_disconnect(ci->usb_phy, USB_SPEED_UNKNOWN);
break; break;
default: default:
dev_dbg(dev, "unknown ci_hdrc event\n"); dev_dbg(dev, "unknown ci_hdrc event\n");
break; break;
} }
return 0;
} }
static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = { static int ci_hdrc_msm_mux_phy(struct ci_hdrc_msm *ci,
.name = "ci_hdrc_msm", struct platform_device *pdev)
.capoffset = DEF_CAPOFFSET, {
.flags = CI_HDRC_REGS_SHARED | struct regmap *regmap;
CI_HDRC_DISABLE_STREAMING, struct device *dev = &pdev->dev;
struct of_phandle_args args;
u32 val;
int ret;
.notify_event = ci_hdrc_msm_notify_event, ret = of_parse_phandle_with_fixed_args(dev->of_node, "phy-select", 2, 0,
}; &args);
if (ret)
return 0;
regmap = syscon_node_to_regmap(args.np);
of_node_put(args.np);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
ret = regmap_write(regmap, args.args[0], args.args[1]);
if (ret)
return ret;
ci->secondary_phy = !!args.args[1];
if (ci->secondary_phy) {
val = readl_relaxed(ci->base + HS_PHY_SEC_CTRL);
val |= HS_PHY_DIG_CLAMP_N;
writel_relaxed(val, ci->base + HS_PHY_SEC_CTRL);
}
return 0;
}
static int ci_hdrc_msm_probe(struct platform_device *pdev) static int ci_hdrc_msm_probe(struct platform_device *pdev)
{ {
struct ci_hdrc_msm *ci;
struct platform_device *plat_ci; struct platform_device *plat_ci;
struct usb_phy *phy; struct clk *clk;
struct reset_control *reset;
struct resource *res;
int ret;
struct device_node *ulpi_node, *phy_node;
dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n"); dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n");
/* ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
* OTG(PHY) driver takes care of PHY initialization, clock management, if (!ci)
* powering up VBUS, mapping of registers address space and power return -ENOMEM;
* management. platform_set_drvdata(pdev, ci);
*/
phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0); ci->pdata.name = "ci_hdrc_msm";
if (IS_ERR(phy)) ci->pdata.capoffset = DEF_CAPOFFSET;
return PTR_ERR(phy); ci->pdata.flags = CI_HDRC_REGS_SHARED | CI_HDRC_DISABLE_STREAMING |
CI_HDRC_OVERRIDE_AHB_BURST |
CI_HDRC_OVERRIDE_PHY_CONTROL;
ci->pdata.notify_event = ci_hdrc_msm_notify_event;
ci_hdrc_msm_platdata.usb_phy = phy; reset = devm_reset_control_get(&pdev->dev, "core");
if (IS_ERR(reset))
return PTR_ERR(reset);
plat_ci = ci_hdrc_add_device(&pdev->dev, ci->core_clk = clk = devm_clk_get(&pdev->dev, "core");
pdev->resource, pdev->num_resources, if (IS_ERR(clk))
&ci_hdrc_msm_platdata); return PTR_ERR(clk);
ci->iface_clk = clk = devm_clk_get(&pdev->dev, "iface");
if (IS_ERR(clk))
return PTR_ERR(clk);
ci->fs_clk = clk = devm_clk_get(&pdev->dev, "fs");
if (IS_ERR(clk)) {
if (PTR_ERR(clk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
ci->fs_clk = NULL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
ci->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ci->base))
return PTR_ERR(ci->base);
ci->rcdev.owner = THIS_MODULE;
ci->rcdev.ops = &ci_hdrc_msm_reset_ops;
ci->rcdev.of_node = pdev->dev.of_node;
ci->rcdev.nr_resets = 2;
ret = reset_controller_register(&ci->rcdev);
if (ret)
return ret;
ret = clk_prepare_enable(ci->fs_clk);
if (ret)
goto err_fs;
reset_control_assert(reset);
usleep_range(10000, 12000);
reset_control_deassert(reset);
clk_disable_unprepare(ci->fs_clk);
ret = clk_prepare_enable(ci->core_clk);
if (ret)
goto err_fs;
ret = clk_prepare_enable(ci->iface_clk);
if (ret)
goto err_iface;
ret = ci_hdrc_msm_mux_phy(ci, pdev);
if (ret)
goto err_mux;
ulpi_node = of_find_node_by_name(pdev->dev.of_node, "ulpi");
if (ulpi_node) {
phy_node = of_get_next_available_child(ulpi_node, NULL);
ci->hsic = of_device_is_compatible(phy_node, "qcom,usb-hsic-phy");
of_node_put(phy_node);
}
of_node_put(ulpi_node);
plat_ci = ci_hdrc_add_device(&pdev->dev, pdev->resource,
pdev->num_resources, &ci->pdata);
if (IS_ERR(plat_ci)) { if (IS_ERR(plat_ci)) {
ret = PTR_ERR(plat_ci);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n"); dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
return PTR_ERR(plat_ci); goto err_mux;
} }
platform_set_drvdata(pdev, plat_ci); ci->ci = plat_ci;
pm_runtime_set_active(&pdev->dev);
pm_runtime_no_callbacks(&pdev->dev); pm_runtime_no_callbacks(&pdev->dev);
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
return 0; return 0;
err_mux:
clk_disable_unprepare(ci->iface_clk);
err_iface:
clk_disable_unprepare(ci->core_clk);
err_fs:
reset_controller_unregister(&ci->rcdev);
return ret;
} }
static int ci_hdrc_msm_remove(struct platform_device *pdev) static int ci_hdrc_msm_remove(struct platform_device *pdev)
{ {
struct platform_device *plat_ci = platform_get_drvdata(pdev); struct ci_hdrc_msm *ci = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
ci_hdrc_remove_device(plat_ci); ci_hdrc_remove_device(ci->ci);
clk_disable_unprepare(ci->iface_clk);
clk_disable_unprepare(ci->core_clk);
reset_controller_unregister(&ci->rcdev);
return 0; return 0;
} }
......
...@@ -74,10 +74,6 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev) ...@@ -74,10 +74,6 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev)
} }
} }
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret)
goto clk_err;
ci_pdata->name = dev_name(dev); ci_pdata->name = dev_name(dev);
priv->ci_pdev = ci_hdrc_add_device(dev, pdev->resource, priv->ci_pdev = ci_hdrc_add_device(dev, pdev->resource,
......
...@@ -62,7 +62,6 @@ ...@@ -62,7 +62,6 @@
#include <linux/usb/chipidea.h> #include <linux/usb/chipidea.h>
#include <linux/usb/of.h> #include <linux/usb/of.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/phy.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/usb/ehci_def.h> #include <linux/usb/ehci_def.h>
...@@ -86,6 +85,7 @@ static const u8 ci_regs_nolpm[] = { ...@@ -86,6 +85,7 @@ static const u8 ci_regs_nolpm[] = {
[OP_ENDPTLISTADDR] = 0x18U, [OP_ENDPTLISTADDR] = 0x18U,
[OP_TTCTRL] = 0x1CU, [OP_TTCTRL] = 0x1CU,
[OP_BURSTSIZE] = 0x20U, [OP_BURSTSIZE] = 0x20U,
[OP_ULPI_VIEWPORT] = 0x30U,
[OP_PORTSC] = 0x44U, [OP_PORTSC] = 0x44U,
[OP_DEVLC] = 0x84U, [OP_DEVLC] = 0x84U,
[OP_OTGSC] = 0x64U, [OP_OTGSC] = 0x64U,
...@@ -110,6 +110,7 @@ static const u8 ci_regs_lpm[] = { ...@@ -110,6 +110,7 @@ static const u8 ci_regs_lpm[] = {
[OP_ENDPTLISTADDR] = 0x18U, [OP_ENDPTLISTADDR] = 0x18U,
[OP_TTCTRL] = 0x1CU, [OP_TTCTRL] = 0x1CU,
[OP_BURSTSIZE] = 0x20U, [OP_BURSTSIZE] = 0x20U,
[OP_ULPI_VIEWPORT] = 0x30U,
[OP_PORTSC] = 0x44U, [OP_PORTSC] = 0x44U,
[OP_DEVLC] = 0x84U, [OP_DEVLC] = 0x84U,
[OP_OTGSC] = 0xC4U, [OP_OTGSC] = 0xC4U,
...@@ -285,7 +286,7 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) ...@@ -285,7 +286,7 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
return 0; return 0;
} }
static void hw_phymode_configure(struct ci_hdrc *ci) void hw_phymode_configure(struct ci_hdrc *ci)
{ {
u32 portsc, lpm, sts = 0; u32 portsc, lpm, sts = 0;
...@@ -325,6 +326,7 @@ static void hw_phymode_configure(struct ci_hdrc *ci) ...@@ -325,6 +326,7 @@ static void hw_phymode_configure(struct ci_hdrc *ci)
hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS); hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS);
} }
} }
EXPORT_SYMBOL_GPL(hw_phymode_configure);
/** /**
* _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy * _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy
...@@ -361,6 +363,9 @@ static int _ci_usb_phy_init(struct ci_hdrc *ci) ...@@ -361,6 +363,9 @@ static int _ci_usb_phy_init(struct ci_hdrc *ci)
*/ */
static void ci_usb_phy_exit(struct ci_hdrc *ci) static void ci_usb_phy_exit(struct ci_hdrc *ci)
{ {
if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL)
return;
if (ci->phy) { if (ci->phy) {
phy_power_off(ci->phy); phy_power_off(ci->phy);
phy_exit(ci->phy); phy_exit(ci->phy);
...@@ -379,6 +384,9 @@ static int ci_usb_phy_init(struct ci_hdrc *ci) ...@@ -379,6 +384,9 @@ static int ci_usb_phy_init(struct ci_hdrc *ci)
{ {
int ret; int ret;
if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL)
return 0;
switch (ci->platdata->phy_mode) { switch (ci->platdata->phy_mode) {
case USBPHY_INTERFACE_MODE_UTMI: case USBPHY_INTERFACE_MODE_UTMI:
case USBPHY_INTERFACE_MODE_UTMIW: case USBPHY_INTERFACE_MODE_UTMIW:
...@@ -419,13 +427,21 @@ void ci_platform_configure(struct ci_hdrc *ci) ...@@ -419,13 +427,21 @@ void ci_platform_configure(struct ci_hdrc *ci)
is_device_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_DC; is_device_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_DC;
is_host_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_HC; is_host_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_HC;
if (is_device_mode && if (is_device_mode) {
(ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING)) phy_set_mode(ci->phy, PHY_MODE_USB_DEVICE);
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
if (is_host_mode && if (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING)
(ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING)) hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); USBMODE_CI_SDIS);
}
if (is_host_mode) {
phy_set_mode(ci->phy, PHY_MODE_USB_HOST);
if (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING)
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
USBMODE_CI_SDIS);
}
if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) { if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) {
if (ci->hw_bank.lpm) if (ci->hw_bank.lpm)
...@@ -495,9 +511,12 @@ int hw_device_reset(struct ci_hdrc *ci) ...@@ -495,9 +511,12 @@ int hw_device_reset(struct ci_hdrc *ci)
return ret; return ret;
} }
if (ci->platdata->notify_event) if (ci->platdata->notify_event) {
ci->platdata->notify_event(ci, ret = ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_RESET_EVENT); CI_HDRC_CONTROLLER_RESET_EVENT);
if (ret)
return ret;
}
/* USBMODE should be configured step by step */ /* USBMODE should be configured step by step */
hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
...@@ -516,38 +535,6 @@ int hw_device_reset(struct ci_hdrc *ci) ...@@ -516,38 +535,6 @@ int hw_device_reset(struct ci_hdrc *ci)
return 0; return 0;
} }
/**
* hw_wait_reg: wait the register value
*
* Sometimes, it needs to wait register value before going on.
* Eg, when switch to device mode, the vbus value should be lower
* than OTGSC_BSV before connects to host.
*
* @ci: the controller
* @reg: register index
* @mask: mast bit
* @value: the bit value to wait
* @timeout_ms: timeout in millisecond
*
* This function returns an error code if timeout
*/
int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
u32 value, unsigned int timeout_ms)
{
unsigned long elapse = jiffies + msecs_to_jiffies(timeout_ms);
while (hw_read(ci, reg, mask) != value) {
if (time_after(jiffies, elapse)) {
dev_err(ci->dev, "timeout waiting for %08x in %d\n",
mask, reg);
return -ETIMEDOUT;
}
msleep(20);
}
return 0;
}
static irqreturn_t ci_irq(int irq, void *data) static irqreturn_t ci_irq(int irq, void *data)
{ {
struct ci_hdrc *ci = data; struct ci_hdrc *ci = data;
...@@ -601,35 +588,14 @@ static irqreturn_t ci_irq(int irq, void *data) ...@@ -601,35 +588,14 @@ static irqreturn_t ci_irq(int irq, void *data)
return ret; return ret;
} }
static int ci_vbus_notifier(struct notifier_block *nb, unsigned long event, static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
void *ptr) void *ptr)
{ {
struct ci_hdrc_cable *vbus = container_of(nb, struct ci_hdrc_cable, nb); struct ci_hdrc_cable *cbl = container_of(nb, struct ci_hdrc_cable, nb);
struct ci_hdrc *ci = vbus->ci; struct ci_hdrc *ci = cbl->ci;
if (event) cbl->connected = event;
vbus->state = true; cbl->changed = true;
else
vbus->state = false;
vbus->changed = true;
ci_irq(ci->irq, ci);
return NOTIFY_DONE;
}
static int ci_id_notifier(struct notifier_block *nb, unsigned long event,
void *ptr)
{
struct ci_hdrc_cable *id = container_of(nb, struct ci_hdrc_cable, nb);
struct ci_hdrc *ci = id->ci;
if (event)
id->state = false;
else
id->state = true;
id->changed = true;
ci_irq(ci->irq, ci); ci_irq(ci->irq, ci);
return NOTIFY_DONE; return NOTIFY_DONE;
...@@ -738,27 +704,27 @@ static int ci_get_platdata(struct device *dev, ...@@ -738,27 +704,27 @@ static int ci_get_platdata(struct device *dev,
} }
cable = &platdata->vbus_extcon; cable = &platdata->vbus_extcon;
cable->nb.notifier_call = ci_vbus_notifier; cable->nb.notifier_call = ci_cable_notifier;
cable->edev = ext_vbus; cable->edev = ext_vbus;
if (!IS_ERR(ext_vbus)) { if (!IS_ERR(ext_vbus)) {
ret = extcon_get_cable_state_(cable->edev, EXTCON_USB); ret = extcon_get_state(cable->edev, EXTCON_USB);
if (ret) if (ret)
cable->state = true; cable->connected = true;
else else
cable->state = false; cable->connected = false;
} }
cable = &platdata->id_extcon; cable = &platdata->id_extcon;
cable->nb.notifier_call = ci_id_notifier; cable->nb.notifier_call = ci_cable_notifier;
cable->edev = ext_id; cable->edev = ext_id;
if (!IS_ERR(ext_id)) { if (!IS_ERR(ext_id)) {
ret = extcon_get_cable_state_(cable->edev, EXTCON_USB_HOST); ret = extcon_get_state(cable->edev, EXTCON_USB_HOST);
if (ret) if (ret)
cable->state = false; cable->connected = true;
else else
cable->state = true; cable->connected = false;
} }
return 0; return 0;
} }
...@@ -771,8 +737,8 @@ static int ci_extcon_register(struct ci_hdrc *ci) ...@@ -771,8 +737,8 @@ static int ci_extcon_register(struct ci_hdrc *ci)
id = &ci->platdata->id_extcon; id = &ci->platdata->id_extcon;
id->ci = ci; id->ci = ci;
if (!IS_ERR(id->edev)) { if (!IS_ERR(id->edev)) {
ret = extcon_register_notifier(id->edev, EXTCON_USB_HOST, ret = devm_extcon_register_notifier(ci->dev, id->edev,
&id->nb); EXTCON_USB_HOST, &id->nb);
if (ret < 0) { if (ret < 0) {
dev_err(ci->dev, "register ID failed\n"); dev_err(ci->dev, "register ID failed\n");
return ret; return ret;
...@@ -782,11 +748,9 @@ static int ci_extcon_register(struct ci_hdrc *ci) ...@@ -782,11 +748,9 @@ static int ci_extcon_register(struct ci_hdrc *ci)
vbus = &ci->platdata->vbus_extcon; vbus = &ci->platdata->vbus_extcon;
vbus->ci = ci; vbus->ci = ci;
if (!IS_ERR(vbus->edev)) { if (!IS_ERR(vbus->edev)) {
ret = extcon_register_notifier(vbus->edev, EXTCON_USB, ret = devm_extcon_register_notifier(ci->dev, vbus->edev,
&vbus->nb); EXTCON_USB, &vbus->nb);
if (ret < 0) { if (ret < 0) {
extcon_unregister_notifier(id->edev, EXTCON_USB_HOST,
&id->nb);
dev_err(ci->dev, "register VBUS failed\n"); dev_err(ci->dev, "register VBUS failed\n");
return ret; return ret;
} }
...@@ -795,20 +759,6 @@ static int ci_extcon_register(struct ci_hdrc *ci) ...@@ -795,20 +759,6 @@ static int ci_extcon_register(struct ci_hdrc *ci)
return 0; return 0;
} }
static void ci_extcon_unregister(struct ci_hdrc *ci)
{
struct ci_hdrc_cable *cable;
cable = &ci->platdata->id_extcon;
if (!IS_ERR(cable->edev))
extcon_unregister_notifier(cable->edev, EXTCON_USB_HOST,
&cable->nb);
cable = &ci->platdata->vbus_extcon;
if (!IS_ERR(cable->edev))
extcon_unregister_notifier(cable->edev, EXTCON_USB, &cable->nb);
}
static DEFINE_IDA(ci_ida); static DEFINE_IDA(ci_ida);
struct platform_device *ci_hdrc_add_device(struct device *dev, struct platform_device *ci_hdrc_add_device(struct device *dev,
...@@ -921,6 +871,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -921,6 +871,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
CI_HDRC_IMX28_WRITE_FIX); CI_HDRC_IMX28_WRITE_FIX);
ci->supports_runtime_pm = !!(ci->platdata->flags & ci->supports_runtime_pm = !!(ci->platdata->flags &
CI_HDRC_SUPPORTS_RUNTIME_PM); CI_HDRC_SUPPORTS_RUNTIME_PM);
platform_set_drvdata(pdev, ci);
ret = hw_device_init(ci, base); ret = hw_device_init(ci, base);
if (ret < 0) { if (ret < 0) {
...@@ -928,6 +879,10 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -928,6 +879,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
} }
ret = ci_ulpi_init(ci);
if (ret)
return ret;
if (ci->platdata->phy) { if (ci->platdata->phy) {
ci->phy = ci->platdata->phy; ci->phy = ci->platdata->phy;
} else if (ci->platdata->usb_phy) { } else if (ci->platdata->usb_phy) {
...@@ -938,11 +893,15 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -938,11 +893,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
/* if both generic PHY and USB PHY layers aren't enabled */ /* if both generic PHY and USB PHY layers aren't enabled */
if (PTR_ERR(ci->phy) == -ENOSYS && if (PTR_ERR(ci->phy) == -ENOSYS &&
PTR_ERR(ci->usb_phy) == -ENXIO) PTR_ERR(ci->usb_phy) == -ENXIO) {
return -ENXIO; ret = -ENXIO;
goto ulpi_exit;
}
if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) {
return -EPROBE_DEFER; ret = -EPROBE_DEFER;
goto ulpi_exit;
}
if (IS_ERR(ci->phy)) if (IS_ERR(ci->phy))
ci->phy = NULL; ci->phy = NULL;
...@@ -1027,7 +986,6 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -1027,7 +986,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
} }
} }
platform_set_drvdata(pdev, ci);
ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED, ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
ci->platdata->name, ci); ci->platdata->name, ci);
if (ret) if (ret)
...@@ -1054,11 +1012,12 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -1054,11 +1012,12 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (!ret) if (!ret)
return 0; return 0;
ci_extcon_unregister(ci);
stop: stop:
ci_role_destroy(ci); ci_role_destroy(ci);
deinit_phy: deinit_phy:
ci_usb_phy_exit(ci); ci_usb_phy_exit(ci);
ulpi_exit:
ci_ulpi_exit(ci);
return ret; return ret;
} }
...@@ -1074,10 +1033,10 @@ static int ci_hdrc_remove(struct platform_device *pdev) ...@@ -1074,10 +1033,10 @@ static int ci_hdrc_remove(struct platform_device *pdev)
} }
dbg_remove_files(ci); dbg_remove_files(ci);
ci_extcon_unregister(ci);
ci_role_destroy(ci); ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true); ci_hdrc_enter_lpm(ci, true);
ci_usb_phy_exit(ci); ci_usb_phy_exit(ci);
ci_ulpi_exit(ci);
return 0; return 0;
} }
...@@ -1125,6 +1084,7 @@ static void ci_controller_suspend(struct ci_hdrc *ci) ...@@ -1125,6 +1084,7 @@ static void ci_controller_suspend(struct ci_hdrc *ci)
static int ci_controller_resume(struct device *dev) static int ci_controller_resume(struct device *dev)
{ {
struct ci_hdrc *ci = dev_get_drvdata(dev); struct ci_hdrc *ci = dev_get_drvdata(dev);
int ret;
dev_dbg(dev, "at %s\n", __func__); dev_dbg(dev, "at %s\n", __func__);
...@@ -1134,6 +1094,11 @@ static int ci_controller_resume(struct device *dev) ...@@ -1134,6 +1094,11 @@ static int ci_controller_resume(struct device *dev)
} }
ci_hdrc_enter_lpm(ci, false); ci_hdrc_enter_lpm(ci, false);
ret = ci_ulpi_resume(ci);
if (ret)
return ret;
if (ci->usb_phy) { if (ci->usb_phy) {
usb_phy_set_suspend(ci->usb_phy, 0); usb_phy_set_suspend(ci->usb_phy, 0);
usb_phy_set_wakeup(ci->usb_phy, false); usb_phy_set_wakeup(ci->usb_phy, false);
......
...@@ -90,6 +90,13 @@ static int ehci_ci_reset(struct usb_hcd *hcd) ...@@ -90,6 +90,13 @@ static int ehci_ci_reset(struct usb_hcd *hcd)
ehci->need_io_watchdog = 0; ehci->need_io_watchdog = 0;
if (ci->platdata->notify_event) {
ret = ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_RESET_EVENT);
if (ret)
return ret;
}
ci_platform_configure(ci); ci_platform_configure(ci);
return ret; return ret;
...@@ -187,6 +194,9 @@ static void host_stop(struct ci_hdrc *ci) ...@@ -187,6 +194,9 @@ static void host_stop(struct ci_hdrc *ci)
struct usb_hcd *hcd = ci->hcd; struct usb_hcd *hcd = ci->hcd;
if (hcd) { if (hcd) {
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_STOPPED_EVENT);
usb_remove_hcd(hcd); usb_remove_hcd(hcd);
ci->role = CI_ROLE_END; ci->role = CI_ROLE_END;
synchronize_irq(ci->irq); synchronize_irq(ci->irq);
......
...@@ -44,12 +44,15 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask) ...@@ -44,12 +44,15 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
else else
val &= ~OTGSC_BSVIS; val &= ~OTGSC_BSVIS;
cable->changed = false; if (cable->connected)
if (cable->state)
val |= OTGSC_BSV; val |= OTGSC_BSV;
else else
val &= ~OTGSC_BSV; val &= ~OTGSC_BSV;
if (cable->enabled)
val |= OTGSC_BSVIE;
else
val &= ~OTGSC_BSVIE;
} }
cable = &ci->platdata->id_extcon; cable = &ci->platdata->id_extcon;
...@@ -59,15 +62,18 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask) ...@@ -59,15 +62,18 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
else else
val &= ~OTGSC_IDIS; val &= ~OTGSC_IDIS;
cable->changed = false; if (cable->connected)
val &= ~OTGSC_ID; /* host */
else
val |= OTGSC_ID; /* device */
if (cable->state) if (cable->enabled)
val |= OTGSC_ID; val |= OTGSC_IDIE;
else else
val &= ~OTGSC_ID; val &= ~OTGSC_IDIE;
} }
return val; return val & mask;
} }
/** /**
...@@ -77,6 +83,36 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask) ...@@ -77,6 +83,36 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
*/ */
void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data) void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
{ {
struct ci_hdrc_cable *cable;
cable = &ci->platdata->vbus_extcon;
if (!IS_ERR(cable->edev)) {
if (data & mask & OTGSC_BSVIS)
cable->changed = false;
/* Don't enable vbus interrupt if using external notifier */
if (data & mask & OTGSC_BSVIE) {
cable->enabled = true;
data &= ~OTGSC_BSVIE;
} else if (mask & OTGSC_BSVIE) {
cable->enabled = false;
}
}
cable = &ci->platdata->id_extcon;
if (!IS_ERR(cable->edev)) {
if (data & mask & OTGSC_IDIS)
cable->changed = false;
/* Don't enable id interrupt if using external notifier */
if (data & mask & OTGSC_IDIE) {
cable->enabled = true;
data &= ~OTGSC_IDIE;
} else if (mask & OTGSC_IDIE) {
cable->enabled = false;
}
}
hw_write(ci, OP_OTGSC, mask | OTGSC_INT_STATUS_BITS, data); hw_write(ci, OP_OTGSC, mask | OTGSC_INT_STATUS_BITS, data);
} }
...@@ -98,13 +134,37 @@ void ci_handle_vbus_change(struct ci_hdrc *ci) ...@@ -98,13 +134,37 @@ void ci_handle_vbus_change(struct ci_hdrc *ci)
if (!ci->is_otg) if (!ci->is_otg)
return; return;
if (hw_read_otgsc(ci, OTGSC_BSV)) if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active)
usb_gadget_vbus_connect(&ci->gadget); usb_gadget_vbus_connect(&ci->gadget);
else else if (!hw_read_otgsc(ci, OTGSC_BSV) && ci->vbus_active)
usb_gadget_vbus_disconnect(&ci->gadget); usb_gadget_vbus_disconnect(&ci->gadget);
} }
#define CI_VBUS_STABLE_TIMEOUT_MS 5000 /**
* When we switch to device mode, the vbus value should be lower
* than OTGSC_BSV before connecting to host.
*
* @ci: the controller
*
* This function returns an error code if timeout
*/
static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci)
{
unsigned long elapse = jiffies + msecs_to_jiffies(5000);
u32 mask = OTGSC_BSV;
while (hw_read_otgsc(ci, mask)) {
if (time_after(jiffies, elapse)) {
dev_err(ci->dev, "timeout waiting for %08x in OTGSC\n",
mask);
return -ETIMEDOUT;
}
msleep(20);
}
return 0;
}
static void ci_handle_id_switch(struct ci_hdrc *ci) static void ci_handle_id_switch(struct ci_hdrc *ci)
{ {
enum ci_role role = ci_otg_role(ci); enum ci_role role = ci_otg_role(ci);
...@@ -115,12 +175,21 @@ static void ci_handle_id_switch(struct ci_hdrc *ci) ...@@ -115,12 +175,21 @@ static void ci_handle_id_switch(struct ci_hdrc *ci)
ci_role_stop(ci); ci_role_stop(ci);
if (role == CI_ROLE_GADGET) if (role == CI_ROLE_GADGET &&
/* wait vbus lower than OTGSC_BSV */ IS_ERR(ci->platdata->vbus_extcon.edev))
hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0, /*
CI_VBUS_STABLE_TIMEOUT_MS); * Wait vbus lower than OTGSC_BSV before connecting
* to host. If connecting status is from an external
* connector instead of register, we don't need to
* care vbus on the board, since it will not affect
* external connector status.
*/
hw_wait_vbus_lower_bsv(ci);
ci_role_start(ci, role); ci_role_start(ci, role);
/* vbus change may have already occurred */
if (role == CI_ROLE_GADGET)
ci_handle_vbus_change(ci);
} }
} }
/** /**
......
...@@ -1725,7 +1725,6 @@ static int ci_udc_start(struct usb_gadget *gadget, ...@@ -1725,7 +1725,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver) struct usb_gadget_driver *driver)
{ {
struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget); struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
unsigned long flags;
int retval = -ENOMEM; int retval = -ENOMEM;
if (driver->disconnect == NULL) if (driver->disconnect == NULL)
...@@ -1752,7 +1751,6 @@ static int ci_udc_start(struct usb_gadget *gadget, ...@@ -1752,7 +1751,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
pm_runtime_get_sync(&ci->gadget.dev); pm_runtime_get_sync(&ci->gadget.dev);
if (ci->vbus_active) { if (ci->vbus_active) {
spin_lock_irqsave(&ci->lock, flags);
hw_device_reset(ci); hw_device_reset(ci);
} else { } else {
usb_udc_vbus_handler(&ci->gadget, false); usb_udc_vbus_handler(&ci->gadget, false);
...@@ -1761,7 +1759,6 @@ static int ci_udc_start(struct usb_gadget *gadget, ...@@ -1761,7 +1759,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
} }
retval = hw_device_state(ci, ci->ep0out->qh.dma); retval = hw_device_state(ci, ci->ep0out->qh.dma);
spin_unlock_irqrestore(&ci->lock, flags);
if (retval) if (retval)
pm_runtime_put_sync(&ci->gadget.dev); pm_runtime_put_sync(&ci->gadget.dev);
...@@ -1796,10 +1793,10 @@ static int ci_udc_stop(struct usb_gadget *gadget) ...@@ -1796,10 +1793,10 @@ static int ci_udc_stop(struct usb_gadget *gadget)
if (ci->vbus_active) { if (ci->vbus_active) {
hw_device_state(ci, 0); hw_device_state(ci, 0);
spin_unlock_irqrestore(&ci->lock, flags);
if (ci->platdata->notify_event) if (ci->platdata->notify_event)
ci->platdata->notify_event(ci, ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_STOPPED_EVENT); CI_HDRC_CONTROLLER_STOPPED_EVENT);
spin_unlock_irqrestore(&ci->lock, flags);
_gadget_stop_activity(&ci->gadget); _gadget_stop_activity(&ci->gadget);
spin_lock_irqsave(&ci->lock, flags); spin_lock_irqsave(&ci->lock, flags);
pm_runtime_put(&ci->gadget.dev); pm_runtime_put(&ci->gadget.dev);
......
/*
* Copyright (c) 2016 Linaro Ltd.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/usb/chipidea.h>
#include <linux/ulpi/interface.h>
#include "ci.h"
#define ULPI_WAKEUP BIT(31)
#define ULPI_RUN BIT(30)
#define ULPI_WRITE BIT(29)
#define ULPI_SYNC_STATE BIT(27)
#define ULPI_ADDR(n) ((n) << 16)
#define ULPI_DATA(n) (n)
static int ci_ulpi_wait(struct ci_hdrc *ci, u32 mask)
{
unsigned long usec = 10000;
while (usec--) {
if (!hw_read(ci, OP_ULPI_VIEWPORT, mask))
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static int ci_ulpi_read(struct device *dev, u8 addr)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
int ret;
hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
if (ret)
return ret;
hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_RUN | ULPI_ADDR(addr));
ret = ci_ulpi_wait(ci, ULPI_RUN);
if (ret)
return ret;
return hw_read(ci, OP_ULPI_VIEWPORT, GENMASK(15, 8)) >> 8;
}
static int ci_ulpi_write(struct device *dev, u8 addr, u8 val)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
int ret;
hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
if (ret)
return ret;
hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff,
ULPI_RUN | ULPI_WRITE | ULPI_ADDR(addr) | val);
return ci_ulpi_wait(ci, ULPI_RUN);
}
int ci_ulpi_init(struct ci_hdrc *ci)
{
if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
return 0;
/*
* Set PORTSC correctly so we can read/write ULPI registers for
* identification purposes
*/
hw_phymode_configure(ci);
ci->ulpi_ops.read = ci_ulpi_read;
ci->ulpi_ops.write = ci_ulpi_write;
ci->ulpi = ulpi_register_interface(ci->dev, &ci->ulpi_ops);
if (IS_ERR(ci->ulpi))
dev_err(ci->dev, "failed to register ULPI interface");
return PTR_ERR_OR_ZERO(ci->ulpi);
}
void ci_ulpi_exit(struct ci_hdrc *ci)
{
if (ci->ulpi) {
ulpi_unregister_interface(ci->ulpi);
ci->ulpi = NULL;
}
}
int ci_ulpi_resume(struct ci_hdrc *ci)
{
int cnt = 100000;
while (cnt-- > 0) {
if (hw_read(ci, OP_ULPI_VIEWPORT, ULPI_SYNC_STATE))
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk/clk-conf.h>
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
...@@ -39,6 +42,10 @@ static int ulpi_match(struct device *dev, struct device_driver *driver) ...@@ -39,6 +42,10 @@ static int ulpi_match(struct device *dev, struct device_driver *driver)
struct ulpi *ulpi = to_ulpi_dev(dev); struct ulpi *ulpi = to_ulpi_dev(dev);
const struct ulpi_device_id *id; const struct ulpi_device_id *id;
/* Some ULPI devices don't have a vendor id so rely on OF match */
if (ulpi->id.vendor == 0)
return of_driver_match_device(dev, driver);
for (id = drv->id_table; id->vendor; id++) for (id = drv->id_table; id->vendor; id++)
if (id->vendor == ulpi->id.vendor && if (id->vendor == ulpi->id.vendor &&
id->product == ulpi->id.product) id->product == ulpi->id.product)
...@@ -50,6 +57,11 @@ static int ulpi_match(struct device *dev, struct device_driver *driver) ...@@ -50,6 +57,11 @@ static int ulpi_match(struct device *dev, struct device_driver *driver)
static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env) static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
{ {
struct ulpi *ulpi = to_ulpi_dev(dev); struct ulpi *ulpi = to_ulpi_dev(dev);
int ret;
ret = of_device_uevent_modalias(dev, env);
if (ret != -ENODEV)
return ret;
if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x", if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x",
ulpi->id.vendor, ulpi->id.product)) ulpi->id.vendor, ulpi->id.product))
...@@ -60,6 +72,11 @@ static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -60,6 +72,11 @@ static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
static int ulpi_probe(struct device *dev) static int ulpi_probe(struct device *dev)
{ {
struct ulpi_driver *drv = to_ulpi_driver(dev->driver); struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
int ret;
ret = of_clk_set_defaults(dev->of_node, false);
if (ret < 0)
return ret;
return drv->probe(to_ulpi_dev(dev)); return drv->probe(to_ulpi_dev(dev));
} }
...@@ -87,8 +104,13 @@ static struct bus_type ulpi_bus = { ...@@ -87,8 +104,13 @@ static struct bus_type ulpi_bus = {
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
int len;
struct ulpi *ulpi = to_ulpi_dev(dev); struct ulpi *ulpi = to_ulpi_dev(dev);
len = of_device_get_modalias(dev, buf, PAGE_SIZE - 1);
if (len != -ENODEV)
return len;
return sprintf(buf, "ulpi:v%04xp%04x\n", return sprintf(buf, "ulpi:v%04xp%04x\n",
ulpi->id.vendor, ulpi->id.product); ulpi->id.vendor, ulpi->id.product);
} }
...@@ -153,23 +175,45 @@ EXPORT_SYMBOL_GPL(ulpi_unregister_driver); ...@@ -153,23 +175,45 @@ EXPORT_SYMBOL_GPL(ulpi_unregister_driver);
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
static int ulpi_register(struct device *dev, struct ulpi *ulpi) static int ulpi_of_register(struct ulpi *ulpi)
{ {
int ret; struct device_node *np = NULL, *child;
struct device *parent;
/* Find a ulpi bus underneath the parent or the grandparent */
parent = ulpi->dev.parent;
if (parent->of_node)
np = of_find_node_by_name(parent->of_node, "ulpi");
else if (parent->parent && parent->parent->of_node)
np = of_find_node_by_name(parent->parent->of_node, "ulpi");
if (!np)
return 0;
ulpi->dev.parent = dev; /* needed early for ops */ child = of_get_next_available_child(np, NULL);
of_node_put(np);
if (!child)
return -EINVAL;
ulpi->dev.of_node = child;
return 0;
}
static int ulpi_read_id(struct ulpi *ulpi)
{
int ret;
/* Test the interface */ /* Test the interface */
ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa); ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
if (ret < 0) if (ret < 0)
return ret; goto err;
ret = ulpi_read(ulpi, ULPI_SCRATCH); ret = ulpi_read(ulpi, ULPI_SCRATCH);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (ret != 0xaa) if (ret != 0xaa)
return -ENODEV; goto err;
ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW); ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW);
ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8; ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8;
...@@ -177,13 +221,35 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi) ...@@ -177,13 +221,35 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW); ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW);
ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8; ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8;
/* Some ULPI devices don't have a vendor id so rely on OF match */
if (ulpi->id.vendor == 0)
goto err;
request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product);
return 0;
err:
of_device_request_module(&ulpi->dev);
return 0;
}
static int ulpi_register(struct device *dev, struct ulpi *ulpi)
{
int ret;
ulpi->dev.parent = dev; /* needed early for ops */
ulpi->dev.bus = &ulpi_bus; ulpi->dev.bus = &ulpi_bus;
ulpi->dev.type = &ulpi_dev_type; ulpi->dev.type = &ulpi_dev_type;
dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev)); dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev)); ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product); ret = ulpi_of_register(ulpi);
if (ret)
return ret;
ret = ulpi_read_id(ulpi);
if (ret)
return ret;
ret = device_register(&ulpi->dev); ret = device_register(&ulpi->dev);
if (ret) if (ret)
...@@ -234,6 +300,7 @@ EXPORT_SYMBOL_GPL(ulpi_register_interface); ...@@ -234,6 +300,7 @@ EXPORT_SYMBOL_GPL(ulpi_register_interface);
*/ */
void ulpi_unregister_interface(struct ulpi *ulpi) void ulpi_unregister_interface(struct ulpi *ulpi)
{ {
of_node_put(ulpi->dev.of_node);
device_unregister(&ulpi->dev); device_unregister(&ulpi->dev);
} }
EXPORT_SYMBOL_GPL(ulpi_unregister_interface); EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
......
...@@ -37,6 +37,7 @@ extern const void *of_device_get_match_data(const struct device *dev); ...@@ -37,6 +37,7 @@ extern const void *of_device_get_match_data(const struct device *dev);
extern ssize_t of_device_get_modalias(struct device *dev, extern ssize_t of_device_get_modalias(struct device *dev,
char *str, ssize_t len); char *str, ssize_t len);
extern int of_device_request_module(struct device *dev);
extern void of_device_uevent(struct device *dev, struct kobj_uevent_env *env); extern void of_device_uevent(struct device *dev, struct kobj_uevent_env *env);
extern int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env); extern int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env);
...@@ -78,6 +79,11 @@ static inline int of_device_get_modalias(struct device *dev, ...@@ -78,6 +79,11 @@ static inline int of_device_get_modalias(struct device *dev,
return -ENODEV; return -ENODEV;
} }
static inline int of_device_request_module(struct device *dev)
{
return -ENODEV;
}
static inline int of_device_uevent_modalias(struct device *dev, static inline int of_device_uevent_modalias(struct device *dev,
struct kobj_uevent_env *env) struct kobj_uevent_env *env)
{ {
......
...@@ -12,16 +12,18 @@ struct ci_hdrc; ...@@ -12,16 +12,18 @@ struct ci_hdrc;
/** /**
* struct ci_hdrc_cable - structure for external connector cable state tracking * struct ci_hdrc_cable - structure for external connector cable state tracking
* @state: current state of the line * @connected: true if cable is connected, false otherwise
* @changed: set to true when extcon event happen * @changed: set to true when extcon event happen
* @enabled: set to true if we've enabled the vbus or id interrupt
* @edev: device which generate events * @edev: device which generate events
* @ci: driver state of the chipidea device * @ci: driver state of the chipidea device
* @nb: hold event notification callback * @nb: hold event notification callback
* @conn: used for notification registration * @conn: used for notification registration
*/ */
struct ci_hdrc_cable { struct ci_hdrc_cable {
bool state; bool connected;
bool changed; bool changed;
bool enabled;
struct extcon_dev *edev; struct extcon_dev *edev;
struct ci_hdrc *ci; struct ci_hdrc *ci;
struct notifier_block nb; struct notifier_block nb;
...@@ -55,10 +57,11 @@ struct ci_hdrc_platform_data { ...@@ -55,10 +57,11 @@ struct ci_hdrc_platform_data {
#define CI_HDRC_OVERRIDE_AHB_BURST BIT(9) #define CI_HDRC_OVERRIDE_AHB_BURST BIT(9)
#define CI_HDRC_OVERRIDE_TX_BURST BIT(10) #define CI_HDRC_OVERRIDE_TX_BURST BIT(10)
#define CI_HDRC_OVERRIDE_RX_BURST BIT(11) #define CI_HDRC_OVERRIDE_RX_BURST BIT(11)
#define CI_HDRC_OVERRIDE_PHY_CONTROL BIT(12) /* Glue layer manages phy */
enum usb_dr_mode dr_mode; enum usb_dr_mode dr_mode;
#define CI_HDRC_CONTROLLER_RESET_EVENT 0 #define CI_HDRC_CONTROLLER_RESET_EVENT 0
#define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1
void (*notify_event) (struct ci_hdrc *ci, unsigned event); int (*notify_event) (struct ci_hdrc *ci, unsigned event);
struct regulator *reg_vbus; struct regulator *reg_vbus;
struct usb_otg_caps ci_otg_caps; struct usb_otg_caps ci_otg_caps;
bool tpl_support; bool tpl_support;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册