linux-pwm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
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 --]

      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).