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", ®); 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