From: Andrew Lunn <andrew@lunn.ch>
To: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: cooloney@gmail.com, rpurdie@rpsys.net,
devicetree@vger.kernel.org, vigneshr@ti.com,
Matthew.Fatheree@belkin.com, linux-leds@vger.kernel.org,
kaloz@openwrt.org,
linux ARM <linux-arm-kernel@lists.infradead.org>
Subject: Re: [PATCHv5 2/2] leds: tlc591xx: Driver for the TI 8/16 Channel i2c LED driver
Date: Mon, 26 Jan 2015 18:41:00 +0100 [thread overview]
Message-ID: <20150126174100.GD5015@lunn.ch> (raw)
In-Reply-To: <54C625D8.7040305@ti.com>
On Mon, Jan 26, 2015 at 01:32:40PM +0200, Tomi Valkeinen wrote:
> On 22/01/15 00:29, Andrew Lunn wrote:
> > The TLC59116 is an I2C bus controlled 16-channel LED driver. The
> > TLC59108 is an I2C bus controlled 8-channel LED driver, which is very
> > similar to the TLC59116. Each LED output has its own 8-bit
> > fixed-frequency PWM controller to control the brightness of the LED.
> > The LEDs can also be fixed off and on, making them suitable for use as
> > GPOs.
> >
> > This is based on a driver from Belkin, but has been extensively
> > rewritten and extended to support both 08 and 16 versions.
> >
> > Signed-off-by: Andrew Lunn <andrew@lunn.ch>
> > Cc: Matthew.Fatheree@belkin.com
> > ---
> > drivers/leds/Kconfig | 8 ++
> > drivers/leds/Makefile | 1 +
> > drivers/leds/leds-tlc591xx.c | 309 +++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 318 insertions(+)
> > create mode 100644 drivers/leds/leds-tlc591xx.c
> >
> > diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> > index a6c3d2f153f3..67cba2032543 100644
> > --- a/drivers/leds/Kconfig
> > +++ b/drivers/leds/Kconfig
> > @@ -457,6 +457,14 @@ config LEDS_TCA6507
> > LED driver chips accessed via the I2C bus.
> > Driver support brightness control and hardware-assisted blinking.
> >
> > +config LEDS_TLC591XX
> > + tristate "LED driver for TLC59108 and TLC59116 controllers"
> > + depends on LEDS_CLASS && I2C
> > + select REGMAP_I2C
> > + help
> > + This option enables support for Texas Instruments TLC59108
> > + and TLC59116 LED controllers.
> > +
> > config LEDS_MAX8997
> > tristate "LED support for MAX8997 PMIC"
> > depends on LEDS_CLASS && MFD_MAX8997
> > diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> > index 1c65a191d907..0558117a9407 100644
> > --- a/drivers/leds/Makefile
> > +++ b/drivers/leds/Makefile
> > @@ -30,6 +30,7 @@ obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o
> > obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
> > obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o
> > obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
> > +obj-$(CONFIG_LEDS_TLC591XX) += leds-tlc591xx.o
> > obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
> > obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o
> > obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
> > diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c
> > new file mode 100644
> > index 000000000000..531e83f54465
> > --- /dev/null
> > +++ b/drivers/leds/leds-tlc591xx.c
> > @@ -0,0 +1,309 @@
> > +/*
> > + * Copyright 2014 Belkin Inc.
> > + * Copyright 2015 Andrew Lunn <andrew@lunn.ch>
> > + *
> > + * 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; version 2 of the License.
> > + */
> > +
> > +#include <linux/i2c.h>
> > +#include <linux/leds.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_device.h>
> > +#include <linux/regmap.h>
> > +#include <linux/slab.h>
> > +#include <linux/workqueue.h>
> > +
> > +#define TLC59116_LEDS 16
> > +#define TLC59108_LEDS 8
>
> These two are quite confusing. In some places they are used as "number
> of leds on TLC5910x device", and in some places they are used as "max
> leds on any TLC device".
>
> When defining the static values for struct tlc59116 and tlc59108 I think
> it would be much cleaner to just use the integer there, instead of one
> of the defines above. And if you needs a "max leds on any TLC device",
> define it clearly, like TLC591XX_MAX_LEDS or such.
Yes, i can change this, hard code tio 8/16 in the structure, and use
TLC591XX_MAX_LEDS.
> > +
> > +#define TLC591XX_REG_MODE1 0x00
> > +#define MODE1_RESPON_ADDR_MASK 0xF0
> > +#define MODE1_NORMAL_MODE (0 << 4)
> > +#define MODE1_SPEED_MODE (1 << 4)
> > +
> > +#define TLC591XX_REG_MODE2 0x01
> > +#define MODE2_DIM (0 << 5)
> > +#define MODE2_BLINK (1 << 5)
> > +#define MODE2_OCH_STOP (0 << 3)
> > +#define MODE2_OCH_ACK (1 << 3)
> > +
> > +#define TLC591XX_REG_PWM(x) (0x02 + (x))
> > +
> > +#define TLC591XX_REG_GRPPWM 0x12
> > +#define TLC591XX_REG_GRPFREQ 0x13
> > +
> > +/* LED Driver Output State, determine the source that drives LED outputs */
> > +#define LEDOUT_OFF 0x0 /* Output LOW */
> > +#define LEDOUT_ON 0x1 /* Output HI-Z */
> > +#define LEDOUT_DIM 0x2 /* Dimming */
> > +#define LEDOUT_BLINK 0x3 /* Blinking */
> > +#define LEDOUT_MASK 0x3
> > +
> > +#define ldev_to_led(c) container_of(c, struct tlc591xx_led, ldev)
> > +#define work_to_led(work) container_of(work, struct tlc591xx_led, work)
> > +
> > +struct tlc591xx_led {
> > + bool active;
> > + struct regmap *regmap;
> > + unsigned int led_no;
> > + struct led_classdev ldev;
> > + struct work_struct work;
> > + const struct tlc591xx *tlc591xx;
> > +};
>
> The struct above contains fields that are common to all led instances.
> Maybe a matter of taste, but I'd rather have one struct representing the
> whole device (struct tlc591xx_priv I guess), which would contains
> 'regmap' and 'tlc591xx' fields. And tlc591xx_led would contain a pointer
> to the tlc591xx_priv.
>
> Not a big difference, but having regmap in the struct above makes me
> think that each leds has its own regmap.
Adding the 08 support made this grow with more shared properties. It
probably is now time to have a common part and a per LED part.
> > +static int
> > +tlc591xx_set_mode(struct regmap *regmap, u8 mode)
> > +{
> > + int err;
> > + u8 val;
> > +
> > + if ((mode != MODE2_DIM) && (mode != MODE2_BLINK))
> > + mode = MODE2_DIM;
>
> If mode is not DIM or BLINK, should this function return an error?
Actually, i would remove this check altogether. This cannot be
influenced outside the driver, so if it is not _DIM or BLANK, it is an
internal driver bug.
> > +static void
> > +tlc591xx_led_work(struct work_struct *work)
> > +{
>
> Maybe it's normal for LED drivers, but why is the workqueue needed? Why
> not just do the work synchronously?
include/linux/leds.h says:
/* Must not sleep, use a workqueue if needed */
void (*brightness_set)(struct led_classdev *led_cdev,
enum led_brightness brightness);
and regmap uses a lock to protect its structures, and so does i2c, etc.
>
> > + struct tlc591xx_led *led = work_to_led(work);
> > + int err;
> > +
> > + switch (led->ldev.brightness) {
>
> Can the brightness here be < 0 or > LED_FULL?
Only if the core is broken. From include/linux/leds.h
enum led_brightness {
LED_OFF = 0,
LED_HALF = 127,
LED_FULL = 255,
};
> > + case 0:
> > + err = tlc591xx_set_ledout(led, LEDOUT_OFF);
> > + break;
> > + case LED_FULL:
> > + err = tlc591xx_set_ledout(led, LEDOUT_ON);
> > + break;
> > + default:
> > + err = tlc591xx_set_ledout(led, LEDOUT_DIM);
> > + if (!err)
> > + err = tlc591xx_set_pwm(led, led->ldev.brightness);
> > + }
>
> There doesn't seem to be any locking used for the fields this function
> accesses. Perhaps none is needed, but an async work without locks and
> without comments about it makes me feel a bit nervous.
I suppose led->ldev.brightness could change value between the switch
and the call to tlc591xx_set_pwm. I can take a local copy and use
that.
> > +static int
> > +tlc591xx_configure(struct device *dev,
> > + struct tlc591xx_priv *priv,
> > + struct regmap *regmap,
> > + const struct tlc591xx *tlc591xx)
> > +{
> > + unsigned int i;
> > + int err = 0;
> > +
> > + tlc591xx_set_mode(regmap, MODE2_DIM);
> > + for (i = 0; i < TLC59116_LEDS; i++) {
>
> Looping for tlc591xx->maxleds should be enough?
Yes, it would, but i'm not sure that is any better. At the moment it
is a constant, so the compiler can optimise it. We might save 8
iterations for TLC59108, but how much do we add by having less well
optimized code? And this is during probe, not some hot path, so do we
really care?
> > +
> > + tlc591xx = of_match_device(of_tlc591xx_leds_match, dev)->data;
>
> I presume of_match_device() can return NULL or an error, making the
> above crash.
It would be very odd. The fact the probe function is being called
means there is a match. So return values like -ENODEV would mean a
core OF bug. There is no memory allocations needed, so -ENOMEM would
also not be expected.
> > +
> > + count = of_get_child_count(np);
>
> 'np' may be NULL. I'm not sure how of_get_child_count() likes that.
How can it be NULL? If the probe has been called, it means the
compatibility string must match. If it matches, there must be a np!
Anyway, of_get_child_count() looks to be happy with NULL and will
return 0.
Andrew
next prev parent reply other threads:[~2015-01-26 17:41 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-01-21 22:29 [PATCHv5 0/2] Driver for TI tlc591xx 8/16 Channel i2c LED driver Andrew Lunn
[not found] ` <1421879364-8573-1-git-send-email-andrew-g2DYL2Zd6BY@public.gmane.org>
2015-01-21 22:29 ` [PATCHv5 1/2] leds: tlc591xx: Document binding for the TI " Andrew Lunn
2015-01-21 22:29 ` [PATCHv5 2/2] leds: tlc591xx: Driver " Andrew Lunn
2015-01-26 11:32 ` Tomi Valkeinen
2015-01-26 17:41 ` Andrew Lunn [this message]
2015-01-27 11:17 ` Tomi Valkeinen
[not found] ` <1421879364-8573-3-git-send-email-andrew-g2DYL2Zd6BY@public.gmane.org>
2015-01-26 11:46 ` Tomi Valkeinen
2015-01-26 17:10 ` Andrew Lunn
2015-01-27 11:11 ` Tomi Valkeinen
2015-02-03 9:14 ` 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=20150126174100.GD5015@lunn.ch \
--to=andrew@lunn.ch \
--cc=Matthew.Fatheree@belkin.com \
--cc=cooloney@gmail.com \
--cc=devicetree@vger.kernel.org \
--cc=kaloz@openwrt.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-leds@vger.kernel.org \
--cc=rpurdie@rpsys.net \
--cc=tomi.valkeinen@ti.com \
--cc=vigneshr@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).