cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

leds-an30259a.c (8834B)


      1// SPDX-License-Identifier: GPL-2.0+
      2//
      3// Driver for Panasonic AN30259A 3-channel LED driver
      4//
      5// Copyright (c) 2018 Simon Shields <simon@lineageos.org>
      6//
      7// Datasheet:
      8// https://www.alliedelec.com/m/d/a9d2b3ee87c2d1a535a41dd747b1c247.pdf
      9
     10#include <linux/i2c.h>
     11#include <linux/leds.h>
     12#include <linux/module.h>
     13#include <linux/mutex.h>
     14#include <linux/of.h>
     15#include <linux/regmap.h>
     16
     17#define AN30259A_MAX_LEDS 3
     18
     19#define AN30259A_REG_SRESET 0x00
     20#define AN30259A_LED_SRESET BIT(0)
     21
     22/* LED power registers */
     23#define AN30259A_REG_LED_ON 0x01
     24#define AN30259A_LED_EN(x) BIT((x) - 1)
     25#define AN30259A_LED_SLOPE(x) BIT(((x) - 1) + 4)
     26
     27#define AN30259A_REG_LEDCC(x) (0x03 + ((x) - 1))
     28
     29/* slope control registers */
     30#define AN30259A_REG_SLOPE(x) (0x06 + ((x) - 1))
     31#define AN30259A_LED_SLOPETIME1(x) (x)
     32#define AN30259A_LED_SLOPETIME2(x) ((x) << 4)
     33
     34#define AN30259A_REG_LEDCNT1(x) (0x09 + (4 * ((x) - 1)))
     35#define AN30259A_LED_DUTYMAX(x) ((x) << 4)
     36#define AN30259A_LED_DUTYMID(x) (x)
     37
     38#define AN30259A_REG_LEDCNT2(x) (0x0A + (4 * ((x) - 1)))
     39#define AN30259A_LED_DELAY(x) ((x) << 4)
     40#define AN30259A_LED_DUTYMIN(x) (x)
     41
     42/* detention time control (length of each slope step) */
     43#define AN30259A_REG_LEDCNT3(x) (0x0B + (4 * ((x) - 1)))
     44#define AN30259A_LED_DT1(x) (x)
     45#define AN30259A_LED_DT2(x) ((x) << 4)
     46
     47#define AN30259A_REG_LEDCNT4(x) (0x0C + (4 * ((x) - 1)))
     48#define AN30259A_LED_DT3(x) (x)
     49#define AN30259A_LED_DT4(x) ((x) << 4)
     50
     51#define AN30259A_REG_MAX 0x14
     52
     53#define AN30259A_BLINK_MAX_TIME 7500 /* ms */
     54#define AN30259A_SLOPE_RESOLUTION 500 /* ms */
     55
     56#define AN30259A_NAME "an30259a"
     57
     58#define STATE_OFF 0
     59#define STATE_KEEP 1
     60#define STATE_ON 2
     61
     62struct an30259a;
     63
     64struct an30259a_led {
     65	struct an30259a *chip;
     66	struct fwnode_handle *fwnode;
     67	struct led_classdev cdev;
     68	u32 num;
     69	u32 default_state;
     70	bool sloping;
     71};
     72
     73struct an30259a {
     74	struct mutex mutex; /* held when writing to registers */
     75	struct i2c_client *client;
     76	struct an30259a_led leds[AN30259A_MAX_LEDS];
     77	struct regmap *regmap;
     78	int num_leds;
     79};
     80
     81static int an30259a_brightness_set(struct led_classdev *cdev,
     82				   enum led_brightness brightness)
     83{
     84	struct an30259a_led *led;
     85	int ret;
     86	unsigned int led_on;
     87
     88	led = container_of(cdev, struct an30259a_led, cdev);
     89	mutex_lock(&led->chip->mutex);
     90
     91	ret = regmap_read(led->chip->regmap, AN30259A_REG_LED_ON, &led_on);
     92	if (ret)
     93		goto error;
     94
     95	switch (brightness) {
     96	case LED_OFF:
     97		led_on &= ~AN30259A_LED_EN(led->num);
     98		led_on &= ~AN30259A_LED_SLOPE(led->num);
     99		led->sloping = false;
    100		break;
    101	default:
    102		led_on |= AN30259A_LED_EN(led->num);
    103		if (led->sloping)
    104			led_on |= AN30259A_LED_SLOPE(led->num);
    105		ret = regmap_write(led->chip->regmap,
    106				   AN30259A_REG_LEDCNT1(led->num),
    107				   AN30259A_LED_DUTYMAX(0xf) |
    108				   AN30259A_LED_DUTYMID(0xf));
    109		if (ret)
    110			goto error;
    111		break;
    112	}
    113
    114	ret = regmap_write(led->chip->regmap, AN30259A_REG_LED_ON, led_on);
    115	if (ret)
    116		goto error;
    117
    118	ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCC(led->num),
    119			   brightness);
    120
    121error:
    122	mutex_unlock(&led->chip->mutex);
    123
    124	return ret;
    125}
    126
    127static int an30259a_blink_set(struct led_classdev *cdev,
    128			      unsigned long *delay_off, unsigned long *delay_on)
    129{
    130	struct an30259a_led *led;
    131	int ret, num;
    132	unsigned int led_on;
    133	unsigned long off = *delay_off, on = *delay_on;
    134
    135	led = container_of(cdev, struct an30259a_led, cdev);
    136
    137	mutex_lock(&led->chip->mutex);
    138	num = led->num;
    139
    140	/* slope time can only be a multiple of 500ms. */
    141	if (off % AN30259A_SLOPE_RESOLUTION || on % AN30259A_SLOPE_RESOLUTION) {
    142		ret = -EINVAL;
    143		goto error;
    144	}
    145
    146	/* up to a maximum of 7500ms. */
    147	if (off > AN30259A_BLINK_MAX_TIME || on > AN30259A_BLINK_MAX_TIME) {
    148		ret = -EINVAL;
    149		goto error;
    150	}
    151
    152	/* if no blink specified, default to 1 Hz. */
    153	if (!off && !on) {
    154		*delay_off = off = 500;
    155		*delay_on = on = 500;
    156	}
    157
    158	/* convert into values the HW will understand. */
    159	off /= AN30259A_SLOPE_RESOLUTION;
    160	on /= AN30259A_SLOPE_RESOLUTION;
    161
    162	/* duty min should be zero (=off), delay should be zero. */
    163	ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCNT2(num),
    164			   AN30259A_LED_DELAY(0) | AN30259A_LED_DUTYMIN(0));
    165	if (ret)
    166		goto error;
    167
    168	/* reset detention time (no "breathing" effect). */
    169	ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCNT3(num),
    170			   AN30259A_LED_DT1(0) | AN30259A_LED_DT2(0));
    171	if (ret)
    172		goto error;
    173	ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCNT4(num),
    174			   AN30259A_LED_DT3(0) | AN30259A_LED_DT4(0));
    175	if (ret)
    176		goto error;
    177
    178	/* slope time controls on/off cycle length. */
    179	ret = regmap_write(led->chip->regmap, AN30259A_REG_SLOPE(num),
    180			   AN30259A_LED_SLOPETIME1(off) |
    181			   AN30259A_LED_SLOPETIME2(on));
    182	if (ret)
    183		goto error;
    184
    185	/* Finally, enable slope mode. */
    186	ret = regmap_read(led->chip->regmap, AN30259A_REG_LED_ON, &led_on);
    187	if (ret)
    188		goto error;
    189
    190	led_on |= AN30259A_LED_SLOPE(num) | AN30259A_LED_EN(led->num);
    191
    192	ret = regmap_write(led->chip->regmap, AN30259A_REG_LED_ON, led_on);
    193
    194	if (!ret)
    195		led->sloping = true;
    196error:
    197	mutex_unlock(&led->chip->mutex);
    198
    199	return ret;
    200}
    201
    202static int an30259a_dt_init(struct i2c_client *client,
    203			    struct an30259a *chip)
    204{
    205	struct device_node *np = dev_of_node(&client->dev), *child;
    206	int count, ret;
    207	int i = 0;
    208	const char *str;
    209	struct an30259a_led *led;
    210
    211	count = of_get_available_child_count(np);
    212	if (!count || count > AN30259A_MAX_LEDS)
    213		return -EINVAL;
    214
    215	for_each_available_child_of_node(np, child) {
    216		u32 source;
    217
    218		ret = of_property_read_u32(child, "reg", &source);
    219		if (ret != 0 || !source || source > AN30259A_MAX_LEDS) {
    220			dev_err(&client->dev, "Couldn't read LED address: %d\n",
    221				ret);
    222			count--;
    223			continue;
    224		}
    225
    226		led = &chip->leds[i];
    227
    228		led->num = source;
    229		led->chip = chip;
    230		led->fwnode = of_fwnode_handle(child);
    231
    232		if (!of_property_read_string(child, "default-state", &str)) {
    233			if (!strcmp(str, "on"))
    234				led->default_state = STATE_ON;
    235			else if (!strcmp(str, "keep"))
    236				led->default_state = STATE_KEEP;
    237			else
    238				led->default_state = STATE_OFF;
    239		}
    240
    241		i++;
    242	}
    243
    244	if (!count)
    245		return -EINVAL;
    246
    247	chip->num_leds = i;
    248
    249	return 0;
    250}
    251
    252static const struct regmap_config an30259a_regmap_config = {
    253	.reg_bits = 8,
    254	.val_bits = 8,
    255	.max_register = AN30259A_REG_MAX,
    256};
    257
    258static void an30259a_init_default_state(struct an30259a_led *led)
    259{
    260	struct an30259a *chip = led->chip;
    261	int led_on, err;
    262
    263	switch (led->default_state) {
    264	case STATE_ON:
    265		led->cdev.brightness = LED_FULL;
    266		break;
    267	case STATE_KEEP:
    268		err = regmap_read(chip->regmap, AN30259A_REG_LED_ON, &led_on);
    269		if (err)
    270			break;
    271
    272		if (!(led_on & AN30259A_LED_EN(led->num))) {
    273			led->cdev.brightness = LED_OFF;
    274			break;
    275		}
    276		regmap_read(chip->regmap, AN30259A_REG_LEDCC(led->num),
    277			    &led->cdev.brightness);
    278		break;
    279	default:
    280		led->cdev.brightness = LED_OFF;
    281	}
    282
    283	an30259a_brightness_set(&led->cdev, led->cdev.brightness);
    284}
    285
    286static int an30259a_probe(struct i2c_client *client)
    287{
    288	struct an30259a *chip;
    289	int i, err;
    290
    291	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
    292	if (!chip)
    293		return -ENOMEM;
    294
    295	err = an30259a_dt_init(client, chip);
    296	if (err < 0)
    297		return err;
    298
    299	mutex_init(&chip->mutex);
    300	chip->client = client;
    301	i2c_set_clientdata(client, chip);
    302
    303	chip->regmap = devm_regmap_init_i2c(client, &an30259a_regmap_config);
    304
    305	if (IS_ERR(chip->regmap)) {
    306		err = PTR_ERR(chip->regmap);
    307		dev_err(&client->dev, "Failed to allocate register map: %d\n",
    308			err);
    309		goto exit;
    310	}
    311
    312	for (i = 0; i < chip->num_leds; i++) {
    313		struct led_init_data init_data = {};
    314
    315		an30259a_init_default_state(&chip->leds[i]);
    316		chip->leds[i].cdev.brightness_set_blocking =
    317			an30259a_brightness_set;
    318		chip->leds[i].cdev.blink_set = an30259a_blink_set;
    319
    320		init_data.fwnode = chip->leds[i].fwnode;
    321		init_data.devicename = AN30259A_NAME;
    322		init_data.default_label = ":";
    323
    324		err = devm_led_classdev_register_ext(&client->dev,
    325						 &chip->leds[i].cdev,
    326						 &init_data);
    327		if (err < 0)
    328			goto exit;
    329	}
    330	return 0;
    331
    332exit:
    333	mutex_destroy(&chip->mutex);
    334	return err;
    335}
    336
    337static int an30259a_remove(struct i2c_client *client)
    338{
    339	struct an30259a *chip = i2c_get_clientdata(client);
    340
    341	mutex_destroy(&chip->mutex);
    342
    343	return 0;
    344}
    345
    346static const struct of_device_id an30259a_match_table[] = {
    347	{ .compatible = "panasonic,an30259a", },
    348	{ /* sentinel */ },
    349};
    350
    351MODULE_DEVICE_TABLE(of, an30259a_match_table);
    352
    353static const struct i2c_device_id an30259a_id[] = {
    354	{ "an30259a", 0 },
    355	{ /* sentinel */ },
    356};
    357MODULE_DEVICE_TABLE(i2c, an30259a_id);
    358
    359static struct i2c_driver an30259a_driver = {
    360	.driver = {
    361		.name = "leds-an30259a",
    362		.of_match_table = of_match_ptr(an30259a_match_table),
    363	},
    364	.probe_new = an30259a_probe,
    365	.remove = an30259a_remove,
    366	.id_table = an30259a_id,
    367};
    368
    369module_i2c_driver(an30259a_driver);
    370
    371MODULE_AUTHOR("Simon Shields <simon@lineageos.org>");
    372MODULE_DESCRIPTION("AN30259A LED driver");
    373MODULE_LICENSE("GPL v2");