* [PATCH v5 1/9] pwm: extend PWM framework with PWM modes
2018-05-22 12:07 [PATCH v5 0/9] extend PWM framework to support PWM modes Claudiu Beznea
@ 2018-05-22 12:07 ` Claudiu Beznea
2018-05-22 12:07 ` [PATCH v5 2/9] pwm: clps711x: populate PWM mode in of_xlate function Claudiu Beznea
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Claudiu Beznea @ 2018-05-22 12:07 UTC (permalink / raw)
To: thierry.reding, shc_work, robh+dt, mark.rutland, corbet,
nicolas.ferre, alexandre.belloni
Cc: linux-pwm, linux-kernel, linux-arm-kernel, Claudiu Beznea
Add basic PWM modes: normal and complementary. These modes should
differentiate the single output PWM channels from two outputs PWM
channels. These modes could be set as follow:
1. PWM channels with one output per channel:
- normal mode
2. PWM channels with two outputs per channel:
- normal mode
- complementary mode
Since users could use a PWM channel with two output as one output PWM
channel, the PWM normal mode is allowed to be set for PWM channels with
two outputs; in fact PWM normal mode should be supported by all PWMs.
The PWM capabilities were implemented per PWM channel. Every PWM controller
will register a function to get PWM capabilities. If this is not explicitly
set by the driver a default function will be used to retrieve the PWM
capabilities (in this case the PWM capabilities will contain only PWM
normal mode). This function is set in pwmchip_add_with_polarity() as a
member of "struct pwm_chip". To retrieve capabilities the pwm_get_caps()
function could be used.
Every PWM channel have associated a mode in the PWM state. Proper
support was added to get/set PWM mode. The mode could also be set
from DT via flag cells. The valid DT modes are located in
include/dt-bindings/pwm/pwm.h. Only modes supported by PWM channel could be
set. If nothing is specified for a PWM channel, via DT, the first available
mode will be used (normally, this will be PWM normal mode).
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
drivers/pwm/core.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++--
drivers/pwm/sysfs.c | 61 ++++++++++++++++++++++++++
include/linux/pwm.h | 39 +++++++++++++++++
3 files changed, 221 insertions(+), 3 deletions(-)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 1581f6ab1b1f..59a9df9120de 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -136,6 +136,7 @@ struct pwm_device *
of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
{
struct pwm_device *pwm;
+ int modebit;
/* check, whether the driver supports a third cell for flags */
if (pc->of_pwm_n_cells < 3)
@@ -154,9 +155,23 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
pwm->args.period = args->args[1];
pwm->args.polarity = PWM_POLARITY_NORMAL;
+ pwm->args.mode = pwm_mode_get_valid(pc, pwm);
- if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED)
- pwm->args.polarity = PWM_POLARITY_INVERSED;
+ if (args->args_count > 2) {
+ if (args->args[2] & PWM_POLARITY_INVERTED)
+ pwm->args.polarity = PWM_POLARITY_INVERSED;
+
+ for (modebit = PWMC_MODE_COMPLEMENTARY_BIT;
+ modebit < PWMC_MODE_CNT; modebit++) {
+ unsigned long mode = BIT(modebit);
+
+ if ((args->args[2] & mode) &&
+ pwm_mode_valid(pwm, mode)) {
+ pwm->args.mode = mode;
+ break;
+ }
+ }
+ }
return pwm;
}
@@ -183,6 +198,7 @@ of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
return pwm;
pwm->args.period = args->args[1];
+ pwm->args.mode = pwm_mode_get_valid(pc, pwm);
return pwm;
}
@@ -250,6 +266,97 @@ static bool pwm_ops_check(const struct pwm_ops *ops)
}
/**
+ * pwm_get_caps() - get PWM capabilities of a PWM device
+ * @chip: PWM chip
+ * @pwm: PWM device to get the capabilities for
+ * @caps: returned capabilities
+ *
+ * Returns: 0 on success or a negative error code on failure
+ */
+int pwm_get_caps(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_caps *caps)
+{
+ if (!chip || !pwm || !caps)
+ return -EINVAL;
+
+ if (chip->ops && chip->ops->get_caps)
+ pwm->chip->ops->get_caps(chip, pwm, caps);
+ else if (chip->get_default_caps)
+ chip->get_default_caps(caps);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pwm_get_caps);
+
+static void pwmchip_get_default_caps(struct pwm_caps *caps)
+{
+ static const struct pwm_caps default_caps = {
+ .modes = PWMC_MODE(NORMAL),
+ };
+
+ if (!caps)
+ return;
+
+ *caps = default_caps;
+}
+
+/**
+ * pwm_mode_get_valid() - get the first available valid mode for PWM
+ * @chip: PWM chip
+ * @pwm: PWM device to get the valid mode for
+ *
+ * Returns: first valid mode for PWM device
+ */
+unsigned long pwm_mode_get_valid(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pwm_caps caps;
+
+ if (pwm_get_caps(chip, pwm, &caps))
+ return PWMC_MODE(NORMAL);
+
+ return BIT(ffs(caps.modes) - 1);
+}
+EXPORT_SYMBOL_GPL(pwm_mode_get_valid);
+
+/**
+ * pwm_mode_valid() - check if mode is valid for PWM device
+ * @pwm: PWM device
+ * @mode: PWM mode to check if valid
+ *
+ * Returns: true if mode is valid and false otherwise
+ */
+bool pwm_mode_valid(struct pwm_device *pwm, unsigned long mode)
+{
+ struct pwm_caps caps;
+
+ if (!pwm || !mode)
+ return false;
+
+ if (hweight_long(mode) != 1 || ffs(mode) - 1 >= PWMC_MODE_CNT)
+ return false;
+
+ if (pwm_get_caps(pwm->chip, pwm, &caps))
+ return false;
+
+ return (caps.modes & mode);
+}
+EXPORT_SYMBOL_GPL(pwm_mode_valid);
+
+const char *pwm_mode_desc(struct pwm_device *pwm, unsigned long mode)
+{
+ static const char * const modes[] = {
+ "invalid",
+ "normal",
+ "complementary",
+ };
+
+ if (!pwm_mode_valid(pwm, mode))
+ return modes[0];
+
+ return modes[ffs(mode)];
+}
+
+/**
* pwmchip_add_with_polarity() - register a new PWM chip
* @chip: the PWM chip to add
* @polarity: initial polarity of PWM channels
@@ -275,6 +382,8 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
mutex_lock(&pwm_lock);
+ chip->get_default_caps = pwmchip_get_default_caps;
+
ret = alloc_pwms(chip->base, chip->npwm);
if (ret < 0)
goto out;
@@ -294,6 +403,7 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
pwm->pwm = chip->base + i;
pwm->hwpwm = i;
pwm->state.polarity = polarity;
+ pwm->state.mode = pwm_mode_get_valid(chip, pwm);
if (chip->ops->get_state)
chip->ops->get_state(chip, pwm, &pwm->state);
@@ -469,7 +579,8 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
int err;
if (!pwm || !state || !state->period ||
- state->duty_cycle > state->period)
+ state->duty_cycle > state->period ||
+ !pwm_mode_valid(pwm, state->mode))
return -EINVAL;
if (!memcmp(state, &pwm->state, sizeof(*state)))
@@ -530,6 +641,9 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
pwm->state.enabled = state->enabled;
}
+
+ /* No mode support for non-atomic PWM. */
+ pwm->state.mode = state->mode;
}
return 0;
@@ -579,6 +693,8 @@ int pwm_adjust_config(struct pwm_device *pwm)
pwm_get_args(pwm, &pargs);
pwm_get_state(pwm, &state);
+ state.mode = pargs.mode;
+
/*
* If the current period is zero it means that either the PWM driver
* does not support initial state retrieval or the PWM has not yet
@@ -850,6 +966,7 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
pwm->args.period = chosen->period;
pwm->args.polarity = chosen->polarity;
+ pwm->args.mode = pwm_mode_get_valid(chip, pwm);
return pwm;
}
@@ -999,6 +1116,7 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
seq_printf(s, " duty: %u ns", state.duty_cycle);
seq_printf(s, " polarity: %s",
state.polarity ? "inverse" : "normal");
+ seq_printf(s, " mode: %s", pwm_mode_desc(pwm, state.mode));
seq_puts(s, "\n");
}
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index 83f2b0b15712..785eda0b1e67 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -223,11 +223,71 @@ static ssize_t capture_show(struct device *child,
return sprintf(buf, "%u %u\n", result.period, result.duty_cycle);
}
+static ssize_t mode_show(struct device *child,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pwm_device *pwm = child_to_pwm_device(child);
+ struct pwm_state state;
+ unsigned long mode;
+ int modebit, len = 0;
+
+ pwm_get_state(pwm, &state);
+
+ for (modebit = PWMC_MODE_NORMAL_BIT;
+ modebit < PWMC_MODE_CNT; modebit++) {
+ mode = BIT(modebit);
+ if (pwm_mode_valid(pwm, mode)) {
+ if (state.mode == mode)
+ len += scnprintf(buf + len,
+ PAGE_SIZE - len, "[%s] ",
+ pwm_mode_desc(pwm, mode));
+ else
+ len += scnprintf(buf + len,
+ PAGE_SIZE - len, "%s ",
+ pwm_mode_desc(pwm, mode));
+ }
+ }
+
+ len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
+ return len;
+}
+
+static ssize_t mode_store(struct device *child,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct pwm_export *export = child_to_pwm_export(child);
+ struct pwm_device *pwm = export->pwm;
+ struct pwm_state state;
+ unsigned long mode;
+ int modebit, ret;
+
+ for (modebit = PWMC_MODE_NORMAL_BIT;
+ modebit < PWMC_MODE_CNT; modebit++) {
+ mode = BIT(modebit);
+ if (sysfs_streq(buf, pwm_mode_desc(pwm, mode)))
+ break;
+ }
+
+ if (modebit == PWMC_MODE_CNT)
+ return -EINVAL;
+
+ mutex_lock(&export->lock);
+ pwm_get_state(pwm, &state);
+ state.mode = mode;
+ ret = pwm_apply_state(pwm, &state);
+ mutex_unlock(&export->lock);
+
+ return ret ? : size;
+}
+
static DEVICE_ATTR_RW(period);
static DEVICE_ATTR_RW(duty_cycle);
static DEVICE_ATTR_RW(enable);
static DEVICE_ATTR_RW(polarity);
static DEVICE_ATTR_RO(capture);
+static DEVICE_ATTR_RW(mode);
static struct attribute *pwm_attrs[] = {
&dev_attr_period.attr,
@@ -235,6 +295,7 @@ static struct attribute *pwm_attrs[] = {
&dev_attr_enable.attr,
&dev_attr_polarity.attr,
&dev_attr_capture.attr,
+ &dev_attr_mode.attr,
NULL
};
ATTRIBUTE_GROUPS(pwm);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 56518adc31dd..a4ce4ad7edf0 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -26,9 +26,32 @@ enum pwm_polarity {
};
/**
+ * PWM modes capabilities
+ * @PWMC_MODE_NORMAL_BIT: PWM has one output
+ * @PWMC_MODE_COMPLEMENTARY_BIT: PWM has 2 outputs with opposite polarities
+ * @PWMC_MODE_CNT: PWM modes count
+ */
+enum {
+ PWMC_MODE_NORMAL_BIT,
+ PWMC_MODE_COMPLEMENTARY_BIT,
+ PWMC_MODE_CNT,
+};
+
+#define PWMC_MODE(name) BIT(PWMC_MODE_##name##_BIT)
+
+/**
+ * struct pwm_caps - PWM capabilities
+ * @modes: PWM modes
+ */
+struct pwm_caps {
+ unsigned long modes;
+};
+
+/**
* struct pwm_args - board-dependent PWM arguments
* @period: reference period
* @polarity: reference polarity
+ * @mode: reference mode
*
* This structure describes board-dependent arguments attached to a PWM
* device. These arguments are usually retrieved from the PWM lookup table or
@@ -41,6 +64,7 @@ enum pwm_polarity {
struct pwm_args {
unsigned int period;
enum pwm_polarity polarity;
+ unsigned long mode;
};
enum {
@@ -53,12 +77,14 @@ enum {
* @period: PWM period (in nanoseconds)
* @duty_cycle: PWM duty cycle (in nanoseconds)
* @polarity: PWM polarity
+ * @mode: PWM mode
* @enabled: PWM enabled status
*/
struct pwm_state {
unsigned int period;
unsigned int duty_cycle;
enum pwm_polarity polarity;
+ unsigned long mode;
bool enabled;
};
@@ -181,6 +207,7 @@ static inline void pwm_init_state(const struct pwm_device *pwm,
state->period = args.period;
state->polarity = args.polarity;
state->duty_cycle = 0;
+ state->mode = args.mode;
}
/**
@@ -254,6 +281,7 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle,
* @get_state: get the current PWM state. This function is only
* called once per PWM device when the PWM chip is
* registered.
+ * @get_caps: get PWM capabilities.
* @dbg_show: optional routine to show contents in debugfs
* @owner: helps prevent removal of modules exporting active PWMs
*/
@@ -272,6 +300,8 @@ struct pwm_ops {
struct pwm_state *state);
void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state);
+ void (*get_caps)(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_caps *caps);
#ifdef CONFIG_DEBUG_FS
void (*dbg_show)(struct pwm_chip *chip, struct seq_file *s);
#endif
@@ -287,6 +317,7 @@ struct pwm_ops {
* @npwm: number of PWMs controlled by this chip
* @pwms: array of PWM devices allocated by the framework
* @of_xlate: request a PWM device given a device tree PWM specifier
+ * @get_default_caps: get default PWM capabilities
* @of_pwm_n_cells: number of cells expected in the device tree PWM specifier
*/
struct pwm_chip {
@@ -300,6 +331,7 @@ struct pwm_chip {
struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
const struct of_phandle_args *args);
+ void (*get_default_caps)(struct pwm_caps *caps);
unsigned int of_pwm_n_cells;
};
@@ -438,6 +470,12 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
unsigned int index,
const char *label);
+int pwm_get_caps(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_caps *caps);
+unsigned long pwm_mode_get_valid(struct pwm_chip *chip,
+ struct pwm_device *pwm);
+bool pwm_mode_valid(struct pwm_device *pwm, unsigned long mode);
+const char *pwm_mode_desc(struct pwm_device *pwm, unsigned long mode);
struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc,
const struct of_phandle_args *args);
@@ -592,6 +630,7 @@ static inline void pwm_apply_args(struct pwm_device *pwm)
state.enabled = false;
state.polarity = pwm->args.polarity;
state.period = pwm->args.period;
+ state.mode = pwm->args.mode;
pwm_apply_state(pwm, &state);
}
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 2/9] pwm: clps711x: populate PWM mode in of_xlate function
2018-05-22 12:07 [PATCH v5 0/9] extend PWM framework to support PWM modes Claudiu Beznea
2018-05-22 12:07 ` [PATCH v5 1/9] pwm: extend PWM framework with " Claudiu Beznea
@ 2018-05-22 12:07 ` Claudiu Beznea
2018-05-22 12:07 ` [PATCH v5 3/9] pwm: cros-ec: " Claudiu Beznea
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Claudiu Beznea @ 2018-05-22 12:07 UTC (permalink / raw)
To: thierry.reding, shc_work, robh+dt, mark.rutland, corbet,
nicolas.ferre, alexandre.belloni
Cc: linux-pwm, linux-kernel, linux-arm-kernel, Claudiu Beznea
Populate PWM mode in of_xlate function to avoid pwm_apply_state() failure.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
drivers/pwm/pwm-clps711x.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c
index 26ec24e457b1..d742e8e375c7 100644
--- a/drivers/pwm/pwm-clps711x.c
+++ b/drivers/pwm/pwm-clps711x.c
@@ -109,10 +109,18 @@ static const struct pwm_ops clps711x_pwm_ops = {
static struct pwm_device *clps711x_pwm_xlate(struct pwm_chip *chip,
const struct of_phandle_args *args)
{
+ struct pwm_device *pwm;
+
if (args->args[0] >= chip->npwm)
return ERR_PTR(-EINVAL);
- return pwm_request_from_chip(chip, args->args[0], NULL);
+ pwm = pwm_request_from_chip(chip, args->args[0], NULL);
+ if (IS_ERR(pwm))
+ return pwm;
+
+ pwm->args.mode = pwm_mode_get_valid(chip, pwm);
+
+ return pwm;
}
static int clps711x_pwm_probe(struct platform_device *pdev)
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 3/9] pwm: cros-ec: populate PWM mode in of_xlate function
2018-05-22 12:07 [PATCH v5 0/9] extend PWM framework to support PWM modes Claudiu Beznea
2018-05-22 12:07 ` [PATCH v5 1/9] pwm: extend PWM framework with " Claudiu Beznea
2018-05-22 12:07 ` [PATCH v5 2/9] pwm: clps711x: populate PWM mode in of_xlate function Claudiu Beznea
@ 2018-05-22 12:07 ` Claudiu Beznea
2018-05-22 12:07 ` [PATCH v5 4/9] pwm: pxa: " Claudiu Beznea
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Claudiu Beznea @ 2018-05-22 12:07 UTC (permalink / raw)
To: thierry.reding, shc_work, robh+dt, mark.rutland, corbet,
nicolas.ferre, alexandre.belloni
Cc: linux-pwm, linux-kernel, linux-arm-kernel, Claudiu Beznea
Populate PWM mode in of_xlate function to avoid pwm_apply_state() failure.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
drivers/pwm/pwm-cros-ec.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/pwm/pwm-cros-ec.c b/drivers/pwm/pwm-cros-ec.c
index 9c13694eaa24..78d28d60a468 100644
--- a/drivers/pwm/pwm-cros-ec.c
+++ b/drivers/pwm/pwm-cros-ec.c
@@ -147,6 +147,7 @@ cros_ec_pwm_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
/* The EC won't let us change the period */
pwm->args.period = EC_PWM_MAX_DUTY;
+ pwm->args.mode = pwm_mode_get_valid(pc, pwm);
return pwm;
}
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 4/9] pwm: pxa: populate PWM mode in of_xlate function
2018-05-22 12:07 [PATCH v5 0/9] extend PWM framework to support PWM modes Claudiu Beznea
` (2 preceding siblings ...)
2018-05-22 12:07 ` [PATCH v5 3/9] pwm: cros-ec: " Claudiu Beznea
@ 2018-05-22 12:07 ` Claudiu Beznea
2018-05-22 12:07 ` [PATCH v5 5/9] pwm: add PWM modes Claudiu Beznea
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Claudiu Beznea @ 2018-05-22 12:07 UTC (permalink / raw)
To: thierry.reding, shc_work, robh+dt, mark.rutland, corbet,
nicolas.ferre, alexandre.belloni
Cc: linux-pwm, linux-kernel, linux-arm-kernel, Claudiu Beznea
Populate PWM mode in of_xlate function to avoid pwm_apply_state() failure.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
drivers/pwm/pwm-pxa.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c
index 4143a46684d2..4c88cb47d6ba 100644
--- a/drivers/pwm/pwm-pxa.c
+++ b/drivers/pwm/pwm-pxa.c
@@ -161,6 +161,7 @@ pxa_pwm_of_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
return pwm;
pwm->args.period = args->args[0];
+ pwm->args.mode = pwm_mode_get_valid(pc, pwm);
return pwm;
}
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 5/9] pwm: add PWM modes
2018-05-22 12:07 [PATCH v5 0/9] extend PWM framework to support PWM modes Claudiu Beznea
` (3 preceding siblings ...)
2018-05-22 12:07 ` [PATCH v5 4/9] pwm: pxa: " Claudiu Beznea
@ 2018-05-22 12:07 ` Claudiu Beznea
2018-05-22 12:07 ` [PATCH v5 6/9] pwm: atmel: add pwm capabilities Claudiu Beznea
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Claudiu Beznea @ 2018-05-22 12:07 UTC (permalink / raw)
To: thierry.reding, shc_work, robh+dt, mark.rutland, corbet,
nicolas.ferre, alexandre.belloni
Cc: linux-pwm, linux-kernel, linux-arm-kernel, Claudiu Beznea
Add PWM normal and complementary modes.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
Documentation/devicetree/bindings/pwm/pwm.txt | 9 +++++++--
Documentation/pwm.txt | 26 +++++++++++++++++++++++---
include/dt-bindings/pwm/pwm.h | 1 +
3 files changed, 31 insertions(+), 5 deletions(-)
diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
index 8556263b8502..7c8aaac43f92 100644
--- a/Documentation/devicetree/bindings/pwm/pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/pwm.txt
@@ -46,11 +46,16 @@ period in nanoseconds.
Optionally, the pwm-specifier can encode a number of flags (defined in
<dt-bindings/pwm/pwm.h>) in a third cell:
- PWM_POLARITY_INVERTED: invert the PWM signal polarity
+- PWM_MODE_COMPLEMENTARY: PWM complementary working mode (for PWM channels
+with two outputs); if not specified, the default for PWM channel will be
+used
-Example with optional PWM specifier for inverse polarity
+Example with optional PWM specifier for inverse polarity and complementary
+mode:
bl: backlight {
- pwms = <&pwm 0 5000000 PWM_POLARITY_INVERTED>;
+ pwms = <&pwm 0 5000000
+ (PWM_MODE_COMPLEMENTARY | PWM_POLARITY_INVERTED)>;
pwm-names = "backlight";
};
diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
index 8fbf0aa3ba2d..912c43da8b48 100644
--- a/Documentation/pwm.txt
+++ b/Documentation/pwm.txt
@@ -61,9 +61,9 @@ In addition to the PWM state, the PWM API also exposes PWM arguments, which
are the reference PWM config one should use on this PWM.
PWM arguments are usually platform-specific and allows the PWM user to only
care about dutycycle relatively to the full period (like, duty = 50% of the
-period). struct pwm_args contains 2 fields (period and polarity) and should
-be used to set the initial PWM config (usually done in the probe function
-of the PWM user). PWM arguments are retrieved with pwm_get_args().
+period). struct pwm_args contains 3 fields (period, polarity and mode) and
+should be used to set the initial PWM config (usually done in the probe
+function of the PWM user). PWM arguments are retrieved with pwm_get_args().
Using PWMs with the sysfs interface
-----------------------------------
@@ -110,6 +110,26 @@ channel that was exported. The following properties will then be available:
- 0 - disabled
- 1 - enabled
+ mode
+ Get/set PWM channel working mode.
+
+ Normal mode - for PWM channels with one output; this should be the
+ default working mode for every PWM channel; output waveforms looks
+ like this:
+ __ __ __ __
+ PWM __| |__| |__| |__| |__
+ <--T-->
+
+ Complementary mode - for PWM channels with two outputs; output waveforms
+ looks line this:
+ __ __ __ __
+ PWMH1 __| |__| |__| |__| |__
+ __ __ __ __ __
+ PWML1 |__| |__| |__| |__|
+ <--T-->
+
+ Where T is the signal period.
+
Implementing a PWM driver
-------------------------
diff --git a/include/dt-bindings/pwm/pwm.h b/include/dt-bindings/pwm/pwm.h
index ab9a077e3c7d..b82279cc1787 100644
--- a/include/dt-bindings/pwm/pwm.h
+++ b/include/dt-bindings/pwm/pwm.h
@@ -11,5 +11,6 @@
#define _DT_BINDINGS_PWM_PWM_H
#define PWM_POLARITY_INVERTED (1 << 0)
+#define PWM_MODE_COMPLEMENTARY (1 << 1)
#endif
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 6/9] pwm: atmel: add pwm capabilities
2018-05-22 12:07 [PATCH v5 0/9] extend PWM framework to support PWM modes Claudiu Beznea
` (4 preceding siblings ...)
2018-05-22 12:07 ` [PATCH v5 5/9] pwm: add PWM modes Claudiu Beznea
@ 2018-05-22 12:07 ` Claudiu Beznea
2018-05-22 12:07 ` [PATCH v5 7/9] pwm: add push-pull mode support Claudiu Beznea
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Claudiu Beznea @ 2018-05-22 12:07 UTC (permalink / raw)
To: thierry.reding, shc_work, robh+dt, mark.rutland, corbet,
nicolas.ferre, alexandre.belloni
Cc: linux-pwm, linux-kernel, linux-arm-kernel, Claudiu Beznea
Add pwm capabilities for Atmel/Microchip PWM controllers.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
drivers/pwm/pwm-atmel.c | 80 ++++++++++++++++++++++++++++++++-----------------
1 file changed, 52 insertions(+), 28 deletions(-)
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index 530d7dc5f1b5..87ef54bd492c 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -65,11 +65,16 @@ struct atmel_pwm_registers {
u8 duty_upd;
};
+struct atmel_pwm_data {
+ struct atmel_pwm_registers regs;
+ struct pwm_caps caps;
+};
+
struct atmel_pwm_chip {
struct pwm_chip chip;
struct clk *clk;
void __iomem *base;
- const struct atmel_pwm_registers *regs;
+ const struct atmel_pwm_data *data;
unsigned int updated_pwms;
/* ISR is cleared when read, ensure only one thread does that */
@@ -150,15 +155,15 @@ static void atmel_pwm_update_cdty(struct pwm_chip *chip, struct pwm_device *pwm,
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
u32 val;
- if (atmel_pwm->regs->duty_upd ==
- atmel_pwm->regs->period_upd) {
+ if (atmel_pwm->data->regs.duty_upd ==
+ atmel_pwm->data->regs.period_upd) {
val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
val &= ~PWM_CMR_UPD_CDTY;
atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
}
atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
- atmel_pwm->regs->duty_upd, cdty);
+ atmel_pwm->data->regs.duty_upd, cdty);
}
static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip,
@@ -168,9 +173,9 @@ static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip,
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
- atmel_pwm->regs->duty, cdty);
+ atmel_pwm->data->regs.duty, cdty);
atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
- atmel_pwm->regs->period, cprd);
+ atmel_pwm->data->regs.period, cprd);
}
static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -225,7 +230,7 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
cstate.polarity == state->polarity &&
cstate.period == state->period) {
cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm,
- atmel_pwm->regs->period);
+ atmel_pwm->data->regs.period);
atmel_pwm_calculate_cdty(state, cprd, &cdty);
atmel_pwm_update_cdty(chip, pwm, cdty);
return 0;
@@ -272,32 +277,51 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
return 0;
}
+static void atmel_pwm_get_caps(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_caps *caps)
+{
+ struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+
+ *caps = atmel_pwm->data->caps;
+}
+
static const struct pwm_ops atmel_pwm_ops = {
.apply = atmel_pwm_apply,
+ .get_caps = atmel_pwm_get_caps,
.owner = THIS_MODULE,
};
-static const struct atmel_pwm_registers atmel_pwm_regs_v1 = {
- .period = PWMV1_CPRD,
- .period_upd = PWMV1_CUPD,
- .duty = PWMV1_CDTY,
- .duty_upd = PWMV1_CUPD,
+static const struct atmel_pwm_data atmel_pwm_data_v1 = {
+ .regs = {
+ .period = PWMV1_CPRD,
+ .period_upd = PWMV1_CUPD,
+ .duty = PWMV1_CDTY,
+ .duty_upd = PWMV1_CUPD,
+ },
+ .caps = {
+ .modes = PWMC_MODE(NORMAL),
+ },
};
-static const struct atmel_pwm_registers atmel_pwm_regs_v2 = {
- .period = PWMV2_CPRD,
- .period_upd = PWMV2_CPRDUPD,
- .duty = PWMV2_CDTY,
- .duty_upd = PWMV2_CDTYUPD,
+static const struct atmel_pwm_data atmel_pwm_data_v2 = {
+ .regs = {
+ .period = PWMV2_CPRD,
+ .period_upd = PWMV2_CPRDUPD,
+ .duty = PWMV2_CDTY,
+ .duty_upd = PWMV2_CDTYUPD,
+ },
+ .caps = {
+ .modes = PWMC_MODE(NORMAL) | PWMC_MODE(COMPLEMENTARY),
+ },
};
static const struct platform_device_id atmel_pwm_devtypes[] = {
{
.name = "at91sam9rl-pwm",
- .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v1,
+ .driver_data = (kernel_ulong_t)&atmel_pwm_data_v1,
}, {
.name = "sama5d3-pwm",
- .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v2,
+ .driver_data = (kernel_ulong_t)&atmel_pwm_data_v2,
}, {
/* sentinel */
},
@@ -307,20 +331,20 @@ MODULE_DEVICE_TABLE(platform, atmel_pwm_devtypes);
static const struct of_device_id atmel_pwm_dt_ids[] = {
{
.compatible = "atmel,at91sam9rl-pwm",
- .data = &atmel_pwm_regs_v1,
+ .data = &atmel_pwm_data_v1,
}, {
.compatible = "atmel,sama5d3-pwm",
- .data = &atmel_pwm_regs_v2,
+ .data = &atmel_pwm_data_v2,
}, {
.compatible = "atmel,sama5d2-pwm",
- .data = &atmel_pwm_regs_v2,
+ .data = &atmel_pwm_data_v2,
}, {
/* sentinel */
},
};
MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids);
-static inline const struct atmel_pwm_registers *
+static inline const struct atmel_pwm_data *
atmel_pwm_get_driver_data(struct platform_device *pdev)
{
const struct platform_device_id *id;
@@ -330,18 +354,18 @@ atmel_pwm_get_driver_data(struct platform_device *pdev)
id = platform_get_device_id(pdev);
- return (struct atmel_pwm_registers *)id->driver_data;
+ return (struct atmel_pwm_data *)id->driver_data;
}
static int atmel_pwm_probe(struct platform_device *pdev)
{
- const struct atmel_pwm_registers *regs;
+ const struct atmel_pwm_data *data;
struct atmel_pwm_chip *atmel_pwm;
struct resource *res;
int ret;
- regs = atmel_pwm_get_driver_data(pdev);
- if (!regs)
+ data = atmel_pwm_get_driver_data(pdev);
+ if (!data)
return -ENODEV;
atmel_pwm = devm_kzalloc(&pdev->dev, sizeof(*atmel_pwm), GFP_KERNEL);
@@ -373,7 +397,7 @@ static int atmel_pwm_probe(struct platform_device *pdev)
atmel_pwm->chip.base = -1;
atmel_pwm->chip.npwm = 4;
- atmel_pwm->regs = regs;
+ atmel_pwm->data = data;
atmel_pwm->updated_pwms = 0;
mutex_init(&atmel_pwm->isr_lock);
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 7/9] pwm: add push-pull mode support
2018-05-22 12:07 [PATCH v5 0/9] extend PWM framework to support PWM modes Claudiu Beznea
` (5 preceding siblings ...)
2018-05-22 12:07 ` [PATCH v5 6/9] pwm: atmel: add pwm capabilities Claudiu Beznea
@ 2018-05-22 12:07 ` Claudiu Beznea
2018-05-22 12:07 ` [PATCH v5 8/9] pwm: add documentation for pwm push-pull mode Claudiu Beznea
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Claudiu Beznea @ 2018-05-22 12:07 UTC (permalink / raw)
To: thierry.reding, shc_work, robh+dt, mark.rutland, corbet,
nicolas.ferre, alexandre.belloni
Cc: linux-pwm, linux-kernel, linux-arm-kernel, Claudiu Beznea
Add push-pull mode support. In push-pull mode the channels' outputs have
same polarities and the edges are complementary delayed for one period.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
drivers/pwm/core.c | 1 +
include/linux/pwm.h | 3 +++
2 files changed, 4 insertions(+)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 59a9df9120de..5fde2e685ca7 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -348,6 +348,7 @@ const char *pwm_mode_desc(struct pwm_device *pwm, unsigned long mode)
"invalid",
"normal",
"complementary",
+ "push-pull",
};
if (!pwm_mode_valid(pwm, mode))
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index a4ce4ad7edf0..eb170e2ab431 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -29,11 +29,14 @@ enum pwm_polarity {
* PWM modes capabilities
* @PWMC_MODE_NORMAL_BIT: PWM has one output
* @PWMC_MODE_COMPLEMENTARY_BIT: PWM has 2 outputs with opposite polarities
+ * @PWMC_MODE_PUSH_PULL_BIT: PWM has 2 outputs with same polarities and the
+ * edges are complementary delayed for one period
* @PWMC_MODE_CNT: PWM modes count
*/
enum {
PWMC_MODE_NORMAL_BIT,
PWMC_MODE_COMPLEMENTARY_BIT,
+ PWMC_MODE_PUSH_PULL_BIT,
PWMC_MODE_CNT,
};
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 8/9] pwm: add documentation for pwm push-pull mode
2018-05-22 12:07 [PATCH v5 0/9] extend PWM framework to support PWM modes Claudiu Beznea
` (6 preceding siblings ...)
2018-05-22 12:07 ` [PATCH v5 7/9] pwm: add push-pull mode support Claudiu Beznea
@ 2018-05-22 12:07 ` Claudiu Beznea
2018-05-22 12:07 ` [PATCH v5 9/9] pwm: atmel: add push-pull mode support Claudiu Beznea
2018-07-18 7:07 ` [PATCH v5 0/9] extend PWM framework to support PWM modes Claudiu Beznea
9 siblings, 0 replies; 11+ messages in thread
From: Claudiu Beznea @ 2018-05-22 12:07 UTC (permalink / raw)
To: thierry.reding, shc_work, robh+dt, mark.rutland, corbet,
nicolas.ferre, alexandre.belloni
Cc: linux-pwm, linux-kernel, linux-arm-kernel, Claudiu Beznea
Add documentation for PWM push-pull mode.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/pwm/pwm.txt | 2 ++
Documentation/pwm.txt | 16 ++++++++++++++++
include/dt-bindings/pwm/pwm.h | 1 +
3 files changed, 19 insertions(+)
diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
index 7c8aaac43f92..6a60c0fca112 100644
--- a/Documentation/devicetree/bindings/pwm/pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/pwm.txt
@@ -49,6 +49,8 @@ Optionally, the pwm-specifier can encode a number of flags (defined in
- PWM_MODE_COMPLEMENTARY: PWM complementary working mode (for PWM channels
with two outputs); if not specified, the default for PWM channel will be
used
+- PWM_MODE_PUSH_PULL: PWM push-pull working modes (for PWM channels with
+two outputs); if not specified the default for PWM channel will be used
Example with optional PWM specifier for inverse polarity and complementary
mode:
diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
index 912c43da8b48..675f9351fed1 100644
--- a/Documentation/pwm.txt
+++ b/Documentation/pwm.txt
@@ -128,6 +128,22 @@ channel that was exported. The following properties will then be available:
PWML1 |__| |__| |__| |__|
<--T-->
+ Push-pull mode - for PWM channels with two outputs; output waveforms
+ for a PWM channel in push-pull mode, with normal polarity looks like
+ this:
+ __ __
+ PWMH __| |________| |________
+ __ __
+ PWML ________| |________| |__
+ <--T-->
+
+ If polarity is inversed:
+ __ ________ ________
+ PWMH |__| |__|
+ ________ ________ __
+ PWML |__| |__|
+ <--T-->
+
Where T is the signal period.
Implementing a PWM driver
diff --git a/include/dt-bindings/pwm/pwm.h b/include/dt-bindings/pwm/pwm.h
index b82279cc1787..cd4265bce968 100644
--- a/include/dt-bindings/pwm/pwm.h
+++ b/include/dt-bindings/pwm/pwm.h
@@ -12,5 +12,6 @@
#define PWM_POLARITY_INVERTED (1 << 0)
#define PWM_MODE_COMPLEMENTARY (1 << 1)
+#define PWM_MODE_PUSH_PULL (1 << 2)
#endif
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v5 9/9] pwm: atmel: add push-pull mode support
2018-05-22 12:07 [PATCH v5 0/9] extend PWM framework to support PWM modes Claudiu Beznea
` (7 preceding siblings ...)
2018-05-22 12:07 ` [PATCH v5 8/9] pwm: add documentation for pwm push-pull mode Claudiu Beznea
@ 2018-05-22 12:07 ` Claudiu Beznea
2018-07-18 7:07 ` [PATCH v5 0/9] extend PWM framework to support PWM modes Claudiu Beznea
9 siblings, 0 replies; 11+ messages in thread
From: Claudiu Beznea @ 2018-05-22 12:07 UTC (permalink / raw)
To: thierry.reding, shc_work, robh+dt, mark.rutland, corbet,
nicolas.ferre, alexandre.belloni
Cc: linux-pwm, linux-kernel, linux-arm-kernel, Claudiu Beznea
Add support for PWM push-pull mode. This is only supported by SAMA5D2 SoCs.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
drivers/pwm/pwm-atmel.c | 40 ++++++++++++++++++++++++++++++++++++----
1 file changed, 36 insertions(+), 4 deletions(-)
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index 87ef54bd492c..aaafc4dd30f2 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -33,8 +33,11 @@
#define PWM_CMR 0x0
/* Bit field in CMR */
-#define PWM_CMR_CPOL (1 << 9)
-#define PWM_CMR_UPD_CDTY (1 << 10)
+#define PWM_CMR_CPOL BIT(9)
+#define PWM_CMR_UPD_CDTY BIT(10)
+#define PWM_CMR_DTHI BIT(17)
+#define PWM_CMR_DTLI BIT(18)
+#define PWM_CMR_PPM BIT(19)
#define PWM_CMR_CPRE_MSK 0xF
/* The following registers for PWM v1 */
@@ -219,16 +222,19 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
{
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
struct pwm_state cstate;
+ struct pwm_caps caps;
unsigned long cprd, cdty;
u32 pres, val;
int ret;
pwm_get_state(pwm, &cstate);
+ pwm_get_caps(chip, pwm, &caps);
if (state->enabled) {
if (cstate.enabled &&
cstate.polarity == state->polarity &&
- cstate.period == state->period) {
+ cstate.period == state->period &&
+ cstate.mode == state->mode) {
cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm,
atmel_pwm->data->regs.period);
atmel_pwm_calculate_cdty(state, cprd, &cdty);
@@ -263,6 +269,18 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
val &= ~PWM_CMR_CPOL;
else
val |= PWM_CMR_CPOL;
+
+ /* PWM mode. */
+ if (caps.modes & PWMC_MODE(PUSH_PULL)) {
+ if (state->mode == PWMC_MODE(PUSH_PULL)) {
+ val |= PWM_CMR_PPM | PWM_CMR_DTLI;
+ val &= ~PWM_CMR_DTHI;
+ } else {
+ val &= ~(PWM_CMR_PPM | PWM_CMR_DTLI |
+ PWM_CMR_DTHI);
+ }
+ }
+
atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
atmel_pwm_set_cprd_cdty(chip, pwm, cprd, cdty);
mutex_lock(&atmel_pwm->isr_lock);
@@ -315,6 +333,20 @@ static const struct atmel_pwm_data atmel_pwm_data_v2 = {
},
};
+static const struct atmel_pwm_data atmel_pwm_data_v3 = {
+ .regs = {
+ .period = PWMV2_CPRD,
+ .period_upd = PWMV2_CPRDUPD,
+ .duty = PWMV2_CDTY,
+ .duty_upd = PWMV2_CDTYUPD,
+ },
+ .caps = {
+ .modes = PWMC_MODE(NORMAL) |
+ PWMC_MODE(COMPLEMENTARY) |
+ PWMC_MODE(PUSH_PULL),
+ },
+};
+
static const struct platform_device_id atmel_pwm_devtypes[] = {
{
.name = "at91sam9rl-pwm",
@@ -337,7 +369,7 @@ static const struct of_device_id atmel_pwm_dt_ids[] = {
.data = &atmel_pwm_data_v2,
}, {
.compatible = "atmel,sama5d2-pwm",
- .data = &atmel_pwm_data_v2,
+ .data = &atmel_pwm_data_v3,
}, {
/* sentinel */
},
--
2.7.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v5 0/9] extend PWM framework to support PWM modes
2018-05-22 12:07 [PATCH v5 0/9] extend PWM framework to support PWM modes Claudiu Beznea
` (8 preceding siblings ...)
2018-05-22 12:07 ` [PATCH v5 9/9] pwm: atmel: add push-pull mode support Claudiu Beznea
@ 2018-07-18 7:07 ` Claudiu Beznea
9 siblings, 0 replies; 11+ messages in thread
From: Claudiu Beznea @ 2018-07-18 7:07 UTC (permalink / raw)
To: thierry.reding, shc_work, robh+dt, mark.rutland, corbet,
nicolas.ferre, alexandre.belloni
Cc: linux-pwm, linux-kernel, linux-arm-kernel
Hi Thierry,
It's been a while since I submitted this version but no feedback so far on
it. I re-done it as you suggested. Do you have any thoughts on this version?
Thank you,
Claudiu beznea
On 22.05.2018 15:07, Claudiu Beznea wrote:
> Hi,
>
> Please give feedback on these patches which extends the PWM framework in
> order to support multiple PWM modes of operations. This series is a rework
> of [1] and [2].
>
> The current patch series add the following PWM modes:
> - PWM mode normal
> - PWM mode complementary
> - PWM mode push-pull
>
> Normal mode - for PWM channels with one output; output waveforms looks like
> this:
> __ __ __ __
> PWM __| |__| |__| |__| |__
> <--T-->
>
> Where T is the signal period
>
> Since PWMs with more than one output per channel could be used as one
> output PWM the normal mode is the default mode for all PWMs (if not
> specified otherwise).
>
> Complementary mode - for PWM channels with two outputs; output waveforms
> for a PWM channel in complementary mode looks line this:
> __ __ __ __
> PWMH1 __| |__| |__| |__| |__
> __ __ __ __ __
> PWML1 |__| |__| |__| |__|
> <--T-->
>
> Where T is the signal period.
>
> Push-pull mode - for PWM channels with two outputs; output waveforms for a
> PWM channel in push-pull mode with normal polarity looks like this:
> __ __
> PWMH __| |________| |________
> __ __
> PWML ________| |________| |__
> <--T-->
>
> If polarity is inversed:
> __ ________ ________
> PWMH |__| |__|
> ________ ________ __
> PWML |__| |__|
> <--T-->
>
> Where T is the signal period.
>
> The PWM working modes are per PWM channel registered as PWM's capabilities.
> The driver registers itself to PWM core a get_caps() function, in
> struct pwm_ops, that will be used by PWM core to retrieve PWM capabilities.
> If this function is not registered in driver's probe, a default function
> will be used to retrieve PWM capabilities. Currently, the default
> capabilities includes only PWM normal mode.
>
> PWM state has been updated to keep PWM mode. PWM mode could be configured
> via sysfs or via DT. pwm_apply_state() will do the preliminary validation
> for PWM mode to be applied.
>
> In sysfs, user could get PWM modes by reading mode file of PWM device:
> root@sama5d2-xplained:/sys/class/pwm/pwmchip0/pwm2# ls -l
> total 0
> -r--r--r-- 1 root root 4096 Oct 9 09:07 capture
> lrwxrwxrwx 1 root root 0 Oct 9 09:07 device -> ../../pwmchip0
> -rw-r--r-- 1 root root 4096 Oct 9 08:42 duty_cycle
> -rw-r--r-- 1 root root 4096 Oct 9 08:44 enable
> --w------- 1 root root 4096 Oct 9 09:07 export
> -rw-r--r-- 1 root root 4096 Oct 9 08:43 mode
> -r--r--r-- 1 root root 4096 Oct 9 09:07 npwm
> -rw-r--r-- 1 root root 4096 Oct 9 08:42 period
> -rw-r--r-- 1 root root 4096 Oct 9 08:44 polarity
> drwxr-xr-x 2 root root 0 Oct 9 09:07 power
> lrwxrwxrwx 1 root root 0 Oct 9 09:07 subsystem -> ../../../../../../../../class/pwm
> -rw-r--r-- 1 root root 4096 Oct 9 08:42 uevent
> --w------- 1 root root 4096 Oct 9 09:07 unexport
> root@sama5d2-xplained:/sys/class/pwm/pwmchip0/pwm2# cat mode
> normal complementary [push-pull]
>
> The mode enclosed in bracket is the currently active mode.
>
> The mode could be set, via sysfs, by writing to mode file one of the modes
> displayed at read:
> root@sama5d2-xplained:/sys/class/pwm/pwmchip0/pwm2# echo normal > mode
> root@sama5d2-xplained:/sys/class/pwm/pwmchip0/pwm2# cat mode
> [normal] complementary push-pull
>
> The PWM push-pull mode could be usefull in applications like half bridge
> converters.
>
> This series also add PWM modes support for Atmel/Microchip SoCs.
>
> Thank you,
> Claudiu Beznea
>
> [1] https://www.spinics.net/lists/arm-kernel/msg580275.html
> [2] https://lkml.org/lkml/2018/1/12/359
>
> Changes in v5:
> - solved kbuild errors by removing dummy functions for case where
> CONFIG_PWM is not defined; adopted this approach since the removed
> function are used only only when CONFIG_PWM is defined (in PWM core
> and few drivers from drivers/pwm/ directory)
>
> Changes in v4:
> - removed changes related to pwm_config() as per maintainer proposals
> - added pwm_mode_get_valid() to retrieve a valid PWM mode fror PWM device
> instead of using BIT(ffs(caps.mode) - 1) and changed drivers to use
> pwm_mode_get_valid() instead of pwm_get_caps() + BIT(ffs(caps.mode) - 1)
> (patches 2, 3, 4 from this series)
> - renamed PWM_MODE() macro in PWMC_MODE() to avoid conflicts with
> pwm-sun4i.c driver ('C' stands for capability)
> - removed pwm_caps_valid() function
> - renamed PWM_DTMODE_COMPLEMENTARY and PWM_DTMODE_PUSH_PULL macros in
> PWM_MODE_COMPLEMENTARY and PWM_MODE_PUSH_PULL
>
> Changes in v3:
> - removed changes related to only one of_xlate function for all PWM drivers
> - switch to PWM capabilities per PWM channel nor per PWM chip
> - squash documentation and bindings patches as requeted by reviewer
> - introduced PWM_MODE(name) macro and used a bit enum for pwm modes
> - related to DT bindings, used flags cell also for PWM modes
> - updated of_xlate specific functions with "state->mode = mode;"
> instructions to avoid pwm_apply_state() failures
> - use available modes for PWM channel in pwm_config() by first calling
> pwm_get_caps() to get caps.modes
> - use loops through available modes in mode_store()/mode_show() and also in
> of_pwm_xlate_with_flags() instead of "if else" instructions; in this way,
> the addition of a new mode is independent of this code sections
> - use DTLI=1, DTHI=0 register settings to obtain push-pull mode waveforms
> for Atmel/Microchip PWM controller.
>
> Changes in v2:
> - remove of_xlate and of_pwm_n_cells and use generic functions to pharse DT
> inputs; this is done in patches 1, 2, 3, 4, 5, 6, 7 of this series; this will
> make easy the addition of PWM mode support from DT
> - add PWM mode normal
> - register PWM modes as capabilities of PWM chips at driver probe and, in case
> driver doesn't provide these capabilities use default ones
> - change the way PWM mode is pharsed via DT by using a new input for pwms
> binding property
>
>
> Claudiu Beznea (9):
> pwm: extend PWM framework with PWM modes
> pwm: clps711x: populate PWM mode in of_xlate function
> pwm: cros-ec: populate PWM mode in of_xlate function
> pwm: pxa: populate PWM mode in of_xlate function
> pwm: add PWM modes
> pwm: atmel: add pwm capabilities
> pwm: add push-pull mode support
> pwm: add documentation for pwm push-pull mode
> pwm: atmel: add push-pull mode support
>
> Documentation/devicetree/bindings/pwm/pwm.txt | 11 ++-
> Documentation/pwm.txt | 42 ++++++++-
> drivers/pwm/core.c | 125 +++++++++++++++++++++++++-
> drivers/pwm/pwm-atmel.c | 118 +++++++++++++++++-------
> drivers/pwm/pwm-clps711x.c | 10 ++-
> drivers/pwm/pwm-cros-ec.c | 1 +
> drivers/pwm/pwm-pxa.c | 1 +
> drivers/pwm/sysfs.c | 61 +++++++++++++
> include/dt-bindings/pwm/pwm.h | 2 +
> include/linux/pwm.h | 64 +++++++++++++
> 10 files changed, 395 insertions(+), 40 deletions(-)
>
^ permalink raw reply [flat|nested] 11+ messages in thread