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-lp5562.c (14929B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * LP5562 LED driver
      4 *
      5 * Copyright (C) 2013 Texas Instruments
      6 *
      7 * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
      8 */
      9
     10#include <linux/delay.h>
     11#include <linux/firmware.h>
     12#include <linux/i2c.h>
     13#include <linux/leds.h>
     14#include <linux/module.h>
     15#include <linux/mutex.h>
     16#include <linux/of.h>
     17#include <linux/platform_data/leds-lp55xx.h>
     18#include <linux/slab.h>
     19
     20#include "leds-lp55xx-common.h"
     21
     22#define LP5562_PROGRAM_LENGTH		32
     23#define LP5562_MAX_LEDS			4
     24
     25/* ENABLE Register 00h */
     26#define LP5562_REG_ENABLE		0x00
     27#define LP5562_EXEC_ENG1_M		0x30
     28#define LP5562_EXEC_ENG2_M		0x0C
     29#define LP5562_EXEC_ENG3_M		0x03
     30#define LP5562_EXEC_M			0x3F
     31#define LP5562_MASTER_ENABLE		0x40	/* Chip master enable */
     32#define LP5562_LOGARITHMIC_PWM		0x80	/* Logarithmic PWM adjustment */
     33#define LP5562_EXEC_RUN			0x2A
     34#define LP5562_ENABLE_DEFAULT	\
     35	(LP5562_MASTER_ENABLE | LP5562_LOGARITHMIC_PWM)
     36#define LP5562_ENABLE_RUN_PROGRAM	\
     37	(LP5562_ENABLE_DEFAULT | LP5562_EXEC_RUN)
     38
     39/* OPMODE Register 01h */
     40#define LP5562_REG_OP_MODE		0x01
     41#define LP5562_MODE_ENG1_M		0x30
     42#define LP5562_MODE_ENG2_M		0x0C
     43#define LP5562_MODE_ENG3_M		0x03
     44#define LP5562_LOAD_ENG1		0x10
     45#define LP5562_LOAD_ENG2		0x04
     46#define LP5562_LOAD_ENG3		0x01
     47#define LP5562_RUN_ENG1			0x20
     48#define LP5562_RUN_ENG2			0x08
     49#define LP5562_RUN_ENG3			0x02
     50#define LP5562_ENG1_IS_LOADING(mode)	\
     51	((mode & LP5562_MODE_ENG1_M) == LP5562_LOAD_ENG1)
     52#define LP5562_ENG2_IS_LOADING(mode)	\
     53	((mode & LP5562_MODE_ENG2_M) == LP5562_LOAD_ENG2)
     54#define LP5562_ENG3_IS_LOADING(mode)	\
     55	((mode & LP5562_MODE_ENG3_M) == LP5562_LOAD_ENG3)
     56
     57/* BRIGHTNESS Registers */
     58#define LP5562_REG_R_PWM		0x04
     59#define LP5562_REG_G_PWM		0x03
     60#define LP5562_REG_B_PWM		0x02
     61#define LP5562_REG_W_PWM		0x0E
     62
     63/* CURRENT Registers */
     64#define LP5562_REG_R_CURRENT		0x07
     65#define LP5562_REG_G_CURRENT		0x06
     66#define LP5562_REG_B_CURRENT		0x05
     67#define LP5562_REG_W_CURRENT		0x0F
     68
     69/* CONFIG Register 08h */
     70#define LP5562_REG_CONFIG		0x08
     71#define LP5562_PWM_HF			0x40
     72#define LP5562_PWRSAVE_EN		0x20
     73#define LP5562_CLK_INT			0x01	/* Internal clock */
     74#define LP5562_DEFAULT_CFG		(LP5562_PWM_HF | LP5562_PWRSAVE_EN)
     75
     76/* RESET Register 0Dh */
     77#define LP5562_REG_RESET		0x0D
     78#define LP5562_RESET			0xFF
     79
     80/* PROGRAM ENGINE Registers */
     81#define LP5562_REG_PROG_MEM_ENG1	0x10
     82#define LP5562_REG_PROG_MEM_ENG2	0x30
     83#define LP5562_REG_PROG_MEM_ENG3	0x50
     84
     85/* LEDMAP Register 70h */
     86#define LP5562_REG_ENG_SEL		0x70
     87#define LP5562_ENG_SEL_PWM		0
     88#define LP5562_ENG_FOR_RGB_M		0x3F
     89#define LP5562_ENG_SEL_RGB		0x1B	/* R:ENG1, G:ENG2, B:ENG3 */
     90#define LP5562_ENG_FOR_W_M		0xC0
     91#define LP5562_ENG1_FOR_W		0x40	/* W:ENG1 */
     92#define LP5562_ENG2_FOR_W		0x80	/* W:ENG2 */
     93#define LP5562_ENG3_FOR_W		0xC0	/* W:ENG3 */
     94
     95/* Program Commands */
     96#define LP5562_CMD_DISABLE		0x00
     97#define LP5562_CMD_LOAD			0x15
     98#define LP5562_CMD_RUN			0x2A
     99#define LP5562_CMD_DIRECT		0x3F
    100#define LP5562_PATTERN_OFF		0
    101
    102static inline void lp5562_wait_opmode_done(void)
    103{
    104	/* operation mode change needs to be longer than 153 us */
    105	usleep_range(200, 300);
    106}
    107
    108static inline void lp5562_wait_enable_done(void)
    109{
    110	/* it takes more 488 us to update ENABLE register */
    111	usleep_range(500, 600);
    112}
    113
    114static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current)
    115{
    116	static const u8 addr[] = {
    117		LP5562_REG_R_CURRENT,
    118		LP5562_REG_G_CURRENT,
    119		LP5562_REG_B_CURRENT,
    120		LP5562_REG_W_CURRENT,
    121	};
    122
    123	led->led_current = led_current;
    124	lp55xx_write(led->chip, addr[led->chan_nr], led_current);
    125}
    126
    127static void lp5562_load_engine(struct lp55xx_chip *chip)
    128{
    129	enum lp55xx_engine_index idx = chip->engine_idx;
    130	static const u8 mask[] = {
    131		[LP55XX_ENGINE_1] = LP5562_MODE_ENG1_M,
    132		[LP55XX_ENGINE_2] = LP5562_MODE_ENG2_M,
    133		[LP55XX_ENGINE_3] = LP5562_MODE_ENG3_M,
    134	};
    135
    136	static const u8 val[] = {
    137		[LP55XX_ENGINE_1] = LP5562_LOAD_ENG1,
    138		[LP55XX_ENGINE_2] = LP5562_LOAD_ENG2,
    139		[LP55XX_ENGINE_3] = LP5562_LOAD_ENG3,
    140	};
    141
    142	lp55xx_update_bits(chip, LP5562_REG_OP_MODE, mask[idx], val[idx]);
    143
    144	lp5562_wait_opmode_done();
    145}
    146
    147static void lp5562_stop_engine(struct lp55xx_chip *chip)
    148{
    149	lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DISABLE);
    150	lp5562_wait_opmode_done();
    151}
    152
    153static void lp5562_run_engine(struct lp55xx_chip *chip, bool start)
    154{
    155	int ret;
    156	u8 mode;
    157	u8 exec;
    158
    159	/* stop engine */
    160	if (!start) {
    161		lp55xx_write(chip, LP5562_REG_ENABLE, LP5562_ENABLE_DEFAULT);
    162		lp5562_wait_enable_done();
    163		lp5562_stop_engine(chip);
    164		lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM);
    165		lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT);
    166		lp5562_wait_opmode_done();
    167		return;
    168	}
    169
    170	/*
    171	 * To run the engine,
    172	 * operation mode and enable register should updated at the same time
    173	 */
    174
    175	ret = lp55xx_read(chip, LP5562_REG_OP_MODE, &mode);
    176	if (ret)
    177		return;
    178
    179	ret = lp55xx_read(chip, LP5562_REG_ENABLE, &exec);
    180	if (ret)
    181		return;
    182
    183	/* change operation mode to RUN only when each engine is loading */
    184	if (LP5562_ENG1_IS_LOADING(mode)) {
    185		mode = (mode & ~LP5562_MODE_ENG1_M) | LP5562_RUN_ENG1;
    186		exec = (exec & ~LP5562_EXEC_ENG1_M) | LP5562_RUN_ENG1;
    187	}
    188
    189	if (LP5562_ENG2_IS_LOADING(mode)) {
    190		mode = (mode & ~LP5562_MODE_ENG2_M) | LP5562_RUN_ENG2;
    191		exec = (exec & ~LP5562_EXEC_ENG2_M) | LP5562_RUN_ENG2;
    192	}
    193
    194	if (LP5562_ENG3_IS_LOADING(mode)) {
    195		mode = (mode & ~LP5562_MODE_ENG3_M) | LP5562_RUN_ENG3;
    196		exec = (exec & ~LP5562_EXEC_ENG3_M) | LP5562_RUN_ENG3;
    197	}
    198
    199	lp55xx_write(chip, LP5562_REG_OP_MODE, mode);
    200	lp5562_wait_opmode_done();
    201
    202	lp55xx_update_bits(chip, LP5562_REG_ENABLE, LP5562_EXEC_M, exec);
    203	lp5562_wait_enable_done();
    204}
    205
    206static int lp5562_update_firmware(struct lp55xx_chip *chip,
    207					const u8 *data, size_t size)
    208{
    209	enum lp55xx_engine_index idx = chip->engine_idx;
    210	u8 pattern[LP5562_PROGRAM_LENGTH] = {0};
    211	static const u8 addr[] = {
    212		[LP55XX_ENGINE_1] = LP5562_REG_PROG_MEM_ENG1,
    213		[LP55XX_ENGINE_2] = LP5562_REG_PROG_MEM_ENG2,
    214		[LP55XX_ENGINE_3] = LP5562_REG_PROG_MEM_ENG3,
    215	};
    216	unsigned cmd;
    217	char c[3];
    218	int program_size;
    219	int nrchars;
    220	int offset = 0;
    221	int ret;
    222	int i;
    223
    224	/* clear program memory before updating */
    225	for (i = 0; i < LP5562_PROGRAM_LENGTH; i++)
    226		lp55xx_write(chip, addr[idx] + i, 0);
    227
    228	i = 0;
    229	while ((offset < size - 1) && (i < LP5562_PROGRAM_LENGTH)) {
    230		/* separate sscanfs because length is working only for %s */
    231		ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
    232		if (ret != 1)
    233			goto err;
    234
    235		ret = sscanf(c, "%2x", &cmd);
    236		if (ret != 1)
    237			goto err;
    238
    239		pattern[i] = (u8)cmd;
    240		offset += nrchars;
    241		i++;
    242	}
    243
    244	/* Each instruction is 16bit long. Check that length is even */
    245	if (i % 2)
    246		goto err;
    247
    248	program_size = i;
    249	for (i = 0; i < program_size; i++)
    250		lp55xx_write(chip, addr[idx] + i, pattern[i]);
    251
    252	return 0;
    253
    254err:
    255	dev_err(&chip->cl->dev, "wrong pattern format\n");
    256	return -EINVAL;
    257}
    258
    259static void lp5562_firmware_loaded(struct lp55xx_chip *chip)
    260{
    261	const struct firmware *fw = chip->fw;
    262
    263	/*
    264	 * the firmware is encoded in ascii hex character, with 2 chars
    265	 * per byte
    266	 */
    267	if (fw->size > (LP5562_PROGRAM_LENGTH * 2)) {
    268		dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
    269			fw->size);
    270		return;
    271	}
    272
    273	/*
    274	 * Program memory sequence
    275	 *  1) set engine mode to "LOAD"
    276	 *  2) write firmware data into program memory
    277	 */
    278
    279	lp5562_load_engine(chip);
    280	lp5562_update_firmware(chip, fw->data, fw->size);
    281}
    282
    283static int lp5562_post_init_device(struct lp55xx_chip *chip)
    284{
    285	int ret;
    286	u8 cfg = LP5562_DEFAULT_CFG;
    287
    288	/* Set all PWMs to direct control mode */
    289	ret = lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT);
    290	if (ret)
    291		return ret;
    292
    293	lp5562_wait_opmode_done();
    294
    295	/* Update configuration for the clock setting */
    296	if (!lp55xx_is_extclk_used(chip))
    297		cfg |= LP5562_CLK_INT;
    298
    299	ret = lp55xx_write(chip, LP5562_REG_CONFIG, cfg);
    300	if (ret)
    301		return ret;
    302
    303	/* Initialize all channels PWM to zero -> leds off */
    304	lp55xx_write(chip, LP5562_REG_R_PWM, 0);
    305	lp55xx_write(chip, LP5562_REG_G_PWM, 0);
    306	lp55xx_write(chip, LP5562_REG_B_PWM, 0);
    307	lp55xx_write(chip, LP5562_REG_W_PWM, 0);
    308
    309	/* Set LED map as register PWM by default */
    310	lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM);
    311
    312	return 0;
    313}
    314
    315static int lp5562_led_brightness(struct lp55xx_led *led)
    316{
    317	struct lp55xx_chip *chip = led->chip;
    318	static const u8 addr[] = {
    319		LP5562_REG_R_PWM,
    320		LP5562_REG_G_PWM,
    321		LP5562_REG_B_PWM,
    322		LP5562_REG_W_PWM,
    323	};
    324	int ret;
    325
    326	mutex_lock(&chip->lock);
    327	ret = lp55xx_write(chip, addr[led->chan_nr], led->brightness);
    328	mutex_unlock(&chip->lock);
    329
    330	return ret;
    331}
    332
    333static void lp5562_write_program_memory(struct lp55xx_chip *chip,
    334					u8 base, const u8 *rgb, int size)
    335{
    336	int i;
    337
    338	if (!rgb || size <= 0)
    339		return;
    340
    341	for (i = 0; i < size; i++)
    342		lp55xx_write(chip, base + i, *(rgb + i));
    343
    344	lp55xx_write(chip, base + i, 0);
    345	lp55xx_write(chip, base + i + 1, 0);
    346}
    347
    348/* check the size of program count */
    349static inline bool _is_pc_overflow(struct lp55xx_predef_pattern *ptn)
    350{
    351	return ptn->size_r >= LP5562_PROGRAM_LENGTH ||
    352	       ptn->size_g >= LP5562_PROGRAM_LENGTH ||
    353	       ptn->size_b >= LP5562_PROGRAM_LENGTH;
    354}
    355
    356static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode)
    357{
    358	struct lp55xx_predef_pattern *ptn;
    359	int i;
    360
    361	if (mode == LP5562_PATTERN_OFF) {
    362		lp5562_run_engine(chip, false);
    363		return 0;
    364	}
    365
    366	ptn = chip->pdata->patterns + (mode - 1);
    367	if (!ptn || _is_pc_overflow(ptn)) {
    368		dev_err(&chip->cl->dev, "invalid pattern data\n");
    369		return -EINVAL;
    370	}
    371
    372	lp5562_stop_engine(chip);
    373
    374	/* Set LED map as RGB */
    375	lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_RGB);
    376
    377	/* Load engines */
    378	for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) {
    379		chip->engine_idx = i;
    380		lp5562_load_engine(chip);
    381	}
    382
    383	/* Clear program registers */
    384	lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1, 0);
    385	lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1 + 1, 0);
    386	lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2, 0);
    387	lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2 + 1, 0);
    388	lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3, 0);
    389	lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3 + 1, 0);
    390
    391	/* Program engines */
    392	lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG1,
    393				ptn->r, ptn->size_r);
    394	lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG2,
    395				ptn->g, ptn->size_g);
    396	lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG3,
    397				ptn->b, ptn->size_b);
    398
    399	/* Run engines */
    400	lp5562_run_engine(chip, true);
    401
    402	return 0;
    403}
    404
    405static ssize_t lp5562_store_pattern(struct device *dev,
    406				struct device_attribute *attr,
    407				const char *buf, size_t len)
    408{
    409	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
    410	struct lp55xx_chip *chip = led->chip;
    411	struct lp55xx_predef_pattern *ptn = chip->pdata->patterns;
    412	int num_patterns = chip->pdata->num_patterns;
    413	unsigned long mode;
    414	int ret;
    415
    416	ret = kstrtoul(buf, 0, &mode);
    417	if (ret)
    418		return ret;
    419
    420	if (mode > num_patterns || !ptn)
    421		return -EINVAL;
    422
    423	mutex_lock(&chip->lock);
    424	ret = lp5562_run_predef_led_pattern(chip, mode);
    425	mutex_unlock(&chip->lock);
    426
    427	if (ret)
    428		return ret;
    429
    430	return len;
    431}
    432
    433static ssize_t lp5562_store_engine_mux(struct device *dev,
    434				     struct device_attribute *attr,
    435				     const char *buf, size_t len)
    436{
    437	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
    438	struct lp55xx_chip *chip = led->chip;
    439	u8 mask;
    440	u8 val;
    441
    442	/* LED map
    443	 * R ... Engine 1 (fixed)
    444	 * G ... Engine 2 (fixed)
    445	 * B ... Engine 3 (fixed)
    446	 * W ... Engine 1 or 2 or 3
    447	 */
    448
    449	if (sysfs_streq(buf, "RGB")) {
    450		mask = LP5562_ENG_FOR_RGB_M;
    451		val = LP5562_ENG_SEL_RGB;
    452	} else if (sysfs_streq(buf, "W")) {
    453		enum lp55xx_engine_index idx = chip->engine_idx;
    454
    455		mask = LP5562_ENG_FOR_W_M;
    456		switch (idx) {
    457		case LP55XX_ENGINE_1:
    458			val = LP5562_ENG1_FOR_W;
    459			break;
    460		case LP55XX_ENGINE_2:
    461			val = LP5562_ENG2_FOR_W;
    462			break;
    463		case LP55XX_ENGINE_3:
    464			val = LP5562_ENG3_FOR_W;
    465			break;
    466		default:
    467			return -EINVAL;
    468		}
    469
    470	} else {
    471		dev_err(dev, "choose RGB or W\n");
    472		return -EINVAL;
    473	}
    474
    475	mutex_lock(&chip->lock);
    476	lp55xx_update_bits(chip, LP5562_REG_ENG_SEL, mask, val);
    477	mutex_unlock(&chip->lock);
    478
    479	return len;
    480}
    481
    482static LP55XX_DEV_ATTR_WO(led_pattern, lp5562_store_pattern);
    483static LP55XX_DEV_ATTR_WO(engine_mux, lp5562_store_engine_mux);
    484
    485static struct attribute *lp5562_attributes[] = {
    486	&dev_attr_led_pattern.attr,
    487	&dev_attr_engine_mux.attr,
    488	NULL,
    489};
    490
    491static const struct attribute_group lp5562_group = {
    492	.attrs = lp5562_attributes,
    493};
    494
    495/* Chip specific configurations */
    496static struct lp55xx_device_config lp5562_cfg = {
    497	.max_channel  = LP5562_MAX_LEDS,
    498	.reset = {
    499		.addr = LP5562_REG_RESET,
    500		.val  = LP5562_RESET,
    501	},
    502	.enable = {
    503		.addr = LP5562_REG_ENABLE,
    504		.val  = LP5562_ENABLE_DEFAULT,
    505	},
    506	.post_init_device   = lp5562_post_init_device,
    507	.set_led_current    = lp5562_set_led_current,
    508	.brightness_fn      = lp5562_led_brightness,
    509	.run_engine         = lp5562_run_engine,
    510	.firmware_cb        = lp5562_firmware_loaded,
    511	.dev_attr_group     = &lp5562_group,
    512};
    513
    514static int lp5562_probe(struct i2c_client *client,
    515			const struct i2c_device_id *id)
    516{
    517	int ret;
    518	struct lp55xx_chip *chip;
    519	struct lp55xx_led *led;
    520	struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
    521	struct device_node *np = dev_of_node(&client->dev);
    522
    523	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
    524	if (!chip)
    525		return -ENOMEM;
    526
    527	chip->cfg = &lp5562_cfg;
    528
    529	if (!pdata) {
    530		if (np) {
    531			pdata = lp55xx_of_populate_pdata(&client->dev, np,
    532							 chip);
    533			if (IS_ERR(pdata))
    534				return PTR_ERR(pdata);
    535		} else {
    536			dev_err(&client->dev, "no platform data\n");
    537			return -EINVAL;
    538		}
    539	}
    540
    541
    542	led = devm_kcalloc(&client->dev,
    543			pdata->num_channels, sizeof(*led), GFP_KERNEL);
    544	if (!led)
    545		return -ENOMEM;
    546
    547	chip->cl = client;
    548	chip->pdata = pdata;
    549
    550	mutex_init(&chip->lock);
    551
    552	i2c_set_clientdata(client, led);
    553
    554	ret = lp55xx_init_device(chip);
    555	if (ret)
    556		goto err_init;
    557
    558	ret = lp55xx_register_leds(led, chip);
    559	if (ret)
    560		goto err_out;
    561
    562	ret = lp55xx_register_sysfs(chip);
    563	if (ret) {
    564		dev_err(&client->dev, "registering sysfs failed\n");
    565		goto err_out;
    566	}
    567
    568	return 0;
    569
    570err_out:
    571	lp55xx_deinit_device(chip);
    572err_init:
    573	return ret;
    574}
    575
    576static int lp5562_remove(struct i2c_client *client)
    577{
    578	struct lp55xx_led *led = i2c_get_clientdata(client);
    579	struct lp55xx_chip *chip = led->chip;
    580
    581	lp5562_stop_engine(chip);
    582
    583	lp55xx_unregister_sysfs(chip);
    584	lp55xx_deinit_device(chip);
    585
    586	return 0;
    587}
    588
    589static const struct i2c_device_id lp5562_id[] = {
    590	{ "lp5562", 0 },
    591	{ }
    592};
    593MODULE_DEVICE_TABLE(i2c, lp5562_id);
    594
    595#ifdef CONFIG_OF
    596static const struct of_device_id of_lp5562_leds_match[] = {
    597	{ .compatible = "ti,lp5562", },
    598	{},
    599};
    600
    601MODULE_DEVICE_TABLE(of, of_lp5562_leds_match);
    602#endif
    603
    604static struct i2c_driver lp5562_driver = {
    605	.driver = {
    606		.name	= "lp5562",
    607		.of_match_table = of_match_ptr(of_lp5562_leds_match),
    608	},
    609	.probe		= lp5562_probe,
    610	.remove		= lp5562_remove,
    611	.id_table	= lp5562_id,
    612};
    613
    614module_i2c_driver(lp5562_driver);
    615
    616MODULE_DESCRIPTION("Texas Instruments LP5562 LED Driver");
    617MODULE_AUTHOR("Milo Kim");
    618MODULE_LICENSE("GPL");