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-cpcap.c (5000B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (c) 2017 Sebastian Reichel <sre@kernel.org>
      4 */
      5
      6#include <linux/leds.h>
      7#include <linux/mfd/motorola-cpcap.h>
      8#include <linux/module.h>
      9#include <linux/mutex.h>
     10#include <linux/of_device.h>
     11#include <linux/platform_device.h>
     12#include <linux/regmap.h>
     13#include <linux/regulator/consumer.h>
     14
     15#define CPCAP_LED_NO_CURRENT 0x0001
     16
     17struct cpcap_led_info {
     18	u16 reg;
     19	u16 mask;
     20	u16 limit;
     21	u16 init_mask;
     22	u16 init_val;
     23};
     24
     25static const struct cpcap_led_info cpcap_led_red = {
     26	.reg	= CPCAP_REG_REDC,
     27	.mask	= 0x03FF,
     28	.limit	= 31,
     29};
     30
     31static const struct cpcap_led_info cpcap_led_green = {
     32	.reg	= CPCAP_REG_GREENC,
     33	.mask	= 0x03FF,
     34	.limit	= 31,
     35};
     36
     37static const struct cpcap_led_info cpcap_led_blue = {
     38	.reg	= CPCAP_REG_BLUEC,
     39	.mask	= 0x03FF,
     40	.limit	= 31,
     41};
     42
     43/* aux display light */
     44static const struct cpcap_led_info cpcap_led_adl = {
     45	.reg		= CPCAP_REG_ADLC,
     46	.mask		= 0x000F,
     47	.limit		= 1,
     48	.init_mask	= 0x7FFF,
     49	.init_val	= 0x5FF0,
     50};
     51
     52/* camera privacy led */
     53static const struct cpcap_led_info cpcap_led_cp = {
     54	.reg		= CPCAP_REG_CLEDC,
     55	.mask		= 0x0007,
     56	.limit		= 1,
     57	.init_mask	= 0x03FF,
     58	.init_val	= 0x0008,
     59};
     60
     61struct cpcap_led {
     62	struct led_classdev led;
     63	const struct cpcap_led_info *info;
     64	struct device *dev;
     65	struct regmap *regmap;
     66	struct mutex update_lock;
     67	struct regulator *vdd;
     68	bool powered;
     69
     70	u32 current_limit;
     71};
     72
     73static u16 cpcap_led_val(u8 current_limit, u8 duty_cycle)
     74{
     75	current_limit &= 0x1f; /* 5 bit */
     76	duty_cycle &= 0x0f; /* 4 bit */
     77
     78	return current_limit << 4 | duty_cycle;
     79}
     80
     81static int cpcap_led_set_power(struct cpcap_led *led, bool status)
     82{
     83	int err;
     84
     85	if (status == led->powered)
     86		return 0;
     87
     88	if (status)
     89		err = regulator_enable(led->vdd);
     90	else
     91		err = regulator_disable(led->vdd);
     92
     93	if (err) {
     94		dev_err(led->dev, "regulator failure: %d", err);
     95		return err;
     96	}
     97
     98	led->powered = status;
     99
    100	return 0;
    101}
    102
    103static int cpcap_led_set(struct led_classdev *ledc, enum led_brightness value)
    104{
    105	struct cpcap_led *led = container_of(ledc, struct cpcap_led, led);
    106	int brightness;
    107	int err;
    108
    109	mutex_lock(&led->update_lock);
    110
    111	if (value > LED_OFF) {
    112		err = cpcap_led_set_power(led, true);
    113		if (err)
    114			goto exit;
    115	}
    116
    117	if (value == LED_OFF) {
    118		/* Avoid HW issue by turning off current before duty cycle */
    119		err = regmap_update_bits(led->regmap,
    120			led->info->reg, led->info->mask, CPCAP_LED_NO_CURRENT);
    121		if (err) {
    122			dev_err(led->dev, "regmap failed: %d", err);
    123			goto exit;
    124		}
    125
    126		brightness = cpcap_led_val(value, LED_OFF);
    127	} else {
    128		brightness = cpcap_led_val(value, LED_ON);
    129	}
    130
    131	err = regmap_update_bits(led->regmap, led->info->reg, led->info->mask,
    132		brightness);
    133	if (err) {
    134		dev_err(led->dev, "regmap failed: %d", err);
    135		goto exit;
    136	}
    137
    138	if (value == LED_OFF) {
    139		err = cpcap_led_set_power(led, false);
    140		if (err)
    141			goto exit;
    142	}
    143
    144exit:
    145	mutex_unlock(&led->update_lock);
    146	return err;
    147}
    148
    149static const struct of_device_id cpcap_led_of_match[] = {
    150	{ .compatible = "motorola,cpcap-led-red", .data = &cpcap_led_red },
    151	{ .compatible = "motorola,cpcap-led-green", .data = &cpcap_led_green },
    152	{ .compatible = "motorola,cpcap-led-blue",  .data = &cpcap_led_blue },
    153	{ .compatible = "motorola,cpcap-led-adl", .data = &cpcap_led_adl },
    154	{ .compatible = "motorola,cpcap-led-cp", .data = &cpcap_led_cp },
    155	{},
    156};
    157MODULE_DEVICE_TABLE(of, cpcap_led_of_match);
    158
    159static int cpcap_led_probe(struct platform_device *pdev)
    160{
    161	struct cpcap_led *led;
    162	int err;
    163
    164	led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
    165	if (!led)
    166		return -ENOMEM;
    167	platform_set_drvdata(pdev, led);
    168	led->info = device_get_match_data(&pdev->dev);
    169	led->dev = &pdev->dev;
    170
    171	if (led->info->reg == 0x0000) {
    172		dev_err(led->dev, "Unsupported LED");
    173		return -ENODEV;
    174	}
    175
    176	led->regmap = dev_get_regmap(pdev->dev.parent, NULL);
    177	if (!led->regmap)
    178		return -ENODEV;
    179
    180	led->vdd = devm_regulator_get(&pdev->dev, "vdd");
    181	if (IS_ERR(led->vdd)) {
    182		err = PTR_ERR(led->vdd);
    183		dev_err(led->dev, "Couldn't get regulator: %d", err);
    184		return err;
    185	}
    186
    187	err = device_property_read_string(&pdev->dev, "label", &led->led.name);
    188	if (err) {
    189		dev_err(led->dev, "Couldn't read LED label: %d", err);
    190		return err;
    191	}
    192
    193	if (led->info->init_mask) {
    194		err = regmap_update_bits(led->regmap, led->info->reg,
    195			led->info->init_mask, led->info->init_val);
    196		if (err) {
    197			dev_err(led->dev, "regmap failed: %d", err);
    198			return err;
    199		}
    200	}
    201
    202	mutex_init(&led->update_lock);
    203
    204	led->led.max_brightness = led->info->limit;
    205	led->led.brightness_set_blocking = cpcap_led_set;
    206	err = devm_led_classdev_register(&pdev->dev, &led->led);
    207	if (err) {
    208		dev_err(led->dev, "Couldn't register LED: %d", err);
    209		return err;
    210	}
    211
    212	return 0;
    213}
    214
    215static struct platform_driver cpcap_led_driver = {
    216	.probe = cpcap_led_probe,
    217	.driver = {
    218		.name = "cpcap-led",
    219		.of_match_table = cpcap_led_of_match,
    220	},
    221};
    222module_platform_driver(cpcap_led_driver);
    223
    224MODULE_DESCRIPTION("CPCAP LED driver");
    225MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
    226MODULE_LICENSE("GPL");