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-bcm6358.c (5340B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Driver for BCM6358 memory-mapped LEDs, based on leds-syscon.c
      4 *
      5 * Copyright 2015 Álvaro Fernández Rojas <noltari@gmail.com>
      6 */
      7#include <linux/delay.h>
      8#include <linux/io.h>
      9#include <linux/leds.h>
     10#include <linux/module.h>
     11#include <linux/of.h>
     12#include <linux/platform_device.h>
     13#include <linux/spinlock.h>
     14
     15#define BCM6358_REG_MODE		0x0
     16#define BCM6358_REG_CTRL		0x4
     17
     18#define BCM6358_SLED_CLKDIV_MASK	3
     19#define BCM6358_SLED_CLKDIV_1		0
     20#define BCM6358_SLED_CLKDIV_2		1
     21#define BCM6358_SLED_CLKDIV_4		2
     22#define BCM6358_SLED_CLKDIV_8		3
     23
     24#define BCM6358_SLED_POLARITY		BIT(2)
     25#define BCM6358_SLED_BUSY		BIT(3)
     26
     27#define BCM6358_SLED_MAX_COUNT		32
     28#define BCM6358_SLED_WAIT		100
     29
     30/**
     31 * struct bcm6358_led - state container for bcm6358 based LEDs
     32 * @cdev: LED class device for this LED
     33 * @mem: memory resource
     34 * @lock: memory lock
     35 * @pin: LED pin number
     36 * @active_low: LED is active low
     37 */
     38struct bcm6358_led {
     39	struct led_classdev cdev;
     40	void __iomem *mem;
     41	spinlock_t *lock;
     42	unsigned long pin;
     43	bool active_low;
     44};
     45
     46static void bcm6358_led_write(void __iomem *reg, unsigned long data)
     47{
     48#ifdef CONFIG_CPU_BIG_ENDIAN
     49	iowrite32be(data, reg);
     50#else
     51	writel(data, reg);
     52#endif
     53}
     54
     55static unsigned long bcm6358_led_read(void __iomem *reg)
     56{
     57#ifdef CONFIG_CPU_BIG_ENDIAN
     58	return ioread32be(reg);
     59#else
     60	return readl(reg);
     61#endif
     62}
     63
     64static unsigned long bcm6358_led_busy(void __iomem *mem)
     65{
     66	unsigned long val;
     67
     68	while ((val = bcm6358_led_read(mem + BCM6358_REG_CTRL)) &
     69		BCM6358_SLED_BUSY)
     70		udelay(BCM6358_SLED_WAIT);
     71
     72	return val;
     73}
     74
     75static void bcm6358_led_set(struct led_classdev *led_cdev,
     76			    enum led_brightness value)
     77{
     78	struct bcm6358_led *led =
     79		container_of(led_cdev, struct bcm6358_led, cdev);
     80	unsigned long flags, val;
     81
     82	spin_lock_irqsave(led->lock, flags);
     83	bcm6358_led_busy(led->mem);
     84	val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
     85	if ((led->active_low && value == LED_OFF) ||
     86	    (!led->active_low && value != LED_OFF))
     87		val |= BIT(led->pin);
     88	else
     89		val &= ~(BIT(led->pin));
     90	bcm6358_led_write(led->mem + BCM6358_REG_MODE, val);
     91	spin_unlock_irqrestore(led->lock, flags);
     92}
     93
     94static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
     95		       void __iomem *mem, spinlock_t *lock)
     96{
     97	struct led_init_data init_data = {};
     98	struct bcm6358_led *led;
     99	const char *state;
    100	int rc;
    101
    102	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
    103	if (!led)
    104		return -ENOMEM;
    105
    106	led->pin = reg;
    107	led->mem = mem;
    108	led->lock = lock;
    109
    110	if (of_property_read_bool(nc, "active-low"))
    111		led->active_low = true;
    112
    113	if (!of_property_read_string(nc, "default-state", &state)) {
    114		if (!strcmp(state, "on")) {
    115			led->cdev.brightness = LED_FULL;
    116		} else if (!strcmp(state, "keep")) {
    117			unsigned long val;
    118			val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
    119			val &= BIT(led->pin);
    120			if ((led->active_low && !val) ||
    121			    (!led->active_low && val))
    122				led->cdev.brightness = LED_FULL;
    123			else
    124				led->cdev.brightness = LED_OFF;
    125		} else {
    126			led->cdev.brightness = LED_OFF;
    127		}
    128	} else {
    129		led->cdev.brightness = LED_OFF;
    130	}
    131
    132	bcm6358_led_set(&led->cdev, led->cdev.brightness);
    133
    134	led->cdev.brightness_set = bcm6358_led_set;
    135	init_data.fwnode = of_fwnode_handle(nc);
    136
    137	rc = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
    138	if (rc < 0)
    139		return rc;
    140
    141	dev_dbg(dev, "registered LED %s\n", led->cdev.name);
    142
    143	return 0;
    144}
    145
    146static int bcm6358_leds_probe(struct platform_device *pdev)
    147{
    148	struct device *dev = &pdev->dev;
    149	struct device_node *np = dev_of_node(&pdev->dev);
    150	struct device_node *child;
    151	void __iomem *mem;
    152	spinlock_t *lock; /* memory lock */
    153	unsigned long val;
    154	u32 clk_div;
    155
    156	mem = devm_platform_ioremap_resource(pdev, 0);
    157	if (IS_ERR(mem))
    158		return PTR_ERR(mem);
    159
    160	lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
    161	if (!lock)
    162		return -ENOMEM;
    163
    164	spin_lock_init(lock);
    165
    166	val = bcm6358_led_busy(mem);
    167	val &= ~(BCM6358_SLED_POLARITY | BCM6358_SLED_CLKDIV_MASK);
    168	if (of_property_read_bool(np, "brcm,clk-dat-low"))
    169		val |= BCM6358_SLED_POLARITY;
    170	of_property_read_u32(np, "brcm,clk-div", &clk_div);
    171	switch (clk_div) {
    172	case 8:
    173		val |= BCM6358_SLED_CLKDIV_8;
    174		break;
    175	case 4:
    176		val |= BCM6358_SLED_CLKDIV_4;
    177		break;
    178	case 2:
    179		val |= BCM6358_SLED_CLKDIV_2;
    180		break;
    181	default:
    182		val |= BCM6358_SLED_CLKDIV_1;
    183		break;
    184	}
    185	bcm6358_led_write(mem + BCM6358_REG_CTRL, val);
    186
    187	for_each_available_child_of_node(np, child) {
    188		int rc;
    189		u32 reg;
    190
    191		if (of_property_read_u32(child, "reg", &reg))
    192			continue;
    193
    194		if (reg >= BCM6358_SLED_MAX_COUNT) {
    195			dev_err(dev, "invalid LED (%u >= %d)\n", reg,
    196				BCM6358_SLED_MAX_COUNT);
    197			continue;
    198		}
    199
    200		rc = bcm6358_led(dev, child, reg, mem, lock);
    201		if (rc < 0) {
    202			of_node_put(child);
    203			return rc;
    204		}
    205	}
    206
    207	return 0;
    208}
    209
    210static const struct of_device_id bcm6358_leds_of_match[] = {
    211	{ .compatible = "brcm,bcm6358-leds", },
    212	{ },
    213};
    214MODULE_DEVICE_TABLE(of, bcm6358_leds_of_match);
    215
    216static struct platform_driver bcm6358_leds_driver = {
    217	.probe = bcm6358_leds_probe,
    218	.driver = {
    219		.name = "leds-bcm6358",
    220		.of_match_table = bcm6358_leds_of_match,
    221	},
    222};
    223
    224module_platform_driver(bcm6358_leds_driver);
    225
    226MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
    227MODULE_DESCRIPTION("LED driver for BCM6358 controllers");
    228MODULE_LICENSE("GPL v2");
    229MODULE_ALIAS("platform:leds-bcm6358");