From: Thierry Reding <thierry.reding@gmail.com>
To: Denis Carikli <denis@eukrea.com>
Cc: "Eric Bénard" <eric@eukrea.com>,
linux-pwm@vger.kernel.org, "Alexander Shiyan" <shc_work@mail.ru>,
"Philippe Rétornaz" <philippe.retornaz@gmail.com>,
"Samuel Ortiz" <sameo@linux.intel.com>,
"Lee Jones" <lee.jones@linaro.org>
Subject: Re: [PATCH v2] pwm: Add MC34708 PWM driver support.
Date: Fri, 27 Jun 2014 08:04:22 +0200 [thread overview]
Message-ID: <20140627060421.GC9258@ulmo> (raw)
In-Reply-To: <1403525405-5336-1-git-send-email-denis@eukrea.com>
[-- Attachment #1: Type: text/plain, Size: 13034 bytes --]
On Mon, Jun 23, 2014 at 02:10:05PM +0200, Denis Carikli wrote:
> Signed-off-by: Denis Carikli <denis@eukrea.com>
> ---
> Changelog v1->v2:
>
> - The driver now uses retrives mc13xxx without having to export it
> trough a globally exported function.
> - The .enable() and .disable() are now handled.
> - The registers calculations have been reworked.
> - Defines have been reworked to be more readable.
> - The driver only supports the mc34708, so now we don't claim to support
> other devices anymore, and the prefix has been changed from mc13xxx
> to mc34708. The documentation was also updated to reflect that.
> - Spelling errors have been fixed.
> - There is now less code thanks to the use of mc13xxx_reg_rmw and
> range checking functions.
> - Many other cosmetics fixes and code cleanups.
> ---
> Documentation/devicetree/bindings/mfd/mc13xxx.txt | 3 +
> drivers/mfd/mc13xxx-core.c | 16 ++
> drivers/pwm/Kconfig | 6 +
> drivers/pwm/Makefile | 1 +
> drivers/pwm/pwm-mc34708.c | 224 +++++++++++++++++++++
> 5 files changed, 250 insertions(+)
> create mode 100644 drivers/pwm/pwm-mc34708.c
>
> diff --git a/Documentation/devicetree/bindings/mfd/mc13xxx.txt b/Documentation/devicetree/bindings/mfd/mc13xxx.txt
> index 8aba488..464a663 100644
> --- a/Documentation/devicetree/bindings/mfd/mc13xxx.txt
> +++ b/Documentation/devicetree/bindings/mfd/mc13xxx.txt
> @@ -22,6 +22,9 @@ Sub-nodes:
> Each led node should contain "reg", which used as LED ID (described below).
> Optional properties "label" and "linux,default-trigger" is described in
> Documentation/devicetree/bindings/leds/common.txt.
> +- pwm: For MC34708, contain the PWM controller:
> + - compatible: must be "fsl,mc34708-pwm".
Shouldn't this be "MC34708" and "fsl,mc134708-pwm"?
> + - #pwm-cells: must be 2.
> - regulators : Contain the regulator nodes. The regulators are bound using
> their names as listed below with their registers and bits for enabling.
>
> diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
> index acf5dd7..71b7d84c 100644
> --- a/drivers/mfd/mc13xxx-core.c
> +++ b/drivers/mfd/mc13xxx-core.c
> @@ -599,6 +599,20 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
> if (!cell.name)
> return -ENOMEM;
>
> + /* mfd_add_devices won't adds a .of_node to the child's dev if the
"won't add"
> + * cell's .off_compatible is NULL, which result in
".of_compatible", "results in"
> + * of_node_to_pwmchip beeing unable to find the pwm device.
"being", "PWM device"
Also I would prefer to remove the reference to of_node_to_pwmchip in the
above description. It's a non-exported API and if it were to be renamed
this comment would likely become stale.
Perhaps: "... results in the PWM core being unable to find the PWM
device."?
> + */
> + if (!strncmp(format, "%s-pwm", sizeof("%s-pwm"))) {
Why special-case the PWM subdevice? Doesn't the comment really apply to
all of the subdevices? Even if the subsystems don't rely on the OF node
I think it would still be useful to set it up properly.
> + if (snprintf(buf, sizeof(buf),
> + "fsl,%s", cell.name) > sizeof(buf))
> + return -E2BIG;
> +
> + cell.of_compatible = kmemdup(buf, strlen(buf) + 1, GFP_KERNEL);
> + if (!cell.of_compatible)
> + return -ENOMEM;
Can't the above simply be:
cell.of_compatible = kasprintf(GFP_KERNEL, "fsl,%s", cell.name);
?
> + }
> +
> return mfd_add_devices(mc13xxx->dev, -1, &cell, 1, NULL, 0, NULL);
> }
>
> @@ -681,6 +695,7 @@ int mc13xxx_common_init(struct device *dev)
> &pdata->regulators, sizeof(pdata->regulators));
> mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led",
> pdata->leds, sizeof(*pdata->leds));
> + mc13xxx_add_subdevice(mc13xxx, "%s-pwm");
> mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton",
> pdata->buttons, sizeof(*pdata->buttons));
> if (mc13xxx->flags & MC13XXX_USE_CODEC)
> @@ -692,6 +707,7 @@ int mc13xxx_common_init(struct device *dev)
> } else {
> mc13xxx_add_subdevice(mc13xxx, "%s-regulator");
> mc13xxx_add_subdevice(mc13xxx, "%s-led");
> + mc13xxx_add_subdevice(mc13xxx, "%s-pwm");
> mc13xxx_add_subdevice(mc13xxx, "%s-pwrbutton");
> if (mc13xxx->flags & MC13XXX_USE_CODEC)
> mc13xxx_add_subdevice(mc13xxx, "%s-codec");
All of the above should be a separate patch that can be applied to the
MFD tree.
> diff --git a/drivers/pwm/pwm-mc34708.c b/drivers/pwm/pwm-mc34708.c
[...]
> +/*
> + * Copyright 2014 Eukréa Electromatique <denis@eukrea.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + * 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.
> + */
> +
> +
One blank line is enough.
> +#include <linux/err.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +#include <linux/slab.h>
> +
> +#include <linux/mfd/mc13783.h>
> +
> +/* PWM register address */
> +#define MC134708_PWM 0x37
> +
> +/* PWM register fields:
> + * Bit # RegisterName Description
> + * [0->5] PWM1DUTY: PWM1 duty cycle
> + * [6->11] PWM1CLKDIV: PWM1 duty cycle
> + * [12->17] PWM2DUTY: PWM2 clock divide setting
> + * [18->23] PWM2CLKDIV: PWM2 clock divide setting
> + */
Block comments should be of this format:
/*
* ...
*/
> +#define MC134708_PWM_MASK 0x3f
> +#define MC134708_PWM_NUM_OFFSET 0x0c
> +
> +#define MC134708_PWM_DUTY_OFFSET(pwm_id) (pwm_id * MC134708_PWM_NUM_OFFSET)
> +#define MC134708_PWM_PERIOD_OFFSET(pwm_id) ((pwm_id * MC134708_PWM_NUM_OFFSET) + 0x06)
You'll need to wrap pwm_id in parentheses to make sure the expansion
does what it's supposed to.
> +
> +/* MC34708 PWM Constraints */
> +#define MC13708_BASE_CLK_FREQ 2000000
Is this really a fixed constant or is there a struct clk that can be
used to obtain this at runtime?
> +#define MC13708_PWM_MAX_DUTY 32
> +#define MC13708_PWM_MAX_CLKDIV 64
> +
> +#define MC13708_MIN_PWM_PERIOD (NSEC_PER_SEC / MC13708_BASE_CLK_FREQ)
> +#define MC13708_MAX_PWM_PERIOD (MC13708_MIN_PWM_PERIOD * MC13708_PWM_MAX_CLKDIV)
> +
> +#define MC134708_PWMS_NUM 2
This is really only needed in one place (see other comments later), so
you can use the literal when assigning it to pwm_chip's .npwm field.
> +
> +struct mc34708_pwm_regs {
_regs isn't really suitable here. These aren't really registers or
register contents.
There's also the mc34708 vs. mc134708 inconsistency here.
> + int enabled;
bool
> + int pwm_duty;
unsigned
> +};
> +
> +struct mc34708_pwm_chip {
> + struct pwm_chip pwm_chip;
> + struct mc13xxx *mc13xxx;
> + struct mc34708_pwm_regs *pwms[MC134708_PWMS_NUM];
You don't need this. Please use the PWM subsystem's pwm_set_chip_data()
to set this data for each PWM device. You can look at the pwm-samsung,
pwm-renesas-tpu, pwm-lp3943, pwm-bfin or pwm-atmel-tcb drivers for
reference.
> +};
> +
> +static inline
> +struct mc34708_pwm_chip *to_mc34708_chip(struct pwm_chip *chip)
There's no need to wrap this, it fits on a single line just fine.
> +{
> + return container_of(chip, struct mc34708_pwm_chip, pwm_chip);
> +}
> +
> +static int
> +pwm_mc34708_config(struct pwm_chip *chip, struct pwm_device *pwm,
> + int duty_ns, int period_ns)
Similarly here. This should be:
static int pwm_mc34708_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
> +{
> + struct mc34708_pwm_chip *mc34708_chip = to_mc34708_chip(chip);
> + struct mc34708_pwm_regs *pwmr = mc34708_chip->pwms[pwm->hwpwm];
If you properly set up chip data, then this becomes:
struct mc34708_pwm_regs *pwmr = pwm_get_chip_data(pwm);
> + struct mc13xxx *mc13xxx = mc34708_chip->mc13xxx;
> +
> + int duty_offset = MC134708_PWM_DUTY_OFFSET(pwm->hwpwm);
> + int period_offset = MC134708_PWM_PERIOD_OFFSET(pwm->hwpwm);
These should be unsigned.
> +
> + int pwm_clkdiv, pwm_duty, ret = 0;
The first two of these can also be unsigned. And there's no need for the
blank lines above.
> +
> + /* Period */
> + period_ns = clamp(period_ns, (int)MC13708_MIN_PWM_PERIOD,
> + (int)MC13708_MAX_PWM_PERIOD);
Rather than clamping the value here, shouldn't this be considered an
error?
> + pwm_clkdiv = DIV_ROUND_UP(NSEC_PER_SEC, period_ns); /* Frequency (Hz) */
> + pwm_clkdiv = DIV_ROUND_UP(MC13708_BASE_CLK_FREQ,
> + pwm_clkdiv) - 1; /* Clock divisor */
> +
> + /* Duty cycle */
> + pwm_duty = DIV_ROUND_UP(MC13708_PWM_MAX_DUTY * duty_ns, period_ns);
> +
> + /* Actual write to the registers */
> + mc13xxx_lock(mc13xxx);
> +
> + ret = mc13xxx_reg_rmw(mc13xxx, MC134708_PWM,
> + MC134708_PWM_MASK << period_offset,
> + pwm_clkdiv << period_offset);
For readability I'd prefer this to be broken out, somewhat like this:
mask = MC134708_PWM_MASK << period_offset;
value = pwm_clkdiv << period_offset;
ret = mc13xxx_reg_rmw(mc13xx, MC134708_PWM, mask, value);
...
> + if (ret) {
> + mc13xxx_unlock(mc13xxx);
> + return ret;
> + }
> +
> + /* The MC34708 doesn't have an enable bit for its PWM unit,
> + * so we cache the pwm duty value for the .enable()
> + */
> + pwmr->pwm_duty = pwm_duty;
> +
> + if (pwmr->enabled) {
> + ret = mc13xxx_reg_rmw(mc13xxx, MC134708_PWM,
> + MC134708_PWM_MASK << duty_offset,
> + pwm_duty << duty_offset);
Similarily here.
> + }
> + mc13xxx_unlock(mc13xxx);
There should be a blank line between the above two.
> +
> + return ret;
> +}
> +
> +static int pwm_mc34708_enable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + struct mc34708_pwm_chip *mc34708_chip = to_mc34708_chip(chip);
> + struct mc34708_pwm_regs *pwmr = mc34708_chip->pwms[pwm->hwpwm];
> + struct mc13xxx *mc13xxx = mc34708_chip->mc13xxx;
> + int duty_offset = MC134708_PWM_DUTY_OFFSET(pwm->hwpwm);
unsigned. And perhaps since there's no period being written here this
could be simply "offset"?
> + int ret;
> +
> + mc13xxx_lock(mc13xxx);
> +
> + ret = mc13xxx_reg_rmw(mc13xxx, MC134708_PWM,
> + MC134708_PWM_MASK << duty_offset,
> + pwmr->pwm_duty << duty_offset);
Similarly this could be broken out for readability.
> + pwmr->enabled = 1;
> +
> + mc13xxx_unlock(mc13xxx);
> +
> + return ret;
> +}
> +
> +static void pwm_mc34708_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + struct mc34708_pwm_chip *mc34708_chip = to_mc34708_chip(chip);
> + struct mc34708_pwm_regs *pwmr = mc34708_chip->pwms[pwm->hwpwm];
> + struct mc13xxx *mc13xxx = mc34708_chip->mc13xxx;
> + int duty_offset = MC134708_PWM_DUTY_OFFSET(pwm->hwpwm);
> +
> + mc13xxx_lock(mc13xxx);
> +
> + /* To disable the PWM, the duty cycle bits have to be cleared */
> + mc13xxx_reg_rmw(mc13xxx, MC134708_PWM,
> + MC134708_PWM_MASK << duty_offset,
> + 0 << duty_offset);
> + pwmr->enabled = 0;
> +
> + mc13xxx_unlock(mc13xxx);
> +}
Same comments as for .enable()
> +static const struct pwm_ops pwm_mc34708_ops = {
> + .enable = pwm_mc34708_enable,
> + .disable = pwm_mc34708_disable,
> + .config = pwm_mc34708_config,
> + .owner = THIS_MODULE,
> +};
Please don't use artificial padding here. Single spaces around '=' will
do just fine.
> +static int pwm_mc34708_probe(struct platform_device *pdev)
> +{
> + struct mc34708_pwm_chip *chip;
> + struct mc13xxx *mc13xxx;
> + int err, i;
> +
> + mc13xxx = dev_get_drvdata(pdev->dev.parent);
You could assign this when initializing to save a few lines.
> + if (!mc13xxx)
> + return -EINVAL;
Can this really ever happen?
> + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
> + if (!chip)
> + return -ENOMEM;
There's an extra tab here.
> +
> + for (i = 0; i < MC134708_PWMS_NUM; i++) {
> + chip->pwms[i] = devm_kzalloc(&pdev->dev,
> + sizeof(struct mc34708_pwm_regs), GFP_KERNEL);
> + }
When you switch to pwm_{set,get}_chip_data() the typical way to allocate
this is in a separate .request() function (and free it in .free()).
> +MODULE_ALIAS("platform:mc34708-pwm");
> +MODULE_AUTHOR("Denis Carikli <denis@eukrea.com>");
> +MODULE_DESCRIPTION("mc34708 Pulse Width Modulation Driver");
This could probably mention Freescale as the vendor? Or perhaps for
consistency with other MFD subdrivers:
"PWM Driver for Freescale MC134708 PMIC"
?
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]
prev parent reply other threads:[~2014-06-27 6:04 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-06-23 12:10 [PATCH v2] pwm: Add MC34708 PWM driver support Denis Carikli
2014-06-23 12:23 ` Alexander Shiyan
2014-06-27 6:04 ` Thierry Reding [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20140627060421.GC9258@ulmo \
--to=thierry.reding@gmail.com \
--cc=denis@eukrea.com \
--cc=eric@eukrea.com \
--cc=lee.jones@linaro.org \
--cc=linux-pwm@vger.kernel.org \
--cc=philippe.retornaz@gmail.com \
--cc=sameo@linux.intel.com \
--cc=shc_work@mail.ru \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).