0001
0002
0003
0004
0005
0006
0007 #include <linux/delay.h>
0008 #include <linux/io.h>
0009 #include <linux/leds.h>
0010 #include <linux/module.h>
0011 #include <linux/of.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/spinlock.h>
0014
0015 #define BCM6358_REG_MODE 0x0
0016 #define BCM6358_REG_CTRL 0x4
0017
0018 #define BCM6358_SLED_CLKDIV_MASK 3
0019 #define BCM6358_SLED_CLKDIV_1 0
0020 #define BCM6358_SLED_CLKDIV_2 1
0021 #define BCM6358_SLED_CLKDIV_4 2
0022 #define BCM6358_SLED_CLKDIV_8 3
0023
0024 #define BCM6358_SLED_POLARITY BIT(2)
0025 #define BCM6358_SLED_BUSY BIT(3)
0026
0027 #define BCM6358_SLED_MAX_COUNT 32
0028 #define BCM6358_SLED_WAIT 100
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038 struct bcm6358_led {
0039 struct led_classdev cdev;
0040 void __iomem *mem;
0041 spinlock_t *lock;
0042 unsigned long pin;
0043 bool active_low;
0044 };
0045
0046 static void bcm6358_led_write(void __iomem *reg, unsigned long data)
0047 {
0048 #ifdef CONFIG_CPU_BIG_ENDIAN
0049 iowrite32be(data, reg);
0050 #else
0051 writel(data, reg);
0052 #endif
0053 }
0054
0055 static unsigned long bcm6358_led_read(void __iomem *reg)
0056 {
0057 #ifdef CONFIG_CPU_BIG_ENDIAN
0058 return ioread32be(reg);
0059 #else
0060 return readl(reg);
0061 #endif
0062 }
0063
0064 static unsigned long bcm6358_led_busy(void __iomem *mem)
0065 {
0066 unsigned long val;
0067
0068 while ((val = bcm6358_led_read(mem + BCM6358_REG_CTRL)) &
0069 BCM6358_SLED_BUSY)
0070 udelay(BCM6358_SLED_WAIT);
0071
0072 return val;
0073 }
0074
0075 static void bcm6358_led_set(struct led_classdev *led_cdev,
0076 enum led_brightness value)
0077 {
0078 struct bcm6358_led *led =
0079 container_of(led_cdev, struct bcm6358_led, cdev);
0080 unsigned long flags, val;
0081
0082 spin_lock_irqsave(led->lock, flags);
0083 bcm6358_led_busy(led->mem);
0084 val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
0085 if ((led->active_low && value == LED_OFF) ||
0086 (!led->active_low && value != LED_OFF))
0087 val |= BIT(led->pin);
0088 else
0089 val &= ~(BIT(led->pin));
0090 bcm6358_led_write(led->mem + BCM6358_REG_MODE, val);
0091 spin_unlock_irqrestore(led->lock, flags);
0092 }
0093
0094 static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
0095 void __iomem *mem, spinlock_t *lock)
0096 {
0097 struct led_init_data init_data = {};
0098 struct bcm6358_led *led;
0099 const char *state;
0100 int rc;
0101
0102 led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
0103 if (!led)
0104 return -ENOMEM;
0105
0106 led->pin = reg;
0107 led->mem = mem;
0108 led->lock = lock;
0109
0110 if (of_property_read_bool(nc, "active-low"))
0111 led->active_low = true;
0112
0113 if (!of_property_read_string(nc, "default-state", &state)) {
0114 if (!strcmp(state, "on")) {
0115 led->cdev.brightness = LED_FULL;
0116 } else if (!strcmp(state, "keep")) {
0117 unsigned long val;
0118 val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
0119 val &= BIT(led->pin);
0120 if ((led->active_low && !val) ||
0121 (!led->active_low && val))
0122 led->cdev.brightness = LED_FULL;
0123 else
0124 led->cdev.brightness = LED_OFF;
0125 } else {
0126 led->cdev.brightness = LED_OFF;
0127 }
0128 } else {
0129 led->cdev.brightness = LED_OFF;
0130 }
0131
0132 bcm6358_led_set(&led->cdev, led->cdev.brightness);
0133
0134 led->cdev.brightness_set = bcm6358_led_set;
0135 init_data.fwnode = of_fwnode_handle(nc);
0136
0137 rc = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
0138 if (rc < 0)
0139 return rc;
0140
0141 dev_dbg(dev, "registered LED %s\n", led->cdev.name);
0142
0143 return 0;
0144 }
0145
0146 static int bcm6358_leds_probe(struct platform_device *pdev)
0147 {
0148 struct device *dev = &pdev->dev;
0149 struct device_node *np = dev_of_node(&pdev->dev);
0150 struct device_node *child;
0151 void __iomem *mem;
0152 spinlock_t *lock;
0153 unsigned long val;
0154 u32 clk_div;
0155
0156 mem = devm_platform_ioremap_resource(pdev, 0);
0157 if (IS_ERR(mem))
0158 return PTR_ERR(mem);
0159
0160 lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
0161 if (!lock)
0162 return -ENOMEM;
0163
0164 spin_lock_init(lock);
0165
0166 val = bcm6358_led_busy(mem);
0167 val &= ~(BCM6358_SLED_POLARITY | BCM6358_SLED_CLKDIV_MASK);
0168 if (of_property_read_bool(np, "brcm,clk-dat-low"))
0169 val |= BCM6358_SLED_POLARITY;
0170 of_property_read_u32(np, "brcm,clk-div", &clk_div);
0171 switch (clk_div) {
0172 case 8:
0173 val |= BCM6358_SLED_CLKDIV_8;
0174 break;
0175 case 4:
0176 val |= BCM6358_SLED_CLKDIV_4;
0177 break;
0178 case 2:
0179 val |= BCM6358_SLED_CLKDIV_2;
0180 break;
0181 default:
0182 val |= BCM6358_SLED_CLKDIV_1;
0183 break;
0184 }
0185 bcm6358_led_write(mem + BCM6358_REG_CTRL, val);
0186
0187 for_each_available_child_of_node(np, child) {
0188 int rc;
0189 u32 reg;
0190
0191 if (of_property_read_u32(child, "reg", ®))
0192 continue;
0193
0194 if (reg >= BCM6358_SLED_MAX_COUNT) {
0195 dev_err(dev, "invalid LED (%u >= %d)\n", reg,
0196 BCM6358_SLED_MAX_COUNT);
0197 continue;
0198 }
0199
0200 rc = bcm6358_led(dev, child, reg, mem, lock);
0201 if (rc < 0) {
0202 of_node_put(child);
0203 return rc;
0204 }
0205 }
0206
0207 return 0;
0208 }
0209
0210 static const struct of_device_id bcm6358_leds_of_match[] = {
0211 { .compatible = "brcm,bcm6358-leds", },
0212 { },
0213 };
0214 MODULE_DEVICE_TABLE(of, bcm6358_leds_of_match);
0215
0216 static struct platform_driver bcm6358_leds_driver = {
0217 .probe = bcm6358_leds_probe,
0218 .driver = {
0219 .name = "leds-bcm6358",
0220 .of_match_table = bcm6358_leds_of_match,
0221 },
0222 };
0223
0224 module_platform_driver(bcm6358_leds_driver);
0225
0226 MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
0227 MODULE_DESCRIPTION("LED driver for BCM6358 controllers");
0228 MODULE_LICENSE("GPL v2");
0229 MODULE_ALIAS("platform:leds-bcm6358");