brintos

brintos / linux-shallow public Read only

0
0
Text · 5.1 KiB · 51fcff2 Raw
227 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Driver for BCM6358 memory-mapped LEDs, based on leds-syscon.c4 *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		0x016#define BCM6358_REG_CTRL		0x417 18#define BCM6358_SLED_CLKDIV_MASK	319#define BCM6358_SLED_CLKDIV_1		020#define BCM6358_SLED_CLKDIV_2		121#define BCM6358_SLED_CLKDIV_4		222#define BCM6358_SLED_CLKDIV_8		323 24#define BCM6358_SLED_POLARITY		BIT(2)25#define BCM6358_SLED_BUSY		BIT(3)26 27#define BCM6358_SLED_MAX_COUNT		3228#define BCM6358_SLED_WAIT		10029 30/**31 * struct bcm6358_led - state container for bcm6358 based LEDs32 * @cdev: LED class device for this LED33 * @mem: memory resource34 * @lock: memory lock35 * @pin: LED pin number36 * @active_low: LED is active low37 */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_ENDIAN49	iowrite32be(data, reg);50#else51	writel(data, reg);52#endif53}54 55static unsigned long bcm6358_led_read(void __iomem *reg)56{57#ifdef CONFIG_CPU_BIG_ENDIAN58	return ioread32be(reg);59#else60	return readl(reg);61#endif62}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	else89		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	enum led_default_state state;100	unsigned long val;101	int rc;102 103	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);104	if (!led)105		return -ENOMEM;106 107	led->pin = reg;108	led->mem = mem;109	led->lock = lock;110 111	if (of_property_read_bool(nc, "active-low"))112		led->active_low = true;113 114	init_data.fwnode = of_fwnode_handle(nc);115 116	state = led_init_default_state_get(init_data.fwnode);117	switch (state) {118	case LEDS_DEFSTATE_ON:119		led->cdev.brightness = LED_FULL;120		break;121	case LEDS_DEFSTATE_KEEP:122		val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);123		val &= BIT(led->pin);124		if ((led->active_low && !val) || (!led->active_low && val))125			led->cdev.brightness = LED_FULL;126		else127			led->cdev.brightness = LED_OFF;128		break;129	default:130		led->cdev.brightness = LED_OFF;131	}132 133	bcm6358_led_set(&led->cdev, led->cdev.brightness);134 135	led->cdev.brightness_set = bcm6358_led_set;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	void __iomem *mem;151	spinlock_t *lock; /* memory lock */152	unsigned long val;153	u32 clk_div;154 155	mem = devm_platform_ioremap_resource(pdev, 0);156	if (IS_ERR(mem))157		return PTR_ERR(mem);158 159	lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);160	if (!lock)161		return -ENOMEM;162 163	spin_lock_init(lock);164 165	val = bcm6358_led_busy(mem);166	val &= ~(BCM6358_SLED_POLARITY | BCM6358_SLED_CLKDIV_MASK);167	if (of_property_read_bool(np, "brcm,clk-dat-low"))168		val |= BCM6358_SLED_POLARITY;169	of_property_read_u32(np, "brcm,clk-div", &clk_div);170	switch (clk_div) {171	case 8:172		val |= BCM6358_SLED_CLKDIV_8;173		break;174	case 4:175		val |= BCM6358_SLED_CLKDIV_4;176		break;177	case 2:178		val |= BCM6358_SLED_CLKDIV_2;179		break;180	default:181		val |= BCM6358_SLED_CLKDIV_1;182		break;183	}184	bcm6358_led_write(mem + BCM6358_REG_CTRL, val);185 186	for_each_available_child_of_node_scoped(np, child) {187		int rc;188		u32 reg;189 190		if (of_property_read_u32(child, "reg", &reg))191			continue;192 193		if (reg >= BCM6358_SLED_MAX_COUNT) {194			dev_err(dev, "invalid LED (%u >= %d)\n", reg,195				BCM6358_SLED_MAX_COUNT);196			continue;197		}198 199		rc = bcm6358_led(dev, child, reg, mem, lock);200		if (rc < 0)201			return rc;202	}203 204	return 0;205}206 207static const struct of_device_id bcm6358_leds_of_match[] = {208	{ .compatible = "brcm,bcm6358-leds", },209	{ },210};211MODULE_DEVICE_TABLE(of, bcm6358_leds_of_match);212 213static struct platform_driver bcm6358_leds_driver = {214	.probe = bcm6358_leds_probe,215	.driver = {216		.name = "leds-bcm6358",217		.of_match_table = bcm6358_leds_of_match,218	},219};220 221module_platform_driver(bcm6358_leds_driver);222 223MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");224MODULE_DESCRIPTION("LED driver for BCM6358 controllers");225MODULE_LICENSE("GPL v2");226MODULE_ALIAS("platform:leds-bcm6358");227