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-wm831x-status.c (6910B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * LED driver for WM831x status LEDs
      4 *
      5 * Copyright(C) 2009 Wolfson Microelectronics PLC.
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/platform_device.h>
     10#include <linux/slab.h>
     11#include <linux/leds.h>
     12#include <linux/err.h>
     13#include <linux/mfd/wm831x/core.h>
     14#include <linux/mfd/wm831x/pdata.h>
     15#include <linux/mfd/wm831x/status.h>
     16#include <linux/module.h>
     17
     18
     19struct wm831x_status {
     20	struct led_classdev cdev;
     21	struct wm831x *wm831x;
     22	struct mutex mutex;
     23
     24	spinlock_t value_lock;
     25	int reg;     /* Control register */
     26	int reg_val; /* Control register value */
     27
     28	int blink;
     29	int blink_time;
     30	int blink_cyc;
     31	int src;
     32	enum led_brightness brightness;
     33};
     34
     35#define to_wm831x_status(led_cdev) \
     36	container_of(led_cdev, struct wm831x_status, cdev)
     37
     38static void wm831x_status_set(struct wm831x_status *led)
     39{
     40	unsigned long flags;
     41
     42	mutex_lock(&led->mutex);
     43
     44	led->reg_val &= ~(WM831X_LED_SRC_MASK | WM831X_LED_MODE_MASK |
     45			  WM831X_LED_DUTY_CYC_MASK | WM831X_LED_DUR_MASK);
     46
     47	spin_lock_irqsave(&led->value_lock, flags);
     48
     49	led->reg_val |= led->src << WM831X_LED_SRC_SHIFT;
     50	if (led->blink) {
     51		led->reg_val |= 2 << WM831X_LED_MODE_SHIFT;
     52		led->reg_val |= led->blink_time << WM831X_LED_DUR_SHIFT;
     53		led->reg_val |= led->blink_cyc;
     54	} else {
     55		if (led->brightness != LED_OFF)
     56			led->reg_val |= 1 << WM831X_LED_MODE_SHIFT;
     57	}
     58
     59	spin_unlock_irqrestore(&led->value_lock, flags);
     60
     61	wm831x_reg_write(led->wm831x, led->reg, led->reg_val);
     62
     63	mutex_unlock(&led->mutex);
     64}
     65
     66static int wm831x_status_brightness_set(struct led_classdev *led_cdev,
     67					 enum led_brightness value)
     68{
     69	struct wm831x_status *led = to_wm831x_status(led_cdev);
     70	unsigned long flags;
     71
     72	spin_lock_irqsave(&led->value_lock, flags);
     73	led->brightness = value;
     74	if (value == LED_OFF)
     75		led->blink = 0;
     76	spin_unlock_irqrestore(&led->value_lock, flags);
     77	wm831x_status_set(led);
     78
     79	return 0;
     80}
     81
     82static int wm831x_status_blink_set(struct led_classdev *led_cdev,
     83				   unsigned long *delay_on,
     84				   unsigned long *delay_off)
     85{
     86	struct wm831x_status *led = to_wm831x_status(led_cdev);
     87	unsigned long flags;
     88	int ret = 0;
     89
     90	/* Pick some defaults if we've not been given times */
     91	if (*delay_on == 0 && *delay_off == 0) {
     92		*delay_on = 250;
     93		*delay_off = 250;
     94	}
     95
     96	spin_lock_irqsave(&led->value_lock, flags);
     97
     98	/* We only have a limited selection of settings, see if we can
     99	 * support the configuration we're being given */
    100	switch (*delay_on) {
    101	case 1000:
    102		led->blink_time = 0;
    103		break;
    104	case 250:
    105		led->blink_time = 1;
    106		break;
    107	case 125:
    108		led->blink_time = 2;
    109		break;
    110	case 62:
    111	case 63:
    112		/* Actually 62.5ms */
    113		led->blink_time = 3;
    114		break;
    115	default:
    116		ret = -EINVAL;
    117		break;
    118	}
    119
    120	if (ret == 0) {
    121		switch (*delay_off / *delay_on) {
    122		case 1:
    123			led->blink_cyc = 0;
    124			break;
    125		case 3:
    126			led->blink_cyc = 1;
    127			break;
    128		case 4:
    129			led->blink_cyc = 2;
    130			break;
    131		case 8:
    132			led->blink_cyc = 3;
    133			break;
    134		default:
    135			ret = -EINVAL;
    136			break;
    137		}
    138	}
    139
    140	if (ret == 0)
    141		led->blink = 1;
    142	else
    143		led->blink = 0;
    144
    145	spin_unlock_irqrestore(&led->value_lock, flags);
    146	wm831x_status_set(led);
    147
    148	return ret;
    149}
    150
    151static const char * const led_src_texts[] = {
    152	"otp",
    153	"power",
    154	"charger",
    155	"soft",
    156};
    157
    158static ssize_t src_show(struct device *dev,
    159			struct device_attribute *attr, char *buf)
    160{
    161	struct led_classdev *led_cdev = dev_get_drvdata(dev);
    162	struct wm831x_status *led = to_wm831x_status(led_cdev);
    163	int i;
    164	ssize_t ret = 0;
    165
    166	mutex_lock(&led->mutex);
    167
    168	for (i = 0; i < ARRAY_SIZE(led_src_texts); i++)
    169		if (i == led->src)
    170			ret += sprintf(&buf[ret], "[%s] ", led_src_texts[i]);
    171		else
    172			ret += sprintf(&buf[ret], "%s ", led_src_texts[i]);
    173
    174	mutex_unlock(&led->mutex);
    175
    176	ret += sprintf(&buf[ret], "\n");
    177
    178	return ret;
    179}
    180
    181static ssize_t src_store(struct device *dev,
    182			 struct device_attribute *attr,
    183			 const char *buf, size_t size)
    184{
    185	struct led_classdev *led_cdev = dev_get_drvdata(dev);
    186	struct wm831x_status *led = to_wm831x_status(led_cdev);
    187	int i;
    188
    189	i = sysfs_match_string(led_src_texts, buf);
    190	if (i >= 0) {
    191		mutex_lock(&led->mutex);
    192		led->src = i;
    193		mutex_unlock(&led->mutex);
    194		wm831x_status_set(led);
    195	}
    196
    197	return size;
    198}
    199
    200static DEVICE_ATTR_RW(src);
    201
    202static struct attribute *wm831x_status_attrs[] = {
    203	&dev_attr_src.attr,
    204	NULL
    205};
    206ATTRIBUTE_GROUPS(wm831x_status);
    207
    208static int wm831x_status_probe(struct platform_device *pdev)
    209{
    210	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
    211	struct wm831x_pdata *chip_pdata;
    212	struct wm831x_status_pdata pdata;
    213	struct wm831x_status *drvdata;
    214	struct resource *res;
    215	int id = pdev->id % ARRAY_SIZE(chip_pdata->status);
    216	int ret;
    217
    218	res = platform_get_resource(pdev, IORESOURCE_REG, 0);
    219	if (res == NULL) {
    220		dev_err(&pdev->dev, "No register resource\n");
    221		return -EINVAL;
    222	}
    223
    224	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_status),
    225			       GFP_KERNEL);
    226	if (!drvdata)
    227		return -ENOMEM;
    228
    229	drvdata->wm831x = wm831x;
    230	drvdata->reg = res->start;
    231
    232	if (dev_get_platdata(wm831x->dev))
    233		chip_pdata = dev_get_platdata(wm831x->dev);
    234	else
    235		chip_pdata = NULL;
    236
    237	memset(&pdata, 0, sizeof(pdata));
    238	if (chip_pdata && chip_pdata->status[id])
    239		memcpy(&pdata, chip_pdata->status[id], sizeof(pdata));
    240	else
    241		pdata.name = dev_name(&pdev->dev);
    242
    243	mutex_init(&drvdata->mutex);
    244	spin_lock_init(&drvdata->value_lock);
    245
    246	/* We cache the configuration register and read startup values
    247	 * from it. */
    248	drvdata->reg_val = wm831x_reg_read(wm831x, drvdata->reg);
    249
    250	if (drvdata->reg_val & WM831X_LED_MODE_MASK)
    251		drvdata->brightness = LED_FULL;
    252	else
    253		drvdata->brightness = LED_OFF;
    254
    255	/* Set a default source if configured, otherwise leave the
    256	 * current hardware setting.
    257	 */
    258	if (pdata.default_src == WM831X_STATUS_PRESERVE) {
    259		drvdata->src = drvdata->reg_val;
    260		drvdata->src &= WM831X_LED_SRC_MASK;
    261		drvdata->src >>= WM831X_LED_SRC_SHIFT;
    262	} else {
    263		drvdata->src = pdata.default_src - 1;
    264	}
    265
    266	drvdata->cdev.name = pdata.name;
    267	drvdata->cdev.default_trigger = pdata.default_trigger;
    268	drvdata->cdev.brightness_set_blocking = wm831x_status_brightness_set;
    269	drvdata->cdev.blink_set = wm831x_status_blink_set;
    270	drvdata->cdev.groups = wm831x_status_groups;
    271
    272	ret = led_classdev_register(wm831x->dev, &drvdata->cdev);
    273	if (ret < 0) {
    274		dev_err(&pdev->dev, "Failed to register LED: %d\n", ret);
    275		return ret;
    276	}
    277
    278	platform_set_drvdata(pdev, drvdata);
    279
    280	return 0;
    281}
    282
    283static int wm831x_status_remove(struct platform_device *pdev)
    284{
    285	struct wm831x_status *drvdata = platform_get_drvdata(pdev);
    286
    287	led_classdev_unregister(&drvdata->cdev);
    288
    289	return 0;
    290}
    291
    292static struct platform_driver wm831x_status_driver = {
    293	.driver = {
    294		   .name = "wm831x-status",
    295		   },
    296	.probe = wm831x_status_probe,
    297	.remove = wm831x_status_remove,
    298};
    299
    300module_platform_driver(wm831x_status_driver);
    301
    302MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
    303MODULE_DESCRIPTION("WM831x status LED driver");
    304MODULE_LICENSE("GPL");
    305MODULE_ALIAS("platform:wm831x-status");