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-ktd2692.c (11291B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * LED driver : leds-ktd2692.c
      4 *
      5 * Copyright (C) 2015 Samsung Electronics
      6 * Ingi Kim <ingi2.kim@samsung.com>
      7 */
      8
      9#include <linux/delay.h>
     10#include <linux/err.h>
     11#include <linux/gpio/consumer.h>
     12#include <linux/led-class-flash.h>
     13#include <linux/module.h>
     14#include <linux/mutex.h>
     15#include <linux/of.h>
     16#include <linux/platform_device.h>
     17#include <linux/regulator/consumer.h>
     18
     19/* Value related the movie mode */
     20#define KTD2692_MOVIE_MODE_CURRENT_LEVELS	16
     21#define KTD2692_MM_TO_FL_RATIO(x)		((x) / 3)
     22#define KTD2692_MM_MIN_CURR_THRESHOLD_SCALE	8
     23
     24/* Value related the flash mode */
     25#define KTD2692_FLASH_MODE_TIMEOUT_LEVELS	8
     26#define KTD2692_FLASH_MODE_TIMEOUT_DISABLE	0
     27#define KTD2692_FLASH_MODE_CURR_PERCENT(x)	(((x) * 16) / 100)
     28
     29/* Macro for getting offset of flash timeout */
     30#define GET_TIMEOUT_OFFSET(timeout, step)	((timeout) / (step))
     31
     32/* Base register address */
     33#define KTD2692_REG_LVP_BASE			0x00
     34#define KTD2692_REG_FLASH_TIMEOUT_BASE		0x20
     35#define KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE	0x40
     36#define KTD2692_REG_MOVIE_CURRENT_BASE		0x60
     37#define KTD2692_REG_FLASH_CURRENT_BASE		0x80
     38#define KTD2692_REG_MODE_BASE			0xA0
     39
     40/* Set bit coding time for expresswire interface */
     41#define KTD2692_TIME_RESET_US			700
     42#define KTD2692_TIME_DATA_START_TIME_US		10
     43#define KTD2692_TIME_HIGH_END_OF_DATA_US	350
     44#define KTD2692_TIME_LOW_END_OF_DATA_US		10
     45#define KTD2692_TIME_SHORT_BITSET_US		4
     46#define KTD2692_TIME_LONG_BITSET_US		12
     47
     48/* KTD2692 default length of name */
     49#define KTD2692_NAME_LENGTH			20
     50
     51enum ktd2692_bitset {
     52	KTD2692_LOW = 0,
     53	KTD2692_HIGH,
     54};
     55
     56/* Movie / Flash Mode Control */
     57enum ktd2692_led_mode {
     58	KTD2692_MODE_DISABLE = 0,	/* default */
     59	KTD2692_MODE_MOVIE,
     60	KTD2692_MODE_FLASH,
     61};
     62
     63struct ktd2692_led_config_data {
     64	/* maximum LED current in movie mode */
     65	u32 movie_max_microamp;
     66	/* maximum LED current in flash mode */
     67	u32 flash_max_microamp;
     68	/* maximum flash timeout */
     69	u32 flash_max_timeout;
     70	/* max LED brightness level */
     71	enum led_brightness max_brightness;
     72};
     73
     74struct ktd2692_context {
     75	/* Related LED Flash class device */
     76	struct led_classdev_flash fled_cdev;
     77
     78	/* secures access to the device */
     79	struct mutex lock;
     80	struct regulator *regulator;
     81
     82	struct gpio_desc *aux_gpio;
     83	struct gpio_desc *ctrl_gpio;
     84
     85	enum ktd2692_led_mode mode;
     86	enum led_brightness torch_brightness;
     87};
     88
     89static struct ktd2692_context *fled_cdev_to_led(
     90				struct led_classdev_flash *fled_cdev)
     91{
     92	return container_of(fled_cdev, struct ktd2692_context, fled_cdev);
     93}
     94
     95static void ktd2692_expresswire_start(struct ktd2692_context *led)
     96{
     97	gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH);
     98	udelay(KTD2692_TIME_DATA_START_TIME_US);
     99}
    100
    101static void ktd2692_expresswire_reset(struct ktd2692_context *led)
    102{
    103	gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW);
    104	udelay(KTD2692_TIME_RESET_US);
    105}
    106
    107static void ktd2692_expresswire_end(struct ktd2692_context *led)
    108{
    109	gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW);
    110	udelay(KTD2692_TIME_LOW_END_OF_DATA_US);
    111	gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH);
    112	udelay(KTD2692_TIME_HIGH_END_OF_DATA_US);
    113}
    114
    115static void ktd2692_expresswire_set_bit(struct ktd2692_context *led, bool bit)
    116{
    117	/*
    118	 * The Low Bit(0) and High Bit(1) is based on a time detection
    119	 * algorithm between time low and time high
    120	 * Time_(L_LB) : Low time of the Low Bit(0)
    121	 * Time_(H_LB) : High time of the LOW Bit(0)
    122	 * Time_(L_HB) : Low time of the High Bit(1)
    123	 * Time_(H_HB) : High time of the High Bit(1)
    124	 *
    125	 * It can be simplified to:
    126	 * Low Bit(0) : 2 * Time_(H_LB) < Time_(L_LB)
    127	 * High Bit(1) : 2 * Time_(L_HB) < Time_(H_HB)
    128	 * HIGH  ___           ____    _..     _________    ___
    129	 *          |_________|    |_..  |____|         |__|
    130	 * LOW        <L_LB>  <H_LB>     <L_HB>  <H_HB>
    131	 *          [  Low Bit (0) ]     [  High Bit(1) ]
    132	 */
    133	if (bit) {
    134		gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW);
    135		udelay(KTD2692_TIME_SHORT_BITSET_US);
    136		gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH);
    137		udelay(KTD2692_TIME_LONG_BITSET_US);
    138	} else {
    139		gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW);
    140		udelay(KTD2692_TIME_LONG_BITSET_US);
    141		gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH);
    142		udelay(KTD2692_TIME_SHORT_BITSET_US);
    143	}
    144}
    145
    146static void ktd2692_expresswire_write(struct ktd2692_context *led, u8 value)
    147{
    148	int i;
    149
    150	ktd2692_expresswire_start(led);
    151	for (i = 7; i >= 0; i--)
    152		ktd2692_expresswire_set_bit(led, value & BIT(i));
    153	ktd2692_expresswire_end(led);
    154}
    155
    156static int ktd2692_led_brightness_set(struct led_classdev *led_cdev,
    157				       enum led_brightness brightness)
    158{
    159	struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
    160	struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
    161
    162	mutex_lock(&led->lock);
    163
    164	if (brightness == LED_OFF) {
    165		led->mode = KTD2692_MODE_DISABLE;
    166		gpiod_direction_output(led->aux_gpio, KTD2692_LOW);
    167	} else {
    168		ktd2692_expresswire_write(led, brightness |
    169					KTD2692_REG_MOVIE_CURRENT_BASE);
    170		led->mode = KTD2692_MODE_MOVIE;
    171	}
    172
    173	ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE);
    174	mutex_unlock(&led->lock);
    175
    176	return 0;
    177}
    178
    179static int ktd2692_led_flash_strobe_set(struct led_classdev_flash *fled_cdev,
    180					bool state)
    181{
    182	struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
    183	struct led_flash_setting *timeout = &fled_cdev->timeout;
    184	u32 flash_tm_reg;
    185
    186	mutex_lock(&led->lock);
    187
    188	if (state) {
    189		flash_tm_reg = GET_TIMEOUT_OFFSET(timeout->val, timeout->step);
    190		ktd2692_expresswire_write(led, flash_tm_reg
    191				| KTD2692_REG_FLASH_TIMEOUT_BASE);
    192
    193		led->mode = KTD2692_MODE_FLASH;
    194		gpiod_direction_output(led->aux_gpio, KTD2692_HIGH);
    195	} else {
    196		led->mode = KTD2692_MODE_DISABLE;
    197		gpiod_direction_output(led->aux_gpio, KTD2692_LOW);
    198	}
    199
    200	ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE);
    201
    202	fled_cdev->led_cdev.brightness = LED_OFF;
    203	led->mode = KTD2692_MODE_DISABLE;
    204
    205	mutex_unlock(&led->lock);
    206
    207	return 0;
    208}
    209
    210static int ktd2692_led_flash_timeout_set(struct led_classdev_flash *fled_cdev,
    211					 u32 timeout)
    212{
    213	return 0;
    214}
    215
    216static void ktd2692_init_movie_current_max(struct ktd2692_led_config_data *cfg)
    217{
    218	u32 offset, step;
    219	u32 movie_current_microamp;
    220
    221	offset = KTD2692_MOVIE_MODE_CURRENT_LEVELS;
    222	step = KTD2692_MM_TO_FL_RATIO(cfg->flash_max_microamp)
    223		/ KTD2692_MOVIE_MODE_CURRENT_LEVELS;
    224
    225	do {
    226		movie_current_microamp = step * offset;
    227		offset--;
    228	} while ((movie_current_microamp > cfg->movie_max_microamp) &&
    229		(offset > 0));
    230
    231	cfg->max_brightness = offset;
    232}
    233
    234static void ktd2692_init_flash_timeout(struct led_classdev_flash *fled_cdev,
    235				       struct ktd2692_led_config_data *cfg)
    236{
    237	struct led_flash_setting *setting;
    238
    239	setting = &fled_cdev->timeout;
    240	setting->min = KTD2692_FLASH_MODE_TIMEOUT_DISABLE;
    241	setting->max = cfg->flash_max_timeout;
    242	setting->step = cfg->flash_max_timeout
    243			/ (KTD2692_FLASH_MODE_TIMEOUT_LEVELS - 1);
    244	setting->val = cfg->flash_max_timeout;
    245}
    246
    247static void ktd2692_setup(struct ktd2692_context *led)
    248{
    249	led->mode = KTD2692_MODE_DISABLE;
    250	ktd2692_expresswire_reset(led);
    251	gpiod_direction_output(led->aux_gpio, KTD2692_LOW);
    252
    253	ktd2692_expresswire_write(led, (KTD2692_MM_MIN_CURR_THRESHOLD_SCALE - 1)
    254				 | KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE);
    255	ktd2692_expresswire_write(led, KTD2692_FLASH_MODE_CURR_PERCENT(45)
    256				 | KTD2692_REG_FLASH_CURRENT_BASE);
    257}
    258
    259static void regulator_disable_action(void *_data)
    260{
    261	struct device *dev = _data;
    262	struct ktd2692_context *led = dev_get_drvdata(dev);
    263	int ret;
    264
    265	ret = regulator_disable(led->regulator);
    266	if (ret)
    267		dev_err(dev, "Failed to disable supply: %d\n", ret);
    268}
    269
    270static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
    271			    struct ktd2692_led_config_data *cfg)
    272{
    273	struct device_node *np = dev_of_node(dev);
    274	struct device_node *child_node;
    275	int ret;
    276
    277	if (!np)
    278		return -ENXIO;
    279
    280	led->ctrl_gpio = devm_gpiod_get(dev, "ctrl", GPIOD_ASIS);
    281	ret = PTR_ERR_OR_ZERO(led->ctrl_gpio);
    282	if (ret)
    283		return dev_err_probe(dev, ret, "cannot get ctrl-gpios\n");
    284
    285	led->aux_gpio = devm_gpiod_get_optional(dev, "aux", GPIOD_ASIS);
    286	if (IS_ERR(led->aux_gpio))
    287		return dev_err_probe(dev, PTR_ERR(led->aux_gpio), "cannot get aux-gpios\n");
    288
    289	led->regulator = devm_regulator_get(dev, "vin");
    290	if (IS_ERR(led->regulator))
    291		led->regulator = NULL;
    292
    293	if (led->regulator) {
    294		ret = regulator_enable(led->regulator);
    295		if (ret) {
    296			dev_err(dev, "Failed to enable supply: %d\n", ret);
    297		} else {
    298			ret = devm_add_action_or_reset(dev,
    299						regulator_disable_action, dev);
    300			if (ret)
    301				return ret;
    302		}
    303	}
    304
    305	child_node = of_get_next_available_child(np, NULL);
    306	if (!child_node) {
    307		dev_err(dev, "No DT child node found for connected LED.\n");
    308		return -EINVAL;
    309	}
    310
    311	led->fled_cdev.led_cdev.name =
    312		of_get_property(child_node, "label", NULL) ? : child_node->name;
    313
    314	ret = of_property_read_u32(child_node, "led-max-microamp",
    315				   &cfg->movie_max_microamp);
    316	if (ret) {
    317		dev_err(dev, "failed to parse led-max-microamp\n");
    318		goto err_parse_dt;
    319	}
    320
    321	ret = of_property_read_u32(child_node, "flash-max-microamp",
    322				   &cfg->flash_max_microamp);
    323	if (ret) {
    324		dev_err(dev, "failed to parse flash-max-microamp\n");
    325		goto err_parse_dt;
    326	}
    327
    328	ret = of_property_read_u32(child_node, "flash-max-timeout-us",
    329				   &cfg->flash_max_timeout);
    330	if (ret) {
    331		dev_err(dev, "failed to parse flash-max-timeout-us\n");
    332		goto err_parse_dt;
    333	}
    334
    335err_parse_dt:
    336	of_node_put(child_node);
    337	return ret;
    338}
    339
    340static const struct led_flash_ops flash_ops = {
    341	.strobe_set = ktd2692_led_flash_strobe_set,
    342	.timeout_set = ktd2692_led_flash_timeout_set,
    343};
    344
    345static int ktd2692_probe(struct platform_device *pdev)
    346{
    347	struct ktd2692_context *led;
    348	struct led_classdev *led_cdev;
    349	struct led_classdev_flash *fled_cdev;
    350	struct ktd2692_led_config_data led_cfg;
    351	int ret;
    352
    353	led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
    354	if (!led)
    355		return -ENOMEM;
    356
    357	fled_cdev = &led->fled_cdev;
    358	led_cdev = &fled_cdev->led_cdev;
    359
    360	ret = ktd2692_parse_dt(led, &pdev->dev, &led_cfg);
    361	if (ret)
    362		return ret;
    363
    364	ktd2692_init_flash_timeout(fled_cdev, &led_cfg);
    365	ktd2692_init_movie_current_max(&led_cfg);
    366
    367	fled_cdev->ops = &flash_ops;
    368
    369	led_cdev->max_brightness = led_cfg.max_brightness;
    370	led_cdev->brightness_set_blocking = ktd2692_led_brightness_set;
    371	led_cdev->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH;
    372
    373	mutex_init(&led->lock);
    374
    375	platform_set_drvdata(pdev, led);
    376
    377	ret = led_classdev_flash_register(&pdev->dev, fled_cdev);
    378	if (ret) {
    379		dev_err(&pdev->dev, "can't register LED %s\n", led_cdev->name);
    380		mutex_destroy(&led->lock);
    381		return ret;
    382	}
    383
    384	ktd2692_setup(led);
    385
    386	return 0;
    387}
    388
    389static int ktd2692_remove(struct platform_device *pdev)
    390{
    391	struct ktd2692_context *led = platform_get_drvdata(pdev);
    392
    393	led_classdev_flash_unregister(&led->fled_cdev);
    394
    395	mutex_destroy(&led->lock);
    396
    397	return 0;
    398}
    399
    400static const struct of_device_id ktd2692_match[] = {
    401	{ .compatible = "kinetic,ktd2692", },
    402	{ /* sentinel */ },
    403};
    404MODULE_DEVICE_TABLE(of, ktd2692_match);
    405
    406static struct platform_driver ktd2692_driver = {
    407	.driver = {
    408		.name  = "ktd2692",
    409		.of_match_table = ktd2692_match,
    410	},
    411	.probe  = ktd2692_probe,
    412	.remove = ktd2692_remove,
    413};
    414
    415module_platform_driver(ktd2692_driver);
    416
    417MODULE_AUTHOR("Ingi Kim <ingi2.kim@samsung.com>");
    418MODULE_DESCRIPTION("Kinetic KTD2692 LED driver");
    419MODULE_LICENSE("GPL v2");