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-wm8350.c (5132B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * LED driver for WM8350 driven LEDS.
      4 *
      5 * Copyright(C) 2007, 2008 Wolfson Microelectronics PLC.
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/platform_device.h>
     10#include <linux/leds.h>
     11#include <linux/err.h>
     12#include <linux/mfd/wm8350/pmic.h>
     13#include <linux/regulator/consumer.h>
     14#include <linux/slab.h>
     15#include <linux/module.h>
     16
     17/* Microamps */
     18static const int isink_cur[] = {
     19	4,
     20	5,
     21	6,
     22	7,
     23	8,
     24	10,
     25	11,
     26	14,
     27	16,
     28	19,
     29	23,
     30	27,
     31	32,
     32	39,
     33	46,
     34	54,
     35	65,
     36	77,
     37	92,
     38	109,
     39	130,
     40	154,
     41	183,
     42	218,
     43	259,
     44	308,
     45	367,
     46	436,
     47	518,
     48	616,
     49	733,
     50	872,
     51	1037,
     52	1233,
     53	1466,
     54	1744,
     55	2073,
     56	2466,
     57	2933,
     58	3487,
     59	4147,
     60	4932,
     61	5865,
     62	6975,
     63	8294,
     64	9864,
     65	11730,
     66	13949,
     67	16589,
     68	19728,
     69	23460,
     70	27899,
     71	33178,
     72	39455,
     73	46920,
     74	55798,
     75	66355,
     76	78910,
     77	93840,
     78	111596,
     79	132710,
     80	157820,
     81	187681,
     82	223191
     83};
     84
     85#define to_wm8350_led(led_cdev) \
     86	container_of(led_cdev, struct wm8350_led, cdev)
     87
     88static int wm8350_led_enable(struct wm8350_led *led)
     89{
     90	int ret = 0;
     91
     92	if (led->enabled)
     93		return ret;
     94
     95	ret = regulator_enable(led->isink);
     96	if (ret != 0) {
     97		dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret);
     98		return ret;
     99	}
    100
    101	ret = regulator_enable(led->dcdc);
    102	if (ret != 0) {
    103		dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret);
    104		regulator_disable(led->isink);
    105		return ret;
    106	}
    107
    108	led->enabled = 1;
    109
    110	return ret;
    111}
    112
    113static int wm8350_led_disable(struct wm8350_led *led)
    114{
    115	int ret = 0;
    116
    117	if (!led->enabled)
    118		return ret;
    119
    120	ret = regulator_disable(led->dcdc);
    121	if (ret != 0) {
    122		dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret);
    123		return ret;
    124	}
    125
    126	ret = regulator_disable(led->isink);
    127	if (ret != 0) {
    128		dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret);
    129		ret = regulator_enable(led->dcdc);
    130		if (ret != 0)
    131			dev_err(led->cdev.dev, "Failed to reenable DCDC: %d\n",
    132				ret);
    133		return ret;
    134	}
    135
    136	led->enabled = 0;
    137
    138	return ret;
    139}
    140
    141static int wm8350_led_set(struct led_classdev *led_cdev,
    142			   enum led_brightness value)
    143{
    144	struct wm8350_led *led = to_wm8350_led(led_cdev);
    145	unsigned long flags;
    146	int ret;
    147	int uA;
    148
    149	led->value = value;
    150
    151	spin_lock_irqsave(&led->value_lock, flags);
    152
    153	if (led->value == LED_OFF) {
    154		spin_unlock_irqrestore(&led->value_lock, flags);
    155		return wm8350_led_disable(led);
    156	}
    157
    158	/* This scales linearly into the index of valid current
    159	 * settings which results in a linear scaling of perceived
    160	 * brightness due to the non-linear current settings provided
    161	 * by the hardware.
    162	 */
    163	uA = (led->max_uA_index * led->value) / LED_FULL;
    164	spin_unlock_irqrestore(&led->value_lock, flags);
    165	BUG_ON(uA >= ARRAY_SIZE(isink_cur));
    166
    167	ret = regulator_set_current_limit(led->isink, isink_cur[uA],
    168					  isink_cur[uA]);
    169	if (ret != 0) {
    170		dev_err(led->cdev.dev, "Failed to set %duA: %d\n",
    171			isink_cur[uA], ret);
    172		return ret;
    173	}
    174
    175	return wm8350_led_enable(led);
    176}
    177
    178static void wm8350_led_shutdown(struct platform_device *pdev)
    179{
    180	struct wm8350_led *led = platform_get_drvdata(pdev);
    181
    182	led->value = LED_OFF;
    183	wm8350_led_disable(led);
    184}
    185
    186static int wm8350_led_probe(struct platform_device *pdev)
    187{
    188	struct regulator *isink, *dcdc;
    189	struct wm8350_led *led;
    190	struct wm8350_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
    191	int i;
    192
    193	if (pdata == NULL) {
    194		dev_err(&pdev->dev, "no platform data\n");
    195		return -ENODEV;
    196	}
    197
    198	if (pdata->max_uA < isink_cur[0]) {
    199		dev_err(&pdev->dev, "Invalid maximum current %duA\n",
    200			pdata->max_uA);
    201		return -EINVAL;
    202	}
    203
    204	isink = devm_regulator_get(&pdev->dev, "led_isink");
    205	if (IS_ERR(isink)) {
    206		dev_err(&pdev->dev, "%s: can't get ISINK\n", __func__);
    207		return PTR_ERR(isink);
    208	}
    209
    210	dcdc = devm_regulator_get(&pdev->dev, "led_vcc");
    211	if (IS_ERR(dcdc)) {
    212		dev_err(&pdev->dev, "%s: can't get DCDC\n", __func__);
    213		return PTR_ERR(dcdc);
    214	}
    215
    216	led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
    217	if (led == NULL)
    218		return -ENOMEM;
    219
    220	led->cdev.brightness_set_blocking = wm8350_led_set;
    221	led->cdev.default_trigger = pdata->default_trigger;
    222	led->cdev.name = pdata->name;
    223	led->cdev.flags |= LED_CORE_SUSPENDRESUME;
    224	led->enabled = regulator_is_enabled(isink);
    225	led->isink = isink;
    226	led->dcdc = dcdc;
    227
    228	for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++)
    229		if (isink_cur[i] >= pdata->max_uA)
    230			break;
    231	led->max_uA_index = i;
    232	if (pdata->max_uA != isink_cur[i])
    233		dev_warn(&pdev->dev,
    234			 "Maximum current %duA is not directly supported,"
    235			 " check platform data\n",
    236			 pdata->max_uA);
    237
    238	spin_lock_init(&led->value_lock);
    239	led->value = LED_OFF;
    240	platform_set_drvdata(pdev, led);
    241
    242	return led_classdev_register(&pdev->dev, &led->cdev);
    243}
    244
    245static int wm8350_led_remove(struct platform_device *pdev)
    246{
    247	struct wm8350_led *led = platform_get_drvdata(pdev);
    248
    249	led_classdev_unregister(&led->cdev);
    250	wm8350_led_disable(led);
    251	return 0;
    252}
    253
    254static struct platform_driver wm8350_led_driver = {
    255	.driver = {
    256		   .name = "wm8350-led",
    257		   },
    258	.probe = wm8350_led_probe,
    259	.remove = wm8350_led_remove,
    260	.shutdown = wm8350_led_shutdown,
    261};
    262
    263module_platform_driver(wm8350_led_driver);
    264
    265MODULE_AUTHOR("Mark Brown");
    266MODULE_DESCRIPTION("WM8350 LED driver");
    267MODULE_LICENSE("GPL");
    268MODULE_ALIAS("platform:wm8350-led");