From: Thierry Reding <thierry.reding@gmail.com>
To: Boris Brezillon <boris.brezillon@free-electrons.com>
Cc: Mark Rutland <mark.rutland@arm.com>,
linux-pwm@vger.kernel.org, devicetree@vger.kernel.org,
Samuel Ortiz <sameo@linux.intel.com>,
Pawel Moll <pawel.moll@arm.com>,
Ian Campbell <ijc+devicetree@hellion.org.uk>,
Lee Jones <lee.jones@linaro.org>,
Nicolas Ferre <nicolas.ferre@atmel.com>,
linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org,
Rob Herring <robh+dt@kernel.org>,
Alexandre Belloni <alexandre.belloni@free-electrons.com>,
Kumar Gala <galak@codeaurora.org>,
Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>,
Andrew Victor <linux@maxim.org.za>,
linux-arm-kernel@lists.infradead.org,
Mark Yao <mark.yao@rock-chips.com>
Subject: Re: [PATCH v7 03/11] pwm: add support for atmel-hlcdc-pwm device
Date: Mon, 6 Oct 2014 12:46:35 +0200 [thread overview]
Message-ID: <20141006104634.GE25202@ulmo> (raw)
In-Reply-To: <1412175188-28278-4-git-send-email-boris.brezillon@free-electrons.com>
[-- Attachment #1.1: Type: text/plain, Size: 7864 bytes --]
On Wed, Oct 01, 2014 at 04:53:00PM +0200, Boris Brezillon wrote:
[...]
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index b800783..afb896b 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -50,6 +50,16 @@ config PWM_ATMEL
> To compile this driver as a module, choose M here: the module
> will be called pwm-atmel.
>
> +config PWM_ATMEL_HLCDC_PWM
> + tristate "Atmel HLCDC PWM support"
> + select MFD_ATMEL_HLCDC
> + depends on OF
This isn't really necessary since MFD_ATMEL_HLCDC already depends on OF.
> diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c
[...]
> new file mode 100644
> index 0000000..0238f7a
> --- /dev/null
> +++ b/drivers/pwm/pwm-atmel-hlcdc.c
> @@ -0,0 +1,229 @@
> +/*
> + * Copyright (C) 2014 Free Electrons
> + * Copyright (C) 2014 Atmel
> + *
> + * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/mfd/atmel-hlcdc.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +#include <linux/regmap.h>
> +
> +#define ATMEL_HLCDC_PWMCVAL_MASK GENMASK(15, 8)
> +#define ATMEL_HLCDC_PWMCVAL(x) ((x << 8) & ATMEL_HLCDC_PWMCVAL_MASK)
You might want to use an extra pair of parentheses around the "x" above.
> +struct atmel_hlcdc_pwm_chip {
Can we make this...
> + struct pwm_chip chip;
> + struct atmel_hlcdc *hlcdc;
> + struct clk *cur_clk;
> +};
> +
> +static inline struct atmel_hlcdc_pwm_chip *
> +pwm_chip_to_atmel_hlcdc_pwm_chip(struct pwm_chip *chip)
... and this a little shorter? There is a lot of line-wrapping below
only because this is very long. It seems like just dropping the
pwm_chip_ prefix on this function would be enough to not exceed the
78/80 character limit.
> +{
> + return container_of(chip, struct atmel_hlcdc_pwm_chip, chip);
> +}
> +
> +static int atmel_hlcdc_pwm_config(struct pwm_chip *c,
> + struct pwm_device *pwm,
> + int duty_ns, int period_ns)
> +{
> + struct atmel_hlcdc_pwm_chip *chip =
> + pwm_chip_to_atmel_hlcdc_pwm_chip(c);
> + struct atmel_hlcdc *hlcdc = chip->hlcdc;
> + struct clk *new_clk = hlcdc->slow_clk;
> + u64 pwmcval = duty_ns * 256;
> + unsigned long clk_freq;
> + u64 clk_period_ns;
> + u32 pwmcfg;
> + int pres;
> +
> + clk_freq = clk_get_rate(new_clk);
> + clk_period_ns = 1000000000;
NSEC_PER_SEC?
> + clk_period_ns *= 256;
Perhaps collapse the above two in a single line:
clk_period_ns = NSEC_PER_SEC * 256;
?
> + do_div(clk_period_ns, clk_freq);
> +
> + if (clk_period_ns > period_ns) {
> + new_clk = hlcdc->sys_clk;
> + clk_freq = clk_get_rate(new_clk);
> + clk_period_ns = 1000000000;
> + clk_period_ns *= 256;
Maybe:
clk_period_ns = NSEC_PER_SEC * 256;
?
> + do_div(clk_period_ns, clk_freq);
> + }
> +
> + for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) {
> + if ((clk_period_ns << pres) >= period_ns)
> + break;
> + }
Technically there's no need for the curly braces.
> +
> + if (pres > ATMEL_HLCDC_PWMPS_MAX)
> + return -EINVAL;
I think the condition above needs to be "pres == ATMEL_HLCDC_PWMPS_MAX",
otherwise this will never be true.
> +
> + pwmcfg = ATMEL_HLCDC_PWMPS(pres);
> +
> + if (new_clk != chip->cur_clk) {
> + u32 gencfg = 0;
> +
> + clk_prepare_enable(new_clk);
This can fail so it needs error-checking.
> + clk_disable_unprepare(chip->cur_clk);
> + chip->cur_clk = new_clk;
> +
> + if (new_clk != hlcdc->slow_clk)
> + gencfg = ATMEL_HLCDC_CLKPWMSEL;
There are lots of negations here, which caused me to think that there
was a third clock involved here, but it seems like new_clk can either be
slow_clk or sys_clk.
Perhaps making this condition "new_clk == hlcdc->sys_clk" would improve
clarity here. Maybe a comment somewhere would help?
> + regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(0),
> + ATMEL_HLCDC_CLKPWMSEL, gencfg);
> + }
> +
> + do_div(pwmcval, period_ns);
> + if (pwmcval > 255)
The PWM core already makes sure that duty_ns <= period_ns, so pwmcval
could be anywhere between 0 and 256 here. Where does the disconnect come
from? Why not make pwmcval = duty_ns * 255 if that's the maximum?
> + pwmcval = 255;
> +
> + pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval);
> +
> + regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
> + ATMEL_HLCDC_PWMCVAL_MASK | ATMEL_HLCDC_PWMPS_MASK,
> + pwmcfg);
> +
> + return 0;
> +}
> +
> +static int atmel_hlcdc_pwm_set_polarity(struct pwm_chip *c,
> + struct pwm_device *pwm,
> + enum pwm_polarity polarity)
> +{
> + struct atmel_hlcdc_pwm_chip *chip =
> + pwm_chip_to_atmel_hlcdc_pwm_chip(c);
> + struct atmel_hlcdc *hlcdc = chip->hlcdc;
> + u32 cfg = 0;
> +
> + if (polarity == PWM_POLARITY_NORMAL)
> + cfg = ATMEL_HLCDC_PWMPOL;
That's strange. Inverse polarity is the default on this hardware?
> +static int atmel_hlcdc_pwm_enable(struct pwm_chip *c,
> + struct pwm_device *pwm)
There's no need for line-wrapping here. The above fits on one line just
fine.
> +{
> + struct atmel_hlcdc_pwm_chip *chip =
> + pwm_chip_to_atmel_hlcdc_pwm_chip(c);
> + struct atmel_hlcdc *hlcdc = chip->hlcdc;
> + u32 status;
> +
> + regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PWM);
> + while (!regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status) &&
> + !(status & ATMEL_HLCDC_PWM))
> + ;
This loop isn't very readable. Can you improve it? Perhaps:
do {
err = regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status);
if (err < 0)
return err;
} while ((status & ATMEL_HLCDC_PWM) == 0);
That also allows errors to be properly propagated. Perhaps you also want
to put a usleep_range() or similar in there.
> +static void atmel_hlcdc_pwm_disable(struct pwm_chip *c,
> + struct pwm_device *pwm)
> +{
> + struct atmel_hlcdc_pwm_chip *chip =
> + pwm_chip_to_atmel_hlcdc_pwm_chip(c);
> + struct atmel_hlcdc *hlcdc = chip->hlcdc;
> + u32 status;
> +
> + regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PWM);
> + while (!regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status) &&
> + (status & ATMEL_HLCDC_PWM))
> + ;
Same here.
> +static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
> +{
> + struct atmel_hlcdc_pwm_chip *chip;
> + struct device *dev = &pdev->dev;
> + struct atmel_hlcdc *hlcdc;
> + int ret;
> +
> + hlcdc = dev_get_drvdata(dev->parent);
> + if (!hlcdc)
> + return -EINVAL;
Can this really happen?
> + ret = clk_prepare_enable(hlcdc->periph_clk);
> + if (ret)
> + return ret;
> +
> + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
> + if (!chip)
> + return -ENOMEM;
Don't you want to disable and unprepare the clock here? Perhaps in order
to avoid this call clk_prepare_enable() only after all resources have
been allocated.
> +MODULE_ALIAS("platform:atmel-hlcdc-pwm");
> +MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
> +MODULE_DESCRIPTION("Atmel HLCDC PWM driver");
> +MODULE_LICENSE("GPL");
According to the file header this needs to be "GPL v2".
Thierry
[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]
[-- Attachment #2: Type: text/plain, Size: 159 bytes --]
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel
WARNING: multiple messages have this Message-ID (diff)
From: thierry.reding@gmail.com (Thierry Reding)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v7 03/11] pwm: add support for atmel-hlcdc-pwm device
Date: Mon, 6 Oct 2014 12:46:35 +0200 [thread overview]
Message-ID: <20141006104634.GE25202@ulmo> (raw)
In-Reply-To: <1412175188-28278-4-git-send-email-boris.brezillon@free-electrons.com>
On Wed, Oct 01, 2014 at 04:53:00PM +0200, Boris Brezillon wrote:
[...]
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index b800783..afb896b 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -50,6 +50,16 @@ config PWM_ATMEL
> To compile this driver as a module, choose M here: the module
> will be called pwm-atmel.
>
> +config PWM_ATMEL_HLCDC_PWM
> + tristate "Atmel HLCDC PWM support"
> + select MFD_ATMEL_HLCDC
> + depends on OF
This isn't really necessary since MFD_ATMEL_HLCDC already depends on OF.
> diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c
[...]
> new file mode 100644
> index 0000000..0238f7a
> --- /dev/null
> +++ b/drivers/pwm/pwm-atmel-hlcdc.c
> @@ -0,0 +1,229 @@
> +/*
> + * Copyright (C) 2014 Free Electrons
> + * Copyright (C) 2014 Atmel
> + *
> + * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/mfd/atmel-hlcdc.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +#include <linux/regmap.h>
> +
> +#define ATMEL_HLCDC_PWMCVAL_MASK GENMASK(15, 8)
> +#define ATMEL_HLCDC_PWMCVAL(x) ((x << 8) & ATMEL_HLCDC_PWMCVAL_MASK)
You might want to use an extra pair of parentheses around the "x" above.
> +struct atmel_hlcdc_pwm_chip {
Can we make this...
> + struct pwm_chip chip;
> + struct atmel_hlcdc *hlcdc;
> + struct clk *cur_clk;
> +};
> +
> +static inline struct atmel_hlcdc_pwm_chip *
> +pwm_chip_to_atmel_hlcdc_pwm_chip(struct pwm_chip *chip)
... and this a little shorter? There is a lot of line-wrapping below
only because this is very long. It seems like just dropping the
pwm_chip_ prefix on this function would be enough to not exceed the
78/80 character limit.
> +{
> + return container_of(chip, struct atmel_hlcdc_pwm_chip, chip);
> +}
> +
> +static int atmel_hlcdc_pwm_config(struct pwm_chip *c,
> + struct pwm_device *pwm,
> + int duty_ns, int period_ns)
> +{
> + struct atmel_hlcdc_pwm_chip *chip =
> + pwm_chip_to_atmel_hlcdc_pwm_chip(c);
> + struct atmel_hlcdc *hlcdc = chip->hlcdc;
> + struct clk *new_clk = hlcdc->slow_clk;
> + u64 pwmcval = duty_ns * 256;
> + unsigned long clk_freq;
> + u64 clk_period_ns;
> + u32 pwmcfg;
> + int pres;
> +
> + clk_freq = clk_get_rate(new_clk);
> + clk_period_ns = 1000000000;
NSEC_PER_SEC?
> + clk_period_ns *= 256;
Perhaps collapse the above two in a single line:
clk_period_ns = NSEC_PER_SEC * 256;
?
> + do_div(clk_period_ns, clk_freq);
> +
> + if (clk_period_ns > period_ns) {
> + new_clk = hlcdc->sys_clk;
> + clk_freq = clk_get_rate(new_clk);
> + clk_period_ns = 1000000000;
> + clk_period_ns *= 256;
Maybe:
clk_period_ns = NSEC_PER_SEC * 256;
?
> + do_div(clk_period_ns, clk_freq);
> + }
> +
> + for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) {
> + if ((clk_period_ns << pres) >= period_ns)
> + break;
> + }
Technically there's no need for the curly braces.
> +
> + if (pres > ATMEL_HLCDC_PWMPS_MAX)
> + return -EINVAL;
I think the condition above needs to be "pres == ATMEL_HLCDC_PWMPS_MAX",
otherwise this will never be true.
> +
> + pwmcfg = ATMEL_HLCDC_PWMPS(pres);
> +
> + if (new_clk != chip->cur_clk) {
> + u32 gencfg = 0;
> +
> + clk_prepare_enable(new_clk);
This can fail so it needs error-checking.
> + clk_disable_unprepare(chip->cur_clk);
> + chip->cur_clk = new_clk;
> +
> + if (new_clk != hlcdc->slow_clk)
> + gencfg = ATMEL_HLCDC_CLKPWMSEL;
There are lots of negations here, which caused me to think that there
was a third clock involved here, but it seems like new_clk can either be
slow_clk or sys_clk.
Perhaps making this condition "new_clk == hlcdc->sys_clk" would improve
clarity here. Maybe a comment somewhere would help?
> + regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(0),
> + ATMEL_HLCDC_CLKPWMSEL, gencfg);
> + }
> +
> + do_div(pwmcval, period_ns);
> + if (pwmcval > 255)
The PWM core already makes sure that duty_ns <= period_ns, so pwmcval
could be anywhere between 0 and 256 here. Where does the disconnect come
from? Why not make pwmcval = duty_ns * 255 if that's the maximum?
> + pwmcval = 255;
> +
> + pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval);
> +
> + regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
> + ATMEL_HLCDC_PWMCVAL_MASK | ATMEL_HLCDC_PWMPS_MASK,
> + pwmcfg);
> +
> + return 0;
> +}
> +
> +static int atmel_hlcdc_pwm_set_polarity(struct pwm_chip *c,
> + struct pwm_device *pwm,
> + enum pwm_polarity polarity)
> +{
> + struct atmel_hlcdc_pwm_chip *chip =
> + pwm_chip_to_atmel_hlcdc_pwm_chip(c);
> + struct atmel_hlcdc *hlcdc = chip->hlcdc;
> + u32 cfg = 0;
> +
> + if (polarity == PWM_POLARITY_NORMAL)
> + cfg = ATMEL_HLCDC_PWMPOL;
That's strange. Inverse polarity is the default on this hardware?
> +static int atmel_hlcdc_pwm_enable(struct pwm_chip *c,
> + struct pwm_device *pwm)
There's no need for line-wrapping here. The above fits on one line just
fine.
> +{
> + struct atmel_hlcdc_pwm_chip *chip =
> + pwm_chip_to_atmel_hlcdc_pwm_chip(c);
> + struct atmel_hlcdc *hlcdc = chip->hlcdc;
> + u32 status;
> +
> + regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PWM);
> + while (!regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status) &&
> + !(status & ATMEL_HLCDC_PWM))
> + ;
This loop isn't very readable. Can you improve it? Perhaps:
do {
err = regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status);
if (err < 0)
return err;
} while ((status & ATMEL_HLCDC_PWM) == 0);
That also allows errors to be properly propagated. Perhaps you also want
to put a usleep_range() or similar in there.
> +static void atmel_hlcdc_pwm_disable(struct pwm_chip *c,
> + struct pwm_device *pwm)
> +{
> + struct atmel_hlcdc_pwm_chip *chip =
> + pwm_chip_to_atmel_hlcdc_pwm_chip(c);
> + struct atmel_hlcdc *hlcdc = chip->hlcdc;
> + u32 status;
> +
> + regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PWM);
> + while (!regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status) &&
> + (status & ATMEL_HLCDC_PWM))
> + ;
Same here.
> +static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
> +{
> + struct atmel_hlcdc_pwm_chip *chip;
> + struct device *dev = &pdev->dev;
> + struct atmel_hlcdc *hlcdc;
> + int ret;
> +
> + hlcdc = dev_get_drvdata(dev->parent);
> + if (!hlcdc)
> + return -EINVAL;
Can this really happen?
> + ret = clk_prepare_enable(hlcdc->periph_clk);
> + if (ret)
> + return ret;
> +
> + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
> + if (!chip)
> + return -ENOMEM;
Don't you want to disable and unprepare the clock here? Perhaps in order
to avoid this call clk_prepare_enable() only after all resources have
been allocated.
> +MODULE_ALIAS("platform:atmel-hlcdc-pwm");
> +MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
> +MODULE_DESCRIPTION("Atmel HLCDC PWM driver");
> +MODULE_LICENSE("GPL");
According to the file header this needs to be "GPL v2".
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20141006/b6e0a85b/attachment.sig>
WARNING: multiple messages have this Message-ID (diff)
From: Thierry Reding <thierry.reding@gmail.com>
To: Boris Brezillon <boris.brezillon@free-electrons.com>
Cc: David Airlie <airlied@linux.ie>,
dri-devel@lists.freedesktop.org,
Nicolas Ferre <nicolas.ferre@atmel.com>,
Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>,
Alexandre Belloni <alexandre.belloni@free-electrons.com>,
Andrew Victor <linux@maxim.org.za>,
Samuel Ortiz <sameo@linux.intel.com>,
Lee Jones <lee.jones@linaro.org>,
linux-pwm@vger.kernel.org, Rob Clark <robdclark@gmail.com>,
linux-arm-kernel@lists.infradead.org,
Rob Herring <robh+dt@kernel.org>, Pawel Moll <pawel.moll@arm.com>,
Mark Rutland <mark.rutland@arm.com>,
Ian Campbell <ijc+devicetree@hellion.org.uk>,
Kumar Gala <galak@codeaurora.org>,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Mark Yao <mark.yao@rock-chips.com>
Subject: Re: [PATCH v7 03/11] pwm: add support for atmel-hlcdc-pwm device
Date: Mon, 6 Oct 2014 12:46:35 +0200 [thread overview]
Message-ID: <20141006104634.GE25202@ulmo> (raw)
In-Reply-To: <1412175188-28278-4-git-send-email-boris.brezillon@free-electrons.com>
[-- Attachment #1: Type: text/plain, Size: 7864 bytes --]
On Wed, Oct 01, 2014 at 04:53:00PM +0200, Boris Brezillon wrote:
[...]
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index b800783..afb896b 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -50,6 +50,16 @@ config PWM_ATMEL
> To compile this driver as a module, choose M here: the module
> will be called pwm-atmel.
>
> +config PWM_ATMEL_HLCDC_PWM
> + tristate "Atmel HLCDC PWM support"
> + select MFD_ATMEL_HLCDC
> + depends on OF
This isn't really necessary since MFD_ATMEL_HLCDC already depends on OF.
> diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c
[...]
> new file mode 100644
> index 0000000..0238f7a
> --- /dev/null
> +++ b/drivers/pwm/pwm-atmel-hlcdc.c
> @@ -0,0 +1,229 @@
> +/*
> + * Copyright (C) 2014 Free Electrons
> + * Copyright (C) 2014 Atmel
> + *
> + * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/mfd/atmel-hlcdc.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +#include <linux/regmap.h>
> +
> +#define ATMEL_HLCDC_PWMCVAL_MASK GENMASK(15, 8)
> +#define ATMEL_HLCDC_PWMCVAL(x) ((x << 8) & ATMEL_HLCDC_PWMCVAL_MASK)
You might want to use an extra pair of parentheses around the "x" above.
> +struct atmel_hlcdc_pwm_chip {
Can we make this...
> + struct pwm_chip chip;
> + struct atmel_hlcdc *hlcdc;
> + struct clk *cur_clk;
> +};
> +
> +static inline struct atmel_hlcdc_pwm_chip *
> +pwm_chip_to_atmel_hlcdc_pwm_chip(struct pwm_chip *chip)
... and this a little shorter? There is a lot of line-wrapping below
only because this is very long. It seems like just dropping the
pwm_chip_ prefix on this function would be enough to not exceed the
78/80 character limit.
> +{
> + return container_of(chip, struct atmel_hlcdc_pwm_chip, chip);
> +}
> +
> +static int atmel_hlcdc_pwm_config(struct pwm_chip *c,
> + struct pwm_device *pwm,
> + int duty_ns, int period_ns)
> +{
> + struct atmel_hlcdc_pwm_chip *chip =
> + pwm_chip_to_atmel_hlcdc_pwm_chip(c);
> + struct atmel_hlcdc *hlcdc = chip->hlcdc;
> + struct clk *new_clk = hlcdc->slow_clk;
> + u64 pwmcval = duty_ns * 256;
> + unsigned long clk_freq;
> + u64 clk_period_ns;
> + u32 pwmcfg;
> + int pres;
> +
> + clk_freq = clk_get_rate(new_clk);
> + clk_period_ns = 1000000000;
NSEC_PER_SEC?
> + clk_period_ns *= 256;
Perhaps collapse the above two in a single line:
clk_period_ns = NSEC_PER_SEC * 256;
?
> + do_div(clk_period_ns, clk_freq);
> +
> + if (clk_period_ns > period_ns) {
> + new_clk = hlcdc->sys_clk;
> + clk_freq = clk_get_rate(new_clk);
> + clk_period_ns = 1000000000;
> + clk_period_ns *= 256;
Maybe:
clk_period_ns = NSEC_PER_SEC * 256;
?
> + do_div(clk_period_ns, clk_freq);
> + }
> +
> + for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) {
> + if ((clk_period_ns << pres) >= period_ns)
> + break;
> + }
Technically there's no need for the curly braces.
> +
> + if (pres > ATMEL_HLCDC_PWMPS_MAX)
> + return -EINVAL;
I think the condition above needs to be "pres == ATMEL_HLCDC_PWMPS_MAX",
otherwise this will never be true.
> +
> + pwmcfg = ATMEL_HLCDC_PWMPS(pres);
> +
> + if (new_clk != chip->cur_clk) {
> + u32 gencfg = 0;
> +
> + clk_prepare_enable(new_clk);
This can fail so it needs error-checking.
> + clk_disable_unprepare(chip->cur_clk);
> + chip->cur_clk = new_clk;
> +
> + if (new_clk != hlcdc->slow_clk)
> + gencfg = ATMEL_HLCDC_CLKPWMSEL;
There are lots of negations here, which caused me to think that there
was a third clock involved here, but it seems like new_clk can either be
slow_clk or sys_clk.
Perhaps making this condition "new_clk == hlcdc->sys_clk" would improve
clarity here. Maybe a comment somewhere would help?
> + regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(0),
> + ATMEL_HLCDC_CLKPWMSEL, gencfg);
> + }
> +
> + do_div(pwmcval, period_ns);
> + if (pwmcval > 255)
The PWM core already makes sure that duty_ns <= period_ns, so pwmcval
could be anywhere between 0 and 256 here. Where does the disconnect come
from? Why not make pwmcval = duty_ns * 255 if that's the maximum?
> + pwmcval = 255;
> +
> + pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval);
> +
> + regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
> + ATMEL_HLCDC_PWMCVAL_MASK | ATMEL_HLCDC_PWMPS_MASK,
> + pwmcfg);
> +
> + return 0;
> +}
> +
> +static int atmel_hlcdc_pwm_set_polarity(struct pwm_chip *c,
> + struct pwm_device *pwm,
> + enum pwm_polarity polarity)
> +{
> + struct atmel_hlcdc_pwm_chip *chip =
> + pwm_chip_to_atmel_hlcdc_pwm_chip(c);
> + struct atmel_hlcdc *hlcdc = chip->hlcdc;
> + u32 cfg = 0;
> +
> + if (polarity == PWM_POLARITY_NORMAL)
> + cfg = ATMEL_HLCDC_PWMPOL;
That's strange. Inverse polarity is the default on this hardware?
> +static int atmel_hlcdc_pwm_enable(struct pwm_chip *c,
> + struct pwm_device *pwm)
There's no need for line-wrapping here. The above fits on one line just
fine.
> +{
> + struct atmel_hlcdc_pwm_chip *chip =
> + pwm_chip_to_atmel_hlcdc_pwm_chip(c);
> + struct atmel_hlcdc *hlcdc = chip->hlcdc;
> + u32 status;
> +
> + regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PWM);
> + while (!regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status) &&
> + !(status & ATMEL_HLCDC_PWM))
> + ;
This loop isn't very readable. Can you improve it? Perhaps:
do {
err = regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status);
if (err < 0)
return err;
} while ((status & ATMEL_HLCDC_PWM) == 0);
That also allows errors to be properly propagated. Perhaps you also want
to put a usleep_range() or similar in there.
> +static void atmel_hlcdc_pwm_disable(struct pwm_chip *c,
> + struct pwm_device *pwm)
> +{
> + struct atmel_hlcdc_pwm_chip *chip =
> + pwm_chip_to_atmel_hlcdc_pwm_chip(c);
> + struct atmel_hlcdc *hlcdc = chip->hlcdc;
> + u32 status;
> +
> + regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PWM);
> + while (!regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status) &&
> + (status & ATMEL_HLCDC_PWM))
> + ;
Same here.
> +static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
> +{
> + struct atmel_hlcdc_pwm_chip *chip;
> + struct device *dev = &pdev->dev;
> + struct atmel_hlcdc *hlcdc;
> + int ret;
> +
> + hlcdc = dev_get_drvdata(dev->parent);
> + if (!hlcdc)
> + return -EINVAL;
Can this really happen?
> + ret = clk_prepare_enable(hlcdc->periph_clk);
> + if (ret)
> + return ret;
> +
> + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
> + if (!chip)
> + return -ENOMEM;
Don't you want to disable and unprepare the clock here? Perhaps in order
to avoid this call clk_prepare_enable() only after all resources have
been allocated.
> +MODULE_ALIAS("platform:atmel-hlcdc-pwm");
> +MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
> +MODULE_DESCRIPTION("Atmel HLCDC PWM driver");
> +MODULE_LICENSE("GPL");
According to the file header this needs to be "GPL v2".
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]
next prev parent reply other threads:[~2014-10-06 10:46 UTC|newest]
Thread overview: 102+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-10-01 14:52 [PATCH v7 00/11] drm: add support for Atmel HLCDC Display Controller Boris Brezillon
2014-10-01 14:52 ` Boris Brezillon
2014-10-01 14:52 ` [PATCH v7 01/11] mfd: add atmel-hlcdc driver Boris Brezillon
2014-10-01 14:52 ` Boris Brezillon
2014-10-01 14:52 ` Boris Brezillon
2014-10-06 10:49 ` Thierry Reding
2014-10-06 10:49 ` Thierry Reding
2014-10-06 10:49 ` Thierry Reding
2014-10-06 11:38 ` Boris Brezillon
2014-10-06 11:38 ` Boris Brezillon
2014-10-01 14:52 ` [PATCH v7 02/11] mfd: add documentation for atmel-hlcdc DT bindings Boris Brezillon
2014-10-01 14:52 ` Boris Brezillon
2014-10-01 14:52 ` Boris Brezillon
2014-10-01 14:53 ` [PATCH v7 03/11] pwm: add support for atmel-hlcdc-pwm device Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-06 10:46 ` Thierry Reding [this message]
2014-10-06 10:46 ` Thierry Reding
2014-10-06 10:46 ` Thierry Reding
2014-10-06 11:50 ` Boris Brezillon
2014-10-06 11:50 ` Boris Brezillon
2014-10-06 11:50 ` Boris Brezillon
2014-10-06 12:28 ` Thierry Reding
2014-10-06 12:28 ` Thierry Reding
2014-10-06 12:28 ` Thierry Reding
2014-10-01 14:53 ` [PATCH v7 04/11] pwm: add DT bindings documentation for atmel-hlcdc-pwm driver Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-06 10:13 ` Thierry Reding
2014-10-06 10:13 ` Thierry Reding
2014-10-06 10:13 ` Thierry Reding
2014-10-06 11:33 ` Mark Rutland
2014-10-06 11:33 ` Mark Rutland
2014-10-06 11:33 ` Mark Rutland
2014-10-06 12:23 ` Thierry Reding
2014-10-06 12:23 ` Thierry Reding
2014-10-06 12:23 ` Thierry Reding
2014-10-06 12:59 ` Boris Brezillon
2014-10-06 12:59 ` Boris Brezillon
2014-10-06 12:59 ` Boris Brezillon
2014-10-06 13:26 ` Thierry Reding
2014-10-06 13:26 ` Thierry Reding
2014-10-06 13:26 ` Thierry Reding
2014-10-01 14:53 ` [PATCH v7 05/11] drm: add Atmel HLCDC Display Controller support Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-08 10:42 ` Nicolas Ferre
2014-10-08 10:42 ` Nicolas Ferre
2014-10-01 14:53 ` [PATCH v7 06/11] drm: add DT bindings documentation for atmel-hlcdc-dc driver Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-06 10:54 ` Thierry Reding
2014-10-06 10:54 ` Thierry Reding
2014-10-06 10:54 ` Thierry Reding
2014-10-06 12:14 ` Boris Brezillon
2014-10-06 12:14 ` Boris Brezillon
2014-10-06 12:35 ` Thierry Reding
2014-10-06 12:35 ` Thierry Reding
2014-10-06 12:35 ` Thierry Reding
2014-10-06 13:53 ` Boris Brezillon
2014-10-06 13:53 ` Boris Brezillon
2014-10-06 13:53 ` Boris Brezillon
2014-10-06 14:26 ` Thierry Reding
2014-10-06 14:26 ` Thierry Reding
2014-10-06 16:02 ` Boris Brezillon
2014-10-06 16:02 ` Boris Brezillon
2014-10-06 16:02 ` Boris Brezillon
2014-10-01 14:53 ` [PATCH v7 07/11] ARM: AT91/dt: split sama5d3 lcd pin definitions to match RGB mode configs Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-01 14:53 ` [PATCH v7 08/11] ARM: AT91/dt: add alternative pin muxing for sama5d3 lcd pins Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-01 14:53 ` [PATCH v7 09/11] ARM: at91/dt: define the HLCDC node available on sama5d3 SoCs Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-01 14:53 ` [PATCH v7 10/11] ARM: at91/dt: add LCD panel description to sama5d3xdm.dtsi Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-06 11:01 ` Thierry Reding
2014-10-06 11:01 ` Thierry Reding
2014-10-06 11:01 ` Thierry Reding
2014-10-06 12:25 ` Boris Brezillon
2014-10-06 12:25 ` Boris Brezillon
2014-10-06 12:25 ` Boris Brezillon
2014-10-06 12:40 ` Thierry Reding
2014-10-06 12:40 ` Thierry Reding
2014-10-06 12:40 ` Thierry Reding
2014-10-06 13:11 ` Boris Brezillon
2014-10-06 13:11 ` Boris Brezillon
2014-10-06 13:11 ` Boris Brezillon
2014-10-06 13:30 ` Thierry Reding
2014-10-06 13:30 ` Thierry Reding
2014-10-06 13:30 ` Thierry Reding
2014-10-06 13:58 ` Boris Brezillon
2014-10-06 13:58 ` Boris Brezillon
[not found] ` <1412175188-28278-1-git-send-email-boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
2014-10-01 14:53 ` [PATCH v7 11/11] ARM: at91/dt: enable the LCD panel on sama5d3xek boards Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-01 14:53 ` Boris Brezillon
2014-10-06 11:01 ` Thierry Reding
2014-10-06 11:01 ` Thierry Reding
2014-10-06 11:01 ` Thierry Reding
2014-10-01 14:59 ` [PATCH v7 00/11] drm: add support for Atmel HLCDC Display Controller Boris Brezillon
2014-10-01 14:59 ` Boris Brezillon
2014-10-01 14:59 ` Boris Brezillon
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=20141006104634.GE25202@ulmo \
--to=thierry.reding@gmail.com \
--cc=alexandre.belloni@free-electrons.com \
--cc=boris.brezillon@free-electrons.com \
--cc=devicetree@vger.kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=galak@codeaurora.org \
--cc=ijc+devicetree@hellion.org.uk \
--cc=lee.jones@linaro.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pwm@vger.kernel.org \
--cc=linux@maxim.org.za \
--cc=mark.rutland@arm.com \
--cc=mark.yao@rock-chips.com \
--cc=nicolas.ferre@atmel.com \
--cc=pawel.moll@arm.com \
--cc=plagnioj@jcrosoft.com \
--cc=robh+dt@kernel.org \
--cc=sameo@linux.intel.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.