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-pm8058.c (4331B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* Copyright (c) 2010, 2011, 2016 The Linux Foundation. All rights reserved.
      3 */
      4#include <linux/leds.h>
      5#include <linux/module.h>
      6#include <linux/of.h>
      7#include <linux/of_device.h>
      8#include <linux/platform_device.h>
      9#include <linux/pm.h>
     10#include <linux/regmap.h>
     11
     12#define PM8058_LED_TYPE_COMMON	0x00
     13#define PM8058_LED_TYPE_KEYPAD	0x01
     14#define PM8058_LED_TYPE_FLASH	0x02
     15
     16#define PM8058_LED_TYPE_COMMON_MASK	0xf8
     17#define PM8058_LED_TYPE_KEYPAD_MASK	0xf0
     18#define PM8058_LED_TYPE_COMMON_SHIFT	3
     19#define PM8058_LED_TYPE_KEYPAD_SHIFT	4
     20
     21struct pm8058_led {
     22	struct regmap *map;
     23	u32 reg;
     24	u32 ledtype;
     25	struct led_classdev cdev;
     26};
     27
     28static void pm8058_led_set(struct led_classdev *cled,
     29	enum led_brightness value)
     30{
     31	struct pm8058_led *led;
     32	int ret = 0;
     33	unsigned int mask = 0;
     34	unsigned int val = 0;
     35
     36	led = container_of(cled, struct pm8058_led, cdev);
     37	switch (led->ledtype) {
     38	case PM8058_LED_TYPE_COMMON:
     39		mask = PM8058_LED_TYPE_COMMON_MASK;
     40		val = value << PM8058_LED_TYPE_COMMON_SHIFT;
     41		break;
     42	case PM8058_LED_TYPE_KEYPAD:
     43	case PM8058_LED_TYPE_FLASH:
     44		mask = PM8058_LED_TYPE_KEYPAD_MASK;
     45		val = value << PM8058_LED_TYPE_KEYPAD_SHIFT;
     46		break;
     47	default:
     48		break;
     49	}
     50
     51	ret = regmap_update_bits(led->map, led->reg, mask, val);
     52	if (ret)
     53		pr_err("Failed to set LED brightness\n");
     54}
     55
     56static enum led_brightness pm8058_led_get(struct led_classdev *cled)
     57{
     58	struct pm8058_led *led;
     59	int ret;
     60	unsigned int val;
     61
     62	led = container_of(cled, struct pm8058_led, cdev);
     63
     64	ret = regmap_read(led->map, led->reg, &val);
     65	if (ret) {
     66		pr_err("Failed to get LED brightness\n");
     67		return LED_OFF;
     68	}
     69
     70	switch (led->ledtype) {
     71	case PM8058_LED_TYPE_COMMON:
     72		val &= PM8058_LED_TYPE_COMMON_MASK;
     73		val >>= PM8058_LED_TYPE_COMMON_SHIFT;
     74		break;
     75	case PM8058_LED_TYPE_KEYPAD:
     76	case PM8058_LED_TYPE_FLASH:
     77		val &= PM8058_LED_TYPE_KEYPAD_MASK;
     78		val >>= PM8058_LED_TYPE_KEYPAD_SHIFT;
     79		break;
     80	default:
     81		val = LED_OFF;
     82		break;
     83	}
     84
     85	return val;
     86}
     87
     88static int pm8058_led_probe(struct platform_device *pdev)
     89{
     90	struct led_init_data init_data = {};
     91	struct device *dev = &pdev->dev;
     92	struct pm8058_led *led;
     93	struct device_node *np;
     94	int ret;
     95	struct regmap *map;
     96	const char *state;
     97	enum led_brightness maxbright;
     98
     99	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
    100	if (!led)
    101		return -ENOMEM;
    102
    103	led->ledtype = (u32)(unsigned long)of_device_get_match_data(dev);
    104
    105	map = dev_get_regmap(dev->parent, NULL);
    106	if (!map) {
    107		dev_err(dev, "Parent regmap unavailable.\n");
    108		return -ENXIO;
    109	}
    110	led->map = map;
    111
    112	np = dev_of_node(dev);
    113
    114	ret = of_property_read_u32(np, "reg", &led->reg);
    115	if (ret) {
    116		dev_err(dev, "no register offset specified\n");
    117		return -EINVAL;
    118	}
    119
    120	led->cdev.brightness_set = pm8058_led_set;
    121	led->cdev.brightness_get = pm8058_led_get;
    122	if (led->ledtype == PM8058_LED_TYPE_COMMON)
    123		maxbright = 31; /* 5 bits */
    124	else
    125		maxbright = 15; /* 4 bits */
    126	led->cdev.max_brightness = maxbright;
    127
    128	state = of_get_property(np, "default-state", NULL);
    129	if (state) {
    130		if (!strcmp(state, "keep")) {
    131			led->cdev.brightness = pm8058_led_get(&led->cdev);
    132		} else if (!strcmp(state, "on")) {
    133			led->cdev.brightness = maxbright;
    134			pm8058_led_set(&led->cdev, maxbright);
    135		} else {
    136			led->cdev.brightness = LED_OFF;
    137			pm8058_led_set(&led->cdev, LED_OFF);
    138		}
    139	}
    140
    141	if (led->ledtype == PM8058_LED_TYPE_KEYPAD ||
    142	    led->ledtype == PM8058_LED_TYPE_FLASH)
    143		led->cdev.flags	= LED_CORE_SUSPENDRESUME;
    144
    145	init_data.fwnode = of_fwnode_handle(np);
    146
    147	ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
    148	if (ret)
    149		dev_err(dev, "Failed to register LED for %pOF\n", np);
    150
    151	return ret;
    152}
    153
    154static const struct of_device_id pm8058_leds_id_table[] = {
    155	{
    156		.compatible = "qcom,pm8058-led",
    157		.data = (void *)PM8058_LED_TYPE_COMMON
    158	},
    159	{
    160		.compatible = "qcom,pm8058-keypad-led",
    161		.data = (void *)PM8058_LED_TYPE_KEYPAD
    162	},
    163	{
    164		.compatible = "qcom,pm8058-flash-led",
    165		.data = (void *)PM8058_LED_TYPE_FLASH
    166	},
    167	{ },
    168};
    169MODULE_DEVICE_TABLE(of, pm8058_leds_id_table);
    170
    171static struct platform_driver pm8058_led_driver = {
    172	.probe		= pm8058_led_probe,
    173	.driver		= {
    174		.name	= "pm8058-leds",
    175		.of_match_table = pm8058_leds_id_table,
    176	},
    177};
    178module_platform_driver(pm8058_led_driver);
    179
    180MODULE_DESCRIPTION("PM8058 LEDs driver");
    181MODULE_LICENSE("GPL v2");
    182MODULE_ALIAS("platform:pm8058-leds");