linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 00/10] cros_ec: Add support for Wilco EC
@ 2018-12-15  0:18 Nick Crews
  2018-12-15  0:18 ` [RFC PATCH 01/10] CHROMIUM: cros_ec: Remove cros_ec dependency in lpc_mec Nick Crews
                   ` (10 more replies)
  0 siblings, 11 replies; 16+ messages in thread
From: Nick Crews @ 2018-12-15  0:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: Nick Crews, Olof Johansson, Benson Leung

The Chromebook named wilco contains a different Embedded Controller
than the rest of the chromebook series, and thus the kernel requires
a different driver than the already existing and generalized
cros_ec_* drivers. Specifically, this driver adds support for getting
and setting the RTC on the EC, adding a binary sysfs attribute
that receives ACPI events from the EC, adding a binary sysfs
attribute to request telemetry data from the EC (useful for enterprise
applications), and adding normal sysfs attributes to get/set various
other properties on the EC. The core of the communication with the EC
is implemented in wilco_ec_mailbox.c, using a simple byte-level protocol
with a checksum, transmitted over an eSPI bus. For debugging purposes,
a raw attribute is also provided which can write/read arbitrary
bytes to/from the eSPI bus.

We attempted to adhere to the sysfs principles of "one piece of data per
attribute" as much as possible, and mostly succeded. However, with the
wilco_ec_adv_power.h attributes, which deal with scheduling power usage,
we found it most elegant to bundle setting event times for an entire day
into a single attribute, so at most you are using attributes formatted
as "%d %d %d %d %d %d". With the telemetry attribute, we had to use a
binary attribute, instead of the preferable human-readable ascii, in
order to keep secure the information which is proprietary to the
enterprise service provider. This opaque binary data will be read and
sent using a proprietary daemon running on the OS. Finally, the
"version" attribute returns a formatted result that looks something
like:
> cat /sys/bus/platform/devices/GOOG000C\:00/version
Label        : 95.00.06
SVN Revision : 5960a.06
Model Number : 08;8
Build Date   : 11/29/18

The RTC driver is exposed as a standard RTC class driver with
read/write functionality.

For event notification, the Wilco EC can return extended events that
are not handled by standard ACPI objects. These events can
include hotkeys which map to standard functions like brightness
controls, or information about EC controlled features like the
charger or battery. These events are triggered with an
ACPI Notify(0x90) and the event data buffer is read through an ACPI
method provided by the BIOS which reads the event buffer from EC RAM.
These events are then processed, with hotkey events being sent
to the input subsystem and other events put into a queue which
can be read by a userspace daemon via a sysfs attribute.

The rest of the attributes are categorized as either "properties" or
"legacy". "legacy" implies that the attribute existed on the EC before it
was modified for ChromeOS, and "properties" implies that the attribute
exposes functionality that was added to the EC specifically for
ChromeOS. They are mostly boolean flags or percentages.

A full thread of the development of these patches can be found at
https://chromium-review.googlesource.com/c/1371034. This thread contains
comments and revisions that could be helpful in understanding how the
driver arrived at the state it is in now. The thread also contains some
ChromeOS specific patches that actually enable the driver. If you want
to test the patch yourself, you would have to install the ChromeOS SDK
and cherry pick in these patches.

I also wrote some integration tests using the Tast testing framework that
ChromeOS uses. It would require a full ChromeOS SDK to actually run the
tests, but the source of the tests, written in Go, are useful for
understanding what the desired behavior is. You can view the tests here:
https://chromium-review.googlesource.com/c/1372575

This is still an initial version of the driver, and we are sending it
upstream for comments now, so that we can incorporate any requested
changes such that it eventually can be merged. Thank you for your
comments!


Duncan Laurie (6):
  CHROMIUM: cros_ec: Remove cros_ec dependency in lpc_mec
  CHROMIUM: wilco_ec: Add new driver for Wilco EC
  CHROMIUM: wilco_ec: Add sysfs attributes
  CHROMIUM: wilco_ec: Add support for raw commands in sysfs
  CHROMIUM: wilco_ec: Add RTC class driver
  CHROMIUM: wilco_ec: Add event handling

Nick Crews (4):
  CHROMIUM: wilco_ec: Move legacy attributes to separate file
  CHROMIUM: wilco_ec: Add EC properties
  CHROMIUM: wilco_ec: Add peakshift and adv_batt_charging
  CHROMIUM: wilco_ec: Add binary telemetry attributes

 drivers/platform/chrome/Kconfig               |  24 +-
 drivers/platform/chrome/Makefile              |   9 +-
 drivers/platform/chrome/cros_ec_lpc_mec.c     |  54 +-
 drivers/platform/chrome/cros_ec_lpc_mec.h     |  45 +-
 drivers/platform/chrome/cros_ec_lpc_reg.c     |  43 +-
 drivers/platform/chrome/wilco_ec.h            | 180 ++++++
 drivers/platform/chrome/wilco_ec_adv_power.c  | 533 ++++++++++++++++++
 drivers/platform/chrome/wilco_ec_adv_power.h  | 193 +++++++
 drivers/platform/chrome/wilco_ec_event.c      | 343 +++++++++++
 drivers/platform/chrome/wilco_ec_legacy.c     | 204 +++++++
 drivers/platform/chrome/wilco_ec_legacy.h     |  96 ++++
 drivers/platform/chrome/wilco_ec_mailbox.c    | 427 ++++++++++++++
 drivers/platform/chrome/wilco_ec_properties.c | 327 +++++++++++
 drivers/platform/chrome/wilco_ec_properties.h | 163 ++++++
 drivers/platform/chrome/wilco_ec_rtc.c        | 163 ++++++
 drivers/platform/chrome/wilco_ec_sysfs.c      | 253 +++++++++
 drivers/platform/chrome/wilco_ec_sysfs_util.h |  47 ++
 drivers/platform/chrome/wilco_ec_telemetry.c  |  66 +++
 drivers/platform/chrome/wilco_ec_telemetry.h  |  42 ++
 19 files changed, 3153 insertions(+), 59 deletions(-)
 create mode 100644 drivers/platform/chrome/wilco_ec.h
 create mode 100644 drivers/platform/chrome/wilco_ec_adv_power.c
 create mode 100644 drivers/platform/chrome/wilco_ec_adv_power.h
 create mode 100644 drivers/platform/chrome/wilco_ec_event.c
 create mode 100644 drivers/platform/chrome/wilco_ec_legacy.c
 create mode 100644 drivers/platform/chrome/wilco_ec_legacy.h
 create mode 100644 drivers/platform/chrome/wilco_ec_mailbox.c
 create mode 100644 drivers/platform/chrome/wilco_ec_properties.c
 create mode 100644 drivers/platform/chrome/wilco_ec_properties.h
 create mode 100644 drivers/platform/chrome/wilco_ec_rtc.c
 create mode 100644 drivers/platform/chrome/wilco_ec_sysfs.c
 create mode 100644 drivers/platform/chrome/wilco_ec_sysfs_util.h
 create mode 100644 drivers/platform/chrome/wilco_ec_telemetry.c
 create mode 100644 drivers/platform/chrome/wilco_ec_telemetry.h

-- 
2.20.0.405.gbc1bbc6f85-goog


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

* [RFC PATCH 01/10] CHROMIUM: cros_ec: Remove cros_ec dependency in lpc_mec
  2018-12-15  0:18 [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Nick Crews
@ 2018-12-15  0:18 ` Nick Crews
  2018-12-17 15:50   ` Enric Balletbo Serra
  2018-12-15  0:18 ` [RFC PATCH 02/10] CHROMIUM: wilco_ec: Add new driver for Wilco EC Nick Crews
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 16+ messages in thread
From: Nick Crews @ 2018-12-15  0:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: Duncan Laurie, Nick Crews, Olof Johansson, Benson Leung

From: Duncan Laurie <dlaurie@google.com>

In order to allow this code to be re-used, remove the dependency
on the rest of the cros_ec code from the cros_ec_lpc_mec functions.

Instead of using a hardcoded register base address of 0x800 have
this be passed in to cros_ec_lpc_mec_init().  The existing cros_ec
use case now passes in the 0x800 base address this way.

Signed-off-by: Duncan Laurie <dlaurie@google.com>
Signed-off-by: Nick Crews <ncrews@google.com>
---

 drivers/platform/chrome/cros_ec_lpc_mec.c | 54 +++++++++++++++++++----
 drivers/platform/chrome/cros_ec_lpc_mec.h | 45 +++++++++++--------
 drivers/platform/chrome/cros_ec_lpc_reg.c | 43 +++++-------------
 3 files changed, 85 insertions(+), 57 deletions(-)

diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.c b/drivers/platform/chrome/cros_ec_lpc_mec.c
index c4edfa83e493..18bd9f82be6c 100644
--- a/drivers/platform/chrome/cros_ec_lpc_mec.c
+++ b/drivers/platform/chrome/cros_ec_lpc_mec.c
@@ -24,6 +24,7 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/mfd/cros_ec_commands.h>
+#include <linux/mfd/cros_ec_lpc_mec.h>
 #include <linux/mutex.h>
 #include <linux/types.h>
 
@@ -34,6 +35,7 @@
  * EC mutex because memmap data may be accessed without it being held.
  */
 static struct mutex io_mutex;
+static u16 mec_emi_base, mec_emi_end;
 
 /*
  * cros_ec_lpc_mec_emi_write_address
@@ -46,10 +48,39 @@ static struct mutex io_mutex;
 static void cros_ec_lpc_mec_emi_write_address(u16 addr,
 			enum cros_ec_lpc_mec_emi_access_mode access_type)
 {
-	/* Address relative to start of EMI range */
-	addr -= MEC_EMI_RANGE_START;
-	outb((addr & 0xfc) | access_type, MEC_EMI_EC_ADDRESS_B0);
-	outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1);
+	outb((addr & 0xfc) | access_type, MEC_EMI_EC_ADDRESS_B0(mec_emi_base));
+	outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1(mec_emi_base));
+}
+
+/*
+ * cros_ec_lpc_mec_in_range
+ *
+ * Determine if requested addresses are in MEC EMI range.
+ *
+ * @offset:      Address offset
+ * @length: Number of bytes to check
+ *
+ * @return 1 if in range, 0 if not, and -1 if there is an error
+ *         such as the mec range not being initialized
+ */
+int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length)
+{
+	if (length == 0)
+		return -1;
+
+	if (WARN_ON(mec_emi_base == 0 || mec_emi_end == 0))
+		return -1;
+
+	if (offset >= mec_emi_base && offset < mec_emi_end) {
+		if (WARN_ON(offset + length - 1 <= mec_emi_end))
+			return -1;
+		return 1;
+	}
+
+	if (WARN_ON(offset + length > mec_emi_base && offset < mec_emi_end))
+		return -1;
+
+	return 0;
 }
 
 /*
@@ -71,6 +102,11 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
 	u8 sum = 0;
 	enum cros_ec_lpc_mec_emi_access_mode access, new_access;
 
+	/* Return checksum of 0 if window is not initialized */
+	WARN_ON(mec_emi_base == 0 || mec_emi_end == 0);
+	if (mec_emi_base == 0 || mec_emi_end == 0)
+		return 0;
+
 	/*
 	 * Long access cannot be used on misaligned data since reading B0 loads
 	 * the data register and writing B3 flushes.
@@ -86,9 +122,9 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
 	cros_ec_lpc_mec_emi_write_address(offset, access);
 
 	/* Skip bytes in case of misaligned offset */
-	io_addr = MEC_EMI_EC_DATA_B0 + (offset & 0x3);
+	io_addr = MEC_EMI_EC_DATA_B0(mec_emi_base) + (offset & 0x3);
 	while (i < length) {
-		while (io_addr <= MEC_EMI_EC_DATA_B3) {
+		while (io_addr <= MEC_EMI_EC_DATA_B3(mec_emi_base)) {
 			if (io_type == MEC_IO_READ)
 				buf[i] = inb(io_addr++);
 			else
@@ -118,7 +154,7 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
 		}
 
 		/* Access [B0, B3] on each loop pass */
-		io_addr = MEC_EMI_EC_DATA_B0;
+		io_addr = MEC_EMI_EC_DATA_B0(mec_emi_base);
 	}
 
 done:
@@ -128,9 +164,11 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
 }
 EXPORT_SYMBOL(cros_ec_lpc_io_bytes_mec);
 
-void cros_ec_lpc_mec_init(void)
+void cros_ec_lpc_mec_init(unsigned int base, unsigned int end)
 {
 	mutex_init(&io_mutex);
+	mec_emi_base = base;
+	mec_emi_end = end;
 }
 EXPORT_SYMBOL(cros_ec_lpc_mec_init);
 
diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.h b/drivers/platform/chrome/cros_ec_lpc_mec.h
index 105068c0e919..a81cc6a8b621 100644
--- a/drivers/platform/chrome/cros_ec_lpc_mec.h
+++ b/drivers/platform/chrome/cros_ec_lpc_mec.h
@@ -24,8 +24,6 @@
 #ifndef __CROS_EC_LPC_MEC_H
 #define __CROS_EC_LPC_MEC_H
 
-#include <linux/mfd/cros_ec_commands.h>
-
 enum cros_ec_lpc_mec_emi_access_mode {
 	/* 8-bit access */
 	ACCESS_TYPE_BYTE = 0x0,
@@ -45,35 +43,46 @@ enum cros_ec_lpc_mec_io_type {
 	MEC_IO_WRITE,
 };
 
-/* Access IO ranges 0x800 thru 0x9ff using EMI interface instead of LPC */
-#define MEC_EMI_RANGE_START EC_HOST_CMD_REGION0
-#define MEC_EMI_RANGE_END   (EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE)
-
 /* EMI registers are relative to base */
-#define MEC_EMI_BASE 0x800
-#define MEC_EMI_HOST_TO_EC (MEC_EMI_BASE + 0)
-#define MEC_EMI_EC_TO_HOST (MEC_EMI_BASE + 1)
-#define MEC_EMI_EC_ADDRESS_B0 (MEC_EMI_BASE + 2)
-#define MEC_EMI_EC_ADDRESS_B1 (MEC_EMI_BASE + 3)
-#define MEC_EMI_EC_DATA_B0 (MEC_EMI_BASE + 4)
-#define MEC_EMI_EC_DATA_B1 (MEC_EMI_BASE + 5)
-#define MEC_EMI_EC_DATA_B2 (MEC_EMI_BASE + 6)
-#define MEC_EMI_EC_DATA_B3 (MEC_EMI_BASE + 7)
+#define MEC_EMI_HOST_TO_EC(MEC_EMI_BASE)	(MEC_EMI_BASE + 0)
+#define MEC_EMI_EC_TO_HOST(MEC_EMI_BASE)	(MEC_EMI_BASE + 1)
+#define MEC_EMI_EC_ADDRESS_B0(MEC_EMI_BASE)	(MEC_EMI_BASE + 2)
+#define MEC_EMI_EC_ADDRESS_B1(MEC_EMI_BASE)	(MEC_EMI_BASE + 3)
+#define MEC_EMI_EC_DATA_B0(MEC_EMI_BASE)	(MEC_EMI_BASE + 4)
+#define MEC_EMI_EC_DATA_B1(MEC_EMI_BASE)	(MEC_EMI_BASE + 5)
+#define MEC_EMI_EC_DATA_B2(MEC_EMI_BASE)	(MEC_EMI_BASE + 6)
+#define MEC_EMI_EC_DATA_B3(MEC_EMI_BASE)	(MEC_EMI_BASE + 7)
 
-/*
+/**
  * cros_ec_lpc_mec_init
  *
  * Initialize MEC I/O.
+ *
+ * @base: MEC EMI Base address
+ * @end:  MEC EMI End address
  */
-void cros_ec_lpc_mec_init(void);
+void cros_ec_lpc_mec_init(unsigned int base, unsigned int end);
 
-/*
+/**
  * cros_ec_lpc_mec_destroy
  *
  * Cleanup MEC I/O.
  */
 void cros_ec_lpc_mec_destroy(void);
 
+/**
+ * cros_ec_lpc_mec_in_range
+ *
+ * Determine if requested addresses are in MEC EMI range.
+ *
+ * @offset: Address offset
+ * @length: Number of bytes to check
+ *
+ * @return 1 if in range, 0 if not, and -1 if there is an error
+ *         such as the mec range not being initialized
+ */
+int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length);
+
 /**
  * cros_ec_lpc_io_bytes_mec - Read / write bytes to MEC EMI port
  *
diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.c b/drivers/platform/chrome/cros_ec_lpc_reg.c
index fc23d535c404..4cba259b5b1e 100644
--- a/drivers/platform/chrome/cros_ec_lpc_reg.c
+++ b/drivers/platform/chrome/cros_ec_lpc_reg.c
@@ -59,51 +59,32 @@ static u8 lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg)
 
 u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest)
 {
-	if (length == 0)
-		return 0;
-
-	/* Access desired range through EMI interface */
-	if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) {
-		/* Ensure we don't straddle EMI region */
-		if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END))
-			return 0;
+	int in_range = cros_ec_lpc_mec_in_range(offset, length);
 
-		return cros_ec_lpc_io_bytes_mec(MEC_IO_READ, offset, length,
-						dest);
-	}
-
-	if (WARN_ON(offset + length > MEC_EMI_RANGE_START &&
-		    offset < MEC_EMI_RANGE_START))
+	if (in_range < 0)
 		return 0;
 
-	return lpc_read_bytes(offset, length, dest);
+	return in_range ?
+		cros_ec_lpc_io_bytes_mec(MEC_IO_READ, offset, length, dest) :
+		lpc_read_bytes(offset, length, dest);
 }
 
 u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg)
 {
-	if (length == 0)
-		return 0;
-
-	/* Access desired range through EMI interface */
-	if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) {
-		/* Ensure we don't straddle EMI region */
-		if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END))
-			return 0;
+	int in_range = cros_ec_lpc_mec_in_range(offset, length);
 
-		return cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, offset, length,
-						msg);
-	}
-
-	if (WARN_ON(offset + length > MEC_EMI_RANGE_START &&
-		    offset < MEC_EMI_RANGE_START))
+	if (in_range < 0)
 		return 0;
 
-	return lpc_write_bytes(offset, length, msg);
+	return in_range ?
+		cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, offset, length, msg) :
+		lpc_write_bytes(offset, length, msg);
 }
 
 void cros_ec_lpc_reg_init(void)
 {
-	cros_ec_lpc_mec_init();
+	cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0,
+			     EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE);
 }
 
 void cros_ec_lpc_reg_destroy(void)
-- 
2.20.0.405.gbc1bbc6f85-goog


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

* [RFC PATCH 02/10] CHROMIUM: wilco_ec: Add new driver for Wilco EC
  2018-12-15  0:18 [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Nick Crews
  2018-12-15  0:18 ` [RFC PATCH 01/10] CHROMIUM: cros_ec: Remove cros_ec dependency in lpc_mec Nick Crews
@ 2018-12-15  0:18 ` Nick Crews
  2018-12-17 15:50   ` Enric Balletbo Serra
  2018-12-15  0:18 ` [RFC PATCH 03/10] CHROMIUM: wilco_ec: Add sysfs attributes Nick Crews
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 16+ messages in thread
From: Nick Crews @ 2018-12-15  0:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: Duncan Laurie, Nick Crews, Olof Johansson, Benson Leung

From: Duncan Laurie <dlaurie@google.com>

This EC is an incompatible variant of the typical Chrome OS embedded
controller.  It uses the same low-level communication and a similar
protocol with some significant differences.  The EC firmware does
not support the same mailbox commands so it is not registered as a
cros_ec device type.

Signed-off-by: Duncan Laurie <dlaurie@google.com>
Signed-off-by: Nick Crews <ncrews@google.com>
---

 drivers/platform/chrome/Kconfig            |  14 +-
 drivers/platform/chrome/Makefile           |   3 +
 drivers/platform/chrome/wilco_ec.h         |  97 +++++
 drivers/platform/chrome/wilco_ec_mailbox.c | 395 +++++++++++++++++++++
 4 files changed, 508 insertions(+), 1 deletion(-)
 create mode 100644 drivers/platform/chrome/wilco_ec.h
 create mode 100644 drivers/platform/chrome/wilco_ec_mailbox.c

diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index 16b1615958aa..4168d5e6bedc 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -49,6 +49,18 @@ config CHROMEOS_TBMC
 	  To compile this driver as a module, choose M here: the
 	  module will be called chromeos_tbmc.
 
+config WILCO_EC
+	tristate "ChromeOS Wilco Embedded Controller"
+	depends on ACPI && (X86 || COMPILE_TEST)
+	select CROS_EC_LPC_MEC
+	help
+	  If you say Y here, you get support for talking to the Chrome OS
+	  Wilco EC over an eSPI bus. This uses a simple byte-level protocol
+	  with a checksum.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wilco_ec.
+
 config CROS_EC_CTL
         tristate
 
@@ -86,7 +98,7 @@ config CROS_EC_LPC
 
 config CROS_EC_LPC_MEC
 	bool "ChromeOS Embedded Controller LPC Microchip EC (MEC) variant"
-	depends on CROS_EC_LPC
+	depends on CROS_EC_LPC || WILCO_EC
 	default n
 	help
 	  If you say Y here, a variant LPC protocol for the Microchip EC
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index cd591bf872bb..b132ba5b3e3d 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -12,4 +12,7 @@ cros_ec_lpcs-objs			:= cros_ec_lpc.o cros_ec_lpc_reg.o
 cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC)	+= cros_ec_lpc_mec.o
 obj-$(CONFIG_CROS_EC_LPC)		+= cros_ec_lpcs.o
 obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o
+
 obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT)	+= cros_kbd_led_backlight.o
+wilco_ec-objs				:= wilco_ec_mailbox.o
+obj-$(CONFIG_WILCO_EC)			+= wilco_ec.o
diff --git a/drivers/platform/chrome/wilco_ec.h b/drivers/platform/chrome/wilco_ec.h
new file mode 100644
index 000000000000..ba16fcff87c4
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec.h
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * wilco_ec - Chrome OS Wilco Embedded Controller
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WILCO_EC_H
+#define WILCO_EC_H
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+#define WILCO_EC_FLAG_NO_RESPONSE	BIT(0) /* EC does not respond */
+#define WILCO_EC_FLAG_EXTENDED_DATA	BIT(1) /* EC returns 256 data bytes */
+#define WILCO_EC_FLAG_RAW_REQUEST	BIT(2) /* Do not trim request data */
+#define WILCO_EC_FLAG_RAW_RESPONSE	BIT(3) /* Do not trim response data */
+#define WILCO_EC_FLAG_RAW		(WILCO_EC_FLAG_RAW_REQUEST | \
+					 WILCO_EC_FLAG_RAW_RESPONSE)
+
+/**
+ * enum wilco_ec_msg_type - Message type to select a set of command codes.
+ * @WILCO_EC_MSG_LEGACY: Legacy EC messages for standard EC behavior.
+ * @WILCO_EC_MSG_PROPERTY: Get/Set/Sync EC controlled NVRAM property.
+ * @WILCO_EC_MSG_TELEMETRY: Telemetry data provided by the EC.
+ */
+enum wilco_ec_msg_type {
+	WILCO_EC_MSG_LEGACY = 0x00f0,
+	WILCO_EC_MSG_PROPERTY = 0x00f2,
+	WILCO_EC_MSG_TELEMETRY = 0x00f5,
+};
+
+/**
+ * struct wilco_ec_device - Wilco Embedded Controller handle.
+ * @dev: Device handle.
+ * @mailbox_lock: Mutex to ensure one mailbox command at a time.
+ * @io_command: I/O port for mailbox command.  Provided by ACPI.
+ * @io_data: I/O port for mailbox data.  Provided by ACPI.
+ * @io_packet: I/O port for mailbox packet data.  Provided by ACPI.
+ * @data_buffer: Buffer used for EC communication.  The same buffer
+ *               is used to hold the request and the response.
+ * @data_size: Size of the data buffer used for EC communication.
+ */
+struct wilco_ec_device {
+	struct device *dev;
+	struct mutex mailbox_lock;
+	struct resource *io_command;
+	struct resource *io_data;
+	struct resource *io_packet;
+	void *data_buffer;
+	size_t data_size;
+};
+
+/**
+ * struct wilco_ec_message - Request and response message.
+ * @type: Mailbox message type.
+ * @flags: Message flags.
+ * @command: Mailbox command code.
+ * @result: Result code from the EC.  Non-zero indicates an error.
+ * @request_size: Number of bytes to send to the EC.
+ * @request_data: Buffer containing the request data.
+ * @response_size: Number of bytes expected from the EC.
+ *                 This is 32 by default and 256 if the flag
+ *                 is set for %WILCO_EC_FLAG_EXTENDED_DATA
+ * @response_data: Buffer containing the response data, should be
+ *                 response_size bytes and allocated by caller.
+ */
+struct wilco_ec_message {
+	enum wilco_ec_msg_type type;
+	u8 flags;
+	u8 command;
+	u8 result;
+	size_t request_size;
+	void *request_data;
+	size_t response_size;
+	void *response_data;
+};
+
+/**
+ * wilco_ec_mailbox() - Send request to the EC and receive the response.
+ * @ec: Wilco EC device.
+ * @msg: Wilco EC message.
+ *
+ * Return: Number of bytes received or negative error code on failure.
+ */
+int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg);
+
+#endif /* WILCO_EC_H */
diff --git a/drivers/platform/chrome/wilco_ec_mailbox.c b/drivers/platform/chrome/wilco_ec_mailbox.c
new file mode 100644
index 000000000000..6613c18c2a82
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec_mailbox.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * wilco_ec_mailbox - Mailbox interface for Wilco Embedded Controller
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * The Wilco EC is similar to a typical Chrome OS embedded controller.
+ * It uses the same MEC based low-level communication and a similar
+ * protocol, but with some important differences.  The EC firmware does
+ * not support the same mailbox commands so it is not registered as a
+ * cros_ec device type.
+ *
+ * Most messages follow a standard format, but there are some exceptions
+ * and an interface is provided to do direct/raw transactions that do not
+ * make assumptions about byte placement.
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/mfd/cros_ec_lpc_mec.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "wilco_ec.h"
+
+/* Version of mailbox interface */
+#define EC_MAILBOX_VERSION		0
+
+/* Command to start mailbox transaction */
+#define EC_MAILBOX_START_COMMAND	0xda
+
+/* Version of EC protocol */
+#define EC_MAILBOX_PROTO_VERSION	3
+
+/* Normal commands have a maximum 32 bytes of data */
+#define EC_MAILBOX_DATA_SIZE		32
+
+/* Extended commands have 256 bytes of response data */
+#define EC_MAILBOX_DATA_SIZE_EXTENDED	256
+
+/* Number of header bytes to be counted as data bytes */
+#define EC_MAILBOX_DATA_EXTRA		2
+
+/* Maximum timeout */
+#define EC_MAILBOX_TIMEOUT		HZ
+
+/* EC response flags */
+#define EC_CMDR_DATA		BIT(0)	/* Data ready for host to read */
+#define EC_CMDR_PENDING		BIT(1)	/* Write pending to EC */
+#define EC_CMDR_BUSY		BIT(2)	/* EC is busy processing a command */
+#define EC_CMDR_CMD		BIT(3)	/* Last host write was a command */
+
+/**
+ * struct wilco_ec_request - Mailbox request message format.
+ * @struct_version: Should be %EC_MAILBOX_PROTO_VERSION
+ * @checksum: Sum of all bytes must be 0.
+ * @mailbox_id: Mailbox identifier, specifies the command set.
+ * @mailbox_version: Mailbox interface version %EC_MAILBOX_VERSION
+ * @reserved: Set to zero.
+ * @data_size: Length of request, data + last 2 bytes of the header.
+ * @command: Mailbox command code, unique for each mailbox_id set.
+ * @reserved_raw: Set to zero for most commands, but is used by
+ *                some command types and for raw commands.
+ */
+struct wilco_ec_request {
+	u8 struct_version;
+	u8 checksum;
+	u16 mailbox_id;
+	u8 mailbox_version;
+	u8 reserved;
+	u16 data_size;
+	u8 command;
+	u8 reserved_raw;
+} __packed;
+
+/**
+ * struct wilco_ec_response - Mailbox response message format.
+ * @struct_version: Should be %EC_MAILBOX_PROTO_VERSION
+ * @checksum: Sum of all bytes must be 0.
+ * @result: Result code from the EC.  Non-zero indicates an error.
+ * @data_size: Length of the response data buffer.
+ * @reserved: Set to zero.
+ * @mbox0: EC returned data at offset 0 is unused (always 0) so this byte
+ *         is treated as part of the header instead of the data.
+ * @data: Response data buffer.  Max size is %EC_MAILBOX_DATA_SIZE_EXTENDED.
+ */
+struct wilco_ec_response {
+	u8 struct_version;
+	u8 checksum;
+	u16 result;
+	u16 data_size;
+	u8 reserved[2];
+	u8 mbox0;
+	u8 data[0];
+} __packed;
+
+/**
+ * wilco_ec_response_timed_out() - Wait for EC response.
+ * @ec: EC device.
+ *
+ * Return: true if EC timed out, false if EC did not time out.
+ */
+static bool wilco_ec_response_timed_out(struct wilco_ec_device *ec)
+{
+	unsigned long timeout = jiffies + EC_MAILBOX_TIMEOUT;
+
+	usleep_range(200, 300);
+	do {
+		if (!(inb(ec->io_command->start) &
+		      (EC_CMDR_PENDING | EC_CMDR_BUSY)))
+			return false;
+		usleep_range(100, 200);
+	} while (time_before(jiffies, timeout));
+
+	return true;
+}
+
+/**
+ * wilco_ec_checksum() - Compute 8bit checksum over data range.
+ * @data: Data to checksum.
+ * @size: Number of bytes to checksum.
+ *
+ * Return: 8bit checksum of provided data.
+ */
+static u8 wilco_ec_checksum(const void *data, size_t size)
+{
+	u8 *data_bytes = (u8 *)data;
+	u8 checksum = 0;
+	size_t i;
+
+	for (i = 0; i < size; i++)
+		checksum += data_bytes[i];
+
+	return checksum;
+}
+
+/**
+ * wilco_ec_prepare() - Prepare the request structure for the EC.
+ * @msg: EC message with request information.
+ * @rq: EC request structure to fill.
+ */
+static void wilco_ec_prepare(struct wilco_ec_message *msg,
+			     struct wilco_ec_request *rq)
+{
+	memset(rq, 0, sizeof(*rq));
+
+	/* Handle messages without trimming bytes from the request */
+	if (msg->request_size && msg->flags & WILCO_EC_FLAG_RAW_REQUEST) {
+		rq->reserved_raw = *(u8 *)msg->request_data;
+		msg->request_size--;
+		memmove(msg->request_data, msg->request_data + 1,
+			msg->request_size);
+	}
+
+	/* Fill in request packet */
+	rq->struct_version = EC_MAILBOX_PROTO_VERSION;
+	rq->mailbox_id = msg->type;
+	rq->mailbox_version = EC_MAILBOX_VERSION;
+	rq->data_size = msg->request_size + EC_MAILBOX_DATA_EXTRA;
+	rq->command = msg->command;
+
+	/* Checksum header and data */
+	rq->checksum = wilco_ec_checksum(rq, sizeof(*rq));
+	rq->checksum += wilco_ec_checksum(msg->request_data, msg->request_size);
+	rq->checksum = -rq->checksum;
+}
+
+/**
+ * wilco_ec_transfer() - Send EC request and receive EC response.
+ * @ec: EC device.
+ * @msg: EC message data for request and response.
+ *
+ * Return: 0 for success or negative error code on failure.
+ */
+static int wilco_ec_transfer(struct wilco_ec_device *ec,
+			     struct wilco_ec_message *msg)
+{
+	struct wilco_ec_request *rq;
+	struct wilco_ec_response *rs;
+	u8 checksum;
+	size_t size;
+
+	/* Prepare request packet */
+	rq = ec->data_buffer;
+	wilco_ec_prepare(msg, rq);
+
+	/* Write request header */
+	cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 0, sizeof(*rq), (u8 *)rq);
+
+	/* Write request data */
+	cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, sizeof(*rq), msg->request_size,
+				 msg->request_data);
+
+	/* Start the command */
+	outb(EC_MAILBOX_START_COMMAND, ec->io_command->start);
+
+	/* Wait for it to complete */
+	if (wilco_ec_response_timed_out(ec)) {
+		dev_err(ec->dev, "response timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	/* Some commands will put the EC into a state where it cannot respond */
+	if (msg->flags & WILCO_EC_FLAG_NO_RESPONSE) {
+		dev_dbg(ec->dev, "EC does not respond to this command\n");
+		return 0;
+	}
+
+	/* Check result */
+	msg->result = inb(ec->io_data->start);
+	if (msg->result) {
+		dev_err(ec->dev, "bad response: 0x%02x\n", msg->result);
+		return -EIO;
+	}
+
+	if (msg->flags & WILCO_EC_FLAG_EXTENDED_DATA) {
+		/* Extended commands return 256 bytes of data */
+		size = EC_MAILBOX_DATA_SIZE_EXTENDED;
+	} else {
+		/* Otherwise EC commands return 32 bytes of data */
+		size = EC_MAILBOX_DATA_SIZE;
+	}
+
+	/* Read back response */
+	rs = ec->data_buffer;
+	checksum = cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 0,
+					    sizeof(*rs) + size, (u8 *)rs);
+	if (checksum) {
+		dev_err(ec->dev, "bad packet checksum 0x%02x\n", rs->checksum);
+		return -EBADMSG;
+	}
+	msg->result = rs->result;
+
+	/* Check the returned data size, skipping the header */
+	if (rs->data_size != size) {
+		dev_err(ec->dev, "unexpected packet size (%u != %zu)",
+			rs->data_size, size);
+		return -EMSGSIZE;
+	}
+
+	/* Skip 1 response data byte unless specified */
+	size = (msg->flags & WILCO_EC_FLAG_RAW_RESPONSE) ? 0 : 1;
+
+	if (msg->response_size > rs->data_size - size) {
+		dev_err(ec->dev, "response data too short (%u > %zu)",
+			rs->data_size - size, msg->response_size);
+		return -EMSGSIZE;
+	}
+
+	/* Ignore response data bytes as requested */
+	memcpy(msg->response_data, rs->data + size, msg->response_size);
+
+	/* Return actual amount of data received */
+	return msg->response_size;
+}
+
+int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg)
+{
+	size_t size = EC_MAILBOX_DATA_SIZE;
+	int ret;
+
+	dev_dbg(ec->dev, "cmd=%02x type=%04x flags=%02x rslen=%d rqlen=%d\n",
+		msg->command, msg->type, msg->flags, msg->response_size,
+		msg->request_size);
+
+	if (msg->request_size > size) {
+		dev_err(ec->dev, "provided request too large: %zu > %zu\n",
+			msg->request_size, size);
+		return -EINVAL;
+	}
+
+	/* Check for extended size on response data if flag is set */
+	if (msg->flags & WILCO_EC_FLAG_EXTENDED_DATA)
+		size = EC_MAILBOX_DATA_SIZE_EXTENDED;
+
+	if (msg->response_size > size) {
+		dev_err(ec->dev, "expected response too large: %zu > %zu\n",
+			msg->response_size, size);
+		return -EINVAL;
+	}
+	if (msg->request_size && !msg->request_data) {
+		dev_err(ec->dev, "request data missing\n");
+		return -EINVAL;
+	}
+	if (msg->response_size && !msg->response_data) {
+		dev_err(ec->dev, "response data missing\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&ec->mailbox_lock);
+
+	ret = wilco_ec_transfer(ec, msg);
+	if (ret >= 0 && msg->result)
+		ret = -EBADMSG;
+
+	mutex_unlock(&ec->mailbox_lock);
+
+	return ret;
+}
+
+static struct resource *wilco_get_resource(struct platform_device *pdev,
+					   int index)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_IO, index);
+	if (!res) {
+		dev_err(dev, "couldn't find IO resource %d\n", index);
+		return NULL;
+	}
+
+	if (!devm_request_region(dev, res->start, resource_size(res),
+				 dev_name(dev))) {
+		dev_err(dev, "couldn't reserve IO region %d\n", index);
+		return NULL;
+	}
+
+	return res;
+}
+
+static int wilco_ec_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct wilco_ec_device *ec;
+
+	ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
+	if (!ec)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, ec);
+	ec->dev = dev;
+	mutex_init(&ec->mailbox_lock);
+
+	/* Largest data buffer size requirement is extended data response */
+	ec->data_size = sizeof(struct wilco_ec_response) +
+		EC_MAILBOX_DATA_SIZE_EXTENDED;
+	ec->data_buffer = devm_kzalloc(dev, ec->data_size, GFP_KERNEL);
+	if (!ec->data_buffer)
+		return -ENOMEM;
+
+	/* Prepare access to IO regions provided by ACPI */
+	ec->io_data = wilco_get_resource(pdev, 0);	/* Host Data */
+	ec->io_command = wilco_get_resource(pdev, 1);	/* Host Command */
+	ec->io_packet = wilco_get_resource(pdev, 2);	/* MEC EMI */
+	if (!ec->io_data || !ec->io_command || !ec->io_packet)
+		return -ENODEV;
+
+	/* Initialize cros_ec register interface for communication */
+	cros_ec_lpc_mec_init(ec->io_packet->start,
+			     ec->io_packet->start + EC_MAILBOX_DATA_SIZE);
+
+	return 0;
+}
+
+static int wilco_ec_remove(struct platform_device *pdev)
+{
+	/* Teardown cros_ec interface */
+	cros_ec_lpc_mec_destroy();
+
+	return 0;
+}
+
+static const struct acpi_device_id wilco_ec_acpi_device_ids[] = {
+	{ "GOOG000C", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, wilco_ec_acpi_device_ids);
+
+static struct platform_driver wilco_ec_driver = {
+	.driver = {
+		.name = "wilco_ec",
+		.acpi_match_table = wilco_ec_acpi_device_ids,
+	},
+	.probe = wilco_ec_probe,
+	.remove = wilco_ec_remove,
+};
+
+module_platform_driver(wilco_ec_driver);
+
+MODULE_AUTHOR("Duncan Laurie <dlaurie@chromium.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Chrome OS Wilco Embedded Controller driver");
+MODULE_ALIAS("platform:wilco-ec");
-- 
2.20.0.405.gbc1bbc6f85-goog


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

* [RFC PATCH 03/10] CHROMIUM: wilco_ec: Add sysfs attributes
  2018-12-15  0:18 [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Nick Crews
  2018-12-15  0:18 ` [RFC PATCH 01/10] CHROMIUM: cros_ec: Remove cros_ec dependency in lpc_mec Nick Crews
  2018-12-15  0:18 ` [RFC PATCH 02/10] CHROMIUM: wilco_ec: Add new driver for Wilco EC Nick Crews
@ 2018-12-15  0:18 ` Nick Crews
  2018-12-17 16:45   ` Enric Balletbo Serra
  2018-12-15  0:18 ` [RFC PATCH 04/10] CHROMIUM: wilco_ec: Add support for raw commands in sysfs Nick Crews
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 16+ messages in thread
From: Nick Crews @ 2018-12-15  0:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: Duncan Laurie, Nick Crews, Olof Johansson, Benson Leung

From: Duncan Laurie <dlaurie@google.com>

Add some sample sysfs attributes for the Wilco EC that show how
the mailbox interface works.

> cat /sys/bus/platform/devices/GOOG000C\:00/version
Label        : 99.99.99
SVN Revision : 738ed.99
Model Number : 08;8
Build Date   : 08/30/18

Signed-off-by: Duncan Laurie <dlaurie@google.com>
Signed-off-by: Nick Crews <ncrews@google.com>
---

 drivers/platform/chrome/Makefile           |   3 +-
 drivers/platform/chrome/wilco_ec.h         |  14 +++
 drivers/platform/chrome/wilco_ec_mailbox.c |  12 ++
 drivers/platform/chrome/wilco_ec_sysfs.c   | 121 +++++++++++++++++++++
 4 files changed, 148 insertions(+), 2 deletions(-)
 create mode 100644 drivers/platform/chrome/wilco_ec_sysfs.c

diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index b132ba5b3e3d..e8603bc5b095 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -13,6 +13,5 @@ cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC)	+= cros_ec_lpc_mec.o
 obj-$(CONFIG_CROS_EC_LPC)		+= cros_ec_lpcs.o
 obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o
 
-obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT)	+= cros_kbd_led_backlight.o
-wilco_ec-objs				:= wilco_ec_mailbox.o
+wilco_ec-objs				:= wilco_ec_mailbox.o wilco_ec_sysfs.o
 obj-$(CONFIG_WILCO_EC)			+= wilco_ec.o
diff --git a/drivers/platform/chrome/wilco_ec.h b/drivers/platform/chrome/wilco_ec.h
index ba16fcff87c4..699f4cf744dc 100644
--- a/drivers/platform/chrome/wilco_ec.h
+++ b/drivers/platform/chrome/wilco_ec.h
@@ -94,4 +94,18 @@ struct wilco_ec_message {
  */
 int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg);
 
+/**
+ * wilco_ec_sysfs_init() - Create sysfs attributes.
+ * @ec: EC device.
+ *
+ * Return: 0 for success or negative error code on failure.
+ */
+int wilco_ec_sysfs_init(struct wilco_ec_device *ec);
+
+/**
+ * wilco_ec_sysfs_remove() - Remove sysfs attributes.
+ * @ec: EC device.
+ */
+void wilco_ec_sysfs_remove(struct wilco_ec_device *ec);
+
 #endif /* WILCO_EC_H */
diff --git a/drivers/platform/chrome/wilco_ec_mailbox.c b/drivers/platform/chrome/wilco_ec_mailbox.c
index 6613c18c2a82..414ea0a8ad03 100644
--- a/drivers/platform/chrome/wilco_ec_mailbox.c
+++ b/drivers/platform/chrome/wilco_ec_mailbox.c
@@ -361,11 +361,23 @@ static int wilco_ec_probe(struct platform_device *pdev)
 	cros_ec_lpc_mec_init(ec->io_packet->start,
 			     ec->io_packet->start + EC_MAILBOX_DATA_SIZE);
 
+	/* Create sysfs attributes for userspace interaction */
+	if (wilco_ec_sysfs_init(ec) < 0) {
+		dev_err(dev, "Failed to create sysfs attributes\n");
+		cros_ec_lpc_mec_destroy();
+		return -ENODEV;
+	}
+
 	return 0;
 }
 
 static int wilco_ec_remove(struct platform_device *pdev)
 {
+	struct wilco_ec_device *ec = platform_get_drvdata(pdev);
+
+	/* Remove sysfs attributes */
+	wilco_ec_sysfs_remove(ec);
+
 	/* Teardown cros_ec interface */
 	cros_ec_lpc_mec_destroy();
 
diff --git a/drivers/platform/chrome/wilco_ec_sysfs.c b/drivers/platform/chrome/wilco_ec_sysfs.c
new file mode 100644
index 000000000000..f9ae6cef6169
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec_sysfs.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * wilco_ec_sysfs - Sysfs attributes for Wilco Embedded Controller
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/ctype.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include "wilco_ec.h"
+
+#define EC_COMMAND_EC_INFO		0x38
+#define EC_INFO_SIZE			 9
+#define EC_COMMAND_STEALTH_MODE		0xfc
+
+struct ec_info {
+	u8 index;
+	const char *label;
+};
+
+static ssize_t wilco_ec_show_info(struct wilco_ec_device *ec, char *buf,
+				  ssize_t count, struct ec_info *info)
+{
+	char result[EC_INFO_SIZE];
+	struct wilco_ec_message msg = {
+		.type = WILCO_EC_MSG_LEGACY,
+		.command = EC_COMMAND_EC_INFO,
+		.request_data = &info->index,
+		.request_size = sizeof(info->index),
+		.response_data = result,
+		.response_size = EC_INFO_SIZE,
+	};
+	int ret;
+
+	ret = wilco_ec_mailbox(ec, &msg);
+	if (ret != EC_INFO_SIZE)
+		return scnprintf(buf + count, PAGE_SIZE - count,
+				 "%-12s : ERROR %d\n", info->label, ret);
+
+	return scnprintf(buf + count, PAGE_SIZE - count,
+			 "%-12s : %s\n", info->label, result);
+}
+
+static ssize_t version_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	struct wilco_ec_device *ec = dev_get_drvdata(dev);
+	struct ec_info wilco_ec_info[] = {
+		{ 0, "Label" },
+		{ 1, "SVN Revision" },
+		{ 2, "Model Number" },
+		{ 3, "Build Date" },
+		{ 0xff, NULL },
+	};
+	struct ec_info *info = wilco_ec_info;
+	ssize_t c = 0;
+
+	for (info = wilco_ec_info; info->label; info++)
+		c += wilco_ec_show_info(ec, buf, c, info);
+
+	return c;
+}
+
+static ssize_t stealth_mode_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct wilco_ec_device *ec = dev_get_drvdata(dev);
+	u8 param;
+	struct wilco_ec_message msg = {
+		.type = WILCO_EC_MSG_LEGACY,
+		.command = EC_COMMAND_STEALTH_MODE,
+		.request_data = &param,
+		.request_size = sizeof(param),
+	};
+	int ret;
+	bool enable;
+
+	ret = kstrtobool(buf, &enable);
+	if (ret)
+		return ret;
+
+	/* Invert input parameter, EC expects 0=on and 1=off */
+	param = enable ? 0 : 1;
+
+	ret = wilco_ec_mailbox(ec, &msg);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static DEVICE_ATTR_RO(version);
+static DEVICE_ATTR_WO(stealth_mode);
+
+static struct attribute *wilco_ec_attrs[] = {
+	&dev_attr_version.attr,
+	&dev_attr_stealth_mode.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(wilco_ec);
+
+int wilco_ec_sysfs_init(struct wilco_ec_device *ec)
+{
+	return sysfs_create_groups(&ec->dev->kobj, wilco_ec_groups);
+}
+
+void wilco_ec_sysfs_remove(struct wilco_ec_device *ec)
+{
+	sysfs_remove_groups(&ec->dev->kobj, wilco_ec_groups);
+}
-- 
2.20.0.405.gbc1bbc6f85-goog


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

* [RFC PATCH 04/10] CHROMIUM: wilco_ec: Add support for raw commands in sysfs
  2018-12-15  0:18 [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Nick Crews
                   ` (2 preceding siblings ...)
  2018-12-15  0:18 ` [RFC PATCH 03/10] CHROMIUM: wilco_ec: Add sysfs attributes Nick Crews
@ 2018-12-15  0:18 ` Nick Crews
  2018-12-15  0:18 ` [RFC PATCH 05/10] CHROMIUM: wilco_ec: Add RTC class driver Nick Crews
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Nick Crews @ 2018-12-15  0:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: Duncan Laurie, Nick Crews, Olof Johansson, Benson Leung

From: Duncan Laurie <dlaurie@google.com>

Add a sysfs attribute that allows sending raw commands to the EC.
This is useful for development and debug but should not be enabled
in a production environment.

> echo 00 f0 38 00 03 00 > /sys/bus/platform/devices/GOOG000C\:00/raw
> cat /sys/bus/platform/devices/GOOG000C\:00/raw
00 37 33 38 65 64 00...

Signed-off-by: Duncan Laurie <dlaurie@google.com>
Signed-off-by: Nick Crews <ncrews@google.com>
---

 drivers/platform/chrome/Kconfig            |  10 ++
 drivers/platform/chrome/wilco_ec.h         |   6 +
 drivers/platform/chrome/wilco_ec_mailbox.c |   6 -
 drivers/platform/chrome/wilco_ec_sysfs.c   | 126 +++++++++++++++++++++
 4 files changed, 142 insertions(+), 6 deletions(-)

diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index 4168d5e6bedc..05c6d9a00395 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -61,6 +61,16 @@ config WILCO_EC
 	  To compile this driver as a module, choose M here: the
 	  module will be called wilco_ec.
 
+config WILCO_EC_SYSFS_RAW
+	bool "Enable raw access to EC via sysfs"
+	depends on WILCO_EC
+	default n
+	help
+	  If you say Y here, you get support for sending raw commands to
+	  the Wilco EC via sysfs.  These commands do not do any byte
+	  manipulation and allow for testing arbitrary commands.  This
+	  interface is intended for debug only and is disabled by default.
+
 config CROS_EC_CTL
         tristate
 
diff --git a/drivers/platform/chrome/wilco_ec.h b/drivers/platform/chrome/wilco_ec.h
index 699f4cf744dc..0b3dec4e2830 100644
--- a/drivers/platform/chrome/wilco_ec.h
+++ b/drivers/platform/chrome/wilco_ec.h
@@ -20,6 +20,12 @@
 #include <linux/device.h>
 #include <linux/kernel.h>
 
+/* Normal commands have a maximum 32 bytes of data */
+#define EC_MAILBOX_DATA_SIZE		32
+
+/* Extended commands have 256 bytes of response data */
+#define EC_MAILBOX_DATA_SIZE_EXTENDED	256
+
 #define WILCO_EC_FLAG_NO_RESPONSE	BIT(0) /* EC does not respond */
 #define WILCO_EC_FLAG_EXTENDED_DATA	BIT(1) /* EC returns 256 data bytes */
 #define WILCO_EC_FLAG_RAW_REQUEST	BIT(2) /* Do not trim request data */
diff --git a/drivers/platform/chrome/wilco_ec_mailbox.c b/drivers/platform/chrome/wilco_ec_mailbox.c
index 414ea0a8ad03..1cb34b7280fd 100644
--- a/drivers/platform/chrome/wilco_ec_mailbox.c
+++ b/drivers/platform/chrome/wilco_ec_mailbox.c
@@ -44,12 +44,6 @@
 /* Version of EC protocol */
 #define EC_MAILBOX_PROTO_VERSION	3
 
-/* Normal commands have a maximum 32 bytes of data */
-#define EC_MAILBOX_DATA_SIZE		32
-
-/* Extended commands have 256 bytes of response data */
-#define EC_MAILBOX_DATA_SIZE_EXTENDED	256
-
 /* Number of header bytes to be counted as data bytes */
 #define EC_MAILBOX_DATA_EXTRA		2
 
diff --git a/drivers/platform/chrome/wilco_ec_sysfs.c b/drivers/platform/chrome/wilco_ec_sysfs.c
index f9ae6cef6169..eeebd4ba4a39 100644
--- a/drivers/platform/chrome/wilco_ec_sysfs.c
+++ b/drivers/platform/chrome/wilco_ec_sysfs.c
@@ -23,6 +23,126 @@
 #define EC_INFO_SIZE			 9
 #define EC_COMMAND_STEALTH_MODE		0xfc
 
+#ifdef CONFIG_WILCO_EC_SYSFS_RAW
+
+/* Raw data buffer, large enough to hold extended responses */
+static size_t raw_response_size;
+static u8 raw_response_data[EC_MAILBOX_DATA_SIZE_EXTENDED];
+
+/*
+ * raw: write a raw command and return the result
+ *
+ * Bytes 0-1 indicate the message type:
+ *  00 F0 = Execute Legacy Command
+ *  00 F2 = Read/Write NVRAM Property
+ * Byte 2 provides the command code
+ * Bytes 3+ consist of the data passed in the request
+ *
+ * example: read the EC info type 1:
+ *  # echo 00 f0 38 00 01 00 > raw
+ *  # cat raw
+ *  00 38 31 34 34 66 00 00 00 00 00 00 00 00 00 00 00...
+ */
+
+static ssize_t raw_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	ssize_t count = 0;
+
+	if (raw_response_size) {
+		int i;
+
+		for (i = 0; i < raw_response_size; ++i)
+			count += scnprintf(buf + count, PAGE_SIZE - count,
+					   "%02x ", raw_response_data[i]);
+
+		count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+
+		/* Only return response the first time it is read */
+		raw_response_size = 0;
+	}
+
+	return count;
+}
+
+static ssize_t raw_store(struct device *dev,
+			 struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct wilco_ec_device *ec = dev_get_drvdata(dev);
+	struct wilco_ec_message msg;
+	u8 raw_request_data[EC_MAILBOX_DATA_SIZE];
+	int in_offset = 0;
+	int out_offset = 0;
+	int ret;
+
+	while (in_offset < count) {
+		char word_buf[EC_MAILBOX_DATA_SIZE];
+		u8 byte;
+		int start_offset = in_offset;
+		int end_offset;
+
+		/* Find the start of the byte */
+		while (buf[start_offset] && isspace(buf[start_offset]))
+			start_offset++;
+		if (!buf[start_offset])
+			break;
+
+		/* Find the start of the next byte, if any */
+		end_offset = start_offset;
+		while (buf[end_offset] && !isspace(buf[end_offset]))
+			end_offset++;
+		if (start_offset > count || end_offset > count)
+			break;
+		if (start_offset > EC_MAILBOX_DATA_SIZE ||
+		    end_offset > EC_MAILBOX_DATA_SIZE)
+			break;
+
+		/* Copy to a new nul-terminated string */
+		memcpy(word_buf, buf + start_offset, end_offset - start_offset);
+		word_buf[end_offset - start_offset] = '\0';
+
+		/* Convert from hex string */
+		ret = kstrtou8(word_buf, 16, &byte);
+		if (ret)
+			break;
+
+		/* Fill this byte into the request buffer */
+		raw_request_data[out_offset++] = byte;
+		if (out_offset >= EC_MAILBOX_DATA_SIZE)
+			break;
+
+		in_offset = end_offset;
+	}
+	if (out_offset == 0)
+		return -EINVAL;
+
+	/* Clear response data buffer */
+	memset(raw_response_data, 0, EC_MAILBOX_DATA_SIZE_EXTENDED);
+
+	msg.type = raw_request_data[0] << 8 | raw_request_data[1];
+	msg.flags = WILCO_EC_FLAG_RAW;
+	msg.command = raw_request_data[2];
+	msg.request_data = raw_request_data + 3;
+	msg.request_size = out_offset - 3;
+	msg.response_data = raw_response_data;
+	msg.response_size = EC_MAILBOX_DATA_SIZE;
+
+	/* Telemetry commands use extended response data */
+	if (msg.type == WILCO_EC_MSG_TELEMETRY) {
+		msg.flags |= WILCO_EC_FLAG_EXTENDED_DATA;
+		msg.response_size = EC_MAILBOX_DATA_SIZE_EXTENDED;
+	}
+
+	ret = wilco_ec_mailbox(ec, &msg);
+	if (ret < 0)
+		return ret;
+	raw_response_size = ret;
+	return count;
+}
+
+#endif /* CONFIG_WILCO_EC_SYSFS_RAW */
+
 struct ec_info {
 	u8 index;
 	const char *label;
@@ -102,10 +222,16 @@ static ssize_t stealth_mode_store(struct device *dev,
 
 static DEVICE_ATTR_RO(version);
 static DEVICE_ATTR_WO(stealth_mode);
+#ifdef CONFIG_WILCO_EC_SYSFS_RAW
+static DEVICE_ATTR_RW(raw);
+#endif
 
 static struct attribute *wilco_ec_attrs[] = {
 	&dev_attr_version.attr,
 	&dev_attr_stealth_mode.attr,
+#ifdef CONFIG_WILCO_EC_SYSFS_RAW
+	&dev_attr_raw.attr,
+#endif
 	NULL
 };
 ATTRIBUTE_GROUPS(wilco_ec);
-- 
2.20.0.405.gbc1bbc6f85-goog


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

* [RFC PATCH 05/10] CHROMIUM: wilco_ec: Add RTC class driver
  2018-12-15  0:18 [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Nick Crews
                   ` (3 preceding siblings ...)
  2018-12-15  0:18 ` [RFC PATCH 04/10] CHROMIUM: wilco_ec: Add support for raw commands in sysfs Nick Crews
@ 2018-12-15  0:18 ` Nick Crews
  2018-12-15  0:18 ` [RFC PATCH 06/10] CHROMIUM: wilco_ec: Add event handling Nick Crews
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Nick Crews @ 2018-12-15  0:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: Duncan Laurie, Nick Crews, Olof Johansson, Benson Leung

From: Duncan Laurie <dlaurie@google.com>

This Embedded Controller has an internal RTC that is exposed
as a standard RTC class driver with read/write functionality.

> hwclock --show --rtc /dev/rtc1
2007-12-31 16:01:20.460959-08:00
> hwclock --systohc --rtc /dev/rtc1
> hwclock --show --rtc /dev/rtc1
2018-11-29 17:08:00.780793-08:00

Signed-off-by: Duncan Laurie <dlaurie@google.com>
Signed-off-by: Nick Crews <ncrews@google.com>
---

 drivers/platform/chrome/Makefile           |   3 +-
 drivers/platform/chrome/wilco_ec.h         |  29 ++++
 drivers/platform/chrome/wilco_ec_mailbox.c |  15 ++
 drivers/platform/chrome/wilco_ec_rtc.c     | 163 +++++++++++++++++++++
 4 files changed, 209 insertions(+), 1 deletion(-)
 create mode 100644 drivers/platform/chrome/wilco_ec_rtc.c

diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index e8603bc5b095..5ca484c2d0d7 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -13,5 +13,6 @@ cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC)	+= cros_ec_lpc_mec.o
 obj-$(CONFIG_CROS_EC_LPC)		+= cros_ec_lpcs.o
 obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o
 
-wilco_ec-objs				:= wilco_ec_mailbox.o wilco_ec_sysfs.o
+wilco_ec-objs				:= wilco_ec_mailbox.o wilco_ec_rtc.o \
+					   wilco_ec_sysfs.o
 obj-$(CONFIG_WILCO_EC)			+= wilco_ec.o
diff --git a/drivers/platform/chrome/wilco_ec.h b/drivers/platform/chrome/wilco_ec.h
index 0b3dec4e2830..eee5c514e720 100644
--- a/drivers/platform/chrome/wilco_ec.h
+++ b/drivers/platform/chrome/wilco_ec.h
@@ -19,6 +19,7 @@
 
 #include <linux/device.h>
 #include <linux/kernel.h>
+#include <linux/rtc.h>
 
 /* Normal commands have a maximum 32 bytes of data */
 #define EC_MAILBOX_DATA_SIZE		32
@@ -55,6 +56,7 @@ enum wilco_ec_msg_type {
  * @data_buffer: Buffer used for EC communication.  The same buffer
  *               is used to hold the request and the response.
  * @data_size: Size of the data buffer used for EC communication.
+ * @rtc: RTC device handler.
  */
 struct wilco_ec_device {
 	struct device *dev;
@@ -64,6 +66,7 @@ struct wilco_ec_device {
 	struct resource *io_packet;
 	void *data_buffer;
 	size_t data_size;
+	struct rtc_device *rtc;
 };
 
 /**
@@ -114,4 +117,30 @@ int wilco_ec_sysfs_init(struct wilco_ec_device *ec);
  */
 void wilco_ec_sysfs_remove(struct wilco_ec_device *ec);
 
+/**
+ * wilco_ec_rtc_read() - Fill RTC time structure with values from the EC.
+ * @dev: EC device.
+ * @tm: Returns RTC time from EC.
+ *
+ * Return: 0 for success or negative error code on failure.
+ */
+int wilco_ec_rtc_read(struct device *dev, struct rtc_time *tm);
+
+/**
+ * wilco_ec_rtc_write() - Write EC time/date from RTC time structure.
+ * @dev: EC device.
+ * @tm: RTC time to write to EC.
+ *
+ * Return: 0 for success or negative error code on failure.
+ */
+int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm);
+
+/**
+ * wilco_ec_rtc_sync() - Write EC time/date from system time.
+ * @dev: EC device.
+ *
+ * Return: 0 for success or negative error code on failure.
+ */
+int wilco_ec_rtc_sync(struct device *dev);
+
 #endif /* WILCO_EC_H */
diff --git a/drivers/platform/chrome/wilco_ec_mailbox.c b/drivers/platform/chrome/wilco_ec_mailbox.c
index 1cb34b7280fd..2f093a281a30 100644
--- a/drivers/platform/chrome/wilco_ec_mailbox.c
+++ b/drivers/platform/chrome/wilco_ec_mailbox.c
@@ -33,6 +33,7 @@
 #include <linux/mfd/cros_ec_lpc_mec.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/rtc.h>
 #include "wilco_ec.h"
 
 /* Version of mailbox interface */
@@ -325,6 +326,11 @@ static struct resource *wilco_get_resource(struct platform_device *pdev,
 	return res;
 }
 
+static const struct rtc_class_ops wilco_ec_rtc_ops = {
+	.read_time = wilco_ec_rtc_read,
+	.set_time = wilco_ec_rtc_write,
+};
+
 static int wilco_ec_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -355,6 +361,15 @@ static int wilco_ec_probe(struct platform_device *pdev)
 	cros_ec_lpc_mec_init(ec->io_packet->start,
 			     ec->io_packet->start + EC_MAILBOX_DATA_SIZE);
 
+	/* Install RTC driver */
+	ec->rtc = devm_rtc_device_register(ec->dev, "wilco_ec",
+					   &wilco_ec_rtc_ops, THIS_MODULE);
+	if (IS_ERR(ec->rtc)) {
+		dev_err(dev, "Failed to install RTC driver\n");
+		cros_ec_lpc_mec_destroy();
+		return PTR_ERR(ec->rtc);
+	}
+
 	/* Create sysfs attributes for userspace interaction */
 	if (wilco_ec_sysfs_init(ec) < 0) {
 		dev_err(dev, "Failed to create sysfs attributes\n");
diff --git a/drivers/platform/chrome/wilco_ec_rtc.c b/drivers/platform/chrome/wilco_ec_rtc.c
new file mode 100644
index 000000000000..3e36403d0cb8
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec_rtc.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * wilco_ec_rtc - RTC interface for Wilco Embedded Controller
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bcd.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/timekeeping.h>
+#include "wilco_ec.h"
+
+#define EC_COMMAND_CMOS			0x7c
+#define  EC_CMOS_TOD_WRITE		0x02
+#define  EC_CMOS_TOD_READ		0x08
+
+/**
+ * struct ec_rtc_read - Format of RTC returned by EC.
+ * @second: Second value (0..59)
+ * @minute: Minute value (0..59)
+ * @hour: Hour value (0..23)
+ * @day: Day value (1..31)
+ * @month: Month value (1..12)
+ * @year: Year value (full year % 100)
+ * @century: Century value (full year / 100)
+ *
+ * All values are presented in binary (not BCD).
+ */
+struct ec_rtc_read {
+	u8 second;
+	u8 minute;
+	u8 hour;
+	u8 day;
+	u8 month;
+	u8 year;
+	u8 century;
+} __packed;
+
+/**
+ * struct ec_rtc_write - Format of RTC sent to the EC.
+ * @param: EC_CMOS_TOD_WRITE
+ * @century: Century value (full year / 100)
+ * @year: Year value (full year % 100)
+ * @month: Month value (1..12)
+ * @day: Day value (1..31)
+ * @hour: Hour value (0..23)
+ * @minute: Minute value (0..59)
+ * @second: Second value (0..59)
+ * @weekday: Day of the week (0=Saturday)
+ *
+ * All values are presented in BCD.
+ */
+struct ec_rtc_write {
+	u8 param;
+	u8 century;
+	u8 year;
+	u8 month;
+	u8 day;
+	u8 hour;
+	u8 minute;
+	u8 second;
+	u8 weekday;
+} __packed;
+
+int wilco_ec_rtc_read(struct device *dev, struct rtc_time *tm)
+{
+	struct wilco_ec_device *ec = dev_get_drvdata(dev);
+	u8 param = EC_CMOS_TOD_READ;
+	struct ec_rtc_read rtc;
+	struct wilco_ec_message msg = {
+		.type = WILCO_EC_MSG_LEGACY,
+		.flags = WILCO_EC_FLAG_RAW_RESPONSE,
+		.command = EC_COMMAND_CMOS,
+		.request_data = &param,
+		.request_size = sizeof(param),
+		.response_data = &rtc,
+		.response_size = sizeof(rtc),
+	};
+	struct rtc_time calc_tm;
+	unsigned long time;
+	int ret;
+
+	ret = wilco_ec_mailbox(ec, &msg);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read EC RTC\n");
+		return ret;
+	}
+
+	tm->tm_sec	= rtc.second;
+	tm->tm_min	= rtc.minute;
+	tm->tm_hour	= rtc.hour;
+	tm->tm_mday	= rtc.day;
+	/*
+	 * The RTC stores the month value as 1-12 but the kernel expects 0-11,
+	 * so ensure invalid/zero month value from RTC is not converted to -1.
+	 */
+	tm->tm_mon	= rtc.month ? rtc.month - 1 : 0;
+	tm->tm_year	= rtc.year + (rtc.century * 100) - 1900;
+	tm->tm_yday	= rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+
+	/* Compute day of week */
+	rtc_tm_to_time(tm, &time);
+	rtc_time_to_tm(time, &calc_tm);
+	tm->tm_wday = calc_tm.tm_wday;
+
+	return 0;
+}
+
+int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm)
+{
+	struct wilco_ec_device *ec = dev_get_drvdata(dev);
+	struct ec_rtc_write rtc;
+	struct wilco_ec_message msg = {
+		.type = WILCO_EC_MSG_LEGACY,
+		.flags = WILCO_EC_FLAG_RAW_RESPONSE,
+		.command = EC_COMMAND_CMOS,
+		.request_data = &rtc,
+		.request_size = sizeof(rtc),
+	};
+	int year = tm->tm_year + 1900;
+	/* Convert from 0=Sunday to 0=Saturday for the EC */
+	int wday = tm->tm_wday == 6 ? 0 : tm->tm_wday + 1;
+	int ret;
+
+	rtc.param	= EC_CMOS_TOD_WRITE;
+	rtc.century	= bin2bcd(year / 100);
+	rtc.year	= bin2bcd(year % 100);
+	rtc.month	= bin2bcd(tm->tm_mon + 1);
+	rtc.day		= bin2bcd(tm->tm_mday);
+	rtc.hour	= bin2bcd(tm->tm_hour);
+	rtc.minute	= bin2bcd(tm->tm_min);
+	rtc.second	= bin2bcd(tm->tm_sec);
+	rtc.weekday	= bin2bcd(wday);
+
+	ret = wilco_ec_mailbox(ec, &msg);
+	if (ret < 0) {
+		dev_err(dev, "Failed to write EC RTC\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+int wilco_ec_rtc_sync(struct device *dev)
+{
+	struct rtc_time tm;
+
+	rtc_time64_to_tm(ktime_get_real_seconds(), &tm);
+
+	return wilco_ec_rtc_write(dev, &tm);
+}
-- 
2.20.0.405.gbc1bbc6f85-goog


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

* [RFC PATCH 06/10] CHROMIUM: wilco_ec: Add event handling
  2018-12-15  0:18 [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Nick Crews
                   ` (4 preceding siblings ...)
  2018-12-15  0:18 ` [RFC PATCH 05/10] CHROMIUM: wilco_ec: Add RTC class driver Nick Crews
@ 2018-12-15  0:18 ` Nick Crews
  2018-12-15  0:18 ` [RFC PATCH 07/10] CHROMIUM: wilco_ec: Move legacy attributes to separate file Nick Crews
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Nick Crews @ 2018-12-15  0:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: Duncan Laurie, Nick Crews, Olof Johansson, Benson Leung

From: Duncan Laurie <dlaurie@google.com>

The Wilco Embedded Controller can return extended events that
are not handled by standard ACPI objects.  These events can
include hotkeys which map to standard functions like brightness
controls, or information about EC controlled features like the
charger or battery.

These events are triggered with an ACPI Notify(0x90) and the
event data buffer is read through an ACPI method provided by
the BIOS which reads the event buffer from EC RAM.

These events are then processed, with hotkey events being sent
to the input subsystem and other events put into a queue which
can be read by a userspace daemon via a sysfs attribute.

> evtest /dev/input/event6
Input driver version is 1.0.1
Input device ID: bus 0x19 vendor 0x0 product 0x0 version 0x0
Input device name: "Wilco EC hotkeys"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 224 (KEY_BRIGHTNESSDOWN)
    Event code 225 (KEY_BRIGHTNESSUP)
    Event code 240 (KEY_UNKNOWN)
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
Properties:
Testing ... (interrupt to exit)
Event: type 4 (EV_MSC), code 4 (MSC_SCAN), value 57
Event: type 1 (EV_KEY), code 224 (KEY_BRIGHTNESSDOWN), value 1
Event: -------------- SYN_REPORT ------------
Event: type 1 (EV_KEY), code 224 (KEY_BRIGHTNESSDOWN), value 0
Event: -------------- SYN_REPORT ------------
Event: type 4 (EV_MSC), code 4 (MSC_SCAN), value 58
Event: type 1 (EV_KEY), code 225 (KEY_BRIGHTNESSUP), value 1
Event: -------------- SYN_REPORT ------------
Event: type 1 (EV_KEY), code 225 (KEY_BRIGHTNESSUP), value 0
Event: -------------- SYN_REPORT ------------

Signed-off-by: Duncan Laurie <dlaurie@google.com>
Signed-off-by: Nick Crews <ncrews@google.com>
---

 drivers/platform/chrome/Makefile           |   4 +-
 drivers/platform/chrome/wilco_ec.h         |  34 ++
 drivers/platform/chrome/wilco_ec_event.c   | 343 +++++++++++++++++++++
 drivers/platform/chrome/wilco_ec_mailbox.c |  11 +
 4 files changed, 390 insertions(+), 2 deletions(-)
 create mode 100644 drivers/platform/chrome/wilco_ec_event.c

diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index 5ca484c2d0d7..60b19190dba1 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -13,6 +13,6 @@ cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC)	+= cros_ec_lpc_mec.o
 obj-$(CONFIG_CROS_EC_LPC)		+= cros_ec_lpcs.o
 obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o
 
-wilco_ec-objs				:= wilco_ec_mailbox.o wilco_ec_rtc.o \
-					   wilco_ec_sysfs.o
+wilco_ec-objs				:= wilco_ec_mailbox.o wilco_ec_event.o \
+					   wilco_ec_rtc.o wilco_ec_sysfs.o
 obj-$(CONFIG_WILCO_EC)			+= wilco_ec.o
diff --git a/drivers/platform/chrome/wilco_ec.h b/drivers/platform/chrome/wilco_ec.h
index eee5c514e720..6f084188faa1 100644
--- a/drivers/platform/chrome/wilco_ec.h
+++ b/drivers/platform/chrome/wilco_ec.h
@@ -18,7 +18,9 @@
 #define WILCO_EC_H
 
 #include <linux/device.h>
+#include <linux/input.h>
 #include <linux/kernel.h>
+#include <linux/list.h>
 #include <linux/rtc.h>
 
 /* Normal commands have a maximum 32 bytes of data */
@@ -46,6 +48,22 @@ enum wilco_ec_msg_type {
 	WILCO_EC_MSG_TELEMETRY = 0x00f5,
 };
 
+/**
+ * struct wilco_ec_event - EC extended events.
+ * @lock: Mutex to guard the list of events.
+ * @list: Queue of EC events to be provided to userspace.
+ * @attr: Sysfs attribute for userspace to read events.
+ * @count: Count of events in the queue.
+ * @input: Input device for hotkey events.
+ */
+struct wilco_ec_event {
+	struct mutex lock;
+	struct list_head list;
+	struct bin_attribute attr;
+	size_t count;
+	struct input_dev *input;
+};
+
 /**
  * struct wilco_ec_device - Wilco Embedded Controller handle.
  * @dev: Device handle.
@@ -57,6 +75,7 @@ enum wilco_ec_msg_type {
  *               is used to hold the request and the response.
  * @data_size: Size of the data buffer used for EC communication.
  * @rtc: RTC device handler.
+ * @event: EC extended event handler.
  */
 struct wilco_ec_device {
 	struct device *dev;
@@ -67,6 +86,7 @@ struct wilco_ec_device {
 	void *data_buffer;
 	size_t data_size;
 	struct rtc_device *rtc;
+	struct wilco_ec_event event;
 };
 
 /**
@@ -143,4 +163,18 @@ int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm);
  */
 int wilco_ec_rtc_sync(struct device *dev);
 
+/**
+ * wilco_ec_event_init() - Prepare to handle EC events.
+ * @ec: EC device.
+ *
+ * Return: 0 for success or negative error code on failure.
+ */
+int wilco_ec_event_init(struct wilco_ec_device *ec);
+
+/**
+ * wilco_ec_event_remove() - Remove EC event handler.
+ * @ec: EC device.
+ */
+void wilco_ec_event_remove(struct wilco_ec_device *ec);
+
 #endif /* WILCO_EC_H */
diff --git a/drivers/platform/chrome/wilco_ec_event.c b/drivers/platform/chrome/wilco_ec_event.c
new file mode 100644
index 000000000000..248f2ec095ab
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec_event.c
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * wilco_ec_event - Event handling for Wilco Embedded Controller
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include "wilco_ec.h"
+
+/* ACPI Notify event code indicating event data is available */
+#define EC_ACPI_NOTIFY_EVENT		0x90
+
+/* ACPI Method to execute to retrieve event data buffer from the EC */
+#define EC_ACPI_GET_EVENT		"^QSET"
+
+/* Maximum number of words in event data returned by the EC */
+#define EC_ACPI_MAX_EVENT_DATA		6
+
+/* Keep at most 100 events in the queue */
+#define EC_EVENT_QUEUE_MAX		100
+
+/**
+ * enum ec_event_type - EC event categories.
+ * @EC_EVENT_TYPE_HOTKEY: Hotkey event for handling special keys.
+ * @EC_EVENT_TYPE_NOTIFY: EC feature state changes.
+ * @EC_EVENT_TYPE_SYSTEM: EC system messages.
+ */
+enum ec_event_type {
+	EC_EVENT_TYPE_HOTKEY = 0x10,
+	EC_EVENT_TYPE_NOTIFY = 0x11,
+	EC_EVENT_TYPE_SYSTEM = 0x12,
+};
+
+/**
+ * struct ec_event - Extended event returned by the EC.
+ * @size: Number of words in structure after the size word.
+ * @type: Extended event type from &enum ec_event_type.
+ * @event: Event data words.  Max count is %EC_ACPI_MAX_EVENT_DATA.
+ */
+struct ec_event {
+	u16 size;
+	u16 type;
+	u16 event[0];
+} __packed;
+
+/**
+ * struct ec_event_entry - Event queue entry.
+ * @list: List node.
+ * @size: Number of bytes in event structure.
+ * @event: Extended event returned by the EC.  This should be the last
+ *         element because &struct ec_event includes a zero length array.
+ */
+struct ec_event_entry {
+	struct list_head list;
+	size_t size;
+	struct ec_event event;
+};
+
+static const struct key_entry wilco_ec_keymap[] = {
+	{ KE_KEY, 0x0057, { KEY_BRIGHTNESSDOWN } },
+	{ KE_KEY, 0x0058, { KEY_BRIGHTNESSUP } },
+	{ KE_END, 0 }
+};
+
+/**
+ * wilco_ec_handle_events() - Handle Embedded Controller events.
+ * @ec: EC device.
+ * @buf: Buffer of event data.
+ * @length: Length of event data buffer.
+ *
+ * Return: Number of events in queue or negative error code on failure.
+ *
+ * This function will handle EC extended events, sending hotkey events
+ * to the input subsystem and queueing others to be read by userspace.
+ */
+static int wilco_ec_handle_events(struct wilco_ec_device *ec,
+				  u8 *buf, u32 length)
+{
+	struct wilco_ec_event *queue = &ec->event;
+	struct ec_event *event;
+	struct ec_event_entry *entry;
+	size_t entry_size, num_words;
+	u32 offset = 0;
+
+	while (offset < length) {
+		event = (struct ec_event *)(buf + offset);
+		if (!event)
+			return -EINVAL;
+
+		dev_dbg(ec->dev, "EC event type 0x%02x size %d\n", event->type,
+			event->size);
+
+		/* Number of 16bit event data words is size - 1 */
+		num_words = event->size - 1;
+		entry_size = sizeof(*event) + (num_words * sizeof(u16));
+
+		if (num_words > EC_ACPI_MAX_EVENT_DATA) {
+			dev_err(ec->dev, "Invalid event word count: %d > %d\n",
+				num_words, EC_ACPI_MAX_EVENT_DATA);
+			return -EOVERFLOW;
+		};
+
+		/* Ensure event does not overflow the available buffer */
+		if ((offset + entry_size) > length) {
+			dev_err(ec->dev, "Event exceeds buffer: %d > %d\n",
+				offset + entry_size, length);
+			return -EOVERFLOW;
+		}
+
+		/* Point to the next event in the buffer */
+		offset += entry_size;
+
+		/* Hotkeys are sent to the input subsystem */
+		if (event->type == EC_EVENT_TYPE_HOTKEY) {
+			if (sparse_keymap_report_event(queue->input,
+						       event->event[0],
+						       1, true))
+				continue;
+
+			/* Unknown hotkeys are put into the event queue */
+			dev_dbg(ec->dev, "Unknown hotkey 0x%04x\n",
+				event->event[0]);
+		}
+
+		/* Prepare event for the queue */
+		entry = kzalloc(entry_size, GFP_KERNEL);
+		if (!entry)
+			return -ENOMEM;
+
+		/* Copy event data */
+		entry->size = entry_size;
+		memcpy(&entry->event, event, entry->size);
+
+		/* Add this event to the queue */
+		mutex_lock(&queue->lock);
+		list_add_tail(&entry->list, &queue->list);
+		queue->count++;
+		mutex_unlock(&queue->lock);
+	}
+
+	return queue->count;
+}
+
+/**
+ * wilco_ec_acpi_notify() - Handler called by ACPI subsystem for Notify.
+ * @device: EC ACPI device.
+ * @value: Value passed to Notify() in ACPI.
+ * @data: Private data, pointer to EC device.
+ */
+static void wilco_ec_acpi_notify(acpi_handle device, u32 value, void *data)
+{
+	struct wilco_ec_device *ec = data;
+	struct acpi_buffer event_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	union acpi_object *obj;
+	acpi_status status;
+	int count;
+
+	/* Currently only handle event notifications */
+	if (value != EC_ACPI_NOTIFY_EVENT) {
+		dev_err(ec->dev, "Invalid event: 0x%08x\n", value);
+		return;
+	}
+
+	/* Execute ACPI method to get event data buffer */
+	status = acpi_evaluate_object(device, EC_ACPI_GET_EVENT,
+				      NULL, &event_buffer);
+	if (ACPI_FAILURE(status)) {
+		dev_err(ec->dev, "Error executing ACPI method %s()\n",
+			 EC_ACPI_GET_EVENT);
+		return;
+	}
+
+	obj = (union acpi_object *)event_buffer.pointer;
+	if (!obj) {
+		dev_err(ec->dev, "Nothing returned from %s()\n",
+			EC_ACPI_GET_EVENT);
+		return;
+	}
+	if (obj->type != ACPI_TYPE_BUFFER) {
+		dev_err(ec->dev, "Invalid object returned from %s()\n",
+			EC_ACPI_GET_EVENT);
+		kfree(obj);
+		return;
+	}
+	if (obj->buffer.length < sizeof(struct ec_event)) {
+		dev_err(ec->dev, "Invalid buffer length %d from %s()\n",
+			obj->buffer.length, EC_ACPI_GET_EVENT);
+		kfree(obj);
+		return;
+	}
+
+	/* Handle events and notify sysfs if any queued for userspace */
+	count = wilco_ec_handle_events(ec, obj->buffer.pointer,
+				       obj->buffer.length);
+
+	if (count > 0) {
+		dev_dbg(ec->dev, "EC event queue has %d entries\n", count);
+		sysfs_notify(&ec->dev->kobj, NULL, "event");
+	}
+
+	kfree(obj);
+}
+
+static ssize_t wilco_ec_event_read(struct file *filp, struct kobject *kobj,
+				   struct bin_attribute *attr,
+				   char *buf, loff_t off, size_t count)
+{
+	struct wilco_ec_device *ec = attr->private;
+	struct ec_event_entry *entry;
+
+	/* Only supports reading full events */
+	if (off != 0)
+		return -EINVAL;
+
+	/* Remove the first event and provide it to userspace */
+	mutex_lock(&ec->event.lock);
+	entry = list_first_entry_or_null(&ec->event.list,
+					 struct ec_event_entry, list);
+	if (entry) {
+		if (entry->size < count)
+			count = entry->size;
+		memcpy(buf, &entry->event, count);
+		list_del(&entry->list);
+		kfree(entry);
+		ec->event.count--;
+	} else {
+		count = 0;
+	}
+	mutex_unlock(&ec->event.lock);
+
+	return count;
+}
+
+static void wilco_ec_event_clear(struct wilco_ec_device *ec)
+{
+	struct ec_event_entry *entry, *next;
+
+	mutex_lock(&ec->event.lock);
+
+	/* Clear the event queue */
+	list_for_each_entry_safe(entry, next, &ec->event.list, list) {
+		list_del(&entry->list);
+		kfree(entry);
+		ec->event.count--;
+	}
+
+	mutex_unlock(&ec->event.lock);
+}
+
+int wilco_ec_event_init(struct wilco_ec_device *ec)
+{
+	struct wilco_ec_event *event = &ec->event;
+	struct device *dev = ec->dev;
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+	acpi_status status;
+	int ret;
+
+	if (!adev) {
+		dev_err(dev, "Unable to find Wilco ACPI Device\n");
+		return -ENODEV;
+	}
+
+	INIT_LIST_HEAD(&event->list);
+	mutex_init(&event->lock);
+
+	/* Allocate input device for hotkeys */
+	event->input = input_allocate_device();
+	if (!event->input)
+		return -ENOMEM;
+	event->input->name = "Wilco EC hotkeys";
+	event->input->phys = "ec/input0";
+	event->input->id.bustype = BUS_HOST;
+	ret = sparse_keymap_setup(event->input, wilco_ec_keymap, NULL);
+	if (ret) {
+		dev_err(dev, "Unable to setup input device keymap\n");
+		input_free_device(event->input);
+		return ret;
+	}
+	ret = input_register_device(event->input);
+	if (ret) {
+		dev_err(dev, "Unable to register input device\n");
+		input_free_device(event->input);
+		return ret;
+	}
+
+	/* Create sysfs attribute for userspace event handling */
+	sysfs_bin_attr_init(&event->attr);
+	event->attr.attr.name = "event";
+	event->attr.attr.mode = 0400;
+	event->attr.read = wilco_ec_event_read;
+	event->attr.private = ec;
+	ret = device_create_bin_file(dev, &event->attr);
+	if (ret) {
+		dev_err(dev, "Failed to create event attribute in sysfs\n");
+		input_unregister_device(event->input);
+		return ret;
+	}
+
+	/* Install ACPI handler for Notify events */
+	status = acpi_install_notify_handler(adev->handle, ACPI_ALL_NOTIFY,
+					     wilco_ec_acpi_notify, ec);
+
+	if (ACPI_FAILURE(status)) {
+		dev_err(dev, "Failed to register notifier %08x\n", status);
+		device_remove_bin_file(dev, &event->attr);
+		input_unregister_device(event->input);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+void wilco_ec_event_remove(struct wilco_ec_device *ec)
+{
+	struct acpi_device *adev = ACPI_COMPANION(ec->dev);
+
+	/* Stop new events */
+	if (adev)
+		acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY,
+					   wilco_ec_acpi_notify);
+
+	/* Remove event interfaces */
+	device_remove_bin_file(ec->dev, &ec->event.attr);
+	input_unregister_device(ec->event.input);
+
+	/* Clear the event queue */
+	wilco_ec_event_clear(ec);
+}
diff --git a/drivers/platform/chrome/wilco_ec_mailbox.c b/drivers/platform/chrome/wilco_ec_mailbox.c
index 2f093a281a30..25c2c539c813 100644
--- a/drivers/platform/chrome/wilco_ec_mailbox.c
+++ b/drivers/platform/chrome/wilco_ec_mailbox.c
@@ -377,6 +377,14 @@ static int wilco_ec_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
+	/* Prepare to handle events */
+	if (wilco_ec_event_init(ec) < 0) {
+		dev_err(dev, "Failed to setup event handling\n");
+		wilco_ec_sysfs_remove(ec);
+		cros_ec_lpc_mec_destroy();
+		return -ENODEV;
+	}
+
 	return 0;
 }
 
@@ -384,6 +392,9 @@ static int wilco_ec_remove(struct platform_device *pdev)
 {
 	struct wilco_ec_device *ec = platform_get_drvdata(pdev);
 
+	/* Stop handling EC events */
+	wilco_ec_event_remove(ec);
+
 	/* Remove sysfs attributes */
 	wilco_ec_sysfs_remove(ec);
 
-- 
2.20.0.405.gbc1bbc6f85-goog


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

* [RFC PATCH 07/10] CHROMIUM: wilco_ec: Move legacy attributes to separate file
  2018-12-15  0:18 [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Nick Crews
                   ` (5 preceding siblings ...)
  2018-12-15  0:18 ` [RFC PATCH 06/10] CHROMIUM: wilco_ec: Add event handling Nick Crews
@ 2018-12-15  0:18 ` Nick Crews
  2018-12-15  0:18 ` [RFC PATCH 08/10] CHROMIUM: wilco_ec: Add EC properties Nick Crews
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Nick Crews @ 2018-12-15  0:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: Nick Crews, Olof Johansson, Benson Leung

Legacy attributes are EC properties that are non-chromebook specific,
ones which existed before the EC was modified for Chromebooks (as I
understand it at least). This adds no new behavior, but just refactors
the existing legacy attributes so adding more attributes in the future
will work in an elegant way.

wilco_sysfs.c should just contain the information you need to understand
how to use the sysfs interface. It will only specify the names of the
attributes and their location within the directory structure.
The API and the implementation for all the attributes will be located
in individual external files, so if you want to see how to use an
attribute, you can look that that specific file. This should help
with the problem of this sysfs file becoming way too big to understand.

With this new structure, it should be easy to add new attributes by
making new implementation files, #include-ing them in wilco_ec_sysfs.c,
and then defining where to actually place the attributes in
wilco_ec_sysfs.c

Signed-off-by: Nick Crews <ncrews@google.com>
---

 drivers/platform/chrome/Makefile          |   3 +-
 drivers/platform/chrome/wilco_ec_legacy.c | 204 ++++++++++++++++++
 drivers/platform/chrome/wilco_ec_legacy.h |  96 +++++++++
 drivers/platform/chrome/wilco_ec_sysfs.c  | 248 ++++------------------
 4 files changed, 343 insertions(+), 208 deletions(-)
 create mode 100644 drivers/platform/chrome/wilco_ec_legacy.c
 create mode 100644 drivers/platform/chrome/wilco_ec_legacy.h

diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index 60b19190dba1..56c39de8e5f5 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -14,5 +14,6 @@ obj-$(CONFIG_CROS_EC_LPC)		+= cros_ec_lpcs.o
 obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o
 
 wilco_ec-objs				:= wilco_ec_mailbox.o wilco_ec_event.o \
-					   wilco_ec_rtc.o wilco_ec_sysfs.o
+					   wilco_ec_rtc.o wilco_ec_legacy.o \
+					   wilco_ec_sysfs.o
 obj-$(CONFIG_WILCO_EC)			+= wilco_ec.o
diff --git a/drivers/platform/chrome/wilco_ec_legacy.c b/drivers/platform/chrome/wilco_ec_legacy.c
new file mode 100644
index 000000000000..6110117af024
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec_legacy.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * wilco_ec_legacy - Legacy sysfs attributes for Wilco EC
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/ctype.h>
+#include <linux/device.h>
+
+#include "wilco_ec.h"
+#include "wilco_ec_legacy.h"
+
+#ifdef CONFIG_WILCO_EC_SYSFS_RAW
+
+/* Raw data buffer, large enough to hold extended responses */
+static size_t raw_response_size;
+static u8 raw_response_data[EC_MAILBOX_DATA_SIZE_EXTENDED];
+
+ssize_t wilco_ec_raw_store(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct wilco_ec_device *ec = dev_get_drvdata(dev);
+	struct wilco_ec_message msg;
+	u8 raw_request_data[EC_MAILBOX_DATA_SIZE];
+	int in_offset = 0;
+	int out_offset = 0;
+	int ret;
+
+	while (in_offset < count) {
+		char word_buf[EC_MAILBOX_DATA_SIZE];
+		u8 byte;
+		int start_offset = in_offset;
+		int end_offset;
+
+		/* Find the start of the byte */
+		while (buf[start_offset] && isspace(buf[start_offset]))
+			start_offset++;
+		if (!buf[start_offset])
+			break;
+
+		/* Find the start of the next byte, if any */
+		end_offset = start_offset;
+		while (buf[end_offset] && !isspace(buf[end_offset]))
+			end_offset++;
+		if (start_offset > count || end_offset > count)
+			break;
+		if (start_offset > EC_MAILBOX_DATA_SIZE ||
+		    end_offset > EC_MAILBOX_DATA_SIZE)
+			break;
+
+		/* Copy to a new NULL terminated string */
+		memcpy(word_buf, buf + start_offset, end_offset - start_offset);
+		word_buf[end_offset - start_offset] = '\0';
+
+		/* Convert from hex string */
+		ret = kstrtou8(word_buf, 16, &byte);
+		if (ret)
+			break;
+
+		/* Fill this byte into the request buffer */
+		raw_request_data[out_offset++] = byte;
+		if (out_offset >= EC_MAILBOX_DATA_SIZE)
+			break;
+
+		in_offset = end_offset;
+	}
+	if (out_offset == 0)
+		return -EINVAL;
+
+	/* Clear response data buffer */
+	memset(raw_response_data, '\0', EC_MAILBOX_DATA_SIZE_EXTENDED);
+
+	msg.type = raw_request_data[0] << 8 | raw_request_data[1];
+	msg.flags = WILCO_EC_FLAG_RAW;
+	msg.command = raw_request_data[2];
+	msg.request_data = raw_request_data + 3;
+	msg.request_size = out_offset - 3;
+	msg.response_data = raw_response_data;
+	msg.response_size = EC_MAILBOX_DATA_SIZE;
+
+	/* Telemetry commands use extended response data */
+	if (msg.type == WILCO_EC_MSG_TELEMETRY) {
+		msg.flags |= WILCO_EC_FLAG_EXTENDED_DATA;
+		msg.response_size = EC_MAILBOX_DATA_SIZE_EXTENDED;
+	}
+
+	ret = wilco_ec_mailbox(ec, &msg);
+	if (ret < 0)
+		return ret;
+	raw_response_size = ret;
+	return count;
+}
+
+ssize_t wilco_ec_raw_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	ssize_t count = 0;
+
+	if (raw_response_size) {
+		int i;
+
+		for (i = 0; i < raw_response_size; ++i)
+			count += scnprintf(buf + count, PAGE_SIZE - count,
+					   "%02x ", raw_response_data[i]);
+
+		count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+
+		/* Only return response the first time it is read */
+		raw_response_size = 0;
+	}
+
+	return count;
+}
+
+#endif /* CONFIG_WILCO_EC_SYSFS_RAW */
+
+struct ec_info {
+	u8 index;
+	const char *label;
+};
+
+static ssize_t wilco_ec_show_info(struct wilco_ec_device *ec, char *buf,
+				  ssize_t count, struct ec_info *info)
+{
+	char result[EC_INFO_SIZE];
+	struct wilco_ec_message msg = {
+		.type = WILCO_EC_MSG_LEGACY,
+		.command = EC_COMMAND_EC_INFO,
+		.request_data = &info->index,
+		.request_size = sizeof(info->index),
+		.response_data = result,
+		.response_size = EC_INFO_SIZE,
+	};
+	int ret;
+
+	ret = wilco_ec_mailbox(ec, &msg);
+	if (ret != EC_INFO_SIZE)
+		return scnprintf(buf + count, PAGE_SIZE - count,
+				 "%-12s : ERROR %d\n", info->label, ret);
+
+	return scnprintf(buf + count, PAGE_SIZE - count,
+			 "%-12s : %s\n", info->label, result);
+}
+
+ssize_t wilco_ec_version_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct wilco_ec_device *ec = dev_get_drvdata(dev);
+	struct ec_info wilco_ec_info[] = {
+		{ 0, "Label" },
+		{ 1, "SVN Revision" },
+		{ 2, "Model Number" },
+		{ 3, "Build Date" },
+		{ 0xff, NULL },
+	};
+	struct ec_info *info = wilco_ec_info;
+	ssize_t c = 0;
+
+	for (info = wilco_ec_info; info->label; info++)
+		c += wilco_ec_show_info(ec, buf, c, info);
+
+	return c;
+}
+
+ssize_t wilco_ec_stealth_mode_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct wilco_ec_device *ec = dev_get_drvdata(dev);
+	u8 param;
+	struct wilco_ec_message msg = {
+		.type = WILCO_EC_MSG_LEGACY,
+		.command = EC_COMMAND_STEALTH_MODE,
+		.request_data = &param,
+		.request_size = sizeof(param),
+	};
+	int ret;
+	bool enable;
+
+	ret = kstrtobool(buf, &enable);
+	if (ret) {
+		dev_err(dev, "Unable to parse '%s' to bool", buf);
+		return ret;
+	}
+
+	/* Invert input parameter, EC expects 0=on and 1=off */
+	param = enable ? 0 : 1;
+
+	ret = wilco_ec_mailbox(ec, &msg);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
diff --git a/drivers/platform/chrome/wilco_ec_legacy.h b/drivers/platform/chrome/wilco_ec_legacy.h
new file mode 100644
index 000000000000..dc9a838d6138
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec_legacy.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * wilco_ec_legacy - Legacy (non-Chrome-specific) sysfs attributes for Wilco EC
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WILCO_EC_LEGACY_H
+#define WILCO_EC_LEGACY_H
+
+#include <linux/device.h>
+
+#define EC_COMMAND_EC_INFO		0x38
+#define EC_INFO_SIZE			 9
+#define EC_COMMAND_STEALTH_MODE		0xfc
+
+#ifdef CONFIG_WILCO_EC_SYSFS_RAW
+
+/**
+ * raw_store() - Write a raw command to EC, store the result to view later
+ * @dev: Device representing the EC
+ * @attr: The attribute in question
+ * @buf: Input buffer, format explained below
+ * @count: Number of bytes in input buffer
+ *
+ * Bytes 0-1 indicate the message type:
+ *  00 F0 = Execute Legacy Command
+ *  00 F2 = Read/Write NVRAM Property
+ * Byte 2 provides the command code
+ * Bytes 3+ consist of the data passed in the request
+ *
+ * example: read the EC info type 1:
+ *  # echo 00 f0 38 00 01 00 > raw
+ *
+ * After calling this function, read the result by using raw_show()
+ *
+ * Return: Number of bytes consumed from input, negative error code on failure
+ */
+ssize_t wilco_ec_raw_store(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count);
+
+/**
+ * raw_show() - Show result from previous call to raw_store()
+ * @dev: Device representing the EC
+ * @attr: The attribute in question
+ * @buf: Output buffer to be filled
+ *
+ * Example usage:
+ *	// Call raw_store(), read EC info type 1
+ *	# echo 00 f0 38 00 01 00 > raw
+ *	// Call this function, view the result
+ *	# cat raw
+ *	00 38 31 34 34 66 00 00 00 00 00 00 00 00 00 00 00...
+ *
+ * Return: Number of bytes written to output, negative error code on failure
+ */
+ssize_t wilco_ec_raw_show(struct device *dev, struct device_attribute *attr,
+			  char *buf);
+
+#endif /* CONFIG_WILCO_EC_SYSFS_RAW */
+
+/**
+ * version_show() - Display Wilco Embedded Controller version info
+ *
+ * Output will be similar to the example below:
+ * Label        : 95.00.06
+ * SVN Revision : 5960a.06
+ * Model Number : 08;8
+ * Build Date   : 11/29/18
+ */
+ssize_t wilco_ec_version_show(struct device *dev, struct device_attribute *attr,
+			      char *buf);
+/**
+ * stealth_mode_store() - Turn stealth_mode on or off on EC
+ * @dev: Device representing the EC
+ * @attr: The attribute in question
+ * @buf: Input buffer, should be parseable by kstrtobool(). Anything parsed to
+ *	 True means enable stealth mode (turn off screen, etc)
+ * @count: Number of bytes in input buffer
+ *
+ * Return: Number of bytes consumed from input, negative error code on failure
+ */
+ssize_t wilco_ec_stealth_mode_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count);
+
+#endif /* WILCO_EC_LEGACY_H */
diff --git a/drivers/platform/chrome/wilco_ec_sysfs.c b/drivers/platform/chrome/wilco_ec_sysfs.c
index eeebd4ba4a39..d11e577f7ff0 100644
--- a/drivers/platform/chrome/wilco_ec_sysfs.c
+++ b/drivers/platform/chrome/wilco_ec_sysfs.c
@@ -18,230 +18,64 @@
 #include <linux/platform_device.h>
 #include <linux/sysfs.h>
 #include "wilco_ec.h"
+#include "wilco_ec_legacy.h"
 
-#define EC_COMMAND_EC_INFO		0x38
-#define EC_INFO_SIZE			 9
-#define EC_COMMAND_STEALTH_MODE		0xfc
+#define WILCO_EC_ATTR_RO(_name)						\
+__ATTR(_name, 0444, wilco_ec_##_name##_show, NULL)
 
-#ifdef CONFIG_WILCO_EC_SYSFS_RAW
-
-/* Raw data buffer, large enough to hold extended responses */
-static size_t raw_response_size;
-static u8 raw_response_data[EC_MAILBOX_DATA_SIZE_EXTENDED];
-
-/*
- * raw: write a raw command and return the result
- *
- * Bytes 0-1 indicate the message type:
- *  00 F0 = Execute Legacy Command
- *  00 F2 = Read/Write NVRAM Property
- * Byte 2 provides the command code
- * Bytes 3+ consist of the data passed in the request
- *
- * example: read the EC info type 1:
- *  # echo 00 f0 38 00 01 00 > raw
- *  # cat raw
- *  00 38 31 34 34 66 00 00 00 00 00 00 00 00 00 00 00...
- */
-
-static ssize_t raw_show(struct device *dev,
-			struct device_attribute *attr, char *buf)
-{
-	ssize_t count = 0;
-
-	if (raw_response_size) {
-		int i;
-
-		for (i = 0; i < raw_response_size; ++i)
-			count += scnprintf(buf + count, PAGE_SIZE - count,
-					   "%02x ", raw_response_data[i]);
-
-		count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
-
-		/* Only return response the first time it is read */
-		raw_response_size = 0;
-	}
-
-	return count;
-}
-
-static ssize_t raw_store(struct device *dev,
-			 struct device_attribute *attr,
-			 const char *buf, size_t count)
-{
-	struct wilco_ec_device *ec = dev_get_drvdata(dev);
-	struct wilco_ec_message msg;
-	u8 raw_request_data[EC_MAILBOX_DATA_SIZE];
-	int in_offset = 0;
-	int out_offset = 0;
-	int ret;
-
-	while (in_offset < count) {
-		char word_buf[EC_MAILBOX_DATA_SIZE];
-		u8 byte;
-		int start_offset = in_offset;
-		int end_offset;
-
-		/* Find the start of the byte */
-		while (buf[start_offset] && isspace(buf[start_offset]))
-			start_offset++;
-		if (!buf[start_offset])
-			break;
-
-		/* Find the start of the next byte, if any */
-		end_offset = start_offset;
-		while (buf[end_offset] && !isspace(buf[end_offset]))
-			end_offset++;
-		if (start_offset > count || end_offset > count)
-			break;
-		if (start_offset > EC_MAILBOX_DATA_SIZE ||
-		    end_offset > EC_MAILBOX_DATA_SIZE)
-			break;
-
-		/* Copy to a new nul-terminated string */
-		memcpy(word_buf, buf + start_offset, end_offset - start_offset);
-		word_buf[end_offset - start_offset] = '\0';
-
-		/* Convert from hex string */
-		ret = kstrtou8(word_buf, 16, &byte);
-		if (ret)
-			break;
-
-		/* Fill this byte into the request buffer */
-		raw_request_data[out_offset++] = byte;
-		if (out_offset >= EC_MAILBOX_DATA_SIZE)
-			break;
-
-		in_offset = end_offset;
-	}
-	if (out_offset == 0)
-		return -EINVAL;
-
-	/* Clear response data buffer */
-	memset(raw_response_data, 0, EC_MAILBOX_DATA_SIZE_EXTENDED);
-
-	msg.type = raw_request_data[0] << 8 | raw_request_data[1];
-	msg.flags = WILCO_EC_FLAG_RAW;
-	msg.command = raw_request_data[2];
-	msg.request_data = raw_request_data + 3;
-	msg.request_size = out_offset - 3;
-	msg.response_data = raw_response_data;
-	msg.response_size = EC_MAILBOX_DATA_SIZE;
-
-	/* Telemetry commands use extended response data */
-	if (msg.type == WILCO_EC_MSG_TELEMETRY) {
-		msg.flags |= WILCO_EC_FLAG_EXTENDED_DATA;
-		msg.response_size = EC_MAILBOX_DATA_SIZE_EXTENDED;
-	}
+#define WILCO_EC_ATTR_WO(_name)						\
+__ATTR(_name, 0200, NULL, wilco_ec_##_name##_store)
 
-	ret = wilco_ec_mailbox(ec, &msg);
-	if (ret < 0)
-		return ret;
-	raw_response_size = ret;
-	return count;
-}
-
-#endif /* CONFIG_WILCO_EC_SYSFS_RAW */
-
-struct ec_info {
-	u8 index;
-	const char *label;
-};
-
-static ssize_t wilco_ec_show_info(struct wilco_ec_device *ec, char *buf,
-				  ssize_t count, struct ec_info *info)
-{
-	char result[EC_INFO_SIZE];
-	struct wilco_ec_message msg = {
-		.type = WILCO_EC_MSG_LEGACY,
-		.command = EC_COMMAND_EC_INFO,
-		.request_data = &info->index,
-		.request_size = sizeof(info->index),
-		.response_data = result,
-		.response_size = EC_INFO_SIZE,
-	};
-	int ret;
-
-	ret = wilco_ec_mailbox(ec, &msg);
-	if (ret != EC_INFO_SIZE)
-		return scnprintf(buf + count, PAGE_SIZE - count,
-				 "%-12s : ERROR %d\n", info->label, ret);
+#define WILCO_EC_ATTR_RW(_name)						\
+__ATTR(_name, 0644, wilco_ec_##_name##_show, wilco_ec_##_name##_store)
 
-	return scnprintf(buf + count, PAGE_SIZE - count,
-			 "%-12s : %s\n", info->label, result);
-}
-
-static ssize_t version_show(struct device *dev,
-			    struct device_attribute *attr, char *buf)
-{
-	struct wilco_ec_device *ec = dev_get_drvdata(dev);
-	struct ec_info wilco_ec_info[] = {
-		{ 0, "Label" },
-		{ 1, "SVN Revision" },
-		{ 2, "Model Number" },
-		{ 3, "Build Date" },
-		{ 0xff, NULL },
-	};
-	struct ec_info *info = wilco_ec_info;
-	ssize_t c = 0;
-
-	for (info = wilco_ec_info; info->label; info++)
-		c += wilco_ec_show_info(ec, buf, c, info);
+/* Make top-level attributes, which will live inside GOOG000C:00/ */
 
-	return c;
-}
-
-static ssize_t stealth_mode_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t count)
-{
-	struct wilco_ec_device *ec = dev_get_drvdata(dev);
-	u8 param;
-	struct wilco_ec_message msg = {
-		.type = WILCO_EC_MSG_LEGACY,
-		.command = EC_COMMAND_STEALTH_MODE,
-		.request_data = &param,
-		.request_size = sizeof(param),
-	};
-	int ret;
-	bool enable;
-
-	ret = kstrtobool(buf, &enable);
-	if (ret)
-		return ret;
-
-	/* Invert input parameter, EC expects 0=on and 1=off */
-	param = enable ? 0 : 1;
-
-	ret = wilco_ec_mailbox(ec, &msg);
-	if (ret < 0)
-		return ret;
-
-	return count;
-}
-
-static DEVICE_ATTR_RO(version);
-static DEVICE_ATTR_WO(stealth_mode);
+static struct device_attribute version_attr = WILCO_EC_ATTR_RO(version);
+static struct device_attribute stealth_attr = WILCO_EC_ATTR_WO(stealth_mode);
 #ifdef CONFIG_WILCO_EC_SYSFS_RAW
-static DEVICE_ATTR_RW(raw);
+static struct device_attribute raw_attr = WILCO_EC_ATTR_RW(raw);
 #endif
 
-static struct attribute *wilco_ec_attrs[] = {
-	&dev_attr_version.attr,
-	&dev_attr_stealth_mode.attr,
+static struct attribute *wilco_ec_toplevel_attrs[] = {
+	&version_attr.attr,
+	&stealth_attr.attr,
 #ifdef CONFIG_WILCO_EC_SYSFS_RAW
-	&dev_attr_raw.attr,
+	&raw_attr.attr,
 #endif
 	NULL
 };
-ATTRIBUTE_GROUPS(wilco_ec);
 
+ATTRIBUTE_GROUPS(wilco_ec_toplevel);
+
+/**
+ * wilco_ec_sysfs_init() - Initialize the sysfs directories and attributes
+ * @dev: The device representing the EC
+ *
+ * Creates the sysfs directory structure and populates it with all attributes.
+ * If there is a problem it will clean up the entire filesystem.
+ *
+ * Return 0 on success, -ENOMEM on failure creating directories or attibutes.
+ */
 int wilco_ec_sysfs_init(struct wilco_ec_device *ec)
 {
-	return sysfs_create_groups(&ec->dev->kobj, wilco_ec_groups);
+	struct device *dev = ec->dev;
+	int ret;
+
+	// add the top-level attributes
+	ret = sysfs_create_groups(&dev->kobj, wilco_ec_toplevel_groups);
+	if (ret) {
+		dev_err(dev, "failed to create sysfs filesystem!");
+		return -ENOMEM;
+	}
+
+	return 0;
 }
 
 void wilco_ec_sysfs_remove(struct wilco_ec_device *ec)
 {
-	sysfs_remove_groups(&ec->dev->kobj, wilco_ec_groups);
+	struct device *dev = ec->dev;
+
+	/* go upwards through the directory structure */
+	sysfs_remove_groups(&dev->kobj, wilco_ec_toplevel_groups);
 }
-- 
2.20.0.405.gbc1bbc6f85-goog


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

* [RFC PATCH 08/10] CHROMIUM: wilco_ec: Add EC properties
  2018-12-15  0:18 [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Nick Crews
                   ` (6 preceding siblings ...)
  2018-12-15  0:18 ` [RFC PATCH 07/10] CHROMIUM: wilco_ec: Move legacy attributes to separate file Nick Crews
@ 2018-12-15  0:18 ` Nick Crews
  2018-12-15  0:18 ` [RFC PATCH 09/10] CHROMIUM: wilco_ec: Add peakshift and adv_batt_charging Nick Crews
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Nick Crews @ 2018-12-15  0:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: Nick Crews, Olof Johansson, Benson Leung

A Property is typically a data item that is stored to NVRAM.
Each of these data items has an index associated with it
known as the Property ID (PID). The Property ID is
used by the system BIOS (and EC) to refer to the Property.
Properties may have variable lengths. Many features are
implemented primarily by EC Firmware with system BIOS
just supporting user configuration via BIOS SETUP and/or
SMBIOS changes. In order to implement many of these types of
features the user configuration information is saved to and
retrieved from the EC. The EC stores this configuration
information to NVRAM and then can use it while the system
BIOS is not running or during early boot. Although this
is a typical scenario there may be other reasons to store
information in the EC NVRAM instead of the System NVRAM.
Most of the property services do not have a valid failure
condition, so this field can be ignored. For items that
are write once, a failure is returned when a second
write is attempted.

Add a get and set interface for EC properties.
properties live within the "properties" directory.
Most of the added properties are boolean, but this also
provides the interface for non-boolean properties,
which will be used late for scheduling power routines.

The wilco_ec_sysfs_util.h stuff will be used for
future attributes as well.

> cd /sys/bus/platform/devices/GOOG000C\:00/
> echo 0 > properties/global_mic_mute_led
[mic mute led on keyboard turns off]
> cat
0
> echo 1 > properties/global_mic_mute_led
[mic mute led on keyboard turns on]
> cat properties/global_mic_mute_led
1
> cat properties/wireless_sw_wlan
cat: wireless_sw_wlan: Permission denied
[Good, that is supposed to be write-only]
> echo 0 > properties/wireless_sw_wlan

Signed-off-by: Nick Crews <ncrews@google.com>
---

 drivers/platform/chrome/Makefile              |   1 +
 drivers/platform/chrome/wilco_ec_properties.c | 327 ++++++++++++++++++
 drivers/platform/chrome/wilco_ec_properties.h | 163 +++++++++
 drivers/platform/chrome/wilco_ec_sysfs.c      |  66 +++-
 drivers/platform/chrome/wilco_ec_sysfs_util.h |  47 +++
 5 files changed, 598 insertions(+), 6 deletions(-)
 create mode 100644 drivers/platform/chrome/wilco_ec_properties.c
 create mode 100644 drivers/platform/chrome/wilco_ec_properties.h
 create mode 100644 drivers/platform/chrome/wilco_ec_sysfs_util.h

diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index 56c39de8e5f5..eefb75e5e69c 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -15,5 +15,6 @@ obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o
 
 wilco_ec-objs				:= wilco_ec_mailbox.o wilco_ec_event.o \
 					   wilco_ec_rtc.o wilco_ec_legacy.o \
+					   wilco_ec_properties.o \
 					   wilco_ec_sysfs.o
 obj-$(CONFIG_WILCO_EC)			+= wilco_ec.o
diff --git a/drivers/platform/chrome/wilco_ec_properties.c b/drivers/platform/chrome/wilco_ec_properties.c
new file mode 100644
index 000000000000..7131bd79aa61
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec_properties.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * wilco_ec_properties - set/get properties of Wilco Embedded Controller
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include "wilco_ec_properties.h"
+#include "wilco_ec.h"
+#include "wilco_ec_sysfs_util.h"
+
+/* Payload length for get/set properties */
+#define PROPERTY_DATA_MAX_LENGTH 4
+
+struct ec_property_get_request {
+	u32 property_id;
+	u8 length;
+} __packed;
+
+struct ec_property_set_request {
+	u32 property_id;
+	u8 length;
+	u8 data[PROPERTY_DATA_MAX_LENGTH];
+} __packed;
+
+struct ec_property_response {
+	u8 status;
+	u8 sub_function;
+	u32 property_id;
+	u8 length;
+	u8 data[PROPERTY_DATA_MAX_LENGTH];
+} __packed;
+
+/* Store a 32 bit property ID into an array or a field in a struct, LSB first */
+static inline void fill_property_id(u32 property_id, u8 field[])
+{
+	field[0] =  property_id        & 0xff;
+	field[1] = (property_id >> 8)  & 0xff;
+	field[2] = (property_id >> 16) & 0xff;
+	field[3] = (property_id >> 24) & 0xff;
+}
+
+/* Extract 32 bit property ID from an array or a field in a struct, LSB first */
+static inline u32 extract_property_id(u8 field[])
+{
+	return (uint32_t)field[0]	|
+	       (uint32_t)field[1] << 8  |
+	       (uint32_t)field[2] << 16 |
+	       (uint32_t)field[3] << 24;
+}
+
+/**
+ * check_property_response() - Verify that the response from the EC is valid.
+ * @ec: EC device
+ * @rs: bytes sent back from the EC, filled into struct
+ * @op: Which of [SET, GET, SYNC] we are responding to
+ * @expected_property_id: Property ID that we were trying to read
+ * @expected_length: Number of bytes of actual payload we expected
+ * @expected_data: What we expect the EC to echo back for a SET. For GETting
+ *		   or SYNCing, we don't know the response, so use NULL to ignore
+ *
+ * Return: 0 on success, -EBADMSG on failure.
+ */
+static int check_property_response(struct wilco_ec_device *ec,
+				   struct ec_property_response *rs,
+				   enum get_set_sync_op op,
+				   u32 expected_property_id, u8 expected_length,
+				   const u8 expected_data[])
+{
+	u32 received_property_id;
+	int i;
+
+	/* check for success/failure flag */
+	if (rs->status) {
+		dev_err(ec->dev, "EC reports failure to get property");
+		return -EBADMSG;
+	}
+
+	/* Which subcommand is the EC responding to? */
+	if (rs->sub_function != op) {
+		dev_err(ec->dev, "For SET/GET/SYNC, EC replied %d, expected %d",
+			rs->sub_function, op);
+		return -EBADMSG;
+	}
+
+	/* Check that returned property_id is what we expect */
+	received_property_id = extract_property_id((u8 *)&rs->property_id);
+	if (received_property_id != expected_property_id) {
+		dev_err(ec->dev,
+			"EC responded to property_id 0x%08x, expected 0x%08x",
+			received_property_id, expected_property_id);
+		return -EBADMSG;
+	}
+
+	/* Did we get the correct number of bytes as a payload? */
+	if (rs->length != expected_length) {
+		dev_err(ec->dev, "EC returned %d bytes when we expected %d",
+			rs->length, expected_length);
+		return -EBADMSG;
+	}
+
+	/* Check that the actual data returned was what we expected */
+	if (expected_length < 1 || !expected_data)
+		return 0;
+	for (i = 0; i < expected_length; i++) {
+		if (rs->data[i] != expected_data[i]) {
+			dev_err(ec->dev, "returned[%d]=%2x != expected[%d]=%2x",
+				i, rs->data[i], i, expected_data[i]);
+			return -EBADMSG;
+		}
+	}
+
+	return 0;
+}
+
+static inline int check_get_property_response(struct wilco_ec_device *ec,
+					      struct ec_property_response *rs,
+					      u32 expected_property_id,
+					      u8 expected_length)
+{
+	return check_property_response(ec, rs, OP_GET, expected_property_id,
+				       expected_length, NULL);
+}
+
+static inline int check_set_property_response(struct wilco_ec_device *ec,
+					      struct ec_property_response *rs,
+					      enum get_set_sync_op op,
+					      u32 expected_property_id,
+					      u8 expected_length,
+					      const u8 expected_data[])
+{
+	return check_property_response(ec, rs, op, expected_property_id,
+				       expected_length, expected_data);
+}
+
+ssize_t wilco_ec_get_property(struct wilco_ec_device *ec, u32 property_id,
+			      u8 result_length, u8 *result)
+{
+	int ret, response_valid;
+	struct ec_property_get_request rq;
+	struct ec_property_response rs;
+	struct wilco_ec_message msg = {
+		.type = WILCO_EC_MSG_PROPERTY,
+		.flags = WILCO_EC_FLAG_RAW,
+		.command = OP_GET,
+		.request_data = &rq,
+		.request_size = sizeof(rq),
+		.response_data = &rs,
+		.response_size = sizeof(rs),
+	};
+
+	/* Create the request struct */
+	if (result_length < 1) {
+		dev_err(ec->dev,
+			"Requested %d bytes when getting property, min is 0\n",
+			result_length);
+		return -EINVAL;
+	}
+	if (result_length > PROPERTY_DATA_MAX_LENGTH) {
+		dev_err(ec->dev,
+			"Requested %d bytes when getting property, max is %d\n",
+			result_length, PROPERTY_DATA_MAX_LENGTH);
+		return -EINVAL;
+	}
+	fill_property_id(property_id, (u8 *)&(rq.property_id));
+	rq.length = 0;
+
+	/* send and receive */
+	ret = wilco_ec_mailbox(ec, &msg);
+	if (ret < 0) {
+		dev_err(ec->dev, "Get Property 0x%08x command failed\n",
+			property_id);
+		return ret;
+	}
+
+	/* verify that the response was valid */
+	response_valid = check_get_property_response(ec, &rs, property_id,
+						     result_length);
+	if (response_valid < 0)
+		return response_valid;
+
+	memcpy(result, &rs.data, result_length);
+	return ret;
+}
+
+ssize_t wilco_ec_set_property(struct wilco_ec_device *ec,
+			      enum get_set_sync_op op, u32 property_id,
+			      u8 length, const u8 *data)
+{
+	int ret;
+	struct ec_property_set_request rq;
+	struct ec_property_response rs;
+	u8 request_length = sizeof(rq) - PROPERTY_DATA_MAX_LENGTH + length;
+	struct wilco_ec_message msg = {
+		.type = WILCO_EC_MSG_PROPERTY,
+		.flags = WILCO_EC_FLAG_RAW,
+		.command = op,
+		.request_data = &rq,
+		.request_size = request_length,
+		.response_data = &rs,
+		.response_size = sizeof(rs),
+	};
+
+	/* make request */
+	if (op != OP_SET && op != OP_SYNC) {
+		dev_err(ec->dev, "Set op must be OP_SET | OP_SYNC, got %d", op);
+		return -EINVAL;
+	}
+	if (length < 1) {
+		dev_err(ec->dev,
+			"Sending %d bytes when setting property, min is 1",
+			length);
+		return -EINVAL;
+	}
+	if (length > PROPERTY_DATA_MAX_LENGTH) {
+		dev_err(ec->dev,
+			"Sending %d bytes when setting property, max is %d",
+			length, PROPERTY_DATA_MAX_LENGTH);
+		return -EINVAL;
+	}
+	fill_property_id(property_id, (u8 *)&(rq.property_id));
+	rq.length = length;
+	memcpy(rq.data, data, length);
+
+	/* send and receive */
+	ret = wilco_ec_mailbox(ec, &msg);
+	if (ret < 0) {
+		dev_err(ec->dev, "Set Property 0x%08x command failed\n",
+			property_id);
+		return ret;
+	}
+
+	/* verify that the response was valid, EC echoing back stored value */
+	ret = check_set_property_response(ec, &rs, op, property_id,
+						     length, data);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+ssize_t wilco_ec_get_bool_prop(struct device *dev, u32 property_id,
+			       char *result)
+{
+	struct wilco_ec_device *ec = dev_get_drvdata(dev);
+	int ret;
+
+	ret = wilco_ec_get_property(ec, property_id, 1, result);
+	if (ret < 0)
+		return ret;
+
+	/* convert the raw byte response into ascii */
+	switch (result[0]) {
+	case 0:
+		result[0] = '0';
+		break;
+	case 1:
+		result[0] = '1';
+		break;
+	default:
+		dev_err(ec->dev, "Expected 0 or 1 as response, got %02x",
+			result[0]);
+		return -EBADMSG;
+	}
+
+	/* Tack on a newline */
+	result[1] = '\n';
+	return 2;
+}
+
+ssize_t wilco_ec_set_bool_prop(struct device *dev, enum get_set_sync_op op,
+			       u32 property_id, const char *buf, size_t count)
+{
+	struct wilco_ec_device *ec = dev_get_drvdata(dev);
+	bool enable;
+	u8 param;
+	int ret;
+
+	ret = kstrtobool(buf, &enable);
+	if (ret) {
+		dev_err(dev, "Unable to parse '%s' to a bool", buf);
+		return ret;
+	}
+	param = enable;
+
+	ret = wilco_ec_set_property(ec, op, property_id, 1, &param);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+ssize_t wilco_ec_bool_prop_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	struct property_attribute *prop_attr;
+	struct device *dev;
+
+	prop_attr = container_of(attr, struct property_attribute, kobj_attr);
+	dev = device_from_kobject(kobj);
+
+	return wilco_ec_get_bool_prop(dev, prop_attr->pid, buf);
+}
+
+ssize_t wilco_ec_bool_prop_store(struct kobject *kobj,
+				 struct kobj_attribute *attr, const char *buf,
+				 size_t count)
+{
+	struct property_attribute *prop_attr;
+	struct device *dev;
+
+	prop_attr = container_of(attr, struct property_attribute, kobj_attr);
+	dev = device_from_kobject(kobj);
+
+	return wilco_ec_set_bool_prop(dev, prop_attr->op, prop_attr->pid, buf,
+				      count);
+}
diff --git a/drivers/platform/chrome/wilco_ec_properties.h b/drivers/platform/chrome/wilco_ec_properties.h
new file mode 100644
index 000000000000..2d69cd6208b8
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec_properties.h
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * wilco_ec_properties - set/get properties of Wilco Embedded Controller
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WILCO_EC_PROPERTIES_H
+#define WILCO_EC_PROPERTIES_H
+
+#include <linux/ctype.h>
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include "wilco_ec.h"
+
+#define PID_GLOBAL_MIC_MUTE_LED			0x0676
+#define PID_FN_LOCK				0x067b
+#define PID_NIC					0x04ea
+#define PID_EXT_USB_PORT_EN			0x0612
+#define PID_WIRELESS_SW_WLAN			0x0620
+#define PID_AUTO_BOOT_ON_TRINITY_DOCK_ATTACH	0x0725
+#define PID_ICH_AZALIA_EN			0x0a07
+#define PID_SIGN_OF_LIFE_KBBL			0x058f
+
+/**
+ * enum get_set_sync_op - three different subcommands for WILCO_EC_MSG_PROPERTY.
+ *
+ * OP_GET requests the property from the EC. OP_SET and OP_SYNC do the exact
+ * same thing from our perspective: save a property. Only one of them works for
+ * a given property, so each property uses either OP_GET and OP_SET, or
+ * OP_GET and OP_SYNC
+ */
+enum get_set_sync_op {
+	OP_GET = 0,
+	OP_SET = 1,
+	OP_SYNC = 4
+};
+
+/**
+ * struct property_attribute - A attribute representing an EC property
+ * @kobj_attr: The underlying kobj_attr that is registered with sysfs
+ * @pid: Property ID of this property
+ * @op: Either OP_SET or OP_SYNC, whichever this property uses
+ */
+struct property_attribute {
+	struct kobj_attribute kobj_attr;
+	u32 pid;
+	enum get_set_sync_op op;
+};
+
+/**
+ * wilco_ec_get_property() - Query a property from the EC
+ * @ec: EC device to query
+ * @property_id: Property ID
+ * @result_length: Number of bytes expected in result
+ * @result: Destination buffer for result, needs to be able to hold at least
+ *	    @result_length bytes
+ *
+ * Return: Number of bytes received from EC (AKA @result_length),
+ *	   negative error code on failure.
+ */
+ssize_t wilco_ec_get_property(struct wilco_ec_device *ec, u32 property_id,
+			      u8 result_length, u8 *result);
+
+/**
+ * wilco_ec_set_property() - Set a property on EC
+ * @ec: EC device to use
+ * @op: either OP_SET or OP_SYNC
+ * @property_id: Property ID
+ * @length: Number of bytes in input buffer @data
+ * @data: Input buffer
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+ssize_t wilco_ec_set_property(struct wilco_ec_device *ec,
+			      enum get_set_sync_op op, u32 property_id,
+			      u8 length, const u8 *data);
+
+/**
+ * wilco_ec_get_bool_prop() - Get a boolean property from EC.
+ * @dev: EC device to use
+ * @property_id: Property ID
+ * @result: Destination buffer to be filled, needs to be able to hold at least
+ *	    two bytes. Will be filled with either "0\n" or "1\n" in ASCII
+ *
+ * Return: Number of bytes copied into result (AKA 2),
+ *	   or negative error code on failure.
+ */
+ssize_t wilco_ec_get_bool_prop(struct device *dev, u32 property_id,
+			       char *result);
+
+/**
+ * wilco_ec_set_bool_prop() - Set a boolean property on EC
+ * @dev: EC device to use
+ * @op: either OP_SET or OP_SYNC
+ * @property_id: Property ID
+ * @buf: Source buffer of ASCII string, parseable by kstrtobool()
+ * @count: Number of bytes in input buffer
+ *
+ * Return: Number of bytes consumed from input buffer (AKA @count),
+ *         or negative error code on failure.
+ */
+ssize_t wilco_ec_set_bool_prop(struct device *dev, enum get_set_sync_op op,
+			       u32 property_id, const char *buf, size_t count);
+
+/**
+ * wilco_ec_bool_prop_show() - Get a boolean property from the EC
+ * @kobj: Kobject representing the directory this attribute lives within
+ * @attr: Attribute stored within relevant "struct property_attribute"
+ * @buf: Destination buffer to be filled, needs to be able to hold at least
+ *	 two bytes. Will be filled with either "0\n" or "1\n" in ASCII
+ *
+ * Return: Number of bytes placed into output buffer (AKA 2),
+ *	   or negative error code on failure.
+ */
+ssize_t wilco_ec_bool_prop_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf);
+
+/**
+ * wilco_ec_bool_prop_store() - Store a boolean property on the EC
+ * @kobj: Kobject representing the directory this attribute lives within
+ * @attr: Attribute stored within relevant "struct property_attribute"
+ * @buf: Source buffer of ASCII string, parseable by kstrtobool()
+ * @count: Number of bytes in input buffer
+ *
+ * Return: Number bytes consumed from input buf (AKA @count),
+ *	   or negative error code on failure.
+ */
+ssize_t wilco_ec_bool_prop_store(struct kobject *kobj,
+				 struct kobj_attribute *attr, const char *buf,
+				 size_t count);
+
+#define BOOL_PROP_KOBJ_ATTR_RW(_name)					\
+__ATTR(_name, 0644, wilco_ec_bool_prop_show, wilco_ec_bool_prop_store)
+
+#define BOOL_PROP_KOBJ_ATTR_WO(_name)					\
+__ATTR(_name, 0200, NULL, wilco_ec_bool_prop_store)
+
+#define BOOLEAN_PROPERTY_RW_ATTRIBUTE(_op, _var, _name, _pid)	\
+struct property_attribute _var = {					\
+	.kobj_attr = BOOL_PROP_KOBJ_ATTR_RW(_name),			\
+	.pid = _pid,							\
+	.op = _op,							\
+}
+
+#define BOOLEAN_PROPERTY_WO_ATTRIBUTE(_op, _var, _name, _pid)	\
+struct property_attribute _var = {					\
+	.kobj_attr = BOOL_PROP_KOBJ_ATTR_WO(_name),			\
+	.pid = _pid,							\
+	.op = _op,							\
+}
+
+#endif /* WILCO_EC_PROPERTIES_H */
diff --git a/drivers/platform/chrome/wilco_ec_sysfs.c b/drivers/platform/chrome/wilco_ec_sysfs.c
index d11e577f7ff0..3cb7cf95098e 100644
--- a/drivers/platform/chrome/wilco_ec_sysfs.c
+++ b/drivers/platform/chrome/wilco_ec_sysfs.c
@@ -14,11 +14,10 @@
  * GNU General Public License for more details.
  */
 
-#include <linux/ctype.h>
-#include <linux/platform_device.h>
 #include <linux/sysfs.h>
 #include "wilco_ec.h"
 #include "wilco_ec_legacy.h"
+#include "wilco_ec_properties.h"
 
 #define WILCO_EC_ATTR_RO(_name)						\
 __ATTR(_name, 0444, wilco_ec_##_name##_show, NULL)
@@ -48,6 +47,42 @@ static struct attribute *wilco_ec_toplevel_attrs[] = {
 
 ATTRIBUTE_GROUPS(wilco_ec_toplevel);
 
+/* Make property attributes, which will live inside GOOG000C:00/properties/  */
+
+BOOLEAN_PROPERTY_RW_ATTRIBUTE(OP_SET, bool_prop_attr_global_mic_mute_led,
+			      global_mic_mute_led, PID_GLOBAL_MIC_MUTE_LED);
+BOOLEAN_PROPERTY_RW_ATTRIBUTE(OP_SET, bool_prop_attr_fn_lock, fn_lock,
+			      PID_FN_LOCK);
+BOOLEAN_PROPERTY_RW_ATTRIBUTE(OP_SET, bool_prop_attr_nic, nic, PID_NIC);
+BOOLEAN_PROPERTY_RW_ATTRIBUTE(OP_SET, bool_prop_attr_ext_usb_port_en,
+			      ext_usb_port_en, PID_EXT_USB_PORT_EN);
+BOOLEAN_PROPERTY_WO_ATTRIBUTE(OP_SYNC, bool_prop_attr_wireless_sw_wlan,
+			      wireless_sw_wlan, PID_WIRELESS_SW_WLAN);
+BOOLEAN_PROPERTY_RW_ATTRIBUTE(OP_SET,
+			      bool_prop_attr_auto_boot_on_trinity_dock_attach,
+			      auto_boot_on_trinity_dock_attach,
+			      PID_AUTO_BOOT_ON_TRINITY_DOCK_ATTACH);
+BOOLEAN_PROPERTY_RW_ATTRIBUTE(OP_SET, bool_prop_attr_ich_azalia_en,
+			      ich_azalia_en, PID_ICH_AZALIA_EN);
+BOOLEAN_PROPERTY_RW_ATTRIBUTE(OP_SET, bool_prop_attr_sign_of_life_kbbl,
+			      sign_of_life_kbbl, PID_SIGN_OF_LIFE_KBBL);
+
+struct attribute *wilco_ec_property_attrs[] = {
+	&bool_prop_attr_global_mic_mute_led.kobj_attr.attr,
+	&bool_prop_attr_fn_lock.kobj_attr.attr,
+	&bool_prop_attr_nic.kobj_attr.attr,
+	&bool_prop_attr_ext_usb_port_en.kobj_attr.attr,
+	&bool_prop_attr_wireless_sw_wlan.kobj_attr.attr,
+	&bool_prop_attr_auto_boot_on_trinity_dock_attach.kobj_attr.attr,
+	&bool_prop_attr_ich_azalia_en.kobj_attr.attr,
+	&bool_prop_attr_sign_of_life_kbbl.kobj_attr.attr,
+	NULL
+};
+
+ATTRIBUTE_GROUPS(wilco_ec_property);
+struct kobject *prop_dir_kobj;
+
+
 /**
  * wilco_ec_sysfs_init() - Initialize the sysfs directories and attributes
  * @dev: The device representing the EC
@@ -64,12 +99,29 @@ int wilco_ec_sysfs_init(struct wilco_ec_device *ec)
 
 	// add the top-level attributes
 	ret = sysfs_create_groups(&dev->kobj, wilco_ec_toplevel_groups);
-	if (ret) {
-		dev_err(dev, "failed to create sysfs filesystem!");
-		return -ENOMEM;
-	}
+	if (ret)
+		goto err;
+
+	// add the directory for properties
+	prop_dir_kobj = kobject_create_and_add("properties", &dev->kobj);
+	if (!prop_dir_kobj)
+		goto rm_toplevel_attrs;
+
+	// add the property attributes into the properties directory
+	ret = sysfs_create_groups(prop_dir_kobj, wilco_ec_property_groups);
+	if (ret)
+		goto rm_properties_dir;
 
 	return 0;
+
+/* Go upwards through the directory structure, cleaning up */
+rm_properties_dir:
+	kobject_put(prop_dir_kobj);
+rm_toplevel_attrs:
+	sysfs_remove_groups(&dev->kobj, wilco_ec_toplevel_groups);
+err:
+	dev_err(dev, "Failed to create sysfs filesystem!");
+	return -ENOMEM;
 }
 
 void wilco_ec_sysfs_remove(struct wilco_ec_device *ec)
@@ -77,5 +129,7 @@ void wilco_ec_sysfs_remove(struct wilco_ec_device *ec)
 	struct device *dev = ec->dev;
 
 	/* go upwards through the directory structure */
+	sysfs_remove_groups(prop_dir_kobj, wilco_ec_property_groups);
+	kobject_put(prop_dir_kobj);
 	sysfs_remove_groups(&dev->kobj, wilco_ec_toplevel_groups);
 }
diff --git a/drivers/platform/chrome/wilco_ec_sysfs_util.h b/drivers/platform/chrome/wilco_ec_sysfs_util.h
new file mode 100644
index 000000000000..6c89b12664a4
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec_sysfs_util.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * wilco_ec_sysfs_util - helpers for sysfs attributes of Wilco EC
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WILCO_EC_SYSFS_UTIL_H
+#define WILCO_EC_SYSFS_UTIL_H
+
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include <linux/string.h>
+
+/**
+ * device_from_kobject() - Get EC device from subdirectory's kobject.
+ * @kobj: kobject associated with a subdirectory
+ *
+ * When we place attributes within directories within the sysfs filesystem,
+ * at each callback we get a reference to the kobject representing the directory
+ * that that attribute is in. Somehow we need to get a pointer to the EC device.
+ * This goes up the directory structure a number of levels until reaching the
+ * top level for the EC device, and then finds the device from the root kobject.
+ *
+ * Example: for attribute GOOG000C:00/properties/peakshift/sunday,
+ * we would go up two levels, from peakshift to properties and then from
+ * properties to GOOG000C:00
+ *
+ * Return: a pointer to the device struct representing the EC.
+ */
+static inline struct device *device_from_kobject(struct kobject *kobj)
+{
+	while (strcmp(kobj->name, "GOOG000C:00") != 0)
+		kobj = kobj->parent;
+	return container_of(kobj, struct device, kobj);
+}
+
+#endif
-- 
2.20.0.405.gbc1bbc6f85-goog


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

* [RFC PATCH 09/10] CHROMIUM: wilco_ec: Add peakshift and adv_batt_charging
  2018-12-15  0:18 [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Nick Crews
                   ` (7 preceding siblings ...)
  2018-12-15  0:18 ` [RFC PATCH 08/10] CHROMIUM: wilco_ec: Add EC properties Nick Crews
@ 2018-12-15  0:18 ` Nick Crews
  2018-12-15  0:18 ` [RFC PATCH 10/10] CHROMIUM: wilco_ec: Add binary telemetry attributes Nick Crews
  2018-12-17 16:15 ` [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Enric Balletbo Serra
  10 siblings, 0 replies; 16+ messages in thread
From: Nick Crews @ 2018-12-15  0:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: Nick Crews, Olof Johansson, Benson Leung

Create "peakshift" and "advanced_battery_charging" directories
within the "properties" directory, and create the relevant
attributes within these. These properties have to do with
configuring some of the advanced power management options that
prolong battery health and reduce energy use at peak hours
of the day.

Scheduling events uses a 24 hour clock, and only supports time
intervals of 15 minutes. For example, to set
advanced_battery_charging to start at 4:15pm and to last for
6 hours and 45 minutes, you would use the argument "16 15 6 45".

> cd /sys/bus/platform/devices/GOOG000C\:00
> cat properties/peakshift/peakshift_battery_threshold
> 015
[means 15 percent]
> cat properties/peakshift/peakshift_monday
16 00 20 30 00 00
[starts at 4:00 pm, ends at 8:30, charging resumes at midnight]
> echo "16 00 20 31 00 00" > properties/peakshift/peakshift_monday
-bash: echo: write error: Invalid argument
> dmesg | tail -n1
[40.34534] wilco_ec GOOG00C:00: minutes must be at the quarter hour
> echo "16 0 20 45 0 0" > properties/peakshift/peakshift_monday
> cat properties/peakshift/peakshift_monday
16 00 20 45 00 00

Signed-off-by: Nick Crews <ncrews@google.com>
---

 drivers/platform/chrome/Makefile             |   3 +-
 drivers/platform/chrome/wilco_ec_adv_power.c | 533 +++++++++++++++++++
 drivers/platform/chrome/wilco_ec_adv_power.h | 193 +++++++
 drivers/platform/chrome/wilco_ec_sysfs.c     | 103 ++++
 4 files changed, 831 insertions(+), 1 deletion(-)
 create mode 100644 drivers/platform/chrome/wilco_ec_adv_power.c
 create mode 100644 drivers/platform/chrome/wilco_ec_adv_power.h

diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index eefb75e5e69c..c9d3d44098f9 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o
 
 wilco_ec-objs				:= wilco_ec_mailbox.o wilco_ec_event.o \
 					   wilco_ec_rtc.o wilco_ec_legacy.o \
+					   wilco_ec_sysfs.o \
 					   wilco_ec_properties.o \
-					   wilco_ec_sysfs.o
+					   wilco_ec_adv_power.o
 obj-$(CONFIG_WILCO_EC)			+= wilco_ec.o
diff --git a/drivers/platform/chrome/wilco_ec_adv_power.c b/drivers/platform/chrome/wilco_ec_adv_power.c
new file mode 100644
index 000000000000..30120e2dd007
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec_adv_power.c
@@ -0,0 +1,533 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * wilco_ec_adv_power - peakshift and adv_batt_charging config of Wilco EC
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include "wilco_ec_adv_power.h"
+#include "wilco_ec_properties.h"
+#include "wilco_ec_sysfs_util.h"
+#include "wilco_ec.h"
+
+struct adv_batt_charging_data {
+	int duration_hours;
+	int duration_minutes;
+	int start_hours;
+	int start_minutes;
+};
+
+struct peakshift_data {
+	int start_hours;
+	int start_minutes;
+	int end_hours;
+	int end_minutes;
+	int charge_start_hours;
+	int charge_start_minutes;
+};
+
+/**
+ * struct time_bcd_format - spec for binary coded decimal time format
+ * @hour_position: how many bits left within the byte is the hour
+ * @minute_position: how many bits left within the byte is the minute
+ *
+ * Date and hour information is passed to/from the EC using packed bytes,
+ * where each byte represents an hour and a minute that some event occurs.
+ * The minute field always happens at quarter-hour intervals, so either
+ * 0, 15, 20, or 45. This allows this info to be packed within 2 bits.
+ * Along with the 5 bits of hour info [0-23], this gives us 7 used bits
+ * within each packed byte. The annoying thing is that the PEAKSHIFT and
+ * ADVANCED_BATTERY_CHARGING properties pack these 7 bits differently,
+ * hence this struct.
+ */
+struct time_bcd_format {
+	u8 hour_position;
+	u8 minute_position;
+};
+
+const struct time_bcd_format PEAKSHIFT_BCD_FORMAT = {
+			     // bit[0] is unused
+	.hour_position = 1,  // bits[1:7]
+	.minute_position = 6 // bits[6:8]
+};
+
+const struct time_bcd_format ADV_BATT_CHARGING_BCD_FORMAT = {
+	.minute_position = 0, // bits[0:2]
+	.hour_position = 2    // bits[2:7]
+			      // bit[7] is unused
+};
+
+/**
+ * struct peakshift_payload - The formatted peakshift time sent/received by EC.
+ * @start_time: packed byte of hour and minute info
+ * @end_time: packed byte of hour and minute info
+ * @charge_start_time: packed byte of hour and minute info
+ * @RESERVED: an unused padding byte
+ */
+struct peakshift_payload {
+	u8 start_time;
+	u8 end_time;
+	u8 charge_start_time;
+	u8 RESERVED;
+} __packed;
+
+struct adv_batt_charging_payload {
+	u16 RESERVED;
+	u8 duration_time;
+	u8 start_time;
+} __packed;
+
+/**
+ * extract_quarter_hour() - Convert from literal minutes to quarter hour.
+ * @minutes: Literal minutes value. Needs to be one of {0, 15, 30, 45}
+ *
+ * Return one of {0, 1, 2, 3} for each of {0, 15, 30, 45}, or -EINVAL on error.
+ */
+static int extract_quarter_hour(int minutes)
+{
+	if ((minutes < 0) || (minutes > 45) || minutes % 15)
+		return -EINVAL;
+	return minutes / 15;
+}
+
+static int check_adv_batt_charging_data(struct device *dev,
+					struct adv_batt_charging_data *data)
+{
+	if (data->start_hours < 0 || data->start_hours > 23) {
+		dev_err(dev, "start_hours must be in [0-23], got %d",
+			data->start_hours);
+		return -EINVAL;
+	}
+	if (data->duration_hours < 0 || data->duration_hours > 23) {
+		dev_err(dev, "duration_hours must be in [0-23], got %d",
+			data->duration_hours);
+		return -EINVAL;
+	}
+	if (data->start_minutes < 0 || data->start_minutes > 59) {
+		dev_err(dev, "start_minutes must be in [0-59], got %d",
+			data->start_minutes);
+		return -EINVAL;
+	}
+	if (data->duration_minutes < 0 || data->duration_minutes > 59) {
+		dev_err(dev, "duration_minutes must be in [0-59], got %d",
+			data->duration_minutes);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int check_peakshift_data(struct device *dev, struct peakshift_data *data)
+{
+	if (data->start_hours < 0 || data->start_hours > 23) {
+		dev_err(dev, "start_hours must be in [0-23], got %d",
+			data->start_hours);
+		return -EINVAL;
+	}
+	if (data->end_hours < 0 || data->end_hours > 23) {
+		dev_err(dev, "end_hours must be in [0-23], got %d",
+			data->end_hours);
+		return -EINVAL;
+	}
+	if (data->charge_start_hours < 0 || data->charge_start_hours > 23) {
+		dev_err(dev, "charge_start_hours must be in [0-23], got %d",
+			data->charge_start_hours);
+		return -EINVAL;
+	}
+	if (data->start_minutes < 0 || data->start_minutes > 59) {
+		dev_err(dev, "start_minutes must be in [0-59], got %d",
+			data->start_minutes);
+		return -EINVAL;
+	}
+	if (data->end_minutes < 0 || data->end_minutes > 59) {
+		dev_err(dev, "end_minutes must be in [0-59], got %d",
+			data->end_minutes);
+		return -EINVAL;
+	}
+	if (data->charge_start_minutes < 0 || data->charge_start_minutes > 59) {
+		dev_err(dev, "charge_start_minutes must be in [0-59], got %d",
+			data->charge_start_minutes);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/**
+ * pack_field() - Pack hour and minute info into a byte.
+ *
+ * @fmt: The format for how to place the info within the byte
+ * @hours: In range [0-23]
+ * @quarter_hour: In range [0-3], representing :00, :15, :30, and :45
+ *
+ * Return the packed byte.
+ */
+static u8 pack_field(struct time_bcd_format fmt, int hours, int quarter_hour)
+{
+	int result = 0;
+
+	result |= hours << fmt.hour_position;
+	result |= quarter_hour << fmt.minute_position;
+	return (u8) result;
+}
+
+/**
+ * unpack_field() - Extract hour and minute info from a byte.
+ *
+ * @fmt: The format for how to place the info within the byte
+ * @field: Byte which contains the packed info
+ * @hours: The value to be filled, in [0, 24]
+ * @quarter_hour: to be filled in range [0-3], meaning :00, :15, :30, and :45
+ */
+static void unpack_field(struct time_bcd_format fmt, u8 field, int *hours,
+			 int *quarter_hour)
+{
+	*hours =	(field >> fmt.hour_position)   & 0x1f; // 00011111
+	*quarter_hour = (field >> fmt.minute_position) & 0x03; // 00000011
+}
+
+static void pack_adv_batt_charging(struct adv_batt_charging_data *data,
+		     struct adv_batt_charging_payload *payload)
+{
+	payload->start_time = pack_field(ADV_BATT_CHARGING_BCD_FORMAT,
+					 data->start_hours,
+					 data->start_minutes);
+	payload->duration_time = pack_field(ADV_BATT_CHARGING_BCD_FORMAT,
+				       data->duration_hours,
+				       data->duration_minutes);
+}
+
+static void unpack_adv_batt_charging(struct adv_batt_charging_data *data,
+		       struct adv_batt_charging_payload *payload)
+{
+	unpack_field(ADV_BATT_CHARGING_BCD_FORMAT, payload->start_time,
+		     &(data->start_hours),
+		     &(data->start_minutes));
+	unpack_field(ADV_BATT_CHARGING_BCD_FORMAT, payload->duration_time,
+		     &(data->duration_hours),
+		     &(data->duration_minutes));
+}
+
+static void pack_peakshift(struct peakshift_data *data,
+			   struct peakshift_payload *payload)
+{
+	payload->start_time = pack_field(PEAKSHIFT_BCD_FORMAT,
+					 data->start_hours,
+					 data->start_minutes);
+	payload->end_time = pack_field(PEAKSHIFT_BCD_FORMAT,
+				       data->end_hours,
+				       data->end_minutes);
+	payload->charge_start_time = pack_field(PEAKSHIFT_BCD_FORMAT,
+						data->charge_start_hours,
+						data->charge_start_minutes);
+}
+
+static void unpack_peakshift(struct peakshift_data *data,
+			     struct peakshift_payload *payload)
+{
+	unpack_field(PEAKSHIFT_BCD_FORMAT, payload->start_time,
+		     &(data->start_hours),
+		     &(data->start_minutes));
+	unpack_field(PEAKSHIFT_BCD_FORMAT, payload->end_time,
+		     &(data->end_hours),
+		     &(data->end_minutes));
+	unpack_field(PEAKSHIFT_BCD_FORMAT, payload->charge_start_time,
+		     &(data->charge_start_hours),
+		     &(data->charge_start_minutes));
+}
+
+ssize_t wilco_ec_peakshift_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	struct property_attribute *prop_attr;
+	struct device *dev;
+	struct wilco_ec_device *ec;
+	struct peakshift_payload payload;
+	struct peakshift_data data;
+	const char FORMAT[] = "%02d %02d %02d %02d %02d %02d\n";
+	const int OUT_LENGTH = 18; //six 2-char nums, 5 spaces, 1 newline
+	int ret;
+
+	if (OUT_LENGTH + 1 > PAGE_SIZE)
+		return -ENOBUFS; //no buffer space for message + null
+
+	prop_attr = container_of(attr, struct property_attribute, kobj_attr);
+	dev = device_from_kobject(kobj);
+	ec = dev_get_drvdata(dev);
+
+	/* get the raw payload of data from the EC */
+	ret = wilco_ec_get_property(ec, prop_attr->pid, sizeof(payload),
+				    (u8 *) &payload);
+	if (ret < 0) {
+		dev_err(dev, "error in wilco_ec_mailbox()");
+		return ret;
+	}
+
+	/* unpack raw bytes, and convert quarter-hour to literal minute */
+	unpack_peakshift(&data, &payload);
+	data.start_minutes *= 15;
+	data.end_minutes *= 15;
+	data.charge_start_minutes *= 15;
+
+	/* Check that the EC returns good data */
+	ret = check_peakshift_data(dev, &data);
+	if (ret < 0) {
+		dev_err(dev, "EC returned out of range minutes or hours");
+		return -EBADMSG;
+	}
+
+	/* Print the numbers to the string */
+	ret = scnprintf(buf, OUT_LENGTH+1, FORMAT,
+			data.start_hours,
+			data.start_minutes,
+			data.end_hours,
+			data.end_minutes,
+			data.charge_start_hours,
+			data.charge_start_minutes);
+	if (ret != OUT_LENGTH) {
+		dev_err(dev, "expected to write %d chars, wrote %d", OUT_LENGTH,
+			ret);
+		return -EIO;
+	}
+
+	return OUT_LENGTH;
+}
+
+ssize_t wilco_ec_peakshift_store(struct kobject *kobj,
+				 struct kobj_attribute *attr, const char *buf,
+				 size_t count)
+{
+	struct property_attribute *prop_attr;
+	struct device *dev;
+	struct wilco_ec_device *ec;
+	struct peakshift_data data;
+	struct peakshift_payload payload;
+	const char FORMAT[] = "%d %d %d %d %d %d";
+	int ret;
+
+	prop_attr = container_of(attr, struct property_attribute, kobj_attr);
+	dev = device_from_kobject(kobj);
+	ec = dev_get_drvdata(dev);
+
+	/* Extract our 6 numbers from the input string */
+	ret = sscanf(buf, FORMAT,
+		     &data.start_hours,
+		     &data.start_minutes,
+		     &data.end_hours,
+		     &data.end_minutes,
+		     &data.charge_start_hours,
+		     &data.charge_start_minutes);
+	if (ret != 6) {
+		dev_err(dev, "unable to parse '%s' into 6 integers", buf);
+		return -EINVAL;
+	}
+
+	/* Ensure the integers we parsed are valid */
+	ret = check_peakshift_data(dev, &data);
+	if (ret < 0)
+		return ret;
+
+	/* Convert the literal minutes to which quarter hour they represent */
+	data.start_minutes = extract_quarter_hour(data.start_minutes);
+	if (data.start_minutes < 0)
+		goto bad_minutes;
+	data.end_minutes = extract_quarter_hour(data.end_minutes);
+	if (data.end_minutes < 0)
+		goto bad_minutes;
+	data.charge_start_minutes = extract_quarter_hour(
+						data.charge_start_minutes);
+	if (data.charge_start_minutes < 0)
+		goto bad_minutes;
+
+	/* Create the raw byte payload and send it off */
+	pack_peakshift(&data, &payload);
+	wilco_ec_set_property(ec, OP_SET, prop_attr->pid, sizeof(payload),
+			      (u8 *) &payload);
+
+	return count;
+
+bad_minutes:
+	dev_err(dev, "minutes must be at the quarter hour");
+	return -EINVAL;
+}
+
+ssize_t wilco_ec_peakshift_batt_thresh_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf)
+{
+	struct device *dev;
+	struct wilco_ec_device *ec;
+	const char FORMAT[] = "%02d\n";
+	size_t RESULT_LENGTH = 3; /* 2-char number and newline */
+	u8 percent;
+	int ret;
+
+	dev = device_from_kobject(kobj);
+	ec = dev_get_drvdata(dev);
+
+	ret = wilco_ec_get_property(ec, PID_PEAKSHIFT_BATTERY_THRESHOLD, 1,
+				    &percent);
+	if (ret < 0)
+		return ret;
+
+	if (percent < 15 || percent > 50) {
+		dev_err(ec->dev, "expected 15 < percentage < 50, got %d",
+			percent);
+		return -EBADMSG;
+	}
+
+	scnprintf(buf, RESULT_LENGTH+1, FORMAT, percent);
+
+	return RESULT_LENGTH;
+}
+
+ssize_t wilco_ec_peakshift_batt_thresh_store(struct kobject *kobj,
+					     struct kobj_attribute *attr,
+					     const char *buf, size_t count)
+{
+	struct device *dev;
+	struct wilco_ec_device *ec;
+	u8 DECIMAL_BASE = 10;
+	u8 percent;
+	int ret;
+
+	dev = device_from_kobject(kobj);
+	ec = dev_get_drvdata(dev);
+
+
+	ret = kstrtou8(buf, DECIMAL_BASE, &percent);
+	if (ret) {
+		dev_err(dev, "unable to parse '%s' to u8", buf);
+		return ret;
+	}
+
+	if (percent < 15 || percent > 50) {
+		dev_err(dev, "require 15 < batt_thresh_percent < 50, got %d",
+			percent);
+		return -EINVAL;
+	}
+
+	ret = wilco_ec_set_property(ec, OP_SET, PID_PEAKSHIFT_BATTERY_THRESHOLD,
+				    1, &percent);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+ssize_t wilco_ec_abc_show(struct kobject *kobj, struct kobj_attribute *attr,
+			  char *buf)
+{
+	struct property_attribute *prop_attr;
+	struct device *dev;
+	struct wilco_ec_device *ec;
+	struct adv_batt_charging_payload payload;
+	struct adv_batt_charging_data data;
+	const char FORMAT[] = "%02d %02d %02d %02d\n";
+	const int OUT_LENGTH = 12; //four 2-char nums, 3 spaces, 1 newline
+	int ret;
+
+	prop_attr = container_of(attr, struct property_attribute, kobj_attr);
+	dev = device_from_kobject(kobj);
+	ec = dev_get_drvdata(dev);
+
+
+	if (OUT_LENGTH + 1 > PAGE_SIZE)
+		return -ENOBUFS; //no buffer space for message + null
+
+	/* get the raw payload of data from the EC */
+	ret = wilco_ec_get_property(ec, prop_attr->pid, sizeof(payload),
+				    (u8 *) &payload);
+	if (ret < 0) {
+		dev_err(dev, "error in wilco_ec_mailbox()");
+		return ret;
+	}
+
+	/* unpack raw bytes, and convert quarter-hour to literal minute */
+	unpack_adv_batt_charging(&data, &payload);
+	data.start_minutes *= 15;
+	data.duration_minutes *= 15;
+
+	// /* Is this needed? can we assume the EC returns good data? */
+	// EC is returning 00 00 27 30. Was this modified, or is EC weird
+	// out of the box?
+	ret = check_adv_batt_charging_data(dev, &data);
+	if (ret < 0) {
+		dev_err(dev, "EC returned out of range minutes or hours");
+		return -EBADMSG;
+	}
+
+	/* Print the numbers to the string */
+	ret = scnprintf(buf, OUT_LENGTH+1, FORMAT,
+			data.start_hours,
+			data.start_minutes,
+			data.duration_hours,
+			data.duration_minutes);
+	if (ret != OUT_LENGTH) {
+		dev_err(dev, "expected to write %d chars, wrote %d", OUT_LENGTH,
+			ret);
+		return -EIO;
+	}
+
+	return OUT_LENGTH;
+}
+
+ssize_t wilco_ec_abc_store(struct kobject *kobj, struct kobj_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct property_attribute *prop_attr;
+	struct device *dev;
+	struct wilco_ec_device *ec;
+	struct adv_batt_charging_data data;
+	struct adv_batt_charging_payload payload;
+	const char FORMAT[] = "%d %d %d %d";
+	int ret;
+
+	prop_attr = container_of(attr, struct property_attribute, kobj_attr);
+	dev = device_from_kobject(kobj);
+	ec = dev_get_drvdata(dev);
+
+	/* Extract our 4 numbers from the input string */
+	ret = sscanf(buf, FORMAT,
+		     &data.start_hours,
+		     &data.start_minutes,
+		     &data.duration_hours,
+		     &data.duration_minutes);
+	if (ret != 4) {
+		dev_err(dev, "unable to parse '%s' into 4 integers", buf);
+		return -EINVAL;
+	}
+
+	/* Ensure the integers we parsed are valid */
+	ret = check_adv_batt_charging_data(dev, &data);
+	if (ret < 0)
+		return ret;
+
+	/* Convert the literal minutes to which quarter hour they represent */
+	data.start_minutes = extract_quarter_hour(data.start_minutes);
+	if (data.start_minutes < 0)
+		goto bad_minutes;
+	data.duration_minutes = extract_quarter_hour(data.duration_minutes);
+	if (data.duration_minutes < 0)
+		goto bad_minutes;
+
+	/* Create the raw byte payload and send it off */
+	pack_adv_batt_charging(&data, &payload);
+	wilco_ec_set_property(ec, OP_SET, prop_attr->pid, sizeof(payload),
+			      (u8 *) &payload);
+
+	return count;
+
+bad_minutes:
+	dev_err(dev, "minutes must be at the quarter hour");
+	return -EINVAL;
+}
diff --git a/drivers/platform/chrome/wilco_ec_adv_power.h b/drivers/platform/chrome/wilco_ec_adv_power.h
new file mode 100644
index 000000000000..8302650144ab
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec_adv_power.h
@@ -0,0 +1,193 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * wilco_ec_adv_power - peakshift and adv_batt_charging config of Wilco EC
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/**
+ * Peakshift:
+ * For each weekday a start and end time to run in Peak Shift mode can be set.
+ * During these times the system will run from the battery even if the AC is
+ * attached as long as the battery stays above the threshold specified.
+ * After the end time specified the system will run from AC if attached but
+ * will not charge the battery. The system will again function normally using AC
+ * and recharging the battery after the specified Charge Start time.
+ *
+ * Advanced Charging Mode:
+ * Advanced Charging Mode allows the user to maximize the battery health.
+ * In Advanced Charging Mode the system will use standard charging algorithm and
+ * other techniques during non-work hours to maximize battery health.
+ * During work hours, an express charge is used. This express charge allows the
+ * battery to be charged faster; therefore, the battery is at
+ * full charge sooner. For each day the time in which the system will be most
+ * heavily used is specified by the start time and the duration.
+ * Please read the Common UEFI BIOS Behavioral Specification and
+ * BatMan 2 BIOS_EC Specification for more details about this feature.
+ */
+
+#ifndef WILCO_EC_ADV_POWER_H
+#define WILCO_EC_ADV_POWER_H
+
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include "wilco_ec.h"
+#include "wilco_ec_sysfs_util.h"
+#include "wilco_ec_properties.h"
+
+#define PID_PEAKSHIFT				0x0412
+#define PID_PEAKSHIFT_BATTERY_THRESHOLD		0x04EB
+#define PID_PEAKSHIFT_SUNDAY_HOURS		0x04F5
+#define PID_PEAKSHIFT_MONDAY_HOURS		0x04F6
+#define PID_PEAKSHIFT_TUESDAY_HOURS		0x04F7
+#define PID_PEAKSHIFT_WEDNESDAY_HOURS		0x04F8
+#define PID_PEAKSHIFT_THURSDAY_HOURS		0x04F9
+#define PID_PEAKSHIFT_FRIDAY_HOURS		0x04Fa
+#define PID_PEAKSHIFT_SATURDAY_HOURS		0x04Fb
+
+#define PID_ABC_MODE				0x04ed
+#define PID_ABC_SUNDAY_HOURS			0x04F5
+#define PID_ABC_MONDAY_HOURS			0x04F6
+#define PID_ABC_TUESDAY_HOURS			0x04F7
+#define PID_ABC_WEDNESDAY_HOURS			0x04F8
+#define PID_ABC_THURSDAY_HOURS			0x04F9
+#define PID_ABC_FRIDAY_HOURS			0x04FA
+#define PID_ABC_SATURDAY_HOURS			0x04FB
+
+/**
+ * wilco_ec_peakshift_show() - Retrieves times stored for the peakshift policy.
+ * @kobj: kobject representing the directory this attribute is in
+ * @attr: Attribute stored within the proper property_attribute
+ * @buf: Output buffer to fill with the result
+ *
+ * The output buffer will be filled with the format
+ * "start_hr start_min end_hr end_min charge_start_hr charge_start_min"
+ * The hour fields will be in the range [0-23], and the minutes will be
+ * one of (0, 15, 30, 45). Each number will be zero padded to two characters.
+ *
+ * An example output is "06 15 09 45 23 00",
+ * which corresponds to 6:15, 9:45, and 23:00
+ *
+ * Return the length of the output buffer, or negative error code on failure.
+ */
+ssize_t wilco_ec_peakshift_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf);
+
+/**
+ * wilco_ec_peakshift_store() - Saves times for the peakshift policy.
+ * @kobj: kobject representing the directory this attribute is in
+ * @attr: Attribute stored within the proper property_attribute
+ * @buf: Raw input buffer
+ * @count: Number of bytes in input buffer
+ *
+ * The input buffer must have the format
+ * "start_hr start_min end_hr end_min charge_start_hr charge_start_min"
+ * The hour fields must be in the range [0-23], and the minutes must be
+ * one of (0, 15, 30, 45). The string must be parseable by sscanf() using the
+ * format string "%d %d %d %d %d %d".
+ *
+ * An example valid input is "6 15     009 45 23 0",
+ * which corresponds to 6:15, 9:45, and 23:00
+ *
+ * Return number of bytes consumed from input, negative error code on failure.
+ */
+ssize_t wilco_ec_peakshift_store(struct kobject *kobj,
+				 struct kobj_attribute *attr, const char *buf,
+				 size_t count);
+
+/**
+ * peakshift_battery_show() - Retrieve batt percentage at which peakshift stops
+ * @kobj: kobject representing the directory this attribute is in
+ * @attr: Attribute stored within the proper property_attribute
+ * @buf: Output buffer to fill with the result
+ *
+ * Result will be a 2 character integer representing the
+ * battery percentage at which peakshift stops. Will be in range [15, 50].
+ *
+ * Return the length of the output buffer, or negative error code on failure.
+ */
+ssize_t wilco_ec_peakshift_batt_thresh_show(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    char *buf);
+
+/**
+ * peakshift_battery_store() - Save batt percentage at which peakshift stops
+ * @kobj: kobject representing the directory this attribute is in
+ * @attr: Attribute stored within the proper property_attribute
+ * @buf: Input buffer, should be parseable to range [15,50] by kstrtou8()
+ * @count: Number of bytes in input buffer
+ *
+ * Return number of bytes consumed from input, negative error code on failure.
+ */
+ssize_t wilco_ec_peakshift_batt_thresh_store(struct kobject *kobj,
+					     struct kobj_attribute *attr,
+					     const char *buf, size_t count);
+
+/**
+ * wilco_ec_abc_show() - Retrieve times for Advanced Battery Charging
+ * @kobj: kobject representing the directory this attribute is in
+ * @attr: Attribute stored within the proper property_attribute
+ * @buf: Output buffer to fill with the result
+ *
+ * The output buffer will be filled with the format
+ * "start_hr start_min duration_hr duration_min"
+ * The hour fields will be in the range [0-23], and the minutes will be
+ * one of (0, 15, 30, 45). Each number will be zero padded to two characters.
+ *
+ * An example output is "06 15 23 45",
+ * which corresponds to a start time of 6:15 and a duration of 23:45
+ *
+ * Return the length of the output buffer, or negative error code on failure.
+ */
+ssize_t wilco_ec_abc_show(struct kobject *kobj, struct kobj_attribute *attr,
+			  char *buf);
+
+/**
+ * wilco_ec_abc_store() - Save times for Advanced Battery Charging
+ * @kobj: kobject representing the directory this attribute is in
+ * @attr: Attribute stored within the proper property_attribute
+ * @buf: The raw input buffer
+ * @count: Number of bytes in input buffer
+ *
+ * The input buffer must have the format
+ * "start_hr start_min duration_hr duration_min"
+ * The hour fields must be in the range [0-23], and the minutes must be
+ * one of (0, 15, 30, 45). The string must be parseable by sscanf() using the
+ * format string "%d %d %d %d %d %d".
+ *
+ * An example valid input is "0006 15     23 45",
+ * which corresponds to a start time of 6:15 and a duration of 23:45
+ *
+ * Return number of bytes consumed, or negative error code on failure.
+ */
+ssize_t wilco_ec_abc_store(struct kobject *kobj, struct kobj_attribute *attr,
+			   const char *buf, size_t count);
+
+#define PEAKSHIFT_KOBJ_ATTR(_name)					\
+__ATTR(_name, 0644, wilco_ec_peakshift_show, wilco_ec_peakshift_store)
+
+#define PEAKSHIFT_ATTRIBUTE(_var, _name, _pid)				\
+struct property_attribute _var = {					\
+	.kobj_attr = PEAKSHIFT_KOBJ_ATTR(_name),			\
+	.pid = _pid,							\
+}
+
+#define ABC_KOBJ_ATTR(_name)						\
+__ATTR(_name, 0644, wilco_ec_abc_show, wilco_ec_abc_store)
+
+#define ABC_ATTRIBUTE(_var, _name, _pid)				\
+struct property_attribute _var = {					\
+	.kobj_attr = ABC_KOBJ_ATTR(_name),				\
+	.pid = _pid,							\
+}
+
+#endif /* WILCO_EC_ADV_POWER_H */
diff --git a/drivers/platform/chrome/wilco_ec_sysfs.c b/drivers/platform/chrome/wilco_ec_sysfs.c
index 3cb7cf95098e..81dded951058 100644
--- a/drivers/platform/chrome/wilco_ec_sysfs.c
+++ b/drivers/platform/chrome/wilco_ec_sysfs.c
@@ -18,6 +18,7 @@
 #include "wilco_ec.h"
 #include "wilco_ec_legacy.h"
 #include "wilco_ec_properties.h"
+#include "wilco_ec_adv_power.h"
 
 #define WILCO_EC_ATTR_RO(_name)						\
 __ATTR(_name, 0444, wilco_ec_##_name##_show, NULL)
@@ -82,6 +83,70 @@ struct attribute *wilco_ec_property_attrs[] = {
 ATTRIBUTE_GROUPS(wilco_ec_property);
 struct kobject *prop_dir_kobj;
 
+/* Make peakshift attrs, which live inside GOOG000C:00/properties/peakshift */
+
+struct kobj_attribute kobj_attr_peakshift_battery_threshold =
+	__ATTR(battery_threshold, 0644, wilco_ec_peakshift_batt_thresh_show,
+	       wilco_ec_peakshift_batt_thresh_store);
+BOOLEAN_PROPERTY_RW_ATTRIBUTE(OP_SET, prop_attr_peakshift, enable,
+			      PID_PEAKSHIFT);
+PEAKSHIFT_ATTRIBUTE(prop_attr_peakshift_sunday, sunday,
+		    PID_PEAKSHIFT_SUNDAY_HOURS);
+PEAKSHIFT_ATTRIBUTE(prop_attr_peakshift_monday, monday,
+		    PID_PEAKSHIFT_MONDAY_HOURS);
+PEAKSHIFT_ATTRIBUTE(prop_attr_peakshift_tuesday, tuesday,
+		    PID_PEAKSHIFT_TUESDAY_HOURS);
+PEAKSHIFT_ATTRIBUTE(prop_attr_peakshift_wednesday, wednesday,
+		    PID_PEAKSHIFT_WEDNESDAY_HOURS);
+PEAKSHIFT_ATTRIBUTE(prop_attr_peakshift_thursday, thursday,
+		    PID_PEAKSHIFT_THURSDAY_HOURS);
+PEAKSHIFT_ATTRIBUTE(prop_attr_peakshift_friday, friday,
+		    PID_PEAKSHIFT_FRIDAY_HOURS);
+PEAKSHIFT_ATTRIBUTE(prop_attr_peakshift_saturday, saturday,
+		    PID_PEAKSHIFT_SATURDAY_HOURS);
+
+struct attribute *wilco_ec_peakshift_attrs[] = {
+	&kobj_attr_peakshift_battery_threshold.attr,
+	&prop_attr_peakshift.kobj_attr.attr,
+	&prop_attr_peakshift_sunday.kobj_attr.attr,
+	&prop_attr_peakshift_monday.kobj_attr.attr,
+	&prop_attr_peakshift_tuesday.kobj_attr.attr,
+	&prop_attr_peakshift_wednesday.kobj_attr.attr,
+	&prop_attr_peakshift_thursday.kobj_attr.attr,
+	&prop_attr_peakshift_friday.kobj_attr.attr,
+	&prop_attr_peakshift_saturday.kobj_attr.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(wilco_ec_peakshift);
+struct kobject *peakshift_dir_kobj;
+
+/**
+ * Make peakshift attrs, which live inside
+ * GOOG000C:00/properties/advanced_battery_charging
+ */
+
+BOOLEAN_PROPERTY_RW_ATTRIBUTE(OP_SET, prop_attr_abc, enable, PID_ABC_MODE);
+ABC_ATTRIBUTE(prop_attr_abc_sunday, sunday, PID_ABC_SUNDAY_HOURS);
+ABC_ATTRIBUTE(prop_attr_abc_monday, monday, PID_ABC_MONDAY_HOURS);
+ABC_ATTRIBUTE(prop_attr_abc_tuesday, tuesday, PID_ABC_TUESDAY_HOURS);
+ABC_ATTRIBUTE(prop_attr_abc_wednesday, wednesday, PID_ABC_WEDNESDAY_HOURS);
+ABC_ATTRIBUTE(prop_attr_abc_thursday, thursday, PID_ABC_THURSDAY_HOURS);
+ABC_ATTRIBUTE(prop_attr_abc_friday, friday, PID_ABC_FRIDAY_HOURS);
+ABC_ATTRIBUTE(prop_attr_abc_saturday, saturday, PID_ABC_SATURDAY_HOURS);
+
+struct attribute *wilco_ec_adv_batt_charging_attrs[] = {
+	&prop_attr_abc.kobj_attr.attr,
+	&prop_attr_abc_sunday.kobj_attr.attr,
+	&prop_attr_abc_monday.kobj_attr.attr,
+	&prop_attr_abc_tuesday.kobj_attr.attr,
+	&prop_attr_abc_wednesday.kobj_attr.attr,
+	&prop_attr_abc_thursday.kobj_attr.attr,
+	&prop_attr_abc_friday.kobj_attr.attr,
+	&prop_attr_abc_saturday.kobj_attr.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(wilco_ec_adv_batt_charging);
+struct kobject *adv_batt_charging_dir_kobj;
 
 /**
  * wilco_ec_sysfs_init() - Initialize the sysfs directories and attributes
@@ -112,9 +177,42 @@ int wilco_ec_sysfs_init(struct wilco_ec_device *ec)
 	if (ret)
 		goto rm_properties_dir;
 
+	// add the directory for adv batt charging into the properties directory
+	adv_batt_charging_dir_kobj = kobject_create_and_add(
+					"advanced_battery_charging",
+					prop_dir_kobj);
+	if (!adv_batt_charging_dir_kobj)
+		goto rm_properties_attrs;
+
+	// add the adv batt charging attributes into the abc directory
+	ret = sysfs_create_groups(adv_batt_charging_dir_kobj,
+				  wilco_ec_adv_batt_charging_groups);
+	if (ret)
+		goto rm_abc_dir;
+
+	// add the directory for peakshift into the properties directory
+	peakshift_dir_kobj = kobject_create_and_add("peakshift", prop_dir_kobj);
+	if (!peakshift_dir_kobj)
+		goto rm_abc_attrs;
+
+	// add the peakshift attributes into the peakshift directory
+	ret = sysfs_create_groups(peakshift_dir_kobj,
+				  wilco_ec_peakshift_groups);
+	if (ret)
+		goto rm_peakshift_dir;
+
 	return 0;
 
 /* Go upwards through the directory structure, cleaning up */
+rm_peakshift_dir:
+	kobject_put(peakshift_dir_kobj);
+rm_abc_attrs:
+	sysfs_remove_groups(adv_batt_charging_dir_kobj,
+			    wilco_ec_adv_batt_charging_groups);
+rm_abc_dir:
+	kobject_put(adv_batt_charging_dir_kobj);
+rm_properties_attrs:
+	sysfs_remove_groups(prop_dir_kobj, wilco_ec_property_groups);
 rm_properties_dir:
 	kobject_put(prop_dir_kobj);
 rm_toplevel_attrs:
@@ -129,6 +227,11 @@ void wilco_ec_sysfs_remove(struct wilco_ec_device *ec)
 	struct device *dev = ec->dev;
 
 	/* go upwards through the directory structure */
+	sysfs_remove_groups(peakshift_dir_kobj, wilco_ec_peakshift_groups);
+	kobject_put(peakshift_dir_kobj);
+	sysfs_remove_groups(adv_batt_charging_dir_kobj,
+			    wilco_ec_adv_batt_charging_groups);
+	kobject_put(adv_batt_charging_dir_kobj);
 	sysfs_remove_groups(prop_dir_kobj, wilco_ec_property_groups);
 	kobject_put(prop_dir_kobj);
 	sysfs_remove_groups(&dev->kobj, wilco_ec_toplevel_groups);
-- 
2.20.0.405.gbc1bbc6f85-goog


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

* [RFC PATCH 10/10] CHROMIUM: wilco_ec: Add binary telemetry attributes
  2018-12-15  0:18 [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Nick Crews
                   ` (8 preceding siblings ...)
  2018-12-15  0:18 ` [RFC PATCH 09/10] CHROMIUM: wilco_ec: Add peakshift and adv_batt_charging Nick Crews
@ 2018-12-15  0:18 ` Nick Crews
  2018-12-17 16:15 ` [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Enric Balletbo Serra
  10 siblings, 0 replies; 16+ messages in thread
From: Nick Crews @ 2018-12-15  0:18 UTC (permalink / raw)
  To: linux-kernel; +Cc: Nick Crews, Olof Johansson, Benson Leung

The Wilco Embedded Controller is able to send telemetry data
which is useful for enterprise applications. A daemon running on
the OS sends a command (possibly with args) to the EC via
this sysfs interface, and the EC responds over the sysfs interface
with the response. Both the request and the response are in an opaque
binary format so that information which is proprietary to the
enterprise service provider is secure.

At this point, the Wilco EC does not implement this telemetry
functionality, so any request using the WILCO_EC_MSG_TELEMETRY
command returns an error. This is just an initial change for
comments, until the EC code is implemented.

Signed-off-by: Nick Crews <ncrews@google.com>
---

 drivers/platform/chrome/Makefile             |  3 +-
 drivers/platform/chrome/wilco_ec_sysfs.c     | 17 ++++-
 drivers/platform/chrome/wilco_ec_telemetry.c | 66 ++++++++++++++++++++
 drivers/platform/chrome/wilco_ec_telemetry.h | 42 +++++++++++++
 4 files changed, 126 insertions(+), 2 deletions(-)
 create mode 100644 drivers/platform/chrome/wilco_ec_telemetry.c
 create mode 100644 drivers/platform/chrome/wilco_ec_telemetry.h

diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index c9d3d44098f9..62444887ce18 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -17,5 +17,6 @@ wilco_ec-objs				:= wilco_ec_mailbox.o wilco_ec_event.o \
 					   wilco_ec_rtc.o wilco_ec_legacy.o \
 					   wilco_ec_sysfs.o \
 					   wilco_ec_properties.o \
-					   wilco_ec_adv_power.o
+					   wilco_ec_adv_power.o  \
+					   wilco_ec_telemetry.o
 obj-$(CONFIG_WILCO_EC)			+= wilco_ec.o
diff --git a/drivers/platform/chrome/wilco_ec_sysfs.c b/drivers/platform/chrome/wilco_ec_sysfs.c
index 81dded951058..0019d17c0cf9 100644
--- a/drivers/platform/chrome/wilco_ec_sysfs.c
+++ b/drivers/platform/chrome/wilco_ec_sysfs.c
@@ -19,6 +19,7 @@
 #include "wilco_ec_legacy.h"
 #include "wilco_ec_properties.h"
 #include "wilco_ec_adv_power.h"
+#include "wilco_ec_telemetry.h"
 
 #define WILCO_EC_ATTR_RO(_name)						\
 __ATTR(_name, 0444, wilco_ec_##_name##_show, NULL)
@@ -46,7 +47,21 @@ static struct attribute *wilco_ec_toplevel_attrs[] = {
 	NULL
 };
 
-ATTRIBUTE_GROUPS(wilco_ec_toplevel);
+static struct bin_attribute telem_attr = TELEMETRY_BIN_ATTR(telemetry);
+static struct bin_attribute *telem_attrs[] = {
+	&telem_attr,
+	NULL
+};
+
+static const struct attribute_group wilco_ec_toplevel_group = {
+	.attrs = wilco_ec_toplevel_attrs,
+	.bin_attrs = telem_attrs,
+};
+
+static const struct attribute_group *wilco_ec_toplevel_groups[] = {
+	&wilco_ec_toplevel_group,
+	NULL,
+};
 
 /* Make property attributes, which will live inside GOOG000C:00/properties/  */
 
diff --git a/drivers/platform/chrome/wilco_ec_telemetry.c b/drivers/platform/chrome/wilco_ec_telemetry.c
new file mode 100644
index 000000000000..5b8168754b43
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec_telemetry.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * wilco_ec_legacy - Telemetry sysfs attributes for Wilco EC
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/fs.h>
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include "wilco_ec.h"
+#include "wilco_ec_sysfs_util.h"
+
+/* Data buffer for holding EC's response for telemtry data */
+static u8 telemetry_data[EC_MAILBOX_DATA_SIZE_EXTENDED];
+
+ssize_t wilco_ec_telem_write(struct file *filp, struct kobject *kobj,
+			     struct bin_attribute *attr, char *buf, loff_t loff,
+			     size_t count)
+{
+	struct wilco_ec_message msg;
+	int ret;
+	struct device *dev = device_from_kobject(kobj);
+	struct wilco_ec_device *ec = dev_get_drvdata(dev);
+
+	if (count < 1 || count > EC_MAILBOX_DATA_SIZE_EXTENDED)
+		return -EINVAL;
+
+	/* Clear response data buffer */
+	memset(telemetry_data, 0, EC_MAILBOX_DATA_SIZE_EXTENDED);
+
+	msg.type = WILCO_EC_MSG_TELEMETRY;
+	msg.flags = WILCO_EC_FLAG_RAW | WILCO_EC_FLAG_EXTENDED_DATA;
+	msg.command = buf[0];
+	msg.request_data = buf + 1;
+	msg.request_size = EC_MAILBOX_DATA_SIZE;
+	msg.response_data = &telemetry_data;
+	msg.response_size = EC_MAILBOX_DATA_SIZE_EXTENDED;
+
+	/* Send the requested command + data as raw transaction */
+	ret = wilco_ec_mailbox(ec, &msg);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+ssize_t wilco_ec_telem_read(struct file *filp, struct kobject *kobj,
+			    struct bin_attribute *attr, char *buf, loff_t off,
+			    size_t count)
+{
+	memcpy(buf, telemetry_data, min_t(unsigned long, count,
+	       EC_MAILBOX_DATA_SIZE_EXTENDED));
+	return count;
+}
diff --git a/drivers/platform/chrome/wilco_ec_telemetry.h b/drivers/platform/chrome/wilco_ec_telemetry.h
new file mode 100644
index 000000000000..874873284080
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec_telemetry.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * wilco_ec_telemetry - Telemetry sysfs attributes for Wilco EC
+ *
+ * Copyright 2018 Google LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WILCO_EC_TELEMETRY_H
+#define WILCO_EC_TELEMETRY_H
+
+#include <linux/fs.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include "wilco_ec.h"
+
+ssize_t wilco_ec_telem_write(struct file *filp, struct kobject *kobj,
+			     struct bin_attribute *attr, char *buf, loff_t loff,
+			     size_t count);
+
+ssize_t wilco_ec_telem_read(struct file *filp, struct kobject *kobj,
+			    struct bin_attribute *attr, char *buf, loff_t off,
+			    size_t count);
+
+#define TELEMETRY_BIN_ATTR(_name) {					\
+	.attr = {.name = __stringify(_name),				\
+		 .mode = VERIFY_OCTAL_PERMISSIONS(0644) },		\
+	.size = EC_MAILBOX_DATA_SIZE_EXTENDED,				\
+	.read = wilco_ec_telem_read,					\
+	.write = wilco_ec_telem_write,					\
+}
+
+#endif /* WILCO_EC_TELEMETRY_H */
-- 
2.20.0.405.gbc1bbc6f85-goog


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

* Re: [RFC PATCH 01/10] CHROMIUM: cros_ec: Remove cros_ec dependency in lpc_mec
  2018-12-15  0:18 ` [RFC PATCH 01/10] CHROMIUM: cros_ec: Remove cros_ec dependency in lpc_mec Nick Crews
@ 2018-12-17 15:50   ` Enric Balletbo Serra
  0 siblings, 0 replies; 16+ messages in thread
From: Enric Balletbo Serra @ 2018-12-17 15:50 UTC (permalink / raw)
  To: Nick Crews
  Cc: linux-kernel, Duncan Laurie, Olof Johansson, Benson Leung, Guenter Roeck

Hi Nick,

Many thanks for sending this upstream. Adding Guenter as also might be
interested and some minor comments below.

Missatge de Nick Crews <ncrews@google.com> del dia ds., 15 de des.
2018 a les 1:21:
>
> From: Duncan Laurie <dlaurie@google.com>
>
> In order to allow this code to be re-used, remove the dependency
> on the rest of the cros_ec code from the cros_ec_lpc_mec functions.
>
> Instead of using a hardcoded register base address of 0x800 have
> this be passed in to cros_ec_lpc_mec_init().  The existing cros_ec
> use case now passes in the 0x800 base address this way.
>
> Signed-off-by: Duncan Laurie <dlaurie@google.com>
> Signed-off-by: Nick Crews <ncrews@google.com>
> ---

For this patch and the rest of this series remove the CHROMIUM tag
from the subject. Use the format 'platform/chrome: cros_ec: ...'

>
>  drivers/platform/chrome/cros_ec_lpc_mec.c | 54 +++++++++++++++++++----
>  drivers/platform/chrome/cros_ec_lpc_mec.h | 45 +++++++++++--------
>  drivers/platform/chrome/cros_ec_lpc_reg.c | 43 +++++-------------
>  3 files changed, 85 insertions(+), 57 deletions(-)
>
> diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.c b/drivers/platform/chrome/cros_ec_lpc_mec.c
> index c4edfa83e493..18bd9f82be6c 100644
> --- a/drivers/platform/chrome/cros_ec_lpc_mec.c
> +++ b/drivers/platform/chrome/cros_ec_lpc_mec.c
> @@ -24,6 +24,7 @@
>  #include <linux/delay.h>
>  #include <linux/io.h>
>  #include <linux/mfd/cros_ec_commands.h>
> +#include <linux/mfd/cros_ec_lpc_mec.h>

This file does not exist, you'll get a build error with only this
patch applied. I think the file is already included.

>  #include <linux/mutex.h>
>  #include <linux/types.h>
>
> @@ -34,6 +35,7 @@
>   * EC mutex because memmap data may be accessed without it being held.
>   */
>  static struct mutex io_mutex;
> +static u16 mec_emi_base, mec_emi_end;
>
>  /*
>   * cros_ec_lpc_mec_emi_write_address
> @@ -46,10 +48,39 @@ static struct mutex io_mutex;
>  static void cros_ec_lpc_mec_emi_write_address(u16 addr,
>                         enum cros_ec_lpc_mec_emi_access_mode access_type)
>  {
> -       /* Address relative to start of EMI range */
> -       addr -= MEC_EMI_RANGE_START;
> -       outb((addr & 0xfc) | access_type, MEC_EMI_EC_ADDRESS_B0);
> -       outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1);
> +       outb((addr & 0xfc) | access_type, MEC_EMI_EC_ADDRESS_B0(mec_emi_base));
> +       outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1(mec_emi_base));
> +}
> +
> +/*
> + * cros_ec_lpc_mec_in_range
> + *

For this and all other functions that you are documenting below use
the standard kernel-doc format, please. See:
https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html

> + * Determine if requested addresses are in MEC EMI range.
> + *
> + * @offset:      Address offset
> + * @length: Number of bytes to check
> + *
> + * @return 1 if in range, 0 if not, and -1 if there is an error
> + *         such as the mec range not being initialized
> + */

Can't you use 0 if it's ok and -errno if it fails here? Will you
handle 0 and -1 errors in a different way?

> +int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length)
> +{
> +       if (length == 0)
> +               return -1;
> +
> +       if (WARN_ON(mec_emi_base == 0 || mec_emi_end == 0))
> +               return -1;
> +
> +       if (offset >= mec_emi_base && offset < mec_emi_end) {
> +               if (WARN_ON(offset + length - 1 <= mec_emi_end))
> +                       return -1;
> +               return 1;
> +       }
> +
> +       if (WARN_ON(offset + length > mec_emi_base && offset < mec_emi_end))
> +               return -1;
> +
> +       return 0;
>  }
>
>  /*
> @@ -71,6 +102,11 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
>         u8 sum = 0;
>         enum cros_ec_lpc_mec_emi_access_mode access, new_access;
>
> +       /* Return checksum of 0 if window is not initialized */
> +       WARN_ON(mec_emi_base == 0 || mec_emi_end == 0);
> +       if (mec_emi_base == 0 || mec_emi_end == 0)
> +               return 0;
> +
>         /*
>          * Long access cannot be used on misaligned data since reading B0 loads
>          * the data register and writing B3 flushes.
> @@ -86,9 +122,9 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
>         cros_ec_lpc_mec_emi_write_address(offset, access);
>
>         /* Skip bytes in case of misaligned offset */
> -       io_addr = MEC_EMI_EC_DATA_B0 + (offset & 0x3);
> +       io_addr = MEC_EMI_EC_DATA_B0(mec_emi_base) + (offset & 0x3);
>         while (i < length) {
> -               while (io_addr <= MEC_EMI_EC_DATA_B3) {
> +               while (io_addr <= MEC_EMI_EC_DATA_B3(mec_emi_base)) {
>                         if (io_type == MEC_IO_READ)
>                                 buf[i] = inb(io_addr++);
>                         else
> @@ -118,7 +154,7 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
>                 }
>
>                 /* Access [B0, B3] on each loop pass */
> -               io_addr = MEC_EMI_EC_DATA_B0;
> +               io_addr = MEC_EMI_EC_DATA_B0(mec_emi_base);
>         }
>
>  done:
> @@ -128,9 +164,11 @@ u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
>  }
>  EXPORT_SYMBOL(cros_ec_lpc_io_bytes_mec);
>
> -void cros_ec_lpc_mec_init(void)
> +void cros_ec_lpc_mec_init(unsigned int base, unsigned int end)
>  {
>         mutex_init(&io_mutex);
> +       mec_emi_base = base;
> +       mec_emi_end = end;
>  }
>  EXPORT_SYMBOL(cros_ec_lpc_mec_init);
>
> diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.h b/drivers/platform/chrome/cros_ec_lpc_mec.h
> index 105068c0e919..a81cc6a8b621 100644
> --- a/drivers/platform/chrome/cros_ec_lpc_mec.h
> +++ b/drivers/platform/chrome/cros_ec_lpc_mec.h
> @@ -24,8 +24,6 @@
>  #ifndef __CROS_EC_LPC_MEC_H
>  #define __CROS_EC_LPC_MEC_H
>
> -#include <linux/mfd/cros_ec_commands.h>
> -
>  enum cros_ec_lpc_mec_emi_access_mode {
>         /* 8-bit access */
>         ACCESS_TYPE_BYTE = 0x0,
> @@ -45,35 +43,46 @@ enum cros_ec_lpc_mec_io_type {
>         MEC_IO_WRITE,
>  };
>
> -/* Access IO ranges 0x800 thru 0x9ff using EMI interface instead of LPC */
> -#define MEC_EMI_RANGE_START EC_HOST_CMD_REGION0
> -#define MEC_EMI_RANGE_END   (EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE)
> -
>  /* EMI registers are relative to base */
> -#define MEC_EMI_BASE 0x800
> -#define MEC_EMI_HOST_TO_EC (MEC_EMI_BASE + 0)
> -#define MEC_EMI_EC_TO_HOST (MEC_EMI_BASE + 1)
> -#define MEC_EMI_EC_ADDRESS_B0 (MEC_EMI_BASE + 2)
> -#define MEC_EMI_EC_ADDRESS_B1 (MEC_EMI_BASE + 3)
> -#define MEC_EMI_EC_DATA_B0 (MEC_EMI_BASE + 4)
> -#define MEC_EMI_EC_DATA_B1 (MEC_EMI_BASE + 5)
> -#define MEC_EMI_EC_DATA_B2 (MEC_EMI_BASE + 6)
> -#define MEC_EMI_EC_DATA_B3 (MEC_EMI_BASE + 7)
> +#define MEC_EMI_HOST_TO_EC(MEC_EMI_BASE)       (MEC_EMI_BASE + 0)
> +#define MEC_EMI_EC_TO_HOST(MEC_EMI_BASE)       (MEC_EMI_BASE + 1)
> +#define MEC_EMI_EC_ADDRESS_B0(MEC_EMI_BASE)    (MEC_EMI_BASE + 2)
> +#define MEC_EMI_EC_ADDRESS_B1(MEC_EMI_BASE)    (MEC_EMI_BASE + 3)
> +#define MEC_EMI_EC_DATA_B0(MEC_EMI_BASE)       (MEC_EMI_BASE + 4)
> +#define MEC_EMI_EC_DATA_B1(MEC_EMI_BASE)       (MEC_EMI_BASE + 5)
> +#define MEC_EMI_EC_DATA_B2(MEC_EMI_BASE)       (MEC_EMI_BASE + 6)
> +#define MEC_EMI_EC_DATA_B3(MEC_EMI_BASE)       (MEC_EMI_BASE + 7)
>
> -/*
> +/**
>   * cros_ec_lpc_mec_init
>   *
>   * Initialize MEC I/O.
> + *
> + * @base: MEC EMI Base address
> + * @end:  MEC EMI End address

Since you're touching this function, please fix the documentation
format also. Thanks :)

>   */
> -void cros_ec_lpc_mec_init(void);
> +void cros_ec_lpc_mec_init(unsigned int base, unsigned int end);
>
> -/*
> +/**

Unrelated change.

>   * cros_ec_lpc_mec_destroy
>   *
>   * Cleanup MEC I/O.
>   */
>  void cros_ec_lpc_mec_destroy(void);
>
> +/**
> + * cros_ec_lpc_mec_in_range
> + *
> + * Determine if requested addresses are in MEC EMI range.
> + *
> + * @offset: Address offset
> + * @length: Number of bytes to check
> + *
> + * @return 1 if in range, 0 if not, and -1 if there is an error
> + *         such as the mec range not being initialized
> + */
> +int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length);
> +
>  /**
>   * cros_ec_lpc_io_bytes_mec - Read / write bytes to MEC EMI port
>   *
> diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.c b/drivers/platform/chrome/cros_ec_lpc_reg.c
> index fc23d535c404..4cba259b5b1e 100644
> --- a/drivers/platform/chrome/cros_ec_lpc_reg.c
> +++ b/drivers/platform/chrome/cros_ec_lpc_reg.c
> @@ -59,51 +59,32 @@ static u8 lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg)
>
>  u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest)
>  {
> -       if (length == 0)
> -               return 0;
> -
> -       /* Access desired range through EMI interface */
> -       if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) {
> -               /* Ensure we don't straddle EMI region */
> -               if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END))
> -                       return 0;
> +       int in_range = cros_ec_lpc_mec_in_range(offset, length);
>
> -               return cros_ec_lpc_io_bytes_mec(MEC_IO_READ, offset, length,
> -                                               dest);
> -       }
> -
> -       if (WARN_ON(offset + length > MEC_EMI_RANGE_START &&
> -                   offset < MEC_EMI_RANGE_START))
> +       if (in_range < 0)
>                 return 0;
>
> -       return lpc_read_bytes(offset, length, dest);
> +       return in_range ?
> +               cros_ec_lpc_io_bytes_mec(MEC_IO_READ, offset, length, dest) :
> +               lpc_read_bytes(offset, length, dest);
>  }
>
>  u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg)
>  {
> -       if (length == 0)
> -               return 0;
> -
> -       /* Access desired range through EMI interface */
> -       if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) {
> -               /* Ensure we don't straddle EMI region */
> -               if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END))
> -                       return 0;
> +       int in_range = cros_ec_lpc_mec_in_range(offset, length);
>
> -               return cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, offset, length,
> -                                               msg);
> -       }
> -
> -       if (WARN_ON(offset + length > MEC_EMI_RANGE_START &&
> -                   offset < MEC_EMI_RANGE_START))
> +       if (in_range < 0)
>                 return 0;
>
> -       return lpc_write_bytes(offset, length, msg);
> +       return in_range ?
> +               cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, offset, length, msg) :
> +               lpc_write_bytes(offset, length, msg);
>  }
>
>  void cros_ec_lpc_reg_init(void)
>  {
> -       cros_ec_lpc_mec_init();
> +       cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0,
> +                            EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE);
>  }
>
>  void cros_ec_lpc_reg_destroy(void)
> --
> 2.20.0.405.gbc1bbc6f85-goog
>

Thanks,
 Enric

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

* Re: [RFC PATCH 02/10] CHROMIUM: wilco_ec: Add new driver for Wilco EC
  2018-12-15  0:18 ` [RFC PATCH 02/10] CHROMIUM: wilco_ec: Add new driver for Wilco EC Nick Crews
@ 2018-12-17 15:50   ` Enric Balletbo Serra
  0 siblings, 0 replies; 16+ messages in thread
From: Enric Balletbo Serra @ 2018-12-17 15:50 UTC (permalink / raw)
  To: Nick Crews
  Cc: linux-kernel, Duncan Laurie, Olof Johansson, Benson Leung, Guenter Roeck

Hi Nick,

Many thanks for sending this upstream. Adding Guenter as also might be
interested, and some minor comments below.

Missatge de Nick Crews <ncrews@google.com> del dia ds., 15 de des.
2018 a les 1:22:
>
> From: Duncan Laurie <dlaurie@google.com>
>
> This EC is an incompatible variant of the typical Chrome OS embedded
> controller.  It uses the same low-level communication and a similar
> protocol with some significant differences.  The EC firmware does
> not support the same mailbox commands so it is not registered as a
> cros_ec device type.
>
> Signed-off-by: Duncan Laurie <dlaurie@google.com>
> Signed-off-by: Nick Crews <ncrews@google.com>
> ---
>
>  drivers/platform/chrome/Kconfig            |  14 +-
>  drivers/platform/chrome/Makefile           |   3 +
>  drivers/platform/chrome/wilco_ec.h         |  97 +++++
>  drivers/platform/chrome/wilco_ec_mailbox.c | 395 +++++++++++++++++++++
>  4 files changed, 508 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/platform/chrome/wilco_ec.h
>  create mode 100644 drivers/platform/chrome/wilco_ec_mailbox.c
>
> diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
> index 16b1615958aa..4168d5e6bedc 100644
> --- a/drivers/platform/chrome/Kconfig
> +++ b/drivers/platform/chrome/Kconfig
> @@ -49,6 +49,18 @@ config CHROMEOS_TBMC
>           To compile this driver as a module, choose M here: the
>           module will be called chromeos_tbmc.
>
> +config WILCO_EC
> +       tristate "ChromeOS Wilco Embedded Controller"
> +       depends on ACPI && (X86 || COMPILE_TEST)
> +       select CROS_EC_LPC_MEC
> +       help
> +         If you say Y here, you get support for talking to the Chrome OS

s/Chrome OS/ChromeOS/ like you did in the config description. For
coherence try to use the same form.

> +         Wilco EC over an eSPI bus. This uses a simple byte-level protocol
> +         with a checksum.
> +
> +         To compile this driver as a module, choose M here: the
> +         module will be called wilco_ec.
> +
>  config CROS_EC_CTL
>          tristate
>
> @@ -86,7 +98,7 @@ config CROS_EC_LPC
>
>  config CROS_EC_LPC_MEC
>         bool "ChromeOS Embedded Controller LPC Microchip EC (MEC) variant"
> -       depends on CROS_EC_LPC
> +       depends on CROS_EC_LPC || WILCO_EC
>         default n
>         help
>           If you say Y here, a variant LPC protocol for the Microchip EC
> diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
> index cd591bf872bb..b132ba5b3e3d 100644
> --- a/drivers/platform/chrome/Makefile
> +++ b/drivers/platform/chrome/Makefile
> @@ -12,4 +12,7 @@ cros_ec_lpcs-objs                     := cros_ec_lpc.o cros_ec_lpc_reg.o
>  cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC) += cros_ec_lpc_mec.o
>  obj-$(CONFIG_CROS_EC_LPC)              += cros_ec_lpcs.o
>  obj-$(CONFIG_CROS_EC_PROTO)            += cros_ec_proto.o
> +

Unrelated change.

>  obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT)   += cros_kbd_led_backlight.o
> +wilco_ec-objs                          := wilco_ec_mailbox.o
> +obj-$(CONFIG_WILCO_EC)                 += wilco_ec.o
> diff --git a/drivers/platform/chrome/wilco_ec.h b/drivers/platform/chrome/wilco_ec.h
> new file mode 100644
> index 000000000000..ba16fcff87c4
> --- /dev/null
> +++ b/drivers/platform/chrome/wilco_ec.h
> @@ -0,0 +1,97 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * wilco_ec - Chrome OS Wilco Embedded Controller

Remove 'wilco_ec -' and s/Chrome OS/ChromeOS/

> + *
> + * Copyright 2018 Google LLC
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.

You already added the SPDX-identifier so can remove the license boiler-plate.


> + */
> +
> +#ifndef WILCO_EC_H
> +#define WILCO_EC_H
> +
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +
> +#define WILCO_EC_FLAG_NO_RESPONSE      BIT(0) /* EC does not respond */
> +#define WILCO_EC_FLAG_EXTENDED_DATA    BIT(1) /* EC returns 256 data bytes */
> +#define WILCO_EC_FLAG_RAW_REQUEST      BIT(2) /* Do not trim request data */
> +#define WILCO_EC_FLAG_RAW_RESPONSE     BIT(3) /* Do not trim response data */
> +#define WILCO_EC_FLAG_RAW              (WILCO_EC_FLAG_RAW_REQUEST | \
> +                                        WILCO_EC_FLAG_RAW_RESPONSE)
> +
> +/**
> + * enum wilco_ec_msg_type - Message type to select a set of command codes.
> + * @WILCO_EC_MSG_LEGACY: Legacy EC messages for standard EC behavior.
> + * @WILCO_EC_MSG_PROPERTY: Get/Set/Sync EC controlled NVRAM property.
> + * @WILCO_EC_MSG_TELEMETRY: Telemetry data provided by the EC.
> + */
> +enum wilco_ec_msg_type {
> +       WILCO_EC_MSG_LEGACY = 0x00f0,
> +       WILCO_EC_MSG_PROPERTY = 0x00f2,
> +       WILCO_EC_MSG_TELEMETRY = 0x00f5,
> +};

I think that this is not used for now, remove and introduce it when you use it.

> +
> +/**
> + * struct wilco_ec_device - Wilco Embedded Controller handle.
> + * @dev: Device handle.
> + * @mailbox_lock: Mutex to ensure one mailbox command at a time.
> + * @io_command: I/O port for mailbox command.  Provided by ACPI.
> + * @io_data: I/O port for mailbox data.  Provided by ACPI.
> + * @io_packet: I/O port for mailbox packet data.  Provided by ACPI.
> + * @data_buffer: Buffer used for EC communication.  The same buffer
> + *               is used to hold the request and the response.
> + * @data_size: Size of the data buffer used for EC communication.
> + */
> +struct wilco_ec_device {
> +       struct device *dev;
> +       struct mutex mailbox_lock;
> +       struct resource *io_command;
> +       struct resource *io_data;
> +       struct resource *io_packet;
> +       void *data_buffer;
> +       size_t data_size;
> +};
> +
> +/**
> + * struct wilco_ec_message - Request and response message.
> + * @type: Mailbox message type.
> + * @flags: Message flags.
> + * @command: Mailbox command code.
> + * @result: Result code from the EC.  Non-zero indicates an error.
> + * @request_size: Number of bytes to send to the EC.
> + * @request_data: Buffer containing the request data.
> + * @response_size: Number of bytes expected from the EC.
> + *                 This is 32 by default and 256 if the flag
> + *                 is set for %WILCO_EC_FLAG_EXTENDED_DATA
> + * @response_data: Buffer containing the response data, should be
> + *                 response_size bytes and allocated by caller.
> + */
> +struct wilco_ec_message {
> +       enum wilco_ec_msg_type type;
> +       u8 flags;
> +       u8 command;
> +       u8 result;
> +       size_t request_size;
> +       void *request_data;
> +       size_t response_size;
> +       void *response_data;
> +};
> +
> +/**
> + * wilco_ec_mailbox() - Send request to the EC and receive the response.
> + * @ec: Wilco EC device.
> + * @msg: Wilco EC message.
> + *
> + * Return: Number of bytes received or negative error code on failure.
> + */
> +int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg);
> +
> +#endif /* WILCO_EC_H */
> diff --git a/drivers/platform/chrome/wilco_ec_mailbox.c b/drivers/platform/chrome/wilco_ec_mailbox.c
> new file mode 100644
> index 000000000000..6613c18c2a82
> --- /dev/null
> +++ b/drivers/platform/chrome/wilco_ec_mailbox.c
> @@ -0,0 +1,395 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * wilco_ec_mailbox - Mailbox interface for Wilco Embedded Controller

Remove 'wilco_ec_mailbox -'

> + *
> + * Copyright 2018 Google LLC
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.

Remove the license boiler-plate.

> + */
> +
> +/*
> + * The Wilco EC is similar to a typical Chrome OS embedded controller.
> + * It uses the same MEC based low-level communication and a similar
> + * protocol, but with some important differences.  The EC firmware does
> + * not support the same mailbox commands so it is not registered as a
> + * cros_ec device type.
> + *
> + * Most messages follow a standard format, but there are some exceptions
> + * and an interface is provided to do direct/raw transactions that do not
> + * make assumptions about byte placement.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/mfd/cros_ec_lpc_mec.h>

This file does not exists.

#include "cros_ec_lpc_mec.h" ?

> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include "wilco_ec.h"
> +
> +/* Version of mailbox interface */
> +#define EC_MAILBOX_VERSION             0
> +
> +/* Command to start mailbox transaction */
> +#define EC_MAILBOX_START_COMMAND       0xda
> +
> +/* Version of EC protocol */
> +#define EC_MAILBOX_PROTO_VERSION       3
> +
> +/* Normal commands have a maximum 32 bytes of data */
> +#define EC_MAILBOX_DATA_SIZE           32
> +
> +/* Extended commands have 256 bytes of response data */
> +#define EC_MAILBOX_DATA_SIZE_EXTENDED  256
> +
> +/* Number of header bytes to be counted as data bytes */
> +#define EC_MAILBOX_DATA_EXTRA          2
> +
> +/* Maximum timeout */
> +#define EC_MAILBOX_TIMEOUT             HZ
> +
> +/* EC response flags */
> +#define EC_CMDR_DATA           BIT(0)  /* Data ready for host to read */
> +#define EC_CMDR_PENDING                BIT(1)  /* Write pending to EC */
> +#define EC_CMDR_BUSY           BIT(2)  /* EC is busy processing a command */
> +#define EC_CMDR_CMD            BIT(3)  /* Last host write was a command */
> +
> +/**
> + * struct wilco_ec_request - Mailbox request message format.
> + * @struct_version: Should be %EC_MAILBOX_PROTO_VERSION
> + * @checksum: Sum of all bytes must be 0.
> + * @mailbox_id: Mailbox identifier, specifies the command set.
> + * @mailbox_version: Mailbox interface version %EC_MAILBOX_VERSION
> + * @reserved: Set to zero.
> + * @data_size: Length of request, data + last 2 bytes of the header.
> + * @command: Mailbox command code, unique for each mailbox_id set.
> + * @reserved_raw: Set to zero for most commands, but is used by
> + *                some command types and for raw commands.
> + */
> +struct wilco_ec_request {
> +       u8 struct_version;
> +       u8 checksum;
> +       u16 mailbox_id;
> +       u8 mailbox_version;
> +       u8 reserved;
> +       u16 data_size;
> +       u8 command;
> +       u8 reserved_raw;
> +} __packed;
> +
> +/**
> + * struct wilco_ec_response - Mailbox response message format.
> + * @struct_version: Should be %EC_MAILBOX_PROTO_VERSION
> + * @checksum: Sum of all bytes must be 0.
> + * @result: Result code from the EC.  Non-zero indicates an error.
> + * @data_size: Length of the response data buffer.
> + * @reserved: Set to zero.
> + * @mbox0: EC returned data at offset 0 is unused (always 0) so this byte
> + *         is treated as part of the header instead of the data.
> + * @data: Response data buffer.  Max size is %EC_MAILBOX_DATA_SIZE_EXTENDED.
> + */
> +struct wilco_ec_response {
> +       u8 struct_version;
> +       u8 checksum;
> +       u16 result;
> +       u16 data_size;
> +       u8 reserved[2];
> +       u8 mbox0;
> +       u8 data[0];
> +} __packed;
> +
> +/**
> + * wilco_ec_response_timed_out() - Wait for EC response.
> + * @ec: EC device.
> + *
> + * Return: true if EC timed out, false if EC did not time out.
> + */
> +static bool wilco_ec_response_timed_out(struct wilco_ec_device *ec)
> +{
> +       unsigned long timeout = jiffies + EC_MAILBOX_TIMEOUT;
> +
> +       usleep_range(200, 300);
> +       do {
> +               if (!(inb(ec->io_command->start) &
> +                     (EC_CMDR_PENDING | EC_CMDR_BUSY)))
> +                       return false;
> +               usleep_range(100, 200);
> +       } while (time_before(jiffies, timeout));
> +
> +       return true;
> +}
> +
> +/**
> + * wilco_ec_checksum() - Compute 8bit checksum over data range.
> + * @data: Data to checksum.
> + * @size: Number of bytes to checksum.
> + *
> + * Return: 8bit checksum of provided data.
> + */
> +static u8 wilco_ec_checksum(const void *data, size_t size)
> +{
> +       u8 *data_bytes = (u8 *)data;
> +       u8 checksum = 0;
> +       size_t i;
> +
> +       for (i = 0; i < size; i++)
> +               checksum += data_bytes[i];
> +
> +       return checksum;
> +}
> +
> +/**
> + * wilco_ec_prepare() - Prepare the request structure for the EC.
> + * @msg: EC message with request information.
> + * @rq: EC request structure to fill.
> + */
> +static void wilco_ec_prepare(struct wilco_ec_message *msg,
> +                            struct wilco_ec_request *rq)
> +{
> +       memset(rq, 0, sizeof(*rq));
> +
> +       /* Handle messages without trimming bytes from the request */
> +       if (msg->request_size && msg->flags & WILCO_EC_FLAG_RAW_REQUEST) {
> +               rq->reserved_raw = *(u8 *)msg->request_data;
> +               msg->request_size--;
> +               memmove(msg->request_data, msg->request_data + 1,
> +                       msg->request_size);
> +       }
> +
> +       /* Fill in request packet */
> +       rq->struct_version = EC_MAILBOX_PROTO_VERSION;
> +       rq->mailbox_id = msg->type;
> +       rq->mailbox_version = EC_MAILBOX_VERSION;
> +       rq->data_size = msg->request_size + EC_MAILBOX_DATA_EXTRA;
> +       rq->command = msg->command;
> +
> +       /* Checksum header and data */
> +       rq->checksum = wilco_ec_checksum(rq, sizeof(*rq));
> +       rq->checksum += wilco_ec_checksum(msg->request_data, msg->request_size);
> +       rq->checksum = -rq->checksum;
> +}
> +
> +/**
> + * wilco_ec_transfer() - Send EC request and receive EC response.
> + * @ec: EC device.
> + * @msg: EC message data for request and response.
> + *
> + * Return: 0 for success or negative error code on failure.
> + */
> +static int wilco_ec_transfer(struct wilco_ec_device *ec,
> +                            struct wilco_ec_message *msg)
> +{
> +       struct wilco_ec_request *rq;
> +       struct wilco_ec_response *rs;
> +       u8 checksum;
> +       size_t size;
> +
> +       /* Prepare request packet */
> +       rq = ec->data_buffer;
> +       wilco_ec_prepare(msg, rq);
> +
> +       /* Write request header */
> +       cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 0, sizeof(*rq), (u8 *)rq);
> +
> +       /* Write request data */
> +       cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, sizeof(*rq), msg->request_size,
> +                                msg->request_data);
> +
> +       /* Start the command */
> +       outb(EC_MAILBOX_START_COMMAND, ec->io_command->start);
> +
> +       /* Wait for it to complete */
> +       if (wilco_ec_response_timed_out(ec)) {
> +               dev_err(ec->dev, "response timed out\n");
> +               return -ETIMEDOUT;
> +       }
> +
> +       /* Some commands will put the EC into a state where it cannot respond */
> +       if (msg->flags & WILCO_EC_FLAG_NO_RESPONSE) {
> +               dev_dbg(ec->dev, "EC does not respond to this command\n");
> +               return 0;
> +       }
> +
> +       /* Check result */
> +       msg->result = inb(ec->io_data->start);
> +       if (msg->result) {
> +               dev_err(ec->dev, "bad response: 0x%02x\n", msg->result);
> +               return -EIO;
> +       }
> +
> +       if (msg->flags & WILCO_EC_FLAG_EXTENDED_DATA) {
> +               /* Extended commands return 256 bytes of data */
> +               size = EC_MAILBOX_DATA_SIZE_EXTENDED;
> +       } else {
> +               /* Otherwise EC commands return 32 bytes of data */
> +               size = EC_MAILBOX_DATA_SIZE;
> +       }
> +
> +       /* Read back response */
> +       rs = ec->data_buffer;
> +       checksum = cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 0,
> +                                           sizeof(*rs) + size, (u8 *)rs);
> +       if (checksum) {
> +               dev_err(ec->dev, "bad packet checksum 0x%02x\n", rs->checksum);
> +               return -EBADMSG;
> +       }
> +       msg->result = rs->result;
> +
> +       /* Check the returned data size, skipping the header */
> +       if (rs->data_size != size) {
> +               dev_err(ec->dev, "unexpected packet size (%u != %zu)",
> +                       rs->data_size, size);
> +               return -EMSGSIZE;
> +       }
> +
> +       /* Skip 1 response data byte unless specified */
> +       size = (msg->flags & WILCO_EC_FLAG_RAW_RESPONSE) ? 0 : 1;
> +
> +       if (msg->response_size > rs->data_size - size) {
> +               dev_err(ec->dev, "response data too short (%u > %zu)",

Fix the following compiler warning:

warning: format ‘%u’ expects argument of type ‘unsigned int’, but
argument 3 has type ‘size_t’ {aka ‘l
ong unsigned int’} [-Wformat=]

> +                       rs->data_size - size, msg->response_size);
> +               return -EMSGSIZE;
> +       }
> +
> +       /* Ignore response data bytes as requested */
> +       memcpy(msg->response_data, rs->data + size, msg->response_size);
> +
> +       /* Return actual amount of data received */
> +       return msg->response_size;
> +}
> +
> +int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg)
> +{
> +       size_t size = EC_MAILBOX_DATA_SIZE;
> +       int ret;
> +
> +       dev_dbg(ec->dev, "cmd=%02x type=%04x flags=%02x rslen=%d rqlen=%d\n",
> +               msg->command, msg->type, msg->flags, msg->response_size,
> +               msg->request_size);
> +

warning: format ‘%d’ expects argument of type ‘int’, but argument 7
has type ‘size_t’ {aka ‘long unsigned int’} [-Wformat=]

> +       if (msg->request_size > size) {
> +               dev_err(ec->dev, "provided request too large: %zu > %zu\n",
> +                       msg->request_size, size);
> +               return -EINVAL;
> +       }
> +
> +       /* Check for extended size on response data if flag is set */
> +       if (msg->flags & WILCO_EC_FLAG_EXTENDED_DATA)
> +               size = EC_MAILBOX_DATA_SIZE_EXTENDED;
> +
> +       if (msg->response_size > size) {
> +               dev_err(ec->dev, "expected response too large: %zu > %zu\n",
> +                       msg->response_size, size);
> +               return -EINVAL;
> +       }
> +       if (msg->request_size && !msg->request_data) {
> +               dev_err(ec->dev, "request data missing\n");
> +               return -EINVAL;
> +       }
> +       if (msg->response_size && !msg->response_data) {
> +               dev_err(ec->dev, "response data missing\n");
> +               return -EINVAL;
> +       }
> +
> +       mutex_lock(&ec->mailbox_lock);
> +
> +       ret = wilco_ec_transfer(ec, msg);
> +       if (ret >= 0 && msg->result)
> +               ret = -EBADMSG;
> +
> +       mutex_unlock(&ec->mailbox_lock);
> +
> +       return ret;
> +}
> +
> +static struct resource *wilco_get_resource(struct platform_device *pdev,
> +                                          int index)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct resource *res;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_IO, index);
> +       if (!res) {
> +               dev_err(dev, "couldn't find IO resource %d\n", index);
> +               return NULL;
> +       }
> +
> +       if (!devm_request_region(dev, res->start, resource_size(res),
> +                                dev_name(dev))) {
> +               dev_err(dev, "couldn't reserve IO region %d\n", index);
> +               return NULL;
> +       }
> +
> +       return res;
> +}
> +
> +static int wilco_ec_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct wilco_ec_device *ec;
> +
> +       ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
> +       if (!ec)
> +               return -ENOMEM;
> +       platform_set_drvdata(pdev, ec);
> +       ec->dev = dev;
> +       mutex_init(&ec->mailbox_lock);
> +
> +       /* Largest data buffer size requirement is extended data response */
> +       ec->data_size = sizeof(struct wilco_ec_response) +
> +               EC_MAILBOX_DATA_SIZE_EXTENDED;
> +       ec->data_buffer = devm_kzalloc(dev, ec->data_size, GFP_KERNEL);
> +       if (!ec->data_buffer)
> +               return -ENOMEM;
> +
> +       /* Prepare access to IO regions provided by ACPI */
> +       ec->io_data = wilco_get_resource(pdev, 0);      /* Host Data */
> +       ec->io_command = wilco_get_resource(pdev, 1);   /* Host Command */
> +       ec->io_packet = wilco_get_resource(pdev, 2);    /* MEC EMI */
> +       if (!ec->io_data || !ec->io_command || !ec->io_packet)
> +               return -ENODEV;
> +
> +       /* Initialize cros_ec register interface for communication */
> +       cros_ec_lpc_mec_init(ec->io_packet->start,
> +                            ec->io_packet->start + EC_MAILBOX_DATA_SIZE);
> +
> +       return 0;
> +}
> +
> +static int wilco_ec_remove(struct platform_device *pdev)
> +{
> +       /* Teardown cros_ec interface */
> +       cros_ec_lpc_mec_destroy();
> +
> +       return 0;
> +}
> +
> +static const struct acpi_device_id wilco_ec_acpi_device_ids[] = {
> +       { "GOOG000C", 0 },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(acpi, wilco_ec_acpi_device_ids);
> +
> +static struct platform_driver wilco_ec_driver = {
> +       .driver = {
> +               .name = "wilco_ec",
> +               .acpi_match_table = wilco_ec_acpi_device_ids,
> +       },
> +       .probe = wilco_ec_probe,
> +       .remove = wilco_ec_remove,
> +};
> +
> +module_platform_driver(wilco_ec_driver);
> +
> +MODULE_AUTHOR("Duncan Laurie <dlaurie@chromium.org>");
> +MODULE_LICENSE("GPL");

License mismatch, according to the SPDX identifier you want GPL v2 here.

> +MODULE_DESCRIPTION("Chrome OS Wilco Embedded Controller driver");

s/Chrome OS/ChromeOS/

> +MODULE_ALIAS("platform:wilco-ec");
> --
> 2.20.0.405.gbc1bbc6f85-goog
>

Thanks,
  Enric

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

* Re: [RFC PATCH 00/10] cros_ec: Add support for Wilco EC
  2018-12-15  0:18 [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Nick Crews
                   ` (9 preceding siblings ...)
  2018-12-15  0:18 ` [RFC PATCH 10/10] CHROMIUM: wilco_ec: Add binary telemetry attributes Nick Crews
@ 2018-12-17 16:15 ` Enric Balletbo Serra
  10 siblings, 0 replies; 16+ messages in thread
From: Enric Balletbo Serra @ 2018-12-17 16:15 UTC (permalink / raw)
  To: Nick Crews; +Cc: linux-kernel, Olof Johansson, Benson Leung, Guenter Roeck

Hi Nick,

Many thanks for sending this upstream. Adding Guenter as also might be
interested and some few questions regarding the driver structure.

Missatge de Nick Crews <ncrews@google.com> del dia ds., 15 de des.
2018 a les 1:21:
>
> The Chromebook named wilco contains a different Embedded Controller
> than the rest of the chromebook series, and thus the kernel requires
> a different driver than the already existing and generalized
> cros_ec_* drivers. Specifically, this driver adds support for getting
> and setting the RTC on the EC, adding a binary sysfs attribute
> that receives ACPI events from the EC, adding a binary sysfs
> attribute to request telemetry data from the EC (useful for enterprise
> applications), and adding normal sysfs attributes to get/set various
> other properties on the EC. The core of the communication with the EC
> is implemented in wilco_ec_mailbox.c, using a simple byte-level protocol
> with a checksum, transmitted over an eSPI bus. For debugging purposes,
> a raw attribute is also provided which can write/read arbitrary
> bytes to/from the eSPI bus.
>
> We attempted to adhere to the sysfs principles of "one piece of data per
> attribute" as much as possible, and mostly succeded. However, with the
> wilco_ec_adv_power.h attributes, which deal with scheduling power usage,
> we found it most elegant to bundle setting event times for an entire day
> into a single attribute, so at most you are using attributes formatted
> as "%d %d %d %d %d %d". With the telemetry attribute, we had to use a
> binary attribute, instead of the preferable human-readable ascii, in
> order to keep secure the information which is proprietary to the
> enterprise service provider. This opaque binary data will be read and
> sent using a proprietary daemon running on the OS. Finally, the
> "version" attribute returns a formatted result that looks something
> like:
> > cat /sys/bus/platform/devices/GOOG000C\:00/version
> Label        : 95.00.06
> SVN Revision : 5960a.06
> Model Number : 08;8
> Build Date   : 11/29/18
>
> The RTC driver is exposed as a standard RTC class driver with
> read/write functionality.
>
> For event notification, the Wilco EC can return extended events that
> are not handled by standard ACPI objects. These events can
> include hotkeys which map to standard functions like brightness
> controls, or information about EC controlled features like the
> charger or battery. These events are triggered with an
> ACPI Notify(0x90) and the event data buffer is read through an ACPI
> method provided by the BIOS which reads the event buffer from EC RAM.
> These events are then processed, with hotkey events being sent
> to the input subsystem and other events put into a queue which
> can be read by a userspace daemon via a sysfs attribute.
>
> The rest of the attributes are categorized as either "properties" or
> "legacy". "legacy" implies that the attribute existed on the EC before it
> was modified for ChromeOS, and "properties" implies that the attribute
> exposes functionality that was added to the EC specifically for
> ChromeOS. They are mostly boolean flags or percentages.
>
> A full thread of the development of these patches can be found at
> https://chromium-review.googlesource.com/c/1371034. This thread contains
> comments and revisions that could be helpful in understanding how the
> driver arrived at the state it is in now. The thread also contains some
> ChromeOS specific patches that actually enable the driver. If you want
> to test the patch yourself, you would have to install the ChromeOS SDK
> and cherry pick in these patches.
>
> I also wrote some integration tests using the Tast testing framework that
> ChromeOS uses. It would require a full ChromeOS SDK to actually run the
> tests, but the source of the tests, written in Go, are useful for
> understanding what the desired behavior is. You can view the tests here:
> https://chromium-review.googlesource.com/c/1372575
>
> This is still an initial version of the driver, and we are sending it
> upstream for comments now, so that we can incorporate any requested
> changes such that it eventually can be merged. Thank you for your
> comments!
>
>
> Duncan Laurie (6):
>   CHROMIUM: cros_ec: Remove cros_ec dependency in lpc_mec
>   CHROMIUM: wilco_ec: Add new driver for Wilco EC
>   CHROMIUM: wilco_ec: Add sysfs attributes
>   CHROMIUM: wilco_ec: Add support for raw commands in sysfs
>   CHROMIUM: wilco_ec: Add RTC class driver
>   CHROMIUM: wilco_ec: Add event handling
>
> Nick Crews (4):
>   CHROMIUM: wilco_ec: Move legacy attributes to separate file
>   CHROMIUM: wilco_ec: Add EC properties
>   CHROMIUM: wilco_ec: Add peakshift and adv_batt_charging
>   CHROMIUM: wilco_ec: Add binary telemetry attributes
>
>  drivers/platform/chrome/Kconfig               |  24 +-
>  drivers/platform/chrome/Makefile              |   9 +-
>  drivers/platform/chrome/cros_ec_lpc_mec.c     |  54 +-
>  drivers/platform/chrome/cros_ec_lpc_mec.h     |  45 +-
>  drivers/platform/chrome/cros_ec_lpc_reg.c     |  43 +-
>  drivers/platform/chrome/wilco_ec.h            | 180 ++++++
>  drivers/platform/chrome/wilco_ec_adv_power.c  | 533 ++++++++++++++++++
>  drivers/platform/chrome/wilco_ec_adv_power.h  | 193 +++++++
>  drivers/platform/chrome/wilco_ec_event.c      | 343 +++++++++++
>  drivers/platform/chrome/wilco_ec_legacy.c     | 204 +++++++
>  drivers/platform/chrome/wilco_ec_legacy.h     |  96 ++++
>  drivers/platform/chrome/wilco_ec_mailbox.c    | 427 ++++++++++++++
>  drivers/platform/chrome/wilco_ec_properties.c | 327 +++++++++++
>  drivers/platform/chrome/wilco_ec_properties.h | 163 ++++++
>  drivers/platform/chrome/wilco_ec_rtc.c        | 163 ++++++

Shouldn't this go to the RTC subsystem? Not sure if there are more
drivers that can go to a specific subsystem.

>  drivers/platform/chrome/wilco_ec_sysfs.c      | 253 +++++++++
>  drivers/platform/chrome/wilco_ec_sysfs_util.h |  47 ++
>  drivers/platform/chrome/wilco_ec_telemetry.c  |  66 +++
>  drivers/platform/chrome/wilco_ec_telemetry.h  |  42 ++
>  19 files changed, 3153 insertions(+), 59 deletions(-)

Wow, seems that this is going to be big, might make sense add a
wilco_ec subdirectory for all this?

>  create mode 100644 drivers/platform/chrome/wilco_ec.h
>  create mode 100644 drivers/platform/chrome/wilco_ec_adv_power.c
>  create mode 100644 drivers/platform/chrome/wilco_ec_adv_power.h
>  create mode 100644 drivers/platform/chrome/wilco_ec_event.c
>  create mode 100644 drivers/platform/chrome/wilco_ec_legacy.c
>  create mode 100644 drivers/platform/chrome/wilco_ec_legacy.h
>  create mode 100644 drivers/platform/chrome/wilco_ec_mailbox.c
>  create mode 100644 drivers/platform/chrome/wilco_ec_properties.c
>  create mode 100644 drivers/platform/chrome/wilco_ec_properties.h
>  create mode 100644 drivers/platform/chrome/wilco_ec_rtc.c
>  create mode 100644 drivers/platform/chrome/wilco_ec_sysfs.c
>  create mode 100644 drivers/platform/chrome/wilco_ec_sysfs_util.h
>  create mode 100644 drivers/platform/chrome/wilco_ec_telemetry.c
>  create mode 100644 drivers/platform/chrome/wilco_ec_telemetry.h
>
> --
> 2.20.0.405.gbc1bbc6f85-goog
>
Thanks,
  Enric

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

* Re: [RFC PATCH 03/10] CHROMIUM: wilco_ec: Add sysfs attributes
  2018-12-15  0:18 ` [RFC PATCH 03/10] CHROMIUM: wilco_ec: Add sysfs attributes Nick Crews
@ 2018-12-17 16:45   ` Enric Balletbo Serra
  2018-12-17 18:12     ` Guenter Roeck
  0 siblings, 1 reply; 16+ messages in thread
From: Enric Balletbo Serra @ 2018-12-17 16:45 UTC (permalink / raw)
  To: Nick Crews
  Cc: linux-kernel, Duncan Laurie, Olof Johansson, Benson Leung, Guenter Roeck

Hi Nick,

Many thanks for sending this upstream. Adding Guenter as also might be
interested, and some comments and questions below.

Missatge de Nick Crews <ncrews@google.com> del dia ds., 15 de des.
2018 a les 1:20:
>
> From: Duncan Laurie <dlaurie@google.com>
>
> Add some sample sysfs attributes for the Wilco EC that show how
> the mailbox interface works.
>
> > cat /sys/bus/platform/devices/GOOG000C\:00/version
> Label        : 99.99.99
> SVN Revision : 738ed.99
> Model Number : 08;8
> Build Date   : 08/30/18
>
> Signed-off-by: Duncan Laurie <dlaurie@google.com>
> Signed-off-by: Nick Crews <ncrews@google.com>
> ---
>
>  drivers/platform/chrome/Makefile           |   3 +-
>  drivers/platform/chrome/wilco_ec.h         |  14 +++
>  drivers/platform/chrome/wilco_ec_mailbox.c |  12 ++
>  drivers/platform/chrome/wilco_ec_sysfs.c   | 121 +++++++++++++++++++++

I am wondering if this should have its own driver (like we recently
did for cros_ec) or if it should be integrated into the wilco core.
Also, I am wondering if this should be attached to the cros_ec_class
or not ( /sys/class/chromeos/wilco_ec ? ). Guenter and Benson, any
opinion here?

Anyway, you should add the documentation of the sysfs entries, please.

>  4 files changed, 148 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/platform/chrome/wilco_ec_sysfs.c
>
> diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
> index b132ba5b3e3d..e8603bc5b095 100644
> --- a/drivers/platform/chrome/Makefile
> +++ b/drivers/platform/chrome/Makefile
> @@ -13,6 +13,5 @@ cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC)        += cros_ec_lpc_mec.o
>  obj-$(CONFIG_CROS_EC_LPC)              += cros_ec_lpcs.o
>  obj-$(CONFIG_CROS_EC_PROTO)            += cros_ec_proto.o
>
> -obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT)   += cros_kbd_led_backlight.o

I guess that you don't want to do this :)

> -wilco_ec-objs                          := wilco_ec_mailbox.o
> +wilco_ec-objs                          := wilco_ec_mailbox.o wilco_ec_sysfs.o
>  obj-$(CONFIG_WILCO_EC)                 += wilco_ec.o
> diff --git a/drivers/platform/chrome/wilco_ec.h b/drivers/platform/chrome/wilco_ec.h
> index ba16fcff87c4..699f4cf744dc 100644
> --- a/drivers/platform/chrome/wilco_ec.h
> +++ b/drivers/platform/chrome/wilco_ec.h
> @@ -94,4 +94,18 @@ struct wilco_ec_message {
>   */
>  int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg);
>
> +/**
> + * wilco_ec_sysfs_init() - Create sysfs attributes.
> + * @ec: EC device.
> + *
> + * Return: 0 for success or negative error code on failure.
> + */
> +int wilco_ec_sysfs_init(struct wilco_ec_device *ec);
> +
> +/**
> + * wilco_ec_sysfs_remove() - Remove sysfs attributes.
> + * @ec: EC device.
> + */
> +void wilco_ec_sysfs_remove(struct wilco_ec_device *ec);
> +
>  #endif /* WILCO_EC_H */
> diff --git a/drivers/platform/chrome/wilco_ec_mailbox.c b/drivers/platform/chrome/wilco_ec_mailbox.c
> index 6613c18c2a82..414ea0a8ad03 100644
> --- a/drivers/platform/chrome/wilco_ec_mailbox.c
> +++ b/drivers/platform/chrome/wilco_ec_mailbox.c
> @@ -361,11 +361,23 @@ static int wilco_ec_probe(struct platform_device *pdev)
>         cros_ec_lpc_mec_init(ec->io_packet->start,
>                              ec->io_packet->start + EC_MAILBOX_DATA_SIZE);
>
> +       /* Create sysfs attributes for userspace interaction */
> +       if (wilco_ec_sysfs_init(ec) < 0) {
> +               dev_err(dev, "Failed to create sysfs attributes\n");
> +               cros_ec_lpc_mec_destroy();
> +               return -ENODEV;
> +       }
> +
>         return 0;
>  }
>
>  static int wilco_ec_remove(struct platform_device *pdev)
>  {
> +       struct wilco_ec_device *ec = platform_get_drvdata(pdev);
> +
> +       /* Remove sysfs attributes */
> +       wilco_ec_sysfs_remove(ec);
> +
>         /* Teardown cros_ec interface */
>         cros_ec_lpc_mec_destroy();
>
> diff --git a/drivers/platform/chrome/wilco_ec_sysfs.c b/drivers/platform/chrome/wilco_ec_sysfs.c
> new file mode 100644
> index 000000000000..f9ae6cef6169
> --- /dev/null
> +++ b/drivers/platform/chrome/wilco_ec_sysfs.c
> @@ -0,0 +1,121 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * wilco_ec_sysfs - Sysfs attributes for Wilco Embedded Controller

Remove 'wilco_ec_sysfs -'.

> + *
> + * Copyright 2018 Google LLC
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.

Remove the license boiler-plate.

> + */
> +
> +#include <linux/ctype.h>
> +#include <linux/platform_device.h>
> +#include <linux/sysfs.h>
> +#include "wilco_ec.h"
> +
> +#define EC_COMMAND_EC_INFO             0x38
> +#define EC_INFO_SIZE                    9
> +#define EC_COMMAND_STEALTH_MODE                0xfc
> +
> +struct ec_info {
> +       u8 index;
> +       const char *label;
> +};
> +
> +static ssize_t wilco_ec_show_info(struct wilco_ec_device *ec, char *buf,
> +                                 ssize_t count, struct ec_info *info)
> +{
> +       char result[EC_INFO_SIZE];
> +       struct wilco_ec_message msg = {
> +               .type = WILCO_EC_MSG_LEGACY,
> +               .command = EC_COMMAND_EC_INFO,
> +               .request_data = &info->index,
> +               .request_size = sizeof(info->index),
> +               .response_data = result,
> +               .response_size = EC_INFO_SIZE,
> +       };
> +       int ret;
> +
> +       ret = wilco_ec_mailbox(ec, &msg);
> +       if (ret != EC_INFO_SIZE)
> +               return scnprintf(buf + count, PAGE_SIZE - count,
> +                                "%-12s : ERROR %d\n", info->label, ret);
> +
> +       return scnprintf(buf + count, PAGE_SIZE - count,
> +                        "%-12s : %s\n", info->label, result);
> +}
> +
> +static ssize_t version_show(struct device *dev,
> +                           struct device_attribute *attr, char *buf)
> +{
> +       struct wilco_ec_device *ec = dev_get_drvdata(dev);
> +       struct ec_info wilco_ec_info[] = {
> +               { 0, "Label" },
> +               { 1, "SVN Revision" },
> +               { 2, "Model Number" },
> +               { 3, "Build Date" },
> +               { 0xff, NULL },
> +       };
> +       struct ec_info *info = wilco_ec_info;
> +       ssize_t c = 0;
> +
> +       for (info = wilco_ec_info; info->label; info++)
> +               c += wilco_ec_show_info(ec, buf, c, info);
> +
> +       return c;
> +}
> +
> +static ssize_t stealth_mode_store(struct device *dev,
> +                                 struct device_attribute *attr,
> +                                 const char *buf, size_t count)
> +{
> +       struct wilco_ec_device *ec = dev_get_drvdata(dev);
> +       u8 param;
> +       struct wilco_ec_message msg = {
> +               .type = WILCO_EC_MSG_LEGACY,
> +               .command = EC_COMMAND_STEALTH_MODE,
> +               .request_data = &param,
> +               .request_size = sizeof(param),
> +       };
> +       int ret;
> +       bool enable;
> +
> +       ret = kstrtobool(buf, &enable);
> +       if (ret)
> +               return ret;
> +
> +       /* Invert input parameter, EC expects 0=on and 1=off */
> +       param = enable ? 0 : 1;
> +
> +       ret = wilco_ec_mailbox(ec, &msg);
> +       if (ret < 0)
> +               return ret;
> +
> +       return count;
> +}
> +
> +static DEVICE_ATTR_RO(version);
> +static DEVICE_ATTR_WO(stealth_mode);
> +
> +static struct attribute *wilco_ec_attrs[] = {
> +       &dev_attr_version.attr,
> +       &dev_attr_stealth_mode.attr,
> +       NULL
> +};
> +ATTRIBUTE_GROUPS(wilco_ec);
> +
> +int wilco_ec_sysfs_init(struct wilco_ec_device *ec)
> +{
> +       return sysfs_create_groups(&ec->dev->kobj, wilco_ec_groups);
> +}
> +
> +void wilco_ec_sysfs_remove(struct wilco_ec_device *ec)
> +{
> +       sysfs_remove_groups(&ec->dev->kobj, wilco_ec_groups);
> +}
> --
> 2.20.0.405.gbc1bbc6f85-goog
>

Thanks,
  Enric

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

* Re: [RFC PATCH 03/10] CHROMIUM: wilco_ec: Add sysfs attributes
  2018-12-17 16:45   ` Enric Balletbo Serra
@ 2018-12-17 18:12     ` Guenter Roeck
  0 siblings, 0 replies; 16+ messages in thread
From: Guenter Roeck @ 2018-12-17 18:12 UTC (permalink / raw)
  To: Enric Balletbo Serra
  Cc: Nick Crews, linux-kernel, Duncan Laurie, Olof Johansson,
	Benson Leung, Guenter Roeck

On Mon, Dec 17, 2018 at 8:46 AM Enric Balletbo Serra
<eballetbo@gmail.com> wrote:
>
> Hi Nick,
>
> Many thanks for sending this upstream. Adding Guenter as also might be
> interested, and some comments and questions below.
>
> Missatge de Nick Crews <ncrews@google.com> del dia ds., 15 de des.
> 2018 a les 1:20:
> >
> > From: Duncan Laurie <dlaurie@google.com>
> >
> > Add some sample sysfs attributes for the Wilco EC that show how
> > the mailbox interface works.
> >
> > > cat /sys/bus/platform/devices/GOOG000C\:00/version
> > Label        : 99.99.99
> > SVN Revision : 738ed.99
> > Model Number : 08;8
> > Build Date   : 08/30/18
> >
> > Signed-off-by: Duncan Laurie <dlaurie@google.com>
> > Signed-off-by: Nick Crews <ncrews@google.com>
> > ---
> >
> >  drivers/platform/chrome/Makefile           |   3 +-
> >  drivers/platform/chrome/wilco_ec.h         |  14 +++
> >  drivers/platform/chrome/wilco_ec_mailbox.c |  12 ++
> >  drivers/platform/chrome/wilco_ec_sysfs.c   | 121 +++++++++++++++++++++
>
> I am wondering if this should have its own driver (like we recently
> did for cros_ec) or if it should be integrated into the wilco core.
> Also, I am wondering if this should be attached to the cros_ec_class
> or not ( /sys/class/chromeos/wilco_ec ? ). Guenter and Benson, any
> opinion here?
>
I would agree.

> Anyway, you should add the documentation of the sysfs entries, please.
>
> >  4 files changed, 148 insertions(+), 2 deletions(-)
> >  create mode 100644 drivers/platform/chrome/wilco_ec_sysfs.c
> >
> > diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
> > index b132ba5b3e3d..e8603bc5b095 100644
> > --- a/drivers/platform/chrome/Makefile
> > +++ b/drivers/platform/chrome/Makefile
> > @@ -13,6 +13,5 @@ cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC)        += cros_ec_lpc_mec.o
> >  obj-$(CONFIG_CROS_EC_LPC)              += cros_ec_lpcs.o
> >  obj-$(CONFIG_CROS_EC_PROTO)            += cros_ec_proto.o
> >
> > -obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT)   += cros_kbd_led_backlight.o
>
> I guess that you don't want to do this :)
>
> > -wilco_ec-objs                          := wilco_ec_mailbox.o
> > +wilco_ec-objs                          := wilco_ec_mailbox.o wilco_ec_sysfs.o
> >  obj-$(CONFIG_WILCO_EC)                 += wilco_ec.o
> > diff --git a/drivers/platform/chrome/wilco_ec.h b/drivers/platform/chrome/wilco_ec.h
> > index ba16fcff87c4..699f4cf744dc 100644
> > --- a/drivers/platform/chrome/wilco_ec.h
> > +++ b/drivers/platform/chrome/wilco_ec.h
> > @@ -94,4 +94,18 @@ struct wilco_ec_message {
> >   */
> >  int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg);
> >
> > +/**
> > + * wilco_ec_sysfs_init() - Create sysfs attributes.
> > + * @ec: EC device.
> > + *
> > + * Return: 0 for success or negative error code on failure.
> > + */
> > +int wilco_ec_sysfs_init(struct wilco_ec_device *ec);
> > +
> > +/**
> > + * wilco_ec_sysfs_remove() - Remove sysfs attributes.
> > + * @ec: EC device.
> > + */
> > +void wilco_ec_sysfs_remove(struct wilco_ec_device *ec);
> > +
> >  #endif /* WILCO_EC_H */
> > diff --git a/drivers/platform/chrome/wilco_ec_mailbox.c b/drivers/platform/chrome/wilco_ec_mailbox.c
> > index 6613c18c2a82..414ea0a8ad03 100644
> > --- a/drivers/platform/chrome/wilco_ec_mailbox.c
> > +++ b/drivers/platform/chrome/wilco_ec_mailbox.c
> > @@ -361,11 +361,23 @@ static int wilco_ec_probe(struct platform_device *pdev)
> >         cros_ec_lpc_mec_init(ec->io_packet->start,
> >                              ec->io_packet->start + EC_MAILBOX_DATA_SIZE);
> >
> > +       /* Create sysfs attributes for userspace interaction */
> > +       if (wilco_ec_sysfs_init(ec) < 0) {
> > +               dev_err(dev, "Failed to create sysfs attributes\n");
> > +               cros_ec_lpc_mec_destroy();
> > +               return -ENODEV;
> > +       }
> > +
> >         return 0;
> >  }
> >
> >  static int wilco_ec_remove(struct platform_device *pdev)
> >  {
> > +       struct wilco_ec_device *ec = platform_get_drvdata(pdev);
> > +
> > +       /* Remove sysfs attributes */
> > +       wilco_ec_sysfs_remove(ec);
> > +
> >         /* Teardown cros_ec interface */
> >         cros_ec_lpc_mec_destroy();
> >
> > diff --git a/drivers/platform/chrome/wilco_ec_sysfs.c b/drivers/platform/chrome/wilco_ec_sysfs.c
> > new file mode 100644
> > index 000000000000..f9ae6cef6169
> > --- /dev/null
> > +++ b/drivers/platform/chrome/wilco_ec_sysfs.c
> > @@ -0,0 +1,121 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * wilco_ec_sysfs - Sysfs attributes for Wilco Embedded Controller
>
> Remove 'wilco_ec_sysfs -'.
>
> > + *
> > + * Copyright 2018 Google LLC
> > + *
> > + * This software is licensed under the terms of the GNU General Public
> > + * License version 2, as published by the Free Software Foundation, and
> > + * may be copied, distributed, and modified under those terms.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
>
> Remove the license boiler-plate.
>
> > + */
> > +
> > +#include <linux/ctype.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/sysfs.h>
> > +#include "wilco_ec.h"
> > +
> > +#define EC_COMMAND_EC_INFO             0x38
> > +#define EC_INFO_SIZE                    9
> > +#define EC_COMMAND_STEALTH_MODE                0xfc
> > +
> > +struct ec_info {
> > +       u8 index;
> > +       const char *label;
> > +};
> > +
> > +static ssize_t wilco_ec_show_info(struct wilco_ec_device *ec, char *buf,
> > +                                 ssize_t count, struct ec_info *info)
> > +{
> > +       char result[EC_INFO_SIZE];
> > +       struct wilco_ec_message msg = {
> > +               .type = WILCO_EC_MSG_LEGACY,
> > +               .command = EC_COMMAND_EC_INFO,
> > +               .request_data = &info->index,
> > +               .request_size = sizeof(info->index),
> > +               .response_data = result,
> > +               .response_size = EC_INFO_SIZE,
> > +       };
> > +       int ret;
> > +
> > +       ret = wilco_ec_mailbox(ec, &msg);
> > +       if (ret != EC_INFO_SIZE)
> > +               return scnprintf(buf + count, PAGE_SIZE - count,
> > +                                "%-12s : ERROR %d\n", info->label, ret);
> > +
> > +       return scnprintf(buf + count, PAGE_SIZE - count,
> > +                        "%-12s : %s\n", info->label, result);
> > +}
> > +
> > +static ssize_t version_show(struct device *dev,
> > +                           struct device_attribute *attr, char *buf)
> > +{
> > +       struct wilco_ec_device *ec = dev_get_drvdata(dev);
> > +       struct ec_info wilco_ec_info[] = {
> > +               { 0, "Label" },
> > +               { 1, "SVN Revision" },
> > +               { 2, "Model Number" },
> > +               { 3, "Build Date" },
> > +               { 0xff, NULL },
> > +       };

sysfs conventions ask for separate attributes for each of those.

> > +       struct ec_info *info = wilco_ec_info;
> > +       ssize_t c = 0;
> > +
> > +       for (info = wilco_ec_info; info->label; info++)
> > +               c += wilco_ec_show_info(ec, buf, c, info);
> > +
> > +       return c;
> > +}
> > +
> > +static ssize_t stealth_mode_store(struct device *dev,
> > +                                 struct device_attribute *attr,
> > +                                 const char *buf, size_t count)
> > +{
> > +       struct wilco_ec_device *ec = dev_get_drvdata(dev);
> > +       u8 param;
> > +       struct wilco_ec_message msg = {
> > +               .type = WILCO_EC_MSG_LEGACY,
> > +               .command = EC_COMMAND_STEALTH_MODE,
> > +               .request_data = &param,
> > +               .request_size = sizeof(param),
> > +       };
> > +       int ret;
> > +       bool enable;
> > +
> > +       ret = kstrtobool(buf, &enable);
> > +       if (ret)
> > +               return ret;
> > +
> > +       /* Invert input parameter, EC expects 0=on and 1=off */
> > +       param = enable ? 0 : 1;

Why not just !enable ?

> > +
> > +       ret = wilco_ec_mailbox(ec, &msg);
> > +       if (ret < 0)
> > +               return ret;
> > +
> > +       return count;
> > +}
> > +
> > +static DEVICE_ATTR_RO(version);
> > +static DEVICE_ATTR_WO(stealth_mode);
> > +
> > +static struct attribute *wilco_ec_attrs[] = {
> > +       &dev_attr_version.attr,
> > +       &dev_attr_stealth_mode.attr,
> > +       NULL
> > +};
> > +ATTRIBUTE_GROUPS(wilco_ec);
> > +
> > +int wilco_ec_sysfs_init(struct wilco_ec_device *ec)
> > +{
> > +       return sysfs_create_groups(&ec->dev->kobj, wilco_ec_groups);
> > +}
> > +
> > +void wilco_ec_sysfs_remove(struct wilco_ec_device *ec)
> > +{
> > +       sysfs_remove_groups(&ec->dev->kobj, wilco_ec_groups);
> > +}
> > --
> > 2.20.0.405.gbc1bbc6f85-goog
> >
>
> Thanks,
>   Enric

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

end of thread, other threads:[~2018-12-17 18:13 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-15  0:18 [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Nick Crews
2018-12-15  0:18 ` [RFC PATCH 01/10] CHROMIUM: cros_ec: Remove cros_ec dependency in lpc_mec Nick Crews
2018-12-17 15:50   ` Enric Balletbo Serra
2018-12-15  0:18 ` [RFC PATCH 02/10] CHROMIUM: wilco_ec: Add new driver for Wilco EC Nick Crews
2018-12-17 15:50   ` Enric Balletbo Serra
2018-12-15  0:18 ` [RFC PATCH 03/10] CHROMIUM: wilco_ec: Add sysfs attributes Nick Crews
2018-12-17 16:45   ` Enric Balletbo Serra
2018-12-17 18:12     ` Guenter Roeck
2018-12-15  0:18 ` [RFC PATCH 04/10] CHROMIUM: wilco_ec: Add support for raw commands in sysfs Nick Crews
2018-12-15  0:18 ` [RFC PATCH 05/10] CHROMIUM: wilco_ec: Add RTC class driver Nick Crews
2018-12-15  0:18 ` [RFC PATCH 06/10] CHROMIUM: wilco_ec: Add event handling Nick Crews
2018-12-15  0:18 ` [RFC PATCH 07/10] CHROMIUM: wilco_ec: Move legacy attributes to separate file Nick Crews
2018-12-15  0:18 ` [RFC PATCH 08/10] CHROMIUM: wilco_ec: Add EC properties Nick Crews
2018-12-15  0:18 ` [RFC PATCH 09/10] CHROMIUM: wilco_ec: Add peakshift and adv_batt_charging Nick Crews
2018-12-15  0:18 ` [RFC PATCH 10/10] CHROMIUM: wilco_ec: Add binary telemetry attributes Nick Crews
2018-12-17 16:15 ` [RFC PATCH 00/10] cros_ec: Add support for Wilco EC Enric Balletbo Serra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).