Loading Documentation/devicetree/bindings/misc/qpnp-misc.txt +7 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,13 @@ Optional properties: if a non-zero PWM source is specified under "qcom,pwm-sel" property. - qcom,support-twm-config Enable configuration for TWM mode. - qcom,twm-mode The TWM mode which PMIC enters post power-off. Valid only if 'qcom,support-twm-config' is defined. If not specified, the default mode is 3. Example: qcom,spmi@fc4c0000 { #address-cells = <1>; Loading Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt +4 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,10 @@ Optional properties: but this applies for the system shutdown case. - qcom,kpdpwr-sw-debounce Boolean property to enable the debounce logic on the KPDPWR_N rising edge. - qcom,support-twm-config Boolean property to allow the PON module to be configured to support TWM modes. - qcom,pbs-client Phandle of the PBS client node. Should be defined if 'qcom,support-twm-config' is present. All the below properties are in the sub-node section (properties of the child Loading arch/arm/boot/dts/qcom/msm-pm660.dtsi +3 −3 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ reg = <0x900 0x100>; }; qcom,power-on@800 { pm660_pon: qcom,power-on@800 { compatible = "qcom,qpnp-power-on"; reg = <0x800 0x100>; interrupts = <0x0 0x8 0x0>, Loading Loading @@ -568,9 +568,9 @@ }; }; pm660_pbs: qcom,pbs@7300 { pm660_pbs: qcom,pbs@7400 { compatible = "qcom,qpnp-pbs"; reg = <0x7300 0x100>; reg = <0x7400 0x100>; status = "disabled"; }; Loading arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +13 −0 Original line number Diff line number Diff line Loading @@ -226,3 +226,16 @@ qcom,out-strength = <2>; }; }; &pm660_misc { qcom,support-twm-config; }; &pm660_pbs { status = "okay"; }; &pm660_pon { qcom,support-twm-config; qcom,pbs-client = <&pm660_pbs>; }; drivers/misc/qpnp-misc.c +147 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <linux/spmi.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/notifier.h> #include <linux/qpnp-misc.h> #define QPNP_MISC_DEV_NAME "qcom,qpnp-misc" Loading @@ -29,8 +30,20 @@ #define PWM_SEL_MAX 0x03 #define GP_DRIVER_EN_BIT BIT(0) enum twm { TWM_MODE_1 = 1, TWM_MODE_2, TWM_MODE_3, }; enum twm_attrib { TWM_ENABLE, TWM_EXIT, }; static DEFINE_MUTEX(qpnp_misc_dev_list_mutex); static LIST_HEAD(qpnp_misc_dev_list); static RAW_NOTIFIER_HEAD(twm_notifier); struct qpnp_misc_version { u8 subtype; Loading @@ -57,9 +70,13 @@ struct qpnp_misc_dev { struct resource *resource; struct spmi_device *spmi; struct qpnp_misc_version version; struct class twm_class; u8 twm_mode; u8 pwm_sel; bool enable_gp_driver; bool support_twm_config; bool twm_enable; }; static struct of_device_id qpnp_misc_match_table[] = { Loading Loading @@ -207,9 +224,99 @@ int qpnp_misc_irqs_available(struct device *consumer_dev) return __misc_irqs_available(mdev_found); } #define MISC_SPARE_1 0x50 #define MISC_SPARE_2 0x51 #define ENABLE_TWM_MODE 0x80 #define DISABLE_TWM_MODE 0x0 #define TWM_EXIT_BIT BIT(0) static ssize_t twm_enable_store(struct class *c, struct class_attribute *attr, const char *buf, size_t count) { struct qpnp_misc_dev *mdev = container_of(c, struct qpnp_misc_dev, twm_class); u8 val = 0; ssize_t rc = 0; rc = kstrtou8(buf, 10, &val); if (rc < 0) return rc; mdev->twm_enable = val ? true : false; /* Notify the TWM state */ raw_notifier_call_chain(&twm_notifier, mdev->twm_enable ? PMIC_TWM_ENABLE : PMIC_TWM_CLEAR, NULL); return count; } static ssize_t twm_enable_show(struct class *c, struct class_attribute *attr, char *buf) { struct qpnp_misc_dev *mdev = container_of(c, struct qpnp_misc_dev, twm_class); return snprintf(buf, PAGE_SIZE, "%d\n", mdev->twm_enable); } static ssize_t twm_exit_show(struct class *c, struct class_attribute *attr, char *buf) { struct qpnp_misc_dev *mdev = container_of(c, struct qpnp_misc_dev, twm_class); ssize_t rc = 0; u8 val = 0; rc = qpnp_read_byte(mdev->spmi, mdev->resource->start + MISC_SPARE_1, &val); if (rc < 0) { pr_err("Failed to read TWM enable (misc_spare_1) rc=%d\n", rc); return rc; } pr_debug("TWM_EXIT (misc_spare_1) register = 0x%02x\n", val); return snprintf(buf, PAGE_SIZE, "%d\n", !!(val & TWM_EXIT_BIT)); } static struct class_attribute twm_attributes[] = { [TWM_ENABLE] = __ATTR(twm_enable, S_IRUGO | S_IWUSR, twm_enable_show, twm_enable_store), [TWM_EXIT] = __ATTR(twm_exit, S_IRUGO | S_IWUSR, twm_exit_show, NULL), __ATTR_NULL, }; int qpnp_misc_twm_notifier_register(struct notifier_block *nb) { return raw_notifier_chain_register(&twm_notifier, nb); } EXPORT_SYMBOL(qpnp_misc_twm_notifier_register); int qpnp_misc_twm_notifier_unregister(struct notifier_block *nb) { return raw_notifier_chain_unregister(&twm_notifier, nb); } EXPORT_SYMBOL(qpnp_misc_twm_notifier_unregister); static int qpnp_misc_dt_init(struct qpnp_misc_dev *mdev) { u32 val; int rc; if (of_property_read_bool(mdev->dev->of_node, "qcom,support-twm-config")) { mdev->support_twm_config = true; mdev->twm_mode = TWM_MODE_3; rc = of_property_read_u8(mdev->dev->of_node, "qcom,twm-mode", &mdev->twm_mode); if (!rc && (mdev->twm_mode < TWM_MODE_1 || mdev->twm_mode > TWM_MODE_3)) { pr_err("Invalid TWM mode %d\n", mdev->twm_mode); return -EINVAL; } } if (!of_property_read_u32(mdev->dev->of_node, "qcom,pwm-sel", &val)) { if (val > PWM_SEL_MAX) { Loading Loading @@ -261,6 +368,18 @@ static int qpnp_misc_config(struct qpnp_misc_dev *mdev) break; } if (mdev->support_twm_config) { mdev->twm_class.name = "pmic_twm", mdev->twm_class.owner = THIS_MODULE, mdev->twm_class.class_attrs = twm_attributes; rc = class_register(&mdev->twm_class); if (rc < 0) { pr_err("Failed to register pmic_twm class rc=%d\n", rc); return rc; } } return 0; } Loading @@ -283,6 +402,7 @@ static int qpnp_misc_probe(struct spmi_device *spmi) mdev->spmi = spmi; mdev->dev = &(spmi->dev); mdev->resource = resource; dev_set_drvdata(&spmi->dev, mdev); rc = qpnp_read_byte(spmi, resource->start + REG_SUBTYPE, &mdev->version.subtype); Loading Loading @@ -320,8 +440,35 @@ static int qpnp_misc_probe(struct spmi_device *spmi) return 0; } static void qpnp_misc_shutdown(struct spmi_device *spmi) { struct qpnp_misc_dev *mdev = dev_get_drvdata(&spmi->dev); int rc; if (mdev->support_twm_config) { rc = qpnp_write_byte(mdev->spmi, mdev->resource->start + MISC_SPARE_2, mdev->twm_enable ? mdev->twm_mode : 0x0); if (rc < 0) pr_err("Failed to write MISC_SPARE_2 (twm_mode) val=%d rc=%d\n", mdev->twm_enable ? mdev->twm_mode : 0x0, rc); rc = qpnp_write_byte(mdev->spmi, mdev->resource->start + MISC_SPARE_1, mdev->twm_enable ? ENABLE_TWM_MODE : 0x0); if (rc < 0) pr_err("Failed to write MISC_SPARE_1 (twm_state) val=%d rc=%d\n", mdev->twm_enable ? ENABLE_TWM_MODE : 0x0, rc); pr_debug("PMIC configured for TWM-%s MODE=%d\n", mdev->twm_enable ? "enabled" : "disabled", mdev->twm_mode); } } static struct spmi_driver qpnp_misc_driver = { .probe = qpnp_misc_probe, .shutdown = qpnp_misc_shutdown, .driver = { .name = QPNP_MISC_DEV_NAME, .owner = THIS_MODULE, Loading Loading
Documentation/devicetree/bindings/misc/qpnp-misc.txt +7 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,13 @@ Optional properties: if a non-zero PWM source is specified under "qcom,pwm-sel" property. - qcom,support-twm-config Enable configuration for TWM mode. - qcom,twm-mode The TWM mode which PMIC enters post power-off. Valid only if 'qcom,support-twm-config' is defined. If not specified, the default mode is 3. Example: qcom,spmi@fc4c0000 { #address-cells = <1>; Loading
Documentation/devicetree/bindings/platform/msm/qpnp-power-on.txt +4 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,10 @@ Optional properties: but this applies for the system shutdown case. - qcom,kpdpwr-sw-debounce Boolean property to enable the debounce logic on the KPDPWR_N rising edge. - qcom,support-twm-config Boolean property to allow the PON module to be configured to support TWM modes. - qcom,pbs-client Phandle of the PBS client node. Should be defined if 'qcom,support-twm-config' is present. All the below properties are in the sub-node section (properties of the child Loading
arch/arm/boot/dts/qcom/msm-pm660.dtsi +3 −3 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ reg = <0x900 0x100>; }; qcom,power-on@800 { pm660_pon: qcom,power-on@800 { compatible = "qcom,qpnp-power-on"; reg = <0x800 0x100>; interrupts = <0x0 0x8 0x0>, Loading Loading @@ -568,9 +568,9 @@ }; }; pm660_pbs: qcom,pbs@7300 { pm660_pbs: qcom,pbs@7400 { compatible = "qcom,qpnp-pbs"; reg = <0x7300 0x100>; reg = <0x7400 0x100>; status = "disabled"; }; Loading
arch/arm/boot/dts/qcom/msm8909w-pm660-mtp.dtsi +13 −0 Original line number Diff line number Diff line Loading @@ -226,3 +226,16 @@ qcom,out-strength = <2>; }; }; &pm660_misc { qcom,support-twm-config; }; &pm660_pbs { status = "okay"; }; &pm660_pon { qcom,support-twm-config; qcom,pbs-client = <&pm660_pbs>; };
drivers/misc/qpnp-misc.c +147 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <linux/spmi.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/notifier.h> #include <linux/qpnp-misc.h> #define QPNP_MISC_DEV_NAME "qcom,qpnp-misc" Loading @@ -29,8 +30,20 @@ #define PWM_SEL_MAX 0x03 #define GP_DRIVER_EN_BIT BIT(0) enum twm { TWM_MODE_1 = 1, TWM_MODE_2, TWM_MODE_3, }; enum twm_attrib { TWM_ENABLE, TWM_EXIT, }; static DEFINE_MUTEX(qpnp_misc_dev_list_mutex); static LIST_HEAD(qpnp_misc_dev_list); static RAW_NOTIFIER_HEAD(twm_notifier); struct qpnp_misc_version { u8 subtype; Loading @@ -57,9 +70,13 @@ struct qpnp_misc_dev { struct resource *resource; struct spmi_device *spmi; struct qpnp_misc_version version; struct class twm_class; u8 twm_mode; u8 pwm_sel; bool enable_gp_driver; bool support_twm_config; bool twm_enable; }; static struct of_device_id qpnp_misc_match_table[] = { Loading Loading @@ -207,9 +224,99 @@ int qpnp_misc_irqs_available(struct device *consumer_dev) return __misc_irqs_available(mdev_found); } #define MISC_SPARE_1 0x50 #define MISC_SPARE_2 0x51 #define ENABLE_TWM_MODE 0x80 #define DISABLE_TWM_MODE 0x0 #define TWM_EXIT_BIT BIT(0) static ssize_t twm_enable_store(struct class *c, struct class_attribute *attr, const char *buf, size_t count) { struct qpnp_misc_dev *mdev = container_of(c, struct qpnp_misc_dev, twm_class); u8 val = 0; ssize_t rc = 0; rc = kstrtou8(buf, 10, &val); if (rc < 0) return rc; mdev->twm_enable = val ? true : false; /* Notify the TWM state */ raw_notifier_call_chain(&twm_notifier, mdev->twm_enable ? PMIC_TWM_ENABLE : PMIC_TWM_CLEAR, NULL); return count; } static ssize_t twm_enable_show(struct class *c, struct class_attribute *attr, char *buf) { struct qpnp_misc_dev *mdev = container_of(c, struct qpnp_misc_dev, twm_class); return snprintf(buf, PAGE_SIZE, "%d\n", mdev->twm_enable); } static ssize_t twm_exit_show(struct class *c, struct class_attribute *attr, char *buf) { struct qpnp_misc_dev *mdev = container_of(c, struct qpnp_misc_dev, twm_class); ssize_t rc = 0; u8 val = 0; rc = qpnp_read_byte(mdev->spmi, mdev->resource->start + MISC_SPARE_1, &val); if (rc < 0) { pr_err("Failed to read TWM enable (misc_spare_1) rc=%d\n", rc); return rc; } pr_debug("TWM_EXIT (misc_spare_1) register = 0x%02x\n", val); return snprintf(buf, PAGE_SIZE, "%d\n", !!(val & TWM_EXIT_BIT)); } static struct class_attribute twm_attributes[] = { [TWM_ENABLE] = __ATTR(twm_enable, S_IRUGO | S_IWUSR, twm_enable_show, twm_enable_store), [TWM_EXIT] = __ATTR(twm_exit, S_IRUGO | S_IWUSR, twm_exit_show, NULL), __ATTR_NULL, }; int qpnp_misc_twm_notifier_register(struct notifier_block *nb) { return raw_notifier_chain_register(&twm_notifier, nb); } EXPORT_SYMBOL(qpnp_misc_twm_notifier_register); int qpnp_misc_twm_notifier_unregister(struct notifier_block *nb) { return raw_notifier_chain_unregister(&twm_notifier, nb); } EXPORT_SYMBOL(qpnp_misc_twm_notifier_unregister); static int qpnp_misc_dt_init(struct qpnp_misc_dev *mdev) { u32 val; int rc; if (of_property_read_bool(mdev->dev->of_node, "qcom,support-twm-config")) { mdev->support_twm_config = true; mdev->twm_mode = TWM_MODE_3; rc = of_property_read_u8(mdev->dev->of_node, "qcom,twm-mode", &mdev->twm_mode); if (!rc && (mdev->twm_mode < TWM_MODE_1 || mdev->twm_mode > TWM_MODE_3)) { pr_err("Invalid TWM mode %d\n", mdev->twm_mode); return -EINVAL; } } if (!of_property_read_u32(mdev->dev->of_node, "qcom,pwm-sel", &val)) { if (val > PWM_SEL_MAX) { Loading Loading @@ -261,6 +368,18 @@ static int qpnp_misc_config(struct qpnp_misc_dev *mdev) break; } if (mdev->support_twm_config) { mdev->twm_class.name = "pmic_twm", mdev->twm_class.owner = THIS_MODULE, mdev->twm_class.class_attrs = twm_attributes; rc = class_register(&mdev->twm_class); if (rc < 0) { pr_err("Failed to register pmic_twm class rc=%d\n", rc); return rc; } } return 0; } Loading @@ -283,6 +402,7 @@ static int qpnp_misc_probe(struct spmi_device *spmi) mdev->spmi = spmi; mdev->dev = &(spmi->dev); mdev->resource = resource; dev_set_drvdata(&spmi->dev, mdev); rc = qpnp_read_byte(spmi, resource->start + REG_SUBTYPE, &mdev->version.subtype); Loading Loading @@ -320,8 +440,35 @@ static int qpnp_misc_probe(struct spmi_device *spmi) return 0; } static void qpnp_misc_shutdown(struct spmi_device *spmi) { struct qpnp_misc_dev *mdev = dev_get_drvdata(&spmi->dev); int rc; if (mdev->support_twm_config) { rc = qpnp_write_byte(mdev->spmi, mdev->resource->start + MISC_SPARE_2, mdev->twm_enable ? mdev->twm_mode : 0x0); if (rc < 0) pr_err("Failed to write MISC_SPARE_2 (twm_mode) val=%d rc=%d\n", mdev->twm_enable ? mdev->twm_mode : 0x0, rc); rc = qpnp_write_byte(mdev->spmi, mdev->resource->start + MISC_SPARE_1, mdev->twm_enable ? ENABLE_TWM_MODE : 0x0); if (rc < 0) pr_err("Failed to write MISC_SPARE_1 (twm_state) val=%d rc=%d\n", mdev->twm_enable ? ENABLE_TWM_MODE : 0x0, rc); pr_debug("PMIC configured for TWM-%s MODE=%d\n", mdev->twm_enable ? "enabled" : "disabled", mdev->twm_mode); } } static struct spmi_driver qpnp_misc_driver = { .probe = qpnp_misc_probe, .shutdown = qpnp_misc_shutdown, .driver = { .name = QPNP_MISC_DEV_NAME, .owner = THIS_MODULE, Loading