From: Thierry Reding <thierry.reding@avionic-design.de>
To: Peter Ujfalusi <peter.ujfalusi@ti.com>
Cc: Tero Kristo <t-kristo@ti.com>,
Grazvydas Ignotas <notasas@gmail.com>,
linux-kernel@vger.kernel.org, linux-omap@vger.kernel.org,
Linus Walleij <linus.walleij@linaro.org>
Subject: Re: [PATCH v3 2/3] pwm: New driver to support PWMs on TWL4030/6030 series of PMICs
Date: Fri, 23 Nov 2012 15:58:45 +0100 [thread overview]
Message-ID: <20121123145844.GA16810@avionic-0098.adnet.avionic-design.de> (raw)
In-Reply-To: <1353405382-9226-3-git-send-email-peter.ujfalusi@ti.com>
[-- Attachment #1: Type: text/plain, Size: 8404 bytes --]
On Tue, Nov 20, 2012 at 10:56:21AM +0100, Peter Ujfalusi wrote:
> The driver supports the following PWM outputs:
> TWL4030 PWM0 and PWM1
> TWL6030 PWM1 and PWM2
>
> On TWL4030 the PWM signals are muxed. Upon requesting the PWM the driver
> will select the correct mux so the PWM can be used. When the PWM has been
> freed the original configuration going to be restored.
"configuration is going to be"
> +config PWM_TWL
> + tristate "TWL4030/6030 PWM support"
> + select HAVE_PWM
Why do you select HAVE_PWM?
> +static int twl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
> + int duty_ns, int period_ns)
> +{
> + int duty_cycle = DIV_ROUND_UP(duty_ns * TWL_PWM_MAX, period_ns);
> + u8 pwm_config[2] = {1, 0};
> + int base, ret;
> +
> + /*
> + * To configure the duty period:
> + * On-cycle is set to 1 (the minimum allowed value)
> + * The off time of 0 is not configurable, so the mapping is:
> + * 0 -> off cycle = 2,
> + * 1 -> off cycle = 2,
> + * 2 -> off cycle = 3,
> + * 126 - > off cycle 127,
> + * 127 - > off cycle 1
> + * When on cycle == off cycle the PWM will be always on
> + */
> + duty_cycle += 1;
The canonical form to write this would be "duty_cycle++", but maybe it
would even be better to add it to the expression that defines
duty_cycle?
> +static int twl4030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + int ret;
> + u8 val;
> +
> + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG);
> + if (ret < 0) {
> + dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label);
> + return ret;
> + }
> +
> + val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE);
> +
> + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG);
> + if (ret < 0)
> + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label);
> +
> + val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE);
> +
> + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG);
> + if (ret < 0)
> + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label);
> +
> + return ret;
> +}
Does this perhaps need some locking to make sure the read-modify-write
sequence isn't interrupted?
> +static void twl4030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + int ret;
> + u8 val;
> +
> + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG);
> + if (ret < 0) {
> + dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label);
> + return;
> + }
> +
> + val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE);
> +
> + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG);
> + if (ret < 0)
> + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
> +
> + val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE);
> +
> + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG);
> + if (ret < 0)
> + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
> +}
Same here.
> +static int twl4030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip,
> + chip);
This could use a macro like to_twl(chip).
> + int ret;
> + u8 val, mask, bits;
> +
> + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG);
> + if (ret < 0) {
> + dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label);
> + return ret;
> + }
> +
> + if (pwm->hwpwm) {
> + /* PWM 1 */
> + mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK;
> + bits = TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1;
> + } else {
> + /* PWM 0 */
> + mask = TWL4030_GPIO6_PWM0_MUTE_MASK;
> + bits = TWL4030_GPIO6_PWM0_MUTE_PWM0;
> + }
> +
> + /* Save the current MUX configuration for the PWM */
> + twl->twl4030_pwm_mux &= ~mask;
> + twl->twl4030_pwm_mux |= (val & mask);
> +
> + /* Select PWM functionality */
> + val &= ~mask;
> + val |= bits;
> +
> + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG);
> + if (ret < 0)
> + dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label);
> +
> + return ret;
> +}
Again, more read-modify-write without locking.
> +
> +static void twl4030_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip,
> + chip);
> + int ret;
> + u8 val, mask;
> +
> + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG);
> + if (ret < 0) {
> + dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label);
> + return;
> + }
> +
> + if (pwm->hwpwm)
> + /* PWM 1 */
> + mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK;
> + else
> + /* PWM 0 */
> + mask = TWL4030_GPIO6_PWM0_MUTE_MASK;
I'm not sure how useful these comments are. You have both an explicit
check for pwm->hwpwm to make it obvious that it isn't 0 and the mask
macros contain the PWM0 and PWM1 substrings respectively.
You could make it even more explicit perhaps by turning the check into:
if (pwm->hwpwm == 1)
But either way I think you can drop the /* PWM 1 */ and /* PWM 0 */
comments.
> +
> + /* Restore the MUX configuration for the PWM */
> + val &= ~mask;
> + val |= (twl->twl4030_pwm_mux & mask);
> +
> + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG);
> + if (ret < 0)
> + dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label);
> +}
> +
> +static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip,
> + chip);
> + int ret;
> + u8 val;
> +
> + val = twl->twl6030_toggle3;
> + val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN);
> + val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR);
> +
> + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
> + if (ret < 0) {
> + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label);
> + return ret;
> + }
> +
> + twl->twl6030_toggle3 = val;
> +
> + return 0;
> +}
> +
> +static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip,
> + chip);
> + int ret;
> + u8 val;
> +
> + val = twl->twl6030_toggle3;
> + val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR);
> + val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN);
> +
> + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
> + if (ret < 0) {
> + dev_err(chip->dev, "%s: Failed to read TOGGLE3\n", pwm->label);
> + return;
> + }
> +
> + val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN);
> +
> + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
> + if (ret < 0) {
> + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
> + return;
> + }
> +
> + twl->twl6030_toggle3 = val;
> +}
> +
> +static struct pwm_ops twl_pwm_ops = {
> + .config = twl_pwm_config,
> +};
It might actually be worth to split this into two pwm_ops structures,
one for 4030 and one for 6030. In that case you would still be able to
make them const, which probably outweighs the space savings here.
Actually, I think this is even potentially buggy since you may have more
than one instance of this driver. For instance if you have a TWL6030 and
a TWL4030 in a single system this will break horribly since you'll over-
write the pwm_ops of one of them.
> + if (twl_class_is_4030()) {
> + twl_pwm_ops.enable = twl4030_pwm_enable;
> + twl_pwm_ops.disable = twl4030_pwm_disable;
> + twl_pwm_ops.request = twl4030_pwm_request;
> + twl_pwm_ops.free = twl4030_pwm_free;
This would become:
twl->chip.ops = &twl4030_pwm_ops;
> + } else {
> + twl_pwm_ops.enable = twl6030_pwm_enable;
> + twl_pwm_ops.disable = twl6030_pwm_disable;
and:
twl->chip.ops = &twl6030_pwm_ops;
> +static struct of_device_id twl_pwm_of_match[] = {
> + { .compatible = "ti,twl4030-pwm" },
> + { .compatible = "ti,twl6030-pwm" },
> +};
> +
> +MODULE_DEVICE_TABLE(of, twl_pwm_of_match);
Nit: I think the blank line between "};" and "MODULE_DEVICE_TABLE(...)"
can go away. And you might want to protect this with an "#ifdef
CONFIG_OF" since you don't depend on OF.
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
next prev parent reply other threads:[~2012-11-23 14:58 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-11-20 9:56 [PATCH v3 0/3] pwm: Drivers for twl4030/6030 PWMs and LEDs Peter Ujfalusi
2012-11-20 9:56 ` [PATCH v3 1/3] pwm: Remove pwm-twl6030 driver Peter Ujfalusi
2012-11-23 15:05 ` Thierry Reding
2012-11-26 9:11 ` Peter Ujfalusi
2012-11-20 9:56 ` [PATCH v3 2/3] pwm: New driver to support PWMs on TWL4030/6030 series of PMICs Peter Ujfalusi
2012-11-23 14:58 ` Thierry Reding [this message]
2012-11-26 9:31 ` Peter Ujfalusi
2012-11-20 9:56 ` [PATCH v3 3/3] pwm: New driver to support PWM driven LEDs " Peter Ujfalusi
2012-11-20 11:54 ` Peter Ujfalusi
2012-11-20 11:58 ` Thierry Reding
2012-11-23 9:51 ` Peter Ujfalusi
2012-11-23 15:04 ` Thierry Reding
2012-11-26 8:30 ` Peter Ujfalusi
2012-11-26 9:17 ` Thierry Reding
2012-11-26 9:33 ` Peter Ujfalusi
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=20121123145844.GA16810@avionic-0098.adnet.avionic-design.de \
--to=thierry.reding@avionic-design.de \
--cc=linus.walleij@linaro.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-omap@vger.kernel.org \
--cc=notasas@gmail.com \
--cc=peter.ujfalusi@ti.com \
--cc=t-kristo@ti.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 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).