From d0104c106c14fa406f47bc7582fdacb0144ac278 Mon Sep 17 00:00:00 2001
From: Michal Hrusecky <Michal@Hrusecky.net>
Date: Sat, 23 Feb 2019 08:31:26 +0100
Subject: [PATCH] omnia: LEDs support

---
 ...nia-Add-kernel-patch-to-support-LEDs.patch | 512 -------------
 .../0005-Turris-Omnia-Improved-suppport.patch | 722 +++++++++++++++++-
 2 files changed, 720 insertions(+), 514 deletions(-)
 delete mode 100644 patches/openwrt/to-upstream/0013-turris-omnia-Add-kernel-patch-to-support-LEDs.patch

diff --git a/patches/openwrt/to-upstream/0013-turris-omnia-Add-kernel-patch-to-support-LEDs.patch b/patches/openwrt/to-upstream/0013-turris-omnia-Add-kernel-patch-to-support-LEDs.patch
deleted file mode 100644
index 25954463c..000000000
--- a/patches/openwrt/to-upstream/0013-turris-omnia-Add-kernel-patch-to-support-LEDs.patch
+++ /dev/null
@@ -1,512 +0,0 @@
-From c817ca7ef0e3f5a44f5e6dcd9ac144b76901c43e Mon Sep 17 00:00:00 2001
-From: Michal Hrusecky <michal.hrusecky@nic.cz>
-Date: Thu, 16 Aug 2018 17:23:18 +0200
-Subject: [PATCH] turris-omnia: Add kernel patch to support LEDs
-
-Signed-off-by: Michal Hrusecky <michal.hrusecky@nic.cz>
----
- .../mvebu/patches-4.14/402-omnia-leds.patch   | 492 ++++++++++++++++++
- 1 file changed, 492 insertions(+)
- create mode 100644 target/linux/mvebu/patches-4.14/402-omnia-leds.patch
-
-diff --git a/target/linux/mvebu/patches-4.14/402-omnia-leds.patch b/target/linux/mvebu/patches-4.14/402-omnia-leds.patch
-new file mode 100644
-index 0000000..68c9d9a
---- /dev/null
-+++ b/target/linux/mvebu/patches-4.14/402-omnia-leds.patch
-@@ -0,0 +1,492 @@
-+Index: linux-4.14.63/drivers/leds/Kconfig
-+===================================================================
-+--- linux-4.14.63.orig/drivers/leds/Kconfig
-++++ linux-4.14.63/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
-+Index: linux-4.14.63/drivers/leds/Makefile
-+===================================================================
-+--- linux-4.14.63.orig/drivers/leds/Makefile
-++++ linux-4.14.63/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
-+Index: linux-4.14.63/drivers/leds/leds-omnia.c
-+===================================================================
-+--- /dev/null
-++++ linux-4.14.63/drivers/leds/leds-omnia.c
-+@@ -0,0 +1,457 @@
-++/*
-++ * Copyright 2016 CZ.NIC, z.s.p.o.
-++ *
-++ * Author: Tomas Hlavacek <tmshlvck@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@gmail.com>");
-++MODULE_DESCRIPTION("Turris Omnia LED driver");
-++MODULE_LICENSE("GPL v2");
-++
--- 
-2.18.0
-
diff --git a/patches/openwrt/wip/0005-Turris-Omnia-Improved-suppport.patch b/patches/openwrt/wip/0005-Turris-Omnia-Improved-suppport.patch
index 65c7da3cf..67eb12b0d 100644
--- a/patches/openwrt/wip/0005-Turris-Omnia-Improved-suppport.patch
+++ b/patches/openwrt/wip/0005-Turris-Omnia-Improved-suppport.patch
@@ -1,4 +1,4 @@
-From 34fad100708cda71a306447c65a211fadde16d33 Mon Sep 17 00:00:00 2001
+From a707dc85b8714af7cd364a1ccae79737d01ad9a0 Mon Sep 17 00:00:00 2001
 From: Michal Hrusecky <Michal@Hrusecky.net>
 Date: Fri, 22 Feb 2019 08:30:39 +0100
 Subject: [PATCH] Turris Omnia: Improved suppport
@@ -6,9 +6,15 @@ Subject: [PATCH] Turris Omnia: Improved suppport
 ---
  ...88-turris-omnia-separate-dtb-for-sfp.patch | 908 ++++++++++++++++++
  ...turris-omnia-disable-unused-ethernet.patch |  38 +
- 2 files changed, 946 insertions(+)
+ .../8890-turris-omnia-led-driver.patch        | 323 +++++++
+ ...91-turris-omnia-custom-led-functions.patch | 269 ++++++
+ ...8892-turris-omnia-enable-leds-in-dts.patch | 102 ++
+ 5 files changed, 1640 insertions(+)
  create mode 100644 target/linux/mvebu/patches-4.14/8888-turris-omnia-separate-dtb-for-sfp.patch
  create mode 100644 target/linux/mvebu/patches-4.14/8889-turris-omnia-disable-unused-ethernet.patch
+ create mode 100644 target/linux/mvebu/patches-4.14/8890-turris-omnia-led-driver.patch
+ create mode 100644 target/linux/mvebu/patches-4.14/8891-turris-omnia-custom-led-functions.patch
+ create mode 100644 target/linux/mvebu/patches-4.14/8892-turris-omnia-enable-leds-in-dts.patch
 
 diff --git a/target/linux/mvebu/patches-4.14/8888-turris-omnia-separate-dtb-for-sfp.patch b/target/linux/mvebu/patches-4.14/8888-turris-omnia-separate-dtb-for-sfp.patch
 new file mode 100644
@@ -968,6 +974,718 @@ index 0000000000..05321731c2
 + 
 + /* Connected to 88E6176 switch, port 5 */
 + &eth1 {
+diff --git a/target/linux/mvebu/patches-4.14/8890-turris-omnia-led-driver.patch b/target/linux/mvebu/patches-4.14/8890-turris-omnia-led-driver.patch
+new file mode 100644
+index 0000000000..477370d4b1
+--- /dev/null
++++ b/target/linux/mvebu/patches-4.14/8890-turris-omnia-led-driver.patch
+@@ -0,0 +1,323 @@
++From d906c53c7433b3f20f4210284ec9b174c91f24c8 Mon Sep 17 00:00:00 2001
++From: Tomas Hlavacek <tmshlvck@gmail.com>
++Date: Thu, 16 Jun 2016 17:44:22 +0200
++Subject: [PATCH] Turris Omnia: Add LED driver for MCU LED driver
++
++Add leds-omnia driver that implements plain LED interface.
++
++Turris Omnia board have sophisticated but unfotunatelly completely proprietary I2C LED driver implemented in the MCU on the board.
++
++The I2C protocol description will be published in https://www.turris.cz/en/hardware .
++
++The driver does not allow to control proprietary functions that are hard to map to the Linux LED API:
++
++ * per-led brigtness - the only two states (LED on/off) are supported, even though each LED supports individual PWM based 8-bit RGB
++
++ * global_brightness - fast common PWM for all LEDs (can be decreased/increased by the HW button)
++
++ * color - slow PWM color functionality for each LED (the LEDs are RGB triplets, but the MCU abstraction makes it hard to do the usual RGB mapping)
++
++ * autonomous operation - each LED can operate autonomously (being driven by MCU based on the assigned indicator output from PHY/ethernet switch/PCI cards etc.), autonomous mode is on by default when the Linux led driver is not loaded. Once the Linux LED driver is loaded and once an individual LED is accessed (by setting the brightness) for the first time, it changes to the non-autonomous mode and it can not be set back to autonomous operation mode from the Linux LED driver without unloading it. When this driver is removed all LEDs are set back to autonomous mode.
++
++The MCU supports setting all LEDs at once by setting the virtual 12th LED. This (hackish) feature is supported as well.
++---
++ drivers/leds/Kconfig      |   7 ++
++ drivers/leds/Makefile     |   1 +
++ drivers/leds/leds-omnia.c | 259 ++++++++++++++++++++++++++++++++++++++
++ 3 files changed, 267 insertions(+)
++ create mode 100644 drivers/leds/leds-omnia.c
++
++Index: linux-4.14.101/drivers/leds/Kconfig
++===================================================================
++--- linux-4.14.101.orig/drivers/leds/Kconfig
+++++ linux-4.14.101/drivers/leds/Kconfig
++@@ -630,6 +630,13 @@ config LEDS_IS31FL32XX
++ 	  LED controllers. They are I2C devices with multiple constant-current
++ 	  channels, each with independent 256-level PWM control.
++ 
+++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.
+++
++ comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)"
++ 
++ config LEDS_BLINKM
++Index: linux-4.14.101/drivers/leds/Makefile
++===================================================================
++--- linux-4.14.101.orig/drivers/leds/Makefile
+++++ linux-4.14.101/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
++Index: linux-4.14.101/drivers/leds/leds-omnia.c
++===================================================================
++--- /dev/null
+++++ linux-4.14.101/drivers/leds/leds-omnia.c
++@@ -0,0 +1,259 @@
+++/*
+++ * Copyright 2016 CZ.NIC, z.s.p.o.
+++ *
+++ * Author: Tomas Hlavacek <tmshlvck@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>
+++
+++#define MAX_LEDS 13
+++#define ALL_LEDS_INDEX 12
+++
+++#define LED_AUTONOMOUS_ADDR 3
+++#define LED_ONOFF_ADDR 4
+++
+++
+++
+++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;
+++};
+++
+++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_led_set(struct led_classdev *led_cdev,
+++	enum led_brightness value)
+++{
+++	struct omnia_led *led;
+++	int ret;
+++
+++	led = container_of(led_cdev, struct omnia_led, led_cdev);
+++
+++	if (led->autonomous) {
+++		ret = omnia_led_autonomous_set(led, 0);
+++		if (ret < 0)
+++			return ret;
+++	}
+++
+++	return omnia_led_brightness_set(led, value);
+++}
+++
+++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 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;
+++
+++		/* 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_blocking = omnia_led_set;
+++
+++		err = led_classdev_register(&client->dev, &leds[i].led_cdev);
+++		if (err < 0)
+++			goto exit;
+++
+++		/* Set AUTO for all LEDs by default */
+++		leds[i].autonomous = 0;
+++		omnia_led_autonomous_set(&leds[i], 1);
+++	}
+++
+++	return 0;
+++
+++exit:
+++	while (i--) {
+++		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;
+++
+++	for (i = 0; i < MAX_LEDS; i++) {
+++		led_classdev_unregister(&chip->leds[i].led_cdev);
+++
+++		/* Set AUTO for the LED */
+++		omnia_led_autonomous_set(&chip->leds[i], 1);
+++	}
+++
+++	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@gmail.com>");
+++MODULE_DESCRIPTION("Turris Omnia LED driver");
+++MODULE_LICENSE("GPL v2");
+++
+diff --git a/target/linux/mvebu/patches-4.14/8891-turris-omnia-custom-led-functions.patch b/target/linux/mvebu/patches-4.14/8891-turris-omnia-custom-led-functions.patch
+new file mode 100644
+index 0000000000..739d30c027
+--- /dev/null
++++ b/target/linux/mvebu/patches-4.14/8891-turris-omnia-custom-led-functions.patch
+@@ -0,0 +1,269 @@
++From d8f1b754703e91bb5156e27e4f0ad5d974328200 Mon Sep 17 00:00:00 2001
++From: Tomas Hlavacek <tmshlvck@gmail.com>
++Date: Fri, 22 Feb 2019 20:52:33 +0100
++Subject: [PATCH] Turris Omnia: Add proprietary part of LED driver
++
++Add proprietary part of leds-omnia driver that implements Linux LED interfacei.
++
++On top of that driver that is able to set non-autonomous mode and use slow PWM to drive individual LEDs and also the 12th virtual ALL LED add following sysfs files for setting:
++
++global_brightness - [0..100] = PWM base for all the LEDs. It is the maximum intensity. Particular colors on each LED might be decreased individually.
++
++color - [0..255] [0..255] [0..255] = PWM for each color on each LED.
++
++autonomous [0|1] = 1=MCU drives the LED according to its default function, blink from Linux LED driver is ignored; 0=Linux LED driver sets brightness/blink.
++---
++ drivers/leds/leds-omnia.c | 195 ++++++++++++++++++++++++++++++++++++++
++ 1 file changed, 195 insertions(+)
++
++diff --git a/drivers/leds/leds-omnia.c b/drivers/leds/leds-omnia.c
++index 0e10e806556e..38d8c8251b2f 100644
++--- a/drivers/leds/leds-omnia.c
+++++ b/drivers/leds/leds-omnia.c
++@@ -23,6 +23,9 @@
++ 
++ #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
++ 
++ 
++ 
++@@ -48,6 +51,9 @@ struct omnia_led {
++ 	int led_num; /* 0 .. 11 + 12=ALL */
++ 	char name[32];
++ 	u8 autonomous;
+++	u8 r;
+++	u8 g;
+++	u8 b;
++ };
++ 
++ static int omnia_led_brightness_set(struct omnia_led *led,
++@@ -89,6 +95,59 @@ static int omnia_led_autonomous_set(struct omnia_led *led, int autonomous)
++ 	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 int omnia_led_set(struct led_classdev *led_cdev,
++ 	enum led_brightness value)
++ {
++@@ -146,6 +205,98 @@ omnia_dt_init(struct i2c_client *client)
++ 	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", },
++ 	{},
++@@ -211,15 +362,52 @@ static int omnia_probe(struct i2c_client *client,
++ 		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);
++ 	}
++ 
++@@ -231,7 +419,14 @@ 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);
++ 
++ 		/* Set AUTO for the LED */
+diff --git a/target/linux/mvebu/patches-4.14/8892-turris-omnia-enable-leds-in-dts.patch b/target/linux/mvebu/patches-4.14/8892-turris-omnia-enable-leds-in-dts.patch
+new file mode 100644
+index 0000000000..c162f2270b
+--- /dev/null
++++ b/target/linux/mvebu/patches-4.14/8892-turris-omnia-enable-leds-in-dts.patch
+@@ -0,0 +1,102 @@
++From d5945cfa05deba62d79d9f75ab3308047f459c51 Mon Sep 17 00:00:00 2001
++From: Tomas Hlavacek <tmshlvck@gmail.com>
++Date: Thu, 16 Jun 2016 17:51:26 +0200
++Subject: [PATCH] Turris Omnia: Add Omnia LED driver to DT
++
++Add configuration for the leds-omnia driver.
++
++Use the labels from enclosure as the LED names. Use I2C address 0x2b.
++
++The MCU on Omnia board have three I2C slave addresses: 0x2a, 0x2b and 0x2c.
++
++0x2a should be used from userspace applications
++0x2b should be used from kernel LED driver
++0x2c is the special address for bootloader image transfer
++---
++ .../arm/boot/dts/armada-385-turris-omnia.dtsi | 73 +++++++++++++++++++
++ 1 file changed, 73 insertions(+)
++
++diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dtsi b/arch/arm/boot/dts/armada-385-turris-omnia.dtsi
++index 5045015ce823..d968cda97113 100644
++--- a/arch/arm/boot/dts/armada-385-turris-omnia.dtsi
+++++ b/arch/arm/boot/dts/armada-385-turris-omnia.dtsi
++@@ -143,6 +143,79 @@
++ 				 * 	};
++ 				 */
++ 			};
+++
+++			leds@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>;
+++				};
+++			};
++ 		};
++ 
++ 		i2c@1 {
 -- 
 2.20.1
 
-- 
GitLab