diff options
| author | Devesh Jhunjhunwala <deveshj@codeaurora.org> | 2016-02-16 13:26:20 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-25 16:02:30 -0700 |
| commit | a6d5966010c2da3f4240dd6ce34cc1145bd6c97b (patch) | |
| tree | 261fdf16428985ee63f42b007805567a0469f376 | |
| parent | bdc703239b44dab7cf0322b314f416d864da0e0e (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.txt | 12 | ||||
| -rw-r--r-- | drivers/misc/qpnp-misc.c | 175 |
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; } |
