mfd: pm8xxx-misc: Add function to write aux clock register

This call configures the XO to the MP3 clocks. These
clocks route over PMIC gpios and are used as an independent
clock source for MP3, haptics.

Change-Id: Ib95f277eae8bcb9a7e9da12fc5fe2baec4e72493
Signed-off-by: Amy Maloche <amaloche@codeaurora.org>
diff --git a/drivers/mfd/pm8xxx-misc.c b/drivers/mfd/pm8xxx-misc.c
index b61287c..b655848 100644
--- a/drivers/mfd/pm8xxx-misc.c
+++ b/drivers/mfd/pm8xxx-misc.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -139,6 +139,12 @@
 #define PM8901_DELAY_AFTER_REG_DISABLE_MS	4
 #define PM8901_DELAY_BEFORE_SHUTDOWN_MS		8
 
+#define REG_PM8XXX_XO_CNTRL_2	0x114
+#define MP3_1_MASK	0xE0
+#define MP3_2_MASK	0x1C
+#define MP3_1_SHIFT	5
+#define MP3_2_SHIFT	2
+
 struct pm8xxx_misc_chip {
 	struct list_head			link;
 	struct pm8xxx_misc_platform_data	pdata;
@@ -1046,6 +1052,48 @@
 }
 EXPORT_SYMBOL_GPL(pm8xxx_preload_dVdd);
 
+int pm8xxx_aux_clk_control(enum pm8xxx_aux_clk_id clk_id,
+				enum pm8xxx_aux_clk_div divider, bool enable)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	u8 clk_mask = 0, value = 0;
+
+	if (clk_id == CLK_MP3_1) {
+		clk_mask = MP3_1_MASK;
+		value = divider << MP3_1_SHIFT;
+	} else if (clk_id == CLK_MP3_2) {
+		clk_mask = MP3_2_MASK;
+		value = divider << MP3_2_SHIFT;
+	} else {
+		pr_err("Invalid clock id of %d\n", clk_id);
+		return -EINVAL;
+	}
+	if (!enable)
+		value = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8038:
+		case PM8XXX_VERSION_8921:
+			pm8xxx_misc_masked_write(chip,
+					REG_PM8XXX_XO_CNTRL_2, clk_mask, value);
+			break;
+		default:
+			/* Functionality not supported */
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_aux_clk_control);
+
 static int __devinit pm8xxx_misc_probe(struct platform_device *pdev)
 {
 	const struct pm8xxx_misc_platform_data *pdata = pdev->dev.platform_data;
diff --git a/include/linux/mfd/pm8xxx/misc.h b/include/linux/mfd/pm8xxx/misc.h
index c4f601b..c90d8d1 100644
--- a/include/linux/mfd/pm8xxx/misc.h
+++ b/include/linux/mfd/pm8xxx/misc.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -73,6 +73,22 @@
 	PM8XXX_RESTART_ON_HARD_RESET,
 };
 
+enum pm8xxx_aux_clk_id {
+	CLK_MP3_1,
+	CLK_MP3_2,
+};
+
+enum pm8xxx_aux_clk_div {
+	X0_DIV_NONE,
+	XO_DIV_1,
+	XO_DIV_2,
+	XO_DIV_4,
+	XO_DIV_8,
+	XO_DIV_16,
+	XO_DIV_32,
+	XO_DIV_64,
+};
+
 #if defined(CONFIG_MFD_PM8XXX_MISC) || defined(CONFIG_MFD_PM8XXX_MISC_MODULE)
 
 /**
@@ -181,6 +197,19 @@
  */
 int pm8xxx_usb_id_pullup(int enable);
 
+/**
+ * pm8xxx_aux_clk_control - Control an auxiliary clock
+ * @clk_id: ID of clock to be programmed, registers of XO_CNTRL2
+ * @divider: divisor to use when configuring desired clock
+ * @enable: enable (1) the designated clock with the supplied division,
+ *		or disable (0) the designated clock
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_aux_clk_control(enum pm8xxx_aux_clk_id clk_id,
+				enum pm8xxx_aux_clk_div divider,
+				bool enable);
+
 #else
 
 static inline int pm8xxx_reset_pwr_off(int reset)
@@ -225,6 +254,12 @@
 {
 	return -ENODEV;
 }
+static inline int pm8xxx_aux_clk_control(enum pm8xxx_aux_clk_id clk_id,
+			enum pm8xxx_aux_clk_div divider, bool enable)
+{
+	return -ENODEV;
+}
+
 #endif
 
 #endif