* [PATCH v7 1/6] pwm: extend PWM framework with PWM modes
2018-12-17 14:13 [PATCH v7 0/6] extend PWM framework to support PWM modes Claudiu.Beznea
@ 2018-12-17 14:13 ` Claudiu.Beznea
2018-12-17 14:13 ` [PATCH v7 2/6] pwm: add " Claudiu.Beznea
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Claudiu.Beznea @ 2018-12-17 14:13 UTC (permalink / raw)
To: thierry.reding, robh, corbet, Nicolas.Ferre, alexandre.belloni,
Ludovic.Desroches
Cc: linux-pwm, linux-doc, linux-kernel, linux-arm-kernel, Claudiu.Beznea
From: Claudiu Beznea <claudiu.beznea@microchip.com>
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). 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. Only modes supported by PWM channel
could be set.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
drivers/pwm/core.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
drivers/pwm/sysfs.c | 61 +++++++++++++++++++++++++++++++++++
include/linux/pwm.h | 35 +++++++++++++++++++++
3 files changed, 186 insertions(+), 1 deletion(-)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 1581f6ab1b1f..eb444ee8d486 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -249,6 +249,88 @@ static bool pwm_ops_check(const struct pwm_ops *ops)
return false;
}
+static int pwm_get_default_caps(struct pwm_caps *caps)
+{
+ static const struct pwm_caps default_caps = {
+ .modes_msk = PWM_MODE_BIT(NORMAL),
+ };
+
+ if (!caps)
+ return -EINVAL;
+
+ *caps = default_caps;
+
+ return 0;
+}
+
+/**
+ * pwm_get_caps() - get PWM capabilities of a PWM device
+ * @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(const struct pwm_device *pwm, struct pwm_caps *caps)
+{
+ if (!pwm || !caps)
+ return -EINVAL;
+
+ if (pwm->chip->ops->get_caps)
+ return pwm->chip->ops->get_caps(pwm->chip, pwm, caps);
+
+ return pwm_get_default_caps(caps);
+}
+EXPORT_SYMBOL_GPL(pwm_get_caps);
+
+/**
+ * pwm_get_default_modebit() - get the default mode for PWM (as a bit mask)
+ * @pwm: PWM device to get the default mode for
+ *
+ * Returns: the default PWM mode (as a bit mask) for PWM device
+ */
+unsigned long pwm_get_default_modebit(const struct pwm_device *pwm)
+{
+ struct pwm_caps caps;
+
+ if (pwm_get_caps(pwm, &caps))
+ return PWM_MODE_BIT(NORMAL);
+
+ return BIT(ffs(caps.modes_msk) - 1);
+}
+EXPORT_SYMBOL_GPL(pwm_get_default_modebit);
+
+/**
+ * pwm_supports_mode() - check if PWM mode is supported by PWM device
+ * @pwm: PWM device
+ * @modebit: PWM mode bit mask to be checked (see PWM_MODE_BIT())
+ *
+ * Returns: true if PWM mode is supported, false otherwise
+ */
+bool pwm_supports_mode(const struct pwm_device *pwm, unsigned long modebit)
+{
+ struct pwm_caps caps;
+
+ if (!pwm || !modebit)
+ return false;
+
+ if (hweight_long(modebit) != 1 || ffs(modebit) - 1 >= PWM_MODE_CNT)
+ return false;
+
+ if (pwm_get_caps(pwm, &caps))
+ return false;
+
+ return !!(caps.modes_msk & modebit);
+}
+EXPORT_SYMBOL_GPL(pwm_supports_mode);
+
+const char *pwm_get_mode_name(unsigned long modebit)
+{
+ if (modebit == PWM_MODE_BIT(COMPLEMENTARY))
+ return "complementary";
+
+ return "normal";
+}
+
/**
* pwmchip_add_with_polarity() - register a new PWM chip
* @chip: the PWM chip to add
@@ -294,6 +376,7 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
pwm->pwm = chip->base + i;
pwm->hwpwm = i;
pwm->state.polarity = polarity;
+ pwm->state.modebit = pwm_get_default_modebit(pwm);
if (chip->ops->get_state)
chip->ops->get_state(chip, pwm, &pwm->state);
@@ -469,7 +552,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_supports_mode(pwm, state->modebit))
return -EINVAL;
if (!memcmp(state, &pwm->state, sizeof(*state)))
@@ -530,6 +614,8 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
pwm->state.enabled = state->enabled;
}
+
+ pwm->state.modebit = state->modebit;
}
return 0;
@@ -579,6 +665,8 @@ int pwm_adjust_config(struct pwm_device *pwm)
pwm_get_args(pwm, &pargs);
pwm_get_state(pwm, &state);
+ state.modebit = pwm_get_default_modebit(pwm);
+
/*
* 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
@@ -999,6 +1087,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_get_mode_name(state.modebit));
seq_puts(s, "\n");
}
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index ceb233dd6048..7865fbafbeb4 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 modebit;
+ enum pwm_mode mode;
+ int len = 0;
+
+ pwm_get_state(pwm, &state);
+
+ for (mode = PWM_MODE_NORMAL; mode < PWM_MODE_CNT; mode++) {
+ modebit = BIT(mode);
+ if (pwm_supports_mode(pwm, modebit)) {
+ if (state.modebit == modebit)
+ len += scnprintf(buf + len,
+ PAGE_SIZE - len, "[%s] ",
+ pwm_get_mode_name(modebit));
+ else
+ len += scnprintf(buf + len,
+ PAGE_SIZE - len, "%s ",
+ pwm_get_mode_name(modebit));
+ }
+ }
+
+ 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 modebit;
+ enum pwm_mode mode;
+ int ret;
+
+ for (mode = PWM_MODE_NORMAL; mode < PWM_MODE_CNT; mode++) {
+ modebit = BIT(mode);
+ if (sysfs_streq(buf, pwm_get_mode_name(modebit)))
+ break;
+ }
+
+ if (mode == PWM_MODE_CNT)
+ return -EINVAL;
+
+ mutex_lock(&export->lock);
+ pwm_get_state(pwm, &state);
+ state.modebit = modebit;
+ 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..abe189d891af 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -10,6 +10,9 @@ struct pwm_capture;
struct seq_file;
struct pwm_chip;
+struct pwm_device;
+
+unsigned long pwm_get_default_modebit(const struct pwm_device *pwm);
/**
* enum pwm_polarity - polarity of a PWM signal
@@ -26,6 +29,28 @@ enum pwm_polarity {
};
/**
+ * PWM modes capabilities
+ * @PWM_MODE_NORMAL: PWM has one output
+ * @PWM_MODE_COMPLEMENTARY: PWM has 2 outputs with opposite polarities
+ * @PWM_MODE_CNT: PWM modes count
+ */
+enum pwm_mode {
+ PWM_MODE_NORMAL,
+ PWM_MODE_COMPLEMENTARY,
+ PWM_MODE_CNT,
+};
+
+#define PWM_MODE_BIT(name) BIT(PWM_MODE_##name)
+
+/**
+ * struct pwm_caps - PWM capabilities
+ * @modes_msk: bitmask of supported modes (see PWM_MODE_*)
+ */
+struct pwm_caps {
+ unsigned long modes_msk;
+};
+
+/**
* struct pwm_args - board-dependent PWM arguments
* @period: reference period
* @polarity: reference polarity
@@ -53,12 +78,14 @@ enum {
* @period: PWM period (in nanoseconds)
* @duty_cycle: PWM duty cycle (in nanoseconds)
* @polarity: PWM polarity
+ * @modebit: PWM mode bit
* @enabled: PWM enabled status
*/
struct pwm_state {
unsigned int period;
unsigned int duty_cycle;
enum pwm_polarity polarity;
+ unsigned long modebit;
bool enabled;
};
@@ -181,6 +208,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->modebit = pwm_get_default_modebit(pwm);
}
/**
@@ -254,6 +282,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 +301,8 @@ struct pwm_ops {
struct pwm_state *state);
void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state);
+ int (*get_caps)(const struct pwm_chip *chip,
+ const struct pwm_device *pwm, struct pwm_caps *caps);
#ifdef CONFIG_DEBUG_FS
void (*dbg_show)(struct pwm_chip *chip, struct seq_file *s);
#endif
@@ -438,6 +469,9 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
unsigned int index,
const char *label);
+int pwm_get_caps(const struct pwm_device *pwm, struct pwm_caps *caps);
+bool pwm_supports_mode(const struct pwm_device *pwm, unsigned long modebit);
+const char *pwm_get_mode_name(unsigned long modebit);
struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc,
const struct of_phandle_args *args);
@@ -592,6 +626,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.modebit = pwm_get_default_modebit(pwm);
pwm_apply_state(pwm, &state);
}
--
2.7.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v7 2/6] pwm: add PWM modes
2018-12-17 14:13 [PATCH v7 0/6] extend PWM framework to support PWM modes Claudiu.Beznea
2018-12-17 14:13 ` [PATCH v7 1/6] pwm: extend PWM framework with " Claudiu.Beznea
@ 2018-12-17 14:13 ` Claudiu.Beznea
2018-12-17 14:13 ` [PATCH v7 3/6] pwm: atmel: add pwm capabilities Claudiu.Beznea
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Claudiu.Beznea @ 2018-12-17 14:13 UTC (permalink / raw)
To: thierry.reding, robh, corbet, Nicolas.Ferre, alexandre.belloni,
Ludovic.Desroches
Cc: linux-pwm, linux-doc, linux-kernel, linux-arm-kernel, Claudiu.Beznea
From: Claudiu Beznea <claudiu.beznea@microchip.com>
Add PWM normal and complementary modes.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
Hi Rob,
In prevous version I had your Acked-by tag on this patch but I removed it
in this version since there are some changes b/w the previous one and this
one. Please see bellow and let me know if you are ok with this.
Thank you,
Claudiu Beznea
Changes in v6:
- remove DT bindings since they were also removed from code
- use 1/3 duty factor ratio
- use '^' symbols to emphasize the beginning of a new period
- use PWMx_y to refrer to output y of PWM x
Documentation/pwm.txt | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
index 8fbf0aa3ba2d..669fd4c9cc8e 100644
--- a/Documentation/pwm.txt
+++ b/Documentation/pwm.txt
@@ -110,6 +110,28 @@ channel that was exported. The following properties will then be available:
- 0 - disabled
- 1 - enabled
+ mode
+ Get/set PWM channel working mode.
+
+ In the following description PWMx_y refers to output y of PWM with ID x.
+
+ Normal mode - for PWM channels with one output; this should be the
+ default working mode for every PWM channel; output waveforms looks
+ like this:
+ __ __ __ __
+ PWMx __| |____| |____| |____| |__
+ ^ ^ ^ ^
+
+ Complementary mode - for PWM channels with two outputs; output waveforms
+ looks line this:
+ __ __ __ __
+ PWMx_0 __| |____| |____| |____| |__
+ __ ____ ____ ____ __
+ PWMx_1 |__| |__| |__| |__|
+ ^ ^ ^ ^
+
+ Where '^' specifies the beginning of a period.
+
Implementing a PWM driver
-------------------------
--
2.7.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v7 3/6] pwm: atmel: add pwm capabilities
2018-12-17 14:13 [PATCH v7 0/6] extend PWM framework to support PWM modes Claudiu.Beznea
2018-12-17 14:13 ` [PATCH v7 1/6] pwm: extend PWM framework with " Claudiu.Beznea
2018-12-17 14:13 ` [PATCH v7 2/6] pwm: add " Claudiu.Beznea
@ 2018-12-17 14:13 ` Claudiu.Beznea
2018-12-17 14:13 ` [PATCH v7 4/6] pwm: add push-pull mode support Claudiu.Beznea
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Claudiu.Beznea @ 2018-12-17 14:13 UTC (permalink / raw)
To: thierry.reding, robh, corbet, Nicolas.Ferre, alexandre.belloni,
Ludovic.Desroches
Cc: linux-pwm, linux-doc, linux-kernel, linux-arm-kernel, Claudiu.Beznea
From: Claudiu Beznea <claudiu.beznea@microchip.com>
Add pwm capabilities for Atmel/Microchip PWM controllers.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
drivers/pwm/pwm-atmel.c | 86 ++++++++++++++++++++++++++++++++-----------------
1 file changed, 57 insertions(+), 29 deletions(-)
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index 530d7dc5f1b5..b72cacd5dca3 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -65,18 +65,23 @@ 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 */
struct mutex isr_lock;
};
-static inline struct atmel_pwm_chip *to_atmel_pwm_chip(struct pwm_chip *chip)
+static inline struct atmel_pwm_chip *to_atmel_pwm_chip(const struct pwm_chip *chip)
{
return container_of(chip, struct atmel_pwm_chip, chip);
}
@@ -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,55 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
return 0;
}
+static int atmel_pwm_get_caps(const struct pwm_chip *chip,
+ const struct pwm_device *pwm,
+ struct pwm_caps *caps)
+{
+ struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+
+ *caps = atmel_pwm->data->caps;
+
+ return 0;
+}
+
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_msk = PWM_MODE_BIT(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_msk = PWM_MODE_BIT(NORMAL) |
+ PWM_MODE_BIT(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 +335,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 +358,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 +401,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] 7+ messages in thread
* [PATCH v7 4/6] pwm: add push-pull mode support
2018-12-17 14:13 [PATCH v7 0/6] extend PWM framework to support PWM modes Claudiu.Beznea
` (2 preceding siblings ...)
2018-12-17 14:13 ` [PATCH v7 3/6] pwm: atmel: add pwm capabilities Claudiu.Beznea
@ 2018-12-17 14:13 ` Claudiu.Beznea
2018-12-17 14:13 ` [PATCH v7 5/6] pwm: add documentation for pwm push-pull mode Claudiu.Beznea
2018-12-17 14:13 ` [PATCH v7 6/6] pwm: atmel: add push-pull mode support Claudiu.Beznea
5 siblings, 0 replies; 7+ messages in thread
From: Claudiu.Beznea @ 2018-12-17 14:13 UTC (permalink / raw)
To: thierry.reding, robh, corbet, Nicolas.Ferre, alexandre.belloni,
Ludovic.Desroches
Cc: linux-pwm, linux-doc, linux-kernel, linux-arm-kernel, Claudiu.Beznea
From: Claudiu Beznea <claudiu.beznea@microchip.com>
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 | 3 +++
include/linux/pwm.h | 3 +++
2 files changed, 6 insertions(+)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index eb444ee8d486..f182d1eb564e 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -328,6 +328,9 @@ const char *pwm_get_mode_name(unsigned long modebit)
if (modebit == PWM_MODE_BIT(COMPLEMENTARY))
return "complementary";
+ if (modebit == PWM_MODE_BIT(PUSH_PULL))
+ return "push-pull";
+
return "normal";
}
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index abe189d891af..cce29733d44e 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -32,11 +32,14 @@ enum pwm_polarity {
* PWM modes capabilities
* @PWM_MODE_NORMAL: PWM has one output
* @PWM_MODE_COMPLEMENTARY: PWM has 2 outputs with opposite polarities
+ * @PWM_MODE_PUSH_PULL: PWM has 2 outputs with same polarities and the edges
+ * are complementary delayed for one period
* @PWM_MODE_CNT: PWM modes count
*/
enum pwm_mode {
PWM_MODE_NORMAL,
PWM_MODE_COMPLEMENTARY,
+ PWM_MODE_PUSH_PULL,
PWM_MODE_CNT,
};
--
2.7.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v7 5/6] pwm: add documentation for pwm push-pull mode
2018-12-17 14:13 [PATCH v7 0/6] extend PWM framework to support PWM modes Claudiu.Beznea
` (3 preceding siblings ...)
2018-12-17 14:13 ` [PATCH v7 4/6] pwm: add push-pull mode support Claudiu.Beznea
@ 2018-12-17 14:13 ` Claudiu.Beznea
2018-12-17 14:13 ` [PATCH v7 6/6] pwm: atmel: add push-pull mode support Claudiu.Beznea
5 siblings, 0 replies; 7+ messages in thread
From: Claudiu.Beznea @ 2018-12-17 14:13 UTC (permalink / raw)
To: thierry.reding, robh, corbet, Nicolas.Ferre, alexandre.belloni,
Ludovic.Desroches
Cc: linux-pwm, linux-doc, linux-kernel, linux-arm-kernel, Claudiu.Beznea
From: Claudiu Beznea <claudiu.beznea@microchip.com>
Add documentation for PWM push-pull mode.
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
Hi Rob,
In prevous version I had your Reviewed-by tag on this patch but I removed it
in this version since there are some changes b/w the previous one and this
one. Please see bellow and let me know if you are ok with this.
Thank you,
Claudiu Beznea
Changes in v6:
- remove bindings part
- use 1/3 duty factor ratio
- use '^' symbols to emphasize the beginning of a new period
- use PWMx_y to refrer to output y of PWM x
Documentation/pwm.txt | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
index 669fd4c9cc8e..80552627627d 100644
--- a/Documentation/pwm.txt
+++ b/Documentation/pwm.txt
@@ -130,6 +130,22 @@ channel that was exported. The following properties will then be available:
PWMx_1 |__| |__| |__| |__|
^ ^ ^ ^
+ 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:
+ __ __
+ PWMx_0 __| |____________| |__________
+ __ __
+ PWMx_1 __________| |____________| |__
+ ^ ^ ^ ^
+
+ If polarity is inversed:
+ __ ____________ __________
+ PWMx_0 |__| |__|
+ __________ ____________ __
+ PWMx_1 |__| |__|
+ ^ ^ ^ ^
+
Where '^' specifies the beginning of a period.
Implementing a PWM driver
--
2.7.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v7 6/6] pwm: atmel: add push-pull mode support
2018-12-17 14:13 [PATCH v7 0/6] extend PWM framework to support PWM modes Claudiu.Beznea
` (4 preceding siblings ...)
2018-12-17 14:13 ` [PATCH v7 5/6] pwm: add documentation for pwm push-pull mode Claudiu.Beznea
@ 2018-12-17 14:13 ` Claudiu.Beznea
5 siblings, 0 replies; 7+ messages in thread
From: Claudiu.Beznea @ 2018-12-17 14:13 UTC (permalink / raw)
To: thierry.reding, robh, corbet, Nicolas.Ferre, alexandre.belloni,
Ludovic.Desroches
Cc: linux-pwm, linux-doc, linux-kernel, linux-arm-kernel, Claudiu.Beznea
From: Claudiu Beznea <claudiu.beznea@microchip.com>
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 b72cacd5dca3..650d475a4c34 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(pwm, &caps);
if (state->enabled) {
if (cstate.enabled &&
cstate.polarity == state->polarity &&
- cstate.period == state->period) {
+ cstate.period == state->period &&
+ cstate.modebit == state->modebit) {
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_msk & PWM_MODE_BIT(PUSH_PULL)) {
+ if (state->modebit == PWM_MODE_BIT(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);
@@ -319,6 +337,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_msk = PWM_MODE_BIT(NORMAL) |
+ PWM_MODE_BIT(COMPLEMENTARY) |
+ PWM_MODE_BIT(PUSH_PULL),
+ },
+};
+
static const struct platform_device_id atmel_pwm_devtypes[] = {
{
.name = "at91sam9rl-pwm",
@@ -341,7 +373,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] 7+ messages in thread