[OpenWrt-Devel] [PATCH 1/4] mvebu: add Turris Omnia LED support
Klaus Kudielka
klaus.kudielka at gmail.com
Thu Dec 13 00:29:49 EST 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", ®);
++ 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
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel
More information about the openwrt-devel
mailing list