Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V5 02/10] ras: acpi/apei: cper: generic error data entry v3 per ACPI 6.1
From: Tyler Baicar @ 2016-11-21 22:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479767763-27532-1-git-send-email-tbaicar@codeaurora.org>

Currently when a RAS error is reported it is not timestamped.
The ACPI 6.1 spec adds the timestamp field to the generic error
data entry v3 structure. The timestamp of when the firmware
generated the error is now being reported.

Signed-off-by: Jonathan (Zhixiong) Zhang <zjzhang@codeaurora.org>
Signed-off-by: Richard Ruigrok <rruigrok@codeaurora.org>
Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
Signed-off-by: Naveen Kaje <nkaje@codeaurora.org>
---
 drivers/acpi/apei/ghes.c    | 14 +++++++---
 drivers/firmware/efi/cper.c | 62 +++++++++++++++++++++++++++++++++++----------
 include/acpi/ghes.h         | 10 ++++++++
 include/linux/cper.h        | 12 +++++++++
 4 files changed, 80 insertions(+), 18 deletions(-)

diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index b79abc5..9063d68 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -420,7 +420,8 @@ static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int
 	int flags = -1;
 	int sec_sev = ghes_severity(gdata->error_severity);
 	struct cper_sec_mem_err *mem_err;
-	mem_err = (struct cper_sec_mem_err *)(gdata + 1);
+
+	mem_err = acpi_hest_generic_data_payload(gdata);
 
 	if (!(mem_err->validation_bits & CPER_MEM_VALID_PA))
 		return;
@@ -450,14 +451,18 @@ static void ghes_do_proc(struct ghes *ghes,
 {
 	int sev, sec_sev;
 	struct acpi_hest_generic_data *gdata;
+	uuid_le sec_type;
 
 	sev = ghes_severity(estatus->error_severity);
 	apei_estatus_for_each_section(estatus, gdata) {
 		sec_sev = ghes_severity(gdata->error_severity);
-		if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
+		sec_type = *(uuid_le *)gdata->section_type;
+
+		if (!uuid_le_cmp(sec_type,
 				 CPER_SEC_PLATFORM_MEM)) {
 			struct cper_sec_mem_err *mem_err;
-			mem_err = (struct cper_sec_mem_err *)(gdata+1);
+
+			mem_err = acpi_hest_generic_data_payload(gdata);
 			ghes_edac_report_mem_error(ghes, sev, mem_err);
 
 			arch_apei_report_mem_error(sev, mem_err);
@@ -467,7 +472,8 @@ static void ghes_do_proc(struct ghes *ghes,
 		else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
 				      CPER_SEC_PCIE)) {
 			struct cper_sec_pcie *pcie_err;
-			pcie_err = (struct cper_sec_pcie *)(gdata+1);
+
+			pcie_err = acpi_hest_generic_data_payload(gdata);
 			if (sev == GHES_SEV_RECOVERABLE &&
 			    sec_sev == GHES_SEV_RECOVERABLE &&
 			    pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID &&
diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c
index d425374..7e2439e 100644
--- a/drivers/firmware/efi/cper.c
+++ b/drivers/firmware/efi/cper.c
@@ -32,6 +32,9 @@
 #include <linux/acpi.h>
 #include <linux/pci.h>
 #include <linux/aer.h>
+#include <linux/printk.h>
+#include <linux/bcd.h>
+#include <acpi/ghes.h>
 
 #define INDENT_SP	" "
 
@@ -386,13 +389,37 @@ static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
 	pfx, pcie->bridge.secondary_status, pcie->bridge.control);
 }
 
+static void cper_estatus_print_section_v300(const char *pfx,
+	const struct acpi_hest_generic_data_v300 *gdata)
+{
+	__u8 hour, min, sec, day, mon, year, century, *timestamp;
+
+	if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
+		timestamp = (__u8 *)&(gdata->time_stamp);
+		sec = bcd2bin(timestamp[0]);
+		min = bcd2bin(timestamp[1]);
+		hour = bcd2bin(timestamp[2]);
+		day = bcd2bin(timestamp[4]);
+		mon = bcd2bin(timestamp[5]);
+		year = bcd2bin(timestamp[6]);
+		century = bcd2bin(timestamp[7]);
+		printk("%stime: %7s %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
+			0x01 & *(timestamp + 3) ? "precise" : "", century,
+			year, mon, day, hour, min, sec);
+	}
+}
+
 static void cper_estatus_print_section(
-	const char *pfx, const struct acpi_hest_generic_data *gdata, int sec_no)
+	const char *pfx, struct acpi_hest_generic_data *gdata, int sec_no)
 {
 	uuid_le *sec_type = (uuid_le *)gdata->section_type;
 	__u16 severity;
 	char newpfx[64];
 
+	if (acpi_hest_generic_data_version(gdata) >= 3)
+		cper_estatus_print_section_v300(pfx,
+			(const struct acpi_hest_generic_data_v300 *)gdata);
+
 	severity = gdata->error_severity;
 	printk("%s""Error %d, type: %s\n", pfx, sec_no,
 	       cper_severity_str(severity));
@@ -403,14 +430,18 @@ static void cper_estatus_print_section(
 
 	snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP);
 	if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) {
-		struct cper_sec_proc_generic *proc_err = (void *)(gdata + 1);
+		struct cper_sec_proc_generic *proc_err;
+
+		proc_err = acpi_hest_generic_data_payload(gdata);
 		printk("%s""section_type: general processor error\n", newpfx);
 		if (gdata->error_data_length >= sizeof(*proc_err))
 			cper_print_proc_generic(newpfx, proc_err);
 		else
 			goto err_section_too_small;
 	} else if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) {
-		struct cper_sec_mem_err *mem_err = (void *)(gdata + 1);
+		struct cper_sec_mem_err *mem_err;
+
+		mem_err = acpi_hest_generic_data_payload(gdata);
 		printk("%s""section_type: memory error\n", newpfx);
 		if (gdata->error_data_length >=
 		    sizeof(struct cper_sec_mem_err_old))
@@ -419,7 +450,9 @@ static void cper_estatus_print_section(
 		else
 			goto err_section_too_small;
 	} else if (!uuid_le_cmp(*sec_type, CPER_SEC_PCIE)) {
-		struct cper_sec_pcie *pcie = (void *)(gdata + 1);
+		struct cper_sec_pcie *pcie;
+
+		pcie = acpi_hest_generic_data_payload(gdata);
 		printk("%s""section_type: PCIe error\n", newpfx);
 		if (gdata->error_data_length >= sizeof(*pcie))
 			cper_print_pcie(newpfx, pcie, gdata);
@@ -438,7 +471,7 @@ void cper_estatus_print(const char *pfx,
 			const struct acpi_hest_generic_status *estatus)
 {
 	struct acpi_hest_generic_data *gdata;
-	unsigned int data_len, gedata_len;
+	unsigned int data_len;
 	int sec_no = 0;
 	char newpfx[64];
 	__u16 severity;
@@ -451,12 +484,12 @@ void cper_estatus_print(const char *pfx,
 	printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
 	data_len = estatus->data_length;
 	gdata = (struct acpi_hest_generic_data *)(estatus + 1);
+
 	snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP);
-	while (data_len >= sizeof(*gdata)) {
-		gedata_len = gdata->error_data_length;
+
+	while (data_len >= acpi_hest_generic_data_size(gdata)) {
 		cper_estatus_print_section(newpfx, gdata, sec_no);
-		data_len -= gedata_len + sizeof(*gdata);
-		gdata = (void *)(gdata + 1) + gedata_len;
+		gdata = acpi_hest_generic_data_next(gdata);
 		sec_no++;
 	}
 }
@@ -486,12 +519,13 @@ int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
 		return rc;
 	data_len = estatus->data_length;
 	gdata = (struct acpi_hest_generic_data *)(estatus + 1);
-	while (data_len >= sizeof(*gdata)) {
-		gedata_len = gdata->error_data_length;
-		if (gedata_len > data_len - sizeof(*gdata))
+
+	while (data_len >= acpi_hest_generic_data_size(gdata)) {
+		gedata_len = acpi_hest_generic_data_error_length(gdata);
+		if (gedata_len > data_len - acpi_hest_generic_data_size(gdata))
 			return -EINVAL;
-		data_len -= gedata_len + sizeof(*gdata);
-		gdata = (void *)(gdata + 1) + gedata_len;
+		data_len -= gedata_len + acpi_hest_generic_data_size(gdata);
+		gdata = acpi_hest_generic_data_next(gdata);
 	}
 	if (data_len)
 		return -EINVAL;
diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h
index 68f088a..56b9679 100644
--- a/include/acpi/ghes.h
+++ b/include/acpi/ghes.h
@@ -73,3 +73,13 @@ static inline void ghes_edac_unregister(struct ghes *ghes)
 {
 }
 #endif
+
+#define acpi_hest_generic_data_version(gdata)			\
+	(gdata->revision >> 8)
+
+static inline void *acpi_hest_generic_data_payload(struct acpi_hest_generic_data *gdata)
+{
+	return acpi_hest_generic_data_version(gdata) >= 3 ?
+		(void *)(((struct acpi_hest_generic_data_v300 *)(gdata)) + 1) :
+		gdata + 1;
+}
diff --git a/include/linux/cper.h b/include/linux/cper.h
index dcacb1a..13ea41c 100644
--- a/include/linux/cper.h
+++ b/include/linux/cper.h
@@ -255,6 +255,18 @@ enum {
 
 #define CPER_PCIE_SLOT_SHIFT			3
 
+#define acpi_hest_generic_data_error_length(gdata)	\
+	(((struct acpi_hest_generic_data *)(gdata))->error_data_length)
+#define acpi_hest_generic_data_size(gdata)		\
+	((acpi_hest_generic_data_version(gdata) >= 3) ?	\
+	sizeof(struct acpi_hest_generic_data_v300) :	\
+	sizeof(struct acpi_hest_generic_data))
+#define acpi_hest_generic_data_record_size(gdata)	\
+	(acpi_hest_generic_data_size(gdata) +		\
+	acpi_hest_generic_data_error_length(gdata))
+#define acpi_hest_generic_data_next(gdata)		\
+	((void *)(gdata) + acpi_hest_generic_data_record_size(gdata))
+
 /*
  * All tables and structs must be byte-packed to match CPER
  * specification, since the tables are provided by the system BIOS
-- 
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH V5 03/10] efi: parse ARMv8 processor error
From: Tyler Baicar @ 2016-11-21 22:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479767763-27532-1-git-send-email-tbaicar@codeaurora.org>

Add support for ARMv8 Common Platform Error Record (CPER).
UEFI 2.6 specification adds support for ARMv8 specific
processor error information to be reported as part of the
CPER records. This provides more detail on for processor error logs.

Signed-off-by: Jonathan (Zhixiong) Zhang <zjzhang@codeaurora.org>
Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
Signed-off-by: Naveen Kaje <nkaje@codeaurora.org>
---
 drivers/firmware/efi/cper.c | 135 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/cper.h        |  72 +++++++++++++++++++++++
 2 files changed, 207 insertions(+)

diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c
index 7e2439e..004aa1b 100644
--- a/drivers/firmware/efi/cper.c
+++ b/drivers/firmware/efi/cper.c
@@ -110,12 +110,15 @@ void cper_print_bits(const char *pfx, unsigned int bits,
 static const char * const proc_type_strs[] = {
 	"IA32/X64",
 	"IA64",
+	"ARMv8",
 };
 
 static const char * const proc_isa_strs[] = {
 	"IA32",
 	"IA64",
 	"X64",
+	"ARM A32/T32",
+	"ARM A64",
 };
 
 static const char * const proc_error_type_strs[] = {
@@ -184,6 +187,129 @@ static void cper_print_proc_generic(const char *pfx,
 		printk("%s""IP: 0x%016llx\n", pfx, proc->ip);
 }
 
+static void cper_print_proc_armv8(const char *pfx,
+				  const struct cper_sec_proc_armv8 *proc)
+{
+	int i, len;
+	struct cper_armv8_err_info *err_info;
+	__u64 *qword = NULL;
+	char newpfx[64];
+
+	printk("%ssection length: %d\n", pfx, proc->section_length);
+	printk("%sMIDR: 0x%016llx\n", pfx, proc->midr);
+
+	len = proc->section_length - (sizeof(*proc) +
+		proc->err_info_num * (sizeof(*err_info)));
+	if (len < 0) {
+		printk("%ssection length is too small.\n", pfx);
+		printk("%sERR_INFO_NUM is %d.\n", pfx, proc->err_info_num);
+		return;
+	}
+
+	if (proc->validation_bits & CPER_ARMV8_VALID_MPIDR)
+		printk("%sMPIDR: 0x%016llx\n", pfx, proc->mpidr);
+	if (proc->validation_bits & CPER_ARMV8_VALID_AFFINITY_LEVEL)
+		printk("%serror affinity level: %d\n", pfx,
+			proc->affinity_level);
+	if (proc->validation_bits & CPER_ARMV8_VALID_RUNNING_STATE) {
+		printk("%srunning state: %d\n", pfx, proc->running_state);
+		printk("%sPSCI state: %d\n", pfx, proc->psci_state);
+	}
+
+	snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP);
+
+	err_info = (struct cper_armv8_err_info *)(proc + 1);
+	for (i = 0; i < proc->err_info_num; i++) {
+		printk("%sError info structure %d:\n", pfx, i);
+		printk("%sversion:%d\n", newpfx, err_info->version);
+		printk("%slength:%d\n", newpfx, err_info->length);
+		if (err_info->validation_bits &
+		    CPER_ARMV8_INFO_VALID_MULTI_ERR) {
+			if (err_info->multiple_error == 0)
+				printk("%ssingle error.\n", newpfx);
+			else if (err_info->multiple_error == 1)
+				printk("%smultiple errors.\n", newpfx);
+			else
+				printk("%smultiple errors count:%d.\n",
+				newpfx, err_info->multiple_error);
+		}
+		if (err_info->validation_bits & CPER_ARMV8_INFO_VALID_FLAGS) {
+			if (err_info->flags & CPER_ARMV8_INFO_FLAGS_FIRST)
+				printk("%sfirst error captured.\n", newpfx);
+			if (err_info->flags & CPER_ARMV8_INFO_FLAGS_LAST)
+				printk("%slast error captured.\n", newpfx);
+			if (err_info->flags & CPER_ARMV8_INFO_FLAGS_PROPAGATED)
+				printk("%spropagated error captured.\n",
+				       newpfx);
+		}
+		printk("%serror_type: %d, %s\n", newpfx, err_info->type,
+			err_info->type < ARRAY_SIZE(proc_error_type_strs) ?
+			proc_error_type_strs[err_info->type] : "unknown");
+		printk("%serror_info: 0x%016llx\n", newpfx,
+		       err_info->error_info);
+		if (err_info->validation_bits & CPER_ARMV8_INFO_VALID_VIRT_ADDR)
+			printk("%svirtual fault address: 0x%016llx\n",
+				newpfx, err_info->virt_fault_addr);
+		if (err_info->validation_bits &
+		    CPER_ARMV8_INFO_VALID_PHYSICAL_ADDR)
+			printk("%sphysical fault address: 0x%016llx\n",
+				newpfx, err_info->physical_fault_addr);
+		err_info += 1;
+	}
+
+	if (len < sizeof(*qword) && proc->context_info_num > 0) {
+		printk("%ssection length is too small.\n", pfx);
+		printk("%sCTX_INFO_NUM is %d.\n", pfx, proc->context_info_num);
+		return;
+	}
+	for (i = 0; i < proc->context_info_num; i++) {
+		qword = (__u64 *)err_info;
+		printk("%sProcessor context info structure %d:\n", pfx, i);
+		printk("%sException level %d.\n", newpfx,
+		       (int)((*qword & CPER_ARMV8_CTX_EL_MASK)
+				>> CPER_ARMV8_CTX_EL_SHIFT));
+		printk("%sSecure bit: %d.\n", newpfx,
+		       (int)((*qword & CPER_ARMV8_CTX_NS_MASK)
+				>> CPER_ARMV8_CTX_NS_SHIFT));
+		if ((*qword & CPER_ARMV8_CTX_TYPE_MASK) == 0) {
+			if (len < CPER_AARCH32_CTX_LEN) {
+				printk("%ssection length is too small.\n", pfx);
+				printk("%sremaining length is %d.\n", pfx, len);
+				return;
+			}
+			printk("%sAArch32 execution context.\n", newpfx);
+			qword++;
+			print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4,
+				qword, CPER_AARCH32_CTX_LEN - sizeof(*qword),
+				0);
+			len -= CPER_AARCH32_CTX_LEN;
+		} else if ((*qword & CPER_ARMV8_CTX_TYPE_MASK) == 1) {
+			if (len < CPER_AARCH64_CTX_LEN) {
+				printk("%ssection length is too small.\n", pfx);
+				printk("%sremaining length is %d.\n", pfx, len);
+				return;
+			}
+			printk("%sAArch64 execution context.\n", newpfx);
+			qword++;
+			print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4,
+				qword, CPER_AARCH64_CTX_LEN - sizeof(*qword),
+				0);
+			len -= CPER_AARCH64_CTX_LEN;
+		} else {
+			printk("%scontext type is incorrect 0x%016llx.\n",
+			pfx, *qword);
+			return;
+		}
+	}
+
+	if (len > 0) {
+		printk("%sVendor specific error info has %d bytes.\n", pfx,
+		       len);
+		print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, qword, len,
+			0);
+	}
+}
+
 static const char * const mem_err_type_strs[] = {
 	"unknown",
 	"no error",
@@ -458,6 +584,15 @@ static void cper_estatus_print_section(
 			cper_print_pcie(newpfx, pcie, gdata);
 		else
 			goto err_section_too_small;
+	} else if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_ARMV8)) {
+		struct cper_sec_proc_armv8 *armv8_err;
+
+		armv8_err = acpi_hest_generic_data_payload(gdata);
+		printk("%ssection_type: ARMv8 processor error\n", newpfx);
+		if (gdata->error_data_length >= sizeof(*armv8_err))
+			cper_print_proc_armv8(newpfx, armv8_err);
+		else
+			goto err_section_too_small;
 	} else
 		printk("%s""section type: unknown, %pUl\n", newpfx, sec_type);
 
diff --git a/include/linux/cper.h b/include/linux/cper.h
index 13ea41c..2a9d553 100644
--- a/include/linux/cper.h
+++ b/include/linux/cper.h
@@ -162,6 +162,11 @@ enum {
  * corrective action before the data is consumed
  */
 #define CPER_SEC_LATENT_ERROR			0x0020
+/*
+ * If set, the section contains an error that is propagated. The error
+ * did not originate from the hardware associated with this section.
+ */
+#define CPER_SEC_PROPAGATED			0x0040
 
 /*
  * Section type definitions, used in section_type field in struct
@@ -180,6 +185,10 @@ enum {
 #define CPER_SEC_PROC_IPF						\
 	UUID_LE(0xE429FAF1, 0x3CB7, 0x11D4, 0x0B, 0xCA, 0x07, 0x00,	\
 		0x80, 0xC7, 0x3C, 0x88, 0x81)
+/* Processor Specific: ARMv8 */
+#define CPER_SEC_PROC_ARMV8						\
+	UUID_LE(0xE19E3D16, 0xBC11, 0x11E4, 0x9C, 0xAA, 0xC2, 0x05,	\
+		0x1D, 0x5D, 0x46, 0xB0)
 /* Platform Memory */
 #define CPER_SEC_PLATFORM_MEM						\
 	UUID_LE(0xA5BC1114, 0x6F64, 0x4EDE, 0xB8, 0x63, 0x3E, 0x83,	\
@@ -255,6 +264,34 @@ enum {
 
 #define CPER_PCIE_SLOT_SHIFT			3
 
+#define CPER_ARMV8_ERR_INFO_NUM_MASK		0x00000000000000FF
+#define CPER_ARMV8_CTX_INFO_NUM_MASK		0x0000000000FFFF00
+#define CPER_ARMV8_CTX_INFO_NUM_SHIFT		8
+
+#define CPER_ARMV8_VALID_MPIDR			0x00000001
+#define CPER_ARMV8_VALID_AFFINITY_LEVEL		0x00000002
+#define CPER_ARMV8_VALID_RUNNING_STATE		0x00000004
+#define CPER_ARMV8_VALID_VENDOR_INFO		0x00000008
+
+#define CPER_ARMV8_INFO_VALID_MULTI_ERR		0x0001
+#define CPER_ARMV8_INFO_VALID_FLAGS		0x0002
+#define CPER_ARMV8_INFO_VALID_ERR_INFO		0x0004
+#define CPER_ARMV8_INFO_VALID_VIRT_ADDR		0x0008
+#define CPER_ARMV8_INFO_VALID_PHYSICAL_ADDR	0x0010
+
+#define CPER_ARMV8_INFO_FLAGS_FIRST		0x0001
+#define CPER_ARMV8_INFO_FLAGS_LAST		0x0002
+#define CPER_ARMV8_INFO_FLAGS_PROPAGATED	0x0004
+
+#define CPER_AARCH64_CTX_LEN			368
+#define CPER_AARCH32_CTX_LEN			256
+
+#define CPER_ARMV8_CTX_TYPE_MASK		0x000000000000000F
+#define CPER_ARMV8_CTX_EL_MASK			0x0000000000000070
+#define CPER_ARMV8_CTX_NS_MASK			0x0000000000000080
+#define CPER_ARMV8_CTX_EL_SHIFT			4
+#define CPER_ARMV8_CTX_NS_SHIFT			7
+
 #define acpi_hest_generic_data_error_length(gdata)	\
 	(((struct acpi_hest_generic_data *)(gdata))->error_data_length)
 #define acpi_hest_generic_data_size(gdata)		\
@@ -352,6 +389,41 @@ struct cper_ia_proc_ctx {
 	__u64	mm_reg_addr;
 };
 
+/* ARMv8 Processor Error Section */
+struct cper_sec_proc_armv8 {
+	__u32	validation_bits;
+	__u16	err_info_num; /* Number of Processor Error Info */
+	__u16	context_info_num; /* Number of Processor Context Info Records*/
+	__u32	section_length;
+	__u8	affinity_level;
+	__u8	reserved[3];	/* must be zero */
+	__u64	mpidr;
+	__u64	midr;
+	__u32	running_state; /* Bit 0 set - Processor running. PSCI = 0 */
+	__u32	psci_state;
+};
+
+/* ARMv8 Processor Error Information Structure */
+struct cper_armv8_err_info {
+	__u8	version;
+	__u8	length;
+	__u16	validation_bits;
+	__u8	type;
+	__u16	multiple_error;
+	__u8	flags;
+	__u64	error_info;
+	__u64	virt_fault_addr;
+	__u64	physical_fault_addr;
+};
+
+/* ARMv8 AARCH64 Processor Context Information Structure */
+struct cper_armv8_aarch64_ctx {
+	__u8	type_el_ns;
+	__u8	reserved[7];	/* must be zero */
+	__u8	gpr[288];
+	__u8	spr[68];
+};
+
 /* Old Memory Error Section UEFI 2.1, 2.2 */
 struct cper_sec_mem_err_old {
 	__u64	validation_bits;
-- 
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH V5 04/10] arm64: exception: handle Synchronous External Abort
From: Tyler Baicar @ 2016-11-21 22:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479767763-27532-1-git-send-email-tbaicar@codeaurora.org>

SEA exceptions are often caused by an uncorrected hardware
error, and are handled when data abort and instruction abort
exception classes have specific values for their Fault Status
Code.
When SEA occurs, before killing the process, go through
the handlers registered in the notification list.
Update fault_info[] with specific SEA faults so that the
new SEA handler is used.

Signed-off-by: Jonathan (Zhixiong) Zhang <zjzhang@codeaurora.org>
Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
Signed-off-by: Naveen Kaje <nkaje@codeaurora.org>
---
 arch/arm64/include/asm/system_misc.h | 13 ++++++++
 arch/arm64/mm/fault.c                | 58 +++++++++++++++++++++++++++++-------
 2 files changed, 61 insertions(+), 10 deletions(-)

diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h
index 57f110b..9040e1d 100644
--- a/arch/arm64/include/asm/system_misc.h
+++ b/arch/arm64/include/asm/system_misc.h
@@ -64,4 +64,17 @@ extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
 
 #endif	/* __ASSEMBLY__ */
 
+/*
+ * The functions below are used to register and unregister callbacks
+ * that are to be invoked when a Synchronous External Abort (SEA)
+ * occurs. An SEA is raised by certain fault status codes that have
+ * either data or instruction abort as the exception class, and
+ * callbacks may be registered to parse or handle such hardware errors.
+ *
+ * Registered callbacks are run in an interrupt/atomic context. They
+ * are not allowed to block or sleep.
+ */
+int register_synchronous_ext_abort_notifier(struct notifier_block *nb);
+void unregister_synchronous_ext_abort_notifier(struct notifier_block *nb);
+
 #endif	/* __ASM_SYSTEM_MISC_H */
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 05d2bd7..fcc49f1 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -39,6 +39,22 @@
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
 
+/*
+ * GHES SEA handler code may register a notifier call here to
+ * handle HW error record passed from platform.
+ */
+static ATOMIC_NOTIFIER_HEAD(sea_handler_chain);
+
+int register_synchronous_ext_abort_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&sea_handler_chain, nb);
+}
+
+void unregister_synchronous_ext_abort_notifier(struct notifier_block *nb)
+{
+	atomic_notifier_chain_unregister(&sea_handler_chain, nb);
+}
+
 static const char *fault_name(unsigned int esr);
 
 #ifdef CONFIG_KPROBES
@@ -480,6 +496,28 @@ static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs)
 	return 1;
 }
 
+/*
+ * This abort handler deals with Synchronous External Abort.
+ * It calls notifiers, and then returns "fault".
+ */
+static int do_synch_ext_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs)
+{
+	struct siginfo info;
+
+	atomic_notifier_call_chain(&sea_handler_chain, 0, NULL);
+
+	pr_err("Synchronous External Abort: %s (0x%08x) at 0x%016lx\n",
+		 fault_name(esr), esr, addr);
+
+	info.si_signo = SIGBUS;
+	info.si_errno = 0;
+	info.si_code  = 0;
+	info.si_addr  = (void __user *)addr;
+	arm64_notify_die("", regs, &info, esr);
+
+	return 0;
+}
+
 static const struct fault_info {
 	int	(*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs);
 	int	sig;
@@ -502,22 +540,22 @@ static const struct fault_info {
 	{ do_page_fault,	SIGSEGV, SEGV_ACCERR,	"level 1 permission fault"	},
 	{ do_page_fault,	SIGSEGV, SEGV_ACCERR,	"level 2 permission fault"	},
 	{ do_page_fault,	SIGSEGV, SEGV_ACCERR,	"level 3 permission fault"	},
-	{ do_bad,		SIGBUS,  0,		"synchronous external abort"	},
+	{ do_synch_ext_abort,	SIGBUS,  0,		"synchronous external abort"	},
 	{ do_bad,		SIGBUS,  0,		"unknown 17"			},
 	{ do_bad,		SIGBUS,  0,		"unknown 18"			},
 	{ do_bad,		SIGBUS,  0,		"unknown 19"			},
-	{ do_bad,		SIGBUS,  0,		"synchronous abort (translation table walk)" },
-	{ do_bad,		SIGBUS,  0,		"synchronous abort (translation table walk)" },
-	{ do_bad,		SIGBUS,  0,		"synchronous abort (translation table walk)" },
-	{ do_bad,		SIGBUS,  0,		"synchronous abort (translation table walk)" },
-	{ do_bad,		SIGBUS,  0,		"synchronous parity error"	},
+	{ do_synch_ext_abort,	SIGBUS,  0,		"level 0 SEA (trans tbl walk)"	},
+	{ do_synch_ext_abort,	SIGBUS,  0,		"level 1 SEA (trans tbl walk)"	},
+	{ do_synch_ext_abort,	SIGBUS,  0,		"level 2 SEA (trans tbl walk)"	},
+	{ do_synch_ext_abort,	SIGBUS,  0,		"level 3 SEA (trans tbl walk)"	},
+	{ do_synch_ext_abort,	SIGBUS,  0,		"synchronous parity or ECC err" },
 	{ do_bad,		SIGBUS,  0,		"unknown 25"			},
 	{ do_bad,		SIGBUS,  0,		"unknown 26"			},
 	{ do_bad,		SIGBUS,  0,		"unknown 27"			},
-	{ do_bad,		SIGBUS,  0,		"synchronous parity error (translation table walk)" },
-	{ do_bad,		SIGBUS,  0,		"synchronous parity error (translation table walk)" },
-	{ do_bad,		SIGBUS,  0,		"synchronous parity error (translation table walk)" },
-	{ do_bad,		SIGBUS,  0,		"synchronous parity error (translation table walk)" },
+	{ do_synch_ext_abort,	SIGBUS,  0,		"level 0 synch parity error"	},
+	{ do_synch_ext_abort,	SIGBUS,  0,		"level 1 synch parity error"	},
+	{ do_synch_ext_abort,	SIGBUS,  0,		"level 2 synch parity error"	},
+	{ do_synch_ext_abort,	SIGBUS,  0,		"level 3 synch parity error"	},
 	{ do_bad,		SIGBUS,  0,		"unknown 32"			},
 	{ do_alignment_fault,	SIGBUS,  BUS_ADRALN,	"alignment fault"		},
 	{ do_bad,		SIGBUS,  0,		"unknown 34"			},
-- 
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH V5 05/10] acpi: apei: handle SEA notification type for ARMv8
From: Tyler Baicar @ 2016-11-21 22:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479767763-27532-1-git-send-email-tbaicar@codeaurora.org>

ARM APEI extension proposal added SEA (Synchrounous External
Abort) notification type for ARMv8.
Add a new GHES error source handling function for SEA. If an error
source's notification type is SEA, then this function can be registered
into the SEA exception handler. That way GHES will parse and report
SEA exceptions when they occur.

Signed-off-by: Jonathan (Zhixiong) Zhang <zjzhang@codeaurora.org>
Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
Signed-off-by: Naveen Kaje <nkaje@codeaurora.org>
---
 arch/arm64/Kconfig        |  1 +
 drivers/acpi/apei/Kconfig | 14 ++++++++
 drivers/acpi/apei/ghes.c  | 83 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 98 insertions(+)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index b380c87..ae34349 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -53,6 +53,7 @@ config ARM64
 	select HANDLE_DOMAIN_IRQ
 	select HARDIRQS_SW_RESEND
 	select HAVE_ACPI_APEI if (ACPI && EFI)
+	select HAVE_ACPI_APEI_SEA if (ACPI && EFI)
 	select HAVE_ALIGNED_STRUCT_PAGE if SLUB
 	select HAVE_ARCH_AUDITSYSCALL
 	select HAVE_ARCH_BITREVERSE
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
index b0140c8..3786ff1 100644
--- a/drivers/acpi/apei/Kconfig
+++ b/drivers/acpi/apei/Kconfig
@@ -4,6 +4,20 @@ config HAVE_ACPI_APEI
 config HAVE_ACPI_APEI_NMI
 	bool
 
+config HAVE_ACPI_APEI_SEA
+	bool "APEI Synchronous External Abort logging/recovering support"
+	depends on ARM64
+	help
+	  This option should be enabled if the system supports
+	  firmware first handling of SEA (Synchronous External Abort).
+	  SEA happens with certain faults of data abort or instruction
+	  abort synchronous exceptions on ARMv8 systems. If a system
+	  supports firmware first handling of SEA, the platform analyzes
+	  and handles hardware error notifications with SEA, and it may then
+	  form a HW error record for the OS to parse and handle. This
+	  option allows the OS to look for such HW error record, and
+	  take appropriate action.
+
 config ACPI_APEI
 	bool "ACPI Platform Error Interface (APEI)"
 	select MISC_FILESYSTEMS
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 9063d68..839a0e2 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -50,6 +50,10 @@
 #include <acpi/apei.h>
 #include <asm/tlbflush.h>
 
+#ifdef CONFIG_HAVE_ACPI_APEI_SEA
+#include <asm/system_misc.h>
+#endif
+
 #include "apei-internal.h"
 
 #define GHES_PFX	"GHES: "
@@ -770,6 +774,62 @@ static struct notifier_block ghes_notifier_sci = {
 	.notifier_call = ghes_notify_sci,
 };
 
+#ifdef CONFIG_HAVE_ACPI_APEI_SEA
+static LIST_HEAD(ghes_sea);
+
+static int ghes_notify_sea(struct notifier_block *this,
+				  unsigned long event, void *data)
+{
+	struct ghes *ghes;
+	int ret = NOTIFY_DONE;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ghes, &ghes_sea, list) {
+		if (!ghes_proc(ghes))
+			ret = NOTIFY_OK;
+	}
+	rcu_read_unlock();
+
+	return ret;
+}
+
+static struct notifier_block ghes_notifier_sea = {
+	.notifier_call = ghes_notify_sea,
+};
+
+static int ghes_sea_add(struct ghes *ghes)
+{
+	mutex_lock(&ghes_list_mutex);
+	if (list_empty(&ghes_sea))
+		register_synchronous_ext_abort_notifier(&ghes_notifier_sea);
+	list_add_rcu(&ghes->list, &ghes_sea);
+	mutex_unlock(&ghes_list_mutex);
+	return 0;
+}
+
+static void ghes_sea_remove(struct ghes *ghes)
+{
+	mutex_lock(&ghes_list_mutex);
+	list_del_rcu(&ghes->list);
+	if (list_empty(&ghes_sea))
+		unregister_synchronous_ext_abort_notifier(&ghes_notifier_sea);
+	mutex_unlock(&ghes_list_mutex);
+}
+#else /* CONFIG_HAVE_ACPI_APEI_SEA */
+static inline int ghes_sea_add(struct ghes *ghes)
+{
+	pr_err(GHES_PFX "ID: %d, trying to add SEA notification which is not supported\n",
+	       ghes->generic->header.source_id);
+	return -ENOTSUPP;
+}
+
+static inline void ghes_sea_remove(struct ghes *ghes)
+{
+	pr_err(GHES_PFX "ID: %d, trying to remove SEA notification which is not supported\n",
+	       ghes->generic->header.source_id);
+}
+#endif /* CONFIG_HAVE_ACPI_APEI_SEA */
+
 #ifdef CONFIG_HAVE_ACPI_APEI_NMI
 /*
  * printk is not safe in NMI context.  So in NMI handler, we allocate
@@ -1014,6 +1074,14 @@ static int ghes_probe(struct platform_device *ghes_dev)
 	case ACPI_HEST_NOTIFY_EXTERNAL:
 	case ACPI_HEST_NOTIFY_SCI:
 		break;
+	case ACPI_HEST_NOTIFY_SEA:
+		if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_SEA)) {
+			pr_warn(GHES_PFX "Generic hardware error source: %d notified via SEA is not supported\n",
+				generic->header.source_id);
+			rc = -ENOTSUPP;
+			goto err;
+		}
+		break;
 	case ACPI_HEST_NOTIFY_NMI:
 		if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_NMI)) {
 			pr_warn(GHES_PFX "Generic hardware error source: %d notified via NMI interrupt is not supported!\n",
@@ -1025,6 +1093,13 @@ static int ghes_probe(struct platform_device *ghes_dev)
 		pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n",
 			   generic->header.source_id);
 		goto err;
+	case ACPI_HEST_NOTIFY_GPIO:
+	case ACPI_HEST_NOTIFY_SEI:
+	case ACPI_HEST_NOTIFY_GSIV:
+		pr_warn(GHES_PFX "Generic hardware error source: %d notified via notification type %u is not supported\n",
+			generic->header.source_id, generic->header.source_id);
+		rc = -ENOTSUPP;
+		goto err;
 	default:
 		pr_warning(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n",
 			   generic->notify.type, generic->header.source_id);
@@ -1079,6 +1154,11 @@ static int ghes_probe(struct platform_device *ghes_dev)
 		list_add_rcu(&ghes->list, &ghes_sci);
 		mutex_unlock(&ghes_list_mutex);
 		break;
+	case ACPI_HEST_NOTIFY_SEA:
+		rc = ghes_sea_add(ghes);
+		if (rc)
+			goto err_edac_unreg;
+		break;
 	case ACPI_HEST_NOTIFY_NMI:
 		ghes_nmi_add(ghes);
 		break;
@@ -1121,6 +1201,9 @@ static int ghes_remove(struct platform_device *ghes_dev)
 			unregister_acpi_hed_notifier(&ghes_notifier_sci);
 		mutex_unlock(&ghes_list_mutex);
 		break;
+	case ACPI_HEST_NOTIFY_SEA:
+		ghes_sea_remove(ghes);
+		break;
 	case ACPI_HEST_NOTIFY_NMI:
 		ghes_nmi_remove(ghes);
 		break;
-- 
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH V5 06/10] acpi: apei: panic OS with fatal error status block
From: Tyler Baicar @ 2016-11-21 22:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479767763-27532-1-git-send-email-tbaicar@codeaurora.org>

From: "Jonathan (Zhixiong) Zhang" <zjzhang@codeaurora.org>

Even if an error status block's severity is fatal, the kernel does not
honor the severity level and panic.

With the firmware first model, the platform could inform the OS about a
fatal hardware error through the non-NMI GHES notification type. The OS
should panic when a hardware error record is received with this
severity.

Call panic() after CPER data in error status block is printed if
severity is fatal, before each error section is handled.

Signed-off-by: Jonathan (Zhixiong) Zhang <zjzhang@codeaurora.org>
Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
---
 drivers/acpi/apei/ghes.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 839a0e2..28f801c 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -141,6 +141,8 @@ static unsigned long ghes_estatus_pool_size_request;
 static struct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
 static atomic_t ghes_estatus_cache_alloced;
 
+static int ghes_panic_timeout __read_mostly = 30;
+
 static int ghes_ioremap_init(void)
 {
 	ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES,
@@ -695,6 +697,13 @@ static int ghes_ack_error(struct acpi_hest_generic_v2 *generic_v2)
 	return rc;
 }
 
+static void __ghes_call_panic(void)
+{
+	if (panic_timeout == 0)
+		panic_timeout = ghes_panic_timeout;
+	panic("Fatal hardware error!");
+}
+
 static int ghes_proc(struct ghes *ghes)
 {
 	int rc;
@@ -706,6 +715,10 @@ static int ghes_proc(struct ghes *ghes)
 		if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus))
 			ghes_estatus_cache_add(ghes->generic, ghes->estatus);
 	}
+	if (ghes_severity(ghes->estatus->error_severity) >= GHES_SEV_PANIC) {
+		__ghes_call_panic();
+	}
+
 	ghes_do_proc(ghes, ghes->estatus);
 
 	if (HEST_TYPE_GENERIC_V2(ghes)) {
@@ -850,8 +863,6 @@ static atomic_t ghes_in_nmi = ATOMIC_INIT(0);
 
 static LIST_HEAD(ghes_nmi);
 
-static int ghes_panic_timeout	__read_mostly = 30;
-
 static void ghes_proc_in_irq(struct irq_work *irq_work)
 {
 	struct llist_node *llnode, *next;
@@ -944,9 +955,7 @@ static void __ghes_panic(struct ghes *ghes)
 	__ghes_print_estatus(KERN_EMERG, ghes->generic, ghes->estatus);
 
 	/* reboot to log the error! */
-	if (panic_timeout == 0)
-		panic_timeout = ghes_panic_timeout;
-	panic("Fatal hardware error!");
+	__ghes_call_panic();
 }
 
 static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
-- 
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH V5 07/10] efi: print unrecognized CPER section
From: Tyler Baicar @ 2016-11-21 22:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479767763-27532-1-git-send-email-tbaicar@codeaurora.org>

UEFI spec allows for non-standard section in Common Platform Error
Record. This is defined in section N.2.3 of UEFI version 2.5.

Currently if the CPER section's type (UUID) does not match with
one of the section types that the kernel knows how to parse, the
section is skipped. Therefore, user is not able to see
such CPER data, for instance, error record of non-standard section.

For above mentioned case, this change prints out the raw data in
hex in dmesg buffer. Data length is taken from Error Data length
field of Generic Error Data Entry.

Following is a sample output from dmesg:
[  115.771702] {1}[Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 2
[  115.779042] {1}[Hardware Error]: It has been corrected by h/w and requires no further action
[  115.787456] {1}[Hardware Error]: event severity: corrected
[  115.792927] {1}[Hardware Error]:  Error 0, type: corrected
[  115.798415] {1}[Hardware Error]:  fru_id: 00000000-0000-0000-0000-000000000000
[  115.805596] {1}[Hardware Error]:  fru_text:
[  115.816105] {1}[Hardware Error]:  section type: d2e2621c-f936-468d-0d84-15a4ed015c8b
[  115.823880] {1}[Hardware Error]:  section length: 88
[  115.828779] {1}[Hardware Error]:   00000000: 01000001 00000002 5f434345 525f4543
[  115.836153] {1}[Hardware Error]:   00000010: 0000574d 00000000 00000000 00000000
[  115.843531] {1}[Hardware Error]:   00000020: 00000000 00000000 00000000 00000000
[  115.850908] {1}[Hardware Error]:   00000030: 00000000 00000000 00000000 00000000
[  115.858288] {1}[Hardware Error]:   00000040: fe800000 00000000 00000004 5f434345
[  115.865665] {1}[Hardware Error]:   00000050: 525f4543 0000574d

Signed-off-by: Jonathan (Zhixiong) Zhang <zjzhang@codeaurora.org>
Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
---
 drivers/firmware/efi/cper.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c
index 004aa1b..bbb576e 100644
--- a/drivers/firmware/efi/cper.c
+++ b/drivers/firmware/efi/cper.c
@@ -593,8 +593,16 @@ static void cper_estatus_print_section(
 			cper_print_proc_armv8(newpfx, armv8_err);
 		else
 			goto err_section_too_small;
-	} else
-		printk("%s""section type: unknown, %pUl\n", newpfx, sec_type);
+	} else {
+		const void *unknown_err;
+
+		unknown_err = acpi_hest_generic_data_payload(gdata);
+		printk("%ssection type: %pUl\n", newpfx, sec_type);
+		printk("%ssection length: %d\n", newpfx,
+		       gdata->error_data_length);
+		print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4,
+			       unknown_err, gdata->error_data_length, 0);
+	}
 
 	return;
 
-- 
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH V5 08/10] ras: acpi / apei: generate trace event for unrecognized CPER section
From: Tyler Baicar @ 2016-11-21 22:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479767763-27532-1-git-send-email-tbaicar@codeaurora.org>

UEFI spec allows for non-standard section in Common Platform Error
Record. This is defined in section N.2.3 of UEFI version 2.5.

Currently if the CPER section's type (UUID) does not match with
any section type that the kernel knows how to parse, trace event
is not generated for such section. And thus user is not able to know
happening of such hardware error, including error record of
non-standard section.

This commit generates a trace event which contains raw error data
for unrecognized CPER section.

Signed-off-by: Jonathan (Zhixiong) Zhang <zjzhang@codeaurora.org>
Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
---
 drivers/acpi/apei/ghes.c | 16 +++++++++++++++-
 drivers/ras/ras.c        |  1 +
 include/ras/ras_event.h  | 45 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 28f801c..c7fbbc1 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -49,6 +49,7 @@
 #include <acpi/ghes.h>
 #include <acpi/apei.h>
 #include <asm/tlbflush.h>
+#include <ras/ras_event.h>
 
 #ifdef CONFIG_HAVE_ACPI_APEI_SEA
 #include <asm/system_misc.h>
@@ -458,12 +459,19 @@ static void ghes_do_proc(struct ghes *ghes,
 	int sev, sec_sev;
 	struct acpi_hest_generic_data *gdata;
 	uuid_le sec_type;
+	uuid_le *fru_id = &NULL_UUID_LE;
+	char *fru_text = "";
 
 	sev = ghes_severity(estatus->error_severity);
 	apei_estatus_for_each_section(estatus, gdata) {
 		sec_sev = ghes_severity(gdata->error_severity);
 		sec_type = *(uuid_le *)gdata->section_type;
 
+		if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
+			fru_id = (uuid_le *)gdata->fru_id;
+		if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
+			fru_text = gdata->fru_text;
+
 		if (!uuid_le_cmp(sec_type,
 				 CPER_SEC_PLATFORM_MEM)) {
 			struct cper_sec_mem_err *mem_err;
@@ -475,7 +483,7 @@ static void ghes_do_proc(struct ghes *ghes,
 			ghes_handle_memory_failure(gdata, sev);
 		}
 #ifdef CONFIG_ACPI_APEI_PCIEAER
-		else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
+		else if (!uuid_le_cmp(sec_type,
 				      CPER_SEC_PCIE)) {
 			struct cper_sec_pcie *pcie_err;
 
@@ -508,6 +516,12 @@ static void ghes_do_proc(struct ghes *ghes,
 
 		}
 #endif
+		else {
+			void *unknown_err = acpi_hest_generic_data_payload(gdata);
+			trace_unknown_sec_event(&sec_type,
+					fru_id, fru_text, sec_sev,
+					unknown_err, gdata->error_data_length);
+		}
 	}
 }
 
diff --git a/drivers/ras/ras.c b/drivers/ras/ras.c
index b67dd36..fb2500b 100644
--- a/drivers/ras/ras.c
+++ b/drivers/ras/ras.c
@@ -27,3 +27,4 @@ subsys_initcall(ras_init);
 EXPORT_TRACEPOINT_SYMBOL_GPL(extlog_mem_event);
 #endif
 EXPORT_TRACEPOINT_SYMBOL_GPL(mc_event);
+EXPORT_TRACEPOINT_SYMBOL_GPL(unknown_sec_event);
diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h
index 1791a12..5861b6f 100644
--- a/include/ras/ras_event.h
+++ b/include/ras/ras_event.h
@@ -162,6 +162,51 @@ TRACE_EVENT(mc_event,
 );
 
 /*
+ * Unknown Section Report
+ *
+ * This event is generated when hardware detected a hardware
+ * error event, which may be of non-standard section as defined
+ * in UEFI spec appendix "Common Platform Error Record", or may
+ * be of sections for which TRACE_EVENT is not defined.
+ *
+ */
+TRACE_EVENT(unknown_sec_event,
+
+	TP_PROTO(const uuid_le *sec_type,
+		 const uuid_le *fru_id,
+		 const char *fru_text,
+		 const u8 sev,
+		 const u8 *err,
+		 const u32 len),
+
+	TP_ARGS(sec_type, fru_id, fru_text, sev, err, len),
+
+	TP_STRUCT__entry(
+		__array(char, sec_type, 16)
+		__array(char, fru_id, 16)
+		__string(fru_text, fru_text)
+		__field(u8, sev)
+		__field(u32, len)
+		__dynamic_array(u8, buf, len)
+	),
+
+	TP_fast_assign(
+		memcpy(__entry->sec_type, sec_type, sizeof(uuid_le));
+		memcpy(__entry->fru_id, fru_id, sizeof(uuid_le));
+		__assign_str(fru_text, fru_text);
+		__entry->sev = sev;
+		__entry->len = len;
+		memcpy(__get_dynamic_array(buf), err, len);
+	),
+
+	TP_printk("severity: %d; sec type:%pU; FRU: %pU %s; data len:%d; raw data:%s",
+		  __entry->sev, __entry->sec_type,
+		  __entry->fru_id, __get_str(fru_text),
+		  __entry->len,
+		  __print_hex(__get_dynamic_array(buf), __entry->len))
+);
+
+/*
  * PCIe AER Trace event
  *
  * These events are generated when hardware detects a corrected or
-- 
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH V5 09/10] trace, ras: add ARM processor error trace event
From: Tyler Baicar @ 2016-11-21 22:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479767763-27532-1-git-send-email-tbaicar@codeaurora.org>

Currently there are trace events for the various RAS
errors with the exception of ARM processor type errors.
Add a new trace event for such errors so that the user
will know when they occur. These trace events are
consistent with the ARM processor error section type
defined in UEFI 2.6 spec section N.2.4.4.

Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
Acked-by: Steven Rostedt <rostedt@goodmis.org>
---
 drivers/acpi/apei/ghes.c    |  9 +++++++-
 drivers/firmware/efi/cper.c |  1 +
 drivers/ras/ras.c           |  1 +
 include/ras/ras_event.h     | 55 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index c7fbbc1..1147e17 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -516,7 +516,14 @@ static void ghes_do_proc(struct ghes *ghes,
 
 		}
 #endif
-		else {
+		else if (!uuid_le_cmp(sec_type, CPER_SEC_PROC_ARMV8)) {
+			struct cper_sec_proc_armv8 *armv8_err;
+			struct cper_armv8_err_info *err_info;
+
+			armv8_err = acpi_hest_generic_data_payload(gdata);
+			err_info = (void *)(armv8_err +1);
+			trace_arm_event(armv8_err, err_info);
+		} else {
 			void *unknown_err = acpi_hest_generic_data_payload(gdata);
 			trace_unknown_sec_event(&sec_type,
 					fru_id, fru_text, sec_sev,
diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c
index bbb576e..0a0cd74 100644
--- a/drivers/firmware/efi/cper.c
+++ b/drivers/firmware/efi/cper.c
@@ -35,6 +35,7 @@
 #include <linux/printk.h>
 #include <linux/bcd.h>
 #include <acpi/ghes.h>
+#include <ras/ras_event.h>
 
 #define INDENT_SP	" "
 
diff --git a/drivers/ras/ras.c b/drivers/ras/ras.c
index fb2500b..8ba5a94 100644
--- a/drivers/ras/ras.c
+++ b/drivers/ras/ras.c
@@ -28,3 +28,4 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(extlog_mem_event);
 #endif
 EXPORT_TRACEPOINT_SYMBOL_GPL(mc_event);
 EXPORT_TRACEPOINT_SYMBOL_GPL(unknown_sec_event);
+EXPORT_TRACEPOINT_SYMBOL_GPL(arm_event);
diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h
index 5861b6f..0060bba 100644
--- a/include/ras/ras_event.h
+++ b/include/ras/ras_event.h
@@ -162,6 +162,61 @@ TRACE_EVENT(mc_event,
 );
 
 /*
+ * ARM Processor Events Report
+ *
+ * This event is generated when hardware detects an ARM processor error
+ * has occurred. UEFI 2.6 spec section N.2.4.4.
+ */
+TRACE_EVENT(arm_event,
+
+	TP_PROTO(const struct cper_sec_proc_armv8 *proc,
+		 struct cper_armv8_err_info *err_info),
+
+	TP_ARGS(proc, err_info),
+
+	TP_STRUCT__entry(
+		__field(u64, mpidr)
+		__field(u64, midr)
+		__field(u64, info)
+		__field(u64, virt_fault_addr)
+		__field(u64, phys_fault_addr)
+		__field(u32, running_state)
+		__field(u32, psci_state)
+		__field(u16, err_count)
+		__field(u8, affinity)
+		__field(u8, version)
+		__field(u8, type)
+		__field(u8, flags)
+	),
+
+	TP_fast_assign(
+		__entry->affinity = proc->affinity_level;
+		__entry->mpidr = proc->mpidr;
+		__entry->midr = proc->midr;
+		__entry->running_state = proc->running_state;
+		__entry->psci_state = proc->psci_state;
+		__entry->version = err_info->version;
+		__entry->type = err_info->type;
+		__entry->err_count = err_info->multiple_error;
+		__entry->flags = err_info->flags;
+		__entry->info = err_info->error_info;
+		__entry->virt_fault_addr = err_info->virt_fault_addr;
+		__entry->phys_fault_addr = err_info->physical_fault_addr;
+	),
+
+	TP_printk("affinity level: %d; MPIDR: %016llx; MIDR: %016llx; "
+		  "running state: %d; PSCI state: %d; version: %d; type: %d; "
+		  "error count: 0x%04x; flags: 0x%02x; info: %016llx; "
+		  "virtual fault address: %016llx; "
+		  "physical fault address: %016llx",
+		  __entry->affinity, __entry->mpidr, __entry->midr,
+		  __entry->running_state, __entry->psci_state, __entry->version,
+		  __entry->type, __entry->err_count, __entry->flags,
+		  __entry->info, __entry->virt_fault_addr,
+		  __entry->phys_fault_addr)
+);
+
+/*
  * Unknown Section Report
  *
  * This event is generated when hardware detected a hardware
-- 
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH V5 10/10] arm/arm64: KVM: add guest SEA support
From: Tyler Baicar @ 2016-11-21 22:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479767763-27532-1-git-send-email-tbaicar@codeaurora.org>

Currently external aborts are unsupported by the guest abort
handling. Add handling for SEAs so that the host kernel reports
SEAs which occur in the guest kernel.

Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
---
 arch/arm/include/asm/kvm_arm.h       |  1 +
 arch/arm/include/asm/system_misc.h   |  5 +++++
 arch/arm/kvm/mmu.c                   | 18 ++++++++++++++++--
 arch/arm64/include/asm/kvm_arm.h     |  1 +
 arch/arm64/include/asm/system_misc.h |  2 ++
 arch/arm64/mm/fault.c                | 13 +++++++++++++
 6 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h
index e22089f..33a77509 100644
--- a/arch/arm/include/asm/kvm_arm.h
+++ b/arch/arm/include/asm/kvm_arm.h
@@ -187,6 +187,7 @@
 #define FSC_FAULT	(0x04)
 #define FSC_ACCESS	(0x08)
 #define FSC_PERM	(0x0c)
+#define FSC_EXTABT	(0x10)
 
 /* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
 #define HPFAR_MASK	(~0xf)
diff --git a/arch/arm/include/asm/system_misc.h b/arch/arm/include/asm/system_misc.h
index a3d61ad..ea45d94 100644
--- a/arch/arm/include/asm/system_misc.h
+++ b/arch/arm/include/asm/system_misc.h
@@ -24,4 +24,9 @@ extern unsigned int user_debug;
 
 #endif /* !__ASSEMBLY__ */
 
+static inline int handle_guest_sea(unsigned long addr, unsigned int esr)
+{
+	return -1;
+}
+
 #endif /* __ASM_ARM_SYSTEM_MISC_H */
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index e9a5c0e..1152966 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -29,6 +29,7 @@
 #include <asm/kvm_asm.h>
 #include <asm/kvm_emulate.h>
 #include <asm/virt.h>
+#include <asm/system_misc.h>
 
 #include "trace.h"
 
@@ -1441,8 +1442,21 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
 
 	/* Check the stage-2 fault is trans. fault or write fault */
 	fault_status = kvm_vcpu_trap_get_fault_type(vcpu);
-	if (fault_status != FSC_FAULT && fault_status != FSC_PERM &&
-	    fault_status != FSC_ACCESS) {
+
+	/* The host kernel will handle the synchronous external abort. There
+	 * is no need to pass the error into the guest.
+	 */
+	if (fault_status == FSC_EXTABT) {
+		if(handle_guest_sea((unsigned long)fault_ipa,
+				    kvm_vcpu_get_hsr(vcpu))) {
+			kvm_err("Failed to handle guest SEA, FSC: EC=%#x xFSC=%#lx ESR_EL2=%#lx\n",
+				kvm_vcpu_trap_get_class(vcpu),
+				(unsigned long)kvm_vcpu_trap_get_fault(vcpu),
+				(unsigned long)kvm_vcpu_get_hsr(vcpu));
+			return -EFAULT;
+		}
+	} else if (fault_status != FSC_FAULT && fault_status != FSC_PERM &&
+		   fault_status != FSC_ACCESS) {
 		kvm_err("Unsupported FSC: EC=%#x xFSC=%#lx ESR_EL2=%#lx\n",
 			kvm_vcpu_trap_get_class(vcpu),
 			(unsigned long)kvm_vcpu_trap_get_fault(vcpu),
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 4b5c977..be0efb6 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -201,6 +201,7 @@
 #define FSC_FAULT	ESR_ELx_FSC_FAULT
 #define FSC_ACCESS	ESR_ELx_FSC_ACCESS
 #define FSC_PERM	ESR_ELx_FSC_PERM
+#define FSC_EXTABT	ESR_ELx_FSC_EXTABT
 
 /* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
 #define HPFAR_MASK	(~UL(0xf))
diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h
index 9040e1d..3a142a5 100644
--- a/arch/arm64/include/asm/system_misc.h
+++ b/arch/arm64/include/asm/system_misc.h
@@ -77,4 +77,6 @@ extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
 int register_synchronous_ext_abort_notifier(struct notifier_block *nb);
 void unregister_synchronous_ext_abort_notifier(struct notifier_block *nb);
 
+int handle_guest_sea(unsigned long addr, unsigned int esr);
+
 #endif	/* __ASM_SYSTEM_MISC_H */
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index fcc49f1..691399e 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -597,6 +597,19 @@ static const char *fault_name(unsigned int esr)
 }
 
 /*
+ * Handle Synchronous External Aborts that occur in a guest kernel.
+ */
+int handle_guest_sea(unsigned long addr, unsigned int esr)
+{
+	atomic_notifier_call_chain(&sea_handler_chain, 0, NULL);
+
+	pr_err("Synchronous External Abort: %s (0x%08x) at 0x%016lx\n",
+		fault_name(esr), esr, addr);
+
+	return 0;
+}
+
+/*
  * Dispatch a data abort to the relevant handler.
  */
 asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
-- 
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [kvm-unit-tests PATCH v10 3/3] arm: pmu: Add CPI checking
From: Wei Huang @ 2016-11-21 22:49 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <13b86440-3b37-ff24-3080-f0deece175fa@codeaurora.org>



On 11/21/2016 03:40 PM, Christopher Covington wrote:
> Hi Wei,
> 
> On 11/21/2016 03:24 PM, Wei Huang wrote:
>> From: Christopher Covington <cov@codeaurora.org>
> 
> I really appreciate your work on these patches. If for any or all of these
> you have more lines added/modified than me (or using any other better
> metric), please make sure to change the author to be you with
> `git commit --amend --reset-author` or equivalent.

Sure, I will if needed. Regarding your comments below, I will fix the
patch series after Drew's comments, if any.

> 
>> Calculate the numbers of cycles per instruction (CPI) implied by ARM
>> PMU cycle counter values. The code includes a strict checking facility
>> intended for the -icount option in TCG mode in the configuration file.
>>
>> Signed-off-by: Christopher Covington <cov@codeaurora.org>
>> Signed-off-by: Wei Huang <wei@redhat.com>
>> ---
>>  arm/pmu.c         | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
>>  arm/unittests.cfg |  14 +++++++
>>  2 files changed, 132 insertions(+), 1 deletion(-)
>>
>> diff --git a/arm/pmu.c b/arm/pmu.c
>> index 176b070..129ef1e 100644
>> --- a/arm/pmu.c
>> +++ b/arm/pmu.c
>> @@ -104,6 +104,25 @@ static inline uint32_t id_dfr0_read(void)
>>  	asm volatile("mrc p15, 0, %0, c0, c1, 2" : "=r" (val));
>>  	return val;
>>  }
>> +
>> +/*
>> + * Extra instructions inserted by the compiler would be difficult to compensate
>> + * for, so hand assemble everything between, and including, the PMCR accesses
>> + * to start and stop counting. Total cycles = isb + mcr + 2*loop = 2 + 2*loop.
                                   ^^^^^^^^^^^^
I will change the comment above to "Total instrs".

>> + */
>> +static inline void precise_cycles_loop(int loop, uint32_t pmcr)
> 
> Nit: I would call this precise_instrs_loop. How many cycles it takes is
> IMPLEMENTATION DEFINED.

You are right. The cycle indeed depends on the design. Will fix.

> 
>> +{
>> +	asm volatile(
>> +	"	mcr	p15, 0, %[pmcr], c9, c12, 0\n"
>> +	"	isb\n"
>> +	"1:	subs	%[loop], %[loop], #1\n"
>> +	"	bgt	1b\n"
> 
> Is there any chance we might need an isb here, to prevent the stop from happening
> before or during the loop? Where ISBs are required, the Linux best practice is to

In theory, I think this can happen when mcr is executed before all loop
instructions completed, causing pmccntr_read() to miss some cycles. But
QEMU TCG mode doesn't support out-order-execution. So the test
condition, "cpi > 0 && cycles != i * cpi", will never be TRUE. Because
cpi==0 in KVM, this same test condition won't be TRUE under KVM mode either.

> diligently comment why they are needed. Perhaps it would be a good habit to
> carry over into kvm-unit-tests.

Agreed. Most isb() instructions were added following CP15 writes (not
all CP15 writes, but at limited locations). We tried to follow what
Linux kernel does in perf_event.c. If you feel that any isb() place
needs special comment, I will be more than happy to add it.

<snip>

^ permalink raw reply

* [PATCH v2 2/4] spi: spi-fsl-dspi: Fix continuous selection format
From: Stefan Agner @ 2016-11-21 23:15 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1317eff8a40789c47311c219d9cfb4105863bd9f.1479706671.git.maitysanchayan@gmail.com>

On 2016-11-20 21:54, Sanchayan Maity wrote:
> Current DMA implementation was not handling the continuous selection
> format viz. SPI chip select would be deasserted even between sequential
> serial transfers. Use the cs_change variable and correctly set or
> reset the CONT bit accordingly for case where peripherals require
> the chip select to be asserted between sequential transfers.
> 
> Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
> ---
>  drivers/spi/spi-fsl-dspi.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
> index b1ee1f5..41422cd 100644
> --- a/drivers/spi/spi-fsl-dspi.c
> +++ b/drivers/spi/spi-fsl-dspi.c
> @@ -261,6 +261,8 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi)
>  	dspi->dma->tx_dma_buf[i] = SPI_PUSHR_TXDATA(val) |
>  					SPI_PUSHR_PCS(dspi->cs) |
>  					SPI_PUSHR_CTAS(0);
> +	if (!dspi->cs_change)
> +		dspi->dma->tx_dma_buf[i] |= SPI_PUSHR_CONT;
>  	dspi->tx += tx_word + 1;
>  
>  	dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx,

Other transfer mode use:

if ((dspi->cs_change) && (!dspi->len))                                  
                                                           
        dspi_pushr &= ~SPI_PUSHR_CONT;

which indicates that they only clear SPI_PUSHR_CONT at the very end of a
transfer... The DMA code currently deselects after every DMA transfer if
dspi->cs_change is set.

Maybe we should use the helper dspi_data_to_pushr to fill the DMA buffer
and _clear_ SPI_PUSHR_CONT if necessary like the other transfer modes
do... Then we can use the for loop to fill the complete buffer and get
rid of some code dupplication.

I see that dspi_data_to_pushr does move len too, which we did not in the
DMA case. dspi->len gets incremented only on successful DMA transfer in
dspi_dma_xfer. However, I wonder if that is not even a bug: We increment
dspi->tx always, but len only on success. This makes len go off sync
with regards to the tx pointer which does not help anybody. So lets get
rid of the update code in dspi_dma_xfer

--
Stefan

^ permalink raw reply

* [PATCH v2 3/4] spi: spi-fsl-dspi: Fix incorrect DMA setup
From: Stefan Agner @ 2016-11-21 23:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <a36bac5f2499d1002693a7dbf95982bab186e51f.1479706671.git.maitysanchayan@gmail.com>

On 2016-11-20 21:54, Sanchayan Maity wrote:
> Currently dmaengine_prep_slave_single was being called with length
> set to the complete DMA buffer size. This resulted in unwanted bytes
> being transferred to the SPI register leading to clock and MOSI lines
> having unwanted data even after chip select got deasserted and the
> required bytes having been transferred.
> 
> While at it also clean up the use of curr_xfer_len which is central
> to the DMA setup, from bytes to DMA transfers for every use.
> 
> Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>

Looks good to me:

Reviewed-by: Stefan Agner <stefan@agner.ch>

> ---
>  drivers/spi/spi-fsl-dspi.c | 35 ++++++++++++++++++-----------------
>  1 file changed, 18 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
> index 41422cd..08882f7 100644
> --- a/drivers/spi/spi-fsl-dspi.c
> +++ b/drivers/spi/spi-fsl-dspi.c
> @@ -151,6 +151,7 @@ static const struct fsl_dspi_devtype_data ls2085a_data = {
>  };
>  
>  struct fsl_dspi_dma {
> +	/* Length of transfer in words of DSPI_FIFO_SIZE */
>  	u32 curr_xfer_len;
>  
>  	u32 *tx_dma_buf;
> @@ -217,15 +218,13 @@ static void dspi_rx_dma_callback(void *arg)
>  	struct fsl_dspi *dspi = arg;
>  	struct fsl_dspi_dma *dma = dspi->dma;
>  	int rx_word;
> -	int i, len;
> +	int i;
>  	u16 d;
>  
>  	rx_word = is_double_byte_mode(dspi);
>  
> -	len = rx_word ? (dma->curr_xfer_len / 2) : dma->curr_xfer_len;
> -
>  	if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) {
> -		for (i = 0; i < len; i++) {
> +		for (i = 0; i < dma->curr_xfer_len; i++) {
>  			d = dspi->dma->rx_dma_buf[i];
>  			rx_word ? (*(u16 *)dspi->rx = d) :
>  						(*(u8 *)dspi->rx = d);
> @@ -242,14 +241,12 @@ static int dspi_next_xfer_dma_submit(struct
> fsl_dspi *dspi)
>  	struct device *dev = &dspi->pdev->dev;
>  	int time_left;
>  	int tx_word;
> -	int i, len;
> +	int i;
>  	u16 val;
>  
>  	tx_word = is_double_byte_mode(dspi);
>  
> -	len = tx_word ? (dma->curr_xfer_len / 2) : dma->curr_xfer_len;
> -
> -	for (i = 0; i < len - 1; i++) {
> +	for (i = 0; i < dma->curr_xfer_len - 1; i++) {
>  		val = tx_word ? *(u16 *) dspi->tx : *(u8 *) dspi->tx;
>  		dspi->dma->tx_dma_buf[i] =
>  			SPI_PUSHR_TXDATA(val) | SPI_PUSHR_PCS(dspi->cs) |
> @@ -267,7 +264,9 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi)
>  
>  	dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx,
>  					dma->tx_dma_phys,
> -					DSPI_DMA_BUFSIZE, DMA_MEM_TO_DEV,
> +					dma->curr_xfer_len *
> +					DMA_SLAVE_BUSWIDTH_4_BYTES,
> +					DMA_MEM_TO_DEV,
>  					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
>  	if (!dma->tx_desc) {
>  		dev_err(dev, "Not able to get desc for DMA xfer\n");
> @@ -283,7 +282,9 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi)
>  
>  	dma->rx_desc = dmaengine_prep_slave_single(dma->chan_rx,
>  					dma->rx_dma_phys,
> -					DSPI_DMA_BUFSIZE, DMA_DEV_TO_MEM,
> +					dma->curr_xfer_len *
> +					DMA_SLAVE_BUSWIDTH_4_BYTES,
> +					DMA_DEV_TO_MEM,
>  					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
>  	if (!dma->rx_desc) {
>  		dev_err(dev, "Not able to get desc for DMA xfer\n");
> @@ -330,17 +331,17 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi)
>  	struct device *dev = &dspi->pdev->dev;
>  	int curr_remaining_bytes;
>  	int bytes_per_buffer;
> -	int tx_word;
> +	int word = 1;
>  	int ret = 0;
>  
> -	tx_word = is_double_byte_mode(dspi);
> +	if (is_double_byte_mode(dspi))
> +		word = 2;
>  	curr_remaining_bytes = dspi->len;
> +	bytes_per_buffer = DSPI_DMA_BUFSIZE / DSPI_FIFO_SIZE;
>  	while (curr_remaining_bytes) {
>  		/* Check if current transfer fits the DMA buffer */
> -		dma->curr_xfer_len = curr_remaining_bytes;
> -		bytes_per_buffer = DSPI_DMA_BUFSIZE /
> -				(DSPI_FIFO_SIZE / (tx_word ? 2 : 1));
> -		if (curr_remaining_bytes > bytes_per_buffer)
> +		dma->curr_xfer_len = curr_remaining_bytes / word;
> +		if (dma->curr_xfer_len > bytes_per_buffer)
>  			dma->curr_xfer_len = bytes_per_buffer;
>  
>  		ret = dspi_next_xfer_dma_submit(dspi);
> @@ -349,7 +350,7 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi)
>  			goto exit;
>  
>  		} else {
> -			curr_remaining_bytes -= dma->curr_xfer_len;
> +			curr_remaining_bytes -= dma->curr_xfer_len * word;
>  			if (curr_remaining_bytes < 0)
>  				curr_remaining_bytes = 0;
>  			dspi->len = curr_remaining_bytes;

^ permalink raw reply

* linux-next: manual merge of the rockchip tree with the arm-soc tree
From: Stephen Rothwell @ 2016-11-21 23:19 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Heiko,

Today's linux-next merge of the rockchip tree got a conflict in:

  arch/arm64/configs/defconfig

between commit:

  a59294b2f7c7 ("ARM64: defconfig: Enable MMC related configs")

from the arm-soc tree and commit:

  5295a3157348 ("arm64: defconfig: enable RK808 components")

from the rockchip tree.

I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging.  You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.

-- 
Cheers,
Stephen Rothwell

diff --cc arch/arm64/configs/defconfig
index 2640e193a8c4,731cc6ade789..000000000000
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@@ -297,23 -293,10 +299,24 @@@ CONFIG_REGULATOR_MAX77620=
  CONFIG_REGULATOR_PWM=y
  CONFIG_REGULATOR_QCOM_SMD_RPM=y
  CONFIG_REGULATOR_QCOM_SPMI=y
+ CONFIG_REGULATOR_RK808=y
  CONFIG_REGULATOR_S2MPS11=y
 +CONFIG_MEDIA_SUPPORT=m
 +CONFIG_MEDIA_CAMERA_SUPPORT=y
 +CONFIG_MEDIA_ANALOG_TV_SUPPORT=y
 +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
 +CONFIG_MEDIA_CONTROLLER=y
 +CONFIG_VIDEO_V4L2_SUBDEV_API=y
 +# CONFIG_DVB_NET is not set
 +CONFIG_V4L_MEM2MEM_DRIVERS=y
 +CONFIG_VIDEO_RENESAS_FCP=m
 +CONFIG_VIDEO_RENESAS_VSP1=m
  CONFIG_DRM=m
  CONFIG_DRM_NOUVEAU=m
 +CONFIG_DRM_RCAR_DU=m
 +CONFIG_DRM_RCAR_HDMI=y
 +CONFIG_DRM_RCAR_LVDS=y
 +CONFIG_DRM_RCAR_VSP=y
  CONFIG_DRM_TEGRA=m
  CONFIG_DRM_PANEL_SIMPLE=m
  CONFIG_DRM_I2C_ADV7511=m
@@@ -408,7 -393,7 +414,8 @@@ CONFIG_XEN_GRANT_DEV_ALLOC=
  CONFIG_COMMON_CLK_SCPI=y
  CONFIG_COMMON_CLK_CS2000_CP=y
  CONFIG_COMMON_CLK_S2MPS11=y
 +CONFIG_COMMON_CLK_PWM=y
+ CONFIG_COMMON_CLK_RK808=y
  CONFIG_CLK_QORIQ=y
  CONFIG_COMMON_CLK_QCOM=y
  CONFIG_MSM_GCC_8916=y
@@@ -423,13 -406,13 +430,15 @@@ CONFIG_ARM_SMMU=
  CONFIG_QCOM_SMEM=y
  CONFIG_QCOM_SMD=y
  CONFIG_QCOM_SMD_RPM=y
+ CONFIG_ROCKCHIP_PM_DOMAINS=y
  CONFIG_ARCH_TEGRA_132_SOC=y
  CONFIG_ARCH_TEGRA_210_SOC=y
 +CONFIG_ARCH_TEGRA_186_SOC=y
  CONFIG_EXTCON_USB_GPIO=y
  CONFIG_PWM=y
+ CONFIG_PWM_ROCKCHIP=y
  CONFIG_PWM_TEGRA=m
 +CONFIG_PWM_MESON=m
  CONFIG_COMMON_RESET_HI6220=y
  CONFIG_PHY_RCAR_GEN3_USB2=y
  CONFIG_PHY_HI6220_USB=y

^ permalink raw reply

* [PATCH v2 4/4] spi: spi-fsl-dspi: Minor code cleanup and error path fixes
From: Stefan Agner @ 2016-11-21 23:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <971874d8e662622c0cfe478adaa4c75b08388d0e.1479706671.git.maitysanchayan@gmail.com>

On 2016-11-20 21:54, Sanchayan Maity wrote:
> Code cleanup for improving code readability and error path fixes
> and cleanup removing use of devm_kfree.

Two things in one, not very nice. Especially the dma_free_coherent is
really a bug and the other is a cleanup. Can you make a separate patch
for the bug?

As for the cleanup, I don't like the one line conditions too, but I
don't think it is worth a patch. At least the TX path should be solved
with my suggestion in patch 2.

--
Stefan

> 
> Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
> ---
>  drivers/spi/spi-fsl-dspi.c | 22 ++++++++++++++++------
>  1 file changed, 16 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
> index 08882f7..2987a16 100644
> --- a/drivers/spi/spi-fsl-dspi.c
> +++ b/drivers/spi/spi-fsl-dspi.c
> @@ -226,8 +226,10 @@ static void dspi_rx_dma_callback(void *arg)
>  	if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) {
>  		for (i = 0; i < dma->curr_xfer_len; i++) {
>  			d = dspi->dma->rx_dma_buf[i];
> -			rx_word ? (*(u16 *)dspi->rx = d) :
> -						(*(u8 *)dspi->rx = d);
> +			if (rx_word)
> +				*(u16 *)dspi->rx = d;
> +			else
> +				*(u8 *)dspi->rx = d;
>  			dspi->rx += rx_word + 1;
>  		}
>  	}
> @@ -247,14 +249,20 @@ static int dspi_next_xfer_dma_submit(struct
> fsl_dspi *dspi)
>  	tx_word = is_double_byte_mode(dspi);
>  
>  	for (i = 0; i < dma->curr_xfer_len - 1; i++) {
> -		val = tx_word ? *(u16 *) dspi->tx : *(u8 *) dspi->tx;
> +		if (tx_word)
> +			val = *(u16 *) dspi->tx;
> +		else
> +			val = *(u8 *) dspi->tx;
>  		dspi->dma->tx_dma_buf[i] =
>  			SPI_PUSHR_TXDATA(val) | SPI_PUSHR_PCS(dspi->cs) |
>  			SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
>  		dspi->tx += tx_word + 1;
>  	}
>  
> -	val = tx_word ? *(u16 *) dspi->tx : *(u8 *) dspi->tx;
> +	if (tx_word)
> +		val = *(u16 *) dspi->tx;
> +	else
> +		val = *(u8 *) dspi->tx;
>  	dspi->dma->tx_dma_buf[i] = SPI_PUSHR_TXDATA(val) |
>  					SPI_PUSHR_PCS(dspi->cs) |
>  					SPI_PUSHR_CTAS(0);
> @@ -430,9 +438,11 @@ static int dspi_request_dma(struct fsl_dspi
> *dspi, phys_addr_t phy_addr)
>  	return 0;
>  
>  err_slave_config:
> -	devm_kfree(dev, dma->rx_dma_buf);
> +	dma_free_coherent(dev, DSPI_DMA_BUFSIZE,
> +			dma->rx_dma_buf, dma->rx_dma_phys);
>  err_rx_dma_buf:
> -	devm_kfree(dev, dma->tx_dma_buf);
> +	dma_free_coherent(dev, DSPI_DMA_BUFSIZE,
> +			dma->tx_dma_buf, dma->tx_dma_phys);
>  err_tx_dma_buf:
>  	dma_release_channel(dma->chan_tx);
>  err_tx_channel:

^ permalink raw reply

* [GIT PULL] Allwinner DT changes for 4.10
From: Olof Johansson @ 2016-11-21 23:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161121222209.ipqft65zcqsfsu2n@lukather>

On Mon, Nov 21, 2016 at 2:22 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> On Mon, Nov 21, 2016 at 08:28:09AM -0800, Olof Johansson wrote:
>> HI,
>>
>> On Mon, Nov 21, 2016 at 5:27 AM, Maxime Ripard
>> <maxime.ripard@free-electrons.com> wrote:
>> > Hi Olof,
>> >
>> > On Fri, Nov 18, 2016 at 04:23:16PM -0800, Olof Johansson wrote:
>> >> Hi,
>> >>
>> >> On Tue, Nov 15, 2016 at 09:41:22PM +0100, Maxime Ripard wrote:
>> >> > Hi Arnd, Olof,
>> >> >
>> >> > Here is our pull request for the next merge window.
>> >> >
>> >> > Thanks!
>> >> > Maxime
>> >> >
>> >> > The following changes since commit 1001354ca34179f3db924eb66672442a173147dc:
>> >> >
>> >> >   Linux 4.9-rc1 (2016-10-15 12:17:50 -0700)
>> >> >
>> >> > are available in the git repository at:
>> >> >
>> >> >   https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux.git tags/sunxi-dt-for-4.10
>> >> >
>> >> > for you to fetch changes up to e39a30cf736144814b0bddb3fff28fbbc2a8be0f:
>> >> >
>> >> >   ARM: sunxi: Add the missing clocks to the pinctrl nodes (2016-11-15 18:49:47 +0100)
>> >> >
>> >> > ----------------------------------------------------------------
>> >> > Allwinner DT additions for 4.10
>> >> >
>> >> > The usual bunch of DT additions, but most notably:
>> >> >   - A31 DRM driver
>> >> >   - A31 audio codec
>> >> >   - WiFi for the A80-Based boards and the CHIP
>> >> >   - Support for the NextThing Co CHIP Pro (the first board with NAND
>> >> >     enabled)
>> >> >   - New boards: NanoPi M1,
>> >> >
>> >> [...]
>> >>
>> >> > Maxime Ripard (16):
>> >> >       ARM: sun5i: a13-olinuxino: Enable VGA bridge
>> >> >       ARM: gr8: Add the UART3
>> >> >       ARM: gr8: Fix typo in the i2s mclk pin group
>> >> >       ARM: gr8: Add missing pwm channel 1 pin
>> >> >       ARM: gr8: Add UART2 pins
>> >> >       ARM: gr8: Add UART3 pins
>> >> >       ARM: gr8: Add CHIP Pro support
>> >> >       ARM: sun5i: chip: Enable Wi-Fi SDIO chip
>> >> >       ARM: sun5i: Rename A10s pins
>> >> >       ARM: sun5i: Add SPI2 pins
>> >> >       ARM: sun5i: Add RGB 565 LCD pins
>> >> >       ARM: sun5i: chip: Add optional buses
>> >> >       ARM: gr8: evb: Enable SPDIF
>> >> >       ARM: gr8: evb: Add i2s codec
>> >> >       ARM: sun8i: sina33: Enable USB gadget
>> >> >       ARM: sunxi: Add the missing clocks to the pinctrl nodes
>> >> >
>> >> [...]
>> >>
>> >> >  arch/arm/boot/dts/Makefile                         |   1 +
>> >> >  arch/arm/boot/dts/ntc-gr8-chip-pro.dts             | 266 +++++++++++++++++++++
>> >> >  arch/arm/boot/dts/ntc-gr8-evb.dts                  |  33 +++
>> >> >  arch/arm/boot/dts/ntc-gr8.dtsi                     |  47 +++-
>> >> >  arch/arm/boot/dts/sun4i-a10.dtsi                   |   3 +-
>> >>
>> >> NTC isn't the SoC manufacturer, and we try to keep the prefixes down to
>> >> manufacturer to keep the namespace a little more manageable, even if
>> >> we never got subdirectories setup as on arm64.
>> >>
>> >> I think this should probably be sun4i-a10-gr8 or sun4i-r8-gr8 as prefix?
>> >
>> > The users really expect a SoC from Nextthing, it's always been
>> > marketed that way, the marking on the SoC also says so, etc. The fact
>> > that it's been a design in cooperation with Allwinner, and that the
>> > design is based on some earlier family is an implementation detail,
>> > and I'd really like not for it to have the sun5i prefix, it's just
>> > confusing.
>>
>> I don't care so much about what's printed on the top of the package, I
>> care a lot more about what's on the insides. We've got a long
>> tradition of not renaming things randomly when companies get acquired
>> or renames themselves, and I think that same spirit is applicable
>> here.
>
> Yet, we called the imx23 and imx28 that way while it was really a
> sigmatel design. And I'm pretty sure there's other examples too.

Had Sigmatel had a whole bunch of SoCs in-tree we might have had
discussions about that as well.

>> Calling it an SoC is inaccurate as well, it's really a
>> system-in-package. It's just a new way to integrate an SoC into a
>> module to build boards out of. Compare to Octavo OSD335x, for example.
>>
>> System-in-package solutions are going to get more and more common, and
>> it's going to become really chaotic if we expect to use the prefix of
>> the company custom-ordering the package for each and every one of
>> them.
>
> This is indeed a SIP, but one with a custom SoC (or whatever the !RAM
> part in a SIP is called) that was designed by Allwinner *and* Next
> Thing Co., unlike the OSD335x that has a standard AM335x SoC in it.

Even NTC's marketing material says it's an Allwinner R8, at
https://getchip.com/pages/chippro: "Powered by a chip you can actually
buy. R8 SoC + 256MB DDR3. 1 package. 1 price. $6 in any quantity."

This is really quite similar to the situation with SoM manufacturers
such as Toradex, who integrate SoC, memory and some I/O on a module
that you then build your product around. The only difference is the
level it's integrated at. We've chosen to slice our naming across SoC
family lines, not who makes the substrate that the SoC is mounted to.

>> > And the ntc prefix has been asked for during the reviews...
>>
>> Having a link to that requeset/email would be helpful if you try to
>> use it as an argument.
>
> Yeah, it turns out it was off list... so it's a pretty weak one :)

:)


-Olof

^ permalink raw reply

* linux-next: manual merge of the rockchip tree with the arm-soc tree
From: Olof Johansson @ 2016-11-21 23:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161122101908.2e967e42@canb.auug.org.au>

Hi,

On Mon, Nov 21, 2016 at 3:19 PM, Stephen Rothwell <sfr@canb.auug.org.au> wrote:
> Hi Heiko,
>
> Today's linux-next merge of the rockchip tree got a conflict in:
>
>   arch/arm64/configs/defconfig
>
> between commit:
>
>   a59294b2f7c7 ("ARM64: defconfig: Enable MMC related configs")
>
> from the arm-soc tree and commit:
>
>   5295a3157348 ("arm64: defconfig: enable RK808 components")
>
> from the rockchip tree.
>
> I fixed it up (see below) and can carry the fix as necessary. This
> is now fixed as far as linux-next is concerned, but any non trivial
> conflicts should be mentioned to your upstream maintainer when your tree
> is submitted for merging.  You may also want to consider cooperating
> with the maintainer of the conflicting tree to minimise any particularly
> complex conflicts.

This sounds fine, and it's a conflict we'll resolve when we merge this
material from Heiko.


-Olof

^ permalink raw reply

* [PATCH 1/2] ARM64: dts: Add support for Meson GXM
From: Kevin Hilman @ 2016-11-21 23:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161121162905.14285-2-narmstrong@baylibre.com>

Neil,

Neil Armstrong <narmstrong@baylibre.com> writes:

> Following the Amlogic Linux kernel, it seem the only differences
> between the GXL and GXM SoCs are the CPU Clusters.
>
> This commit renames the gxl-s905d-p23x DTSI in a common file for
> S905D p20x and S912 q20x boards.

s/p20x/p23x/ ??

> Then adds a meson-gxm dtsi and reproduce the P23x to Q20x boards
> dts files since the S905D and S912 SoCs shares the same pinout
> and the P23x and Q20x boards are identical.
>
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>

I had to drop this as it breaks the network on (at least) gxbb-p200, but...

[...]

> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p200.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-p200.dts
> index 03e3d76..17bb77c 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p200.dts
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p200.dts
> @@ -56,3 +56,22 @@
>  	pinctrl-0 = <&i2c_b_pins>;
>  	pinctrl-names = "default";
>  };
> +
> +&ethmac {
> +	status = "okay";
> +	pinctrl-0 = <&eth_rgmii_pins>;
> +	pinctrl-names = "default";
> +
> +	phy-handle = <&eth_phy0>;
> +
> +	mdio {
> +		compatible = "snps,dwmac-mdio";
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		eth_phy0: ethernet-phy at 0 {
> +			reg = <0>;
> +			realtek,disable-eee-1000t;
> +		};
> +	};
> +};

... backing out this change makes it work again.

This change also looks suspicious as it's using the proposed disable-eee
properties, which I don't think have been merged yet.

> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p201.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-p201.dts
> index 39bb037..5608c51 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p201.dts
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p201.dts
> @@ -50,3 +50,10 @@
>  	compatible = "amlogic,p201", "amlogic,meson-gxbb";
>  	model = "Amlogic Meson GXBB P201 Development Board";
>  };
> +
> +&ethmac {
> +	status = "okay";
> +	pinctrl-0 = <&eth_rmii_pins>;
> +	pinctrl-names = "default";
> +	phy-mode = "rmii";
> +};

This also doesn't look releveant to the GXL/GXM changes being introduced
in this patch.

Could you separate out any GXBB-related changes into a separate patch
(if they are in fact needed) and re-spin this?

Thanks,

Kevin

^ permalink raw reply

* [PATCH 0/6] apalis-tk1: updates for v1.1 hw
From: Marcel Ziswiler @ 2016-11-22  0:14 UTC (permalink / raw)
  To: linux-arm-kernel


This series updates the device tree for the upcoming V1.1 HW samples.
All changes are purely opportunistic meaning they fix stuff which on
older HW was anyway broken so there should be no backwards
compatibility issues.


Marcel Ziswiler (6):
  apalis-tk1: remove spurious new lines
  apalis-tk1: temp alert pull-up
  apalis-tk1: optional displayport hot-plug detect
  apalis-tk1: adjust pin muxing for v1.1 hw
  apalis-tk1: working sd card detect on v1.1 hw
  apalis-tk1: update compatibility comment

 arch/arm/boot/dts/tegra124-apalis-eval.dts | 11 +----
 arch/arm/boot/dts/tegra124-apalis.dtsi     | 73 +++++++++++-------------------
 2 files changed, 29 insertions(+), 55 deletions(-)

-- 
2.5.5

^ permalink raw reply

* [PATCH 1/6] apalis-tk1: remove spurious new lines
From: Marcel Ziswiler @ 2016-11-22  0:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479773647-14726-1-git-send-email-marcel.ziswiler@toradex.com>

Remove some spurious new lines.

Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
---

 arch/arm/boot/dts/tegra124-apalis-eval.dts |  1 -
 arch/arm/boot/dts/tegra124-apalis.dtsi     | 12 ------------
 2 files changed, 13 deletions(-)

diff --git a/arch/arm/boot/dts/tegra124-apalis-eval.dts b/arch/arm/boot/dts/tegra124-apalis-eval.dts
index 653044a..2b5a0f3 100644
--- a/arch/arm/boot/dts/tegra124-apalis-eval.dts
+++ b/arch/arm/boot/dts/tegra124-apalis-eval.dts
@@ -232,7 +232,6 @@
 
 	backlight: backlight {
 		compatible = "pwm-backlight";
-
 		/* BKL1_PWM */
 		pwms = <&pwm 3 5000000>;
 		brightness-levels = <255 231 223 207 191 159 127 0>;
diff --git a/arch/arm/boot/dts/tegra124-apalis.dtsi b/arch/arm/boot/dts/tegra124-apalis.dtsi
index 0819721..6aa4952 100644
--- a/arch/arm/boot/dts/tegra124-apalis.dtsi
+++ b/arch/arm/boot/dts/tegra124-apalis.dtsi
@@ -56,7 +56,6 @@
 
 	pcie-controller at 01003000 {
 		status = "okay";
-
 		avddio-pex-supply = <&vdd_1v05>;
 		avdd-pex-pll-supply = <&vdd_1v05>;
 		avdd-pll-erefe-supply = <&avdd_1v05>;
@@ -85,7 +84,6 @@
 		hdmi at 54280000 {
 			pll-supply = <&reg_1v05_avdd_hdmi_pll>;
 			vdd-supply = <&reg_3v3_avdd_hdmi>;
-
 			nvidia,ddc-i2c-bus = <&hdmi_ddc>;
 			nvidia,hpd-gpio =
 				<&gpio TEGRA_GPIO(N, 7) GPIO_ACTIVE_HIGH>;
@@ -1607,15 +1605,11 @@
 			compatible = "ams,as3722";
 			reg = <0x40>;
 			interrupts = <0 86 IRQ_TYPE_LEVEL_HIGH>;
-
 			ams,system-power-controller;
-
 			#interrupt-cells = <2>;
 			interrupt-controller;
-
 			gpio-controller;
 			#gpio-cells = <2>;
-
 			pinctrl-names = "default";
 			pinctrl-0 = <&as3722_default>;
 
@@ -1790,7 +1784,6 @@
 			reg = <0x4c>;
 			interrupt-parent = <&gpio>;
 			interrupts = <TEGRA_GPIO(I, 6) IRQ_TYPE_LEVEL_LOW>;
-
 			#thermal-sensor-cells = <1>;
 		};
 	};
@@ -1823,7 +1816,6 @@
 	sata at 70020000 {
 		phys = <&{/padctl@7009f000/pads/sata/lanes/sata-0}>;
 		phy-names = "sata-0";
-
 		avdd-supply = <&vdd_1v05>;
 		hvdd-supply = <&reg_3v3>;
 		vddio-supply = <&vdd_1v05>;
@@ -1837,7 +1829,6 @@
 		       <&{/padctl@7009f000/pads/usb2/lanes/usb2-2}>,
 		       <&{/padctl@7009f000/pads/pcie/lanes/pcie-0}>;
 		phy-names = "usb2-0", "usb3-1", "usb2-1", "usb2-2", "usb3-0";
-
 		avddio-pex-supply = <&vdd_1v05>;
 		avdd-pll-erefe-supply = <&avdd_1v05>;
 		avdd-pll-utmip-supply = <&vddio_1v8>;
@@ -1919,7 +1910,6 @@
 			usb2-0 {
 				status = "okay";
 				mode = "otg";
-
 				vbus-supply = <&reg_usbo1_vbus>;
 			};
 
@@ -1927,7 +1917,6 @@
 			usb2-1 {
 				status = "okay";
 				mode = "host";
-
 				vbus-supply = <&reg_usbh_vbus>;
 			};
 
@@ -1935,7 +1924,6 @@
 			usb2-2 {
 				status = "okay";
 				mode = "host";
-
 				vbus-supply = <&reg_usbh_vbus>;
 			};
 
-- 
2.5.5

^ permalink raw reply related

* [PATCH 2/6] apalis-tk1: temp alert pull-up
From: Marcel Ziswiler @ 2016-11-22  0:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479773647-14726-1-git-send-email-marcel.ziswiler@toradex.com>

Pull-up GPIO_PI6 connected to TMP451's ALERT#/THERM2#.

Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
---

 arch/arm/boot/dts/tegra124-apalis.dtsi | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/tegra124-apalis.dtsi b/arch/arm/boot/dts/tegra124-apalis.dtsi
index 6aa4952..0534601 100644
--- a/arch/arm/boot/dts/tegra124-apalis.dtsi
+++ b/arch/arm/boot/dts/tegra124-apalis.dtsi
@@ -1151,11 +1151,11 @@
 				nvidia,enable-input = <TEGRA_PIN_DISABLE>;
 			};
 
-			/* GPIO_PI6 aka TEMP_ALERT_L */
+			/* GPIO_PI6 aka TMP451 ALERT#/THERM2# */
 			pi6 {
 				nvidia,pins = "pi6";
 				nvidia,function = "rsvd1";
-				nvidia,pull = <TEGRA_PIN_PULL_NONE>;
+				nvidia,pull = <TEGRA_PIN_PULL_UP>;
 				nvidia,tristate = <TEGRA_PIN_ENABLE>;
 				nvidia,enable-input = <TEGRA_PIN_ENABLE>;
 			};
-- 
2.5.5

^ permalink raw reply related

* [PATCH 3/6] apalis-tk1: optional displayport hot-plug detect
From: Marcel Ziswiler @ 2016-11-22  0:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479773647-14726-1-git-send-email-marcel.ziswiler@toradex.com>

Configure DP_HPD_PFF0 pin as optional DisplayPort hot-plug detect.

Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
---

 arch/arm/boot/dts/tegra124-apalis.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/tegra124-apalis.dtsi b/arch/arm/boot/dts/tegra124-apalis.dtsi
index 0534601..747ce81 100644
--- a/arch/arm/boot/dts/tegra124-apalis.dtsi
+++ b/arch/arm/boot/dts/tegra124-apalis.dtsi
@@ -255,7 +255,7 @@
 			};
 			dp_hpd_pff0 {
 				nvidia,pins = "dp_hpd_pff0";
-				nvidia,function = "rsvd2";
+				nvidia,function = "dp";
 				nvidia,pull = <TEGRA_PIN_PULL_NONE>;
 				nvidia,tristate = <TEGRA_PIN_DISABLE>;
 				nvidia,enable-input = <TEGRA_PIN_ENABLE>;
-- 
2.5.5

^ permalink raw reply related

* [PATCH 4/6] apalis-tk1: adjust pin muxing for v1.1 hw
From: Marcel Ziswiler @ 2016-11-22  0:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479773647-14726-1-git-send-email-marcel.ziswiler@toradex.com>

Configure Apalis MMC1 D6 GPIO on SDMMC3_CLK_LB_IN as reserved function
without any pull-up/down.

Configure GPIO_PV2 as SD1_CD# according to latest V1.1 HW.

Leave SDMMC3_CLK_LB_OUT muxed as SDMMC3 with output driver enabled aka
not tristated and input driver enabled as well as it features some
magic properties even though the external loopback is disabled and the
internal loopback used as per SDMMC_VENDOR_MISC_CNTRL_0 register's
SDMMC_SPARE1 bits being set to 0xfffd according to the TRM! This pin is
now a not-connect on V1.1 HW in order to avoid any interference.

Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
---

 arch/arm/boot/dts/tegra124-apalis.dtsi | 53 +++++++++++++++-------------------
 1 file changed, 23 insertions(+), 30 deletions(-)

diff --git a/arch/arm/boot/dts/tegra124-apalis.dtsi b/arch/arm/boot/dts/tegra124-apalis.dtsi
index 747ce81..2bfc579 100644
--- a/arch/arm/boot/dts/tegra124-apalis.dtsi
+++ b/arch/arm/boot/dts/tegra124-apalis.dtsi
@@ -414,18 +414,10 @@
 				nvidia,tristate = <TEGRA_PIN_DISABLE>;
 				nvidia,enable-input = <TEGRA_PIN_ENABLE>;
 			};
-			/*
-			 * Don't use MMC1_D6 aka SDMMC3_CLK_LB_IN for now as it
-			 * features some magic properties even though the
-			 * external loopback is disabled and the internal
-			 * loopback used as per SDMMC_VENDOR_MISC_CNTRL_0
-			 * register's SDMMC_SPARE1 bits being set to 0xfffd
-			 * according to the TRM!
-			 */
 			sdmmc3_clk_lb_in_pee5 { /* D6 GPIO */
 				nvidia,pins = "sdmmc3_clk_lb_in_pee5";
-				nvidia,function = "sdmmc3";
-				nvidia,pull = <TEGRA_PIN_PULL_UP>;
+				nvidia,function = "rsvd2";
+				nvidia,pull = <TEGRA_PIN_PULL_NONE>;
 				nvidia,tristate = <TEGRA_PIN_DISABLE>;
 				nvidia,enable-input = <TEGRA_PIN_ENABLE>;
 			};
@@ -520,20 +512,12 @@
 				nvidia,tristate = <TEGRA_PIN_DISABLE>;
 				nvidia,enable-input = <TEGRA_PIN_ENABLE>;
 			};
-			/*
-			 * Don't use SD1_CD# aka SDMMC3_CLK_LB_OUT for now as it
-			 * features some magic properties even though the
-			 * external loopback is disabled and the internal
-			 * loopback used as per SDMMC_VENDOR_MISC_CNTRL_0
-			 * register's SDMMC_SPARE1 bits being set to 0xfffd
-			 * according to the TRM!
-			 */
-			sdmmc3_clk_lb_out_pee4 { /* CD# GPIO */
-				nvidia,pins = "sdmmc3_clk_lb_out_pee4";
-				nvidia,function = "rsvd2";
-				nvidia,pull = <TEGRA_PIN_PULL_NONE>;
+			sdmmc3_cd_n_pv2 { /* CD# GPIO */
+				nvidia,pins = "sdmmc3_cd_n_pv2";
+				nvidia,function = "rsvd3";
+				nvidia,pull = <TEGRA_PIN_PULL_UP>;
 				nvidia,tristate = <TEGRA_PIN_ENABLE>;
-				nvidia,enable-input = <TEGRA_PIN_DISABLE>;
+				nvidia,enable-input = <TEGRA_PIN_ENABLE>;
 			};
 
 			/* Apalis SPDIF */
@@ -1512,13 +1496,6 @@
 				nvidia,tristate = <TEGRA_PIN_ENABLE>;
 				nvidia,enable-input = <TEGRA_PIN_DISABLE>;
 			};
-			sdmmc3_cd_n_pv2 { /* NC */
-				nvidia,pins = "sdmmc3_cd_n_pv2";
-				nvidia,function = "rsvd3";
-				nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
-				nvidia,tristate = <TEGRA_PIN_ENABLE>;
-				nvidia,enable-input = <TEGRA_PIN_DISABLE>;
-			};
 			gpio_x1_aud_px1 { /* NC */
 				nvidia,pins = "gpio_x1_aud_px1";
 				nvidia,function = "rsvd2";
@@ -1568,6 +1545,22 @@
 				nvidia,tristate = <TEGRA_PIN_ENABLE>;
 				nvidia,enable-input = <TEGRA_PIN_DISABLE>;
 			};
+			/*
+			 * Leave SDMMC3_CLK_LB_OUT muxed as SDMMC3 with output
+			 * driver enabled aka not tristated and input driver
+			 * enabled as well as it features some magic properties
+			 * even though the external loopback is disabled and the
+			 * internal loopback used as per
+			 * SDMMC_VENDOR_MISC_CNTRL_0 register's SDMMC_SPARE1
+			 * bits being set to 0xfffd according to the TRM!
+			 */
+			sdmmc3_clk_lb_out_pee4 { /* NC */
+				nvidia,pins = "sdmmc3_clk_lb_out_pee4";
+				nvidia,function = "sdmmc3";
+				nvidia,pull = <TEGRA_PIN_PULL_NONE>;
+				nvidia,tristate = <TEGRA_PIN_DISABLE>;
+				nvidia,enable-input = <TEGRA_PIN_ENABLE>;
+			};
 		};
 	};
 
-- 
2.5.5

^ permalink raw reply related

* [PATCH 5/6] apalis-tk1: working sd card detect on v1.1 hw
From: Marcel Ziswiler @ 2016-11-22  0:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479773647-14726-1-git-send-email-marcel.ziswiler@toradex.com>

Add sd card detect SD1_CD# applicable for V1.1 modules using GPIO_PV2.

Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
---

 arch/arm/boot/dts/tegra124-apalis-eval.dts | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/arch/arm/boot/dts/tegra124-apalis-eval.dts b/arch/arm/boot/dts/tegra124-apalis-eval.dts
index 2b5a0f3..2715692 100644
--- a/arch/arm/boot/dts/tegra124-apalis-eval.dts
+++ b/arch/arm/boot/dts/tegra124-apalis-eval.dts
@@ -187,14 +187,8 @@
 	/* Apalis SD1 */
 	sdhci at 700b0400 {
 		status = "okay";
-		/*
-		 * Don't use SD1_CD# aka SDMMC3_CLK_LB_OUT for now as it
-		 * features some magic properties even though the external
-		 * loopback is disabled and the internal loopback used as per
-		 * SDMMC_VENDOR_MISC_CNTRL_0 register's SDMMC_SPARE1 bits being
-		 * set to 0xfffd according to the TRM!
-		 * cd-gpios = <&gpio TEGRA_GPIO(EE, 4) GPIO_ACTIVE_LOW>;
-		 */
+		/* SD1_CD# */
+		cd-gpios = <&gpio TEGRA_GPIO(V, 2) GPIO_ACTIVE_LOW>;
 		bus-width = <4>;
 		vqmmc-supply = <&vddio_sdmmc3>;
 	};
-- 
2.5.5

^ permalink raw reply related

* [PATCH 6/6] apalis-tk1: update compatibility comment
From: Marcel Ziswiler @ 2016-11-22  0:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479773647-14726-1-git-send-email-marcel.ziswiler@toradex.com>

Now with the new V1.1A HW card detect being implemented update resp.
compatibility information.

Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
---

 arch/arm/boot/dts/tegra124-apalis.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/tegra124-apalis.dtsi b/arch/arm/boot/dts/tegra124-apalis.dtsi
index 2bfc579..2276073 100644
--- a/arch/arm/boot/dts/tegra124-apalis.dtsi
+++ b/arch/arm/boot/dts/tegra124-apalis.dtsi
@@ -44,7 +44,7 @@
 
 /*
  * Toradex Apalis TK1 Module Device Tree
- * Compatible for Revisions 2GB: V1.0A
+ * Compatible for Revisions 2GB: V1.0A, V1.0B, V1.1A
  */
 / {
 	model = "Toradex Apalis TK1";
-- 
2.5.5

^ permalink raw reply related

* [PATCH v10 2/8] power: add power sequence library
From: Peter Chen @ 2016-11-22  0:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1479087359-7547-3-git-send-email-peter.chen@nxp.com>

On Mon, Nov 14, 2016 at 09:35:53AM +0800, Peter Chen wrote:
> We have an well-known problem that the device needs to do some power
> sequence before it can be recognized by related host, the typical
> example like hard-wired mmc devices and usb devices.
> 
> This power sequence is hard to be described at device tree and handled by
> related host driver, so we have created a common power sequence
> library to cover this requirement. The core code has supplied
> some common helpers for host driver, and individual power sequence
> libraries handle kinds of power sequence for devices. The pwrseq
> librares always need to allocate extra instance for compatible
> string match.
> 
> pwrseq_generic is intended for general purpose of power sequence, which
> handles gpios and clocks currently, and can cover other controls in
> future. The host driver just needs to call of_pwrseq_on/of_pwrseq_off
> if only one power sequence is needed, else call of_pwrseq_on_list
> /of_pwrseq_off_list instead (eg, USB hub driver).
> 
> For new power sequence library, it can add its compatible string
> to pwrseq_of_match_table, then the pwrseq core will match it with
> DT's, and choose this library at runtime.
> 

Rafael, would you get any chances to review this version, it makes
some changes according to your comments, I hope this patch set could
be in v4.10-rc1, thanks.

Peter
> Signed-off-by: Peter Chen <peter.chen@nxp.com>
> Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
> Tested-by Joshua Clayton <stillcompiling@gmail.com>
> Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
> Tested-by: Matthias Kaehlcke <mka@chromium.org>
> ---
>  MAINTAINERS                           |   9 ++
>  drivers/power/Kconfig                 |   1 +
>  drivers/power/Makefile                |   1 +
>  drivers/power/pwrseq/Kconfig          |  21 +++
>  drivers/power/pwrseq/Makefile         |   2 +
>  drivers/power/pwrseq/core.c           | 237 ++++++++++++++++++++++++++++++++++
>  drivers/power/pwrseq/pwrseq_generic.c | 183 ++++++++++++++++++++++++++
>  include/linux/power/pwrseq.h          |  60 +++++++++
>  8 files changed, 514 insertions(+)
>  create mode 100644 drivers/power/pwrseq/Kconfig
>  create mode 100644 drivers/power/pwrseq/Makefile
>  create mode 100644 drivers/power/pwrseq/core.c
>  create mode 100644 drivers/power/pwrseq/pwrseq_generic.c
>  create mode 100644 include/linux/power/pwrseq.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3d838cf..066b1e4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9621,6 +9621,15 @@ F:	include/linux/pm_*
>  F:	include/linux/powercap.h
>  F:	drivers/powercap/
>  
> +POWER SEQUENCE LIBRARY
> +M:	Peter Chen <Peter.Chen@nxp.com>
> +T:	git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
> +L:	linux-pm at vger.kernel.org
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/power/pwrseq/
> +F:	drivers/power/pwrseq/
> +F:	include/linux/power/pwrseq.h/
> +
>  POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS
>  M:	Sebastian Reichel <sre@kernel.org>
>  L:	linux-pm at vger.kernel.org
> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
> index 63454b5..c1bb046 100644
> --- a/drivers/power/Kconfig
> +++ b/drivers/power/Kconfig
> @@ -1,3 +1,4 @@
>  source "drivers/power/avs/Kconfig"
>  source "drivers/power/reset/Kconfig"
>  source "drivers/power/supply/Kconfig"
> +source "drivers/power/pwrseq/Kconfig"
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index ff35c71..7db8035 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -1,3 +1,4 @@
>  obj-$(CONFIG_POWER_AVS)		+= avs/
>  obj-$(CONFIG_POWER_RESET)	+= reset/
>  obj-$(CONFIG_POWER_SUPPLY)	+= supply/
> +obj-$(CONFIG_POWER_SEQUENCE)	+= pwrseq/
> diff --git a/drivers/power/pwrseq/Kconfig b/drivers/power/pwrseq/Kconfig
> new file mode 100644
> index 0000000..88f5597
> --- /dev/null
> +++ b/drivers/power/pwrseq/Kconfig
> @@ -0,0 +1,21 @@
> +#
> +# Power Sequence library
> +#
> +
> +menuconfig POWER_SEQUENCE
> +	bool "Power sequence control"
> +	depends on OF
> +	help
> +	   It is used for drivers which needs to do power sequence
> +	   (eg, turn on clock, toggle reset gpio) before the related
> +	   devices can be found by hardware, eg, USB bus.
> +
> +if POWER_SEQUENCE
> +
> +config PWRSEQ_GENERIC
> +	bool "Generic power sequence control"
> +	default y
> +	help
> +	   This is the generic power sequence control library, and is
> +	   supposed to support common power sequence usage.
> +endif
> diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
> new file mode 100644
> index 0000000..ad82389
> --- /dev/null
> +++ b/drivers/power/pwrseq/Makefile
> @@ -0,0 +1,2 @@
> +obj-$(CONFIG_POWER_SEQUENCE) += core.o
> +obj-$(CONFIG_PWRSEQ_GENERIC) += pwrseq_generic.o
> diff --git a/drivers/power/pwrseq/core.c b/drivers/power/pwrseq/core.c
> new file mode 100644
> index 0000000..e3c1fbb
> --- /dev/null
> +++ b/drivers/power/pwrseq/core.c
> @@ -0,0 +1,237 @@
> +/*
> + * core.c	power sequence core file
> + *
> + * Copyright (C) 2016 Freescale Semiconductor, Inc.
> + * Author: Peter Chen <peter.chen@nxp.com>
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2  of
> + * the License as published by the Free Software Foundation.
> + *
> + * 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 should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/slab.h>
> +#include <linux/power/pwrseq.h>
> +
> +static DEFINE_MUTEX(pwrseq_list_mutex);
> +static LIST_HEAD(pwrseq_list);
> +
> +static int pwrseq_get(struct device_node *np, struct pwrseq *p)
> +{
> +	if (p && p->get)
> +		return p->get(np, p);
> +
> +	return -ENOTSUPP;
> +}
> +
> +static int pwrseq_on(struct pwrseq *p)
> +{
> +	if (p && p->on)
> +		return p->on(p);
> +
> +	return -ENOTSUPP;
> +}
> +
> +static void pwrseq_off(struct pwrseq *p)
> +{
> +	if (p && p->off)
> +		p->off(p);
> +}
> +
> +static void pwrseq_put(struct pwrseq *p)
> +{
> +	if (p && p->put)
> +		p->put(p);
> +}
> +
> +static int pwrseq_suspend(struct pwrseq *p)
> +{
> +	if (p && p->suspend)
> +		return p->suspend(p);
> +
> +	return 0;
> +}
> +
> +static int pwrseq_resume(struct pwrseq *p)
> +{
> +	if (p && p->resume)
> +		return p->resume(p);
> +
> +	return 0;
> +}
> +
> +/**
> + * pwrseq_register: add pwrseq instance to global pwrseq list
> + *
> + * @pwrseq: the pwrseq instance
> + */
> +void pwrseq_register(struct pwrseq *pwrseq)
> +{
> +	mutex_lock(&pwrseq_list_mutex);
> +	list_add(&pwrseq->node, &pwrseq_list);
> +	mutex_unlock(&pwrseq_list_mutex);
> +}
> +EXPORT_SYMBOL_GPL(pwrseq_register);
> +
> +/**
> + * pwrseq_unregister: remove pwrseq instance from global pwrseq list
> + *
> + * @pwrseq: the pwrseq instance
> + */
> +void pwrseq_unregister(struct pwrseq *pwrseq)
> +{
> +	mutex_lock(&pwrseq_list_mutex);
> +	list_del(&pwrseq->node);
> +	mutex_unlock(&pwrseq_list_mutex);
> +}
> +EXPORT_SYMBOL_GPL(pwrseq_unregister);
> +
> +static struct pwrseq *pwrseq_find_available_instance(struct device_node *np)
> +{
> +	struct pwrseq *pwrseq;
> +
> +	list_for_each_entry(pwrseq, &pwrseq_list, node) {
> +		if (pwrseq->used)
> +			continue;
> +
> +		/* compare compatible string for pwrseq node */
> +		if (of_match_node(pwrseq->pwrseq_of_match_table, np)) {
> +			pwrseq->used = true;
> +			return pwrseq;
> +		}
> +
> +		/* return generic pwrseq instance */
> +		if (!strcmp(pwrseq->pwrseq_of_match_table->compatible,
> +				"generic")) {
> +			pr_debug("using generic pwrseq instance for %s\n",
> +				np->full_name);
> +			pwrseq->used = true;
> +			return pwrseq;
> +		}
> +	}
> +	pr_warn("Can't find any pwrseq instances for %s\n", np->full_name);
> +
> +	return NULL;
> +}
> +
> +/**
> + * of_pwrseq_on: do power sequence on for device node
> + *
> + * This API is used to power on single device, if the host
> + * controller only needs to handle one child device (this device
> + * node points to), use this API. If multiply devices are needed
> + * to handle on bus, use of_pwrseq_on_list.
> + *
> + * @np: the device node would like to power on
> + *
> + * On successful, it returns pwrseq instance, otherwise an error value.
> + */
> +struct pwrseq *of_pwrseq_on(struct device_node *np)
> +{
> +	struct pwrseq *pwrseq;
> +	int ret;
> +
> +	pwrseq = pwrseq_find_available_instance(np);
> +	if (!pwrseq)
> +		return ERR_PTR(-ENONET);
> +
> +	ret = pwrseq_get(np, pwrseq);
> +	if (ret) {
> +		/* Mark current pwrseq as unused */
> +		pwrseq->used = false;
> +		return ERR_PTR(ret);
> +	}
> +
> +	ret = pwrseq_on(pwrseq);
> +	if (ret)
> +		goto pwr_put;
> +
> +	return pwrseq;
> +
> +pwr_put:
> +	pwrseq_put(pwrseq);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(of_pwrseq_on);
> +
> +/**
> + * of_pwrseq_off: do power sequence off for this pwrseq instance
> + *
> + * This API is used to power off single device, it is the opposite
> + * operation for of_pwrseq_on.
> + *
> + * @pwrseq: the pwrseq instance which related device would like to be off
> + */
> +void of_pwrseq_off(struct pwrseq *pwrseq)
> +{
> +	pwrseq_off(pwrseq);
> +	pwrseq_put(pwrseq);
> +}
> +EXPORT_SYMBOL_GPL(of_pwrseq_off);
> +
> +/**
> + * of_pwrseq_on_list: do power sequence on for list
> + *
> + * This API is used to power on multiple devices at single bus.
> + * If there are several devices on bus (eg, USB bus), uses this
> + * this API. Otherwise, use of_pwrseq_on. After the device
> + * is powered on successfully, it will be added to pwrseq list for
> + * this bus.
> + *
> + * @np: the device node would like to power on
> + * @head: the list head for pwrseq list on this bus
> + *
> + * On successful, it returns 0, otherwise an error value.
> + */
> +int of_pwrseq_on_list(struct device_node *np, struct list_head *head)
> +{
> +	struct pwrseq *pwrseq;
> +	struct pwrseq_list_per_dev *pwrseq_list_node;
> +
> +	pwrseq = of_pwrseq_on(np);
> +	if (IS_ERR(pwrseq))
> +		return PTR_ERR(pwrseq);
> +
> +	pwrseq_list_node = kzalloc(sizeof(*pwrseq_list_node), GFP_KERNEL);
> +	if (!pwrseq_list_node) {
> +		of_pwrseq_off(pwrseq);
> +		return -ENOMEM;
> +	}
> +	pwrseq_list_node->pwrseq = pwrseq;
> +	list_add(&pwrseq_list_node->list, head);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_pwrseq_on_list);
> +
> +/**
> + * of_pwrseq_off_list: do power sequence off for the list
> + *
> + * This API is used to power off all devices on this bus, it is
> + * the opposite operation for of_pwrseq_on_list.
> + *
> + * @head: the list head for pwrseq instance list on this bus
> + */
> +void of_pwrseq_off_list(struct list_head *head)
> +{
> +	struct pwrseq *pwrseq;
> +	struct pwrseq_list_per_dev *pwrseq_list_node, *tmp_node;
> +
> +	list_for_each_entry_safe(pwrseq_list_node, tmp_node, head, list) {
> +		pwrseq = pwrseq_list_node->pwrseq;
> +		of_pwrseq_off(pwrseq);
> +		list_del(&pwrseq_list_node->list);
> +		kfree(pwrseq_list_node);
> +	}
> +}
> +EXPORT_SYMBOL_GPL(of_pwrseq_off_list);
> diff --git a/drivers/power/pwrseq/pwrseq_generic.c b/drivers/power/pwrseq/pwrseq_generic.c
> new file mode 100644
> index 0000000..d7a77f2
> --- /dev/null
> +++ b/drivers/power/pwrseq/pwrseq_generic.c
> @@ -0,0 +1,183 @@
> +/*
> + * pwrseq_generic.c	Generic power sequence handling
> + *
> + * Copyright (C) 2016 Freescale Semiconductor, Inc.
> + * Author: Peter Chen <peter.chen@nxp.com>
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2  of
> + * the License as published by the Free Software Foundation.
> + *
> + * 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 should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/slab.h>
> +
> +#include <linux/power/pwrseq.h>
> +
> +struct pwrseq_generic {
> +	struct pwrseq pwrseq;
> +	struct gpio_desc *gpiod_reset;
> +	struct clk *clks[PWRSEQ_MAX_CLKS];
> +	u32 duration_us;
> +};
> +
> +#define to_generic_pwrseq(p) container_of(p, struct pwrseq_generic, pwrseq)
> +
> +static int pwrseq_generic_alloc_instance(void);
> +static const struct of_device_id generic_id_table[] = {
> +	{ .compatible = "generic",},
> +	{ /* sentinel */ }
> +};
> +
> +static void pwrseq_generic_put(struct pwrseq *pwrseq)
> +{
> +	struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
> +	int clk;
> +
> +	if (pwrseq_gen->gpiod_reset)
> +		gpiod_put(pwrseq_gen->gpiod_reset);
> +
> +	for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++)
> +		clk_put(pwrseq_gen->clks[clk]);
> +
> +	pwrseq_unregister(&pwrseq_gen->pwrseq);
> +	kfree(pwrseq_gen);
> +}
> +
> +static void pwrseq_generic_off(struct pwrseq *pwrseq)
> +{
> +	struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
> +	int clk;
> +
> +	for (clk = PWRSEQ_MAX_CLKS - 1; clk >= 0; clk--)
> +		clk_disable_unprepare(pwrseq_gen->clks[clk]);
> +}
> +
> +static int pwrseq_generic_on(struct pwrseq *pwrseq)
> +{
> +	struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
> +	int clk, ret = 0;
> +	struct gpio_desc *gpiod_reset = pwrseq_gen->gpiod_reset;
> +
> +	for (clk = 0; clk < PWRSEQ_MAX_CLKS && pwrseq_gen->clks[clk]; clk++) {
> +		ret = clk_prepare_enable(pwrseq_gen->clks[clk]);
> +		if (ret) {
> +			pr_err("Can't enable clock, ret=%d\n", ret);
> +			goto err_disable_clks;
> +		}
> +	}
> +
> +	if (gpiod_reset) {
> +		u32 duration_us = pwrseq_gen->duration_us;
> +
> +		if (duration_us <= 10)
> +			udelay(10);
> +		else
> +			usleep_range(duration_us, duration_us + 100);
> +		gpiod_set_value(gpiod_reset, 0);
> +	}
> +
> +	return ret;
> +
> +err_disable_clks:
> +	while (--clk >= 0)
> +		clk_disable_unprepare(pwrseq_gen->clks[clk]);
> +
> +	return ret;
> +}
> +
> +static int pwrseq_generic_get(struct device_node *np, struct pwrseq *pwrseq)
> +{
> +	struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
> +	enum of_gpio_flags flags;
> +	int reset_gpio, clk, ret = 0;
> +
> +	for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++) {
> +		pwrseq_gen->clks[clk] = of_clk_get(np, clk);
> +		if (IS_ERR(pwrseq_gen->clks[clk])) {
> +			ret = PTR_ERR(pwrseq_gen->clks[clk]);
> +			if (ret != -ENOENT)
> +				goto err_put_clks;
> +			pwrseq_gen->clks[clk] = NULL;
> +			break;
> +		}
> +	}
> +
> +	reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &flags);
> +	if (gpio_is_valid(reset_gpio)) {
> +		unsigned long gpio_flags;
> +
> +		if (flags & OF_GPIO_ACTIVE_LOW)
> +			gpio_flags = GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_LOW;
> +		else
> +			gpio_flags = GPIOF_OUT_INIT_HIGH;
> +
> +		ret = gpio_request_one(reset_gpio, gpio_flags,
> +				"pwrseq-reset-gpios");
> +		if (ret)
> +			goto err_put_clks;
> +
> +		pwrseq_gen->gpiod_reset = gpio_to_desc(reset_gpio);
> +		of_property_read_u32(np, "reset-duration-us",
> +				&pwrseq_gen->duration_us);
> +	} else if (reset_gpio == -ENOENT) {
> +		; /* no such gpio */
> +	} else {
> +		ret = reset_gpio;
> +		pr_err("Failed to get reset gpio on %s, err = %d\n",
> +				np->full_name, reset_gpio);
> +		goto err_put_clks;
> +	}
> +
> +	/* allocate new one for later pwrseq instance request */
> +	ret = pwrseq_generic_alloc_instance();
> +	if (ret)
> +		goto err_put_gpio;
> +
> +	return 0;
> +
> +err_put_gpio:
> +	if (pwrseq_gen->gpiod_reset)
> +		gpiod_put(pwrseq_gen->gpiod_reset);
> +err_put_clks:
> +	while (--clk >= 0)
> +		clk_put(pwrseq_gen->clks[clk]);
> +	return ret;
> +}
> +
> +static int pwrseq_generic_alloc_instance(void)
> +{
> +	struct pwrseq_generic *pwrseq_gen;
> +
> +	pwrseq_gen = kzalloc(sizeof(*pwrseq_gen), GFP_KERNEL);
> +	if (!pwrseq_gen)
> +		return -ENOMEM;
> +
> +	pwrseq_gen->pwrseq.pwrseq_of_match_table = generic_id_table;
> +	pwrseq_gen->pwrseq.get = pwrseq_generic_get;
> +	pwrseq_gen->pwrseq.on = pwrseq_generic_on;
> +	pwrseq_gen->pwrseq.off = pwrseq_generic_off;
> +	pwrseq_gen->pwrseq.put = pwrseq_generic_put;
> +
> +	pwrseq_register(&pwrseq_gen->pwrseq);
> +	return 0;
> +}
> +
> +static int __init pwrseq_generic_register(void)
> +{
> +	return pwrseq_generic_alloc_instance();
> +}
> +postcore_initcall(pwrseq_generic_register)
> diff --git a/include/linux/power/pwrseq.h b/include/linux/power/pwrseq.h
> new file mode 100644
> index 0000000..598301a
> --- /dev/null
> +++ b/include/linux/power/pwrseq.h
> @@ -0,0 +1,60 @@
> +#ifndef __LINUX_PWRSEQ_H
> +#define __LINUX_PWRSEQ_H
> +
> +#include <linux/of.h>
> +
> +#define PWRSEQ_MAX_CLKS		3
> +
> +/**
> + * struct pwrseq - the power sequence structure
> + * @pwrseq_of_match_table: the OF device id table this pwrseq library supports
> + * @node: the list pointer to be added to pwrseq list
> + * @get: the API is used to get pwrseq instance from the device node
> + * @on: do power on for this pwrseq instance
> + * @off: do power off for this pwrseq instance
> + * @put: release the resources on this pwrseq instance
> + * @suspend: do suspend operation on this pwrseq instance
> + * @resume: do resume operation on this pwrseq instance
> + * @used: this pwrseq instance is used by device
> + */
> +struct pwrseq {
> +	const struct of_device_id *pwrseq_of_match_table;
> +	struct list_head node;
> +	int (*get)(struct device_node *np, struct pwrseq *p);
> +	int (*on)(struct pwrseq *p);
> +	void (*off)(struct pwrseq *p);
> +	void (*put)(struct pwrseq *p);
> +	int (*suspend)(struct pwrseq *p);
> +	int (*resume)(struct pwrseq *p);
> +	bool used;
> +};
> +
> +/* used for power sequence instance list in one driver */
> +struct pwrseq_list_per_dev {
> +	struct pwrseq *pwrseq;
> +	struct list_head list;
> +};
> +
> +#if IS_ENABLED(CONFIG_POWER_SEQUENCE)
> +void pwrseq_register(struct pwrseq *pwrseq);
> +void pwrseq_unregister(struct pwrseq *pwrseq);
> +struct pwrseq *of_pwrseq_on(struct device_node *np);
> +void of_pwrseq_off(struct pwrseq *pwrseq);
> +int of_pwrseq_on_list(struct device_node *np, struct list_head *head);
> +void of_pwrseq_off_list(struct list_head *head);
> +#else
> +static inline void pwrseq_register(struct pwrseq *pwrseq) {}
> +static inline void pwrseq_unregister(struct pwrseq *pwrseq) {}
> +static inline struct pwrseq *of_pwrseq_on(struct device_node *np)
> +{
> +	return NULL;
> +}
> +void of_pwrseq_off(struct pwrseq *pwrseq) {}
> +int of_pwrseq_on_list(struct device_node *np, struct list_head *head)
> +{
> +	return 0;
> +}
> +void of_pwrseq_off_list(struct list_head *head) {}
> +#endif /* CONFIG_POWER_SEQUENCE */
> +
> +#endif  /* __LINUX_PWRSEQ_H */
> -- 
> 2.7.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 

Best Regards,
Peter Chen

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox