[Meego-kernel] [PATCH 1/10] usb: penwell_otg: add USB charger detection

Wu, Hao hao.wu
Mon Sep 27 03:45:09 PDT 2010


>From 6b7b5bc07235285983ea4e2c3a85ab6f8354cc16 Mon Sep 17 00:00:00 2001
From: Hao Wu <hao.wu at intel.com>
Date: Mon, 27 Sep 2010 15:26:08 +0800
Subject: [PATCH] usb: penwell_otg: add USB charger detection.

This patch enables penwell USB OTG Transceiver driver USB Charger Detection
support. It can detect different types of USB charger based on MSIC.
SDP (Standard Downstream Port - USB Host port charger), DCP (Dedicated
Charging Port - USB Wall charger), CDP (Charging Downstream Port - Special
USB Host port charger).

This is one of the patches to support Charging via USB OTG port.

Please notice:
- CDP case is not tested yet due to no such device available.

Signed-off-by: Hao Wu <hao.wu at intel.com>
---
 drivers/usb/otg/penwell_otg.c   |  267 ++++++++++++++++++++++++++++++++++++++-
 include/linux/usb/penwell_otg.h |   67 ++++++++---
 2 files changed, 318 insertions(+), 16 deletions(-)

diff --git a/drivers/usb/otg/penwell_otg.c b/drivers/usb/otg/penwell_otg.c
index aaac6eb..58f9708 100644
--- a/drivers/usb/otg/penwell_otg.c
+++ b/drivers/usb/otg/penwell_otg.c
@@ -148,6 +148,10 @@ static int penwell_otg_set_peripheral(struct otg_transceiver *otg,
 static int penwell_otg_set_power(struct otg_transceiver *otg,
 				unsigned mA)
 {
+	struct penwell_otg	*pnw = the_transceiver;
+
+	dev_dbg(pnw->dev, "set_power = %d\n", mA);
+
 	return 0;
 }
 
@@ -410,6 +414,246 @@ static void penwell_otg_HABA(int on)
 					pnw->iotg.base + CI_OTGSC);
 }
 
+/* USB related register in MSIC can be access via SPI address and ulpi address
+ * Access the control register to switch */
+static void penwell_otg_msic_spi_access(bool enabled)
+{
+	struct penwell_otg	*pnw = the_transceiver;
+	u16			addr;
+	u8			data, mask;
+
+	/* Set ULPI ACCESS MODE */
+	addr = MSIC_ULPIACCESSMODE;
+	data = enabled ? SPIMODE : 0;
+	mask = SPIMODE;
+
+	dev_dbg(pnw->dev, "%s --->\n", __func__);
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return ;
+	}
+
+	dev_dbg(pnw->dev, "%s <---\n", __func__);
+}
+
+/* USB Battery Charger detection related functions */
+/* Data contact detection is the first step for charger detection */
+static int penwell_otg_data_contact_detect(void)
+{
+	struct penwell_otg	*pnw = the_transceiver;
+	u16			addr;
+	u8			data;
+	int			count = 10;
+
+	dev_dbg(pnw->dev, "%s --->\n", __func__);
+
+	/* Enable SPI access */
+	penwell_otg_msic_spi_access(true);
+
+	/* Set POWER_CTRL_CLR */
+	addr = MSIC_PWRCTRLCLR;
+	data = DPVSRCEN;
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return -EBUSY;
+	}
+
+	/* Set FUNC_CTRL_SET */
+	addr = MSIC_FUNCTRLSET;
+	data = OPMODE0;
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return -EBUSY;
+	}
+
+	/* Set FUNC_CTRL_CLR */
+	addr = MSIC_FUNCTRLCLR;
+	data = OPMODE1;
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return -EBUSY;
+	}
+
+	/* Set OTG_CTRL_CLR */
+	addr = MSIC_OTGCTRLCLR;
+	data = DMPULLDOWN | DPPULLDOWN;
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return -EBUSY;
+	}
+
+	/* Set POWER_CTRL_CLR */
+	addr = MSIC_PWRCTRLCLR;
+	data = SWCNTRL;
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return -EBUSY;
+	}
+
+	dev_warn(pnw->dev, "start to set SW_USB_DET in VS3\n");
+
+	addr = MSIC_VS3SET;
+	data = DATACONEN | SWUSBDET;
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return -EBUSY;
+	}
+
+	dev_dbg(pnw->dev, "Start Polling for Data contact detection!!!!\n");
+
+	while (count) {
+		if (intel_scu_ipc_ioread8(MSIC_PWRCTRL, &data)) {
+			dev_warn(pnw->dev,
+				"Failed to update MSIC register %x\n", addr);
+			return -EBUSY;
+		}
+		dev_warn(pnw->dev, "Reg MSIC_PWRCTRL addr = %x, data = %x\n",
+							MSIC_VS3, data);
+
+		if (data & DPVSRCEN) {
+			dev_dbg(pnw->dev, "Data contact detected!!\n");
+			return 0;
+		}
+		count--;
+		/* Interval is 50ms */
+		msleep(50);
+	}
+
+	dev_dbg(pnw->dev, "Data contact Timeout\n");
+
+	dev_dbg(pnw->dev, "Reset the HW Battery charger detection FSM\n");
+
+	addr = MSIC_VS3CLR;
+	data = DATACONEN | SWUSBDET;
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return -EBUSY;
+	}
+
+	udelay(100);
+
+	addr = MSIC_VS3SET;
+	data = SWUSBDET;
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return -EBUSY;
+	}
+
+	dev_dbg(pnw->dev, "%s <---\n", __func__);
+	return 0;
+}
+
+static int penwell_otg_charger_detect(void)
+{
+	struct penwell_otg		*pnw = the_transceiver;
+
+	dev_dbg(pnw->dev, "%s --->\n", __func__);
+
+	msleep(125);
+
+	dev_dbg(pnw->dev, "%s <---\n", __func__);
+
+	return 0;
+}
+
+static enum usb_charger_type penwell_otg_charger_type_detect(void)
+{
+	struct penwell_otg		*pnw = the_transceiver;
+	u16				addr;
+	u8				data;
+	enum usb_charger_type		charger;
+
+	dev_dbg(pnw->dev, "%s --->\n", __func__);
+
+	addr = MSIC_VS3CLR;
+	data = DATACONEN;
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return CHRG_UNKNOWN;
+	}
+	dev_dbg(pnw->dev, "write ADDR = %x, DATA = %x\n", addr, data);
+
+	addr = MSIC_PWRCTRLSET;
+	data = DPWKPUEN | SWCNTRL;
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return CHRG_UNKNOWN;
+	}
+	dev_dbg(pnw->dev, "write ADDR = %x, DATA = %x\n", addr, data);
+
+	addr = MSIC_PWRCTRLCLR;
+	data = DPVSRCEN;
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return CHRG_UNKNOWN;
+	}
+	dev_dbg(pnw->dev, "write ADDR = %x, DATA = %x\n", addr, data);
+
+	addr = MSIC_OTGCTRLCLR;
+	data = DMPULLDOWN | DPPULLDOWN;
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return CHRG_UNKNOWN;
+	}
+	dev_dbg(pnw->dev, "write ADDR = %x, DATA = %x\n", addr, data);
+
+	msleep(55);
+
+	addr = MSIC_PWRCTRLCLR;
+	data = SWCNTRL | DPWKPUEN | HWDET;
+
+	if (intel_scu_ipc_iowrite8(addr, data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return CHRG_UNKNOWN;
+	}
+	dev_dbg(pnw->dev, "write ADDR = %x, DATA = %x\n", addr, data);
+
+	msleep(1);
+
+	/* Enable ULPI mode */
+	penwell_otg_msic_spi_access(false);
+
+	if (intel_scu_ipc_ioread8(MSIC_SPWRSRINT1, &data)) {
+		dev_warn(pnw->dev, "Failed to update MSIC register %x\n", addr);
+		return CHRG_UNKNOWN;
+	}
+	dev_dbg(pnw->dev, "After MSIC_PWRCTRL register write\n");
+	dev_dbg(pnw->dev, "Reg MSIC_PWRCTRL addr = %x, data = %x\n",
+							MSIC_PWRCTRL, data);
+
+	switch (data & MSIC_SPWRSRINT1_MASK) {
+	case SPWRSRINT1_SDP:
+		charger = CHRG_SDP;
+		break;
+	case SPWRSRINT1_DCP:
+		charger = CHRG_DCP;
+		break;
+	case SPWRSRINT1_CDP:
+		charger = CHRG_CDP;
+		break;
+	default:
+		charger = CHRG_UNKNOWN;
+		break;
+	}
+
+	dev_dbg(pnw->dev, "%s <---\n", __func__);
+
+	return charger;
+}
+
 void penwell_otg_nsf_msg(unsigned long indicator)
 {
 	switch (indicator) {
@@ -845,6 +1089,7 @@ static void penwell_otg_work(struct work_struct *work)
 					struct penwell_otg, work);
 	struct intel_mid_otg_xceiv	*iotg = &pnw->iotg;
 	struct otg_hsm			*hsm = &iotg->hsm;
+	enum usb_charger_type		charger_type;
 
 	dev_dbg(pnw->dev,
 		"old state = %s\n", state_string(iotg->otg.state));
@@ -905,6 +1150,27 @@ static void penwell_otg_work(struct work_struct *work)
 			hsm->b_sess_end = 0;
 			hsm->a_bus_suspend = 0;
 
+			/* Start USB Battery charger detection flow */
+
+			/* Enable data contact detection */
+			penwell_otg_data_contact_detect();
+			/* Enable charger detection functionality */
+			penwell_otg_charger_detect();
+			charger_type = penwell_otg_charger_type_detect();
+
+			if (charger_type == CHRG_DCP) {
+				dev_info(pnw->dev, "DCP detected\n");
+				penwell_otg_phy_low_power(1);
+				iotg->otg.set_power(&iotg->otg, CHRG_CURR_DCP);
+				break;
+			} else if (charger_type == CHRG_CDP) {
+				dev_info(pnw->dev, "CDP detected\n");
+				iotg->otg.set_power(&iotg->otg, CHRG_CURR_CDP);
+			} else if (charger_type == CHRG_SDP)
+				dev_info(pnw->dev, "SDP detected\n");
+			else if (charger_type == CHRG_UNKNOWN)
+				dev_info(pnw->dev, "Unknown Charger Found\n");
+
 			if (iotg->start_peripheral) {
 				iotg->start_peripheral(iotg);
 				iotg->otg.state = OTG_STATE_B_PERIPHERAL;
@@ -912,7 +1178,6 @@ static void penwell_otg_work(struct work_struct *work)
 				dev_dbg(pnw->dev, "client driver not loaded\n");
 				break;
 			}
-
 		} else if ((hsm->b_bus_req || hsm->power_up ||
 				hsm->adp_change) && !hsm->b_srp_fail_tmr) {
 			if ((hsm->b_ssend_srp && hsm->b_se0_srp) ||
diff --git a/include/linux/usb/penwell_otg.h b/include/linux/usb/penwell_otg.h
index 2408640..c09dd14 100644
--- a/include/linux/usb/penwell_otg.h
+++ b/include/linux/usb/penwell_otg.h
@@ -112,31 +112,33 @@
 #	define SUSBCHPDET		BIT(6)
 #	define SUSBDCDET		BIT(2)
 #	define MSIC_SPWRSRINT1_MASK	(BIT(6) | BIT(2))
-#	define SPWRSRINT1_CHRG_PORT	BIT(6)
-#	define SPWRSRINT1_HOST_PORT	0
-#	define SPWRSRINT1_DEDT_CHRG	(BIT(6) | BIT(2))
+#	define SPWRSRINT1_CDP		BIT(6)
+#	define SPWRSRINT1_SDP		0
+#	define SPWRSRINT1_DCP		BIT(2)
 #define MSIC_IS4SET		0x2c8	/* Intel Specific */
 #	define IS4_CHGDSERXDPINV	BIT(5)
+#define MSIC_OTGCTRL		0x39c
 #define MSIC_OTGCTRLSET		0x340
 #define MSIC_OTGCTRLCLR		0x341
-#	define DMPULLDOWNCLR		BIT(2)
-#	define DPPULLDOWNCLR		BIT(1)
+#	define DMPULLDOWN		BIT(2)
+#	define DPPULLDOWN		BIT(1)
+#define MSIC_PWRCTRL		0x3b5
 #define MSIC_PWRCTRLSET		0x342
-#	define DPWKPUENSET		BIT(4)
-#	define SWCNTRLSET		BIT(0)
 #define MSIC_PWRCTRLCLR		0x343
-#	define DPVSRCENCLR		BIT(6)
-#	define SWCNTRLCLR		BIT(0)
+#	define HWDET			BIT(7)
+#	define DPVSRCEN			BIT(6)
+#	define DPWKPUEN			BIT(4)
+#	define SWCNTRL			BIT(0)
+#define MSIC_FUNCTRL		0x398
 #define MSIC_FUNCTRLSET		0x344
-#	define OPMODESET0		BIT(3)
 #define MSIC_FUNCTRLCLR		0x345
-#	define OPMODECLR1		BIT(4)
+#	define OPMODE1			BIT(4)
+#	define OPMODE0			BIT(3)
+#define MSIC_VS3		0x3b9
 #define MSIC_VS3SET		0x346	/* Vendor Specific */
-#	define SWUSBDETSET		BIT(4)
-#	define DATACONENSET		BIT(3)
 #define MSIC_VS3CLR		0x347
-#	define SWUSBDETCLR		BIT(4)
-#	define DATACONENCLR		BIT(3)
+#	define SWUSBDET			BIT(4)
+#	define DATACONEN		BIT(3)
 #define MSIC_ULPIACCESSMODE	0x348
 #	define SPIMODE			BIT(0)
 
@@ -210,6 +212,10 @@
 
 #define FS_ADPI_MASK	(ADPIS_ADPRAMPI | ADPIS_SNSMISSI | ADPIS_PRBTRGI)
 
+/* define Data connect checking timeout and polling interval */
+#define DATACON_TIMEOUT		1000
+#define DATACON_INTERVAL	50
+
 enum penwell_otg_timer_type {
 	TA_WAIT_VRISE_TMR,
 	TA_WAIT_BCON_TMR,
@@ -242,11 +248,42 @@ enum msic_vendor {
 	MSIC_VD_UNKNOWN
 };
 
+/* charger defined in BC 1.1 */
+enum usb_charger_type {
+	CHRG_UNKNOWN,
+	CHRG_SDP,	/* Standard Downstream Port */
+	CHRG_CDP,	/* Charging Downstream Port */
+	CHRG_DCP,	/* Dedicated Charging Port */
+	CHRG_ACA	/* Accessory Charger Adapter */
+};
+
 struct adp_status {
 	struct completion	adp_comp;
 	u8			t_adp_rise;
 };
 
+/* OTG Battery Charging capability is used in charger capability detection */
+struct otg_bc_cap {
+	enum usb_charger_type	chrg_type;
+	unsigned int		mA;
+#define CHRG_CURR_UNKNOWN	0
+#define CHRG_CURR_DISCONN	0
+#define CHRG_CURR_SDP_SUSP	2
+#define CHRG_CURR_SDP_LOW	100
+#define CHRG_CURR_SDP_HIGH	500
+#define CHRG_CURR_CDP		500
+#define CHRG_CURR_CDP_HS	900
+#define CHRG_CURR_DCP	1500
+#define CHRG_CURR_ACA	1500
+};
+
+/* define event ids to notify battery driver */
+#define USBCHRG_EVENT_CONNECT	1
+#define USBCHRG_EVENT_DISCONN	2
+#define USBCHRG_EVENT_SUSPEND	3
+#define USBCHRG_EVENT_RESUME	4
+#define USBCHRG_EVENT_UPDATE	5
+
 struct penwell_otg {
 	struct intel_mid_otg_xceiv	iotg;
 	struct device			*dev;
-- 
1.5.5

-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-usb-penwell_otg-add-USB-charger-detection.patch
Type: application/octet-stream
Size: 12114 bytes
Desc: 0001-usb-penwell_otg-add-USB-charger-detection.patch
URL: <http://lists.meego.com/pipermail/meego-kernel/attachments/20100927/f9971669/attachment-0001.obj>




More information about the Meego-kernel mailing list