| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 | // SPDX-License-Identifier: GPL-2.0+/* * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> */#include <common.h>#include <dm.h>#include <errno.h>#include <led.h>#include <asm/io.h>#include <dm/lists.h>#define LEDS_MAX			24/* LED Init register */#define LED_INIT_REG			0x00#define LED_INIT_FASTINTV_MS		20#define LED_INIT_FASTINTV_SHIFT		6#define LED_INIT_FASTINTV_MASK		(0x3f << LED_INIT_FASTINTV_SHIFT)#define LED_INIT_SLEDEN_SHIFT		12#define LED_INIT_SLEDEN_MASK		(1 << LED_INIT_SLEDEN_SHIFT)#define LED_INIT_SLEDMUX_SHIFT		13#define LED_INIT_SLEDMUX_MASK		(1 << LED_INIT_SLEDMUX_SHIFT)#define LED_INIT_SLEDCLKNPOL_SHIFT	14#define LED_INIT_SLEDCLKNPOL_MASK	(1 << LED_INIT_SLEDCLKNPOL_SHIFT)#define LED_INIT_SLEDDATAPPOL_SHIFT	15#define LED_INIT_SLEDDATANPOL_MASK	(1 << LED_INIT_SLEDDATAPPOL_SHIFT)#define LED_INIT_SLEDSHIFTDIR_SHIFT	16#define LED_INIT_SLEDSHIFTDIR_MASK	(1 << LED_INIT_SLEDSHIFTDIR_SHIFT)/* LED Mode registers */#define LED_MODE_REG_HI			0x04#define LED_MODE_REG_LO			0x08#define LED_MODE_ON			0#define LED_MODE_FAST			1#define LED_MODE_BLINK			2#define LED_MODE_OFF			3#define LED_MODE_MASK			0x3struct bcm6328_led_priv {	void __iomem *regs;	void __iomem *mode;	uint8_t shift;	bool active_low;};static unsigned long bcm6328_led_get_mode(struct bcm6328_led_priv *priv){	return ((readl_be(priv->mode) >> priv->shift) & LED_MODE_MASK);}static int bcm6328_led_set_mode(struct bcm6328_led_priv *priv, uint8_t mode){	clrsetbits_be32(priv->mode, (LED_MODE_MASK << priv->shift),			(mode << priv->shift));	return 0;}static enum led_state_t bcm6328_led_get_state(struct udevice *dev){	struct bcm6328_led_priv *priv = dev_get_priv(dev);	enum led_state_t state = LEDST_OFF;	switch (bcm6328_led_get_mode(priv)) {#ifdef CONFIG_LED_BLINK	case LED_MODE_BLINK:	case LED_MODE_FAST:		state = LEDST_BLINK;		break;#endif	case LED_MODE_OFF:		state = (priv->active_low ? LEDST_ON : LEDST_OFF);		break;	case LED_MODE_ON:		state = (priv->active_low ? LEDST_OFF : LEDST_ON);		break;	}	return state;}static int bcm6328_led_set_state(struct udevice *dev, enum led_state_t state){	struct bcm6328_led_priv *priv = dev_get_priv(dev);	unsigned long mode;	switch (state) {#ifdef CONFIG_LED_BLINK	case LEDST_BLINK:		mode = LED_MODE_BLINK;		break;#endif	case LEDST_OFF:		mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF);		break;	case LEDST_ON:		mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON);		break;	case LEDST_TOGGLE:		if (bcm6328_led_get_state(dev) == LEDST_OFF)			return bcm6328_led_set_state(dev, LEDST_ON);		else			return bcm6328_led_set_state(dev, LEDST_OFF);		break;	default:		return -ENOSYS;	}	return bcm6328_led_set_mode(priv, mode);}#ifdef CONFIG_LED_BLINKstatic unsigned long bcm6328_blink_delay(int delay){	unsigned long bcm6328_delay = delay;	bcm6328_delay += (LED_INIT_FASTINTV_MS / 2);	bcm6328_delay /= LED_INIT_FASTINTV_MS;	bcm6328_delay <<= LED_INIT_FASTINTV_SHIFT;	if (bcm6328_delay > LED_INIT_FASTINTV_MASK)		return LED_INIT_FASTINTV_MASK;	else		return bcm6328_delay;}static int bcm6328_led_set_period(struct udevice *dev, int period_ms){	struct bcm6328_led_priv *priv = dev_get_priv(dev);	clrsetbits_be32(priv->regs + LED_INIT_REG, LED_INIT_FASTINTV_MASK,			bcm6328_blink_delay(period_ms));	return 0;}#endifstatic const struct led_ops bcm6328_led_ops = {	.get_state = bcm6328_led_get_state,	.set_state = bcm6328_led_set_state,#ifdef CONFIG_LED_BLINK	.set_period = bcm6328_led_set_period,#endif};static int bcm6328_led_probe(struct udevice *dev){	struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev);	/* Top-level LED node */	if (!uc_plat->label) {		void __iomem *regs;		u32 set_bits = 0;		regs = dev_remap_addr(dev);		if (!regs)			return -EINVAL;		if (dev_read_bool(dev, "brcm,serial-leds"))			set_bits |= LED_INIT_SLEDEN_MASK;		if (dev_read_bool(dev, "brcm,serial-mux"))			set_bits |= LED_INIT_SLEDMUX_MASK;		if (dev_read_bool(dev, "brcm,serial-clk-low"))			set_bits |= LED_INIT_SLEDCLKNPOL_MASK;		if (!dev_read_bool(dev, "brcm,serial-dat-low"))			set_bits |= LED_INIT_SLEDDATANPOL_MASK;		if (!dev_read_bool(dev, "brcm,serial-shift-inv"))			set_bits |= LED_INIT_SLEDSHIFTDIR_MASK;		clrsetbits_be32(regs + LED_INIT_REG, ~0, set_bits);	} else {		struct bcm6328_led_priv *priv = dev_get_priv(dev);		unsigned int pin;		priv->regs = dev_remap_addr(dev);		if (!priv->regs)			return -EINVAL;		pin = dev_read_u32_default(dev, "reg", LEDS_MAX);		if (pin >= LEDS_MAX)			return -EINVAL;		if (pin < 8) {			/* LEDs 0-7 (bits 47:32) */			priv->mode = priv->regs + LED_MODE_REG_HI;			priv->shift = (pin << 1);		} else {			/* LEDs 8-23 (bits 31:0) */			priv->mode = priv->regs + LED_MODE_REG_LO;			priv->shift = ((pin - 8) << 1);		}		if (dev_read_bool(dev, "active-low"))			priv->active_low = true;	}	return 0;}static int bcm6328_led_bind(struct udevice *parent){	ofnode node;	dev_for_each_subnode(node, parent) {		struct led_uc_plat *uc_plat;		struct udevice *dev;		const char *label;		int ret;		label = ofnode_read_string(node, "label");		if (!label) {			debug("%s: node %s has no label\n", __func__,			      ofnode_get_name(node));			return -EINVAL;		}		ret = device_bind_driver_to_node(parent, "bcm6328-led",						 ofnode_get_name(node),						 node, &dev);		if (ret)			return ret;		uc_plat = dev_get_uclass_platdata(dev);		uc_plat->label = label;	}	return 0;}static const struct udevice_id bcm6328_led_ids[] = {	{ .compatible = "brcm,bcm6328-leds" },	{ /* sentinel */ }};U_BOOT_DRIVER(bcm6328_led) = {	.name = "bcm6328-led",	.id = UCLASS_LED,	.of_match = bcm6328_led_ids,	.ops = &bcm6328_led_ops,	.bind = bcm6328_led_bind,	.probe = bcm6328_led_probe,	.priv_auto_alloc_size = sizeof(struct bcm6328_led_priv),};
 |