[OpenWrt-Devel] [PATCH 1/4] mvebu: add Turris Omnia LED support

Klaus Kudielka klaus.kudielka at gmail.com
Wed Dec 12 21:29:49 PST 2018


The kernel driver is directly pulled from TurrisOS.
https://gitlab.labs.nic.cz/turris/turris-build/commit/60f75b6b

I have added the associated extension of the device tree.

Signed-off-by: Klaus Kudielka <klaus.kudielka at gmail.com>
---
 .../mvebu/patches-4.14/490-omnia-leds.patch   | 568 ++++++++++++++++++
 1 file changed, 568 insertions(+)
 create mode 100644 target/linux/mvebu/patches-4.14/490-omnia-leds.patch

diff --git a/target/linux/mvebu/patches-4.14/490-omnia-leds.patch b/target/linux/mvebu/patches-4.14/490-omnia-leds.patch
new file mode 100644
index 0000000000..e819eb247f
--- /dev/null
+++ b/target/linux/mvebu/patches-4.14/490-omnia-leds.patch
@@ -0,0 +1,568 @@
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -632,6 +632,13 @@ config LEDS_IS31FL32XX
+ 
+ comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)"
+ 
++config LEDS_OMNIA
++       tristate "LED support for the Turris Omnia board"
++       depends on LEDS_CLASS
++       help
++         Say Y here to include support for the LED driver on Turris Omnia
++         board.
++
+ config LEDS_BLINKM
+ 	tristate "LED support for the BlinkM I2C RGB LED"
+ 	depends on LEDS_CLASS
+--- a/drivers/leds/Makefile
++++ b/drivers/leds/Makefile
+@@ -73,6 +73,7 @@ obj-$(CONFIG_LEDS_PM8058)		+= leds-pm805
+ obj-$(CONFIG_LEDS_MLXCPLD)		+= leds-mlxcpld.o
+ obj-$(CONFIG_LEDS_NIC78BX)		+= leds-nic78bx.o
+ obj-$(CONFIG_LEDS_MT6323)		+= leds-mt6323.o
++obj-$(CONFIG_LEDS_OMNIA)		+= leds-omnia.o
+ 
+ # LED SPI Drivers
+ obj-$(CONFIG_LEDS_DAC124S085)		+= leds-dac124s085.o
+--- /dev/null
++++ b/drivers/leds/leds-omnia.c
+@@ -0,0 +1,457 @@
++/*
++ * Copyright 2016 CZ.NIC, z.s.p.o.
++ *
++ * Author: Tomas Hlavacek <tmshlvck at gmail.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/string.h>
++#include <linux/ctype.h>
++#include <linux/leds.h>
++#include <linux/err.h>
++#include <linux/i2c.h>
++#include <linux/slab.h>
++#include <linux/of.h>
++#include <linux/workqueue.h>
++
++#define MAX_LEDS 13
++#define ALL_LEDS_INDEX 12
++
++#define LED_AUTONOMOUS_ADDR 3
++#define LED_ONOFF_ADDR 4
++#define LED_COLOR_ADDR 5
++#define GLOB_BRIGHTNESS_READ 8
++#define GLOB_BRIGHTNESS_WRITE 7
++
++
++
++struct omnia_platform_data {
++        struct led_platform_data leds;
++};
++
++static const struct i2c_device_id omnia_id[] = {
++	{ "omnia", 0 },
++	{ }
++};
++MODULE_DEVICE_TABLE(i2c, omnia_id);
++
++struct omnia_led_mcu {
++	struct mutex mutex;
++	struct i2c_client *client;
++	struct omnia_led *leds;
++};
++
++struct omnia_led {
++	struct omnia_led_mcu *chip;
++	struct led_classdev led_cdev;
++	int led_num; /* 0 .. 11 + 12=ALL */
++	char name[32];
++	u8 autonomous;
++	u8 r;
++	u8 g;
++	u8 b;
++	struct work_struct work;
++	enum led_brightness new_brightness;
++};
++
++static int omnia_led_brightness_set(struct omnia_led *led,
++				enum led_brightness brightness)
++{
++	int ret;
++
++	mutex_lock(&led->chip->mutex);
++
++	ret = i2c_smbus_write_byte_data(led->chip->client, LED_ONOFF_ADDR,
++			(led->led_num | ((brightness != LED_OFF)<<4)));
++
++	mutex_unlock(&led->chip->mutex);
++	return ret;
++}
++
++static int omnia_led_autonomous_set(struct omnia_led *led, int autonomous)
++{
++	int ret, i;
++
++	mutex_lock(&led->chip->mutex);
++
++	if (led->autonomous == (autonomous != 0)) {
++		mutex_unlock(&led->chip->mutex);
++		return 0;
++	}
++
++	led->autonomous = (autonomous != 0);
++
++	if (led->led_num == ALL_LEDS_INDEX) {
++		for (i=0; i<(MAX_LEDS-1); i++)
++			led->chip->leds[i].autonomous = led->autonomous;
++	}
++
++	ret = i2c_smbus_write_byte_data(led->chip->client, LED_AUTONOMOUS_ADDR,
++			(u8)(led->led_num | ((!led->autonomous) << 4)));
++
++	mutex_unlock(&led->chip->mutex);
++	return ret;
++}
++
++static int omnia_glob_brightness_set(struct omnia_led_mcu *chip,
++					int glob_brightness)
++{
++	int ret;
++
++	mutex_lock(&chip->mutex);
++
++	ret = i2c_smbus_write_byte_data(chip->client, GLOB_BRIGHTNESS_WRITE,
++						(u8)glob_brightness);
++
++	mutex_unlock(&chip->mutex);
++	return ret;
++}
++
++static int omnia_glob_brightness_get(struct omnia_led_mcu *chip)
++{
++	int ret;
++
++	mutex_lock(&chip->mutex);
++
++	ret = i2c_smbus_read_byte_data(chip->client, GLOB_BRIGHTNESS_READ);
++
++	mutex_unlock(&chip->mutex);
++	return ret;
++}
++
++static int omnia_led_color_set(struct omnia_led *led, u8 r, u8 g, u8 b)
++{
++	int ret, i;
++	u8 buf[5];
++
++	buf[0] = LED_COLOR_ADDR;
++	buf[1] = led->led_num;
++	buf[2] = r;
++	buf[3] = g;
++	buf[4] = b;
++
++	mutex_lock(&led->chip->mutex);
++
++	ret = i2c_master_send(led->chip->client, buf, 5);
++
++	if (led->led_num == ALL_LEDS_INDEX) {
++		for (i=0; i<(MAX_LEDS-1); i++) {
++			led->chip->leds[i].r = led->r;
++			led->chip->leds[i].g = led->g;
++			led->chip->leds[i].b = led->b;
++		}
++	}
++
++	mutex_unlock(&led->chip->mutex);
++	return -(ret<=0);
++}
++
++static void omnia_led_set_async(struct led_classdev *led_cdev,
++        enum led_brightness value)
++{
++	struct omnia_led *led =
++		container_of(led_cdev, struct omnia_led, led_cdev);
++
++	led->new_brightness = value;
++
++	schedule_work(&led->work);
++}
++
++static void omnia_led_work(struct work_struct *work)
++{
++        struct omnia_led *led;
++        led = container_of(work, struct omnia_led, work);
++
++        omnia_led_brightness_set(led, led->new_brightness);
++}
++
++static struct omnia_platform_data *
++omnia_dt_init(struct i2c_client *client)
++{
++	struct device_node *np = client->dev.of_node, *child;
++	struct omnia_platform_data *pdata;
++	struct led_info *leds;
++	int count;
++
++	count = of_get_child_count(np);
++	if (!count || count > MAX_LEDS)
++		return ERR_PTR(-ENODEV);
++
++	leds = devm_kzalloc(&client->dev,
++			sizeof(struct led_info) * MAX_LEDS, GFP_KERNEL);
++	if (!leds)
++		return ERR_PTR(-ENOMEM);
++
++	for_each_child_of_node(np, child) {
++		u32 reg;
++		int res;
++
++		res = of_property_read_u32(child, "reg", &reg);
++		if ((res != 0) || (reg >= MAX_LEDS))
++			continue;
++		leds[reg].name =
++			of_get_property(child, "label", NULL) ? : child->name;
++		leds[reg].default_trigger =
++			of_get_property(child, "linux,default-trigger", NULL);
++	}
++	pdata = devm_kzalloc(&client->dev,
++			     sizeof(struct omnia_platform_data), GFP_KERNEL);
++	if (!pdata)
++		return ERR_PTR(-ENOMEM);
++
++	pdata->leds.leds = leds;
++	pdata->leds.num_leds = MAX_LEDS;
++
++	return pdata;
++}
++
++static ssize_t global_brightness_show(struct device *d,
++                struct device_attribute *attr, char *buf)
++{
++	struct i2c_client *client = to_i2c_client(d);
++	struct omnia_led_mcu *chip = i2c_get_clientdata(client);
++
++	return scnprintf(buf, PAGE_SIZE, "%d\n",
++				omnia_glob_brightness_get(chip));
++}
++
++static ssize_t global_brightness_store(struct device *d,
++                struct device_attribute *attr, const char *buf, size_t count)
++{
++	struct i2c_client *client = to_i2c_client(d);
++        struct omnia_led_mcu *chip = i2c_get_clientdata(client);
++	int ret;
++	int global_brightness;
++
++	if ((sscanf(buf, "%i", &global_brightness)) != 1)
++		return -EINVAL;
++
++	ret = omnia_glob_brightness_set(chip, global_brightness);
++	if (ret < 0)
++		return ret;
++
++	return count;
++}
++static DEVICE_ATTR_RW(global_brightness);
++
++static ssize_t autonomous_show(struct device *d,
++                struct device_attribute *attr, char *buf)
++{
++	struct led_classdev *led_cdev = dev_get_drvdata(d);
++	struct omnia_led *led =
++			container_of(led_cdev, struct omnia_led, led_cdev);
++
++	return scnprintf(buf, PAGE_SIZE, "%d\n", led->autonomous);
++}
++
++static ssize_t autonomous_store(struct device *d,
++                struct device_attribute *attr, const char *buf, size_t count)
++{
++	int ret, autonomous;
++	struct led_classdev *led_cdev = dev_get_drvdata(d);
++	struct omnia_led *led =
++			container_of(led_cdev, struct omnia_led, led_cdev);
++
++	if ((sscanf(buf, "%i", &autonomous)) != 1)
++		return -EINVAL;
++
++	ret = omnia_led_autonomous_set(led, autonomous);
++	if (ret < 0)
++		return ret;
++
++	led->autonomous = autonomous;
++	return count;
++}
++static DEVICE_ATTR_RW(autonomous);
++
++static ssize_t color_show(struct device *d,
++                struct device_attribute *attr, char *buf)
++{
++	struct led_classdev *led_cdev = dev_get_drvdata(d);
++	struct omnia_led *led =
++			container_of(led_cdev, struct omnia_led, led_cdev);
++
++	return scnprintf(buf, PAGE_SIZE, "%d %d %d\n", led->r, led->g, led->b);
++}
++
++static ssize_t color_store(struct device *d,
++                struct device_attribute *attr, const char *buf, size_t count)
++{
++	int ret, r, g, b;
++	struct led_classdev *led_cdev = dev_get_drvdata(d);
++	struct omnia_led *led =
++			container_of(led_cdev, struct omnia_led, led_cdev);
++
++	if ((sscanf(buf, "%i %i %i", &r, &g, &b)) != 3)
++		return -EINVAL;
++
++	ret = omnia_led_color_set(led, r, g, b);
++	if (ret < 0)
++		return ret;
++
++	led->r = r;
++	led->g = g;
++	led->b = b;
++	return count;
++}
++static DEVICE_ATTR_RW(color);
++
++
++static const struct of_device_id of_omnia_match[] = {
++	{ .compatible = "turris-leds,omnia", },
++	{},
++};
++MODULE_DEVICE_TABLE(of, of_omnia_match);
++
++static int omnia_probe(struct i2c_client *client,
++					const struct i2c_device_id *id)
++{
++	struct omnia_led_mcu *chip;
++	struct omnia_led *leds;
++	struct omnia_platform_data *pdata;
++	int i, err;
++
++	pdata = dev_get_platdata(&client->dev);
++
++	if (!pdata) {
++		pdata = omnia_dt_init(client);
++		if (IS_ERR(pdata)) {
++			dev_warn(&client->dev, "could not parse configuration\n");
++			pdata = NULL;
++		}
++	}
++
++	chip = devm_kzalloc(&client->dev, sizeof(*chip),
++				GFP_KERNEL);
++	if (!chip)
++		return -ENOMEM;
++	leds = devm_kzalloc(&client->dev, MAX_LEDS * sizeof(*leds),
++				GFP_KERNEL);
++	if (!leds)
++		return -ENOMEM;
++
++	i2c_set_clientdata(client, chip);
++
++	mutex_init(&chip->mutex);
++	chip->client = client;
++	chip->leds = leds;
++
++	for (i = 0; i < MAX_LEDS; i++) {
++		leds[i].led_num = i;
++		leds[i].chip = chip;
++		INIT_WORK(&leds[i].work, omnia_led_work);
++
++		/* Platform data can specify LED names and default triggers */
++		if (pdata && i < pdata->leds.num_leds) {
++			if (pdata->leds.leds[i].name)
++				snprintf(leds[i].name,
++					 sizeof(leds[i].name), "omnia-led:%s",
++					 pdata->leds.leds[i].name);
++			if (pdata->leds.leds[i].default_trigger)
++				leds[i].led_cdev.default_trigger =
++					pdata->leds.leds[i].default_trigger;
++		}
++		if (!pdata || i >= pdata->leds.num_leds ||
++						!pdata->leds.leds[i].name)
++			snprintf(leds[i].name, sizeof(leds[i].name),
++				 "omnia-led:%d", i);
++
++		leds[i].led_cdev.name = leds[i].name;
++		leds[i].led_cdev.brightness_set = omnia_led_set_async;
++
++		err = led_classdev_register(&client->dev, &leds[i].led_cdev);
++		if (err < 0)
++			goto exit;
++
++		err = device_create_file(leds[i].led_cdev.dev,
++						&dev_attr_autonomous);
++		if (err < 0) {
++			dev_err(leds[i].led_cdev.dev,
++				"failed to create attribute autonomous\n");
++			goto exit;
++		}
++
++		err = device_create_file(leds[i].led_cdev.dev,
++						&dev_attr_color);
++		if (err < 0) {
++			dev_err(leds[i].led_cdev.dev,
++				"failed to create attribute color\n");
++			goto exit;
++		}
++
++		/* Set AUTO for all LEDs by default */
++		leds[i].autonomous = 0;
++		omnia_led_autonomous_set(&leds[i], 1);
++
++		/* Set brightness to LED_OFF by default */
++		omnia_led_brightness_set(&leds[i], LED_OFF);
++
++		/* MCU default color is white */
++		leds[i].r = 255;
++		leds[i].g = 255;
++		leds[i].b = 255;
++	}
++
++	err = device_create_file(&client->dev, &dev_attr_global_brightness);
++	if (err < 0) {
++		dev_err(&client->dev,
++			"failed to create attribute global_brightness\n");
++		goto exit;
++	}
++
++	return 0;
++
++exit:
++	device_remove_file(&client->dev, &dev_attr_global_brightness);
++	while (i--) {
++		device_remove_file(chip->leds[i].led_cdev.dev,
++			&dev_attr_color);
++		device_remove_file(chip->leds[i].led_cdev.dev,
++			&dev_attr_autonomous);
++
++		led_classdev_unregister(&leds[i].led_cdev);
++	}
++
++	return err;
++}
++
++static int omnia_remove(struct i2c_client *client)
++{
++	struct omnia_led_mcu *chip = i2c_get_clientdata(client);
++	int i;
++
++	device_remove_file(&client->dev, &dev_attr_global_brightness);
++
++	for (i = 0; i < MAX_LEDS; i++) {
++		device_remove_file(chip->leds[i].led_cdev.dev,
++			&dev_attr_color);
++		device_remove_file(chip->leds[i].led_cdev.dev,
++			&dev_attr_autonomous);
++
++		led_classdev_unregister(&chip->leds[i].led_cdev);
++	}
++
++	return 0;
++}
++
++static struct i2c_driver omnia_driver = {
++	.driver = {
++		.name	= "leds-omnia",
++		.of_match_table = of_match_ptr(of_omnia_match),
++	},
++	.probe	= omnia_probe,
++	.remove	= omnia_remove,
++	.id_table = omnia_id,
++};
++
++module_i2c_driver(omnia_driver);
++
++MODULE_AUTHOR("Tomas Hlavacek <tmshlvck at gmail.com>");
++MODULE_DESCRIPTION("Turris Omnia LED driver");
++MODULE_LICENSE("GPL v2");
++
+--- a/arch/arm/boot/dts/armada-385-turris-omnia.dts
++++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts
+@@ -168,7 +168,78 @@
+ 			reg = <0>;
+ 
+ 			/* STM32F0 command interface at address 0x2a */
+-			/* leds device (in STM32F0) at address 0x2b */
++
++			leds at 2b {
++				compatible = "turris-leds,omnia";
++				reg = <0x2b>;
++				#address-cells = <1>;
++				#size-cells = <0>;
++
++				led0 {
++					label = "user2";
++					reg = <0x0>;
++				};
++
++				led1 {
++					label = "user1";
++					reg = <0x1>;
++				};
++
++				led2 {
++					label = "pci3";
++					reg = <0x2>;
++				};
++
++				led3 {
++					label = "pci2";
++					reg = <0x3>;
++				};
++
++				led4 {
++					label = "pci1";
++					reg = <0x4>;
++				};
++
++				led5 {
++					label = "wan";
++					reg = <0x5>;
++				};
++
++				led6 {
++					label = "lan4";
++					reg = <0x6>;
++				};
++
++				led7 {
++					label = "lan3";
++					reg = <0x7>;
++				};
++
++				led8 {
++					label = "lan2";
++					reg = <0x8>;
++				};
++
++				led9 {
++					label = "lan1";
++					reg = <0x9>;
++				};
++
++				led10 {
++					label = "lan0";
++					reg = <0xa>;
++				};
++
++				led11 {
++					label = "power";
++					reg = <0xb>;
++				};
++
++				led12 {
++					label = "all";
++					reg = <0xc>;
++				};
++			};
+ 
+ 			eeprom at 54 {
+ 				compatible = "atmel,24c64";
-- 
2.17.1




More information about the openwrt-devel mailing list