summaryrefslogtreecommitdiff
path: root/drivers/usb/chipidea/ci_hdrc_imx.c
diff options
context:
space:
mode:
authorPeter Chen <peter.chen@freescale.com>2013-11-14 16:27:05 +0800
committerNitin Garg <nitin.garg@freescale.com>2014-04-16 08:47:14 -0500
commit2008bba70da642f3c7d299d0c0e84535fdaee77c (patch)
tree599aa596aef1e80b16e0bb7bef86e2dabca4abf8 /drivers/usb/chipidea/ci_hdrc_imx.c
parent8b71b4c673f8b40fc3cf4745175c080d2e759175 (diff)
ENGR00287992-6 usb: chipidea: imx: add usb charger detection for imx6
The usb controller driver creates usb charger, and notify the charger connect and disconnect using vbus connect and disconnect event. Signed-off-by: Peter Chen <peter.chen@freescale.com>
Diffstat (limited to 'drivers/usb/chipidea/ci_hdrc_imx.c')
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c70
1 files changed, 67 insertions, 3 deletions
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index bf52352f1a64..8994f4b91151 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -20,6 +20,10 @@
#include <linux/usb/chipidea.h>
#include <linux/clk.h>
#include <linux/busfreq-imx6.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/power/imx6_usb_charger.h>
#include "ci.h"
#include "ci_hdrc_imx.h"
@@ -70,6 +74,8 @@ struct ci_hdrc_imx_data {
struct imx_usbmisc_data *usbmisc_data;
bool supports_runtime_pm;
bool in_lpm;
+ bool imx6_usb_charger_detection;
+ struct usb_charger charger;
};
/* Common functions shared by usbmisc drivers */
@@ -114,6 +120,37 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
/* End of common functions shared by usbmisc drivers*/
+static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned event)
+{
+ struct device *dev = ci->dev->parent;
+ struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
+ int ret = 0;
+
+ switch (event) {
+ case CI_HDRC_CONTROLLER_CHARGER_EVENT:
+ if (!data->imx6_usb_charger_detection)
+ return ret;
+ if (ci->vbus_active) {
+ ret = imx6_usb_vbus_connect(&data->charger);
+ if (!ret && data->charger.psy.type
+ != POWER_SUPPLY_TYPE_USB)
+ ret = CI_HDRC_NOTIFY_RET_DEFER_EVENT;
+ } else {
+ ret = imx6_usb_vbus_disconnect(&data->charger);
+ }
+ break;
+ case CI_HDRC_CONTROLLER_CHARGER_POST_EVENT:
+ if (!data->imx6_usb_charger_detection)
+ return ret;
+ imx6_usb_charger_detect_post(&data->charger);
+ break;
+ default:
+ dev_dbg(dev, "unknown event\n");
+ }
+
+ return ret;
+}
+
static int ci_hdrc_imx_probe(struct platform_device *pdev)
{
struct ci_hdrc_imx_data *data;
@@ -122,11 +159,13 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
.capoffset = DEF_CAPOFFSET,
.flags = CI_HDRC_REQUIRE_TRANSCEIVER |
CI_HDRC_DISABLE_STREAMING,
+ .notify_event = ci_hdrc_imx_notify_event,
};
int ret;
const struct of_device_id *of_id =
of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data;
+ struct device_node *np = pdev->dev.of_node;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
@@ -134,6 +173,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ platform_set_drvdata(pdev, data);
+
data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
if (IS_ERR(data->usbmisc_data))
return PTR_ERR(data->usbmisc_data);
@@ -187,6 +228,26 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
}
}
+ if (of_find_property(np, "imx6-usb-charger-detection", NULL))
+ data->imx6_usb_charger_detection = true;
+
+ if (data->imx6_usb_charger_detection) {
+ data->charger.anatop = syscon_regmap_lookup_by_phandle
+ (np, "fsl,anatop");
+ if (IS_ERR(data->charger.anatop)) {
+ dev_dbg(&pdev->dev,
+ "failed to find regmap for anatop\n");
+ ret = PTR_ERR(data->charger.anatop);
+ goto err_clk;
+ }
+ data->charger.dev = &pdev->dev;
+ ret = imx6_usb_create_charger(&data->charger,
+ "imx6_usb_charger");
+ if (ret)
+ goto err_clk;
+ dev_dbg(&pdev->dev, "USB Charger is created\n");
+ }
+
data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
pdev->resource, pdev->num_resources,
&pdata);
@@ -195,7 +256,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"Can't register ci_hdrc platform device, err=%d\n",
ret);
- goto err_clk;
+ goto remove_charger;
}
if (data->usbmisc_data) {
@@ -216,8 +277,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
}
}
- platform_set_drvdata(pdev, data);
-
device_set_wakeup_capable(&pdev->dev, true);
if (data->supports_runtime_pm) {
@@ -229,6 +288,9 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
disable_device:
ci_hdrc_remove_device(data->ci_pdev);
+remove_charger:
+ if (data->imx6_usb_charger_detection)
+ imx6_usb_remove_charger(&data->charger);
err_clk:
clk_disable_unprepare(data->clk);
release_bus_freq(BUS_FREQ_HIGH);
@@ -247,6 +309,8 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
}
clk_disable_unprepare(data->clk);
release_bus_freq(BUS_FREQ_HIGH);
+ if (data->imx6_usb_charger_detection)
+ imx6_usb_remove_charger(&data->charger);
return 0;
}