* Re: [PATCH v2] fsldma: add support to 36-bit physical address
From: Timur Tabi @ 2010-11-13 22:43 UTC (permalink / raw)
To: Kumar Gala; +Cc: dan.j.williams, linuxppc-dev, linux-kernel
In-Reply-To: <54AAF9B7-9533-45B8-9C49-A964203AF707@kernel.crashing.org>
On Thu, Nov 11, 2010 at 5:56 AM, Kumar Gala <galak@kernel.crashing.org> wrote:
> Is there any reason we shouldn't set DMA_BIT_MASK(64) since the DMA block programming model allows the address to be 64-bits?
Can you explain that? The DMA registers only have room for 36 bits
for the physical address.
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* [PATCH 3/6] nvram: Always capture start of oops report to NVRAM
From: Jim Keniston @ 2010-11-14 4:15 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20101114041510.9457.92921.stgit@localhost.localdomain>
If we don't have room to capture the entire oops report, capture as much
as possible, starting 150 chars before the "Oops:" line.
Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---
arch/powerpc/platforms/pseries/nvram.c | 91 ++++++++++++++++++++++++++++++++
1 files changed, 91 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 6c88cda..e1bc1a4 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -446,6 +446,87 @@ static size_t capture_last_msgs(const char *old_msgs, size_t old_len,
}
}
+/* Find the last occurrence of needle in haystack, which is haystack_len long.*/
+static const char *strnrstr(const char *haystack, const char *needle,
+ size_t haystack_len)
+{
+ size_t needle_len = strlen(needle);
+ const char *haystack_end = haystack + haystack_len;
+ const char *prev, *next = NULL;
+ do {
+ prev = next;
+ next = strnstr(haystack, needle, haystack_len);
+ if (next) {
+ haystack = next + needle_len;
+ haystack_len = haystack_end - haystack;
+ }
+ } while (next);
+ return prev;
+}
+
+/* The preamble is the last bit of messages logged before the oops. */
+#define PREAMBLE_CHARS 150
+#define OOPS_TAG "Oops: "
+
+/*
+ * Find the beginning of the most recent oops report, back up PREAMBLE_CHARS
+ * characters, and copy up to captured_len characters from there to captured[].
+ * If we can't find the oops, just capture the end of the printk buffer,
+ * if we haven't already.
+ */
+static size_t capture_oops(const char *old_msgs, size_t old_len,
+ const char *new_msgs, size_t new_len,
+ char *captured, size_t capture_len,
+ size_t already_captured)
+{
+ const char *poops; /* Points to the 'O' in "Oops: ..." */
+ const char *preamble;
+ const char *old_end = old_msgs + old_len;
+ const char *new_end = new_msgs + new_len;
+ size_t nc1, nc2;
+
+ if ((poops = strnrstr(new_msgs, OOPS_TAG, new_len)) != NULL) {
+ /* Oops starts in new_msgs -- the most common case. */
+ preamble = poops - PREAMBLE_CHARS;
+ if (preamble >= new_msgs) {
+ /* preamble is also in new_msgs. */
+ nc1 = min(capture_len, (size_t)(new_end - preamble));
+ memcpy(captured, preamble, nc1);
+ nc2 = 0;
+ } else {
+ /* Have to get some of the preamble from old_msgs */
+ nc1 = min((size_t)(new_msgs - preamble), old_len);
+ memcpy(captured, (old_end - nc1), nc1);
+ nc2 = min(new_len, capture_len - nc1);
+ memcpy(captured + nc1, new_msgs, nc2);
+ }
+ } else if ((poops = strnrstr(old_msgs, OOPS_TAG, old_len)) != NULL) {
+ /* Oops starts in old_msgs. */
+ preamble = poops - PREAMBLE_CHARS;
+ if (preamble < old_msgs)
+ preamble = old_msgs;
+ nc1 = min(capture_len, (size_t)(old_end - preamble));
+ memcpy(captured, preamble, nc1);
+ nc2 = min((size_t)(capture_len - nc1), new_len);
+ memcpy(captured + nc1, new_msgs, nc2);
+ } else {
+ /*
+ * Either there was a VERY long oops report that scrolled
+ * out of the printk buffer, or the "Oops" tag is split
+ * across old_msgs and new_msgs, or oopses don't start with
+ * "Oops" anymore. Just capture as much of the last messages
+ * as we think we can squeeze into NVRAM.
+ */
+ if (already_captured)
+ return already_captured;
+ nc1 = capture_last_msgs(old_msgs, old_len, new_msgs,
+ new_len, captured, capture_len);
+ nc2 = 0;
+ }
+
+ return nc1 + nc2;
+}
+
/* our kmsg_dump callback */
static void oops_to_nvram(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason,
@@ -457,6 +538,16 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len,
oops_buf, oops_buf_sz);
+ if (reason == KMSG_DUMP_OOPS) {
+ /*
+ * Ensure that we have the start of the oops report,
+ * and the message(s) leading up to it.
+ */
+ const char *poops = strnrstr(oops_buf, OOPS_TAG, oops_buf_sz);
+ if (!poops || poops < oops_buf + PREAMBLE_CHARS)
+ text_len = capture_oops(old_msgs, old_len, new_msgs,
+ new_len, oops_buf, oops_buf_sz, text_len);
+ }
(void) nvram_write_os_partition(&oops_log_partition, oops_buf,
(int) text_len, ERR_TYPE_KERNEL_PANIC, ++oops_count);
}
^ permalink raw reply related
* [PATCH 2/6] nvram: Capture oops/panic reports in ibm, oops-log partition
From: Jim Keniston @ 2010-11-14 4:15 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20101114041510.9457.92921.stgit@localhost.localdomain>
Create the ibm,oops-log NVRAM partition, and capture the end of the printk
buffer in it when there's an oops or panic. If we can't create the
ibm,oops-log partition, capture the oops/panic report in ibm,rtas-log.
Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---
arch/powerpc/platforms/pseries/nvram.c | 89 ++++++++++++++++++++++++++++++++
1 files changed, 88 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 43d5c52..6c88cda 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -16,6 +16,8 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/kmsg_dump.h>
#include <asm/uaccess.h>
#include <asm/nvram.h>
#include <asm/rtas.h>
@@ -50,11 +52,32 @@ static struct os_partition rtas_log_partition = {
.index = -1
};
+static struct os_partition oops_log_partition = {
+ .name = "ibm,oops-log",
+ .req_size = 4000,
+ .min_size = 2000,
+ .index = -1
+};
+
static const char *valid_os_partitions[] = {
"ibm,rtas-log",
+ "ibm,oops-log",
NULL
};
+static void oops_to_nvram(struct kmsg_dumper *dumper,
+ enum kmsg_dump_reason reason,
+ const char *old_msgs, unsigned long old_len,
+ const char *new_msgs, unsigned long new_len);
+
+static struct kmsg_dumper nvram_kmsg_dumper = {
+ .dump = oops_to_nvram
+};
+
+/* We preallocate oops_buf during init to avoid kmalloc during oops/panic. */
+static size_t oops_buf_sz;
+static char *oops_buf;
+
static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
{
unsigned int i;
@@ -337,9 +360,36 @@ static int __init pseries_nvram_init_os_partition(struct os_partition *part)
return 0;
}
+static void __init nvram_init_oops_partition(int rtas_partition_exists)
+{
+ int rc;
+
+ rc = pseries_nvram_init_os_partition(&oops_log_partition);
+ if (rc != 0) {
+ if (!rtas_partition_exists)
+ return;
+ pr_notice("nvram: Using %s partition to log both"
+ " RTAS errors and oops/panic reports\n",
+ rtas_log_partition.name);
+ memcpy(&oops_log_partition, &rtas_log_partition,
+ sizeof(rtas_log_partition));
+ }
+ oops_buf_sz = oops_log_partition.size - sizeof(struct err_log_info);
+ oops_buf = kmalloc(oops_buf_sz, GFP_KERNEL);
+ rc = kmsg_dump_register(&nvram_kmsg_dumper);
+ if (rc != 0) {
+ pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc);
+ kfree(oops_buf);
+ return;
+ }
+}
+
static int __init pseries_nvram_init_log_partitions(void)
{
- (void) pseries_nvram_init_os_partition(&rtas_log_partition);
+ int rc;
+
+ rc = pseries_nvram_init_os_partition(&rtas_log_partition);
+ nvram_init_oops_partition(rc == 0);
return 0;
}
machine_late_initcall(pseries, pseries_nvram_init_log_partitions);
@@ -373,3 +423,40 @@ int __init pSeries_nvram_init(void)
return 0;
}
+
+/*
+ * Try to capture the last capture_len bytes of the printk buffer. Return
+ * the amount actually captured.
+ */
+static size_t capture_last_msgs(const char *old_msgs, size_t old_len,
+ const char *new_msgs, size_t new_len,
+ char *captured, size_t capture_len)
+{
+ if (new_len >= capture_len) {
+ memcpy(captured, new_msgs + (new_len - capture_len),
+ capture_len);
+ return capture_len;
+ } else {
+ /* Grab the end of old_msgs. */
+ size_t old_tail_len = min(old_len, capture_len - new_len);
+ memcpy(captured, old_msgs + (old_len - old_tail_len),
+ old_tail_len);
+ memcpy(captured + old_tail_len, new_msgs, new_len);
+ return old_tail_len + new_len;
+ }
+}
+
+/* our kmsg_dump callback */
+static void oops_to_nvram(struct kmsg_dumper *dumper,
+ enum kmsg_dump_reason reason,
+ const char *old_msgs, unsigned long old_len,
+ const char *new_msgs, unsigned long new_len)
+{
+ static unsigned int oops_count = 0;
+ size_t text_len;
+
+ text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len,
+ oops_buf, oops_buf_sz);
+ (void) nvram_write_os_partition(&oops_log_partition, oops_buf,
+ (int) text_len, ERR_TYPE_KERNEL_PANIC, ++oops_count);
+}
^ permalink raw reply related
* [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM
From: Jim Keniston @ 2010-11-14 4:15 UTC (permalink / raw)
To: linuxppc-dev
This patch series enables p Series systems to capture oops and panic
reports from the printk buffer into NVRAM, where they can be examined
after reboot using the nvram command.
Patches 1-2 implement the basic feature, and patch 4 adds compression,
so we can capture more of the printk buffer. Patches 3 and 5-6 add
refinements. Patch 5 is not powerpc-specific, and I plan to pursue that
independently on LKML.
This series applies atop the little bug-fix series I posted two days
ago --
"More ppc64 nvram code cleanup"
http://lists.ozlabs.org/pipermail/linuxppc-dev/2010-November/087009.html
-- which in turn applies atop Ben Herrenschmidt's Aug. 2 patch set --
"[RFC] Clean up ppc64 nvram code"
http://lists.ozlabs.org/pipermail/linuxppc-dev/2010-August/084601.html
My patches have been tested against 2.6.37-rc1.
---
Jim Keniston (6):
Shrink our zlib_deflate workspace from 268K to 24K
Slim down zlib_deflate workspace when possible
Add compression to fit more printk output into NVRAM
Always capture start of oops report to NVRAM
Capture oops/panic reports in ibm,oops-log partition
Generalize code for OS partitions in NVRAM
arch/powerpc/include/asm/nvram.h | 3
arch/powerpc/include/asm/rtas.h | 6
arch/powerpc/kernel/nvram_64.c | 31 ++
arch/powerpc/platforms/pseries/nvram.c | 470 ++++++++++++++++++++++++++++----
include/linux/zlib.h | 14 +
lib/zlib_deflate/deflate.c | 33 ++
lib/zlib_deflate/deflate_syms.c | 1
lib/zlib_deflate/defutil.h | 17 +
8 files changed, 504 insertions(+), 71 deletions(-)
--
Jim Keniston
IBM Linux Technology Center
Beaverton, OR
^ permalink raw reply
* [PATCH 1/6] nvram: Generalize code for OS partitions in NVRAM
From: Jim Keniston @ 2010-11-14 4:15 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20101114041510.9457.92921.stgit@localhost.localdomain>
Adapt the functions used to create and write to the RTAS-log partition
to work with any OS-type partition.
Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---
arch/powerpc/include/asm/nvram.h | 3 -
arch/powerpc/kernel/nvram_64.c | 31 ++++++-
arch/powerpc/platforms/pseries/nvram.c | 138 +++++++++++++++++++-------------
3 files changed, 110 insertions(+), 62 deletions(-)
diff --git a/arch/powerpc/include/asm/nvram.h b/arch/powerpc/include/asm/nvram.h
index 457a1a5..3cd90fd 100644
--- a/arch/powerpc/include/asm/nvram.h
+++ b/arch/powerpc/include/asm/nvram.h
@@ -50,7 +50,8 @@ static inline int mmio_nvram_init(void)
extern loff_t nvram_create_partition(const char *name, int sig,
int req_size, int min_size);
-extern int nvram_remove_partition(const char *name, int sig);
+extern int nvram_remove_partition(const char *name, int sig,
+ const char *exceptions[]);
extern int nvram_get_partition_size(loff_t data_index);
extern loff_t nvram_find_partition(const char *name, int sig, int *out_size);
diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
index cda7c3f..3de46cd 100644
--- a/arch/powerpc/kernel/nvram_64.c
+++ b/arch/powerpc/kernel/nvram_64.c
@@ -237,22 +237,45 @@ static unsigned char __init nvram_checksum(struct nvram_header *p)
return c_sum;
}
+/*
+ * Per the criteria passed via nvram_remove_partition(), should this
+ * partition be removed? 1=remove, 0=keep
+ */
+static int nvram_condemn_partition(struct nvram_partition *part,
+ const char *name, int sig, const char *exceptions[])
+{
+ if (part->header.signature != sig)
+ return 0;
+ if (name) {
+ if (strncmp(name, part->header.name, 12))
+ return 0;
+ } else if (exceptions) {
+ const char **except;
+ for (except = exceptions; *except; except++) {
+ if (!strncmp(*except, part->header.name, 12))
+ return 0;
+ }
+ }
+ return 1;
+}
+
/**
* nvram_remove_partition - Remove one or more partitions in nvram
* @name: name of the partition to remove, or NULL for a
* signature only match
* @sig: signature of the partition(s) to remove
+ * @exceptions: When removing all partitions with a matching signature,
+ * leave these alone.
*/
-int __init nvram_remove_partition(const char *name, int sig)
+int __init nvram_remove_partition(const char *name, int sig,
+ const char *exceptions[])
{
struct nvram_partition *part, *prev, *tmp;
int rc;
list_for_each_entry(part, &nvram_partitions, partition) {
- if (part->header.signature != sig)
- continue;
- if (name && strncmp(name, part->header.name, 12))
+ if (!nvram_condemn_partition(part, name, sig, exceptions))
continue;
/* Make partition a free partition */
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 4b705dc..43d5c52 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -30,17 +30,30 @@ static int nvram_fetch, nvram_store;
static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */
static DEFINE_SPINLOCK(nvram_lock);
-static long nvram_error_log_index = -1;
-static long nvram_error_log_size = 0;
-
struct err_log_info {
int error_type;
unsigned int seq_num;
};
-#define NVRAM_MAX_REQ 2079
-#define NVRAM_MIN_REQ 1055
-#define NVRAM_LOG_PART_NAME "ibm,rtas-log"
+struct os_partition {
+ const char *name;
+ int req_size; /* desired size, in bytes */
+ int min_size; /* minimum acceptable size (0 means req_size) */
+ long size; /* size of data portion of partition */
+ long index; /* offset of data portion of partition */
+};
+
+static struct os_partition rtas_log_partition = {
+ .name = "ibm,rtas-log",
+ .req_size = 2079,
+ .min_size = 1055,
+ .index = -1
+};
+
+static const char *valid_os_partitions[] = {
+ "ibm,rtas-log",
+ NULL
+};
static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
{
@@ -134,7 +147,7 @@ static ssize_t pSeries_nvram_get_size(void)
}
-/* nvram_write_error_log
+/* nvram_write_os_partition, nvram_write_error_log
*
* We need to buffer the error logs into nvram to ensure that we have
* the failure information to decode. If we have a severe error there
@@ -156,48 +169,55 @@ static ssize_t pSeries_nvram_get_size(void)
* The 'data' section would look like (in bytes):
* +--------------+------------+-----------------------------------+
* | event_logged | sequence # | error log |
- * |0 3|4 7|8 nvram_error_log_size-1|
+ * |0 3|4 7|8 error_log_size-1|
* +--------------+------------+-----------------------------------+
*
* event_logged: 0 if event has not been logged to syslog, 1 if it has
* sequence #: The unique sequence # for each event. (until it wraps)
* error log: The error log from event_scan
*/
-int nvram_write_error_log(char * buff, int length,
+int nvram_write_os_partition(struct os_partition *part, char * buff, int length,
unsigned int err_type, unsigned int error_log_cnt)
{
int rc;
loff_t tmp_index;
struct err_log_info info;
- if (nvram_error_log_index == -1) {
+ if (part->index == -1) {
return -ESPIPE;
}
- if (length > nvram_error_log_size) {
- length = nvram_error_log_size;
+ if (length > part->size) {
+ length = part->size;
}
info.error_type = err_type;
info.seq_num = error_log_cnt;
- tmp_index = nvram_error_log_index;
+ tmp_index = part->index;
rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index);
if (rc <= 0) {
- printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
+ printk(KERN_ERR "nvram_write_os_partition: Failed nvram_write (%d)\n", rc);
return rc;
}
rc = ppc_md.nvram_write(buff, length, &tmp_index);
if (rc <= 0) {
- printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
+ printk(KERN_ERR "nvram_write_os_partition: Failed nvram_write (%d)\n", rc);
return rc;
}
return 0;
}
+int nvram_write_error_log(char * buff, int length,
+ unsigned int err_type, unsigned int error_log_cnt)
+{
+ return nvram_write_os_partition(&rtas_log_partition, buff, length,
+ err_type, error_log_cnt);
+}
+
/* nvram_read_error_log
*
* Reads nvram for error log for at most 'length'
@@ -209,13 +229,13 @@ int nvram_read_error_log(char * buff, int length,
loff_t tmp_index;
struct err_log_info info;
- if (nvram_error_log_index == -1)
+ if (rtas_log_partition.index == -1)
return -1;
- if (length > nvram_error_log_size)
- length = nvram_error_log_size;
+ if (length > rtas_log_partition.size)
+ length = rtas_log_partition.size;
- tmp_index = nvram_error_log_index;
+ tmp_index = rtas_log_partition.index;
rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index);
if (rc <= 0) {
@@ -244,10 +264,10 @@ int nvram_clear_error_log(void)
int clear_word = ERR_FLAG_ALREADY_LOGGED;
int rc;
- if (nvram_error_log_index == -1)
+ if (rtas_log_partition.index == -1)
return -1;
- tmp_index = nvram_error_log_index;
+ tmp_index = rtas_log_partition.index;
rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index);
if (rc <= 0) {
@@ -258,67 +278,71 @@ int nvram_clear_error_log(void)
return 0;
}
-/* pseries_nvram_init_log_partition
+/* pseries_nvram_init_os_partition
*
- * This will setup the partition we need for buffering the
- * error logs and cleanup partitions if needed.
+ * This set up a partition with an "OS" signature.
*
* The general strategy is the following:
- * 1.) If there is log partition large enough then use it.
- * 2.) If there is none large enough, search
- * for a free partition that is large enough.
- * 3.) If there is not a free partition large enough remove
- * _all_ OS partitions and consolidate the space.
- * 4.) Will first try getting a chunk that will satisfy the maximum
- * error log size (NVRAM_MAX_REQ).
- * 5.) If the max chunk cannot be allocated then try finding a chunk
- * that will satisfy the minum needed (NVRAM_MIN_REQ).
+ * 1.) If a partition with the indicated name already exists...
+ * - If it's large enough, use it.
+ * - Otherwise, recycle it and keep going.
+ * 2.) Search for a free partition that is large enough.
+ * 3.) If there's not a free partition large enough, recycle any obsolete
+ * OS partitions and try again.
+ * 4.) Will first try getting a chunk that will satisfy the requested size.
+ * 5.) If a chunk of the requested size cannot be allocated, then try finding
+ * a chunk that will satisfy the minum needed.
+ *
+ * Returns 0 on success, else -1.
*/
-static int __init pseries_nvram_init_log_partition(void)
+static int __init pseries_nvram_init_os_partition(struct os_partition *part)
{
loff_t p;
int size;
- p = nvram_find_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS, &size);
+ p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size);
/* Found one but too small, remove it */
- if (p && size < NVRAM_MIN_REQ) {
- pr_info("nvram: Found too small "NVRAM_LOG_PART_NAME" partition"
- ",removing it...");
- nvram_remove_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS);
+ if (p && size < part->min_size) {
+ pr_info("nvram: Found too small %s partition,"
+ " removing it...\n", part->name);
+ nvram_remove_partition(part->name, NVRAM_SIG_OS, NULL);
p = 0;
}
/* Create one if we didn't find */
if (!p) {
- p = nvram_create_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS,
- NVRAM_MAX_REQ, NVRAM_MIN_REQ);
- /* No room for it, try to get rid of any OS partition
- * and try again
- */
+ p = nvram_create_partition(part->name, NVRAM_SIG_OS,
+ part->req_size, part->min_size);
if (p == -ENOSPC) {
- pr_info("nvram: No room to create "NVRAM_LOG_PART_NAME
- " partition, deleting all OS partitions...");
- nvram_remove_partition(NULL, NVRAM_SIG_OS);
- p = nvram_create_partition(NVRAM_LOG_PART_NAME,
- NVRAM_SIG_OS, NVRAM_MAX_REQ,
- NVRAM_MIN_REQ);
+ pr_info("nvram: No room to create %s partition, "
+ "deleting any obsolete OS partitions...\n",
+ part->name);
+ nvram_remove_partition(NULL, NVRAM_SIG_OS,
+ valid_os_partitions);
+ p = nvram_create_partition(part->name, NVRAM_SIG_OS,
+ part->req_size, part->min_size);
}
}
if (p <= 0) {
- pr_err("nvram: Failed to find or create "NVRAM_LOG_PART_NAME
- " partition, err %d\n", (int)p);
- return 0;
+ pr_err("nvram: Failed to find or create %s"
+ " partition, err %d\n", part->name, (int)p);
+ return -1;
}
- nvram_error_log_index = p;
- nvram_error_log_size = nvram_get_partition_size(p) -
- sizeof(struct err_log_info);
+ part->index = p;
+ part->size = nvram_get_partition_size(p) - sizeof(struct err_log_info);
return 0;
}
-machine_late_initcall(pseries, pseries_nvram_init_log_partition);
+
+static int __init pseries_nvram_init_log_partitions(void)
+{
+ (void) pseries_nvram_init_os_partition(&rtas_log_partition);
+ return 0;
+}
+machine_late_initcall(pseries, pseries_nvram_init_log_partitions);
int __init pSeries_nvram_init(void)
{
^ permalink raw reply related
* [PATCH 4/6] nvram: Add compression to fit more printk output into NVRAM
From: Jim Keniston @ 2010-11-14 4:15 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20101114041510.9457.92921.stgit@localhost.localdomain>
Capture more than twice as much text from the printk buffer, and
compress it to fit it in the ibm,oops-log NVRAM partition.
Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---
arch/powerpc/include/asm/rtas.h | 6 +
arch/powerpc/platforms/pseries/nvram.c | 195 ++++++++++++++++++++++++++++----
2 files changed, 177 insertions(+), 24 deletions(-)
diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
index 3d35f8a..6e0f4b5 100644
--- a/arch/powerpc/include/asm/rtas.h
+++ b/arch/powerpc/include/asm/rtas.h
@@ -205,10 +205,12 @@ extern void pSeries_log_error(char *buf, unsigned int err_type, int fatal);
#define ERR_FLAG_ALREADY_LOGGED 0x0
#define ERR_FLAG_BOOT 0x1 /* log was pulled from NVRAM on boot */
#define ERR_TYPE_RTAS_LOG 0x2 /* from rtas event-scan */
-#define ERR_TYPE_KERNEL_PANIC 0x4 /* from panic() */
+#define ERR_TYPE_KERNEL_PANIC 0x4 /* from die()/panic() */
+#define ERR_TYPE_KERNEL_PANIC_GZ 0x8 /* ditto, compressed */
/* All the types and not flags */
-#define ERR_TYPE_MASK (ERR_TYPE_RTAS_LOG | ERR_TYPE_KERNEL_PANIC)
+#define ERR_TYPE_MASK \
+ (ERR_TYPE_RTAS_LOG | ERR_TYPE_KERNEL_PANIC | ERR_TYPE_KERNEL_PANIC_GZ)
#define RTAS_DEBUG KERN_DEBUG "RTAS: "
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index e1bc1a4..8e5ed74 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -17,7 +17,10 @@
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/ctype.h>
#include <linux/kmsg_dump.h>
+#include <linux/zlib.h>
#include <asm/uaccess.h>
#include <asm/nvram.h>
#include <asm/rtas.h>
@@ -74,9 +77,24 @@ static struct kmsg_dumper nvram_kmsg_dumper = {
.dump = oops_to_nvram
};
-/* We preallocate oops_buf during init to avoid kmalloc during oops/panic. */
-static size_t oops_buf_sz;
-static char *oops_buf;
+/*
+ * big_oops_buf[] holds the uncompressed text we're capturing. little_oops_buf
+ * holds the compressed text, plus its length. little_oops_buf gets written
+ * to NVRAM.
+ *
+ * We preallocate these buffers during init to avoid kmalloc during oops/panic.
+ */
+static size_t big_oops_buf_sz, little_oops_buf_sz;
+static char *big_oops_buf;
+
+static struct oops_parition_data {
+#define OOPS_PTN_PREFIX_SZ sizeof(unsigned short)
+ unsigned short length;
+ char buf[0];
+} *little_oops_buf;
+
+#define COMPR_LEVEL 6
+static struct z_stream_s stream;
static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
{
@@ -374,13 +392,42 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
memcpy(&oops_log_partition, &rtas_log_partition,
sizeof(rtas_log_partition));
}
- oops_buf_sz = oops_log_partition.size - sizeof(struct err_log_info);
- oops_buf = kmalloc(oops_buf_sz, GFP_KERNEL);
+
+ little_oops_buf_sz = oops_log_partition.size - OOPS_PTN_PREFIX_SZ;
+ little_oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL);
+ if (!little_oops_buf) {
+ pr_err("nvram: No memory for %s partition\n",
+ oops_log_partition.name);
+ return;
+ }
+ /*
+ * Figure compression (preceded by elimination of each line's <n>
+ * severity prefix) will reduce the oops/panic report to at most
+ * 45% of its original size.
+ */
+ big_oops_buf_sz = (little_oops_buf_sz * 100) / 45;
+ big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
+ if (big_oops_buf) {
+ stream.workspace = vmalloc(zlib_deflate_workspacesize());
+ if (!stream.workspace) {
+ pr_err("nvram: No memory for compression workspace; "
+ "skipping compression of %s partition data\n",
+ oops_log_partition.name);
+ kfree(big_oops_buf);
+ big_oops_buf = NULL;
+ }
+ } else {
+ pr_err("No memory for uncompressed %s data; "
+ "skipping compression\n", oops_log_partition.name);
+ stream.workspace = NULL;
+ }
+
rc = kmsg_dump_register(&nvram_kmsg_dumper);
if (rc != 0) {
pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc);
- kfree(oops_buf);
- return;
+ kfree(little_oops_buf);
+ kfree(big_oops_buf);
+ vfree(stream.workspace);
}
}
@@ -527,7 +574,105 @@ static size_t capture_oops(const char *old_msgs, size_t old_len,
return nc1 + nc2;
}
-/* our kmsg_dump callback */
+/*
+ * For a panic, capture the last capture_len chars of the printk buffer.
+ * For an oops, ensure that we have the start of the oops report, and the
+ * message(s) leading up to it.
+ */
+static size_t capture_msgs(enum kmsg_dump_reason reason,
+ const char *old_msgs, size_t old_len,
+ const char *new_msgs, size_t new_len,
+ char *captured, size_t capture_len)
+{
+ size_t text_len;
+
+ text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len,
+ captured, capture_len);
+ if (reason == KMSG_DUMP_OOPS) {
+ const char *poops = strnrstr(captured, OOPS_TAG, capture_len);
+ if (!poops || poops < captured + PREAMBLE_CHARS)
+ text_len = capture_oops(old_msgs, old_len, new_msgs,
+ new_len, captured, capture_len, text_len);
+ }
+ return text_len;
+}
+
+/* Squeeze out each line's <n> severity prefix. */
+static size_t elide_severities(char *buf, size_t len)
+{
+ char *in, *out, *buf_end = buf + len;
+ /* Assume a <n> at the very beginning marks the start of a line. */
+ int newline = 1;
+
+ in = out = buf;
+ while (in < buf_end) {
+ if (newline && in+3 <= buf_end &&
+ *in == '<' && isdigit(in[1]) && in[2] == '>') {
+ in += 3;
+ newline = 0;
+ } else {
+ newline = (*in == '\n');
+ *out++ = *in++;
+ }
+ }
+ return out - buf;
+}
+
+/* Derived from logfs_compress() */
+static int nvram_compress(const void *in, void *out, size_t inlen,
+ size_t outlen)
+{
+ int err, ret;
+
+ ret = -EIO;
+ err = zlib_deflateInit(&stream, COMPR_LEVEL);
+ if (err != Z_OK)
+ goto error;
+
+ stream.next_in = in;
+ stream.avail_in = inlen;
+ stream.total_in = 0;
+ stream.next_out = out;
+ stream.avail_out = outlen;
+ stream.total_out = 0;
+
+ err = zlib_deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END)
+ goto error;
+
+ err = zlib_deflateEnd(&stream);
+ if (err != Z_OK)
+ goto error;
+
+ if (stream.total_out >= stream.total_in)
+ goto error;
+
+ ret = stream.total_out;
+error:
+ return ret;
+}
+
+/* Compress the text from big_oops_buf into little_oops_buf. */
+static int zip_oops(size_t text_len)
+{
+ int zipped_len = nvram_compress(big_oops_buf, little_oops_buf->buf,
+ text_len, little_oops_buf_sz);
+ if (zipped_len < 0) {
+ pr_err("nvram: compression failed; returned %d\n", zipped_len);
+ pr_err("nvram: logging uncompressed oops/panic report\n");
+ return -1;
+ }
+ little_oops_buf->length = (unsigned short) zipped_len;
+ return 0;
+}
+
+/*
+ * This is our kmsg_dump callback, called after an oops or panic report
+ * has been written to the printk buffer. We want to capture as much
+ * of the printk buffer as possible. First, capture as much as we can
+ * that we think will compress sufficiently to fit in the ibm,oops-log
+ * partition. If that's too much, go back and capture uncompressed text.
+ */
static void oops_to_nvram(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason,
const char *old_msgs, unsigned long old_len,
@@ -535,19 +680,25 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
{
static unsigned int oops_count = 0;
size_t text_len;
-
- text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len,
- oops_buf, oops_buf_sz);
- if (reason == KMSG_DUMP_OOPS) {
- /*
- * Ensure that we have the start of the oops report,
- * and the message(s) leading up to it.
- */
- const char *poops = strnrstr(oops_buf, OOPS_TAG, oops_buf_sz);
- if (!poops || poops < oops_buf + PREAMBLE_CHARS)
- text_len = capture_oops(old_msgs, old_len, new_msgs,
- new_len, oops_buf, oops_buf_sz, text_len);
+ unsigned int err_type = ERR_TYPE_KERNEL_PANIC_GZ;
+ int rc = -1;
+
+ if (big_oops_buf) {
+ text_len = capture_msgs(reason, old_msgs, old_len,
+ new_msgs, new_len, big_oops_buf, big_oops_buf_sz);
+ text_len = elide_severities(big_oops_buf, text_len);
+ rc = zip_oops(text_len);
}
- (void) nvram_write_os_partition(&oops_log_partition, oops_buf,
- (int) text_len, ERR_TYPE_KERNEL_PANIC, ++oops_count);
+ if (rc != 0) {
+ text_len = capture_msgs(reason, old_msgs, old_len,
+ new_msgs, new_len, little_oops_buf->buf,
+ little_oops_buf_sz);
+ err_type = ERR_TYPE_KERNEL_PANIC;
+ little_oops_buf->length = (unsigned short) text_len;
+ }
+
+ (void) nvram_write_os_partition(&oops_log_partition,
+ (char*) little_oops_buf,
+ (int) (OOPS_PTN_PREFIX_SZ + little_oops_buf->length),
+ err_type, ++oops_count);
}
^ permalink raw reply related
* [PATCH 6/6] nvram: Shrink our zlib_deflate workspace from 268K to 24K
From: Jim Keniston @ 2010-11-14 4:15 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20101114041510.9457.92921.stgit@localhost.localdomain>
Exploit zlib_deflate_workspacesize2() to create a much smaller
zlib_deflate workspace when capturing oops/panic reports to NVRAM.
Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---
arch/powerpc/platforms/pseries/nvram.c | 11 +++++++----
1 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 8e5ed74..6409cb6 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -17,7 +17,6 @@
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
-#include <linux/vmalloc.h>
#include <linux/ctype.h>
#include <linux/kmsg_dump.h>
#include <linux/zlib.h>
@@ -94,6 +93,8 @@ static struct oops_parition_data {
} *little_oops_buf;
#define COMPR_LEVEL 6
+#define WINDOW_BITS 12
+#define MEM_LEVEL 4
static struct z_stream_s stream;
static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
@@ -408,7 +409,8 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
big_oops_buf_sz = (little_oops_buf_sz * 100) / 45;
big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
if (big_oops_buf) {
- stream.workspace = vmalloc(zlib_deflate_workspacesize());
+ stream.workspace = kmalloc(zlib_deflate_workspacesize2(
+ WINDOW_BITS, MEM_LEVEL), GFP_KERNEL);
if (!stream.workspace) {
pr_err("nvram: No memory for compression workspace; "
"skipping compression of %s partition data\n",
@@ -427,7 +429,7 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc);
kfree(little_oops_buf);
kfree(big_oops_buf);
- vfree(stream.workspace);
+ kfree(stream.workspace);
}
}
@@ -625,7 +627,8 @@ static int nvram_compress(const void *in, void *out, size_t inlen,
int err, ret;
ret = -EIO;
- err = zlib_deflateInit(&stream, COMPR_LEVEL);
+ err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS,
+ MEM_LEVEL, Z_DEFAULT_STRATEGY);
if (err != Z_OK)
goto error;
^ permalink raw reply related
* [PATCH 5/6] nvram: Slim down zlib_deflate workspace when possible
From: Jim Keniston @ 2010-11-14 4:15 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20101114041510.9457.92921.stgit@localhost.localdomain>
Instead of always creating a huge (268K) deflate_workspace with the
maximum compression parameters (windowBits=15, memLevel=8), allow the
caller to obtain a smaller workspace (24K in our case) by specifying
smaller parameter values -- via zlib_deflate_workspacesize2(). In our
case, a small workspace is a win because our choices are to allocate
the workspace when we need it (i.e., during an oops or panic) or
allocate it at boot time. (We do the latter.)
Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---
include/linux/zlib.h | 14 ++++++++++++--
lib/zlib_deflate/deflate.c | 33 ++++++++++++++++++++++++++++++++-
lib/zlib_deflate/deflate_syms.c | 1 +
lib/zlib_deflate/defutil.h | 17 +++++++++++++----
4 files changed, 58 insertions(+), 7 deletions(-)
diff --git a/include/linux/zlib.h b/include/linux/zlib.h
index 40c49cb..3f15036 100644
--- a/include/linux/zlib.h
+++ b/include/linux/zlib.h
@@ -179,11 +179,21 @@ typedef z_stream *z_streamp;
/* basic functions */
+extern int zlib_deflate_workspacesize2 (int windowBits, int memLevel);
+/*
+ Returns the number of bytes that needs to be allocated for a per-
+ stream workspace with the specified parameters. A pointer to this
+ number of bytes should be returned in stream->workspace before
+ calling zlib_deflateInit2(); and the windowBits and memLevel
+ parameters passed to zlib_deflateInit2() must not exceed those
+ passed here.
+*/
+
extern int zlib_deflate_workspacesize (void);
/*
Returns the number of bytes that needs to be allocated for a per-
- stream workspace. A pointer to this number of bytes should be
- returned in stream->workspace before calling zlib_deflateInit().
+ stream workspace with the default (large) windowBits and memLevel
+ parameters.
*/
/*
diff --git a/lib/zlib_deflate/deflate.c b/lib/zlib_deflate/deflate.c
index 46a31e5..cdb207a 100644
--- a/lib/zlib_deflate/deflate.c
+++ b/lib/zlib_deflate/deflate.c
@@ -176,6 +176,7 @@ int zlib_deflateInit2(
deflate_state *s;
int noheader = 0;
deflate_workspace *mem;
+ char *next;
ush *overlay;
/* We overlay pending_buf and d_buf+l_buf. This works since the average
@@ -199,6 +200,21 @@ int zlib_deflateInit2(
strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
return Z_STREAM_ERROR;
}
+
+ /*
+ * Direct the workspace's pointers to the chunks that were allocated
+ * along with the deflate_workspace struct.
+ */
+ next = (char *) mem;
+ next += sizeof(*mem);
+ mem->window_memory = (Byte *) next;
+ next += zlib_deflate_window_memsize(windowBits);
+ mem->prev_memory = (Pos *) next;
+ next += zlib_deflate_prev_memsize(windowBits);
+ mem->head_memory = (Pos *) next;
+ next += zlib_deflate_head_memsize(memLevel);
+ mem->overlay_memory = next;
+
s = (deflate_state *) &(mem->deflate_memory);
strm->state = (struct internal_state *)s;
s->strm = strm;
@@ -1249,5 +1265,20 @@ static block_state deflate_slow(
int zlib_deflate_workspacesize(void)
{
- return sizeof(deflate_workspace);
+ return zlib_deflate_workspacesize2(MAX_WBITS, MAX_MEM_LEVEL);
+}
+
+int zlib_deflate_workspacesize2(int windowBits, int memLevel)
+{
+ if (windowBits < 0) /* undocumented feature: suppress zlib header */
+ windowBits = -windowBits;
+ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL ||
+ windowBits < 9 || windowBits > 15)
+ return -1;
+
+ return sizeof(deflate_workspace)
+ + zlib_deflate_window_memsize(windowBits)
+ + zlib_deflate_prev_memsize(windowBits)
+ + zlib_deflate_head_memsize(memLevel)
+ + zlib_deflate_overlay_memsize(memLevel);
}
diff --git a/lib/zlib_deflate/deflate_syms.c b/lib/zlib_deflate/deflate_syms.c
index ccfe25f..cdf1cdd 100644
--- a/lib/zlib_deflate/deflate_syms.c
+++ b/lib/zlib_deflate/deflate_syms.c
@@ -11,6 +11,7 @@
#include <linux/zlib.h>
EXPORT_SYMBOL(zlib_deflate_workspacesize);
+EXPORT_SYMBOL(zlib_deflate_workspacesize2);
EXPORT_SYMBOL(zlib_deflate);
EXPORT_SYMBOL(zlib_deflateInit2);
EXPORT_SYMBOL(zlib_deflateEnd);
diff --git a/lib/zlib_deflate/defutil.h b/lib/zlib_deflate/defutil.h
index 6b15a90..b640b64 100644
--- a/lib/zlib_deflate/defutil.h
+++ b/lib/zlib_deflate/defutil.h
@@ -241,12 +241,21 @@ typedef struct deflate_state {
typedef struct deflate_workspace {
/* State memory for the deflator */
deflate_state deflate_memory;
- Byte window_memory[2 * (1 << MAX_WBITS)];
- Pos prev_memory[1 << MAX_WBITS];
- Pos head_memory[1 << (MAX_MEM_LEVEL + 7)];
- char overlay_memory[(1 << (MAX_MEM_LEVEL + 6)) * (sizeof(ush)+2)];
+ Byte *window_memory;
+ Pos *prev_memory;
+ Pos *head_memory;
+ char *overlay_memory;
} deflate_workspace;
+#define zlib_deflate_window_memsize(windowBits) \
+ (2 * (1 << (windowBits)) * sizeof(Byte))
+#define zlib_deflate_prev_memsize(windowBits) \
+ ((1 << (windowBits)) * sizeof(Pos))
+#define zlib_deflate_head_memsize(memLevel) \
+ ((1 << ((memLevel)+7)) * sizeof(Pos))
+#define zlib_deflate_overlay_memsize(memLevel) \
+ ((1 << ((memLevel)+6)) * (sizeof(ush)+2))
+
/* Output a byte on the stream.
* IN assertion: there is enough room in pending_buf.
*/
^ permalink raw reply related
* Re: [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM
From: Jim Keniston @ 2010-11-14 4:36 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <20101114041510.9457.92921.stgit@localhost.localdomain>
On Sat, 2010-11-13 at 20:15 -0800, Jim Keniston wrote:
> This patch series enables p Series systems to capture oops and panic
> reports from the printk buffer into NVRAM, where they can be examined
> after reboot using the nvram command.
>
Here's a patch to the nvram command to add --unzip and --ascii options,
for examination of oops/panic reports captured in ibm,oops-log or
ibm,rtas-log.
The nvram command is part of powerpc-utils --
git://powerpc-utils.git.sourceforge.net/gitroot/powerpc-utils/powerpc-utils
You can build it using
cc nvram.c -ldl -lz -o nvram
BTW, as far as I can tell, the zlib_deflate code in the kernel can't
produce the header that the gunzip command wants -- hence the reliance
on libz in the nvram command.
Jim
---
src/nvram.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 136 insertions(+), 0 deletions(-)
diff --git a/src/nvram.c b/src/nvram.c
index d25e073..e00ae12 100644
--- a/src/nvram.c
+++ b/src/nvram.c
@@ -43,6 +43,7 @@
#include <glob.h>
#include <getopt.h>
#include <inttypes.h>
+#include <zlib.h>
#include "nvram.h"
@@ -62,6 +63,8 @@ static struct option long_options[] = {
{"print-event-scan", no_argument, NULL, 'E'},
{"partitions", no_argument, NULL, 'P'},
{"dump", required_argument, NULL, 'd'},
+ {"ascii", required_argument, NULL, 'a'},
+ {"unzip", required_argument, NULL, 'z'},
{"nvram-file", required_argument, NULL, 'n'},
{"nvram-size", required_argument, NULL, 's'},
{"update-config", required_argument, NULL, 'u'},
@@ -99,6 +102,10 @@ help(void)
" print NVRAM paritition header info\n"
" --dump <name>\n"
" raw dump of partition (use --partitions to see names)\n"
+ " --ascii <name>\n"
+ " print partition contents as ASCII text\n"
+ " --unzip <name>\n"
+ " decompress and print compressed data from partition\n"
" --nvram-file <path>\n"
" specify alternate nvram data file (default is /dev/nvram)\n"
" --nvram-size\n"
@@ -1189,6 +1196,121 @@ dump_raw_partition(struct nvram *nvram, char *name)
}
/**
+ * dump_ascii_partition
+ * @brief ASCII data dump of a partition, excluding header
+ *
+ * @param nvram nvram struct containing partition
+ * @param name name of partition to dump
+ * @return 0 on success, !0 otherwise
+ *
+ * Partition subheaders, if any, are dumped along with the rest of the data.
+ * We substitute periods for unprintable characters.
+ */
+int
+dump_ascii_partition(struct nvram *nvram, char *name)
+{
+ struct partition_header *phead;
+ char *start, *end, *c;
+
+ phead = nvram_find_partition(nvram, 0, name, NULL);
+ if (!phead) {
+ err_msg("there is no %s partition!\n", name);
+ return -1;
+ }
+
+ start = (char*) phead;
+ end = start + phead->length * NVRAM_BLOCK_SIZE;
+ start += sizeof(*phead); /* Skip partition header. */
+ for (c = start; c < end; c++) {
+ if (isprint(*c) || isspace(*c))
+ putchar(*c);
+ else
+ putchar('.');
+ }
+ /* Always end with a newline.*/
+ putchar('\n');
+ return 0;
+}
+
+int
+dump_zipped_text(char *zipped_text, unsigned int zipped_length)
+{
+ z_stream strm;
+ int result;
+ char unzipped_text[4096];
+
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = zipped_length;
+ strm.next_in = zipped_text;
+ result = inflateInit(&strm);
+ if (result != Z_OK) {
+ err_msg("can't decompress text: inflateInit() returned %d\n", result);
+ return -1;
+ }
+
+ do {
+ strm.avail_out = 4096;
+ strm.next_out = unzipped_text;
+ result = inflate(&strm, Z_NO_FLUSH);
+ switch (result) {
+ case Z_STREAM_ERROR:
+ case Z_NEED_DICT:
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ err_msg("can't decompress text: inflate() returned %d\n", result);
+ (void) inflateEnd(&strm);
+ return -1;
+ }
+ if (fwrite(unzipped_text, 4096 - strm.avail_out, 1, stdout) != 1) {
+ err_msg("can't decompress text: fwrite() failed\n");
+ (void) inflateEnd(&strm);
+ return -1;
+ }
+ } while (strm.avail_out == 0);
+
+ (void) inflateEnd(&strm);
+ return 0;
+}
+
+/**
+ * unzip_partition
+ * @brief Uncompress and print compressed data from a partition.
+ *
+ * @param nvram nvram struct containing partition
+ * @param name name of partition to dump
+ * @return 0 on success, !0 otherwise
+ */
+int
+unzip_partition(struct nvram *nvram, char *name)
+{
+ struct partition_header *phead;
+ char *start, *next;
+ unsigned short zipped_length;
+
+ phead = nvram_find_partition(nvram, 0, name, NULL);
+ if (!phead) {
+ err_msg("there is no %s partition!\n", name);
+ return -1;
+ }
+
+ start = (char*) phead;
+ next = start + sizeof(*phead); /* Skip partition header. */
+ next += sizeof(struct err_log_info); /* Skip sub-header. */
+ zipped_length = *((unsigned short*) next);
+ next += sizeof(unsigned short); /* Skip compressed length. */
+
+ if ((next-start) + zipped_length > phead->length * NVRAM_BLOCK_SIZE) {
+ err_msg("bogus size for compressed data in partition %s: %u\n", name,
+ zipped_length);
+ return -1;
+ }
+
+ return dump_zipped_text(next, zipped_length);
+}
+
+/**
* print_of_config_part
* @brief Print the name/value pairs of a partition
*
@@ -1476,6 +1598,8 @@ main (int argc, char *argv[])
int print_event_scan = 0;
int print_config_var = 0;
char *dump_name = NULL;
+ char *ascii_name = NULL;
+ char *zip_name = NULL;
char *update_config_var = NULL;
char *config_pname = "common";
@@ -1504,6 +1628,12 @@ main (int argc, char *argv[])
case 'd': /* dump */
dump_name = optarg;
break;
+ case 'a': /* ASCII dump */
+ ascii_name = optarg;
+ break;
+ case 'z': /* dump compressed data */
+ zip_name = optarg;
+ break;
case 'n': /* nvram-file */
nvram.filename = optarg;
break;
@@ -1641,6 +1771,12 @@ main (int argc, char *argv[])
if (dump_name)
if (dump_raw_partition(&nvram, dump_name) != 0)
ret = -1;
+ if (ascii_name)
+ if (dump_ascii_partition(&nvram, ascii_name) != 0)
+ ret = -1;
+ if (zip_name)
+ if (unzip_partition(&nvram, zip_name) != 0)
+ ret = -1;
err_exit:
if (nvram.data)
^ permalink raw reply related
* Re: [PATCH 2/2] ucc_geth: Fix deadlock
From: Joakim Tjernlund @ 2010-11-14 14:43 UTC (permalink / raw)
To: Anton Vorontsov; +Cc: netdev, linuxppc-dev
In-Reply-To: <20101112140947.GB28223@oksana.dev.rtsoft.ru>
Anton Vorontsov <cbouatmailru@gmail.com> wrote on 2010/11/12 15:09:47:
>
> On Fri, Nov 12, 2010 at 02:55:09PM +0100, Joakim Tjernlund wrote:
> > This script:
> > while [ 1==1 ] ; do ifconfig eth0 up; usleep 1950000 ;ifconfig eth0 down; dmesg -c ;done
> > causes in just a second or two:
> > INFO: task ifconfig:572 blocked for more than 120 seconds.
> [...]
> > The reason appears to be ucc_geth_stop meets adjust_link as the
> > PHY reports PHY changes. I belive adjust_link hangs somewhere,
> > holding the PHY lock, because ucc_geth_stop disabled the
> > controller HW.
> > Fix is to stop the PHY before disabling the controller.
> >
> > Signed-off-by: Joakim Tjernlund <Joakim.Tjernlund@transmode.se>
>
> It's unclear where exactly adjust_link() hangs, but the patch
> looks as the right thing overall.
Yes, I too cannot find where it is hanging, just that it is hanging somewhere.
I am starting to think it is hanging somewhere else. Anyhow, the hang
goes away 100% when this patch is applied.
Jocke
^ permalink raw reply
* Re: [PATCH v2] fsldma: add support to 36-bit physical address
From: Kumar Gala @ 2010-11-15 15:16 UTC (permalink / raw)
To: Timur Tabi; +Cc: dan.j.williams, linuxppc-dev, linux-kernel
In-Reply-To: <AANLkTi=1nutREorMFY2VJyUYQD0SHfiSeskw-u22P+R-@mail.gmail.com>
On Nov 13, 2010, at 4:43 PM, Timur Tabi wrote:
> On Thu, Nov 11, 2010 at 5:56 AM, Kumar Gala =
<galak@kernel.crashing.org> wrote:
>=20
>> Is there any reason we shouldn't set DMA_BIT_MASK(64) since the DMA =
block programming model allows the address to be 64-bits?
>=20
> Can you explain that? The DMA registers only have room for 36 bits
> for the physical address.
The programming model (if you look at the free-space in the registers =
and data structures) supports a 64-bit address. I'm trying to avoid =
changing the driver in the future if we have >36-bit. However this is =
such a minor worry that I'll stop and just ack the patch as is.
- k=
^ permalink raw reply
* Re: [PATCH v2] fsldma: add support to 36-bit physical address
From: Timur Tabi @ 2010-11-15 16:13 UTC (permalink / raw)
To: Kumar Gala; +Cc: dan.j.williams, linuxppc-dev, linux-kernel
In-Reply-To: <3B38AD35-39A2-4A54-8109-65D6DE436227@kernel.crashing.org>
On Mon, Nov 15, 2010 at 9:16 AM, Kumar Gala <galak@kernel.crashing.org> wro=
te:
> The programming model (if you look at the free-space in the registers and=
data structures) supports a 64-bit address. =A0I'm trying to avoid changin=
g the driver in the future if we have >36-bit. =A0However this is such a mi=
nor worry that I'll stop and just ack the patch as is.
I must still be missing something. I'm looking at the description of
the SATR register in the MPC8572 RM, and it shows this:
0 - 3 | 4 - 5 | 6 | 7 | 8 - 11 | 12 - 15 | 16-21 | 22-=
31
--- | STFLOWLVL | SPCIORDER | SSME | STRANSINT | SREADTTYPE | --- | ES=
AD
The most that we can extend ESAD to is 16 bits, for a total of a
48-bit physical address. Where are the other 16 bits supposed to go?
--=20
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* [PATCH 1/2] misc: at24: parse OF-data, too
From: Wolfram Sang @ 2010-11-15 17:25 UTC (permalink / raw)
To: devicetree-discuss; +Cc: linuxppc-dev
Information about the pagesize and read-only-status may also come from
the devicetree. Parse this data, too, and act accordingly. While we are
here, change the initialization printout a bit. write_max is useful to
know to detect performance bottlenecks, the rest is superfluous.
Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
---
Grant: As mentioned at ELCE10, I could pretty much respin this old approach I
tried roughly a year ago (just with archdata then). If the approach and docs
are good, I am fine with the patches entering via one of your trees.
Documentation/powerpc/dts-bindings/eeprom.txt | 28 ++++++++++++++++++++++
drivers/misc/eeprom/at24.c | 33 ++++++++++++++++++++-----
2 files changed, 53 insertions(+), 6 deletions(-)
create mode 100644 Documentation/powerpc/dts-bindings/eeprom.txt
diff --git a/Documentation/powerpc/dts-bindings/eeprom.txt b/Documentation/powerpc/dts-bindings/eeprom.txt
new file mode 100644
index 0000000..4342c10
--- /dev/null
+++ b/Documentation/powerpc/dts-bindings/eeprom.txt
@@ -0,0 +1,28 @@
+EEPROMs (I2C)
+
+Required properties:
+
+ - compatible : should be "<manufacturer>,<type>"
+ If there is no specific driver for <manufacturer>, a generic
+ driver based on <type> is selected. Possible types are:
+ 24c00, 24c01, 24c02, 24c04, 24c08, 24c16, 24c32, 24c64,
+ 24c128, 24c256, 24c512, 24c1024, spd
+
+ - reg : the I2C address of the EEPROM
+
+Optional properties:
+
+ - pagesize : the length of the pagesize for writing. Please consult the
+ manual of your device, that value varies a lot. A wrong value
+ may result in data loss! If not specified, a safety value of
+ '1' is used which will be very slow.
+
+ - read-only: this parameterless property disables writes to the eeprom
+
+Example:
+
+eeprom@52 {
+ compatible = "atmel,24c32";
+ reg = <0x52>;
+ pagesize = <32>;
+};
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index 559b0b3..aaf16cb 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -20,6 +20,7 @@
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
+#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/i2c/at24.h>
@@ -457,6 +458,27 @@ static ssize_t at24_macc_write(struct memory_accessor *macc, const char *buf,
/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_OF
+static void at24_get_ofdata(struct i2c_client *client,
+ struct at24_platform_data *chip)
+{
+ const u32 *val;
+ struct device_node *node = client->dev.of_node;
+
+ if (node) {
+ if (of_get_property(node, "read-only", NULL))
+ chip->flags |= AT24_FLAG_READONLY;
+ val = of_get_property(node, "pagesize", NULL);
+ if (val)
+ chip->page_size = *val;
+ }
+}
+#else
+static void at24_get_ofdata(struct i2c_client *client,
+ struct at24_platform_data *chip)
+{ }
+#endif /* CONFIG_OF */
+
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct at24_platform_data chip;
@@ -485,6 +507,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
*/
chip.page_size = 1;
+ /* update chipdata if OF is present */
+ at24_get_ofdata(client, &chip);
+
chip.setup = NULL;
chip.context = NULL;
}
@@ -597,19 +622,15 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
i2c_set_clientdata(client, at24);
- dev_info(&client->dev, "%zu byte %s EEPROM %s\n",
+ dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n",
at24->bin.size, client->name,
- writable ? "(writable)" : "(read-only)");
+ writable ? "writable" : "read-only", at24->write_max);
if (use_smbus == I2C_SMBUS_WORD_DATA ||
use_smbus == I2C_SMBUS_BYTE_DATA) {
dev_notice(&client->dev, "Falling back to %s reads, "
"performance will suffer\n", use_smbus ==
I2C_SMBUS_WORD_DATA ? "word" : "byte");
}
- dev_dbg(&client->dev,
- "page_size %d, num_addresses %d, write_max %d, use_smbus %d\n",
- chip.page_size, num_addresses,
- at24->write_max, use_smbus);
/* export data to kernel code */
if (chip.setup)
--
1.7.2.3
^ permalink raw reply related
* [PATCH 2/2] powerpc: pcm030/032: add pagesize to dts
From: Wolfram Sang @ 2010-11-15 17:25 UTC (permalink / raw)
To: devicetree-discuss; +Cc: linuxppc-dev
In-Reply-To: <1289841916-3825-1-git-send-email-w.sang@pengutronix.de>
Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
---
arch/powerpc/boot/dts/pcm030.dts | 1 +
arch/powerpc/boot/dts/pcm032.dts | 3 ++-
2 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/boot/dts/pcm030.dts b/arch/powerpc/boot/dts/pcm030.dts
index 8a4ec30..e7c36bc 100644
--- a/arch/powerpc/boot/dts/pcm030.dts
+++ b/arch/powerpc/boot/dts/pcm030.dts
@@ -259,6 +259,7 @@
eeprom@52 {
compatible = "catalyst,24c32";
reg = <0x52>;
+ pagesize = <32>;
};
};
diff --git a/arch/powerpc/boot/dts/pcm032.dts b/arch/powerpc/boot/dts/pcm032.dts
index 85d857a..e175e2c 100644
--- a/arch/powerpc/boot/dts/pcm032.dts
+++ b/arch/powerpc/boot/dts/pcm032.dts
@@ -257,8 +257,9 @@
reg = <0x51>;
};
eeprom@52 {
- compatible = "at24,24c32";
+ compatible = "catalyst,24c32";
reg = <0x52>;
+ pagesize = <32>;
};
};
--
1.7.2.3
^ permalink raw reply related
* Re: [PATCH 2/2] powerpc: pcm030/032: add pagesize to dts
From: Anton Vorontsov @ 2010-11-15 17:32 UTC (permalink / raw)
To: Wolfram Sang; +Cc: linuxppc-dev, devicetree-discuss
In-Reply-To: <1289841916-3825-2-git-send-email-w.sang@pengutronix.de>
On Mon, Nov 15, 2010 at 06:25:16PM +0100, Wolfram Sang wrote:
> Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
> ---
> arch/powerpc/boot/dts/pcm030.dts | 1 +
> arch/powerpc/boot/dts/pcm032.dts | 3 ++-
> 2 files changed, 3 insertions(+), 1 deletions(-)
>
> diff --git a/arch/powerpc/boot/dts/pcm030.dts b/arch/powerpc/boot/dts/pcm030.dts
> index 8a4ec30..e7c36bc 100644
> --- a/arch/powerpc/boot/dts/pcm030.dts
> +++ b/arch/powerpc/boot/dts/pcm030.dts
> @@ -259,6 +259,7 @@
> eeprom@52 {
> compatible = "catalyst,24c32";
> reg = <0x52>;
> + pagesize = <32>;
I think you'd better drop the pagesize property altogether, and
instead make the compatible string more specific (if needed at
all. are there any 'catalyst,24c32' chips with pagesize != 32?)
Thanks,
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: [PATCH v2] fsldma: add support to 36-bit physical address
From: Kumar Gala @ 2010-11-15 15:17 UTC (permalink / raw)
To: Li Yang; +Cc: dan.j.williams, linuxppc-dev, linux-kernel
In-Reply-To: <1289477789-10651-1-git-send-email-leoli@freescale.com>
On Nov 11, 2010, at 6:16 AM, Li Yang wrote:
> Expand the dma_mask of fsldma device to 36-bit, indicating that the
> DMA engine can deal with 36-bit physical address and does not need
> the SWIOTLB to create bounce buffer for it when doing dma_map_*().
>
> Signed-off-by: Li Yang <leoli@freescale.com>
> ---
> Add more detailed commit message
>
> drivers/dma/fsldma.c | 4 +++-
> 1 files changed, 3 insertions(+), 1 deletions(-)
Acked-by: Kumar Gala <galak@kernel.crashing.org>
- k
^ permalink raw reply
* Re: [PATCH v2] fsldma: add support to 36-bit physical address
From: Kumar Gala @ 2010-11-15 17:43 UTC (permalink / raw)
To: Timur Tabi; +Cc: dan.j.williams, linuxppc-dev, linux-kernel
In-Reply-To: <AANLkTin8+13mr-tV-2iCG+zGRfF6xZ23cN+=44dsaQor@mail.gmail.com>
On Nov 15, 2010, at 10:13 AM, Timur Tabi wrote:
> On Mon, Nov 15, 2010 at 9:16 AM, Kumar Gala =
<galak@kernel.crashing.org> wrote:
>=20
>> The programming model (if you look at the free-space in the registers =
and data structures) supports a 64-bit address. I'm trying to avoid =
changing the driver in the future if we have >36-bit. However this is =
such a minor worry that I'll stop and just ack the patch as is.
>=20
> I must still be missing something. I'm looking at the description of
> the SATR register in the MPC8572 RM, and it shows this:
>=20
> 0 - 3 | 4 - 5 | 6 | 7 | 8 - 11 | 12 - 15 | 16-21 =
| 22-31
> --- | STFLOWLVL | SPCIORDER | SSME | STRANSINT | SREADTTYPE | --- | =
ESAD
>=20
> The most that we can extend ESAD to is 16 bits, for a total of a
> 48-bit physical address. Where are the other 16 bits supposed to go?
I was looking at the link addresses. I stand corrected so our max is =
48-bits.
- k=
^ permalink raw reply
* Re: [PATCH v2] fsldma: add support to 36-bit physical address
From: Scott Wood @ 2010-11-15 17:53 UTC (permalink / raw)
To: Kumar Gala; +Cc: Timur Tabi, linuxppc-dev, dan.j.williams, linux-kernel
In-Reply-To: <72D46FED-AFC8-4599-ADB0-2A2B634CCE48@kernel.crashing.org>
On Mon, 15 Nov 2010 11:43:12 -0600
Kumar Gala <galak@kernel.crashing.org> wrote:
>
> On Nov 15, 2010, at 10:13 AM, Timur Tabi wrote:
>
> > On Mon, Nov 15, 2010 at 9:16 AM, Kumar Gala <galak@kernel.crashing.org> wrote:
> >
> >> The programming model (if you look at the free-space in the registers and data structures) supports a 64-bit address. I'm trying to avoid changing the driver in the future if we have >36-bit. However this is such a minor worry that I'll stop and just ack the patch as is.
> >
> > I must still be missing something. I'm looking at the description of
> > the SATR register in the MPC8572 RM, and it shows this:
> >
> > 0 - 3 | 4 - 5 | 6 | 7 | 8 - 11 | 12 - 15 | 16-21 | 22-31
> > --- | STFLOWLVL | SPCIORDER | SSME | STRANSINT | SREADTTYPE | --- | ESAD
> >
> > The most that we can extend ESAD to is 16 bits, for a total of a
> > 48-bit physical address. Where are the other 16 bits supposed to go?
>
> I was looking at the link addresses. I stand corrected so our max is 48-bits.
Looks like 42 bits -- just because bits 16-21 could be used to extend
ESAD doesn't mean that they have been.
-Scott
^ permalink raw reply
* [PATCH v2] PPC4xx: Adding PCI(E) MSI support
From: tmarri @ 2010-11-15 20:15 UTC (permalink / raw)
To: linuxppc-dev; +Cc: tmarri
From: Tirumala Marri <tmarri@apm.com>
This patch adds MSI support for 440SPe, 460Ex, 460Sx and 405Ex.
Signed-off-by: Tirumala R Marri <tmarri@apm.com>
---
v1:
* Get rid of bitmap functions.
* Remove irq mapping as each MSI is tied to UIC.
* Cleaning up of prints.
v2:
* Remove or add blank lines at appropriate places.
* Added BITMAP as it is easy to request and free the MSIs
* Removed UPPER_4BITS_OF36BIT & LOWER_32BITS_OF36BIT;
* Remove unused feature variable.
* Remove initialization of "virq".
* remove static int_no varaible and replace with bitmap.
* Eliminated reading count from DTS tree and added a macro.
* Remove printK.
* Remove else in setup_irqs.
* Free interrupts in teardown_msi_interrupts().
* Print contraints in check_device().
* Replace ioremap with of_iomap().
* Use msi_data in setup_pcieh_hw().
* Don't unmap in the setup_pcieh_hw().
* don't use WARN_ON.
* Remove ppc4xx_msi_ids[].
---
arch/powerpc/boot/dts/canyonlands.dts | 18 ++
arch/powerpc/boot/dts/katmai.dts | 18 ++
arch/powerpc/boot/dts/kilauea.dts | 28 +++
arch/powerpc/boot/dts/redwood.dts | 20 ++
arch/powerpc/platforms/40x/Kconfig | 2 +
arch/powerpc/platforms/44x/Kconfig | 6 +
arch/powerpc/sysdev/Kconfig | 7 +
arch/powerpc/sysdev/Makefile | 1 +
arch/powerpc/sysdev/ppc4xx_msi.c | 311 +++++++++++++++++++++++++++++++++
9 files changed, 411 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/sysdev/ppc4xx_msi.c
diff --git a/arch/powerpc/boot/dts/canyonlands.dts b/arch/powerpc/boot/dts/canyonlands.dts
index a303703..5a8e04e 100644
--- a/arch/powerpc/boot/dts/canyonlands.dts
+++ b/arch/powerpc/boot/dts/canyonlands.dts
@@ -519,5 +519,23 @@
0x0 0x0 0x0 0x3 &UIC3 0x12 0x4 /* swizzled int C */
0x0 0x0 0x0 0x4 &UIC3 0x13 0x4 /* swizzled int D */>;
};
+
+ MSI: ppc4xx-msi@C10000000 {
+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+ reg = < 0xC 0x10000000 0x100>;
+ sdr-base = <0x36C>;
+ msi-data = <0x00000000>;
+ msi-mask = <0x44440000>;
+ interrupt-count = <3>;
+ interrupts = <0 1 2 3>;
+ interrupt-parent = <&UIC3>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC3 0x18 1
+ 1 &UIC3 0x19 1
+ 2 &UIC3 0x1A 1
+ 3 &UIC3 0x1B 1>;
+ };
};
};
diff --git a/arch/powerpc/boot/dts/katmai.dts b/arch/powerpc/boot/dts/katmai.dts
index 7c3be5e..f913dbe 100644
--- a/arch/powerpc/boot/dts/katmai.dts
+++ b/arch/powerpc/boot/dts/katmai.dts
@@ -442,6 +442,24 @@
0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>;
};
+ MSI: ppc4xx-msi@400300000 {
+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+ reg = < 0x4 0x00300000 0x100>;
+ sdr-base = <0x3B0>;
+ msi-data = <0x00000000>;
+ msi-mask = <0x44440000>;
+ interrupt-count = <3>;
+ interrupts =<0 1 2 3>;
+ interrupt-parent = <&UIC0>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC0 0xC 1
+ 1 &UIC0 0x0D 1
+ 2 &UIC0 0x0E 1
+ 3 &UIC0 0x0F 1>;
+ };
+
I2O: i2o@400100000 {
compatible = "ibm,i2o-440spe";
reg = <0x00000004 0x00100000 0x100>;
diff --git a/arch/powerpc/boot/dts/kilauea.dts b/arch/powerpc/boot/dts/kilauea.dts
index 083e68e..21e88f5 100644
--- a/arch/powerpc/boot/dts/kilauea.dts
+++ b/arch/powerpc/boot/dts/kilauea.dts
@@ -394,5 +394,33 @@
0x0 0x0 0x0 0x3 &UIC2 0xd 0x4 /* swizzled int C */
0x0 0x0 0x0 0x4 &UIC2 0xe 0x4 /* swizzled int D */>;
};
+
+ MSI: ppc4xx-msi@C10000000 {
+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+ reg = < 0x0 0xEF620000 0x100>;
+ sdr-base = <0x4B0>;
+ msi-data = <0x00000000>;
+ msi-mask = <0x44440000>;
+ interrupt-count = <12>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 0xA 0xB 0xC 0xD>;
+ interrupt-parent = <&UIC2>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC2 0x10 1
+ 1 &UIC2 0x11 1
+ 2 &UIC2 0x12 1
+ 2 &UIC2 0x13 1
+ 2 &UIC2 0x14 1
+ 2 &UIC2 0x15 1
+ 2 &UIC2 0x16 1
+ 2 &UIC2 0x17 1
+ 2 &UIC2 0x18 1
+ 2 &UIC2 0x19 1
+ 2 &UIC2 0x1A 1
+ 2 &UIC2 0x1B 1
+ 2 &UIC2 0x1C 1
+ 3 &UIC2 0x1D 1>;
+ };
};
};
diff --git a/arch/powerpc/boot/dts/redwood.dts b/arch/powerpc/boot/dts/redwood.dts
index 81636c0..d86a3a4 100644
--- a/arch/powerpc/boot/dts/redwood.dts
+++ b/arch/powerpc/boot/dts/redwood.dts
@@ -358,8 +358,28 @@
0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>;
};
+ MSI: ppc4xx-msi@400300000 {
+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+ reg = < 0x4 0x00300000 0x100
+ 0x4 0x00300000 0x100>;
+ sdr-base = <0x3B0>;
+ msi-data = <0x00000000>;
+ msi-mask = <0x44440000>;
+ interrupt-count = <3>;
+ interrupts =<0 1 2 3>;
+ interrupt-parent = <&UIC0>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC0 0xC 1
+ 1 &UIC0 0x0D 1
+ 2 &UIC0 0x0E 1
+ 3 &UIC0 0x0F 1>;
+ };
+
};
+
chosen {
linux,stdout-path = "/plb/opb/serial@ef600200";
};
diff --git a/arch/powerpc/platforms/40x/Kconfig b/arch/powerpc/platforms/40x/Kconfig
index b721764..92aeee6 100644
--- a/arch/powerpc/platforms/40x/Kconfig
+++ b/arch/powerpc/platforms/40x/Kconfig
@@ -57,6 +57,8 @@ config KILAUEA
select 405EX
select PPC40x_SIMPLE
select PPC4xx_PCI_EXPRESS
+ select PCI_MSI
+ select 4xx_MSI
help
This option enables support for the AMCC PPC405EX evaluation board.
diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig
index 0f979c5..3836353 100644
--- a/arch/powerpc/platforms/44x/Kconfig
+++ b/arch/powerpc/platforms/44x/Kconfig
@@ -74,6 +74,8 @@ config KATMAI
select 440SPe
select PCI
select PPC4xx_PCI_EXPRESS
+ select PCI_MSI
+ select 4xx_MSI
help
This option enables support for the AMCC PPC440SPe evaluation board.
@@ -119,6 +121,8 @@ config CANYONLANDS
select 460EX
select PCI
select PPC4xx_PCI_EXPRESS
+ select PCI_MSI
+ select 4xx_MSI
select IBM_NEW_EMAC_RGMII
select IBM_NEW_EMAC_ZMII
help
@@ -145,6 +149,8 @@ config REDWOOD
select 460SX
select PCI
select PPC4xx_PCI_EXPRESS
+ select PCI_MSI
+ select 4xx_MSI
help
This option enables support for the AMCC PPC460SX Redwood board.
diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig
index 3965828..32f5a40 100644
--- a/arch/powerpc/sysdev/Kconfig
+++ b/arch/powerpc/sysdev/Kconfig
@@ -7,8 +7,15 @@ config PPC4xx_PCI_EXPRESS
depends on PCI && 4xx
default n
+config 4xx_MSI
+ bool
+ depends on PCI_MSI
+ depends on PCI && 4xx
+ default n
+
config PPC_MSI_BITMAP
bool
depends on PCI_MSI
default y if MPIC
default y if FSL_PCI
+ default y if 4xx_MSI
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 0bef9da..df859a6 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_OF_RTC) += of_rtc.o
ifeq ($(CONFIG_PCI),y)
obj-$(CONFIG_4xx) += ppc4xx_pci.o
endif
+obj-$(CONFIG_4xx_MSI) += ppc4xx_msi.o
obj-$(CONFIG_PPC4xx_GPIO) += ppc4xx_gpio.o
obj-$(CONFIG_CPM) += cpm_common.o
diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c
new file mode 100644
index 0000000..9ed559f
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_msi.c
@@ -0,0 +1,311 @@
+/*
+ * Adding PCI-E MSI support for PPC4XX SoCs.
+ *
+ * Copyright (c) 2010, Applied Micro Circuits Corporation
+ * Authors: Tirumala R Marri <tmarri@apm.com>
+ * Feng Kan <fkan@apm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/irq.h>
+#include <linux/bootmem.h>
+#include <linux/pci.h>
+#include <linux/msi.h>
+#include <linux/of_platform.h>
+#include <linux/interrupt.h>
+#include <asm/prom.h>
+#include <asm/hw_irq.h>
+#include <asm/ppc-pci.h>
+#include <boot/dcr.h>
+#include <asm/dcr-regs.h>
+#include <asm/msi_bitmap.h>
+
+#define PEIH_TERMADH 0x00
+#define PEIH_TERMADL 0x08
+#define PEIH_MSIED 0x10
+#define PEIH_MSIMK 0x18
+#define PEIH_MSIASS 0x20
+#define PEIH_FLUSH0 0x30
+#define PEIH_FLUSH1 0x38
+#define PEIH_CNTRST 0x48
+#define NR_MSI_IRQS 4
+
+LIST_HEAD(msi_head);
+struct ppc4xx_msi {
+ u32 msi_addr_lo;
+ u32 msi_addr_hi;
+ void __iomem *msi_regs;
+ int msi_virqs[NR_MSI_IRQS];
+ struct msi_bitmap bitmap;
+ struct list_head list;
+};
+
+struct ppc4xx_msi_feature {
+ u32 ppc4xx_pic_ip;
+ u32 msiir_offset;
+};
+
+static int ppc4xx_msi_init_allocator(struct platform_device *dev,
+ struct ppc4xx_msi *msi_data)
+{
+ int err;
+
+ err = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS,
+ dev->dev.of_node);
+ if (err)
+ return err;
+
+ err = msi_bitmap_reserve_dt_hwirqs(&msi_data->bitmap);
+ if (err < 0) {
+ msi_bitmap_free(&msi_data->bitmap);
+ return err;
+ }
+
+ return 0;
+}
+
+static int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+{
+ int err = 0;
+ int int_no = -ENOMEM;
+ unsigned int virq;
+ struct msi_msg msg;
+ struct msi_desc *entry;
+ struct device_node *msi_dev = NULL;
+ struct ppc4xx_msi *msi_data = dev->dev.platform_data;
+
+ msi_dev = of_find_node_by_name(NULL, "ppc4xx-msi");
+ if (msi_dev) {
+ err = -ENODEV;
+ goto out_free;
+ }
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ list_for_each_entry(msi_data, &msi_head, list) {
+ int_no = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1);
+ if(int_no >= 0)
+ break;
+ }
+ if(int_no < 0) {
+
+ err = int_no;
+ pr_debug("%s: fail allocating msi interrupt\n",
+ __func__);
+ }
+ virq = irq_of_parse_and_map(msi_dev, int_no);
+ if (virq == NO_IRQ) {
+ dev_err(&dev->dev, "%s: fail mapping irq\n", __func__);
+ msi_bitmap_free_hwirqs(&msi_data->bitmap, int_no, 1);
+ err = -ENOSPC;
+ goto out_free;
+ }
+ msi_data->msi_virqs[int_no] = virq;
+ set_irq_data(virq, (void *)int_no);
+ dev_dbg(&dev->dev, "%s: virq = %d \n", __func__, virq);
+
+ /* Setup msi address space */
+ msg.address_hi = msi_data->msi_addr_hi;
+ msg.address_lo = msi_data->msi_addr_lo;
+
+ set_irq_msi(virq, entry);
+ msg.data = int_no;
+ write_msi_msg(virq, &msg);
+ }
+ of_node_put(msi_dev);
+ return err;
+
+out_free:
+ of_node_put(msi_dev);
+ return err;
+}
+
+void ppc4xx_teardown_msi_irqs(struct pci_dev *dev)
+{
+ struct msi_desc *entry;
+ struct ppc4xx_msi *msi_data = dev->dev.platform_data;
+
+ dev_dbg(&dev->dev, "PCIE-MSI: tearing down msi irqs\n");
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ if (entry->irq == NO_IRQ)
+ continue;
+ set_irq_msi(entry->irq, NULL);
+ msi_bitmap_free_hwirqs(&msi_data->bitmap,
+ virq_to_hw(entry->irq), 1);
+ irq_dispose_mapping(entry->irq);
+ }
+
+ return;
+}
+
+static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type)
+{
+ dev_dbg(&pdev->dev, "PCIE-MSI:%s called. vec %x type %d\n",
+ __func__, nvec, type);
+ if (type == PCI_CAP_ID_MSIX)
+ pr_debug("fslmsi: MSI-X untested, trying anyway.\n");
+
+ return 0;
+}
+
+static int ppc4xx_setup_pcieh_hw(struct platform_device *dev,
+ struct resource res, struct ppc4xx_msi *msi)
+{
+ const u32 *msi_data;
+ const u32 *msi_mask;
+ const u32 *sdr_addr;
+ int err = 0;
+ dma_addr_t msi_phys;
+ void *msi_virt;
+ struct device_node *msi_dev = NULL;
+
+ sdr_addr = of_get_property(dev->dev.of_node, "sdr-base", NULL);
+ if (!sdr_addr)
+ return -1;
+
+ SDR0_WRITE(sdr_addr, (u64)res.start >> 32); /*HIGH addr */
+ SDR0_WRITE(sdr_addr + 1, res.start & 0xFFFFFFFF); /* Low addr */
+
+
+ msi_dev = of_find_node_by_name(NULL, "ppc4xx-msi");
+ if (msi_dev) {
+ err = -ENODEV;
+ goto error_out;
+ }
+ msi->msi_regs = of_iomap(msi_dev, 0);
+ if (!msi->msi_regs) {
+ dev_err(&dev->dev, "ioremap problem failed\n");
+ return -ENOMEM;
+ }
+ of_node_put(msi_dev);
+ dev_dbg(&dev->dev, "PCIE-MSI: msi register mapped 0x%x 0x%x\n",
+ (u32) (msi->msi_regs + PEIH_TERMADH), (u32) (msi->msi_regs));
+
+ msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys, GFP_KERNEL);
+ msi->msi_addr_hi = 0x0;
+ msi->msi_addr_lo = (u32) msi_phys;
+ dev_dbg(&dev->dev, "PCIE-MSI: msi address 0x%x \n", msi->msi_addr_lo);
+
+ /* Progam the Interrupt handler Termination addr registers */
+ out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi);
+ out_be32(msi->msi_regs + PEIH_TERMADL, msi->msi_addr_lo);
+
+ msi_data = of_get_property(dev->dev.of_node, "msi-data", NULL);
+ if (!msi_data) {
+ err = -1;
+ goto error_out;
+ }
+
+ msi_mask = of_get_property(dev->dev.of_node, "msi-mask", NULL);
+ if (!msi_mask) {
+ err = -1;
+ goto error_out;
+ }
+
+ /* Program MSI Expected data and Mask bits */
+ out_be32(msi->msi_regs + PEIH_MSIED, *msi_data);
+ out_be32(msi->msi_regs + PEIH_MSIMK, *msi_mask);
+
+ return err;
+error_out:
+ return err;
+}
+
+static int ppc4xx_of_msi_remove(struct platform_device *dev)
+{
+ struct ppc4xx_msi *msi = dev->dev.platform_data;
+ int i;
+ int virq;
+
+ for(i = 0; i < NR_MSI_IRQS; i++) {
+ virq = msi->msi_virqs[i];
+ if (virq != NO_IRQ)
+ irq_dispose_mapping(virq);
+ }
+
+ if (msi->list.prev != NULL)
+ list_del(&msi->list);
+
+ if (msi->bitmap.bitmap)
+ msi_bitmap_free(&msi->bitmap);
+ iounmap(msi->msi_regs);
+ kfree(msi);
+
+ return 0;
+}
+
+static int __devinit ppc4xx_msi_probe(struct platform_device *dev,
+ const struct of_device_id *match)
+{
+ struct ppc4xx_msi *msi;
+ struct resource res;
+ int err = 0;
+
+ dev_dbg(&dev->dev, "PCIE-MSI: Setting up MSI support...\n");
+
+ msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL);
+ if (!msi) {
+ dev_err(&dev->dev, "No memory for MSI structure\n");
+ err = -ENOMEM;
+ goto error_out;
+ }
+ dev->dev.platform_data = msi;
+
+ /* Get MSI ranges */
+ err = of_address_to_resource(dev->dev.of_node, 0, &res);
+ if (err) {
+ dev_err(&dev->dev, "%s resource error!\n",
+ dev->dev.of_node->full_name);
+ goto error_out;
+ }
+
+ if (ppc4xx_setup_pcieh_hw(dev, res, msi))
+ goto error_out;
+
+ err = ppc4xx_msi_init_allocator(dev, msi);
+ if (err) {
+ dev_err(&dev->dev, "Error allocating MSI bitmap\n");
+ goto error_out;
+ }
+
+ list_add_tail(&msi->list, &msi_head);
+
+ ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs;
+ ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
+ ppc_md.msi_check_device = ppc4xx_msi_check_device;
+ return err;
+
+error_out:
+ ppc4xx_of_msi_remove(dev);
+ return err;
+}
+
+static struct of_platform_driver ppc4xx_msi_driver = {
+ .driver = {
+ .name = "ppc4xx-msi",
+ .owner = THIS_MODULE,
+ },
+ .probe = ppc4xx_msi_probe,
+ .remove = ppc4xx_of_msi_remove,
+};
+
+static __init int ppc4xx_msi_init(void)
+{
+ return of_register_platform_driver(&ppc4xx_msi_driver);
+}
+
+subsys_initcall(ppc4xx_msi_init);
--
1.6.1.rc3
^ permalink raw reply related
* Re: [PATCH 2/2] powerpc: pcm030/032: add pagesize to dts
From: Mitch Bradley @ 2010-11-15 21:06 UTC (permalink / raw)
To: Anton Vorontsov; +Cc: linuxppc-dev, devicetree-discuss
In-Reply-To: <20101115173250.GA22104@oksana.dev.rtsoft.ru>
On 11/15/2010 7:32 AM, Anton Vorontsov wrote:
> On Mon, Nov 15, 2010 at 06:25:16PM +0100, Wolfram Sang wrote:
>> Signed-off-by: Wolfram Sang<w.sang@pengutronix.de>
>> ---
>> arch/powerpc/boot/dts/pcm030.dts | 1 +
>> arch/powerpc/boot/dts/pcm032.dts | 3 ++-
>> 2 files changed, 3 insertions(+), 1 deletions(-)
>>
>> diff --git a/arch/powerpc/boot/dts/pcm030.dts b/arch/powerpc/boot/dts/pcm030.dts
>> index 8a4ec30..e7c36bc 100644
>> --- a/arch/powerpc/boot/dts/pcm030.dts
>> +++ b/arch/powerpc/boot/dts/pcm030.dts
>> @@ -259,6 +259,7 @@
>> eeprom@52 {
>> compatible = "catalyst,24c32";
>> reg =<0x52>;
>> + pagesize =<32>;
>
> I think you'd better drop the pagesize property altogether, and
> instead make the compatible string more specific (if needed at
> all. are there any 'catalyst,24c32' chips with pagesize != 32?)
Microchip makes a 24c32 part that looks pretty similar to the catalyst
part, but Microchip's has a 64-byte page size compared to Catalyst's 32.
It would probably be feasible to have a generic I2C EEPROM driver that
could handle many different parts, parameterized by total size, block
size, and page size.
>
> Thanks,
>
^ permalink raw reply
* Re: [PATCH 2/2] powerpc: pcm030/032: add pagesize to dts
From: Anton Vorontsov @ 2010-11-15 21:24 UTC (permalink / raw)
To: Mitch Bradley; +Cc: linuxppc-dev, devicetree-discuss
In-Reply-To: <4CE1A0E4.5030505@firmworks.com>
On Mon, Nov 15, 2010 at 11:06:44AM -1000, Mitch Bradley wrote:
[...]
> >> eeprom@52 {
> >> compatible = "catalyst,24c32";
> >> reg =<0x52>;
> >>+ pagesize =<32>;
> >
> >I think you'd better drop the pagesize property altogether, and
> >instead make the compatible string more specific (if needed at
> >all. are there any 'catalyst,24c32' chips with pagesize != 32?)
>
> Microchip makes a 24c32 part that looks pretty similar to the
> catalyst part, but Microchip's has a 64-byte page size compared to
> Catalyst's 32.
Well, when using microchip part, the compatible string would be
"microchip,24c32", correct? Then we have all the information
already, no need for the pagesize.
> It would probably be feasible to have a generic I2C EEPROM driver
> that could handle many different parts, parameterized by total size,
> block size, and page size.
I guess it can do this already via I2C ID table. The problem
is that I2C core is only using part ID (no vendor ID matching).
So, the current driver may just implement quirks like this:
if (of_device_is_compatible(np, "catalyst,24c32"))
pagesize = 32;
Or, if it's some generic pattern, something like
if (of_device_is_compatible_vendor(np, "catalyst"))
pagesize /= 2;
Thanks,
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: [PATCH 1/2] misc: at24: parse OF-data, too
From: Grant Likely @ 2010-11-15 21:41 UTC (permalink / raw)
To: Wolfram Sang; +Cc: linuxppc-dev, devicetree-discuss
In-Reply-To: <1289841916-3825-1-git-send-email-w.sang@pengutronix.de>
On Mon, Nov 15, 2010 at 10:25 AM, Wolfram Sang <w.sang@pengutronix.de> wrot=
e:
> Information about the pagesize and read-only-status may also come from
> the devicetree. Parse this data, too, and act accordingly. While we are
> here, change the initialization printout a bit. write_max is useful to
> know to detect performance bottlenecks, the rest is superfluous.
>
> Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
> ---
>
> Grant: As mentioned at ELCE10, I could pretty much respin this old approa=
ch I
> tried roughly a year ago (just with archdata then). If the approach and d=
ocs
> are good, I am fine with the patches entering via one of your trees.
>
> =A0Documentation/powerpc/dts-bindings/eeprom.txt | =A0 28 +++++++++++++++=
+++++++
> =A0drivers/misc/eeprom/at24.c =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0| =
=A0 33 ++++++++++++++++++++-----
> =A02 files changed, 53 insertions(+), 6 deletions(-)
> =A0create mode 100644 Documentation/powerpc/dts-bindings/eeprom.txt
>
> diff --git a/Documentation/powerpc/dts-bindings/eeprom.txt b/Documentatio=
n/powerpc/dts-bindings/eeprom.txt
> new file mode 100644
> index 0000000..4342c10
> --- /dev/null
> +++ b/Documentation/powerpc/dts-bindings/eeprom.txt
> @@ -0,0 +1,28 @@
> +EEPROMs (I2C)
> +
> +Required properties:
> +
> + =A0- compatible : should be "<manufacturer>,<type>"
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0If there is no specific driver for <manu=
facturer>, a generic
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0driver based on <type> is selected. Poss=
ible types are:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A024c00, 24c01, 24c02, 24c04, 24c08, 24c16=
, 24c32, 24c64,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A024c128, 24c256, 24c512, 24c1024, spd
> +
> + =A0- reg : the I2C address of the EEPROM
> +
> +Optional properties:
> +
> + =A0- pagesize : the length of the pagesize for writing. Please consult =
the
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 manual of your device, that value varies a =
lot. A wrong value
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0may result in data loss! If not specified, a=
safety value of
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0'1' is used which will be very slow.
As Anton mentions, the compatible value should be specific enough that
a pagesize property isn't needed (assuming there aren't at24 devices
with differing page sizes), but that leaves the question of where to
put the page size data in the driver. I think the obvious answer is
to put it in the at24_ids table. This would also be a good thing for
the non-OF use-cases too. Unfortunately the driver_data field is
already in use and requires refactoring to turn driver_data into a
pointer to a structure, and so requires more work (sorry).
A refactored at24_ids table might look something like (there may be a
cleaner way to go about it though, I used the macro to solve the
problem of constructing an at24_platform_data structure for each entry
and storing a pointer to it in the unsigned long driver_data field):
#define AT24_DEVDATA(_len, _page_size, _flags) \
((unsigned long)((struct at24_platform_data[]) \
{{.byte_len =3D len, .page_size =3D _page_size, .flags =3D
_flags}}))
static const struct i2c_device_id at24_ids[] =3D {
/* needs 8 addresses as A0-A2 are ignored */
{ "24c00", AT24_DEVDATA(128 / 8, 16, AT24_FLAG_TAKE8ADDR) },
/* old variants can't be handled with this generic entry! */
{ "24c01", AT24_DEVDATA(1024 / 8, 16, 0) },
{ "24c02", AT24_DEVDATA(2048 / 8, 16, 0) },
...
};
This will also make the probe code simpler.
However, if I am wrong and similarly named at24 devices have different
page sizes, then encoding it in a property is the right thing to do.
Also, the read-only property is fine.
g.
> +
> + =A0- read-only: this parameterless property disables writes to the eepr=
om
> +
> +Example:
> +
> +eeprom@52 {
> + =A0 =A0 =A0 compatible =3D "atmel,24c32";
> + =A0 =A0 =A0 reg =3D <0x52>;
> + =A0 =A0 =A0 pagesize =3D <32>;
> +};
> diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
> index 559b0b3..aaf16cb 100644
> --- a/drivers/misc/eeprom/at24.c
> +++ b/drivers/misc/eeprom/at24.c
> @@ -20,6 +20,7 @@
> =A0#include <linux/log2.h>
> =A0#include <linux/bitops.h>
> =A0#include <linux/jiffies.h>
> +#include <linux/of.h>
> =A0#include <linux/i2c.h>
> =A0#include <linux/i2c/at24.h>
>
> @@ -457,6 +458,27 @@ static ssize_t at24_macc_write(struct memory_accesso=
r *macc, const char *buf,
>
> =A0/*--------------------------------------------------------------------=
-----*/
>
> +#ifdef CONFIG_OF
> +static void at24_get_ofdata(struct i2c_client *client,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct at24_platform_data *chip)
> +{
> + =A0 =A0 =A0 const u32 *val;
> + =A0 =A0 =A0 struct device_node *node =3D client->dev.of_node;
> +
> + =A0 =A0 =A0 if (node) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (of_get_property(node, "read-only", NULL=
))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 chip->flags |=3D AT24_FLAG_=
READONLY;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 val =3D of_get_property(node, "pagesize", N=
ULL);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (val)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 chip->page_size =3D *val;
> + =A0 =A0 =A0 }
> +}
> +#else
> +static void at24_get_ofdata(struct i2c_client *client,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct at24_platform_data *chip)
> +{ }
> +#endif /* CONFIG_OF */
> +
> =A0static int at24_probe(struct i2c_client *client, const struct i2c_devi=
ce_id *id)
> =A0{
> =A0 =A0 =A0 =A0struct at24_platform_data chip;
> @@ -485,6 +507,9 @@ static int at24_probe(struct i2c_client *client, cons=
t struct i2c_device_id *id)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 */
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0chip.page_size =3D 1;
>
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* update chipdata if OF is present */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 at24_get_ofdata(client, &chip);
> +
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0chip.setup =3D NULL;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0chip.context =3D NULL;
> =A0 =A0 =A0 =A0}
> @@ -597,19 +622,15 @@ static int at24_probe(struct i2c_client *client, co=
nst struct i2c_device_id *id)
>
> =A0 =A0 =A0 =A0i2c_set_clientdata(client, at24);
>
> - =A0 =A0 =A0 dev_info(&client->dev, "%zu byte %s EEPROM %s\n",
> + =A0 =A0 =A0 dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/wr=
ite\n",
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0at24->bin.size, client->name,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 writable ? "(writable)" : "(read-only)");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 writable ? "writable" : "read-only", at24->=
write_max);
> =A0 =A0 =A0 =A0if (use_smbus =3D=3D I2C_SMBUS_WORD_DATA ||
> =A0 =A0 =A0 =A0 =A0 =A0use_smbus =3D=3D I2C_SMBUS_BYTE_DATA) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0dev_notice(&client->dev, "Falling back to =
%s reads, "
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "performance will suf=
fer\n", use_smbus =3D=3D
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 I2C_SMBUS_WORD_DATA ?=
"word" : "byte");
> =A0 =A0 =A0 =A0}
> - =A0 =A0 =A0 dev_dbg(&client->dev,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 "page_size %d, num_addresses %d, write_max =
%d, use_smbus %d\n",
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 chip.page_size, num_addresses,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 at24->write_max, use_smbus);
>
> =A0 =A0 =A0 =A0/* export data to kernel code */
> =A0 =A0 =A0 =A0if (chip.setup)
> --
> 1.7.2.3
>
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH 2/2] powerpc: pcm030/032: add pagesize to dts
From: Grant Likely @ 2010-11-15 21:58 UTC (permalink / raw)
To: Anton Vorontsov; +Cc: Mitch Bradley, linuxppc-dev, devicetree-discuss
In-Reply-To: <20101115212432.GA17754@oksana.dev.rtsoft.ru>
On Mon, Nov 15, 2010 at 2:24 PM, Anton Vorontsov <cbouatmailru@gmail.com> w=
rote:
> On Mon, Nov 15, 2010 at 11:06:44AM -1000, Mitch Bradley wrote:
> [...]
>> >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0eeprom@52 {
>> >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0compatible =3D=
"catalyst,24c32";
>> >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0reg =3D<0x52>;
>> >>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pagesize =3D<32>=
;
>> >
>> >I think you'd better drop the pagesize property altogether, and
>> >instead make the compatible string more specific (if needed at
>> >all. are there any 'catalyst,24c32' chips with pagesize !=3D 32?)
>>
>> Microchip makes a 24c32 part that looks pretty similar to the
>> catalyst part, but Microchip's has a 64-byte page size compared to
>> Catalyst's 32.
>
> Well, when using microchip part, the compatible string would be
> "microchip,24c32", correct? Then we have all the information
> already, no need for the pagesize.
>
>> It would probably be feasible to have a generic I2C EEPROM driver
>> that could handle many different parts, parameterized by total size,
>> block size, and page size.
The current at24.c driver is already parameterized; but part of the
parameter data is statically linked into the board support code.
>
> I guess it can do this already via I2C ID table. The problem
> is that I2C core is only using part ID (no vendor ID matching).
This could potentially be changed for at24 devices since the i2c
subsystem already matches by name. It would mean adding the vendor
prefix to all instantiations of the device in the kernel, although it
would mess up the current heuristic used by of_modalias_node() (not a
show-stopper).
>
> So, the current driver may just implement quirks like this:
>
> if (of_device_is_compatible(np, "catalyst,24c32"))
> =A0 =A0 =A0 =A0pagesize =3D 32;
>
> Or, if it's some generic pattern, something like
>
> if (of_device_is_compatible_vendor(np, "catalyst"))
> =A0 =A0 =A0 =A0pagesize /=3D 2;
This would get ugly in a hurry. It would be better to make it data
driven by searching for a better match in an of_device_id table so
that the workarounds don't grow over time and eventually achieve
sentience.
g.
^ permalink raw reply
* Re: [PATCH 2/2] powerpc: pcm030/032: add pagesize to dts
From: Mitch Bradley @ 2010-11-15 22:17 UTC (permalink / raw)
To: Grant Likely; +Cc: linuxppc-dev, devicetree-discuss
In-Reply-To: <AANLkTi=H_zONah7YuKSffkGrSiGa+7-LSbXE+qO=86Zj@mail.gmail.com>
In general I think it's better to report parameter values directly,
instead of inferring them from manufacturer and part numbers. That way
you at least have a fighting chance of avoiding a kernel upgrade when a
part changes.
Of course, that only works when the device tree is exported from the
boot firmware instead of having to carry the device tree inside the kernel.
^ permalink raw reply
* Re: [PATCH 2/2] powerpc: pcm030/032: add pagesize to dts
From: Wolfram Sang @ 2010-11-15 22:11 UTC (permalink / raw)
To: Anton Vorontsov; +Cc: Mitch Bradley, linuxppc-dev, devicetree-discuss
In-Reply-To: <20101115212432.GA17754@oksana.dev.rtsoft.ru>
[-- Attachment #1: Type: text/plain, Size: 1352 bytes --]
> > >I think you'd better drop the pagesize property altogether, and
> > >instead make the compatible string more specific (if needed at
> > >all. are there any 'catalyst,24c32' chips with pagesize != 32?)
> >
> > Microchip makes a 24c32 part that looks pretty similar to the
> > catalyst part, but Microchip's has a 64-byte page size compared to
> > Catalyst's 32.
>
> Well, when using microchip part, the compatible string would be
> "microchip,24c32", correct? Then we have all the information
> already, no need for the pagesize.
Hmm, there are myriads of I2C eeproms out there, this table would be enourmous.
Even worse, I seem to recall that I had once seen a manufacturer increasing the
page-size from one charge to the next without changing the part-number, so I
got this feeling "you can't map pagesize to manufacturer/type" which I still
have. Sadly, this was long ago, so I can't proof it right now. Will try to dig
up some datasheets when in the office tomorrow. In general, I2C EEPROMs are
really a mess, the basic access method is the same, but except that everything
else is possible :) Thus, this approach. Thus, this approach.
Regards,
Wolfram
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox