162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * leds-tca6507
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * The TCA6507 is a programmable LED controller that can drive 7
662306a36Sopenharmony_ci * separate lines either by holding them low, or by pulsing them
762306a36Sopenharmony_ci * with modulated width.
862306a36Sopenharmony_ci * The modulation can be varied in a simple pattern to produce a
962306a36Sopenharmony_ci * blink or double-blink.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * This driver can configure each line either as a 'GPIO' which is
1262306a36Sopenharmony_ci * out-only (pull-up resistor required) or as an LED with variable
1362306a36Sopenharmony_ci * brightness and hardware-assisted blinking.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * Apart from OFF and ON there are three programmable brightness
1662306a36Sopenharmony_ci * levels which can be programmed from 0 to 15 and indicate how many
1762306a36Sopenharmony_ci * 500usec intervals in each 8msec that the led is 'on'.  The levels
1862306a36Sopenharmony_ci * are named MASTER, BANK0 and BANK1.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * There are two different blink rates that can be programmed, each
2162306a36Sopenharmony_ci * with separate time for rise, on, fall, off and second-off.  Thus if
2262306a36Sopenharmony_ci * 3 or more different non-trivial rates are required, software must
2362306a36Sopenharmony_ci * be used for the extra rates. The two different blink rates must
2462306a36Sopenharmony_ci * align with the two levels BANK0 and BANK1.  This driver does not
2562306a36Sopenharmony_ci * support double-blink so 'second-off' always matches 'off'.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * Only 16 different times can be programmed in a roughly logarithmic
2862306a36Sopenharmony_ci * scale from 64ms to 16320ms.  To be precise the possible times are:
2962306a36Sopenharmony_ci *    0, 64, 128, 192, 256, 384, 512, 768,
3062306a36Sopenharmony_ci *    1024, 1536, 2048, 3072, 4096, 5760, 8128, 16320
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * Times that cannot be closely matched with these must be handled in
3362306a36Sopenharmony_ci * software.  This driver allows 12.5% error in matching.
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * This driver does not allow rise/fall rates to be set explicitly.
3662306a36Sopenharmony_ci * When trying to match a given 'on' or 'off' period, an appropriate
3762306a36Sopenharmony_ci * pair of 'change' and 'hold' times are chosen to get a close match.
3862306a36Sopenharmony_ci * If the target delay is even, the 'change' number will be the
3962306a36Sopenharmony_ci * smaller; if odd, the 'hold' number will be the smaller.
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci * Choosing pairs of delays with 12.5% errors allows us to match
4262306a36Sopenharmony_ci * delays in the ranges: 56-72, 112-144, 168-216, 224-27504,
4362306a36Sopenharmony_ci * 28560-36720.
4462306a36Sopenharmony_ci * 26% of the achievable sums can be matched by multiple pairings.
4562306a36Sopenharmony_ci * For example 1536 == 1536+0, 1024+512, or 768+768.
4662306a36Sopenharmony_ci * This driver will always choose the pairing with the least
4762306a36Sopenharmony_ci * maximum - 768+768 in this case.  Other pairings are not available.
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci * Access to the 3 levels and 2 blinks are on a first-come,
5062306a36Sopenharmony_ci * first-served basis.  Access can be shared by multiple leds if they
5162306a36Sopenharmony_ci * have the same level and either same blink rates, or some don't
5262306a36Sopenharmony_ci * blink.  When a led changes, it relinquishes access and tries again,
5362306a36Sopenharmony_ci * so it might lose access to hardware blink.
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * If a blink engine cannot be allocated, software blink is used.  If
5662306a36Sopenharmony_ci * the desired brightness cannot be allocated, the closest available
5762306a36Sopenharmony_ci * non-zero brightness is used.  As 'full' is always available, the
5862306a36Sopenharmony_ci * worst case would be to have two different blink rates at '1', with
5962306a36Sopenharmony_ci * Max at '2', then other leds will have to choose between '2' and
6062306a36Sopenharmony_ci * '16'.  Hopefully this is not likely.
6162306a36Sopenharmony_ci *
6262306a36Sopenharmony_ci * Each bank (BANK0 and BANK1) has two usage counts - LEDs using the
6362306a36Sopenharmony_ci * brightness and LEDs using the blink.  It can only be reprogrammed
6462306a36Sopenharmony_ci * when the appropriate counter is zero.  The MASTER level has a
6562306a36Sopenharmony_ci * single usage count.
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci * Each LED has programmable 'on' and 'off' time as milliseconds.
6862306a36Sopenharmony_ci * With each there is a flag saying if it was explicitly requested or
6962306a36Sopenharmony_ci * defaulted.  Similarly the banks know if each time was explicit or a
7062306a36Sopenharmony_ci * default.  Defaults are permitted to be changed freely - they are
7162306a36Sopenharmony_ci * not recognised when matching.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#include <linux/module.h>
7562306a36Sopenharmony_ci#include <linux/slab.h>
7662306a36Sopenharmony_ci#include <linux/leds.h>
7762306a36Sopenharmony_ci#include <linux/err.h>
7862306a36Sopenharmony_ci#include <linux/i2c.h>
7962306a36Sopenharmony_ci#include <linux/gpio/driver.h>
8062306a36Sopenharmony_ci#include <linux/property.h>
8162306a36Sopenharmony_ci#include <linux/workqueue.h>
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/* LED select registers determine the source that drives LED outputs */
8462306a36Sopenharmony_ci#define TCA6507_LS_LED_OFF	0x0	/* Output HI-Z (off) */
8562306a36Sopenharmony_ci#define TCA6507_LS_LED_OFF1	0x1	/* Output HI-Z (off) - not used */
8662306a36Sopenharmony_ci#define TCA6507_LS_LED_PWM0	0x2	/* Output LOW with Bank0 rate */
8762306a36Sopenharmony_ci#define TCA6507_LS_LED_PWM1	0x3	/* Output LOW with Bank1 rate */
8862306a36Sopenharmony_ci#define TCA6507_LS_LED_ON	0x4	/* Output LOW (on) */
8962306a36Sopenharmony_ci#define TCA6507_LS_LED_MIR	0x5	/* Output LOW with Master Intensity */
9062306a36Sopenharmony_ci#define TCA6507_LS_BLINK0	0x6	/* Blink at Bank0 rate */
9162306a36Sopenharmony_ci#define TCA6507_LS_BLINK1	0x7	/* Blink at Bank1 rate */
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistruct tca6507_platform_data {
9462306a36Sopenharmony_ci	struct led_platform_data leds;
9562306a36Sopenharmony_ci#ifdef CONFIG_GPIOLIB
9662306a36Sopenharmony_ci	int gpio_base;
9762306a36Sopenharmony_ci#endif
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define	TCA6507_MAKE_GPIO 1
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cienum {
10362306a36Sopenharmony_ci	BANK0,
10462306a36Sopenharmony_ci	BANK1,
10562306a36Sopenharmony_ci	MASTER,
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_cistatic int bank_source[3] = {
10862306a36Sopenharmony_ci	TCA6507_LS_LED_PWM0,
10962306a36Sopenharmony_ci	TCA6507_LS_LED_PWM1,
11062306a36Sopenharmony_ci	TCA6507_LS_LED_MIR,
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_cistatic int blink_source[2] = {
11362306a36Sopenharmony_ci	TCA6507_LS_BLINK0,
11462306a36Sopenharmony_ci	TCA6507_LS_BLINK1,
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/* PWM registers */
11862306a36Sopenharmony_ci#define	TCA6507_REG_CNT			11
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci/*
12162306a36Sopenharmony_ci * 0x00, 0x01, 0x02 encode the TCA6507_LS_* values, each output
12262306a36Sopenharmony_ci * owns one bit in each register
12362306a36Sopenharmony_ci */
12462306a36Sopenharmony_ci#define	TCA6507_FADE_ON			0x03
12562306a36Sopenharmony_ci#define	TCA6507_FULL_ON			0x04
12662306a36Sopenharmony_ci#define	TCA6507_FADE_OFF		0x05
12762306a36Sopenharmony_ci#define	TCA6507_FIRST_OFF		0x06
12862306a36Sopenharmony_ci#define	TCA6507_SECOND_OFF		0x07
12962306a36Sopenharmony_ci#define	TCA6507_MAX_INTENSITY		0x08
13062306a36Sopenharmony_ci#define	TCA6507_MASTER_INTENSITY	0x09
13162306a36Sopenharmony_ci#define	TCA6507_INITIALIZE		0x0A
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#define	INIT_CODE			0x8
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#define TIMECODES 16
13662306a36Sopenharmony_cistatic int time_codes[TIMECODES] = {
13762306a36Sopenharmony_ci	0, 64, 128, 192, 256, 384, 512, 768,
13862306a36Sopenharmony_ci	1024, 1536, 2048, 3072, 4096, 5760, 8128, 16320
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/* Convert an led.brightness level (0..255) to a TCA6507 level (0..15) */
14262306a36Sopenharmony_cistatic inline int TO_LEVEL(int brightness)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	return brightness >> 4;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/* ...and convert back */
14862306a36Sopenharmony_cistatic inline int TO_BRIGHT(int level)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	if (level)
15162306a36Sopenharmony_ci		return (level << 4) | 0xf;
15262306a36Sopenharmony_ci	return 0;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci#define NUM_LEDS 7
15662306a36Sopenharmony_cistruct tca6507_chip {
15762306a36Sopenharmony_ci	int			reg_set;	/* One bit per register where
15862306a36Sopenharmony_ci						 * a '1' means the register
15962306a36Sopenharmony_ci						 * should be written */
16062306a36Sopenharmony_ci	u8			reg_file[TCA6507_REG_CNT];
16162306a36Sopenharmony_ci	/* Bank 2 is Master Intensity and doesn't use times */
16262306a36Sopenharmony_ci	struct bank {
16362306a36Sopenharmony_ci		int level;
16462306a36Sopenharmony_ci		int ontime, offtime;
16562306a36Sopenharmony_ci		int on_dflt, off_dflt;
16662306a36Sopenharmony_ci		int time_use, level_use;
16762306a36Sopenharmony_ci	} bank[3];
16862306a36Sopenharmony_ci	struct i2c_client	*client;
16962306a36Sopenharmony_ci	struct work_struct	work;
17062306a36Sopenharmony_ci	spinlock_t		lock;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	struct tca6507_led {
17362306a36Sopenharmony_ci		struct tca6507_chip	*chip;
17462306a36Sopenharmony_ci		struct led_classdev	led_cdev;
17562306a36Sopenharmony_ci		int			num;
17662306a36Sopenharmony_ci		int			ontime, offtime;
17762306a36Sopenharmony_ci		int			on_dflt, off_dflt;
17862306a36Sopenharmony_ci		int			bank;	/* Bank used, or -1 */
17962306a36Sopenharmony_ci		int			blink;	/* Set if hardware-blinking */
18062306a36Sopenharmony_ci	} leds[NUM_LEDS];
18162306a36Sopenharmony_ci#ifdef CONFIG_GPIOLIB
18262306a36Sopenharmony_ci	struct gpio_chip		gpio;
18362306a36Sopenharmony_ci	int				gpio_map[NUM_LEDS];
18462306a36Sopenharmony_ci#endif
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic const struct i2c_device_id tca6507_id[] = {
18862306a36Sopenharmony_ci	{ "tca6507" },
18962306a36Sopenharmony_ci	{ }
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tca6507_id);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic int choose_times(int msec, int *c1p, int *c2p)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	/*
19662306a36Sopenharmony_ci	 * Choose two timecodes which add to 'msec' as near as
19762306a36Sopenharmony_ci	 * possible.  The first returned is the 'on' or 'off' time.
19862306a36Sopenharmony_ci	 * The second is to be used as a 'fade-on' or 'fade-off' time.
19962306a36Sopenharmony_ci	 * If 'msec' is even, the first will not be smaller than the
20062306a36Sopenharmony_ci	 * second.  If 'msec' is odd, the first will not be larger
20162306a36Sopenharmony_ci	 * than the second.
20262306a36Sopenharmony_ci	 * If we cannot get a sum within 1/8 of 'msec' fail with
20362306a36Sopenharmony_ci	 * -EINVAL, otherwise return the sum that was achieved, plus 1
20462306a36Sopenharmony_ci	 * if the first is smaller.
20562306a36Sopenharmony_ci	 * If two possibilities are equally good (e.g. 512+0,
20662306a36Sopenharmony_ci	 * 256+256), choose the first pair so there is more
20762306a36Sopenharmony_ci	 * change-time visible (i.e. it is softer).
20862306a36Sopenharmony_ci	 */
20962306a36Sopenharmony_ci	int c1, c2;
21062306a36Sopenharmony_ci	int tmax = msec * 9 / 8;
21162306a36Sopenharmony_ci	int tmin = msec * 7 / 8;
21262306a36Sopenharmony_ci	int diff = 65536;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* We start at '1' to ensure we never even think of choosing a
21562306a36Sopenharmony_ci	 * total time of '0'.
21662306a36Sopenharmony_ci	 */
21762306a36Sopenharmony_ci	for (c1 = 1; c1 < TIMECODES; c1++) {
21862306a36Sopenharmony_ci		int t = time_codes[c1];
21962306a36Sopenharmony_ci		if (t*2 < tmin)
22062306a36Sopenharmony_ci			continue;
22162306a36Sopenharmony_ci		if (t > tmax)
22262306a36Sopenharmony_ci			break;
22362306a36Sopenharmony_ci		for (c2 = 0; c2 <= c1; c2++) {
22462306a36Sopenharmony_ci			int tt = t + time_codes[c2];
22562306a36Sopenharmony_ci			int d;
22662306a36Sopenharmony_ci			if (tt < tmin)
22762306a36Sopenharmony_ci				continue;
22862306a36Sopenharmony_ci			if (tt > tmax)
22962306a36Sopenharmony_ci				break;
23062306a36Sopenharmony_ci			/* This works! */
23162306a36Sopenharmony_ci			d = abs(msec - tt);
23262306a36Sopenharmony_ci			if (d >= diff)
23362306a36Sopenharmony_ci				continue;
23462306a36Sopenharmony_ci			/* Best yet */
23562306a36Sopenharmony_ci			*c1p = c1;
23662306a36Sopenharmony_ci			*c2p = c2;
23762306a36Sopenharmony_ci			diff = d;
23862306a36Sopenharmony_ci			if (d == 0)
23962306a36Sopenharmony_ci				return msec;
24062306a36Sopenharmony_ci		}
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci	if (diff < 65536) {
24362306a36Sopenharmony_ci		int actual;
24462306a36Sopenharmony_ci		if (msec & 1) {
24562306a36Sopenharmony_ci			swap(*c2p, *c1p);
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci		actual = time_codes[*c1p] + time_codes[*c2p];
24862306a36Sopenharmony_ci		if (*c1p < *c2p)
24962306a36Sopenharmony_ci			return actual + 1;
25062306a36Sopenharmony_ci		else
25162306a36Sopenharmony_ci			return actual;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci	/* No close match */
25462306a36Sopenharmony_ci	return -EINVAL;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci/*
25862306a36Sopenharmony_ci * Update the register file with the appropriate 3-bit state for the
25962306a36Sopenharmony_ci * given led.
26062306a36Sopenharmony_ci */
26162306a36Sopenharmony_cistatic void set_select(struct tca6507_chip *tca, int led, int val)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	int mask = (1 << led);
26462306a36Sopenharmony_ci	int bit;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	for (bit = 0; bit < 3; bit++) {
26762306a36Sopenharmony_ci		int n = tca->reg_file[bit] & ~mask;
26862306a36Sopenharmony_ci		if (val & (1 << bit))
26962306a36Sopenharmony_ci			n |= mask;
27062306a36Sopenharmony_ci		if (tca->reg_file[bit] != n) {
27162306a36Sopenharmony_ci			tca->reg_file[bit] = n;
27262306a36Sopenharmony_ci			tca->reg_set |= (1 << bit);
27362306a36Sopenharmony_ci		}
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/* Update the register file with the appropriate 4-bit code for one
27862306a36Sopenharmony_ci * bank or other.  This can be used for timers, for levels, or for
27962306a36Sopenharmony_ci * initialization.
28062306a36Sopenharmony_ci */
28162306a36Sopenharmony_cistatic void set_code(struct tca6507_chip *tca, int reg, int bank, int new)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	int mask = 0xF;
28462306a36Sopenharmony_ci	int n;
28562306a36Sopenharmony_ci	if (bank) {
28662306a36Sopenharmony_ci		mask <<= 4;
28762306a36Sopenharmony_ci		new <<= 4;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci	n = tca->reg_file[reg] & ~mask;
29062306a36Sopenharmony_ci	n |= new;
29162306a36Sopenharmony_ci	if (tca->reg_file[reg] != n) {
29262306a36Sopenharmony_ci		tca->reg_file[reg] = n;
29362306a36Sopenharmony_ci		tca->reg_set |= 1 << reg;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/* Update brightness level. */
29862306a36Sopenharmony_cistatic void set_level(struct tca6507_chip *tca, int bank, int level)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	switch (bank) {
30162306a36Sopenharmony_ci	case BANK0:
30262306a36Sopenharmony_ci	case BANK1:
30362306a36Sopenharmony_ci		set_code(tca, TCA6507_MAX_INTENSITY, bank, level);
30462306a36Sopenharmony_ci		break;
30562306a36Sopenharmony_ci	case MASTER:
30662306a36Sopenharmony_ci		set_code(tca, TCA6507_MASTER_INTENSITY, 0, level);
30762306a36Sopenharmony_ci		break;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci	tca->bank[bank].level = level;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci/* Record all relevant time codes for a given bank */
31362306a36Sopenharmony_cistatic void set_times(struct tca6507_chip *tca, int bank)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	int c1, c2;
31662306a36Sopenharmony_ci	int result;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	result = choose_times(tca->bank[bank].ontime, &c1, &c2);
31962306a36Sopenharmony_ci	if (result < 0)
32062306a36Sopenharmony_ci		return;
32162306a36Sopenharmony_ci	dev_dbg(&tca->client->dev,
32262306a36Sopenharmony_ci		"Chose on  times %d(%d) %d(%d) for %dms\n",
32362306a36Sopenharmony_ci		c1, time_codes[c1],
32462306a36Sopenharmony_ci		c2, time_codes[c2], tca->bank[bank].ontime);
32562306a36Sopenharmony_ci	set_code(tca, TCA6507_FADE_ON, bank, c2);
32662306a36Sopenharmony_ci	set_code(tca, TCA6507_FULL_ON, bank, c1);
32762306a36Sopenharmony_ci	tca->bank[bank].ontime = result;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	result = choose_times(tca->bank[bank].offtime, &c1, &c2);
33062306a36Sopenharmony_ci	dev_dbg(&tca->client->dev,
33162306a36Sopenharmony_ci		"Chose off times %d(%d) %d(%d) for %dms\n",
33262306a36Sopenharmony_ci		c1, time_codes[c1],
33362306a36Sopenharmony_ci		c2, time_codes[c2], tca->bank[bank].offtime);
33462306a36Sopenharmony_ci	set_code(tca, TCA6507_FADE_OFF, bank, c2);
33562306a36Sopenharmony_ci	set_code(tca, TCA6507_FIRST_OFF, bank, c1);
33662306a36Sopenharmony_ci	set_code(tca, TCA6507_SECOND_OFF, bank, c1);
33762306a36Sopenharmony_ci	tca->bank[bank].offtime = result;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	set_code(tca, TCA6507_INITIALIZE, bank, INIT_CODE);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/* Write all needed register of tca6507 */
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic void tca6507_work(struct work_struct *work)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct tca6507_chip *tca = container_of(work, struct tca6507_chip,
34762306a36Sopenharmony_ci						work);
34862306a36Sopenharmony_ci	struct i2c_client *cl = tca->client;
34962306a36Sopenharmony_ci	int set;
35062306a36Sopenharmony_ci	u8 file[TCA6507_REG_CNT];
35162306a36Sopenharmony_ci	int r;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	spin_lock_irq(&tca->lock);
35462306a36Sopenharmony_ci	set = tca->reg_set;
35562306a36Sopenharmony_ci	memcpy(file, tca->reg_file, TCA6507_REG_CNT);
35662306a36Sopenharmony_ci	tca->reg_set = 0;
35762306a36Sopenharmony_ci	spin_unlock_irq(&tca->lock);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	for (r = 0; r < TCA6507_REG_CNT; r++)
36062306a36Sopenharmony_ci		if (set & (1<<r))
36162306a36Sopenharmony_ci			i2c_smbus_write_byte_data(cl, r, file[r]);
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic void led_release(struct tca6507_led *led)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	/* If led owns any resource, release it. */
36762306a36Sopenharmony_ci	struct tca6507_chip *tca = led->chip;
36862306a36Sopenharmony_ci	if (led->bank >= 0) {
36962306a36Sopenharmony_ci		struct bank *b = tca->bank + led->bank;
37062306a36Sopenharmony_ci		if (led->blink)
37162306a36Sopenharmony_ci			b->time_use--;
37262306a36Sopenharmony_ci		b->level_use--;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	led->blink = 0;
37562306a36Sopenharmony_ci	led->bank = -1;
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic int led_prepare(struct tca6507_led *led)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	/* Assign this led to a bank, configuring that bank if
38162306a36Sopenharmony_ci	 * necessary. */
38262306a36Sopenharmony_ci	int level = TO_LEVEL(led->led_cdev.brightness);
38362306a36Sopenharmony_ci	struct tca6507_chip *tca = led->chip;
38462306a36Sopenharmony_ci	int c1, c2;
38562306a36Sopenharmony_ci	int i;
38662306a36Sopenharmony_ci	struct bank *b;
38762306a36Sopenharmony_ci	int need_init = 0;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	led->led_cdev.brightness = TO_BRIGHT(level);
39062306a36Sopenharmony_ci	if (level == 0) {
39162306a36Sopenharmony_ci		set_select(tca, led->num, TCA6507_LS_LED_OFF);
39262306a36Sopenharmony_ci		return 0;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (led->ontime == 0 || led->offtime == 0) {
39662306a36Sopenharmony_ci		/*
39762306a36Sopenharmony_ci		 * Just set the brightness, choosing first usable
39862306a36Sopenharmony_ci		 * bank.  If none perfect, choose best.  Count
39962306a36Sopenharmony_ci		 * backwards so we check MASTER bank first to avoid
40062306a36Sopenharmony_ci		 * wasting a timer.
40162306a36Sopenharmony_ci		 */
40262306a36Sopenharmony_ci		int best = -1;/* full-on */
40362306a36Sopenharmony_ci		int diff = 15-level;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		if (level == 15) {
40662306a36Sopenharmony_ci			set_select(tca, led->num, TCA6507_LS_LED_ON);
40762306a36Sopenharmony_ci			return 0;
40862306a36Sopenharmony_ci		}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		for (i = MASTER; i >= BANK0; i--) {
41162306a36Sopenharmony_ci			int d;
41262306a36Sopenharmony_ci			if (tca->bank[i].level == level ||
41362306a36Sopenharmony_ci			    tca->bank[i].level_use == 0) {
41462306a36Sopenharmony_ci				best = i;
41562306a36Sopenharmony_ci				break;
41662306a36Sopenharmony_ci			}
41762306a36Sopenharmony_ci			d = abs(level - tca->bank[i].level);
41862306a36Sopenharmony_ci			if (d < diff) {
41962306a36Sopenharmony_ci				diff = d;
42062306a36Sopenharmony_ci				best = i;
42162306a36Sopenharmony_ci			}
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci		if (best == -1) {
42462306a36Sopenharmony_ci			/* Best brightness is full-on */
42562306a36Sopenharmony_ci			set_select(tca, led->num, TCA6507_LS_LED_ON);
42662306a36Sopenharmony_ci			led->led_cdev.brightness = LED_FULL;
42762306a36Sopenharmony_ci			return 0;
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		if (!tca->bank[best].level_use)
43162306a36Sopenharmony_ci			set_level(tca, best, level);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		tca->bank[best].level_use++;
43462306a36Sopenharmony_ci		led->bank = best;
43562306a36Sopenharmony_ci		set_select(tca, led->num, bank_source[best]);
43662306a36Sopenharmony_ci		led->led_cdev.brightness = TO_BRIGHT(tca->bank[best].level);
43762306a36Sopenharmony_ci		return 0;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/*
44162306a36Sopenharmony_ci	 * We have on/off time so we need to try to allocate a timing
44262306a36Sopenharmony_ci	 * bank.  First check if times are compatible with hardware
44362306a36Sopenharmony_ci	 * and give up if not.
44462306a36Sopenharmony_ci	 */
44562306a36Sopenharmony_ci	if (choose_times(led->ontime, &c1, &c2) < 0)
44662306a36Sopenharmony_ci		return -EINVAL;
44762306a36Sopenharmony_ci	if (choose_times(led->offtime, &c1, &c2) < 0)
44862306a36Sopenharmony_ci		return -EINVAL;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	for (i = BANK0; i <= BANK1; i++) {
45162306a36Sopenharmony_ci		if (tca->bank[i].level_use == 0)
45262306a36Sopenharmony_ci			/* not in use - it is ours! */
45362306a36Sopenharmony_ci			break;
45462306a36Sopenharmony_ci		if (tca->bank[i].level != level)
45562306a36Sopenharmony_ci			/* Incompatible level - skip */
45662306a36Sopenharmony_ci			/* FIX: if timer matches we maybe should consider
45762306a36Sopenharmony_ci			 * this anyway...
45862306a36Sopenharmony_ci			 */
45962306a36Sopenharmony_ci			continue;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		if (tca->bank[i].time_use == 0)
46262306a36Sopenharmony_ci			/* Timer not in use, and level matches - use it */
46362306a36Sopenharmony_ci			break;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		if (!(tca->bank[i].on_dflt ||
46662306a36Sopenharmony_ci		      led->on_dflt ||
46762306a36Sopenharmony_ci		      tca->bank[i].ontime == led->ontime))
46862306a36Sopenharmony_ci			/* on time is incompatible */
46962306a36Sopenharmony_ci			continue;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		if (!(tca->bank[i].off_dflt ||
47262306a36Sopenharmony_ci		      led->off_dflt ||
47362306a36Sopenharmony_ci		      tca->bank[i].offtime == led->offtime))
47462306a36Sopenharmony_ci			/* off time is incompatible */
47562306a36Sopenharmony_ci			continue;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		/* looks like a suitable match */
47862306a36Sopenharmony_ci		break;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (i > BANK1)
48262306a36Sopenharmony_ci		/* Nothing matches - how sad */
48362306a36Sopenharmony_ci		return -EINVAL;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	b = &tca->bank[i];
48662306a36Sopenharmony_ci	if (b->level_use == 0)
48762306a36Sopenharmony_ci		set_level(tca, i, level);
48862306a36Sopenharmony_ci	b->level_use++;
48962306a36Sopenharmony_ci	led->bank = i;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (b->on_dflt ||
49262306a36Sopenharmony_ci	    !led->on_dflt ||
49362306a36Sopenharmony_ci	    b->time_use == 0) {
49462306a36Sopenharmony_ci		b->ontime = led->ontime;
49562306a36Sopenharmony_ci		b->on_dflt = led->on_dflt;
49662306a36Sopenharmony_ci		need_init = 1;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (b->off_dflt ||
50062306a36Sopenharmony_ci	    !led->off_dflt ||
50162306a36Sopenharmony_ci	    b->time_use == 0) {
50262306a36Sopenharmony_ci		b->offtime = led->offtime;
50362306a36Sopenharmony_ci		b->off_dflt = led->off_dflt;
50462306a36Sopenharmony_ci		need_init = 1;
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (need_init)
50862306a36Sopenharmony_ci		set_times(tca, i);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	led->ontime = b->ontime;
51162306a36Sopenharmony_ci	led->offtime = b->offtime;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	b->time_use++;
51462306a36Sopenharmony_ci	led->blink = 1;
51562306a36Sopenharmony_ci	led->led_cdev.brightness = TO_BRIGHT(b->level);
51662306a36Sopenharmony_ci	set_select(tca, led->num, blink_source[i]);
51762306a36Sopenharmony_ci	return 0;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic int led_assign(struct tca6507_led *led)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct tca6507_chip *tca = led->chip;
52362306a36Sopenharmony_ci	int err;
52462306a36Sopenharmony_ci	unsigned long flags;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	spin_lock_irqsave(&tca->lock, flags);
52762306a36Sopenharmony_ci	led_release(led);
52862306a36Sopenharmony_ci	err = led_prepare(led);
52962306a36Sopenharmony_ci	if (err) {
53062306a36Sopenharmony_ci		/*
53162306a36Sopenharmony_ci		 * Can only fail on timer setup.  In that case we need
53262306a36Sopenharmony_ci		 * to re-establish as steady level.
53362306a36Sopenharmony_ci		 */
53462306a36Sopenharmony_ci		led->ontime = 0;
53562306a36Sopenharmony_ci		led->offtime = 0;
53662306a36Sopenharmony_ci		led_prepare(led);
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci	spin_unlock_irqrestore(&tca->lock, flags);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (tca->reg_set)
54162306a36Sopenharmony_ci		schedule_work(&tca->work);
54262306a36Sopenharmony_ci	return err;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic void tca6507_brightness_set(struct led_classdev *led_cdev,
54662306a36Sopenharmony_ci				   enum led_brightness brightness)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	struct tca6507_led *led = container_of(led_cdev, struct tca6507_led,
54962306a36Sopenharmony_ci					       led_cdev);
55062306a36Sopenharmony_ci	led->led_cdev.brightness = brightness;
55162306a36Sopenharmony_ci	led->ontime = 0;
55262306a36Sopenharmony_ci	led->offtime = 0;
55362306a36Sopenharmony_ci	led_assign(led);
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic int tca6507_blink_set(struct led_classdev *led_cdev,
55762306a36Sopenharmony_ci			     unsigned long *delay_on,
55862306a36Sopenharmony_ci			     unsigned long *delay_off)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	struct tca6507_led *led = container_of(led_cdev, struct tca6507_led,
56162306a36Sopenharmony_ci					       led_cdev);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if (*delay_on == 0)
56462306a36Sopenharmony_ci		led->on_dflt = 1;
56562306a36Sopenharmony_ci	else if (delay_on != &led_cdev->blink_delay_on)
56662306a36Sopenharmony_ci		led->on_dflt = 0;
56762306a36Sopenharmony_ci	led->ontime = *delay_on;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (*delay_off == 0)
57062306a36Sopenharmony_ci		led->off_dflt = 1;
57162306a36Sopenharmony_ci	else if (delay_off != &led_cdev->blink_delay_off)
57262306a36Sopenharmony_ci		led->off_dflt = 0;
57362306a36Sopenharmony_ci	led->offtime = *delay_off;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	if (led->ontime == 0)
57662306a36Sopenharmony_ci		led->ontime = 512;
57762306a36Sopenharmony_ci	if (led->offtime == 0)
57862306a36Sopenharmony_ci		led->offtime = 512;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	if (led->led_cdev.brightness == LED_OFF)
58162306a36Sopenharmony_ci		led->led_cdev.brightness = LED_FULL;
58262306a36Sopenharmony_ci	if (led_assign(led) < 0) {
58362306a36Sopenharmony_ci		led->ontime = 0;
58462306a36Sopenharmony_ci		led->offtime = 0;
58562306a36Sopenharmony_ci		led->led_cdev.brightness = LED_OFF;
58662306a36Sopenharmony_ci		return -EINVAL;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci	*delay_on = led->ontime;
58962306a36Sopenharmony_ci	*delay_off = led->offtime;
59062306a36Sopenharmony_ci	return 0;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci#ifdef CONFIG_GPIOLIB
59462306a36Sopenharmony_cistatic void tca6507_gpio_set_value(struct gpio_chip *gc,
59562306a36Sopenharmony_ci				   unsigned offset, int val)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	struct tca6507_chip *tca = gpiochip_get_data(gc);
59862306a36Sopenharmony_ci	unsigned long flags;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	spin_lock_irqsave(&tca->lock, flags);
60162306a36Sopenharmony_ci	/*
60262306a36Sopenharmony_ci	 * 'OFF' is floating high, and 'ON' is pulled down, so it has
60362306a36Sopenharmony_ci	 * the inverse sense of 'val'.
60462306a36Sopenharmony_ci	 */
60562306a36Sopenharmony_ci	set_select(tca, tca->gpio_map[offset],
60662306a36Sopenharmony_ci		   val ? TCA6507_LS_LED_OFF : TCA6507_LS_LED_ON);
60762306a36Sopenharmony_ci	spin_unlock_irqrestore(&tca->lock, flags);
60862306a36Sopenharmony_ci	if (tca->reg_set)
60962306a36Sopenharmony_ci		schedule_work(&tca->work);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int tca6507_gpio_direction_output(struct gpio_chip *gc,
61362306a36Sopenharmony_ci					  unsigned offset, int val)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	tca6507_gpio_set_value(gc, offset, val);
61662306a36Sopenharmony_ci	return 0;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic int tca6507_probe_gpios(struct device *dev,
62062306a36Sopenharmony_ci			       struct tca6507_chip *tca,
62162306a36Sopenharmony_ci			       struct tca6507_platform_data *pdata)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	int err;
62462306a36Sopenharmony_ci	int i = 0;
62562306a36Sopenharmony_ci	int gpios = 0;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	for (i = 0; i < NUM_LEDS; i++)
62862306a36Sopenharmony_ci		if (pdata->leds.leds[i].name && pdata->leds.leds[i].flags) {
62962306a36Sopenharmony_ci			/* Configure as a gpio */
63062306a36Sopenharmony_ci			tca->gpio_map[gpios] = i;
63162306a36Sopenharmony_ci			gpios++;
63262306a36Sopenharmony_ci		}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	if (!gpios)
63562306a36Sopenharmony_ci		return 0;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	tca->gpio.label = "gpio-tca6507";
63862306a36Sopenharmony_ci	tca->gpio.ngpio = gpios;
63962306a36Sopenharmony_ci	tca->gpio.base = pdata->gpio_base;
64062306a36Sopenharmony_ci	tca->gpio.owner = THIS_MODULE;
64162306a36Sopenharmony_ci	tca->gpio.direction_output = tca6507_gpio_direction_output;
64262306a36Sopenharmony_ci	tca->gpio.set = tca6507_gpio_set_value;
64362306a36Sopenharmony_ci	tca->gpio.parent = dev;
64462306a36Sopenharmony_ci	err = gpiochip_add_data(&tca->gpio, tca);
64562306a36Sopenharmony_ci	if (err) {
64662306a36Sopenharmony_ci		tca->gpio.ngpio = 0;
64762306a36Sopenharmony_ci		return err;
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci	return 0;
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic void tca6507_remove_gpio(struct tca6507_chip *tca)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	if (tca->gpio.ngpio)
65562306a36Sopenharmony_ci		gpiochip_remove(&tca->gpio);
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci#else /* CONFIG_GPIOLIB */
65862306a36Sopenharmony_cistatic int tca6507_probe_gpios(struct device *dev,
65962306a36Sopenharmony_ci			       struct tca6507_chip *tca,
66062306a36Sopenharmony_ci			       struct tca6507_platform_data *pdata)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	return 0;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_cistatic void tca6507_remove_gpio(struct tca6507_chip *tca)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci#endif /* CONFIG_GPIOLIB */
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic struct tca6507_platform_data *
67062306a36Sopenharmony_citca6507_led_dt_init(struct device *dev)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	struct tca6507_platform_data *pdata;
67362306a36Sopenharmony_ci	struct fwnode_handle *child;
67462306a36Sopenharmony_ci	struct led_info *tca_leds;
67562306a36Sopenharmony_ci	int count;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	count = device_get_child_node_count(dev);
67862306a36Sopenharmony_ci	if (!count || count > NUM_LEDS)
67962306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	tca_leds = devm_kcalloc(dev, NUM_LEDS, sizeof(struct led_info),
68262306a36Sopenharmony_ci				GFP_KERNEL);
68362306a36Sopenharmony_ci	if (!tca_leds)
68462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	device_for_each_child_node(dev, child) {
68762306a36Sopenharmony_ci		struct led_info led;
68862306a36Sopenharmony_ci		u32 reg;
68962306a36Sopenharmony_ci		int ret;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci		if (fwnode_property_read_string(child, "label", &led.name))
69262306a36Sopenharmony_ci			led.name = fwnode_get_name(child);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci		if (fwnode_property_read_string(child, "linux,default-trigger",
69562306a36Sopenharmony_ci						&led.default_trigger))
69662306a36Sopenharmony_ci			led.default_trigger = NULL;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		led.flags = 0;
69962306a36Sopenharmony_ci		if (fwnode_device_is_compatible(child, "gpio"))
70062306a36Sopenharmony_ci			led.flags |= TCA6507_MAKE_GPIO;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		ret = fwnode_property_read_u32(child, "reg", &reg);
70362306a36Sopenharmony_ci		if (ret || reg >= NUM_LEDS) {
70462306a36Sopenharmony_ci			fwnode_handle_put(child);
70562306a36Sopenharmony_ci			return ERR_PTR(ret ? : -EINVAL);
70662306a36Sopenharmony_ci		}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci		tca_leds[reg] = led;
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	pdata = devm_kzalloc(dev, sizeof(struct tca6507_platform_data),
71262306a36Sopenharmony_ci			     GFP_KERNEL);
71362306a36Sopenharmony_ci	if (!pdata)
71462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	pdata->leds.leds = tca_leds;
71762306a36Sopenharmony_ci	pdata->leds.num_leds = NUM_LEDS;
71862306a36Sopenharmony_ci#ifdef CONFIG_GPIOLIB
71962306a36Sopenharmony_ci	pdata->gpio_base = -1;
72062306a36Sopenharmony_ci#endif
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	return pdata;
72362306a36Sopenharmony_ci}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_cistatic const struct of_device_id __maybe_unused of_tca6507_leds_match[] = {
72662306a36Sopenharmony_ci	{ .compatible = "ti,tca6507", },
72762306a36Sopenharmony_ci	{},
72862306a36Sopenharmony_ci};
72962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_tca6507_leds_match);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic int tca6507_probe(struct i2c_client *client)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	struct device *dev = &client->dev;
73462306a36Sopenharmony_ci	struct i2c_adapter *adapter;
73562306a36Sopenharmony_ci	struct tca6507_chip *tca;
73662306a36Sopenharmony_ci	struct tca6507_platform_data *pdata;
73762306a36Sopenharmony_ci	int err;
73862306a36Sopenharmony_ci	int i = 0;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	adapter = client->adapter;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
74362306a36Sopenharmony_ci		return -EIO;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	pdata = tca6507_led_dt_init(dev);
74662306a36Sopenharmony_ci	if (IS_ERR(pdata)) {
74762306a36Sopenharmony_ci		dev_err(dev, "Need %d entries in platform-data list\n", NUM_LEDS);
74862306a36Sopenharmony_ci		return PTR_ERR(pdata);
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci	tca = devm_kzalloc(dev, sizeof(*tca), GFP_KERNEL);
75162306a36Sopenharmony_ci	if (!tca)
75262306a36Sopenharmony_ci		return -ENOMEM;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	tca->client = client;
75562306a36Sopenharmony_ci	INIT_WORK(&tca->work, tca6507_work);
75662306a36Sopenharmony_ci	spin_lock_init(&tca->lock);
75762306a36Sopenharmony_ci	i2c_set_clientdata(client, tca);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	for (i = 0; i < NUM_LEDS; i++) {
76062306a36Sopenharmony_ci		struct tca6507_led *l = tca->leds + i;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci		l->chip = tca;
76362306a36Sopenharmony_ci		l->num = i;
76462306a36Sopenharmony_ci		if (pdata->leds.leds[i].name && !pdata->leds.leds[i].flags) {
76562306a36Sopenharmony_ci			l->led_cdev.name = pdata->leds.leds[i].name;
76662306a36Sopenharmony_ci			l->led_cdev.default_trigger
76762306a36Sopenharmony_ci				= pdata->leds.leds[i].default_trigger;
76862306a36Sopenharmony_ci			l->led_cdev.brightness_set = tca6507_brightness_set;
76962306a36Sopenharmony_ci			l->led_cdev.blink_set = tca6507_blink_set;
77062306a36Sopenharmony_ci			l->bank = -1;
77162306a36Sopenharmony_ci			err = led_classdev_register(dev, &l->led_cdev);
77262306a36Sopenharmony_ci			if (err < 0)
77362306a36Sopenharmony_ci				goto exit;
77462306a36Sopenharmony_ci		}
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci	err = tca6507_probe_gpios(dev, tca, pdata);
77762306a36Sopenharmony_ci	if (err)
77862306a36Sopenharmony_ci		goto exit;
77962306a36Sopenharmony_ci	/* set all registers to known state - zero */
78062306a36Sopenharmony_ci	tca->reg_set = 0x7f;
78162306a36Sopenharmony_ci	schedule_work(&tca->work);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	return 0;
78462306a36Sopenharmony_ciexit:
78562306a36Sopenharmony_ci	while (i--) {
78662306a36Sopenharmony_ci		if (tca->leds[i].led_cdev.name)
78762306a36Sopenharmony_ci			led_classdev_unregister(&tca->leds[i].led_cdev);
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci	return err;
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_cistatic void tca6507_remove(struct i2c_client *client)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	int i;
79562306a36Sopenharmony_ci	struct tca6507_chip *tca = i2c_get_clientdata(client);
79662306a36Sopenharmony_ci	struct tca6507_led *tca_leds = tca->leds;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	for (i = 0; i < NUM_LEDS; i++) {
79962306a36Sopenharmony_ci		if (tca_leds[i].led_cdev.name)
80062306a36Sopenharmony_ci			led_classdev_unregister(&tca_leds[i].led_cdev);
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci	tca6507_remove_gpio(tca);
80362306a36Sopenharmony_ci	cancel_work_sync(&tca->work);
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic struct i2c_driver tca6507_driver = {
80762306a36Sopenharmony_ci	.driver   = {
80862306a36Sopenharmony_ci		.name    = "leds-tca6507",
80962306a36Sopenharmony_ci		.of_match_table = of_match_ptr(of_tca6507_leds_match),
81062306a36Sopenharmony_ci	},
81162306a36Sopenharmony_ci	.probe    = tca6507_probe,
81262306a36Sopenharmony_ci	.remove   = tca6507_remove,
81362306a36Sopenharmony_ci	.id_table = tca6507_id,
81462306a36Sopenharmony_ci};
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cimodule_i2c_driver(tca6507_driver);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ciMODULE_AUTHOR("NeilBrown <neilb@suse.de>");
81962306a36Sopenharmony_ciMODULE_DESCRIPTION("TCA6507 LED/GPO driver");
82062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
821