提交 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)
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
......@@ -287,3 +311,4 @@ int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
return 0;
}
EXPORT_SYMBOL_GPL(of_device_uevent_modalias);
......@@ -2,6 +2,7 @@ config USB_CHIPIDEA
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
select EXTCON
select RESET_CONTROLLER
help
Say Y here if your system has a dual role high speed USB
controller based on ChipIdea silicon IP. It supports:
......@@ -38,4 +39,11 @@ config USB_CHIPIDEA_HOST
Say Y here to enable host controller functionality of the
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
......@@ -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_HOST) += host.o
ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_ULPI) += ulpi.o
# Glue/Bridge layers go here
......
......@@ -18,6 +18,8 @@
#include <linux/usb.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg-fsm.h>
#include <linux/usb/otg.h>
#include <linux/ulpi/interface.h>
/******************************************************************************
* DEFINE
......@@ -52,6 +54,7 @@ enum ci_hw_regs {
OP_ENDPTLISTADDR,
OP_TTCTRL,
OP_BURSTSIZE,
OP_ULPI_VIEWPORT,
OP_PORTSC,
OP_DEVLC,
OP_OTGSC,
......@@ -187,6 +190,8 @@ struct hw_bank {
* @test_mode: the selected test mode
* @platdata: platform specific information supplied by parent device
* @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
* @usb_phy: pointer to USB PHY, if any and if using the USB PHY framework
* @hcd: pointer to usb_hcd for ehci host driver
......@@ -236,6 +241,10 @@ struct ci_hdrc {
struct ci_hdrc_platform_data *platdata;
int vbus_active;
#ifdef CONFIG_USB_CHIPIDEA_ULPI
struct ulpi *ulpi;
struct ulpi_ops ulpi_ops;
#endif
struct phy *phy;
/* old usb_phy interface */
struct usb_phy *usb_phy;
......@@ -418,6 +427,16 @@ static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci)
#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_status(struct ci_hdrc *ci);
......@@ -428,8 +447,7 @@ int hw_port_test_set(struct ci_hdrc *ci, u8 mode);
u8 hw_port_test_get(struct ci_hdrc *ci);
int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
u32 value, unsigned int timeout_ms);
void hw_phymode_configure(struct ci_hdrc *ci);
void ci_platform_configure(struct ci_hdrc *ci);
......
......@@ -8,90 +8,292 @@
#include <linux/module.h>
#include <linux/platform_device.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/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"
#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 device *dev = ci->gadget.dev.parent;
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->dev->parent;
struct ci_hdrc_msm *msm_ci = dev_get_drvdata(dev);
int ret;
switch (event) {
case CI_HDRC_CONTROLLER_RESET_EVENT:
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 */
writel(0x8, USB_AHBMODE);
usb_phy_init(ci->usb_phy);
hw_write_id_reg(ci, HS_PHY_AHB_MODE, 0xffffffff, 0x8);
/* 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;
case CI_HDRC_CONTROLLER_STOPPED_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n");
/*
* Put the phy in non-driving mode. Otherwise host
* may not detect soft-disconnection.
*/
usb_phy_notify_disconnect(ci->usb_phy, USB_SPEED_UNKNOWN);
phy_power_off(ci->phy);
phy_exit(ci->phy);
break;
default:
dev_dbg(dev, "unknown ci_hdrc event\n");
break;
}
return 0;
}
static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
.name = "ci_hdrc_msm",
.capoffset = DEF_CAPOFFSET,
.flags = CI_HDRC_REGS_SHARED |
CI_HDRC_DISABLE_STREAMING,
static int ci_hdrc_msm_mux_phy(struct ci_hdrc_msm *ci,
struct platform_device *pdev)
{
struct regmap *regmap;
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)
{
struct ci_hdrc_msm *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");
/*
* OTG(PHY) driver takes care of PHY initialization, clock management,
* powering up VBUS, mapping of registers address space and power
* management.
*/
phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
if (IS_ERR(phy))
return PTR_ERR(phy);
ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
if (!ci)
return -ENOMEM;
platform_set_drvdata(pdev, ci);
ci->pdata.name = "ci_hdrc_msm";
ci->pdata.capoffset = DEF_CAPOFFSET;
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;
reset = devm_reset_control_get(&pdev->dev, "core");
if (IS_ERR(reset))
return PTR_ERR(reset);
ci->core_clk = clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(clk))
return PTR_ERR(clk);
ci_hdrc_msm_platdata.usb_phy = phy;
ci->iface_clk = clk = devm_clk_get(&pdev->dev, "iface");
if (IS_ERR(clk))
return PTR_ERR(clk);
plat_ci = ci_hdrc_add_device(&pdev->dev,
pdev->resource, pdev->num_resources,
&ci_hdrc_msm_platdata);
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)) {
dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
return PTR_ERR(plat_ci);
ret = PTR_ERR(plat_ci);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
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_enable(&pdev->dev);
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)
{
struct platform_device *plat_ci = platform_get_drvdata(pdev);
struct ci_hdrc_msm *ci = platform_get_drvdata(pdev);
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;
}
......
......@@ -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);
priv->ci_pdev = ci_hdrc_add_device(dev, pdev->resource,
......
......@@ -62,7 +62,6 @@
#include <linux/usb/chipidea.h>
#include <linux/usb/of.h>
#include <linux/of.h>
#include <linux/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/ehci_def.h>
......@@ -86,6 +85,7 @@ static const u8 ci_regs_nolpm[] = {
[OP_ENDPTLISTADDR] = 0x18U,
[OP_TTCTRL] = 0x1CU,
[OP_BURSTSIZE] = 0x20U,
[OP_ULPI_VIEWPORT] = 0x30U,
[OP_PORTSC] = 0x44U,
[OP_DEVLC] = 0x84U,
[OP_OTGSC] = 0x64U,
......@@ -110,6 +110,7 @@ static const u8 ci_regs_lpm[] = {
[OP_ENDPTLISTADDR] = 0x18U,
[OP_TTCTRL] = 0x1CU,
[OP_BURSTSIZE] = 0x20U,
[OP_ULPI_VIEWPORT] = 0x30U,
[OP_PORTSC] = 0x44U,
[OP_DEVLC] = 0x84U,
[OP_OTGSC] = 0xC4U,
......@@ -285,7 +286,7 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
return 0;
}
static void hw_phymode_configure(struct ci_hdrc *ci)
void hw_phymode_configure(struct ci_hdrc *ci)
{
u32 portsc, lpm, sts = 0;
......@@ -325,6 +326,7 @@ static void hw_phymode_configure(struct ci_hdrc *ci)
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
......@@ -361,6 +363,9 @@ static int _ci_usb_phy_init(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) {
phy_power_off(ci->phy);
phy_exit(ci->phy);
......@@ -379,6 +384,9 @@ static int ci_usb_phy_init(struct ci_hdrc *ci)
{
int ret;
if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL)
return 0;
switch (ci->platdata->phy_mode) {
case USBPHY_INTERFACE_MODE_UTMI:
case USBPHY_INTERFACE_MODE_UTMIW:
......@@ -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_host_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_HC;
if (is_device_mode &&
(ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING))
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
if (is_device_mode) {
phy_set_mode(ci->phy, PHY_MODE_USB_DEVICE);
if (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING)
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
USBMODE_CI_SDIS);
}
if (is_host_mode) {
phy_set_mode(ci->phy, PHY_MODE_USB_HOST);
if (is_host_mode &&
(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_DISABLE_HOST_STREAMING)
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
USBMODE_CI_SDIS);
}
if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) {
if (ci->hw_bank.lpm)
......@@ -495,9 +511,12 @@ int hw_device_reset(struct ci_hdrc *ci)
return ret;
}
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
if (ci->platdata->notify_event) {
ret = ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_RESET_EVENT);
if (ret)
return ret;
}
/* USBMODE should be configured step by step */
hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
......@@ -516,38 +535,6 @@ int hw_device_reset(struct ci_hdrc *ci)
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)
{
struct ci_hdrc *ci = data;
......@@ -601,35 +588,14 @@ static irqreturn_t ci_irq(int irq, void *data)
return ret;
}
static int ci_vbus_notifier(struct notifier_block *nb, unsigned long event,
void *ptr)
{
struct ci_hdrc_cable *vbus = container_of(nb, struct ci_hdrc_cable, nb);
struct ci_hdrc *ci = vbus->ci;
if (event)
vbus->state = 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)
static int ci_cable_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;
struct ci_hdrc_cable *cbl = container_of(nb, struct ci_hdrc_cable, nb);
struct ci_hdrc *ci = cbl->ci;
if (event)
id->state = false;
else
id->state = true;
id->changed = true;
cbl->connected = event;
cbl->changed = true;
ci_irq(ci->irq, ci);
return NOTIFY_DONE;
......@@ -738,27 +704,27 @@ static int ci_get_platdata(struct device *dev,
}
cable = &platdata->vbus_extcon;
cable->nb.notifier_call = ci_vbus_notifier;
cable->nb.notifier_call = ci_cable_notifier;
cable->edev = 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)
cable->state = true;
cable->connected = true;
else
cable->state = false;
cable->connected = false;
}
cable = &platdata->id_extcon;
cable->nb.notifier_call = ci_id_notifier;
cable->nb.notifier_call = ci_cable_notifier;
cable->edev = 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)
cable->state = false;
cable->connected = true;
else
cable->state = true;
cable->connected = false;
}
return 0;
}
......@@ -771,8 +737,8 @@ static int ci_extcon_register(struct ci_hdrc *ci)
id = &ci->platdata->id_extcon;
id->ci = ci;
if (!IS_ERR(id->edev)) {
ret = extcon_register_notifier(id->edev, EXTCON_USB_HOST,
&id->nb);
ret = devm_extcon_register_notifier(ci->dev, id->edev,
EXTCON_USB_HOST, &id->nb);
if (ret < 0) {
dev_err(ci->dev, "register ID failed\n");
return ret;
......@@ -782,11 +748,9 @@ static int ci_extcon_register(struct ci_hdrc *ci)
vbus = &ci->platdata->vbus_extcon;
vbus->ci = ci;
if (!IS_ERR(vbus->edev)) {
ret = extcon_register_notifier(vbus->edev, EXTCON_USB,
&vbus->nb);
ret = devm_extcon_register_notifier(ci->dev, vbus->edev,
EXTCON_USB, &vbus->nb);
if (ret < 0) {
extcon_unregister_notifier(id->edev, EXTCON_USB_HOST,
&id->nb);
dev_err(ci->dev, "register VBUS failed\n");
return ret;
}
......@@ -795,20 +759,6 @@ static int ci_extcon_register(struct ci_hdrc *ci)
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);
struct platform_device *ci_hdrc_add_device(struct device *dev,
......@@ -921,6 +871,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
CI_HDRC_IMX28_WRITE_FIX);
ci->supports_runtime_pm = !!(ci->platdata->flags &
CI_HDRC_SUPPORTS_RUNTIME_PM);
platform_set_drvdata(pdev, ci);
ret = hw_device_init(ci, base);
if (ret < 0) {
......@@ -928,6 +879,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV;
}
ret = ci_ulpi_init(ci);
if (ret)
return ret;
if (ci->platdata->phy) {
ci->phy = ci->platdata->phy;
} else if (ci->platdata->usb_phy) {
......@@ -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 (PTR_ERR(ci->phy) == -ENOSYS &&
PTR_ERR(ci->usb_phy) == -ENXIO)
return -ENXIO;
PTR_ERR(ci->usb_phy) == -ENXIO) {
ret = -ENXIO;
goto ulpi_exit;
}
if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))
return -EPROBE_DEFER;
if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) {
ret = -EPROBE_DEFER;
goto ulpi_exit;
}
if (IS_ERR(ci->phy))
ci->phy = NULL;
......@@ -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,
ci->platdata->name, ci);
if (ret)
......@@ -1054,11 +1012,12 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (!ret)
return 0;
ci_extcon_unregister(ci);
stop:
ci_role_destroy(ci);
deinit_phy:
ci_usb_phy_exit(ci);
ulpi_exit:
ci_ulpi_exit(ci);
return ret;
}
......@@ -1074,10 +1033,10 @@ static int ci_hdrc_remove(struct platform_device *pdev)
}
dbg_remove_files(ci);
ci_extcon_unregister(ci);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
ci_usb_phy_exit(ci);
ci_ulpi_exit(ci);
return 0;
}
......@@ -1125,6 +1084,7 @@ static void ci_controller_suspend(struct ci_hdrc *ci)
static int ci_controller_resume(struct device *dev)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
int ret;
dev_dbg(dev, "at %s\n", __func__);
......@@ -1134,6 +1094,11 @@ static int ci_controller_resume(struct device *dev)
}
ci_hdrc_enter_lpm(ci, false);
ret = ci_ulpi_resume(ci);
if (ret)
return ret;
if (ci->usb_phy) {
usb_phy_set_suspend(ci->usb_phy, 0);
usb_phy_set_wakeup(ci->usb_phy, false);
......
......@@ -90,6 +90,13 @@ static int ehci_ci_reset(struct usb_hcd *hcd)
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);
return ret;
......@@ -187,6 +194,9 @@ static void host_stop(struct ci_hdrc *ci)
struct usb_hcd *hcd = ci->hcd;
if (hcd) {
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_STOPPED_EVENT);
usb_remove_hcd(hcd);
ci->role = CI_ROLE_END;
synchronize_irq(ci->irq);
......
......@@ -44,12 +44,15 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
else
val &= ~OTGSC_BSVIS;
cable->changed = false;
if (cable->state)
if (cable->connected)
val |= OTGSC_BSV;
else
val &= ~OTGSC_BSV;
if (cable->enabled)
val |= OTGSC_BSVIE;
else
val &= ~OTGSC_BSVIE;
}
cable = &ci->platdata->id_extcon;
......@@ -59,15 +62,18 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
else
val &= ~OTGSC_IDIS;
cable->changed = false;
if (cable->connected)
val &= ~OTGSC_ID; /* host */
else
val |= OTGSC_ID; /* device */
if (cable->state)
val |= OTGSC_ID;
if (cable->enabled)
val |= OTGSC_IDIE;
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)
*/
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);
}
......@@ -98,13 +134,37 @@ void ci_handle_vbus_change(struct ci_hdrc *ci)
if (!ci->is_otg)
return;
if (hw_read_otgsc(ci, OTGSC_BSV))
if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active)
usb_gadget_vbus_connect(&ci->gadget);
else
else if (!hw_read_otgsc(ci, OTGSC_BSV) && ci->vbus_active)
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)
{
enum ci_role role = ci_otg_role(ci);
......@@ -115,12 +175,21 @@ static void ci_handle_id_switch(struct ci_hdrc *ci)
ci_role_stop(ci);
if (role == CI_ROLE_GADGET)
/* wait vbus lower than OTGSC_BSV */
hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0,
CI_VBUS_STABLE_TIMEOUT_MS);
if (role == CI_ROLE_GADGET &&
IS_ERR(ci->platdata->vbus_extcon.edev))
/*
* 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);
/* 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,
struct usb_gadget_driver *driver)
{
struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
unsigned long flags;
int retval = -ENOMEM;
if (driver->disconnect == NULL)
......@@ -1752,7 +1751,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
pm_runtime_get_sync(&ci->gadget.dev);
if (ci->vbus_active) {
spin_lock_irqsave(&ci->lock, flags);
hw_device_reset(ci);
} else {
usb_udc_vbus_handler(&ci->gadget, false);
......@@ -1761,7 +1759,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
}
retval = hw_device_state(ci, ci->ep0out->qh.dma);
spin_unlock_irqrestore(&ci->lock, flags);
if (retval)
pm_runtime_put_sync(&ci->gadget.dev);
......@@ -1796,10 +1793,10 @@ static int ci_udc_stop(struct usb_gadget *gadget)
if (ci->vbus_active) {
hw_device_state(ci, 0);
spin_unlock_irqrestore(&ci->lock, flags);
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_STOPPED_EVENT);
spin_unlock_irqrestore(&ci->lock, flags);
_gadget_stop_activity(&ci->gadget);
spin_lock_irqsave(&ci->lock, flags);
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 @@
#include <linux/module.h>
#include <linux/slab.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)
struct ulpi *ulpi = to_ulpi_dev(dev);
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++)
if (id->vendor == ulpi->id.vendor &&
id->product == ulpi->id.product)
......@@ -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)
{
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",
ulpi->id.vendor, ulpi->id.product))
......@@ -60,6 +72,11 @@ static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
static int ulpi_probe(struct device *dev)
{
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));
}
......@@ -87,8 +104,13 @@ static struct bus_type ulpi_bus = {
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
int len;
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",
ulpi->id.vendor, ulpi->id.product);
}
......@@ -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;
child = of_get_next_available_child(np, NULL);
of_node_put(np);
if (!child)
return -EINVAL;
ulpi->dev.parent = dev; /* needed early for ops */
ulpi->dev.of_node = child;
return 0;
}
static int ulpi_read_id(struct ulpi *ulpi)
{
int ret;
/* Test the interface */
ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
if (ret < 0)
return ret;
goto err;
ret = ulpi_read(ulpi, ULPI_SCRATCH);
if (ret < 0)
return ret;
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_HIGH) << 8;
......@@ -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_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.type = &ulpi_dev_type;
dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product);
ret = ulpi_of_register(ulpi);
if (ret)
return ret;
ret = ulpi_read_id(ulpi);
if (ret)
return ret;
ret = device_register(&ulpi->dev);
if (ret)
......@@ -234,6 +300,7 @@ EXPORT_SYMBOL_GPL(ulpi_register_interface);
*/
void ulpi_unregister_interface(struct ulpi *ulpi)
{
of_node_put(ulpi->dev.of_node);
device_unregister(&ulpi->dev);
}
EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
......
......@@ -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,
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 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,
return -ENODEV;
}
static inline int of_device_request_module(struct device *dev)
{
return -ENODEV;
}
static inline int of_device_uevent_modalias(struct device *dev,
struct kobj_uevent_env *env)
{
......
......@@ -12,16 +12,18 @@ struct ci_hdrc;
/**
* 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
* @enabled: set to true if we've enabled the vbus or id interrupt
* @edev: device which generate events
* @ci: driver state of the chipidea device
* @nb: hold event notification callback
* @conn: used for notification registration
*/
struct ci_hdrc_cable {
bool state;
bool connected;
bool changed;
bool enabled;
struct extcon_dev *edev;
struct ci_hdrc *ci;
struct notifier_block nb;
......@@ -55,10 +57,11 @@ struct ci_hdrc_platform_data {
#define CI_HDRC_OVERRIDE_AHB_BURST BIT(9)
#define CI_HDRC_OVERRIDE_TX_BURST BIT(10)
#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;
#define CI_HDRC_CONTROLLER_RESET_EVENT 0
#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 usb_otg_caps ci_otg_caps;
bool tpl_support;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册