summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDevesh Jhunjhunwala <deveshj@codeaurora.org>2016-02-16 13:26:20 -0800
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-25 16:02:30 -0700
commita6d5966010c2da3f4240dd6ce34cc1145bd6c97b (patch)
tree261fdf16428985ee63f42b007805567a0469f376
parentbdc703239b44dab7cf0322b314f416d864da0e0e (diff)
msm: qpnp-misc: add support to configure PWM source
Add the qcom,pwm-sel and qcom,enable-gp-driver properties to the MISC device driver to enable configuring the PWM source. Currently this will be supported only for pmdcalifornium. CRs-Fixed: 972331 Change-Id: I320edac22b20d748a531a41eaff06162092cfd3d Signed-off-by: Devesh Jhunjhunwala <deveshj@codeaurora.org>
-rw-r--r--Documentation/devicetree/bindings/misc/qpnp-misc.txt12
-rw-r--r--drivers/misc/qpnp-misc.c175
2 files changed, 159 insertions, 28 deletions
diff --git a/Documentation/devicetree/bindings/misc/qpnp-misc.txt b/Documentation/devicetree/bindings/misc/qpnp-misc.txt
index 34c344a8de76..9de6857a2643 100644
--- a/Documentation/devicetree/bindings/misc/qpnp-misc.txt
+++ b/Documentation/devicetree/bindings/misc/qpnp-misc.txt
@@ -6,6 +6,16 @@ Required properties:
- compatible : should be "qcom,qpnp-misc"
- reg : offset and length of the PMIC peripheral register map.
+Optional properties:
+- qcom,pwm-sel: Select PWM source. Possible values:
+ 0: LOW
+ 1: PWM1_in
+ 2: PWM2_in
+ 3: PWM1_in & PWM2_in
+- qcom,enable-gp-driver: Enable the GP driver. Should only be specified
+ if a non-zero PWM source is specified under
+ "qcom,pwm-sel" property.
+
Example:
qcom,spmi@fc4c0000 {
#address-cells = <1>;
@@ -22,6 +32,8 @@ Example:
qcom,misc@900 {
compatible = "qcom,qpnp-misc";
reg = <0x900 0x100>;
+ qcom,pwm-sel = <2>;
+ qcom,enable-gp-driver;
};
}
};
diff --git a/drivers/misc/qpnp-misc.c b/drivers/misc/qpnp-misc.c
index 82b5b6bec49a..5b321b17c43e 100644
--- a/drivers/misc/qpnp-misc.c
+++ b/drivers/misc/qpnp-misc.c
@@ -23,10 +23,20 @@
#define REG_DIG_MAJOR_REV 0x01
#define REG_SUBTYPE 0x05
+#define REG_PWM_SEL 0x49
+#define REG_GP_DRIVER_EN 0x4C
+
+#define PWM_SEL_MAX 0x03
+#define GP_DRIVER_EN_BIT BIT(0)
static DEFINE_MUTEX(qpnp_misc_dev_list_mutex);
static LIST_HEAD(qpnp_misc_dev_list);
+struct qpnp_misc_version {
+ u8 subtype;
+ u8 dig_major_rev;
+};
+
/**
* struct qpnp_misc_dev - holds controller device specific information
* @list: Doubly-linked list parameter linking to other
@@ -37,6 +47,8 @@ static LIST_HEAD(qpnp_misc_dev_list);
* @dev: Device pointer to the misc device
* @resource: Resource pointer that holds base address
* @spmi: Spmi pointer which holds spmi information
+ * @version: struct that holds the subtype and dig_major_rev
+ * of the chip.
*/
struct qpnp_misc_dev {
struct list_head list;
@@ -44,11 +56,10 @@ struct qpnp_misc_dev {
struct device *dev;
struct resource *resource;
struct spmi_device *spmi;
-};
+ struct qpnp_misc_version version;
-struct qpnp_misc_version {
- u8 subtype;
- u8 dig_major_rev;
+ u8 pwm_sel;
+ bool enable_gp_driver;
};
static struct of_device_id qpnp_misc_match_table[] = {
@@ -56,43 +67,65 @@ static struct of_device_id qpnp_misc_match_table[] = {
{}
};
-static u8 qpnp_read_byte(struct spmi_device *spmi, u16 addr)
+enum qpnp_misc_version_name {
+ INVALID,
+ PM8941,
+ PM8226,
+ PMA8084,
+ PMDCALIFORNIUM,
+};
+
+static struct qpnp_misc_version irq_support_version[] = {
+ {0x00, 0x00}, /* INVALID */
+ {0x01, 0x02}, /* PM8941 */
+ {0x07, 0x00}, /* PM8226 */
+ {0x09, 0x00}, /* PMA8084 */
+ {0x16, 0x00}, /* PMDCALIFORNIUM */
+};
+
+static int qpnp_write_byte(struct spmi_device *spmi, u16 addr, u8 val)
{
int rc;
- u8 val;
- rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, &val, 1);
+ rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, addr, &val, 1);
+ if (rc)
+ pr_err("SPMI write failed rc=%d\n", rc);
+
+ return rc;
+}
+
+static int qpnp_read_byte(struct spmi_device *spmi, u16 addr, u8 *val)
+{
+ int rc;
+
+ rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, val, 1);
if (rc) {
pr_err("SPMI read failed rc=%d\n", rc);
- return 0;
+ return rc;
}
- return val;
+ return rc;
}
-static struct qpnp_misc_version irq_support_version[] = {
- {0x01, 0x02}, /* PM8941 */
- {0x07, 0x00}, /* PM8226 */
- {0x09, 0x00}, /* PMA8084 */
-};
-
-static bool __misc_irqs_available(struct qpnp_misc_dev *dev)
+static int get_qpnp_misc_version_name(struct qpnp_misc_dev *dev)
{
int i;
- u8 subtype, dig_major_rev;
- subtype = qpnp_read_byte(dev->spmi, dev->resource->start + REG_SUBTYPE);
- pr_debug("subtype = 0x%02X\n", subtype);
+ for (i = 1; i < ARRAY_SIZE(irq_support_version); i++)
+ if (dev->version.subtype == irq_support_version[i].subtype &&
+ dev->version.dig_major_rev >=
+ irq_support_version[i].dig_major_rev)
+ return i;
- dig_major_rev = qpnp_read_byte(dev->spmi,
- dev->resource->start + REG_DIG_MAJOR_REV);
- pr_debug("dig_major rev = 0x%02X\n", dig_major_rev);
+ return INVALID;
+}
- for (i = 0; i < ARRAY_SIZE(irq_support_version); i++)
- if (subtype == irq_support_version[i].subtype
- && dig_major_rev >= irq_support_version[i].dig_major_rev)
- return 1;
+static bool __misc_irqs_available(struct qpnp_misc_dev *dev)
+{
+ int version_name = get_qpnp_misc_version_name(dev);
- return 0;
+ if (version_name == INVALID)
+ return 0;
+ return 1;
}
int qpnp_misc_irqs_available(struct device *consumer_dev)
@@ -133,10 +166,68 @@ int qpnp_misc_irqs_available(struct device *consumer_dev)
return __misc_irqs_available(mdev_found);
}
+static int qpnp_misc_dt_init(struct qpnp_misc_dev *mdev)
+{
+ u32 val;
+
+ if (!of_property_read_u32(mdev->dev->of_node, "qcom,pwm-sel", &val)) {
+ if (val > PWM_SEL_MAX) {
+ dev_err(mdev->dev, "Invalid value for pwm-sel\n");
+ return -EINVAL;
+ }
+ mdev->pwm_sel = (u8)val;
+ }
+ mdev->enable_gp_driver = of_property_read_bool(mdev->dev->of_node,
+ "qcom,enable-gp-driver");
+
+ WARN((mdev->pwm_sel > 0 && !mdev->enable_gp_driver),
+ "Setting PWM source without enabling gp driver\n");
+ WARN((mdev->pwm_sel == 0 && mdev->enable_gp_driver),
+ "Enabling gp driver without setting PWM source\n");
+
+ return 0;
+}
+
+static int qpnp_misc_config(struct qpnp_misc_dev *mdev)
+{
+ int rc, version_name;
+
+ version_name = get_qpnp_misc_version_name(mdev);
+
+ switch (version_name) {
+ case PMDCALIFORNIUM:
+ if (mdev->pwm_sel > 0 && mdev->enable_gp_driver) {
+ rc = qpnp_write_byte(mdev->spmi,
+ mdev->resource->start + REG_PWM_SEL,
+ mdev->pwm_sel);
+ if (rc < 0) {
+ dev_err(mdev->dev,
+ "Failed to write PWM_SEL reg\n");
+ return rc;
+ }
+
+ rc = qpnp_write_byte(mdev->spmi,
+ mdev->resource->start + REG_GP_DRIVER_EN,
+ GP_DRIVER_EN_BIT);
+ if (rc < 0) {
+ dev_err(mdev->dev,
+ "Failed to write GP_DRIVER_EN reg\n");
+ return rc;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int qpnp_misc_probe(struct spmi_device *spmi)
{
struct resource *resource;
struct qpnp_misc_dev *mdev = ERR_PTR(-EINVAL);
+ int rc;
resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
if (!resource) {
@@ -152,11 +243,39 @@ static int qpnp_misc_probe(struct spmi_device *spmi)
mdev->dev = &(spmi->dev);
mdev->resource = resource;
+ rc = qpnp_read_byte(spmi, resource->start + REG_SUBTYPE,
+ &mdev->version.subtype);
+ if (rc < 0) {
+ dev_err(mdev->dev, "Failed to read subtype, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_read_byte(spmi, resource->start + REG_DIG_MAJOR_REV,
+ &mdev->version.dig_major_rev);
+ if (rc < 0) {
+ dev_err(mdev->dev, "Failed to read dig_major_rev, rc=%d\n", rc);
+ return rc;
+ }
+
mutex_lock(&qpnp_misc_dev_list_mutex);
list_add_tail(&mdev->list, &qpnp_misc_dev_list);
mutex_unlock(&qpnp_misc_dev_list_mutex);
- pr_debug("probed successfully\n");
+ rc = qpnp_misc_dt_init(mdev);
+ if (rc < 0) {
+ dev_err(mdev->dev,
+ "Error reading device tree properties, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_misc_config(mdev);
+ if (rc < 0) {
+ dev_err(mdev->dev,
+ "Error configuring module registers, rc=%d\n", rc);
+ return rc;
+ }
+
+ dev_info(mdev->dev, "probe successful\n");
return 0;
}