All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] LED interface for PowerNV platform
@ 2015-03-08 11:13 ` Vasant Hegde
  0 siblings, 0 replies; 12+ messages in thread
From: Vasant Hegde @ 2015-03-08 11:13 UTC (permalink / raw)
  To: linuxppc-dev, linux-leds; +Cc: mpe, khandual, cooloney, rpurdie, benh

The following series implements LED driver for PowerNV platform.

PowerNV platform has below type of indicators:
  - System attention indicator (Check log indicator)
        Indicates there is a problem with the system that needs attention.
  - Identify
        Helps the user locate/identify a particular FRU or resource in the
        system.
  - Fault
	Indicates there is a problem with the FRU or resource at the
	location with which the indicator is associated.

On PowerNV (Non Virtualized) platform OPAL firmware provides LED
information to host via device tree (location code and LED type).
During init we check for 'ibm,opal/led' node in device tree to
enabled LED driver. And we use OPAL API's to get/set LEDs.

patch 1/2: PowerNV architecture specific code. This adds necessary
  OPAL APIs.
patch 2/2 is actual LED driver implemenation for PowerNV platform.

TODO:
  - Add support for System Attention Indicator
---
Anshuman Khandual (2):
      powerpc/powernv: Add OPAL interfaces for accessing and modifying system LED states
      leds/powernv: Add driver for PowerNV platform


 arch/powerpc/include/asm/opal.h                |   19 +
 arch/powerpc/platforms/powernv/opal-wrappers.S |    2 
 arch/powerpc/platforms/powernv/opal.c          |   12 
 drivers/leds/Kconfig                           |    9 
 drivers/leds/Makefile                          |    1 
 drivers/leds/leds-powernv.c                    |  599 ++++++++++++++++++++++++
 6 files changed, 635 insertions(+), 7 deletions(-)
 create mode 100644 drivers/leds/leds-powernv.c

--
Vasant
Signature

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 0/2] LED interface for PowerNV platform
@ 2015-03-08 11:13 ` Vasant Hegde
  0 siblings, 0 replies; 12+ messages in thread
From: Vasant Hegde @ 2015-03-08 11:13 UTC (permalink / raw)
  To: linuxppc-dev, linux-leds; +Cc: cooloney, rpurdie, khandual

The following series implements LED driver for PowerNV platform.

PowerNV platform has below type of indicators:
  - System attention indicator (Check log indicator)
        Indicates there is a problem with the system that needs attention.
  - Identify
        Helps the user locate/identify a particular FRU or resource in the
        system.
  - Fault
	Indicates there is a problem with the FRU or resource at the
	location with which the indicator is associated.

On PowerNV (Non Virtualized) platform OPAL firmware provides LED
information to host via device tree (location code and LED type).
During init we check for 'ibm,opal/led' node in device tree to
enabled LED driver. And we use OPAL API's to get/set LEDs.

patch 1/2: PowerNV architecture specific code. This adds necessary
  OPAL APIs.
patch 2/2 is actual LED driver implemenation for PowerNV platform.

TODO:
  - Add support for System Attention Indicator
---
Anshuman Khandual (2):
      powerpc/powernv: Add OPAL interfaces for accessing and modifying system LED states
      leds/powernv: Add driver for PowerNV platform


 arch/powerpc/include/asm/opal.h                |   19 +
 arch/powerpc/platforms/powernv/opal-wrappers.S |    2 
 arch/powerpc/platforms/powernv/opal.c          |   12 
 drivers/leds/Kconfig                           |    9 
 drivers/leds/Makefile                          |    1 
 drivers/leds/leds-powernv.c                    |  599 ++++++++++++++++++++++++
 6 files changed, 635 insertions(+), 7 deletions(-)
 create mode 100644 drivers/leds/leds-powernv.c

--
Vasant
Signature

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/2] powerpc/powernv: Add OPAL interfaces for accessing and modifying system LED states
  2015-03-08 11:13 ` Vasant Hegde
@ 2015-03-08 11:14   ` Vasant Hegde
  -1 siblings, 0 replies; 12+ messages in thread
From: Vasant Hegde @ 2015-03-08 11:14 UTC (permalink / raw)
  To: linuxppc-dev, linux-leds; +Cc: mpe, khandual, cooloney, rpurdie, benh

From: Anshuman Khandual <khandual@linux.vnet.ibm.com>

This patch registers the following two new OPAL interfaces calls
for the platform LED subsystem. With the help of these new OPAL calls,
the kernel will be able to get or set the state of various individual
LEDs on the system at any given location code which is passed through
the LED specific device tree nodes.

	(1) OPAL_LEDS_GET_INDICATOR     opal_leds_get_ind
	(2) OPAL_LEDS_SET_INDICATOR     opal_leds_set_ind

Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/opal.h                |   19 +++++++++++++------
 arch/powerpc/platforms/powernv/opal-wrappers.S |    2 ++
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 9ee0a30..462ee59 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -169,6 +169,8 @@ struct opal_sg_list {
 #define OPAL_IPMI_SEND				107
 #define OPAL_IPMI_RECV				108
 #define OPAL_I2C_REQUEST			109
+#define OPAL_LEDS_GET_INDICATOR			114
+#define OPAL_LEDS_SET_INDICATOR			115
 
 /* Device tree flags */
 
@@ -380,14 +382,14 @@ enum OpalPciMaskAction {
 };
 
 enum OpalSlotLedType {
-	OPAL_SLOT_LED_ID_TYPE = 0,
-	OPAL_SLOT_LED_FAULT_TYPE = 1
+	OPAL_SLOT_LED_TYPE_ID = 0,	/* IDENTIFY LED */
+	OPAL_SLOT_LED_TYPE_FAULT = 1,	/* FAULT LED */
+	OPAL_SLOT_LED_TYPE_MAX = 2
 };
 
-enum OpalLedAction {
-	OPAL_TURN_OFF_LED = 0,
-	OPAL_TURN_ON_LED = 1,
-	OPAL_QUERY_LED_STATE_AFTER_BUSY = 2
+enum OpalSlotLedState {
+	OPAL_SLOT_LED_STATE_OFF = 0,	/* LED is OFF */
+	OPAL_SLOT_LED_STATE_ON = 1	/* LED is ON */
 };
 
 enum OpalEpowStatus {
@@ -931,6 +933,11 @@ int64_t opal_ipmi_recv(uint64_t interface, struct opal_ipmi_msg *msg,
 		uint64_t *msg_len);
 int64_t opal_i2c_request(uint64_t async_token, uint32_t bus_id,
 			 struct opal_i2c_request *oreq);
+int64_t opal_leds_get_ind(char *loc_code, u64 *led_mask,
+			  u64 *led_value, u64 *max_led_type);
+int64_t opal_leds_set_ind(uint64_t token, char *loc_code, const u64 led_mask,
+			  const u64 led_value, u64 *max_led_type);
+
 
 /* Internal functions */
 extern int early_init_dt_scan_opal(unsigned long node, const char *uname,
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index 0509bca..86f2acf 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -292,3 +292,5 @@ OPAL_CALL(opal_tpo_read,			OPAL_READ_TPO);
 OPAL_CALL(opal_ipmi_send,			OPAL_IPMI_SEND);
 OPAL_CALL(opal_ipmi_recv,			OPAL_IPMI_RECV);
 OPAL_CALL(opal_i2c_request,			OPAL_I2C_REQUEST);
+OPAL_CALL(opal_leds_get_ind,			OPAL_LEDS_GET_INDICATOR);
+OPAL_CALL(opal_leds_set_ind,			OPAL_LEDS_SET_INDICATOR);

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 1/2] powerpc/powernv: Add OPAL interfaces for accessing and modifying system LED states
@ 2015-03-08 11:14   ` Vasant Hegde
  0 siblings, 0 replies; 12+ messages in thread
From: Vasant Hegde @ 2015-03-08 11:14 UTC (permalink / raw)
  To: linuxppc-dev, linux-leds; +Cc: cooloney, rpurdie, khandual

From: Anshuman Khandual <khandual@linux.vnet.ibm.com>

This patch registers the following two new OPAL interfaces calls
for the platform LED subsystem. With the help of these new OPAL calls,
the kernel will be able to get or set the state of various individual
LEDs on the system at any given location code which is passed through
the LED specific device tree nodes.

	(1) OPAL_LEDS_GET_INDICATOR     opal_leds_get_ind
	(2) OPAL_LEDS_SET_INDICATOR     opal_leds_set_ind

Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/opal.h                |   19 +++++++++++++------
 arch/powerpc/platforms/powernv/opal-wrappers.S |    2 ++
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 9ee0a30..462ee59 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -169,6 +169,8 @@ struct opal_sg_list {
 #define OPAL_IPMI_SEND				107
 #define OPAL_IPMI_RECV				108
 #define OPAL_I2C_REQUEST			109
+#define OPAL_LEDS_GET_INDICATOR			114
+#define OPAL_LEDS_SET_INDICATOR			115
 
 /* Device tree flags */
 
@@ -380,14 +382,14 @@ enum OpalPciMaskAction {
 };
 
 enum OpalSlotLedType {
-	OPAL_SLOT_LED_ID_TYPE = 0,
-	OPAL_SLOT_LED_FAULT_TYPE = 1
+	OPAL_SLOT_LED_TYPE_ID = 0,	/* IDENTIFY LED */
+	OPAL_SLOT_LED_TYPE_FAULT = 1,	/* FAULT LED */
+	OPAL_SLOT_LED_TYPE_MAX = 2
 };
 
-enum OpalLedAction {
-	OPAL_TURN_OFF_LED = 0,
-	OPAL_TURN_ON_LED = 1,
-	OPAL_QUERY_LED_STATE_AFTER_BUSY = 2
+enum OpalSlotLedState {
+	OPAL_SLOT_LED_STATE_OFF = 0,	/* LED is OFF */
+	OPAL_SLOT_LED_STATE_ON = 1	/* LED is ON */
 };
 
 enum OpalEpowStatus {
@@ -931,6 +933,11 @@ int64_t opal_ipmi_recv(uint64_t interface, struct opal_ipmi_msg *msg,
 		uint64_t *msg_len);
 int64_t opal_i2c_request(uint64_t async_token, uint32_t bus_id,
 			 struct opal_i2c_request *oreq);
+int64_t opal_leds_get_ind(char *loc_code, u64 *led_mask,
+			  u64 *led_value, u64 *max_led_type);
+int64_t opal_leds_set_ind(uint64_t token, char *loc_code, const u64 led_mask,
+			  const u64 led_value, u64 *max_led_type);
+
 
 /* Internal functions */
 extern int early_init_dt_scan_opal(unsigned long node, const char *uname,
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index 0509bca..86f2acf 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -292,3 +292,5 @@ OPAL_CALL(opal_tpo_read,			OPAL_READ_TPO);
 OPAL_CALL(opal_ipmi_send,			OPAL_IPMI_SEND);
 OPAL_CALL(opal_ipmi_recv,			OPAL_IPMI_RECV);
 OPAL_CALL(opal_i2c_request,			OPAL_I2C_REQUEST);
+OPAL_CALL(opal_leds_get_ind,			OPAL_LEDS_GET_INDICATOR);
+OPAL_CALL(opal_leds_set_ind,			OPAL_LEDS_SET_INDICATOR);

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/2] leds/powernv: Add driver for PowerNV platform
  2015-03-08 11:13 ` Vasant Hegde
@ 2015-03-08 11:14   ` Vasant Hegde
  -1 siblings, 0 replies; 12+ messages in thread
From: Vasant Hegde @ 2015-03-08 11:14 UTC (permalink / raw)
  To: linuxppc-dev, linux-leds; +Cc: mpe, khandual, cooloney, rpurdie, benh

From: Anshuman Khandual <khandual@linux.vnet.ibm.com>

This patch implements LED driver for PowerNV platform using the existing
generic LED class framework. It registers classdev structures for all
individual LEDs detected on the system through LED specific device tree
nodes. Device tree nodes specify what all kind of LEDs present on the
same location code. It registers LED classdev structure for each of them.

The platform level implementation of LED get and set state has been
achieved through OPAL calls. These calls are made available for the
driver by exporting from architecture specific codes.

As per the LED class framework, the 'brightness_set' function should not
sleep. Hence these functions have been implemented through global work
queue tasks which might sleep on OPAL async call completion.

All the system LEDs can be found in the same regular path /sys/class/leds/.
There are two different kind of LEDs present for the same location code,
one being the identify indicator and other one being the fault indicator.
We don't use LED colors. Hence our LEDs have names in this format.

        <location_code>:<IDENTIFY|FAULT>

Any positive brightness value would turn on the LED and a zero value
would turn off the LED. The driver will return LED_FULL (255) for any
turned on LED and LED_OFF for any turned off LED.

Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
---
 arch/powerpc/platforms/powernv/opal.c |   12 +
 drivers/leds/Kconfig                  |    9 
 drivers/leds/Makefile                 |    1 
 drivers/leds/leds-powernv.c           |  599 +++++++++++++++++++++++++++++++++
 4 files changed, 620 insertions(+), 1 deletion(-)
 create mode 100644 drivers/leds/leds-powernv.c

diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 18fd4e7..a2cad1d 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -743,7 +743,7 @@ static void __init opal_irq_init(struct device_node *dn)
 
 static int __init opal_init(void)
 {
-	struct device_node *np, *consoles;
+	struct device_node *np, *consoles, *led;
 	int rc;
 
 	opal_node = of_find_node_by_path("/ibm,opal");
@@ -769,6 +769,13 @@ static int __init opal_init(void)
 	/* Create i2c platform devices */
 	opal_i2c_create_devs();
 
+	/* Create led platform devices */
+	led = of_find_node_by_path("/ibm,opal/led");
+	if (led) {
+		of_platform_device_create(led, "opal_led", NULL);
+		of_node_put(led);
+	}
+
 	/* Find all OPAL interrupts and request them */
 	opal_irq_init(opal_node);
 
@@ -900,3 +907,6 @@ EXPORT_SYMBOL_GPL(opal_rtc_write);
 EXPORT_SYMBOL_GPL(opal_tpo_read);
 EXPORT_SYMBOL_GPL(opal_tpo_write);
 EXPORT_SYMBOL_GPL(opal_i2c_request);
+/* Export these symbols for PowerNV LED class driver */
+EXPORT_SYMBOL_GPL(opal_leds_get_ind);
+EXPORT_SYMBOL_GPL(opal_leds_set_ind);
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 25b320d..a93223c 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -508,6 +508,15 @@ config LEDS_BLINKM
 	  This option enables support for the BlinkM RGB LED connected
 	  through I2C. Say Y to enable support for the BlinkM LED.
 
+config LEDS_POWERNV
+	tristate "LED support for PowerNV Platform"
+	depends on LEDS_CLASS
+	depends on PPC_POWERNV
+	help
+	  This option enables support for the system LEDs present on
+	  PowerNV platforms. Say 'y' to enable this support in kernel.
+	  Say 'm' enable this support as module.
+
 config LEDS_SYSCON
 	bool "LED support for LEDs on system controllers"
 	depends on LEDS_CLASS=y
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index cbba921..604ffc9 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_LEDS_BLINKM)		+= leds-blinkm.o
 obj-$(CONFIG_LEDS_SYSCON)		+= leds-syscon.o
 obj-$(CONFIG_LEDS_VERSATILE)		+= leds-versatile.o
 obj-$(CONFIG_LEDS_MENF21BMC)		+= leds-menf21bmc.o
+obj-$(CONFIG_LEDS_POWERNV)		+= leds-powernv.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)		+= leds-dac124s085.o
diff --git a/drivers/leds/leds-powernv.c b/drivers/leds/leds-powernv.c
new file mode 100644
index 0000000..698fbc3
--- /dev/null
+++ b/drivers/leds/leds-powernv.c
@@ -0,0 +1,599 @@
+/*
+ * PowerNV LED Driver
+ *
+ * Copyright 2015 Anshuman Khandual, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define PREFIX		"POWERNV_LED"
+#define pr_fmt(fmt)	PREFIX ": " fmt
+
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+#include <asm/opal.h>
+
+#define LED_STR_IDENT ":IDENTIFY"
+#define LED_STR_FAULT ":FAULT"
+
+/* LED operation state
+ *
+ * led_classdev_unregister resets the brightness values. However
+ * we want to retain the LED state across boot. Hence disable
+ * LED operation before calling led_classdev_unregister.
+ *
+ */
+static bool led_disabled = false;
+
+/*
+ * LED type map
+ *
+ * Converts LED event type into it's description.
+ */
+static const char *led_type_map[OPAL_SLOT_LED_TYPE_MAX] = {
+	"Identify", "Fault"
+};
+
+/*
+ * LED set routines have been implemented as work queue tasks scheduled
+ * on the global work queue. Individual task calls OPAL interface to set
+ * the LED state which might sleep for some time. A lot of work queue
+ * tasks attempting to set the LED states can prevent each other from
+ * completing their task. So there is a need to restrict the total number
+ * of work queue tasks at any point in time related to LED.
+ */
+#define MAX_LED_REQ_COUNT	100
+static int led_req_count;
+
+/*
+ * LED classdev
+ *
+ * Each LED classdev structure registered on the platform will be put into
+ * a list which will be eventually used for de-registration purpose.
+ */
+struct powernv_led {
+	struct led_classdev cdev;
+	struct list_head    link;
+};
+static LIST_HEAD(powernv_led_list);
+static DEFINE_SPINLOCK(powernv_led_spinlock);
+
+/*
+ * LED set state command
+ *
+ * Each set LED state request will be saved and put into a list to be
+ * processed later by a work queue task.
+ */
+struct powernv_led_cmd {
+	struct led_classdev *led_cdev; /* Points to classdev structure */
+	enum led_brightness value;     /* Brightness value */
+	u64                 led_type;  /* Identify or Fault */
+	struct list_head    link;
+};
+static LIST_HEAD(powernv_led_cmd_list);
+static DEFINE_SPINLOCK(powernv_led_cmd_spinlock);
+
+/*
+ * LED set state task
+ *
+ * Each set LED state request added into the request list for post
+ * processing will be followed by one of this structure. The work
+ * struct here will be scheduled on the global work queue to process
+ * one of the requests from the request list. Though the order of
+ * execution is not guaraneteed.
+ */
+struct powernv_led_work {
+	struct work_struct work;      /* Individual task */
+	bool               deletion;  /* Need deletion */
+	struct list_head   link;
+};
+static LIST_HEAD(powernv_led_work_list);
+static DEFINE_SPINLOCK(powernv_led_work_spinlock);
+
+/*
+ * powernv_led_compact_work_list
+ *
+ * This function goes through the entire list of scheduled powernv_led_work
+ * nodes and removes the nodes which have already processed one set LED
+ * state request from request list and has been marked for deletion. This is
+ * essential for cleaning the list before adding new elements into it. This
+ * also tracks the total number of pending tasks. Once it reaches the
+ * threshold the function will throttle till all the scheduled tasks completes
+ * execution during which the user space thread will block and will be
+ * prevented from queuing up more LED state change requests.
+ */
+static void powernv_led_compact_work_list(void)
+{
+	struct powernv_led_work *pwork, *pwork_ne;
+	unsigned long flags;
+
+	spin_lock_irqsave(&powernv_led_work_spinlock, flags);
+	led_req_count = 0;
+	list_for_each_entry_safe(pwork, pwork_ne,
+				 &powernv_led_work_list, link) {
+		if (pwork->deletion) {
+			list_del(&pwork->link);
+			kfree(pwork);
+			continue;
+		}
+		led_req_count++;
+	}
+	spin_unlock_irqrestore(&powernv_led_work_spinlock, flags);
+
+	/* Throttle if the threshold reached */
+	if (led_req_count == MAX_LED_REQ_COUNT) {
+		list_for_each_entry(pwork, &powernv_led_work_list, link)
+			flush_work(&pwork->work);
+	}
+}
+
+/*
+ * commit_led_state
+ *
+ * This commits the state change of the requested LED through an OPAL call.
+ * This function is called from work queue task context when ever it gets
+ * scheduled. This function can sleep at opal_async_wait_response call.
+ */
+static void commit_led_state(struct led_classdev *led_cdev,
+			     enum led_brightness value, u64 led_type)
+{
+	char *loc_code;
+	int rc, token;
+	u64 led_mask, max_led_type, led_value = 0;
+	struct opal_msg msg;
+
+	/* Location code of the LED */
+	loc_code = kasprintf(GFP_KERNEL, "%s", led_cdev->name);
+	if (!loc_code) {
+		pr_err(PREFIX "Memory allocation failed at %s\n", __func__);
+		return;
+	}
+
+	if (led_type == OPAL_SLOT_LED_TYPE_ID)
+		loc_code[strlen(loc_code) - strlen(LED_STR_IDENT)] = '\0';
+	else if (led_type == OPAL_SLOT_LED_TYPE_FAULT)
+		loc_code[strlen(loc_code) - strlen(LED_STR_FAULT)] = '\0';
+	else /* Unknown LED type */
+		goto out_loc;
+
+	/* Prepare for the OPAL call */
+	max_led_type = cpu_to_be64(OPAL_SLOT_LED_TYPE_MAX);
+	led_mask = OPAL_SLOT_LED_STATE_ON << led_type;
+	if (value)
+		led_value = OPAL_SLOT_LED_STATE_ON << led_type;
+
+	/* OPAL async call */
+	token = opal_async_get_token_interruptible();
+	if (token < 0) {
+		if (token != -ERESTARTSYS)
+			pr_err("%s: Couldn't get the token, returning\n",
+			       __func__);
+		goto out_loc;
+	}
+
+	rc = opal_leds_set_ind(token, loc_code,
+			       led_mask, led_value, &max_led_type);
+	if (rc != OPAL_ASYNC_COMPLETION) {
+		pr_err("OPAL call opal_leds_set_ind failed for %s with %d\n",
+		       loc_code, rc);
+		goto out_token;
+	}
+
+	rc = opal_async_wait_response(token, &msg);
+	if (rc) {
+		pr_err("%s: Failed to wait for the async response, %d\n",
+			__func__, rc);
+		goto out_token;
+	}
+
+	rc = be64_to_cpu(msg.params[1]);
+	if (rc != OPAL_SUCCESS)
+		pr_err("Async call returned with failed status: %d\n", rc);
+
+out_token:
+	opal_async_release_token(token);
+
+out_loc:
+	kfree(loc_code);
+}
+
+/*
+ * powernv_led_get
+ *
+ * This function fetches the LED state for a given LED type for
+ * mentioned LED classdev structure.
+ */
+static enum led_brightness powernv_led_get(struct led_classdev *led_cdev,
+					   u64 led_type)
+{
+	char *loc_code;
+	int rc;
+	u64 led_mask, led_value, max_led_type;
+
+	/* LED location code */
+	loc_code = kasprintf(GFP_KERNEL, "%s", led_cdev->name);
+	if (!loc_code) {
+		pr_err("Memory allocation failed at: %s\n", __func__);
+		return -ENOMEM;
+	}
+
+	if (led_type == OPAL_SLOT_LED_TYPE_ID)
+		loc_code[strlen(loc_code) - strlen(LED_STR_IDENT)] = '\0';
+	else if (led_type == OPAL_SLOT_LED_TYPE_FAULT)
+		loc_code[strlen(loc_code) - strlen(LED_STR_FAULT)] = '\0';
+	else /* Unsupported LED type */
+		goto led_fail;
+
+	/* Fetch all LED status */
+	led_mask = cpu_to_be64(0);
+	led_value = cpu_to_be64(0);
+	max_led_type = cpu_to_be64(OPAL_SLOT_LED_TYPE_MAX);
+
+	rc = opal_leds_get_ind(loc_code, &led_mask, &led_value, &max_led_type);
+	if (rc != OPAL_SUCCESS && rc != OPAL_PARTIAL) {
+		pr_err("OPAL call opal_leds_get_ind failed with %d\n", rc);
+		goto led_fail;
+	}
+
+	led_mask = be64_to_cpu(led_mask);
+	led_value = be64_to_cpu(led_value);
+
+	/* LED status available */
+	if (!((led_mask >> led_type) & OPAL_SLOT_LED_STATE_ON)) {
+		pr_err("%s LED status not available for %s\n",
+		       led_type_map[led_type], loc_code);
+		goto led_fail;
+	}
+
+	/* LED status value */
+	if ((led_value >> led_type) & OPAL_SLOT_LED_STATE_ON) {
+		kfree(loc_code);
+		return LED_FULL;
+	}
+
+led_fail:
+	kfree(loc_code);
+	return LED_OFF;
+}
+
+/*
+ * powernv_led_work_func
+ *
+ * This the function which will be executed by any LED work task on the
+ * global work queue. This function de-queues one of the request node
+ * from the request list, processes it and then deletes the request node.
+ * This also accesses it's own work list node and sets the deletion flag
+ * in there making itself a candidate for removal the next time the
+ * compact function gets called.
+ */
+static void powernv_led_work_func(struct work_struct *work)
+{
+	struct powernv_led_work *pwork;
+	struct powernv_led_cmd *req;
+	unsigned long flags;
+
+	/* De-queue one request, process it and then delete */
+	spin_lock_irqsave(&powernv_led_cmd_spinlock, flags);
+	if (list_empty(&powernv_led_cmd_list)) {
+		pr_err("Request list empty, but work queue task queued\n");
+		spin_unlock_irqrestore(&powernv_led_cmd_spinlock, flags);
+		return;
+	}
+
+	req = list_first_entry(&powernv_led_cmd_list,
+			       struct powernv_led_cmd, link);
+	list_del(&req->link);
+	spin_unlock_irqrestore(&powernv_led_cmd_spinlock, flags);
+
+	commit_led_state(req->led_cdev, req->value, req->led_type);
+	kfree(req);
+
+	/* Mark the work queue task for deletion */
+	spin_lock_irqsave(&powernv_led_work_spinlock, flags);
+	pwork = container_of(work, struct powernv_led_work, work);
+	pwork->deletion = true;
+	spin_unlock_irqrestore(&powernv_led_work_spinlock, flags);
+}
+
+/*
+ * powernv_led_set_ident
+ *
+ * LED classdev 'brightness_set' function for identify LED types.
+ */
+static void powernv_led_set_ident(struct led_classdev *led_cdev,
+				  enum led_brightness value)
+{
+	struct powernv_led_work *pwork;
+	struct powernv_led_cmd *ident;
+	unsigned long flags;
+
+	if (led_disabled)
+		return;
+
+	/* Allocate the request */
+	ident = kzalloc(sizeof(struct powernv_led_cmd), GFP_KERNEL);
+	if (!ident) {
+		pr_err("Memory allocation failed at: %s\n", __func__);
+		return;
+	}
+
+	/* Allocate the work */
+	pwork = kzalloc(sizeof(struct powernv_led_work), GFP_KERNEL);
+	if (!pwork) {
+		pr_err("Memory allocation failed at: %s\n", __func__);
+		kfree(ident);
+		return;
+	}
+
+	/* Prepare the request */
+	ident->led_cdev = led_cdev;
+	ident->value = value;
+	ident->led_type = OPAL_SLOT_LED_TYPE_ID;
+
+	/* Queue the request */
+	spin_lock_irqsave(&powernv_led_cmd_spinlock, flags);
+	list_add_tail(&ident->link, &powernv_led_cmd_list);
+	spin_unlock_irqrestore(&powernv_led_cmd_spinlock, flags);
+
+	/* Compact the work list */
+	powernv_led_compact_work_list();
+
+	/* Add the new task into the work list */
+	spin_lock_irqsave(&powernv_led_work_spinlock, flags);
+	list_add_tail(&pwork->link, &powernv_led_work_list);
+	spin_unlock_irqrestore(&powernv_led_work_spinlock, flags);
+
+	/* Schedule the new task */
+	INIT_WORK(&pwork->work, powernv_led_work_func);
+	schedule_work(&pwork->work);
+}
+
+/*
+ * powernv_led_get_ident
+ *
+ * LED classdev 'brightness_get' function for identify LED types.
+ */
+
+static enum led_brightness powernv_led_get_ident(struct led_classdev *led_cdev)
+{
+	return powernv_led_get(led_cdev, OPAL_SLOT_LED_TYPE_ID);
+}
+
+/*
+ * powernv_led_set_fault
+ *
+ * LED classdev 'brightness_set' function for fault LED types.
+ */
+static void powernv_led_set_fault(struct led_classdev *led_cdev,
+				  enum led_brightness value)
+{
+	struct powernv_led_work *pwork;
+	struct powernv_led_cmd *fault;
+	unsigned long flags;
+
+	if (led_disabled)
+		return;
+
+	/* Allocate the request */
+	fault = kzalloc(sizeof(struct powernv_led_cmd), GFP_KERNEL);
+	if (!fault) {
+		pr_err("Memory allocation failed at: %s\n", __func__);
+		return;
+	}
+
+	/* Allocate the work */
+	pwork = kzalloc(sizeof(struct powernv_led_work), GFP_KERNEL);
+	if (!pwork) {
+		pr_err("Memory allocation failed at: %s\n", __func__);
+		kfree(fault);
+		return;
+	}
+
+	/* Prepare the request */
+	fault->led_cdev = led_cdev;
+	fault->value = value;
+	fault->led_type = OPAL_SLOT_LED_TYPE_FAULT;
+
+	/* Queue the request */
+	spin_lock_irqsave(&powernv_led_cmd_spinlock, flags);
+	list_add_tail(&fault->link, &powernv_led_cmd_list);
+	spin_unlock_irqrestore(&powernv_led_cmd_spinlock, flags);
+
+	/* Compact the work list */
+	powernv_led_compact_work_list();
+
+	/* Add the new task into the work list */
+	spin_lock_irqsave(&powernv_led_work_spinlock, flags);
+	list_add_tail(&pwork->link, &powernv_led_work_list);
+	spin_unlock_irqrestore(&powernv_led_work_spinlock, flags);
+
+	/* Schedule the new task */
+	INIT_WORK(&pwork->work, powernv_led_work_func);
+	schedule_work(&pwork->work);
+}
+
+/*
+ * powernv_led_get_fault
+ *
+ * LED classdev 'brightness_get' function for fault LED types.
+ */
+static enum led_brightness powernv_led_get_fault(struct led_classdev *led_cdev)
+{
+	return powernv_led_get(led_cdev, OPAL_SLOT_LED_TYPE_FAULT);
+}
+
+/*
+ * has_led_type
+ *
+ * This function verifies whether the child LED device node supports certain
+ * type of LED or not. This will be used to register LEDclassdev structures
+ * for that particual type of LED for a given device tree node.
+ */
+static bool has_led_type(struct device_node *cled, const char *led_type)
+{
+	bool result = false;
+
+	if (of_property_match_string(cled, "led-types", led_type) >= 0)
+		result = true;
+
+	return result;
+}
+
+/*
+ * power_led_classdev
+ *
+ * This function registers classdev structure for any given type of LED on
+ * a given child LED device node.
+ */
+static int power_led_classdev(struct platform_device *pdev,
+			      struct device_node *cled, u64 led_type)
+{
+	int rc;
+	unsigned long flags;
+	struct powernv_led *cpled;
+
+	cpled = kzalloc(sizeof(struct powernv_led), GFP_KERNEL);
+	if (!cpled) {
+		pr_err("Memory allocation failed at %s\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* Create the name for classdev */
+	if (led_type == OPAL_SLOT_LED_TYPE_ID) {
+		cpled->cdev.name = kasprintf(GFP_KERNEL, "%s%s",
+					     cled->name, LED_STR_IDENT);
+		cpled->cdev.brightness_set = powernv_led_set_ident;
+		cpled->cdev.brightness_get = powernv_led_get_ident;
+	} else if (led_type == OPAL_SLOT_LED_TYPE_FAULT) {
+		cpled->cdev.name = kasprintf(GFP_KERNEL, "%s%s",
+						cled->name, LED_STR_FAULT);
+		cpled->cdev.brightness_set = powernv_led_set_fault;
+		cpled->cdev.brightness_get = powernv_led_get_fault;
+	} else { /* Unsupported LED type */
+		return -EINVAL;
+	}
+
+	if (!cpled->cdev.name) {
+		pr_err("Memory allocation failed for classdev name\n");
+		return -ENOMEM;
+	}
+
+	cpled->cdev.brightness = LED_OFF;
+	cpled->cdev.max_brightness = LED_FULL;
+	cpled->cdev.flags = LED_CORE_SUSPENDRESUME;
+
+	/* Register the classdev */
+	rc = led_classdev_register(&pdev->dev, &cpled->cdev);
+	if (rc) {
+		pr_err("Classdev registration failed for %s\n",
+		       cpled->cdev.name);
+	} else {
+		spin_lock_irqsave(&powernv_led_spinlock, flags);
+		list_add_tail(&cpled->link, &powernv_led_list);
+		spin_unlock_irqrestore(&powernv_led_spinlock, flags);
+		pr_debug("Classdev registration successful for %s\n",
+			 cpled->cdev.name);
+	}
+	return rc;
+}
+
+/* Platform driver probe */
+static int powernv_led_probe(struct platform_device *pdev)
+{
+	struct device_node *led, *cled;
+	int rc = 0;
+
+	led = of_find_node_by_path("/ibm,opal/led");
+	if (!led) {
+		pr_err("LED parent device node not found\n");
+		return -EINVAL;
+	}
+
+	for_each_child_of_node(led, cled) {
+		if (has_led_type(cled, "identify"))
+			rc = power_led_classdev(pdev, cled,
+						OPAL_SLOT_LED_TYPE_ID);
+
+		if (has_led_type(cled, "fault"))
+			rc = power_led_classdev(pdev, cled,
+						OPAL_SLOT_LED_TYPE_FAULT);
+	}
+	return rc;
+}
+
+/* Platform driver remove */
+static int powernv_led_remove(struct platform_device *pdev)
+{
+	struct powernv_led *pled;
+	struct powernv_led_work *pwork;
+	unsigned long flags;
+
+	/* Disable LED operation */
+	led_disabled = true;
+
+	pr_info("Unregister all classdev structures\n");
+	list_for_each_entry(pled, &powernv_led_list, link)
+		led_classdev_unregister(&pled->cdev);
+
+	pr_info("Wait for all work tasks to finish\n");
+	list_for_each_entry(pwork, &powernv_led_work_list, link)
+		flush_work(&pwork->work);
+
+	/* Free nodes from the work list */
+	powernv_led_compact_work_list();
+
+	/* Free nodes from the classdev list */
+	spin_lock_irqsave(&powernv_led_spinlock, flags);
+	while (!list_empty(&powernv_led_list)) {
+		pled = list_first_entry(&powernv_led_list,
+					struct powernv_led, link);
+		list_del(&pled->link);
+		kfree(pled);
+	}
+	spin_unlock_irqrestore(&powernv_led_spinlock, flags);
+
+	/* Check for memory leaks */
+	if (!list_empty(&powernv_led_work_list))
+		pr_warn("Work list not empty, memory leak\n");
+
+	if (!list_empty(&powernv_led_cmd_list))
+		pr_warn("Request list not empty, memory leak\n");
+
+	if (!list_empty(&powernv_led_list))
+		pr_warn("Classdev list not empty, memory leak\n");
+
+	return 0;
+}
+
+/* Platform driver property match */
+static struct of_device_id powernv_led_match[] = {
+	{
+		.compatible	= "ibm,opal-v3-led",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, powernv_led_match);
+
+static struct platform_driver powernv_led_driver = {
+	.probe	= powernv_led_probe,
+	.remove = powernv_led_remove,
+	.driver = {
+		.name = "powernv-led-driver",
+		.owner = THIS_MODULE,
+		.of_match_table = powernv_led_match,
+	},
+};
+
+module_platform_driver(powernv_led_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PowerNV LED driver");
+MODULE_AUTHOR("Anshuman Khandual <khandual@linux.vnet.ibm.com>");

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/2] leds/powernv: Add driver for PowerNV platform
@ 2015-03-08 11:14   ` Vasant Hegde
  0 siblings, 0 replies; 12+ messages in thread
From: Vasant Hegde @ 2015-03-08 11:14 UTC (permalink / raw)
  To: linuxppc-dev, linux-leds; +Cc: cooloney, rpurdie, khandual

From: Anshuman Khandual <khandual@linux.vnet.ibm.com>

This patch implements LED driver for PowerNV platform using the existing
generic LED class framework. It registers classdev structures for all
individual LEDs detected on the system through LED specific device tree
nodes. Device tree nodes specify what all kind of LEDs present on the
same location code. It registers LED classdev structure for each of them.

The platform level implementation of LED get and set state has been
achieved through OPAL calls. These calls are made available for the
driver by exporting from architecture specific codes.

As per the LED class framework, the 'brightness_set' function should not
sleep. Hence these functions have been implemented through global work
queue tasks which might sleep on OPAL async call completion.

All the system LEDs can be found in the same regular path /sys/class/leds/.
There are two different kind of LEDs present for the same location code,
one being the identify indicator and other one being the fault indicator.
We don't use LED colors. Hence our LEDs have names in this format.

        <location_code>:<IDENTIFY|FAULT>

Any positive brightness value would turn on the LED and a zero value
would turn off the LED. The driver will return LED_FULL (255) for any
turned on LED and LED_OFF for any turned off LED.

Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
---
 arch/powerpc/platforms/powernv/opal.c |   12 +
 drivers/leds/Kconfig                  |    9 
 drivers/leds/Makefile                 |    1 
 drivers/leds/leds-powernv.c           |  599 +++++++++++++++++++++++++++++++++
 4 files changed, 620 insertions(+), 1 deletion(-)
 create mode 100644 drivers/leds/leds-powernv.c

diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 18fd4e7..a2cad1d 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -743,7 +743,7 @@ static void __init opal_irq_init(struct device_node *dn)
 
 static int __init opal_init(void)
 {
-	struct device_node *np, *consoles;
+	struct device_node *np, *consoles, *led;
 	int rc;
 
 	opal_node = of_find_node_by_path("/ibm,opal");
@@ -769,6 +769,13 @@ static int __init opal_init(void)
 	/* Create i2c platform devices */
 	opal_i2c_create_devs();
 
+	/* Create led platform devices */
+	led = of_find_node_by_path("/ibm,opal/led");
+	if (led) {
+		of_platform_device_create(led, "opal_led", NULL);
+		of_node_put(led);
+	}
+
 	/* Find all OPAL interrupts and request them */
 	opal_irq_init(opal_node);
 
@@ -900,3 +907,6 @@ EXPORT_SYMBOL_GPL(opal_rtc_write);
 EXPORT_SYMBOL_GPL(opal_tpo_read);
 EXPORT_SYMBOL_GPL(opal_tpo_write);
 EXPORT_SYMBOL_GPL(opal_i2c_request);
+/* Export these symbols for PowerNV LED class driver */
+EXPORT_SYMBOL_GPL(opal_leds_get_ind);
+EXPORT_SYMBOL_GPL(opal_leds_set_ind);
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 25b320d..a93223c 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -508,6 +508,15 @@ config LEDS_BLINKM
 	  This option enables support for the BlinkM RGB LED connected
 	  through I2C. Say Y to enable support for the BlinkM LED.
 
+config LEDS_POWERNV
+	tristate "LED support for PowerNV Platform"
+	depends on LEDS_CLASS
+	depends on PPC_POWERNV
+	help
+	  This option enables support for the system LEDs present on
+	  PowerNV platforms. Say 'y' to enable this support in kernel.
+	  Say 'm' enable this support as module.
+
 config LEDS_SYSCON
 	bool "LED support for LEDs on system controllers"
 	depends on LEDS_CLASS=y
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index cbba921..604ffc9 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_LEDS_BLINKM)		+= leds-blinkm.o
 obj-$(CONFIG_LEDS_SYSCON)		+= leds-syscon.o
 obj-$(CONFIG_LEDS_VERSATILE)		+= leds-versatile.o
 obj-$(CONFIG_LEDS_MENF21BMC)		+= leds-menf21bmc.o
+obj-$(CONFIG_LEDS_POWERNV)		+= leds-powernv.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)		+= leds-dac124s085.o
diff --git a/drivers/leds/leds-powernv.c b/drivers/leds/leds-powernv.c
new file mode 100644
index 0000000..698fbc3
--- /dev/null
+++ b/drivers/leds/leds-powernv.c
@@ -0,0 +1,599 @@
+/*
+ * PowerNV LED Driver
+ *
+ * Copyright 2015 Anshuman Khandual, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define PREFIX		"POWERNV_LED"
+#define pr_fmt(fmt)	PREFIX ": " fmt
+
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+#include <asm/opal.h>
+
+#define LED_STR_IDENT ":IDENTIFY"
+#define LED_STR_FAULT ":FAULT"
+
+/* LED operation state
+ *
+ * led_classdev_unregister resets the brightness values. However
+ * we want to retain the LED state across boot. Hence disable
+ * LED operation before calling led_classdev_unregister.
+ *
+ */
+static bool led_disabled = false;
+
+/*
+ * LED type map
+ *
+ * Converts LED event type into it's description.
+ */
+static const char *led_type_map[OPAL_SLOT_LED_TYPE_MAX] = {
+	"Identify", "Fault"
+};
+
+/*
+ * LED set routines have been implemented as work queue tasks scheduled
+ * on the global work queue. Individual task calls OPAL interface to set
+ * the LED state which might sleep for some time. A lot of work queue
+ * tasks attempting to set the LED states can prevent each other from
+ * completing their task. So there is a need to restrict the total number
+ * of work queue tasks at any point in time related to LED.
+ */
+#define MAX_LED_REQ_COUNT	100
+static int led_req_count;
+
+/*
+ * LED classdev
+ *
+ * Each LED classdev structure registered on the platform will be put into
+ * a list which will be eventually used for de-registration purpose.
+ */
+struct powernv_led {
+	struct led_classdev cdev;
+	struct list_head    link;
+};
+static LIST_HEAD(powernv_led_list);
+static DEFINE_SPINLOCK(powernv_led_spinlock);
+
+/*
+ * LED set state command
+ *
+ * Each set LED state request will be saved and put into a list to be
+ * processed later by a work queue task.
+ */
+struct powernv_led_cmd {
+	struct led_classdev *led_cdev; /* Points to classdev structure */
+	enum led_brightness value;     /* Brightness value */
+	u64                 led_type;  /* Identify or Fault */
+	struct list_head    link;
+};
+static LIST_HEAD(powernv_led_cmd_list);
+static DEFINE_SPINLOCK(powernv_led_cmd_spinlock);
+
+/*
+ * LED set state task
+ *
+ * Each set LED state request added into the request list for post
+ * processing will be followed by one of this structure. The work
+ * struct here will be scheduled on the global work queue to process
+ * one of the requests from the request list. Though the order of
+ * execution is not guaraneteed.
+ */
+struct powernv_led_work {
+	struct work_struct work;      /* Individual task */
+	bool               deletion;  /* Need deletion */
+	struct list_head   link;
+};
+static LIST_HEAD(powernv_led_work_list);
+static DEFINE_SPINLOCK(powernv_led_work_spinlock);
+
+/*
+ * powernv_led_compact_work_list
+ *
+ * This function goes through the entire list of scheduled powernv_led_work
+ * nodes and removes the nodes which have already processed one set LED
+ * state request from request list and has been marked for deletion. This is
+ * essential for cleaning the list before adding new elements into it. This
+ * also tracks the total number of pending tasks. Once it reaches the
+ * threshold the function will throttle till all the scheduled tasks completes
+ * execution during which the user space thread will block and will be
+ * prevented from queuing up more LED state change requests.
+ */
+static void powernv_led_compact_work_list(void)
+{
+	struct powernv_led_work *pwork, *pwork_ne;
+	unsigned long flags;
+
+	spin_lock_irqsave(&powernv_led_work_spinlock, flags);
+	led_req_count = 0;
+	list_for_each_entry_safe(pwork, pwork_ne,
+				 &powernv_led_work_list, link) {
+		if (pwork->deletion) {
+			list_del(&pwork->link);
+			kfree(pwork);
+			continue;
+		}
+		led_req_count++;
+	}
+	spin_unlock_irqrestore(&powernv_led_work_spinlock, flags);
+
+	/* Throttle if the threshold reached */
+	if (led_req_count == MAX_LED_REQ_COUNT) {
+		list_for_each_entry(pwork, &powernv_led_work_list, link)
+			flush_work(&pwork->work);
+	}
+}
+
+/*
+ * commit_led_state
+ *
+ * This commits the state change of the requested LED through an OPAL call.
+ * This function is called from work queue task context when ever it gets
+ * scheduled. This function can sleep at opal_async_wait_response call.
+ */
+static void commit_led_state(struct led_classdev *led_cdev,
+			     enum led_brightness value, u64 led_type)
+{
+	char *loc_code;
+	int rc, token;
+	u64 led_mask, max_led_type, led_value = 0;
+	struct opal_msg msg;
+
+	/* Location code of the LED */
+	loc_code = kasprintf(GFP_KERNEL, "%s", led_cdev->name);
+	if (!loc_code) {
+		pr_err(PREFIX "Memory allocation failed at %s\n", __func__);
+		return;
+	}
+
+	if (led_type == OPAL_SLOT_LED_TYPE_ID)
+		loc_code[strlen(loc_code) - strlen(LED_STR_IDENT)] = '\0';
+	else if (led_type == OPAL_SLOT_LED_TYPE_FAULT)
+		loc_code[strlen(loc_code) - strlen(LED_STR_FAULT)] = '\0';
+	else /* Unknown LED type */
+		goto out_loc;
+
+	/* Prepare for the OPAL call */
+	max_led_type = cpu_to_be64(OPAL_SLOT_LED_TYPE_MAX);
+	led_mask = OPAL_SLOT_LED_STATE_ON << led_type;
+	if (value)
+		led_value = OPAL_SLOT_LED_STATE_ON << led_type;
+
+	/* OPAL async call */
+	token = opal_async_get_token_interruptible();
+	if (token < 0) {
+		if (token != -ERESTARTSYS)
+			pr_err("%s: Couldn't get the token, returning\n",
+			       __func__);
+		goto out_loc;
+	}
+
+	rc = opal_leds_set_ind(token, loc_code,
+			       led_mask, led_value, &max_led_type);
+	if (rc != OPAL_ASYNC_COMPLETION) {
+		pr_err("OPAL call opal_leds_set_ind failed for %s with %d\n",
+		       loc_code, rc);
+		goto out_token;
+	}
+
+	rc = opal_async_wait_response(token, &msg);
+	if (rc) {
+		pr_err("%s: Failed to wait for the async response, %d\n",
+			__func__, rc);
+		goto out_token;
+	}
+
+	rc = be64_to_cpu(msg.params[1]);
+	if (rc != OPAL_SUCCESS)
+		pr_err("Async call returned with failed status: %d\n", rc);
+
+out_token:
+	opal_async_release_token(token);
+
+out_loc:
+	kfree(loc_code);
+}
+
+/*
+ * powernv_led_get
+ *
+ * This function fetches the LED state for a given LED type for
+ * mentioned LED classdev structure.
+ */
+static enum led_brightness powernv_led_get(struct led_classdev *led_cdev,
+					   u64 led_type)
+{
+	char *loc_code;
+	int rc;
+	u64 led_mask, led_value, max_led_type;
+
+	/* LED location code */
+	loc_code = kasprintf(GFP_KERNEL, "%s", led_cdev->name);
+	if (!loc_code) {
+		pr_err("Memory allocation failed at: %s\n", __func__);
+		return -ENOMEM;
+	}
+
+	if (led_type == OPAL_SLOT_LED_TYPE_ID)
+		loc_code[strlen(loc_code) - strlen(LED_STR_IDENT)] = '\0';
+	else if (led_type == OPAL_SLOT_LED_TYPE_FAULT)
+		loc_code[strlen(loc_code) - strlen(LED_STR_FAULT)] = '\0';
+	else /* Unsupported LED type */
+		goto led_fail;
+
+	/* Fetch all LED status */
+	led_mask = cpu_to_be64(0);
+	led_value = cpu_to_be64(0);
+	max_led_type = cpu_to_be64(OPAL_SLOT_LED_TYPE_MAX);
+
+	rc = opal_leds_get_ind(loc_code, &led_mask, &led_value, &max_led_type);
+	if (rc != OPAL_SUCCESS && rc != OPAL_PARTIAL) {
+		pr_err("OPAL call opal_leds_get_ind failed with %d\n", rc);
+		goto led_fail;
+	}
+
+	led_mask = be64_to_cpu(led_mask);
+	led_value = be64_to_cpu(led_value);
+
+	/* LED status available */
+	if (!((led_mask >> led_type) & OPAL_SLOT_LED_STATE_ON)) {
+		pr_err("%s LED status not available for %s\n",
+		       led_type_map[led_type], loc_code);
+		goto led_fail;
+	}
+
+	/* LED status value */
+	if ((led_value >> led_type) & OPAL_SLOT_LED_STATE_ON) {
+		kfree(loc_code);
+		return LED_FULL;
+	}
+
+led_fail:
+	kfree(loc_code);
+	return LED_OFF;
+}
+
+/*
+ * powernv_led_work_func
+ *
+ * This the function which will be executed by any LED work task on the
+ * global work queue. This function de-queues one of the request node
+ * from the request list, processes it and then deletes the request node.
+ * This also accesses it's own work list node and sets the deletion flag
+ * in there making itself a candidate for removal the next time the
+ * compact function gets called.
+ */
+static void powernv_led_work_func(struct work_struct *work)
+{
+	struct powernv_led_work *pwork;
+	struct powernv_led_cmd *req;
+	unsigned long flags;
+
+	/* De-queue one request, process it and then delete */
+	spin_lock_irqsave(&powernv_led_cmd_spinlock, flags);
+	if (list_empty(&powernv_led_cmd_list)) {
+		pr_err("Request list empty, but work queue task queued\n");
+		spin_unlock_irqrestore(&powernv_led_cmd_spinlock, flags);
+		return;
+	}
+
+	req = list_first_entry(&powernv_led_cmd_list,
+			       struct powernv_led_cmd, link);
+	list_del(&req->link);
+	spin_unlock_irqrestore(&powernv_led_cmd_spinlock, flags);
+
+	commit_led_state(req->led_cdev, req->value, req->led_type);
+	kfree(req);
+
+	/* Mark the work queue task for deletion */
+	spin_lock_irqsave(&powernv_led_work_spinlock, flags);
+	pwork = container_of(work, struct powernv_led_work, work);
+	pwork->deletion = true;
+	spin_unlock_irqrestore(&powernv_led_work_spinlock, flags);
+}
+
+/*
+ * powernv_led_set_ident
+ *
+ * LED classdev 'brightness_set' function for identify LED types.
+ */
+static void powernv_led_set_ident(struct led_classdev *led_cdev,
+				  enum led_brightness value)
+{
+	struct powernv_led_work *pwork;
+	struct powernv_led_cmd *ident;
+	unsigned long flags;
+
+	if (led_disabled)
+		return;
+
+	/* Allocate the request */
+	ident = kzalloc(sizeof(struct powernv_led_cmd), GFP_KERNEL);
+	if (!ident) {
+		pr_err("Memory allocation failed at: %s\n", __func__);
+		return;
+	}
+
+	/* Allocate the work */
+	pwork = kzalloc(sizeof(struct powernv_led_work), GFP_KERNEL);
+	if (!pwork) {
+		pr_err("Memory allocation failed at: %s\n", __func__);
+		kfree(ident);
+		return;
+	}
+
+	/* Prepare the request */
+	ident->led_cdev = led_cdev;
+	ident->value = value;
+	ident->led_type = OPAL_SLOT_LED_TYPE_ID;
+
+	/* Queue the request */
+	spin_lock_irqsave(&powernv_led_cmd_spinlock, flags);
+	list_add_tail(&ident->link, &powernv_led_cmd_list);
+	spin_unlock_irqrestore(&powernv_led_cmd_spinlock, flags);
+
+	/* Compact the work list */
+	powernv_led_compact_work_list();
+
+	/* Add the new task into the work list */
+	spin_lock_irqsave(&powernv_led_work_spinlock, flags);
+	list_add_tail(&pwork->link, &powernv_led_work_list);
+	spin_unlock_irqrestore(&powernv_led_work_spinlock, flags);
+
+	/* Schedule the new task */
+	INIT_WORK(&pwork->work, powernv_led_work_func);
+	schedule_work(&pwork->work);
+}
+
+/*
+ * powernv_led_get_ident
+ *
+ * LED classdev 'brightness_get' function for identify LED types.
+ */
+
+static enum led_brightness powernv_led_get_ident(struct led_classdev *led_cdev)
+{
+	return powernv_led_get(led_cdev, OPAL_SLOT_LED_TYPE_ID);
+}
+
+/*
+ * powernv_led_set_fault
+ *
+ * LED classdev 'brightness_set' function for fault LED types.
+ */
+static void powernv_led_set_fault(struct led_classdev *led_cdev,
+				  enum led_brightness value)
+{
+	struct powernv_led_work *pwork;
+	struct powernv_led_cmd *fault;
+	unsigned long flags;
+
+	if (led_disabled)
+		return;
+
+	/* Allocate the request */
+	fault = kzalloc(sizeof(struct powernv_led_cmd), GFP_KERNEL);
+	if (!fault) {
+		pr_err("Memory allocation failed at: %s\n", __func__);
+		return;
+	}
+
+	/* Allocate the work */
+	pwork = kzalloc(sizeof(struct powernv_led_work), GFP_KERNEL);
+	if (!pwork) {
+		pr_err("Memory allocation failed at: %s\n", __func__);
+		kfree(fault);
+		return;
+	}
+
+	/* Prepare the request */
+	fault->led_cdev = led_cdev;
+	fault->value = value;
+	fault->led_type = OPAL_SLOT_LED_TYPE_FAULT;
+
+	/* Queue the request */
+	spin_lock_irqsave(&powernv_led_cmd_spinlock, flags);
+	list_add_tail(&fault->link, &powernv_led_cmd_list);
+	spin_unlock_irqrestore(&powernv_led_cmd_spinlock, flags);
+
+	/* Compact the work list */
+	powernv_led_compact_work_list();
+
+	/* Add the new task into the work list */
+	spin_lock_irqsave(&powernv_led_work_spinlock, flags);
+	list_add_tail(&pwork->link, &powernv_led_work_list);
+	spin_unlock_irqrestore(&powernv_led_work_spinlock, flags);
+
+	/* Schedule the new task */
+	INIT_WORK(&pwork->work, powernv_led_work_func);
+	schedule_work(&pwork->work);
+}
+
+/*
+ * powernv_led_get_fault
+ *
+ * LED classdev 'brightness_get' function for fault LED types.
+ */
+static enum led_brightness powernv_led_get_fault(struct led_classdev *led_cdev)
+{
+	return powernv_led_get(led_cdev, OPAL_SLOT_LED_TYPE_FAULT);
+}
+
+/*
+ * has_led_type
+ *
+ * This function verifies whether the child LED device node supports certain
+ * type of LED or not. This will be used to register LEDclassdev structures
+ * for that particual type of LED for a given device tree node.
+ */
+static bool has_led_type(struct device_node *cled, const char *led_type)
+{
+	bool result = false;
+
+	if (of_property_match_string(cled, "led-types", led_type) >= 0)
+		result = true;
+
+	return result;
+}
+
+/*
+ * power_led_classdev
+ *
+ * This function registers classdev structure for any given type of LED on
+ * a given child LED device node.
+ */
+static int power_led_classdev(struct platform_device *pdev,
+			      struct device_node *cled, u64 led_type)
+{
+	int rc;
+	unsigned long flags;
+	struct powernv_led *cpled;
+
+	cpled = kzalloc(sizeof(struct powernv_led), GFP_KERNEL);
+	if (!cpled) {
+		pr_err("Memory allocation failed at %s\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* Create the name for classdev */
+	if (led_type == OPAL_SLOT_LED_TYPE_ID) {
+		cpled->cdev.name = kasprintf(GFP_KERNEL, "%s%s",
+					     cled->name, LED_STR_IDENT);
+		cpled->cdev.brightness_set = powernv_led_set_ident;
+		cpled->cdev.brightness_get = powernv_led_get_ident;
+	} else if (led_type == OPAL_SLOT_LED_TYPE_FAULT) {
+		cpled->cdev.name = kasprintf(GFP_KERNEL, "%s%s",
+						cled->name, LED_STR_FAULT);
+		cpled->cdev.brightness_set = powernv_led_set_fault;
+		cpled->cdev.brightness_get = powernv_led_get_fault;
+	} else { /* Unsupported LED type */
+		return -EINVAL;
+	}
+
+	if (!cpled->cdev.name) {
+		pr_err("Memory allocation failed for classdev name\n");
+		return -ENOMEM;
+	}
+
+	cpled->cdev.brightness = LED_OFF;
+	cpled->cdev.max_brightness = LED_FULL;
+	cpled->cdev.flags = LED_CORE_SUSPENDRESUME;
+
+	/* Register the classdev */
+	rc = led_classdev_register(&pdev->dev, &cpled->cdev);
+	if (rc) {
+		pr_err("Classdev registration failed for %s\n",
+		       cpled->cdev.name);
+	} else {
+		spin_lock_irqsave(&powernv_led_spinlock, flags);
+		list_add_tail(&cpled->link, &powernv_led_list);
+		spin_unlock_irqrestore(&powernv_led_spinlock, flags);
+		pr_debug("Classdev registration successful for %s\n",
+			 cpled->cdev.name);
+	}
+	return rc;
+}
+
+/* Platform driver probe */
+static int powernv_led_probe(struct platform_device *pdev)
+{
+	struct device_node *led, *cled;
+	int rc = 0;
+
+	led = of_find_node_by_path("/ibm,opal/led");
+	if (!led) {
+		pr_err("LED parent device node not found\n");
+		return -EINVAL;
+	}
+
+	for_each_child_of_node(led, cled) {
+		if (has_led_type(cled, "identify"))
+			rc = power_led_classdev(pdev, cled,
+						OPAL_SLOT_LED_TYPE_ID);
+
+		if (has_led_type(cled, "fault"))
+			rc = power_led_classdev(pdev, cled,
+						OPAL_SLOT_LED_TYPE_FAULT);
+	}
+	return rc;
+}
+
+/* Platform driver remove */
+static int powernv_led_remove(struct platform_device *pdev)
+{
+	struct powernv_led *pled;
+	struct powernv_led_work *pwork;
+	unsigned long flags;
+
+	/* Disable LED operation */
+	led_disabled = true;
+
+	pr_info("Unregister all classdev structures\n");
+	list_for_each_entry(pled, &powernv_led_list, link)
+		led_classdev_unregister(&pled->cdev);
+
+	pr_info("Wait for all work tasks to finish\n");
+	list_for_each_entry(pwork, &powernv_led_work_list, link)
+		flush_work(&pwork->work);
+
+	/* Free nodes from the work list */
+	powernv_led_compact_work_list();
+
+	/* Free nodes from the classdev list */
+	spin_lock_irqsave(&powernv_led_spinlock, flags);
+	while (!list_empty(&powernv_led_list)) {
+		pled = list_first_entry(&powernv_led_list,
+					struct powernv_led, link);
+		list_del(&pled->link);
+		kfree(pled);
+	}
+	spin_unlock_irqrestore(&powernv_led_spinlock, flags);
+
+	/* Check for memory leaks */
+	if (!list_empty(&powernv_led_work_list))
+		pr_warn("Work list not empty, memory leak\n");
+
+	if (!list_empty(&powernv_led_cmd_list))
+		pr_warn("Request list not empty, memory leak\n");
+
+	if (!list_empty(&powernv_led_list))
+		pr_warn("Classdev list not empty, memory leak\n");
+
+	return 0;
+}
+
+/* Platform driver property match */
+static struct of_device_id powernv_led_match[] = {
+	{
+		.compatible	= "ibm,opal-v3-led",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, powernv_led_match);
+
+static struct platform_driver powernv_led_driver = {
+	.probe	= powernv_led_probe,
+	.remove = powernv_led_remove,
+	.driver = {
+		.name = "powernv-led-driver",
+		.owner = THIS_MODULE,
+		.of_match_table = powernv_led_match,
+	},
+};
+
+module_platform_driver(powernv_led_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PowerNV LED driver");
+MODULE_AUTHOR("Anshuman Khandual <khandual@linux.vnet.ibm.com>");

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/2] powerpc/powernv: Add OPAL interfaces for accessing and modifying system LED states
  2015-03-08 11:14   ` Vasant Hegde
  (?)
@ 2015-03-19 21:35   ` Stewart Smith
  -1 siblings, 0 replies; 12+ messages in thread
From: Stewart Smith @ 2015-03-19 21:35 UTC (permalink / raw)
  To: Vasant Hegde, linuxppc-dev, linux-leds; +Cc: cooloney, rpurdie, khandual

Vasant Hegde <hegdevasant@linux.vnet.ibm.com> writes:
> From: Anshuman Khandual <khandual@linux.vnet.ibm.com>
>
> This patch registers the following two new OPAL interfaces calls
> for the platform LED subsystem. With the help of these new OPAL calls,
> the kernel will be able to get or set the state of various individual
> LEDs on the system at any given location code which is passed through
> the LED specific device tree nodes.
>
> 	(1) OPAL_LEDS_GET_INDICATOR     opal_leds_get_ind
> 	(2) OPAL_LEDS_SET_INDICATOR     opal_leds_set_ind
>
> Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>

I also just merged the skiboot side of these calls.

Acked-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Tested-by: Stewart Smith <stewart@linux.vnet.ibm.com>


(well, it boots, interacts with firmware. I didn't go and look at the
LEDs themselves).

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/2] leds/powernv: Add driver for PowerNV platform
  2015-03-08 11:14   ` Vasant Hegde
  (?)
@ 2015-03-19 21:35   ` Stewart Smith
  -1 siblings, 0 replies; 12+ messages in thread
From: Stewart Smith @ 2015-03-19 21:35 UTC (permalink / raw)
  To: Vasant Hegde, linuxppc-dev, linux-leds; +Cc: cooloney, rpurdie, khandual

Vasant Hegde <hegdevasant@linux.vnet.ibm.com> writes:

> From: Anshuman Khandual <khandual@linux.vnet.ibm.com>
>
> This patch implements LED driver for PowerNV platform using the existing
> generic LED class framework. It registers classdev structures for all
> individual LEDs detected on the system through LED specific device tree
> nodes. Device tree nodes specify what all kind of LEDs present on the
> same location code. It registers LED classdev structure for each of them.
>
> The platform level implementation of LED get and set state has been
> achieved through OPAL calls. These calls are made available for the
> driver by exporting from architecture specific codes.
>
> As per the LED class framework, the 'brightness_set' function should not
> sleep. Hence these functions have been implemented through global work
> queue tasks which might sleep on OPAL async call completion.
>
> All the system LEDs can be found in the same regular path /sys/class/leds/.
> There are two different kind of LEDs present for the same location code,
> one being the identify indicator and other one being the fault indicator.
> We don't use LED colors. Hence our LEDs have names in this format.
>
>         <location_code>:<IDENTIFY|FAULT>
>
> Any positive brightness value would turn on the LED and a zero value
> would turn off the LED. The driver will return LED_FULL (255) for any
> turned on LED and LED_OFF for any turned off LED.
>
> Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>

Acked-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Tested-by: Stewart Smith <stewart@linux.vnet.ibm.com>


(well, it boots, interacts with firmware. I didn't go and look at the
LEDs themselves).

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/2] powerpc/powernv: Add OPAL interfaces for accessing and modifying system LED states
  2015-03-08 11:14   ` Vasant Hegde
@ 2015-03-20  6:23     ` Michael Ellerman
  -1 siblings, 0 replies; 12+ messages in thread
From: Michael Ellerman @ 2015-03-20  6:23 UTC (permalink / raw)
  To: Vasant Hegde; +Cc: linuxppc-dev, linux-leds, khandual, cooloney, rpurdie, benh

On Sun, 2015-03-08 at 16:44 +0530, Vasant Hegde wrote:
> From: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> 
> This patch registers the following two new OPAL interfaces calls
> for the platform LED subsystem. With the help of these new OPAL calls,
> the kernel will be able to get or set the state of various individual
> LEDs on the system at any given location code which is passed through
> the LED specific device tree nodes.
> 
> 	(1) OPAL_LEDS_GET_INDICATOR     opal_leds_get_ind
> 	(2) OPAL_LEDS_SET_INDICATOR     opal_leds_set_ind
> 
> Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
> ---
>  arch/powerpc/include/asm/opal.h                |   19 +++++++++++++------

We've split opal.h in next.

Can you please rebase on top of it:

https://git.kernel.org/cgit/linux/kernel/git/mpe/linux.git/log/?h=next


cheers

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/2] powerpc/powernv: Add OPAL interfaces for accessing and modifying system LED states
@ 2015-03-20  6:23     ` Michael Ellerman
  0 siblings, 0 replies; 12+ messages in thread
From: Michael Ellerman @ 2015-03-20  6:23 UTC (permalink / raw)
  To: Vasant Hegde; +Cc: cooloney, rpurdie, linuxppc-dev, linux-leds, khandual

On Sun, 2015-03-08 at 16:44 +0530, Vasant Hegde wrote:
> From: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> 
> This patch registers the following two new OPAL interfaces calls
> for the platform LED subsystem. With the help of these new OPAL calls,
> the kernel will be able to get or set the state of various individual
> LEDs on the system at any given location code which is passed through
> the LED specific device tree nodes.
> 
> 	(1) OPAL_LEDS_GET_INDICATOR     opal_leds_get_ind
> 	(2) OPAL_LEDS_SET_INDICATOR     opal_leds_set_ind
> 
> Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
> ---
>  arch/powerpc/include/asm/opal.h                |   19 +++++++++++++------

We've split opal.h in next.

Can you please rebase on top of it:

https://git.kernel.org/cgit/linux/kernel/git/mpe/linux.git/log/?h=next


cheers

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/2] powerpc/powernv: Add OPAL interfaces for accessing and modifying system LED states
  2015-03-20  6:23     ` Michael Ellerman
@ 2015-03-20  8:07       ` Vasant Hegde
  -1 siblings, 0 replies; 12+ messages in thread
From: Vasant Hegde @ 2015-03-20  8:07 UTC (permalink / raw)
  To: Michael Ellerman
  Cc: linuxppc-dev, linux-leds, khandual, cooloney, rpurdie, benh

On 03/20/2015 11:53 AM, Michael Ellerman wrote:
> On Sun, 2015-03-08 at 16:44 +0530, Vasant Hegde wrote:
>> From: Anshuman Khandual <khandual@linux.vnet.ibm.com>
>>
>> This patch registers the following two new OPAL interfaces calls
>> for the platform LED subsystem. With the help of these new OPAL calls,
>> the kernel will be able to get or set the state of various individual
>> LEDs on the system at any given location code which is passed through
>> the LED specific device tree nodes.
>>
>> 	(1) OPAL_LEDS_GET_INDICATOR     opal_leds_get_ind
>> 	(2) OPAL_LEDS_SET_INDICATOR     opal_leds_set_ind
>>
>> Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
>> Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
>> ---
>>  arch/powerpc/include/asm/opal.h                |   19 +++++++++++++------
> 
> We've split opal.h in next.
> 
> Can you please rebase on top of it:
> 
> https://git.kernel.org/cgit/linux/kernel/git/mpe/linux.git/log/?h=next

Sure.. will rebase and send v2 soon.

-Vasant

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/2] powerpc/powernv: Add OPAL interfaces for accessing and modifying system LED states
@ 2015-03-20  8:07       ` Vasant Hegde
  0 siblings, 0 replies; 12+ messages in thread
From: Vasant Hegde @ 2015-03-20  8:07 UTC (permalink / raw)
  To: Michael Ellerman; +Cc: cooloney, rpurdie, linuxppc-dev, linux-leds, khandual

On 03/20/2015 11:53 AM, Michael Ellerman wrote:
> On Sun, 2015-03-08 at 16:44 +0530, Vasant Hegde wrote:
>> From: Anshuman Khandual <khandual@linux.vnet.ibm.com>
>>
>> This patch registers the following two new OPAL interfaces calls
>> for the platform LED subsystem. With the help of these new OPAL calls,
>> the kernel will be able to get or set the state of various individual
>> LEDs on the system at any given location code which is passed through
>> the LED specific device tree nodes.
>>
>> 	(1) OPAL_LEDS_GET_INDICATOR     opal_leds_get_ind
>> 	(2) OPAL_LEDS_SET_INDICATOR     opal_leds_set_ind
>>
>> Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
>> Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
>> ---
>>  arch/powerpc/include/asm/opal.h                |   19 +++++++++++++------
> 
> We've split opal.h in next.
> 
> Can you please rebase on top of it:
> 
> https://git.kernel.org/cgit/linux/kernel/git/mpe/linux.git/log/?h=next

Sure.. will rebase and send v2 soon.

-Vasant

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2015-03-20  8:08 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-08 11:13 [PATCH 0/2] LED interface for PowerNV platform Vasant Hegde
2015-03-08 11:13 ` Vasant Hegde
2015-03-08 11:14 ` [PATCH 1/2] powerpc/powernv: Add OPAL interfaces for accessing and modifying system LED states Vasant Hegde
2015-03-08 11:14   ` Vasant Hegde
2015-03-19 21:35   ` Stewart Smith
2015-03-20  6:23   ` Michael Ellerman
2015-03-20  6:23     ` Michael Ellerman
2015-03-20  8:07     ` Vasant Hegde
2015-03-20  8:07       ` Vasant Hegde
2015-03-08 11:14 ` [PATCH 2/2] leds/powernv: Add driver for PowerNV platform Vasant Hegde
2015-03-08 11:14   ` Vasant Hegde
2015-03-19 21:35   ` Stewart Smith

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.