diff options
author | Peter Chen <peter.chen@freescale.com> | 2013-11-14 16:27:05 +0800 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 08:47:14 -0500 |
commit | 2008bba70da642f3c7d299d0c0e84535fdaee77c (patch) | |
tree | 599aa596aef1e80b16e0bb7bef86e2dabca4abf8 /drivers/usb/chipidea/ci_hdrc_imx.c | |
parent | 8b71b4c673f8b40fc3cf4745175c080d2e759175 (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.c | 70 |
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; } |