* [PATCH V1 0/2] *** Add support for QTI TRI_LED and LPG module ***
@ 2017-05-31 6:14 fenglinw
2017-05-31 6:14 ` [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module fenglinw
2017-05-31 6:14 ` [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module fenglinw
0 siblings, 2 replies; 15+ messages in thread
From: fenglinw @ 2017-05-31 6:14 UTC (permalink / raw)
To: linux-arm-msm, linux-kernel; +Cc: subbaram, aghayal, wruan, kgunda, Fenglin Wu
From: Fenglin Wu <fenglinw@codeaurora.org>
QTI TRI_LED module and LPG modules are existing in some Qualcomm PMICs
like PMI8998, PM660, etc. TRI_LED module has 3 LED drivers and each is
controlled by a PWM channel used for LED dimming or blinking. LPG (Light
Pulse Generator) module can be configured to operate in PWM mode to
output PWM signal with a fixed amplitude or in LUT (Look Up Table) mode
to output PWM signal with a modulated amplitude. Normally, each LED driver
in TRI_LED module is controlled by the output of a LPG module.
This patchset adds the support of registering TRI_LED as LED devices and
adding LPGs as PWM devices, but only the LPG PWM mode is supported. LUT mode
is not included because current PWM driver framework doesn't support hardware
autonomously modulating amplitude with vary duties. This is going to be
supported in next patchset with some PWM framework change.
Fenglin Wu (2):
leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module
.../devicetree/bindings/leds/leds-qti-rgb.txt | 66 +++
.../devicetree/bindings/pwm/pwm-qti-lpg.txt | 39 ++
drivers/leds/Kconfig | 8 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-qti-rgb.c | 634 +++++++++++++++++++++
drivers/pwm/Kconfig | 10 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-qti-lpg.c | 578 +++++++++++++++++++
8 files changed, 1337 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/leds-qti-rgb.txt
create mode 100644 Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
create mode 100644 drivers/leds/leds-qti-rgb.c
create mode 100644 drivers/pwm/pwm-qti-lpg.c
--
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
2017-05-31 6:14 [PATCH V1 0/2] *** Add support for QTI TRI_LED and LPG module *** fenglinw
@ 2017-05-31 6:14 ` fenglinw
[not found] ` <20170531061541.10808-2-fenglinw-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2017-05-31 6:14 ` [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module fenglinw
1 sibling, 1 reply; 15+ messages in thread
From: fenglinw @ 2017-05-31 6:14 UTC (permalink / raw)
To: linux-arm-msm, linux-kernel, Richard Purdie, Jacek Anaszewski,
Pavel Machek, Rob Herring, Mark Rutland, linux-leds, devicetree
Cc: subbaram, aghayal, wruan, kgunda, Fenglin Wu
From: Fenglin Wu <fenglinw@codeaurora.org>
QTI TRI_LED module has 3 current sinks for LED driver and each is
controlled by a PWM channel used for LED dimming or blinking. Add
the driver to support it.
Signed-off-by: Fenglin Wu <fenglinw@codeaurora.org>
---
.../devicetree/bindings/leds/leds-qti-rgb.txt | 66 +++
drivers/leds/Kconfig | 8 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-qti-rgb.c | 634 +++++++++++++++++++++
4 files changed, 709 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/leds-qti-rgb.txt
create mode 100644 drivers/leds/leds-qti-rgb.c
diff --git a/Documentation/devicetree/bindings/leds/leds-qti-rgb.txt b/Documentation/devicetree/bindings/leds/leds-qti-rgb.txt
new file mode 100644
index 0000000..62daf38
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-qti-rgb.txt
@@ -0,0 +1,66 @@
+Qualcomm Technologies, Inc. TRI_LED driver specific bindings
+
+This binding document describes the properties of TRI_LED module in
+Qualcomm Technologies, Inc. PMIC chips.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Must be "qcom,leds-rgb".
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Register base of the TRI_LED module and length.
+
+- pwm-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: A list of string to label the PWM devices defined in pwms
+ property which are using for controlling LEDs.
+ It must be: "blue", "green", "red".
+
+- pwms:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: A list of the PWM devices (phandles) used for controlling
+ LEDs.
+
+- qcom,support-blink:
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: An array of integer values to indicate if "blue", "green", "red"
+ LEDs support blink control. The values are listed as the fixed
+ order for "blue", "green", "red" LEDs.
+
+- qcom,on-ms:
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: An array of time values (milli-seconds) to represent the
+ on duration for "blue", "green", "red" LEDs. The values are
+ listed as the fixed order for "blue", "green", "red" LEDs.
+ This property has to be defined if "qcom,support-blink" is
+ present.
+
+- qcom,off-ms:
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: An array of time values (milli-seconds) to represent the
+ off duration for "blue", "green", "red" LEDs. The values are
+ listed as the fixed order for "blue", "green", "red" LEDs.
+ This property has to be defined if "qcom,support-blink" is
+ present.
+
+Example:
+
+ pmi8998_rgb: rgb@d000{
+ compatible = "qcom,leds-rgb";
+ reg = <0xd000 0x100>;
+ pwm-names = "blue", "green", "red";
+ pwms = <&pmi8998_pwm_3 0 1000000>,
+ <&pmi8998_pwm_4 0 1000000>,
+ <&pmi8998_pwm_5 0 1000000>;
+ qcom,support-blink = <1 1 1>;
+ qcom,on-ms = <500 500 500>;
+ qcom,off-ms = <500 500 500>;
+ };
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 6c29998..8996bde 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -667,6 +667,14 @@ config LEDS_PM8058
Choose this option if you want to use the LED drivers in
the Qualcomm PM8058 PMIC.
+config LEDS_QTI_RGB
+ tristate "Qualcomm Technologies, Inc. TRI_LED driver"
+ depends on LEDS_CLASS && MFD_SPMI_PMIC && PWM && OF
+ help
+ This driver supports the TRI_LED module found in Qualcomm
+ Technologies, Inc. PMIC's chips. TRI_LED supports 3 LED drivers and
+ each is controlled by a PWM channel used for dimming or blinking.
+
config LEDS_MLXCPLD
tristate "LED support for the Mellanox boards"
depends on X86_64 && DMI
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 45f1339..d1a967c 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
+obj-$(CONFIG_LEDS_QTI_RGB) += leds-qti-rgb.o
obj-$(CONFIG_LEDS_GPIO_REGISTER) += leds-gpio-register.o
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
diff --git a/drivers/leds/leds-qti-rgb.c b/drivers/leds/leds-qti-rgb.c
new file mode 100644
index 0000000..0e363d4
--- /dev/null
+++ b/drivers/leds/leds-qti-rgb.c
@@ -0,0 +1,634 @@
+/* Copyright (c) 2017, The Linux Foundation. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#define REG_LED_SRC_SEL 0x45
+#define REG_LED_EN_CTL 0x46
+#define REG_LED_ATC_EN_CTL 0x47
+
+/* REG_LED_SRC_SEL */
+#define LED_SRC_SEL_MASK GENMASK(1, 0)
+#define LED_SRC_GND 0x00
+#define LED_SRC_VINRGB_VBOOST 0x01
+#define LED_SRC_VSYS 0x03
+
+/* REG_LED_EN_CTL */
+#define LED_EN_CTL_MASK GENMASK(7, 5)
+#define LED_EN_CTL_OFFSET 5
+
+/* REG_LED_ATC_EN_CTL */
+#define LED_ATC_EN_MASK GENMASK(7, 5)
+
+#define NUM_LEDS 3
+const char * const led_names[NUM_LEDS] = {"blue", "green", "red"};
+
+struct pwm_setting {
+ u32 initial_period_ns;
+ u32 period_ns;
+ u32 duty_ns;
+};
+
+struct led_setting {
+ u32 brightness;
+ u32 on_ms;
+ u32 off_ms;
+ bool blink;
+};
+
+struct qti_rgb_led_dev {
+ struct led_classdev cdev;
+ struct pwm_device *pwm_dev;
+ struct pwm_setting pwm_setting;
+ struct led_setting led_setting;
+ struct qti_rgb_chip *chip;
+ struct work_struct work;
+ struct mutex lock;
+ bool support_blink;
+ bool blinking;
+ u8 idx;
+};
+
+struct qti_rgb_chip {
+ struct device *dev;
+ struct regmap *regmap;
+ struct qti_rgb_led_dev leds[NUM_LEDS];
+ struct mutex bus_lock;
+ u16 reg_base;
+};
+
+static int qti_rgb_masked_write(struct qti_rgb_chip *chip,
+ u16 addr, u8 mask, u8 val)
+{
+ int rc;
+
+ mutex_lock(&chip->bus_lock);
+ rc = regmap_update_bits(chip->regmap, chip->reg_base + addr, mask, val);
+ if (rc < 0)
+ dev_err(chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
+ addr, val, mask, rc);
+ mutex_unlock(&chip->bus_lock);
+
+ return rc;
+}
+
+static int __rgb_led_config_pwm(struct qti_rgb_led_dev *led,
+ struct pwm_setting *pwm)
+{
+ int rc;
+
+ if (pwm->duty_ns == 0) {
+ pwm_disable(led->pwm_dev);
+ return 0;
+ }
+
+ rc = pwm_config(led->pwm_dev, pwm->duty_ns, pwm->period_ns);
+ if (rc < 0) {
+ dev_err(led->chip->dev, "Config PWM settings for %s led failed, rc=%d\n",
+ led->cdev.name, rc);
+ return rc;
+ }
+
+ rc = pwm_enable(led->pwm_dev);
+ if (rc < 0)
+ dev_err(led->chip->dev, "Enable PWM for %s led failed, rc=%d\n",
+ led->cdev.name, rc);
+
+ return rc;
+}
+
+static int __rgb_led_set(struct qti_rgb_led_dev *led)
+{
+ int rc = 0;
+ u8 val = 0, mask = 0;
+
+ rc = __rgb_led_config_pwm(led, &led->pwm_setting);
+ if (rc < 0) {
+ dev_err(led->chip->dev, "Configure PWM for %s led failed, rc=%d\n",
+ led->cdev.name, rc);
+ return rc;
+ }
+
+ mask |= 1 << (led->idx + LED_EN_CTL_OFFSET);
+
+ if (led->pwm_setting.duty_ns == 0)
+ val = 0;
+ else
+ val = mask;
+
+ rc = qti_rgb_masked_write(led->chip, REG_LED_EN_CTL, mask, val);
+ if (rc < 0)
+ dev_err(led->chip->dev, "Update addr 0x%x failed, rc=%d\n",
+ REG_LED_EN_CTL, rc);
+
+ return rc;
+}
+
+static void rgb_led_set_work(struct work_struct *work)
+{
+ struct qti_rgb_led_dev *led = container_of(work,
+ struct qti_rgb_led_dev, work);
+ u32 brightness = 0, on_ms, off_ms, period_ns, duty_ns;
+ int rc = 0;
+
+ mutex_lock(&led->lock);
+ if (led->led_setting.blink) {
+ on_ms = led->led_setting.on_ms;
+ off_ms = led->led_setting.off_ms;
+
+ if (on_ms > INT_MAX / NSEC_PER_MSEC)
+ duty_ns = INT_MAX - 1;
+ else
+ duty_ns = on_ms * NSEC_PER_MSEC;
+
+ if (on_ms + off_ms > INT_MAX / NSEC_PER_MSEC) {
+ period_ns = INT_MAX;
+ duty_ns = (period_ns / (on_ms + off_ms)) * on_ms;
+ } else {
+ period_ns = (on_ms + off_ms) * NSEC_PER_MSEC;
+ }
+
+ if (period_ns < duty_ns && duty_ns != 0)
+ period_ns = duty_ns + 1;
+ } else {
+ brightness = led->led_setting.brightness;
+ period_ns = pwm_get_period(led->pwm_dev);
+ /* Use initial period if no blinking is required */
+ if (period_ns > led->pwm_setting.initial_period_ns)
+ period_ns = led->pwm_setting.initial_period_ns;
+
+ if (period_ns > INT_MAX / brightness)
+ duty_ns = (period_ns / LED_FULL) * brightness;
+ else
+ duty_ns = (period_ns * brightness) / LED_FULL;
+
+ if (period_ns < duty_ns && duty_ns != 0)
+ period_ns = duty_ns + 1;
+ }
+ pr_debug("PWM settings for %s led: period = %dns, duty = %dns\n",
+ led->cdev.name, period_ns, duty_ns);
+
+ led->pwm_setting.duty_ns = duty_ns;
+ led->pwm_setting.period_ns = period_ns;
+
+ rc = __rgb_led_set(led);
+ if (rc < 0) {
+ dev_err(led->chip->dev, "rgb_led_set %s failed, rc=%d\n",
+ led->cdev.name, rc);
+ goto unlock;
+ }
+
+ if (led->led_setting.blink) {
+ led->cdev.brightness = LED_FULL;
+ led->blinking = true;
+ } else {
+ led->cdev.brightness = brightness;
+ led->blinking = false;
+ }
+
+unlock:
+ mutex_unlock(&led->lock);
+}
+
+static void qti_rgb_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct qti_rgb_led_dev *led =
+ container_of(led_cdev, struct qti_rgb_led_dev, cdev);
+
+ mutex_lock(&led->lock);
+ if (brightness > LED_FULL)
+ brightness = LED_FULL;
+
+ if (brightness == led->led_setting.brightness &&
+ !led->blinking) {
+ mutex_unlock(&led->lock);
+ return;
+ }
+ led->led_setting.blink = false;
+ led->led_setting.brightness = brightness;
+ mutex_unlock(&led->lock);
+
+ schedule_work(&led->work);
+}
+
+static enum led_brightness qti_rgb_led_get(struct led_classdev *led_cdev)
+{
+ return led_cdev->brightness;
+}
+
+static int qti_rgb_led_blink(struct led_classdev *led_cdev,
+ unsigned long *on_ms, unsigned long *off_ms)
+{
+ struct qti_rgb_led_dev *led =
+ container_of(led_cdev, struct qti_rgb_led_dev, cdev);
+
+ if (*on_ms == 0 || *off_ms == 0) {
+ dev_err(led->chip->dev, "Can't set blink for on=%lums off=%lums\n",
+ *on_ms, *off_ms);
+ return -EINVAL;
+ }
+
+ mutex_lock(&led->lock);
+ if (led->blinking && *on_ms == led->led_setting.on_ms &&
+ *off_ms == led->led_setting.off_ms) {
+ pr_debug("Ignore, on/off setting is not changed: on %lums, off %lums\n",
+ *on_ms, *off_ms);
+ mutex_unlock(&led->lock);
+ return 0;
+ }
+
+ led->led_setting.blink = true;
+ led->led_setting.on_ms = (u32)*on_ms;
+ led->led_setting.off_ms = (u32)*off_ms;
+ mutex_unlock(&led->lock);
+
+ schedule_work(&led->work);
+
+ return 0;
+}
+
+static ssize_t blink_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc;
+ u32 blink;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct qti_rgb_led_dev *led =
+ container_of(led_cdev, struct qti_rgb_led_dev, cdev);
+
+ rc = kstrtouint(buf, 0, &blink);
+ if (rc)
+ return rc;
+
+ if (!!blink)
+ qti_rgb_led_blink(led_cdev,
+ (unsigned long *)&led->led_setting.on_ms,
+ (unsigned long *)&led->led_setting.off_ms);
+ else
+ qti_rgb_led_set(led_cdev, LED_OFF);
+
+ return count;
+}
+
+static ssize_t blink_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct qti_rgb_led_dev *led =
+ container_of(led_cdev, struct qti_rgb_led_dev, cdev);
+ bool blink;
+
+ blink = led->led_setting.blink &&
+ led->cdev.brightness == LED_FULL;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", blink);
+}
+
+static ssize_t on_off_ms_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ int rc, buff_size;
+ char buff[32];
+ char *token, *buff_ptr;
+ bool blink;
+ u32 on_ms, off_ms, brightness;
+
+ buff_size = min(count, sizeof(buff) - 1);
+ memcpy(buff, buf, buff_size);
+ buff[buff_size] = '\0';
+ buff_ptr = buff;
+
+ token = strsep(&buff_ptr, " ");
+ if (!token)
+ return -EINVAL;
+
+ rc = kstrtouint(token, 0, &on_ms);
+ if (rc < 0)
+ return rc;
+
+ token = strsep(&buff_ptr, " ");
+ if (!token)
+ return -EINVAL;
+
+ rc = kstrtouint(token, 0, &off_ms);
+ if (rc < 0)
+ return rc;
+
+ blink = !(on_ms == 0 || off_ms == 0);
+ if (on_ms == 0)
+ brightness = LED_OFF;
+ else if (off_ms == 0)
+ brightness = LED_FULL;
+
+ if (blink)
+ qti_rgb_led_blink(led_cdev, (unsigned long *)&on_ms,
+ (unsigned long *)&off_ms);
+ else
+ qti_rgb_led_set(led_cdev, brightness);
+
+ return count;
+}
+
+static ssize_t on_off_ms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct qti_rgb_led_dev *led =
+ container_of(led_cdev, struct qti_rgb_led_dev, cdev);
+
+ return snprintf(buf, PAGE_SIZE, "on: %dms, off: %dms\n",
+ led->led_setting.on_ms, led->led_setting.off_ms);
+}
+
+static DEVICE_ATTR(blink, 0644, blink_show, blink_store);
+static DEVICE_ATTR(on_off_ms, 0644, on_off_ms_show, on_off_ms_store);
+
+static struct attribute *blink_attrs[] = {
+ &dev_attr_blink.attr,
+ &dev_attr_on_off_ms.attr,
+ NULL
+};
+
+static const struct attribute_group blink_attrs_group = {
+ .attrs = blink_attrs,
+};
+
+static int qti_rgb_leds_register(struct qti_rgb_chip *chip)
+{
+ int rc, i, j;
+
+ for (i = 0; i < NUM_LEDS; i++) {
+ INIT_WORK(&chip->leds[i].work, rgb_led_set_work);
+ mutex_init(&chip->leds[i].lock);
+ chip->leds[i].cdev.name = led_names[i];
+ chip->leds[i].cdev.max_brightness = LED_FULL;
+ chip->leds[i].cdev.brightness = LED_OFF;
+ chip->leds[i].cdev.brightness_set = qti_rgb_led_set;
+ chip->leds[i].cdev.brightness_get = qti_rgb_led_get;
+ if (chip->leds[i].support_blink)
+ chip->leds[i].cdev.blink_set = qti_rgb_led_blink;
+
+ rc = devm_led_classdev_register(chip->dev, &chip->leds[i].cdev);
+ if (rc < 0) {
+ dev_err(chip->dev, "%s led class device registering failed, rc=%d\n",
+ led_names[i], rc);
+ goto destroy;
+ }
+
+ if (chip->leds[i].support_blink) {
+ rc = sysfs_create_group(&chip->leds[i].cdev.dev->kobj,
+ &blink_attrs_group);
+ if (rc < 0) {
+ dev_err(chip->dev, "Create blink_attrs for %s led failed, rc=%d\n",
+ led_names[i], rc);
+ goto destroy;
+ }
+ }
+ }
+
+ return 0;
+destroy:
+ for (j = 0; j < i; j++) {
+ mutex_destroy(&chip->leds[i].lock);
+ sysfs_remove_group(&chip->leds[i].cdev.dev->kobj,
+ &blink_attrs_group);
+ }
+
+ return rc;
+}
+
+static int qti_rgb_leds_init_pwm_settings(struct qti_rgb_chip *chip)
+{
+ u32 period_ns, duty_ns;
+ bool is_enabled;
+ int i;
+
+ for (i = 0; i < NUM_LEDS; i++) {
+ period_ns = pwm_get_period(chip->leds[i].pwm_dev);
+ duty_ns = pwm_get_duty_cycle(chip->leds[i].pwm_dev);
+ is_enabled = pwm_is_enabled(chip->leds[i].pwm_dev);
+
+ pr_debug("%s led PWM default setting: period = %dns, duty = %dns, is_enabled = %d\n",
+ led_names[i], period_ns, duty_ns, is_enabled);
+ chip->leds[i].pwm_setting.initial_period_ns = period_ns;
+ if (duty_ns > period_ns) {
+ duty_ns = period_ns - 1;
+ pwm_set_duty_cycle(chip->leds[i].pwm_dev, duty_ns);
+ }
+
+ if (is_enabled)
+ pwm_disable(chip->leds[i].pwm_dev);
+ }
+
+ return 0;
+}
+
+static int qti_rgb_leds_hw_init(struct qti_rgb_chip *chip)
+{
+ int rc = 0;
+
+ /* Disable ATC_EN for LEDs */
+ rc = qti_rgb_masked_write(chip, REG_LED_ATC_EN_CTL,
+ LED_ATC_EN_MASK, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Writing ATC_EN_CTL failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Select VINRGB_VBOOST as the source */
+ rc = qti_rgb_masked_write(chip, REG_LED_SRC_SEL, LED_SRC_SEL_MASK,
+ LED_SRC_VINRGB_VBOOST);
+ if (rc < 0) {
+ dev_err(chip->dev, "Writing SRC_SEL failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int qti_rgb_leds_parse_dt(struct qti_rgb_chip *chip)
+{
+ int rc, i, count;
+ const __be32 *addr;
+ u32 support_blink[NUM_LEDS], on_ms[NUM_LEDS], off_ms[NUM_LEDS];
+
+ addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(chip->dev, "Getting address failed\n");
+ return -EINVAL;
+ }
+ chip->reg_base = be32_to_cpu(addr[0]);
+
+ for (i = 0; i < NUM_LEDS; i++) {
+ chip->leds[i].pwm_dev = devm_pwm_get(chip->dev, led_names[i]);
+ if (IS_ERR(chip->leds[i].pwm_dev)) {
+ rc = PTR_ERR(chip->leds[i].pwm_dev);
+ if (rc != -EPROBE_DEFER)
+ dev_err(chip->dev, "Get pwm device for %s led failed, rc=%d\n",
+ led_names[i], rc);
+ return rc;
+ }
+ chip->leds[i].chip = chip;
+ chip->leds[i].idx = i;
+ }
+
+ count = of_property_count_elems_of_size(chip->dev->of_node,
+ "qcom,support-blink", sizeof(u32));
+ if (count > 0) {
+ if (count != NUM_LEDS) {
+ dev_err(chip->dev, "qcom,support-blink property expects %d elements, but it has %d\n",
+ NUM_LEDS, count);
+ return -EINVAL;
+ }
+ rc = of_property_read_u32_array(chip->dev->of_node,
+ "qcom,support-blink", support_blink, count);
+ if (rc < 0) {
+ dev_err(chip->dev, "qcom,support-blink property reading failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+ rc = of_property_read_u32_array(chip->dev->of_node,
+ "qcom,on-ms", on_ms, count);
+ if (rc < 0) {
+ dev_err(chip->dev, "qcom,on-ms property reading failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+ rc = of_property_read_u32_array(chip->dev->of_node,
+ "qcom,off-ms", off_ms, count);
+ if (rc < 0) {
+ dev_err(chip->dev, "qcom,off-ms property reading failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ for (i = 0; i < NUM_LEDS; i++) {
+ chip->leds[i].support_blink = !!support_blink[i];
+ chip->leds[i].led_setting.on_ms = on_ms[i];
+ chip->leds[i].led_setting.off_ms = off_ms[i];
+ if (chip->leds[i].support_blink)
+ pr_debug("%s led supports blink, on_ms=%d, off_ms=%d!\n",
+ led_names[i], on_ms[i], off_ms[i]);
+ else
+ pr_debug("%s led doesn't support blink\n",
+ led_names[i]);
+ }
+ }
+
+ return rc;
+}
+
+static int qti_rgb_leds_probe(struct platform_device *pdev)
+{
+ struct qti_rgb_chip *chip;
+ int rc = 0;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &pdev->dev;
+ chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+ if (!chip->regmap) {
+ dev_err(chip->dev, "Getting regmap failed\n");
+ return -EINVAL;
+ }
+
+ rc = qti_rgb_leds_parse_dt(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = qti_rgb_leds_init_pwm_settings(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Init PWM setting failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ mutex_init(&chip->bus_lock);
+
+ rc = qti_rgb_leds_hw_init(chip);
+ if (rc) {
+ dev_err(chip->dev, "HW initialization failed, rc=%d\n", rc);
+ goto destroy;
+ }
+
+ dev_set_drvdata(chip->dev, chip);
+ rc = qti_rgb_leds_register(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Registering LED class devices failed, rc=%d\n",
+ rc);
+ goto destroy;
+ }
+
+ return 0;
+destroy:
+ mutex_destroy(&chip->bus_lock);
+ dev_set_drvdata(chip->dev, NULL);
+
+ return rc;
+}
+
+static int qti_rgb_leds_remove(struct platform_device *pdev)
+{
+ int i;
+ struct qti_rgb_chip *chip = dev_get_drvdata(&pdev->dev);
+
+ mutex_destroy(&chip->bus_lock);
+ for (i = 0; i < NUM_LEDS; i++) {
+ if (chip->leds[i].support_blink)
+ sysfs_remove_group(&chip->leds[i].cdev.dev->kobj,
+ &blink_attrs_group);
+ mutex_destroy(&chip->leds[i].lock);
+ }
+ dev_set_drvdata(chip->dev, NULL);
+ return 0;
+}
+
+static const struct of_device_id qti_rgb_of_match[] = {
+ { .compatible = "qcom,leds-rgb",},
+ { },
+};
+
+static struct platform_driver qti_rgb_leds_driver = {
+ .driver = {
+ .name = "qcom,leds-rgb",
+ .of_match_table = qti_rgb_of_match,
+ },
+ .probe = qti_rgb_leds_probe,
+ .remove = qti_rgb_leds_remove,
+};
+module_platform_driver(qti_rgb_leds_driver);
+
+MODULE_DESCRIPTION("QTI TRI_LED (RGB) driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("leds:leds-qti-rgb");
--
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module
2017-05-31 6:14 [PATCH V1 0/2] *** Add support for QTI TRI_LED and LPG module *** fenglinw
2017-05-31 6:14 ` [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module fenglinw
@ 2017-05-31 6:14 ` fenglinw
2017-06-03 23:20 ` kbuild test robot
2017-06-07 21:19 ` Rob Herring
1 sibling, 2 replies; 15+ messages in thread
From: fenglinw @ 2017-05-31 6:14 UTC (permalink / raw)
To: linux-arm-msm, linux-kernel, Thierry Reding, Rob Herring,
Mark Rutland, linux-pwm, devicetree
Cc: subbaram, aghayal, wruan, kgunda, Fenglin Wu
From: Fenglin Wu <fenglinw@codeaurora.org>
Add pwm_chip to support QTI LPG module and export LPG channels as
PWM devices for consumer drivers' usage.
Signed-off-by: Fenglin Wu <fenglinw@codeaurora.org>
---
.../devicetree/bindings/pwm/pwm-qti-lpg.txt | 39 ++
drivers/pwm/Kconfig | 10 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-qti-lpg.c | 578 +++++++++++++++++++++
4 files changed, 628 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
create mode 100644 drivers/pwm/pwm-qti-lpg.c
diff --git a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
new file mode 100644
index 0000000..df81f5f
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
@@ -0,0 +1,39 @@
+Qualcomm Technologies, Inc. LPG driver specific bindings
+
+This binding document describes the properties of LPG (Light Pulse Generator)
+device module in Qualcomm Technologies, Inc. PMIC chips.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Must be "qcom,pwm-lpg".
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Register base and length for LPG modules. The length
+ varies based on the number of channels available in
+ the PMIC chips.
+
+- reg-names:
+ Usage: required
+ Value type: <string>
+ Definition: The name of the register defined in the reg property.
+ It must be "lpg-base".
+
+- #pwm-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: The number of cells in "pwms" property specified in
+ PWM user nodes. It should be 2. The first cell is
+ the PWM channel ID indexed from 0, and the second
+ cell is the PWM default period in nanoseconds.
+
+Example:
+
+ pmi8998_lpg: lpg@b100 {
+ compatible = "qcom,pwm-lpg";
+ reg = <0xb100 0x600>;
+ reg-names = "lpg-base";
+ #pwm-cells = <2>;
+ };
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 313c107..711104f 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -349,6 +349,16 @@ config PWM_PXA
To compile this driver as a module, choose M here: the module
will be called pwm-pxa.
+config PWM_QTI_LPG
+ tristate "Qualcomm Technologies, Inc. LPG driver"
+ depends on MFD_SPMI_PMIC && OF
+ help
+ This driver supports the LPG (Light Pulse Generator) module found in
+ Qualcomm Technologies, Inc. PMIC chips. Each LPG channel can be
+ configured to operate in PWM mode to output a fixed amplitude with
+ variable duty cycle or in LUT (Look up table) mode to output PWM
+ signal with a modulated amplitude.
+
config PWM_RCAR
tristate "Renesas R-Car PWM support"
depends on ARCH_RENESAS || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 93da1f7..16958be 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o
obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o
obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o
obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
+obj-$(CONFIG_PWM_QTI_LPG) += pwm-qti-lpg.o
obj-$(CONFIG_PWM_RCAR) += pwm-rcar.o
obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
diff --git a/drivers/pwm/pwm-qti-lpg.c b/drivers/pwm/pwm-qti-lpg.c
new file mode 100644
index 0000000..929b6a4
--- /dev/null
+++ b/drivers/pwm/pwm-qti-lpg.c
@@ -0,0 +1,578 @@
+/* Copyright (c) 2017, The Linux Foundation. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define REG_SIZE_PER_LPG 0x100
+
+#define REG_LPG_PWM_SIZE_CLK 0x41
+#define REG_LPG_PWM_FREQ_PREDIV_CLK 0x42
+#define REG_LPG_PWM_TYPE_CONFIG 0x43
+#define REG_LPG_PWM_VALUE_LSB 0x44
+#define REG_LPG_PWM_VALUE_MSB 0x45
+#define REG_LPG_ENABLE_CONTROL 0x46
+#define REG_LPG_PWM_SYNC 0x47
+
+/* REG_LPG_PWM_SIZE_CLK */
+#define LPG_PWM_SIZE_MASK BIT(4)
+#define LPG_PWM_SIZE_SHIFT 4
+#define LPG_PWM_CLK_FREQ_SEL_MASK GENMASK(1, 0)
+
+/* REG_LPG_PWM_FREQ_PREDIV_CLK */
+#define LPG_PWM_FREQ_PREDIV_MASK GENMASK(6, 5)
+#define LPG_PWM_FREQ_PREDIV_SHIFT 5
+#define LPG_PWM_FREQ_EXPONENT_MASK GENMASK(2, 0)
+
+/* REG_LPG_PWM_TYPE_CONFIG */
+#define LPG_PWM_EN_GLITCH_REMOVAL_MASK BIT(5)
+
+/* REG_LPG_PWM_VALUE_LSB */
+#define LPG_PWM_VALUE_LSB_MASK GENMASK(7, 0)
+
+/* REG_LPG_PWM_VALUE_MSB */
+#define LPG_PWM_VALUE_MSB_MASK BIT(0)
+
+/* REG_LPG_ENABLE_CONTROL */
+#define LPG_EN_LPG_OUT_BIT BIT(7)
+#define LPG_PWM_SRC_SELECT_MASK BIT(2)
+#define LPG_PWM_SRC_SELECT_SHIFT 2
+#define LPG_EN_RAMP_GEN_MASK BIT(1)
+#define LPG_EN_RAMP_GEN_SHIFT 1
+
+/* REG_LPG_PWM_SYNC */
+#define LPG_PWM_VALUE_SYNC BIT(0)
+
+#define NUM_PWM_SIZE 2
+#define NUM_PWM_CLK 3
+#define NUM_CLK_PREDIV 4
+#define NUM_PWM_EXP 8
+
+enum {
+ LUT_PATTERN = 0,
+ PWM_OUTPUT,
+};
+
+static const int pwm_size[NUM_PWM_SIZE] = {6, 9};
+static const int clk_freq_hz[NUM_PWM_CLK] = {1024, 32768, 19200000};
+static const int clk_prediv[NUM_CLK_PREDIV] = {1, 3, 5, 6};
+static const int pwm_exponent[NUM_PWM_EXP] = {0, 1, 2, 3, 4, 5, 6, 7};
+
+struct lpg_pwm_config {
+ u32 pwm_size;
+ u32 pwm_clk;
+ u32 prediv;
+ u32 clk_exp;
+ u16 pwm_value;
+ u32 best_period_ns;
+};
+
+struct qti_lpg_channel {
+ struct qti_lpg_chip *chip;
+ struct lpg_pwm_config pwm_config;
+ u32 lpg_idx;
+ u32 reg_base;
+ u8 src_sel;
+ int current_period_ns;
+ int current_duty_ns;
+};
+
+struct qti_lpg_chip {
+ struct pwm_chip pwm_chip;
+ struct regmap *regmap;
+ struct device *dev;
+ struct qti_lpg_channel *lpgs;
+ struct mutex bus_lock;
+ u32 lpg_nums;
+};
+
+static int qti_lpg_write(struct qti_lpg_channel *lpg, u16 addr, u8 val)
+{
+ int rc;
+
+ mutex_lock(&lpg->chip->bus_lock);
+ rc = regmap_write(lpg->chip->regmap, lpg->reg_base + addr, val);
+ if (rc < 0)
+ dev_err(lpg->chip->dev, "Write addr 0x%x with value %d failed, rc=%d\n",
+ lpg->reg_base + addr, val, rc);
+ mutex_unlock(&lpg->chip->bus_lock);
+
+ return rc;
+}
+
+static int qti_lpg_masked_write(struct qti_lpg_channel *lpg,
+ u16 addr, u8 mask, u8 val)
+{
+ int rc;
+
+ mutex_lock(&lpg->chip->bus_lock);
+ rc = regmap_update_bits(lpg->chip->regmap, lpg->reg_base + addr,
+ mask, val);
+ if (rc < 0)
+ dev_err(lpg->chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
+ lpg->reg_base + addr, val, mask, rc);
+ mutex_unlock(&lpg->chip->bus_lock);
+
+ return rc;
+}
+
+static struct qti_lpg_channel *pwm_dev_to_qti_lpg(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm) {
+
+ struct qti_lpg_chip *chip = container_of(pwm_chip,
+ struct qti_lpg_chip, pwm_chip);
+ u32 hw_idx = pwm->hwpwm;
+
+ if (hw_idx >= chip->lpg_nums) {
+ dev_err(chip->dev, "hw index %d out of range [0-%d]\n",
+ hw_idx, chip->lpg_nums - 1);
+ return NULL;
+ }
+
+ return &chip->lpgs[hw_idx];
+}
+
+static int __find_index_in_array(int member, const int array[], int length)
+{
+ int i;
+
+ for (i = 0; i < length; i++) {
+ if (member == array[i])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int qti_lpg_set_pwm_config(struct qti_lpg_channel *lpg)
+{
+ int rc;
+ u8 val, mask;
+ int pwm_size_idx, pwm_clk_idx, prediv_idx, clk_exp_idx;
+
+ pwm_size_idx = __find_index_in_array(lpg->pwm_config.pwm_size,
+ pwm_size, ARRAY_SIZE(pwm_size));
+ pwm_clk_idx = __find_index_in_array(lpg->pwm_config.pwm_clk,
+ clk_freq_hz, ARRAY_SIZE(clk_freq_hz));
+ prediv_idx = __find_index_in_array(lpg->pwm_config.prediv,
+ clk_prediv, ARRAY_SIZE(clk_prediv));
+ clk_exp_idx = __find_index_in_array(lpg->pwm_config.clk_exp,
+ pwm_exponent, ARRAY_SIZE(pwm_exponent));
+
+ if (pwm_size_idx < 0 || pwm_clk_idx < 0
+ || prediv_idx < 0 || clk_exp_idx < 0)
+ return -EINVAL;
+
+ pwm_clk_idx += 1;
+ val = pwm_size_idx << LPG_PWM_SIZE_SHIFT | pwm_clk_idx;
+ mask = LPG_PWM_SIZE_MASK | LPG_PWM_CLK_FREQ_SEL_MASK;
+ rc = qti_lpg_masked_write(lpg, REG_LPG_PWM_SIZE_CLK, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_SIZE_CLK failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = prediv_idx << LPG_PWM_FREQ_PREDIV_SHIFT | clk_exp_idx;
+ mask = LPG_PWM_FREQ_PREDIV_MASK | LPG_PWM_FREQ_EXPONENT_MASK;
+ rc = qti_lpg_masked_write(lpg, REG_LPG_PWM_FREQ_PREDIV_CLK, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_FREQ_PREDIV_CLK failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = lpg->pwm_config.pwm_value & LPG_PWM_VALUE_LSB_MASK;
+ rc = qti_lpg_write(lpg, REG_LPG_PWM_VALUE_LSB, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_VALUE_LSB failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = lpg->pwm_config.pwm_value >> 8;
+ mask = LPG_PWM_VALUE_MSB_MASK;
+ rc = qti_lpg_masked_write(lpg, REG_LPG_PWM_VALUE_MSB, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_VALUE_MSB failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = LPG_PWM_VALUE_SYNC;
+ rc = qti_lpg_write(lpg, REG_LPG_PWM_SYNC, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_SYNC failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static void __qti_lpg_calc_pwm_period(int period_ns,
+ struct lpg_pwm_config *pwm_config)
+{
+ struct lpg_pwm_config configs[NUM_PWM_SIZE];
+ int i, j, m, n;
+ int tmp1, tmp2;
+ int clk_period_ns = 0, pwm_clk_period_ns;
+ int clk_delta_ns = INT_MAX, min_clk_delta_ns = INT_MAX;
+ int pwm_period_delta = INT_MAX, min_pwm_period_delta = INT_MAX;
+ int pwm_size_step;
+
+ /*
+ * (2^pwm_size) * (2^pwm_exp) * prediv * NSEC_PER_SEC
+ * pwm_period = ---------------------------------------------------
+ * clk_freq_hz
+ *
+ * Searching the closest settings for the requested PWM period.
+ */
+ for (n = 0; n < ARRAY_SIZE(pwm_size); n++) {
+ pwm_clk_period_ns = period_ns >> pwm_size[n];
+ for (i = ARRAY_SIZE(clk_freq_hz) - 1; i >= 0; i--) {
+ for (j = 0; j < ARRAY_SIZE(clk_prediv); j++) {
+ for (m = 0; m < ARRAY_SIZE(pwm_exponent); m++) {
+ tmp1 = 1 << pwm_exponent[m];
+ tmp1 *= clk_prediv[j];
+ tmp2 = NSEC_PER_SEC / clk_freq_hz[i];
+
+ clk_period_ns = tmp1 * tmp2;
+
+ clk_delta_ns = abs(pwm_clk_period_ns
+ - clk_period_ns);
+ /*
+ * Find the closet setting for
+ * PWM frequency predivide value
+ */
+ if (clk_delta_ns < min_clk_delta_ns) {
+ min_clk_delta_ns
+ = clk_delta_ns;
+ configs[n].pwm_clk
+ = clk_freq_hz[i];
+ configs[n].prediv
+ = clk_prediv[j];
+ configs[n].clk_exp
+ = pwm_exponent[m];
+ configs[n].pwm_size
+ = pwm_size[n];
+ configs[n].best_period_ns
+ = clk_period_ns;
+ }
+ }
+ }
+ }
+ configs[n].best_period_ns *= 1 << pwm_size[n];
+ /* Find the closet setting for PWM period */
+ pwm_period_delta = min_clk_delta_ns << pwm_size[n];
+ if (pwm_period_delta < min_pwm_period_delta) {
+ min_pwm_period_delta = pwm_period_delta;
+ memcpy(pwm_config, &configs[n],
+ sizeof(struct lpg_pwm_config));
+ }
+ }
+
+ /* Larger PWM size can achieve better resolution for PWM duty */
+ for (n = ARRAY_SIZE(pwm_size) - 1; n > 0; n--) {
+ if (pwm_config->pwm_size >= pwm_size[n])
+ break;
+ pwm_size_step = pwm_size[n] - pwm_config->pwm_size;
+ if (pwm_config->clk_exp >= pwm_size_step) {
+ pwm_config->pwm_size = pwm_size[n];
+ pwm_config->clk_exp -= pwm_size_step;
+ }
+ }
+ pr_debug("PWM setting for period_ns %d: pwm_clk = %dHZ, prediv = %d, exponent = %d, pwm_size = %d\n",
+ period_ns, pwm_config->pwm_clk, pwm_config->prediv,
+ pwm_config->clk_exp, pwm_config->pwm_size);
+ pr_debug("Actual period: %dns\n", pwm_config->best_period_ns);
+}
+
+static void __qti_lpg_calc_pwm_duty(int period_ns, int duty_ns,
+ struct lpg_pwm_config *pwm_config)
+{
+ u16 pwm_value, max_pwm_value;
+
+ if ((1 << pwm_config->pwm_size) > (INT_MAX / duty_ns))
+ pwm_value = duty_ns / (period_ns >> pwm_config->pwm_size);
+ else
+ pwm_value = (duty_ns << pwm_config->pwm_size) / period_ns;
+
+ max_pwm_value = (1 << pwm_config->pwm_size) - 1;
+ if (pwm_value > max_pwm_value)
+ pwm_value = max_pwm_value;
+ pwm_config->pwm_value = pwm_value;
+}
+
+static int qti_lpg_pwm_config(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ struct qti_lpg_channel *lpg;
+ int rc = 0;
+
+ lpg = pwm_dev_to_qti_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return -ENODEV;
+ }
+
+ if (duty_ns > period_ns) {
+ dev_err(pwm_chip->dev, "Duty %dns is larger than period %dns\n",
+ duty_ns, period_ns);
+ return -EINVAL;
+ }
+
+ if (period_ns != lpg->current_period_ns)
+ __qti_lpg_calc_pwm_period(period_ns, &lpg->pwm_config);
+
+ if (period_ns != lpg->current_period_ns ||
+ duty_ns != lpg->current_duty_ns)
+ __qti_lpg_calc_pwm_duty(period_ns, duty_ns, &lpg->pwm_config);
+
+ rc = qti_lpg_set_pwm_config(lpg);
+ if (rc < 0)
+ dev_err(pwm_chip->dev, "Config PWM failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+
+ return rc;
+}
+
+static int qti_lpg_pwm_enable(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm)
+{
+ struct qti_lpg_channel *lpg;
+ int rc = 0;
+ u8 mask, val;
+
+ lpg = pwm_dev_to_qti_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return -ENODEV;
+ }
+
+ mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
+
+ val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT | LPG_EN_LPG_OUT_BIT;
+
+ rc = qti_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+ if (rc < 0)
+ dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+
+ return rc;
+}
+
+static void qti_lpg_pwm_disable(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm)
+{
+ struct qti_lpg_channel *lpg;
+ int rc;
+ u8 mask, val;
+
+ lpg = pwm_dev_to_qti_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return;
+ }
+
+ mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
+
+ val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;
+
+ rc = qti_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+ if (rc < 0)
+ dev_err(pwm_chip->dev, "Disable PWM output failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void qti_lpg_pwm_dbg_show(struct pwm_chip *pwm_chip, struct seq_file *s)
+{
+ struct qti_lpg_channel *lpg;
+ struct lpg_pwm_config *cfg;
+ struct pwm_device *pwm;
+ int i;
+
+ for (i = 0; i < pwm_chip->npwm; i++) {
+ pwm = &pwm_chip->pwms[i];
+
+ lpg = pwm_dev_to_qti_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return;
+ }
+
+ if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
+ seq_printf(s, "LPG %d is requested by %s\n",
+ lpg->lpg_idx + 1, pwm->label);
+ } else {
+ seq_printf(s, "LPG %d is free\n",
+ lpg->lpg_idx + 1);
+ continue;
+ }
+
+ if (pwm_is_enabled(pwm)) {
+ seq_puts(s, " enabled\n");
+ } else {
+ seq_puts(s, " disabled\n");
+ continue;
+ }
+
+ cfg = &lpg->pwm_config;
+ seq_printf(s, " clk = %dHz\n", cfg->pwm_clk);
+ seq_printf(s, " pwm_size = %d\n", cfg->pwm_size);
+ seq_printf(s, " prediv = %d\n", cfg->prediv);
+ seq_printf(s, " exponent = %d\n", cfg->clk_exp);
+ seq_printf(s, " pwm_value = %d\n", cfg->pwm_value);
+ seq_printf(s, " Requested period: %dns, best period = %dns\n",
+ pwm_get_period(pwm), cfg->best_period_ns);
+ }
+}
+#endif
+
+static const struct pwm_ops qti_lpg_pwm_ops = {
+ .config = qti_lpg_pwm_config,
+ .enable = qti_lpg_pwm_enable,
+ .disable = qti_lpg_pwm_disable,
+#ifdef CONFIG_DEBUG_FS
+ .dbg_show = qti_lpg_pwm_dbg_show,
+#endif
+ .owner = THIS_MODULE,
+};
+
+static int qti_lpg_add_pwmchip(struct qti_lpg_chip *chip)
+{
+ int rc;
+
+ chip->pwm_chip.dev = chip->dev;
+ chip->pwm_chip.base = -1;
+ chip->pwm_chip.npwm = chip->lpg_nums;
+ chip->pwm_chip.ops = &qti_lpg_pwm_ops;
+
+ rc = pwmchip_add(&chip->pwm_chip);
+ if (rc < 0)
+ dev_err(chip->dev, "Add pwmchip failed, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int qti_lpg_parse_dt(struct qti_lpg_chip *chip)
+{
+ int rc = 0, i;
+ u64 base, length;
+ const __be32 *addr;
+
+ addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(chip->dev, "Getting address failed\n");
+ return -EINVAL;
+ }
+ base = be32_to_cpu(addr[0]);
+ length = be32_to_cpu(addr[1]);
+
+ chip->lpg_nums = length / REG_SIZE_PER_LPG;
+ chip->lpgs = devm_kcalloc(chip->dev, chip->lpg_nums,
+ sizeof(*chip->lpgs), GFP_KERNEL);
+ if (!chip->lpgs)
+ return -ENOMEM;
+
+ for (i = 0; i < chip->lpg_nums; i++) {
+ chip->lpgs[i].chip = chip;
+ chip->lpgs[i].lpg_idx = i;
+ chip->lpgs[i].reg_base = base + i * REG_SIZE_PER_LPG;
+ chip->lpgs[i].src_sel = PWM_OUTPUT;
+ }
+
+ return rc;
+}
+
+static int qti_lpg_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct qti_lpg_chip *chip;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &pdev->dev;
+ chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+ if (!chip->regmap) {
+ dev_err(chip->dev, "Getting regmap failed\n");
+ return -EINVAL;
+ }
+
+ rc = qti_lpg_parse_dt(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ dev_set_drvdata(chip->dev, chip);
+
+ mutex_init(&chip->bus_lock);
+ rc = qti_lpg_add_pwmchip(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Add pwmchip failed, rc=%d\n", rc);
+ mutex_destroy(&chip->bus_lock);
+ }
+
+ return rc;
+}
+
+static int qti_lpg_remove(struct platform_device *pdev)
+{
+ struct qti_lpg_chip *chip = dev_get_drvdata(&pdev->dev);
+ int rc = 0;
+
+ rc = pwmchip_remove(&chip->pwm_chip);
+ if (rc < 0)
+ dev_err(chip->dev, "Remove pwmchip failed, rc=%d\n", rc);
+
+ mutex_destroy(&chip->bus_lock);
+ dev_set_drvdata(chip->dev, NULL);
+
+ return rc;
+}
+
+static const struct of_device_id qti_lpg_of_match[] = {
+ { .compatible = "qcom,pwm-lpg",},
+ { },
+};
+
+static struct platform_driver qti_lpg_driver = {
+ .driver = {
+ .name = "qcom,pwm-lpg",
+ .of_match_table = qti_lpg_of_match,
+ },
+ .probe = qti_lpg_probe,
+ .remove = qti_lpg_remove,
+};
+module_platform_driver(qti_lpg_driver);
+
+MODULE_DESCRIPTION("QTI LPG driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("pwm:pwm-lpg");
--
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
2017-05-31 6:14 ` [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module fenglinw
@ 2017-05-31 7:55 ` Pavel Machek
0 siblings, 0 replies; 15+ messages in thread
From: Pavel Machek @ 2017-05-31 7:55 UTC (permalink / raw)
To: fenglinw-sgV2jX0FEOL9JmXXK+q4OQ
Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Richard Purdie,
Jacek Anaszewski, Rob Herring, Mark Rutland,
linux-leds-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
subbaram-jfJNa2p1gH1BDgjK7y7TUQ, aghayal-Rm6X0d1/PG5y9aJCnZT0Uw,
wruan-jfJNa2p1gH1BDgjK7y7TUQ, kgunda-Rm6X0d1/PG5y9aJCnZT0Uw
[-- Attachment #1: Type: text/plain, Size: 1249 bytes --]
Hi!
> +- qcom,support-blink:
> + Usage: optional
> + Value type: <prop-encoded-array>
> + Definition: An array of integer values to indicate if "blue", "green", "red"
> + LEDs support blink control. The values are listed as the fixed
> + order for "blue", "green", "red" LEDs.
Normal order is RGB, and no need for the "s.
> +- qcom,on-ms:
> + Usage: optional
> + Value type: <prop-encoded-array>
> + Definition: An array of time values (milli-seconds) to represent the
> + on duration for "blue", "green", "red" LEDs. The values are
> + listed as the fixed order for "blue", "green", "red" LEDs.
> + This property has to be defined if "qcom,support-blink" is
> + present.
> +
> +- qcom,off-ms:
> + Usage: optional
> + Value type: <prop-encoded-array>
> + Definition: An array of time values (milli-seconds) to represent the
> + off duration for "blue", "green", "red" LEDs. The values are
> + listed as the fixed order for "blue", "green", "red" LEDs.
> + This property has to be defined if "qcom,support-blink" is
> + present.
I don't get it; why is this needed?
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
@ 2017-05-31 7:55 ` Pavel Machek
0 siblings, 0 replies; 15+ messages in thread
From: Pavel Machek @ 2017-05-31 7:55 UTC (permalink / raw)
To: fenglinw
Cc: linux-arm-msm, linux-kernel, Richard Purdie, Jacek Anaszewski,
Rob Herring, Mark Rutland, linux-leds, devicetree, subbaram,
aghayal, wruan, kgunda
[-- Attachment #1: Type: text/plain, Size: 1249 bytes --]
Hi!
> +- qcom,support-blink:
> + Usage: optional
> + Value type: <prop-encoded-array>
> + Definition: An array of integer values to indicate if "blue", "green", "red"
> + LEDs support blink control. The values are listed as the fixed
> + order for "blue", "green", "red" LEDs.
Normal order is RGB, and no need for the "s.
> +- qcom,on-ms:
> + Usage: optional
> + Value type: <prop-encoded-array>
> + Definition: An array of time values (milli-seconds) to represent the
> + on duration for "blue", "green", "red" LEDs. The values are
> + listed as the fixed order for "blue", "green", "red" LEDs.
> + This property has to be defined if "qcom,support-blink" is
> + present.
> +
> +- qcom,off-ms:
> + Usage: optional
> + Value type: <prop-encoded-array>
> + Definition: An array of time values (milli-seconds) to represent the
> + off duration for "blue", "green", "red" LEDs. The values are
> + listed as the fixed order for "blue", "green", "red" LEDs.
> + This property has to be defined if "qcom,support-blink" is
> + present.
I don't get it; why is this needed?
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* RE: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
2017-05-31 7:55 ` Pavel Machek
@ 2017-05-31 8:23 ` Wu Fenglin
-1 siblings, 0 replies; 15+ messages in thread
From: Wu Fenglin @ 2017-05-31 8:23 UTC (permalink / raw)
To: 'Pavel Machek'
Cc: linux-arm-msm, linux-kernel, 'Richard Purdie',
'Jacek Anaszewski', 'Rob Herring',
'Mark Rutland',
linux-leds, devicetree, subbaram, aghayal, wruan, kgunda
Hi Pavel,
Thanks for the reviewing.
For the order, the hardware register mapping has this order (blue/green/red)
from bit0/1/2, I can revert it to (red/green/blue) if there is a strong
concern.
For these two properties: qcom,off-ms/ qcom,on-ms, I am using them to assign
the default blinking on/off time, then the LEDs would have a default
blinking pattern if you do "echo 1 > /sys/class/leds/red/blink"
Fenglin Wu
-----Original Message-----
From: Pavel Machek [mailto:pavel@ucw.cz]
Sent: Wednesday, May 31, 2017 3:56 PM
To: fenglinw@codeaurora.org
Cc: linux-arm-msm@vger.kernel.org; linux-kernel@vger.kernel.org; Richard
Purdie <rpurdie@rpsys.net>; Jacek Anaszewski <jacek.anaszewski@gmail.com>;
Rob Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>;
linux-leds@vger.kernel.org; devicetree@vger.kernel.org;
subbaram@quicinc.com; aghayal@qti.qualcomm.com; wruan@quicinc.com;
kgunda@qti.qualcomm.com
Subject: Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI
TRI_LED module
Hi!
> +- qcom,support-blink:
> + Usage: optional
> + Value type: <prop-encoded-array>
> + Definition: An array of integer values to indicate if "blue",
"green", "red"
> + LEDs support blink control. The values are listed as the
fixed
> + order for "blue", "green", "red" LEDs.
Normal order is RGB, and no need for the "s.
> +- qcom,on-ms:
> + Usage: optional
> + Value type: <prop-encoded-array>
> + Definition: An array of time values (milli-seconds) to represent the
> + on duration for "blue", "green", "red" LEDs. The values are
> + listed as the fixed order for "blue", "green", "red" LEDs.
> + This property has to be defined if "qcom,support-blink" is
> + present.
> +
> +- qcom,off-ms:
> + Usage: optional
> + Value type: <prop-encoded-array>
> + Definition: An array of time values (milli-seconds) to represent the
> + off duration for "blue", "green", "red" LEDs. The values are
> + listed as the fixed order for "blue", "green", "red" LEDs.
> + This property has to be defined if "qcom,support-blink" is
> + present.
I don't get it; why is this needed?
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures)
http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 15+ messages in thread
* RE: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
@ 2017-05-31 8:23 ` Wu Fenglin
0 siblings, 0 replies; 15+ messages in thread
From: Wu Fenglin @ 2017-05-31 8:23 UTC (permalink / raw)
To: 'Pavel Machek'
Cc: linux-arm-msm, linux-kernel, 'Richard Purdie',
'Jacek Anaszewski', 'Rob Herring',
'Mark Rutland',
linux-leds, devicetree, subbaram, aghayal, wruan, kgunda
Hi Pavel,
Thanks for the reviewing.
For the order, the hardware register mapping has this order (blue/green/red)
from bit0/1/2, I can revert it to (red/green/blue) if there is a strong
concern.
For these two properties: qcom,off-ms/ qcom,on-ms, I am using them to assign
the default blinking on/off time, then the LEDs would have a default
blinking pattern if you do "echo 1 > /sys/class/leds/red/blink"
Fenglin Wu
-----Original Message-----
From: Pavel Machek [mailto:pavel@ucw.cz]
Sent: Wednesday, May 31, 2017 3:56 PM
To: fenglinw@codeaurora.org
Cc: linux-arm-msm@vger.kernel.org; linux-kernel@vger.kernel.org; Richard
Purdie <rpurdie@rpsys.net>; Jacek Anaszewski <jacek.anaszewski@gmail.com>;
Rob Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>;
linux-leds@vger.kernel.org; devicetree@vger.kernel.org;
subbaram@quicinc.com; aghayal@qti.qualcomm.com; wruan@quicinc.com;
kgunda@qti.qualcomm.com
Subject: Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI
TRI_LED module
Hi!
> +- qcom,support-blink:
> + Usage: optional
> + Value type: <prop-encoded-array>
> + Definition: An array of integer values to indicate if "blue",
"green", "red"
> + LEDs support blink control. The values are listed as the
fixed
> + order for "blue", "green", "red" LEDs.
Normal order is RGB, and no need for the "s.
> +- qcom,on-ms:
> + Usage: optional
> + Value type: <prop-encoded-array>
> + Definition: An array of time values (milli-seconds) to represent the
> + on duration for "blue", "green", "red" LEDs. The values are
> + listed as the fixed order for "blue", "green", "red" LEDs.
> + This property has to be defined if "qcom,support-blink" is
> + present.
> +
> +- qcom,off-ms:
> + Usage: optional
> + Value type: <prop-encoded-array>
> + Definition: An array of time values (milli-seconds) to represent the
> + off duration for "blue", "green", "red" LEDs. The values are
> + listed as the fixed order for "blue", "green", "red" LEDs.
> + This property has to be defined if "qcom,support-blink" is
> + present.
I don't get it; why is this needed?
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures)
http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
2017-05-31 8:23 ` Wu Fenglin
(?)
@ 2017-05-31 16:55 ` Pavel Machek
2017-06-01 0:30 ` Wu Fenglin
-1 siblings, 1 reply; 15+ messages in thread
From: Pavel Machek @ 2017-05-31 16:55 UTC (permalink / raw)
To: Wu Fenglin
Cc: linux-arm-msm, linux-kernel, 'Richard Purdie',
'Jacek Anaszewski', 'Rob Herring',
'Mark Rutland',
linux-leds, devicetree, subbaram, aghayal, wruan, kgunda
[-- Attachment #1: Type: text/plain, Size: 705 bytes --]
Hi!
> Thanks for the reviewing.
>
> For the order, the hardware register mapping has this order (blue/green/red)
> from bit0/1/2, I can revert it to (red/green/blue) if there is a strong
> concern.
I'd do that.
> For these two properties: qcom,off-ms/ qcom,on-ms, I am using them to assign
> the default blinking on/off time, then the LEDs would have a default
> blinking pattern if you do "echo 1 > /sys/class/leds/red/blink"
Normally, dts describes hardware; this does not seem to be hardware
description, so I'd leave it out.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* RE: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
2017-05-31 16:55 ` Pavel Machek
@ 2017-06-01 0:30 ` Wu Fenglin
0 siblings, 0 replies; 15+ messages in thread
From: Wu Fenglin @ 2017-06-01 0:30 UTC (permalink / raw)
To: 'Pavel Machek'
Cc: linux-arm-msm, linux-kernel, 'Richard Purdie',
'Jacek Anaszewski', 'Rob Herring',
'Mark Rutland',
linux-leds, devicetree, subbaram, aghayal, wruan, kgunda
Thanks Pavel.
I will remove these two dts properties and define the default on/off time
values in C code.
--
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux
Foundation Collaborative Project.
^ permalink raw reply [flat|nested] 15+ messages in thread
* RE: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
@ 2017-06-01 0:30 ` Wu Fenglin
0 siblings, 0 replies; 15+ messages in thread
From: Wu Fenglin @ 2017-06-01 0:30 UTC (permalink / raw)
To: 'Pavel Machek'
Cc: linux-arm-msm, linux-kernel, 'Richard Purdie',
'Jacek Anaszewski', 'Rob Herring',
'Mark Rutland',
linux-leds, devicetree, subbaram, aghayal, wruan, kgunda
Thanks Pavel.
I will remove these two dts properties and define the default on/off time
values in C code.
--
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux
Foundation Collaborative Project.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
2017-06-01 0:30 ` Wu Fenglin
@ 2017-06-01 19:31 ` Jacek Anaszewski
-1 siblings, 0 replies; 15+ messages in thread
From: Jacek Anaszewski @ 2017-06-01 19:31 UTC (permalink / raw)
To: Wu Fenglin, 'Pavel Machek'
Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, 'Richard Purdie',
'Rob Herring', 'Mark Rutland',
linux-leds-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
subbaram-jfJNa2p1gH1BDgjK7y7TUQ, aghayal-Rm6X0d1/PG5y9aJCnZT0Uw,
wruan-jfJNa2p1gH1BDgjK7y7TUQ, kgunda-Rm6X0d1/PG5y9aJCnZT0Uw
Hi Wu,
On 06/01/2017 02:30 AM, Wu Fenglin wrote:
> Thanks Pavel.
> I will remove these two dts properties and define the default on/off time
> values in C code.
There are many things to sort out regarding your patch. I'll be able to
give you a detailed feedback probably no sooner than at the weekend.
--
Best regards,
Jacek Anaszewski
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module
@ 2017-06-01 19:31 ` Jacek Anaszewski
0 siblings, 0 replies; 15+ messages in thread
From: Jacek Anaszewski @ 2017-06-01 19:31 UTC (permalink / raw)
To: Wu Fenglin, 'Pavel Machek'
Cc: linux-arm-msm, linux-kernel, 'Richard Purdie',
'Rob Herring', 'Mark Rutland',
linux-leds, devicetree, subbaram, aghayal, wruan, kgunda
Hi Wu,
On 06/01/2017 02:30 AM, Wu Fenglin wrote:
> Thanks Pavel.
> I will remove these two dts properties and define the default on/off time
> values in C code.
There are many things to sort out regarding your patch. I'll be able to
give you a detailed feedback probably no sooner than at the weekend.
--
Best regards,
Jacek Anaszewski
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module
2017-05-31 6:14 ` [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module fenglinw
@ 2017-06-03 23:20 ` kbuild test robot
2017-06-07 21:19 ` Rob Herring
1 sibling, 0 replies; 15+ messages in thread
From: kbuild test robot @ 2017-06-03 23:20 UTC (permalink / raw)
Cc: kbuild-all, linux-arm-msm, linux-kernel, Thierry Reding,
Rob Herring, Mark Rutland, linux-pwm, devicetree, subbaram,
aghayal, wruan, kgunda, Fenglin Wu
[-- Attachment #1: Type: text/plain, Size: 2055 bytes --]
Hi Fenglin,
[auto build test ERROR on j.anaszewski-leds/for-next]
[also build test ERROR on v4.12-rc3 next-20170602]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/fenglinw-codeaurora-org/leds-leds-qti-rgb-Add-LED-driver-for-QTI-TRI_LED-module/20170531-153634
base: https://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git for-next
config: m68k-allyesconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 4.9.0
reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=m68k
All errors (new ones prefixed by >>):
drivers/pwm/pwm-qti-lpg.c: In function 'qti_lpg_pwm_dbg_show':
>> drivers/pwm/pwm-qti-lpg.c:428:4: error: implicit declaration of function 'seq_printf' [-Werror=implicit-function-declaration]
seq_printf(s, "LPG %d is requested by %s\n",
^
>> drivers/pwm/pwm-qti-lpg.c:437:4: error: implicit declaration of function 'seq_puts' [-Werror=implicit-function-declaration]
seq_puts(s, " enabled\n");
^
cc1: some warnings being treated as errors
vim +/seq_printf +428 drivers/pwm/pwm-qti-lpg.c
422 if (lpg == NULL) {
423 dev_err(pwm_chip->dev, "lpg not found\n");
424 return;
425 }
426
427 if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
> 428 seq_printf(s, "LPG %d is requested by %s\n",
429 lpg->lpg_idx + 1, pwm->label);
430 } else {
431 seq_printf(s, "LPG %d is free\n",
432 lpg->lpg_idx + 1);
433 continue;
434 }
435
436 if (pwm_is_enabled(pwm)) {
> 437 seq_puts(s, " enabled\n");
438 } else {
439 seq_puts(s, " disabled\n");
440 continue;
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 41150 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module
@ 2017-06-03 23:20 ` kbuild test robot
0 siblings, 0 replies; 15+ messages in thread
From: kbuild test robot @ 2017-06-03 23:20 UTC (permalink / raw)
To: fenglinw
Cc: kbuild-all, linux-arm-msm, linux-kernel, Thierry Reding,
Rob Herring, Mark Rutland, linux-pwm, devicetree, subbaram,
aghayal, wruan, kgunda, Fenglin Wu
[-- Attachment #1: Type: text/plain, Size: 2055 bytes --]
Hi Fenglin,
[auto build test ERROR on j.anaszewski-leds/for-next]
[also build test ERROR on v4.12-rc3 next-20170602]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/fenglinw-codeaurora-org/leds-leds-qti-rgb-Add-LED-driver-for-QTI-TRI_LED-module/20170531-153634
base: https://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git for-next
config: m68k-allyesconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 4.9.0
reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=m68k
All errors (new ones prefixed by >>):
drivers/pwm/pwm-qti-lpg.c: In function 'qti_lpg_pwm_dbg_show':
>> drivers/pwm/pwm-qti-lpg.c:428:4: error: implicit declaration of function 'seq_printf' [-Werror=implicit-function-declaration]
seq_printf(s, "LPG %d is requested by %s\n",
^
>> drivers/pwm/pwm-qti-lpg.c:437:4: error: implicit declaration of function 'seq_puts' [-Werror=implicit-function-declaration]
seq_puts(s, " enabled\n");
^
cc1: some warnings being treated as errors
vim +/seq_printf +428 drivers/pwm/pwm-qti-lpg.c
422 if (lpg == NULL) {
423 dev_err(pwm_chip->dev, "lpg not found\n");
424 return;
425 }
426
427 if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
> 428 seq_printf(s, "LPG %d is requested by %s\n",
429 lpg->lpg_idx + 1, pwm->label);
430 } else {
431 seq_printf(s, "LPG %d is free\n",
432 lpg->lpg_idx + 1);
433 continue;
434 }
435
436 if (pwm_is_enabled(pwm)) {
> 437 seq_puts(s, " enabled\n");
438 } else {
439 seq_puts(s, " disabled\n");
440 continue;
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 41150 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module
2017-05-31 6:14 ` [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module fenglinw
2017-06-03 23:20 ` kbuild test robot
@ 2017-06-07 21:19 ` Rob Herring
1 sibling, 0 replies; 15+ messages in thread
From: Rob Herring @ 2017-06-07 21:19 UTC (permalink / raw)
To: fenglinw
Cc: linux-arm-msm, linux-kernel, Thierry Reding, Mark Rutland,
linux-pwm, devicetree, subbaram, aghayal, wruan, kgunda
On Wed, May 31, 2017 at 02:14:38PM +0800, fenglinw@codeaurora.org wrote:
> From: Fenglin Wu <fenglinw@codeaurora.org>
>
> Add pwm_chip to support QTI LPG module and export LPG channels as
> PWM devices for consumer drivers' usage.
>
> Signed-off-by: Fenglin Wu <fenglinw@codeaurora.org>
> ---
> .../devicetree/bindings/pwm/pwm-qti-lpg.txt | 39 ++
Please put binding in a separate patch.
> drivers/pwm/Kconfig | 10 +
> drivers/pwm/Makefile | 1 +
> drivers/pwm/pwm-qti-lpg.c | 578 +++++++++++++++++++++
> 4 files changed, 628 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
> create mode 100644 drivers/pwm/pwm-qti-lpg.c
>
> diff --git a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
> new file mode 100644
> index 0000000..df81f5f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
> @@ -0,0 +1,39 @@
> +Qualcomm Technologies, Inc. LPG driver specific bindings
> +
> +This binding document describes the properties of LPG (Light Pulse Generator)
> +device module in Qualcomm Technologies, Inc. PMIC chips.
> +
> +- compatible:
> + Usage: required
> + Value type: <string>
> + Definition: Must be "qcom,pwm-lpg".
Needs SoC specific compatible strings.
> +
> +- reg:
> + Usage: required
> + Value type: <prop-encoded-array>
> + Definition: Register base and length for LPG modules. The length
> + varies based on the number of channels available in
> + the PMIC chips.
> +
> +- reg-names:
> + Usage: required
> + Value type: <string>
> + Definition: The name of the register defined in the reg property.
> + It must be "lpg-base".
-names is pointless when there is only 1.
> +
> +- #pwm-cells:
> + Usage: required
> + Value type: <u32>
> + Definition: The number of cells in "pwms" property specified in
> + PWM user nodes. It should be 2. The first cell is
> + the PWM channel ID indexed from 0, and the second
> + cell is the PWM default period in nanoseconds.
> +
> +Example:
> +
> + pmi8998_lpg: lpg@b100 {
> + compatible = "qcom,pwm-lpg";
> + reg = <0xb100 0x600>;
> + reg-names = "lpg-base";
> + #pwm-cells = <2>;
> + };
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2017-06-07 21:19 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-31 6:14 [PATCH V1 0/2] *** Add support for QTI TRI_LED and LPG module *** fenglinw
2017-05-31 6:14 ` [PATCH V1 1/2] leds: leds-qti-rgb: Add LED driver for QTI TRI_LED module fenglinw
[not found] ` <20170531061541.10808-2-fenglinw-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2017-05-31 7:55 ` Pavel Machek
2017-05-31 7:55 ` Pavel Machek
2017-05-31 8:23 ` Wu Fenglin
2017-05-31 8:23 ` Wu Fenglin
2017-05-31 16:55 ` Pavel Machek
2017-06-01 0:30 ` Wu Fenglin
2017-06-01 0:30 ` Wu Fenglin
2017-06-01 19:31 ` Jacek Anaszewski
2017-06-01 19:31 ` Jacek Anaszewski
2017-05-31 6:14 ` [PATCH V1 2/2] pwm: pwm-qti-lpg: Add PWM driver for QTI LPG module fenglinw
2017-06-03 23:20 ` kbuild test robot
2017-06-03 23:20 ` kbuild test robot
2017-06-07 21:19 ` Rob Herring
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.