summaryrefslogtreecommitdiff
path: root/arch/arm/plat-mxc/mc13783_xc.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-mxc/mc13783_xc.c')
-rw-r--r--arch/arm/plat-mxc/mc13783_xc.c299
1 files changed, 299 insertions, 0 deletions
diff --git a/arch/arm/plat-mxc/mc13783_xc.c b/arch/arm/plat-mxc/mc13783_xc.c
new file mode 100644
index 000000000000..9c41fbf5a8a8
--- /dev/null
+++ b/arch/arm/plat-mxc/mc13783_xc.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/usb/fsl_xcvr.h>
+#include <mach/pmic_external.h>
+#include <mach/pmic_convity.h>
+#include <mach/arc_otg.h>
+
+/* Events to be passed to the thread */
+#define MC13783_USB_VBUS_ON 0x0001
+#define MC13783_USB_VBUS_OFF 0x0002
+#define MC13783_USB_DETECT_MINI_A 0x0004
+#define MC13783_USB_DETECT_MINI_B 0x0008
+
+extern void otg_set_serial_peripheral(void);
+extern void otg_set_serial_host(void);
+
+static unsigned int p_event;
+static PMIC_CONVITY_EVENTS g_event;
+static PMIC_CONVITY_HANDLE pmic_handle = (PMIC_CONVITY_HANDLE) NULL;
+
+static void xc_workqueue_handler(struct work_struct *work);
+
+DECLARE_WORK(xc_work, xc_workqueue_handler);
+
+DECLARE_MUTEX(pmic_mx);
+
+static void pmic_event_handler(const PMIC_CONVITY_EVENTS event)
+{
+ if (event & USB_DETECT_4V4_RISE)
+ pr_debug("%s: USB_DETECT_4V4_RISE\n", __func__);
+
+ if (event & USB_DETECT_4V4_FALL)
+ pr_debug("%s: USB_DETECT_4V4_FALL\n", __func__);
+
+ if (event & USB_DETECT_2V0_RISE)
+ pr_debug("%s: USB_DETECT_2V0_RISE\n", __func__);
+
+ if (event & USB_DETECT_2V0_FALL)
+ pr_debug("%s: USB_DETECT_2V0_FALL\n", __func__);
+
+ if (event & USB_DETECT_0V8_RISE)
+ pr_debug("%s: USB_DETECT_0V8_RISE\n", __func__);
+
+ if (event & USB_DETECT_0V8_FALL)
+ pr_debug("%s: USB_DETECT_0V8_FALL\n", __func__);
+
+ if (event & USB_DETECT_MINI_B) {
+ pr_debug("%s: USB_DETECT_MINI_B\n", __func__);
+ otg_set_serial_peripheral();
+ g_event = USB_DETECT_MINI_B;
+ p_event = MC13783_USB_DETECT_MINI_B;
+ schedule_work(&xc_work);
+ }
+ if (event & USB_DETECT_MINI_A) {
+ pr_debug("%s: USB_DETECT_MINI_A\n", __func__);
+ otg_set_serial_host();
+ g_event = USB_DETECT_MINI_A;
+ p_event = MC13783_USB_DETECT_MINI_A;
+ schedule_work(&xc_work);
+ }
+
+ /*
+ * Mini-B cable insertion/removal does not generate cable-detect
+ * event, so we rely on the VBUS changes to identify a mini-b cable
+ * connect. This logic is only used if mini-b is the first cable that
+ * is connected after bootup. At all other times, removal of mini-a
+ * cable is used to initialize peripheral.
+ */
+ if (g_event != USB_DETECT_MINI_A && g_event != USB_DETECT_MINI_B) {
+ if ((event & USB_DETECT_0V8_RISE) &&
+ (event & USB_DETECT_2V0_RISE) &&
+ (event & USB_DETECT_4V4_RISE)) {
+ otg_set_serial_peripheral();
+ g_event = USB_DETECT_MINI_B;
+ p_event = MC13783_USB_DETECT_MINI_B;
+ schedule_work(&xc_work);
+ }
+ }
+}
+
+static int usb_pmic_mod_init(void)
+{
+ PMIC_STATUS rs = PMIC_ERROR;
+
+ init_MUTEX_LOCKED(&pmic_mx);
+
+ rs = pmic_convity_open(&pmic_handle, USB);
+ if (rs != PMIC_SUCCESS) {
+ printk(KERN_ERR "pmic_convity_open returned error %d\n", rs);
+ return rs;
+ }
+
+ rs = pmic_convity_set_callback(pmic_handle, pmic_event_handler,
+ USB_DETECT_4V4_RISE | USB_DETECT_4V4_FALL
+ | USB_DETECT_2V0_RISE |
+ USB_DETECT_2V0_FALL | USB_DETECT_0V8_RISE
+ | USB_DETECT_0V8_FALL | USB_DETECT_MINI_A
+ | USB_DETECT_MINI_B);
+
+ if (rs != PMIC_SUCCESS) {
+ printk(KERN_ERR
+ "pmic_convity_set_callback returned error %d\n", rs);
+ return rs;
+ }
+
+ return rs;
+}
+
+static void usb_pmic_mod_exit(void)
+{
+ PMIC_STATUS rs;
+
+ pmic_convity_set_mode(pmic_handle, RS232_1);
+ pmic_convity_clear_callback(pmic_handle);
+
+ if (pmic_handle != (PMIC_CONVITY_HANDLE) NULL) {
+ rs = pmic_convity_close(pmic_handle);
+ if (rs != PMIC_SUCCESS) {
+ printk(KERN_ERR
+ "pmic_convity_close() returned error %d", rs);
+ } else {
+ pmic_handle = (PMIC_CONVITY_HANDLE) NULL;
+ }
+ }
+}
+
+static inline void mc13783_set_host(void)
+{
+ PMIC_STATUS rs = PMIC_ERROR;
+
+ rs = pmic_convity_usb_otg_clear_config(pmic_handle, USB_OTG_SE0CONN);
+ rs |= pmic_convity_usb_otg_clear_config(pmic_handle, USB_PU);
+
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_UDM_PD);
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_UDP_PD);
+
+ if (rs != PMIC_SUCCESS)
+ printk(KERN_ERR "mc13783_set_host failed\n");
+
+}
+
+static inline void mc13783_set_peripheral(void)
+{
+ PMIC_STATUS rs = PMIC_ERROR;
+
+ rs = pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDM_PD);
+ rs |= pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDP_PD);
+
+ rs |= pmic_convity_usb_set_speed(pmic_handle, USB_FULL_SPEED);
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_OTG_SE0CONN);
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_PU);
+
+ if (rs != PMIC_SUCCESS)
+ printk(KERN_ERR "mc13783_set_peripheral failed\n");
+}
+
+void mc13783_set_vbus_power(struct fsl_xcvr_ops *this,
+ struct fsl_usb2_platform_data *pdata, int on)
+{
+ if (on) {
+ p_event = MC13783_USB_VBUS_ON;
+ schedule_work(&xc_work);
+ }
+}
+
+static struct fsl_xcvr_ops mc13783_ops_otg = {
+ .name = "mc13783",
+ .xcvr_type = PORTSC_PTS_SERIAL,
+ .set_host = mc13783_set_host,
+ .set_device = mc13783_set_peripheral,
+ .set_vbus_power = mc13783_set_vbus_power,
+};
+
+extern void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops);
+
+static void xc_workqueue_handler(struct work_struct *work)
+{
+ PMIC_STATUS rs = PMIC_ERROR;
+
+ down(&pmic_mx);
+
+ switch (p_event) {
+ case MC13783_USB_VBUS_OFF:
+ mc13783_set_peripheral();
+ break;
+ case MC13783_USB_VBUS_ON:
+ mc13783_set_host();
+ break;
+ case MC13783_USB_DETECT_MINI_B:
+ rs = pmic_convity_set_output(pmic_handle, true, false);
+ rs |=
+ pmic_convity_usb_otg_clear_config(pmic_handle,
+ USB_VBUS_CURRENT_LIMIT_LOW_30MS);
+
+ if (rs != PMIC_SUCCESS)
+ printk(KERN_ERR "MC13783_USB_VBUS_OFF failed\n");
+ break;
+ case MC13783_USB_DETECT_MINI_A:
+ rs = pmic_convity_set_output(pmic_handle, true, true);
+ rs |=
+ pmic_convity_usb_otg_set_config(pmic_handle,
+ USB_VBUS_CURRENT_LIMIT_LOW_30MS);
+
+ if (rs != PMIC_SUCCESS)
+ printk(KERN_ERR "MC13783_USB_VBUS_ON failed\n");
+ break;
+ default:
+ break;
+ }
+ up(&pmic_mx);
+}
+
+int mc13783xc_init(void)
+{
+ PMIC_STATUS rs = PMIC_ERROR;
+
+#if defined(CONFIG_MXC_USB_SB3)
+ int xc_mode = USB_SINGLE_ENDED_BIDIR;
+#elif defined(CONFIG_MXC_USB_SU6)
+ int xc_mode = USB_SINGLE_ENDED_UNIDIR;
+#elif defined(CONFIG_MXC_USB_DB4)
+ int xc_mode = USB_DIFFERENTIAL_BIDIR;
+#else
+ int xc_mode = USB_DIFFERENTIAL_UNIDIR;
+#endif
+
+ rs = usb_pmic_mod_init();
+ if (rs != PMIC_SUCCESS) {
+ usb_pmic_mod_exit();
+ printk(KERN_ERR "usb_pmic_mod_init failed\n");
+ return rs;
+ }
+
+ rs = pmic_convity_usb_set_xcvr(pmic_handle, xc_mode);
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_OTG_SE0CONN);
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USBXCVREN);
+ rs |= pmic_convity_set_output(pmic_handle, false, true);
+
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_PULL_OVERRIDE);
+ rs |= pmic_convity_usb_otg_clear_config(pmic_handle, USB_USBCNTRL);
+ rs |= pmic_convity_usb_otg_clear_config(pmic_handle, USB_DP150K_PU);
+
+ if (rs != PMIC_SUCCESS)
+ printk(KERN_ERR "pmic configuration failed\n");
+
+ fsl_usb_xcvr_register(&mc13783_ops_otg);
+
+ mc13783_set_peripheral();
+
+ return rs;
+}
+
+extern void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops);
+
+void mc13783xc_uninit(void)
+{
+ /* Clear stuff from init */
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_OTG_SE0CONN);
+ pmic_convity_usb_otg_clear_config(pmic_handle, USBXCVREN);
+ pmic_convity_set_output(pmic_handle, false, false);
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_PULL_OVERRIDE);
+
+ /* Clear host mode */
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDP_PD);
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDM_PD);
+
+ /* Clear peripheral mode */
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_PU);
+
+ /* Vbus off */
+ pmic_convity_set_output(pmic_handle, true, false);
+ pmic_convity_usb_otg_clear_config(pmic_handle,
+ USB_VBUS_CURRENT_LIMIT_LOW_30MS);
+
+ usb_pmic_mod_exit();
+
+ fsl_usb_xcvr_unregister(&mc13783_ops_otg);
+}
+
+subsys_initcall(mc13783xc_init);
+module_exit(mc13783xc_uninit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("mc13783xc");
+MODULE_LICENSE("GPL");