* [PATCH][v3] Add support for RTC device: pt7c4338 in rtc-ds1307.c
From: Priyanka Jain @ 2011-06-16 4:05 UTC (permalink / raw)
To: akpm, w.sang, cbouatmailru, a.zummo, benh, galak, grant.likely,
b04825, linuxppc-dev, rtc-linux, p_gortmaker
Cc: Priyanka Jain
PT7C4338 chip is being manufactured by Pericom Technology Inc.
It is a serial real-time clock which provides:
1)Low-power clock/calendar.
2)Programmable square-wave output.
It has 56 bytes of nonvolatile RAM.
Its register set is same as that of rtc device: DS1307.
Signed-off-by: Priyanka Jain <Priyanka.Jain@freescale.com>
---
Change for v3:
Incorporting Timur Tabi's comments to drop Kconfig and copyright
changes
Change for v2:
Incorporting Andrew Morton's comments to shorten patch by using
hunk: { "pt7c4338", ds_1307 }
Changes :
This patch will supersede patch:
"RTC driver(Linux) for PT7C4338 chip"
Incorporting Wolfram Sang's comments to reuse ds1307 driver.
drivers/rtc/rtc-ds1307.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 4724ba3..b2005b4 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -149,6 +149,7 @@ static const struct i2c_device_id ds1307_id[] = {
{ "ds1340", ds_1340 },
{ "ds3231", ds_3231 },
{ "m41t00", m41t00 },
+ { "pt7c4338", ds_1307 },
{ "rx8025", rx_8025 },
{ }
};
--
1.6.5.6
^ permalink raw reply related
* [21/91] seqlock: Dont smp_rmb in seqlock reader spin loop
From: Greg KH @ 2011-06-16 0:15 UTC (permalink / raw)
To: linux-kernel, stable
Cc: Nick Piggin, Eric Dumazet, torvalds, Milton Miller, Andi Kleen,
Thomas Gleixner, Anton Blanchard, akpm, Paul McKenney,
linuxppc-dev, stable-review, alan
In-Reply-To: <20110616001900.GA25375@kroah.com>
2.6.32-longterm review patch. If anyone has any objections, please let us know.
------------------
From: Milton Miller <miltonm@bga.com>
commit 5db1256a5131d3b133946fa02ac9770a784e6eb2 upstream.
Move the smp_rmb after cpu_relax loop in read_seqlock and add
ACCESS_ONCE to make sure the test and return are consistent.
A multi-threaded core in the lab didn't like the update
from 2.6.35 to 2.6.36, to the point it would hang during
boot when multiple threads were active. Bisection showed
af5ab277ded04bd9bc6b048c5a2f0e7d70ef0867 (clockevents:
Remove the per cpu tick skew) as the culprit and it is
supported with stack traces showing xtime_lock waits including
tick_do_update_jiffies64 and/or update_vsyscall.
Experimentation showed the combination of cpu_relax and smp_rmb
was significantly slowing the progress of other threads sharing
the core, and this patch is effective in avoiding the hang.
A theory is the rmb is affecting the whole core while the
cpu_relax is causing a resource rebalance flush, together they
cause an interfernce cadance that is unbroken when the seqlock
reader has interrupts disabled.
At first I was confused why the refactor in
3c22cd5709e8143444a6d08682a87f4c57902df3 (kernel: optimise
seqlock) didn't affect this patch application, but after some
study that affected seqcount not seqlock. The new seqcount was
not factored back into the seqlock. I defer that the future.
While the removal of the timer interrupt offset created
contention for the xtime lock while a cpu does the
additonal work to update the system clock, the seqlock
implementation with the tight rmb spin loop goes back much
further, and is just waiting for the right trigger.
Signed-off-by: Milton Miller <miltonm@bga.com>
Cc: <linuxppc-dev@lists.ozlabs.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Nick Piggin <npiggin@kernel.dk>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Anton Blanchard <anton@samba.org>
Cc: Paul McKenney <paulmck@linux.vnet.ibm.com>
Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
Link: http://lkml.kernel.org/r/%3Cseqlock-rmb%40mdm.bga.com%3E
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
include/linux/seqlock.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/include/linux/seqlock.h
+++ b/include/linux/seqlock.h
@@ -88,12 +88,12 @@ static __always_inline unsigned read_seq
unsigned ret;
repeat:
- ret = sl->sequence;
- smp_rmb();
+ ret = ACCESS_ONCE(sl->sequence);
if (unlikely(ret & 1)) {
cpu_relax();
goto repeat;
}
+ smp_rmb();
return ret;
}
^ permalink raw reply
* [21/85] seqlock: Dont smp_rmb in seqlock reader spin loop
From: Greg KH @ 2011-06-16 0:28 UTC (permalink / raw)
To: linux-kernel, stable
Cc: Nick Piggin, Eric Dumazet, torvalds, Milton Miller, Andi Kleen,
Thomas Gleixner, Anton Blanchard, akpm, Paul McKenney,
linuxppc-dev, stable-review, alan
In-Reply-To: <20110616002917.GA3627@kroah.com>
2.6.33-longterm review patch. If anyone has any objections, please let us know.
------------------
From: Milton Miller <miltonm@bga.com>
commit 5db1256a5131d3b133946fa02ac9770a784e6eb2 upstream.
Move the smp_rmb after cpu_relax loop in read_seqlock and add
ACCESS_ONCE to make sure the test and return are consistent.
A multi-threaded core in the lab didn't like the update
from 2.6.35 to 2.6.36, to the point it would hang during
boot when multiple threads were active. Bisection showed
af5ab277ded04bd9bc6b048c5a2f0e7d70ef0867 (clockevents:
Remove the per cpu tick skew) as the culprit and it is
supported with stack traces showing xtime_lock waits including
tick_do_update_jiffies64 and/or update_vsyscall.
Experimentation showed the combination of cpu_relax and smp_rmb
was significantly slowing the progress of other threads sharing
the core, and this patch is effective in avoiding the hang.
A theory is the rmb is affecting the whole core while the
cpu_relax is causing a resource rebalance flush, together they
cause an interfernce cadance that is unbroken when the seqlock
reader has interrupts disabled.
At first I was confused why the refactor in
3c22cd5709e8143444a6d08682a87f4c57902df3 (kernel: optimise
seqlock) didn't affect this patch application, but after some
study that affected seqcount not seqlock. The new seqcount was
not factored back into the seqlock. I defer that the future.
While the removal of the timer interrupt offset created
contention for the xtime lock while a cpu does the
additonal work to update the system clock, the seqlock
implementation with the tight rmb spin loop goes back much
further, and is just waiting for the right trigger.
Signed-off-by: Milton Miller <miltonm@bga.com>
Cc: <linuxppc-dev@lists.ozlabs.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Nick Piggin <npiggin@kernel.dk>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Anton Blanchard <anton@samba.org>
Cc: Paul McKenney <paulmck@linux.vnet.ibm.com>
Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
Link: http://lkml.kernel.org/r/%3Cseqlock-rmb%40mdm.bga.com%3E
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
include/linux/seqlock.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/include/linux/seqlock.h
+++ b/include/linux/seqlock.h
@@ -88,12 +88,12 @@ static __always_inline unsigned read_seq
unsigned ret;
repeat:
- ret = sl->sequence;
- smp_rmb();
+ ret = ACCESS_ONCE(sl->sequence);
if (unlikely(ret & 1)) {
cpu_relax();
goto repeat;
}
+ smp_rmb();
return ret;
}
^ permalink raw reply
* Re: libata/ipr/powerpc: regression between 2.6.39-rc4 and 2.6.39-rc5
From: Tejun Heo @ 2011-06-16 7:51 UTC (permalink / raw)
To: Nishanth Aravamudan
Cc: linuxppc-dev, wayneb, linux-kernel, linux-ide, Brian King, mbizon,
jgarzik
In-Reply-To: <20110615233417.GB6324@us.ibm.com>
On Wed, Jun 15, 2011 at 04:34:17PM -0700, Nishanth Aravamudan wrote:
> > That looks like the right thing to do. For ipr's usage of
> > libata, we don't have the concept of a port frozen state, so this flag
> > should really never get set. The alternate way to fix this would be to
> > only set ATA_PFLAG_FROZEN in ata_port_alloc if ap->ops->error_handler
> > is not NULL.
>
> It seemed like ipr is as you say, but I wasn't sure if it was
> appropriate to make the change above in the common libata-scis code or
> not. I don't want to break some other device on accident.
>
> Also, I tried your suggestion, but I don't think that can happen in
> ata_port_alloc? ata_port_alloc is allocated ap itself, and it seems like
> ap->ops typically gets set only after ata_port_alloc returns?
Maybe we can test error_handler in ata_sas_port_start()?
Thanks.
--
tejun
^ permalink raw reply
* [PATCH v2] kexec-tools: powerpc: Use the #address-cells information to parsememory/reg
From: Suzuki K. Poulose @ 2011-06-16 10:45 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: linux ppc dev, Sebastian Andrzej Siewior, David Laight,
kexec@lists.infradead.org, Suzuki K. Poulose
The format of memory/reg is based on the #address-cells,#size-cells. Currently,
the kexec-tools doesn't use the above values in parsing the memory/reg values.
Hence the kexec cannot handle cases where #address-cells, #size-cells are
different, (for e.g, PPC440X ).
This patch introduces a read_memory_region_limits(), which parses the
memory/reg contents based on the values of #address-cells and #size-cells.
Signed-off-by: Suzuki K. Poulose <suzuki@in.ibm.com>
---
kexec/arch/ppc/crashdump-powerpc.c | 33 ++------
kexec/arch/ppc/fs2dt.c | 14 ---
kexec/arch/ppc/kexec-ppc.c | 158 ++++++++++++++++++++++++++----------
kexec/arch/ppc/kexec-ppc.h | 6 +
4 files changed, 129 insertions(+), 82 deletions(-)
diff --git a/kexec/arch/ppc/crashdump-powerpc.c b/kexec/arch/ppc/crashdump-powerpc.c
index 1dd6485..77a01e1 100644
--- a/kexec/arch/ppc/crashdump-powerpc.c
+++ b/kexec/arch/ppc/crashdump-powerpc.c
@@ -81,7 +81,7 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges)
char fname[256];
char buf[MAXBYTES];
DIR *dir, *dmem;
- FILE *file;
+ int fd;
struct dirent *dentry, *mentry;
int i, n, crash_rng_len = 0;
unsigned long long start, end, cstart, cend;
@@ -123,17 +123,16 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges)
if (strcmp(mentry->d_name, "reg"))
continue;
strcat(fname, "/reg");
- file = fopen(fname, "r");
- if (!file) {
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
perror(fname);
closedir(dmem);
closedir(dir);
goto err;
}
- n = fread(buf, 1, MAXBYTES, file);
- if (n < 0) {
- perror(fname);
- fclose(file);
+ n = read_memory_region_limits(fd, &start, &end);
+ if (n != 0) {
+ close(fd);
closedir(dmem);
closedir(dir);
goto err;
@@ -146,24 +145,6 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges)
goto err;
}
- /*
- * FIXME: This code fails on platforms that
- * have more than one memory range specified
- * in the device-tree's /memory/reg property.
- * or where the #address-cells and #size-cells
- * are not identical.
- *
- * We should interpret the /memory/reg property
- * based on the values of the #address-cells and
- * #size-cells properites.
- */
- if (n == (sizeof(unsigned long) * 2)) {
- start = ((unsigned long *)buf)[0];
- end = start + ((unsigned long *)buf)[1];
- } else {
- start = ((unsigned long long *)buf)[0];
- end = start + ((unsigned long long *)buf)[1];
- }
if (start == 0 && end >= (BACKUP_SRC_END + 1))
start = BACKUP_SRC_END + 1;
@@ -212,7 +193,7 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges)
= RANGE_RAM;
memory_ranges++;
}
- fclose(file);
+ close(fd);
}
closedir(dmem);
}
diff --git a/kexec/arch/ppc/fs2dt.c b/kexec/arch/ppc/fs2dt.c
index 238a3f2..733515a 100644
--- a/kexec/arch/ppc/fs2dt.c
+++ b/kexec/arch/ppc/fs2dt.c
@@ -137,21 +137,11 @@ static void add_usable_mem_property(int fd, int len)
if (strncmp(bname, "/memory@", 8) && strcmp(bname, "/memory"))
return;
- if (len < 2 * sizeof(unsigned long))
- die("unrecoverable error: not enough data for mem property\n");
- len = 2 * sizeof(unsigned long);
-
if (lseek(fd, 0, SEEK_SET) < 0)
die("unrecoverable error: error seeking in \"%s\": %s\n",
pathname, strerror(errno));
- if (read(fd, buf, len) != len)
- die("unrecoverable error: error reading \"%s\": %s\n",
- pathname, strerror(errno));
-
- if (~0ULL - buf[0] < buf[1])
- die("unrecoverable error: mem property overflow\n");
- base = buf[0];
- end = base + buf[1];
+ if (read_memory_region_limits(fd, &base, &end) != 0)
+ die("unrecoverable error: error parsing memory/reg limits\n");
for (range = 0; range < usablemem_rgns.size; range++) {
loc_base = usablemem_rgns.ranges[range].start;
diff --git a/kexec/arch/ppc/kexec-ppc.c b/kexec/arch/ppc/kexec-ppc.c
index db00b96..4e3569f 100644
--- a/kexec/arch/ppc/kexec-ppc.c
+++ b/kexec/arch/ppc/kexec-ppc.c
@@ -16,6 +16,7 @@
#include <dirent.h>
#include <stdlib.h>
#include <sys/stat.h>
+#include <fcntl.h>
#include <unistd.h>
#include "../../kexec.h"
@@ -26,6 +27,7 @@
#include "config.h"
+unsigned long dt_address_cells = 0, dt_size_cells = 0;
uint64_t rmo_top;
unsigned long long crash_base = 0, crash_size = 0;
unsigned long long initrd_base = 0, initrd_size = 0;
@@ -34,6 +36,98 @@ unsigned int rtas_base, rtas_size;
int max_memory_ranges;
const char *ramdisk;
+/*
+ * Reads the #address-cells and #size-cells on this platform.
+ * This is used to parse the memory/reg info from the device-tree
+ */
+int init_memory_region_info()
+{
+ size_t res = 0;
+ int fd;
+ char *file;
+
+ file = "/proc/device-tree/#address-cells";
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open %s\n", file);
+ return -1;
+ }
+
+ res = read(fd, &dt_address_cells, sizeof(dt_address_cells));
+ if (res != sizeof(dt_address_cells)) {
+ fprintf(stderr, "Error reading %s\n", file);
+ return -1;
+ }
+ close(fd);
+
+ file = "/proc/device-tree/#size-cells";
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open %s\n", file);
+ return -1;
+ }
+
+ res = read(fd, &dt_size_cells, sizeof(dt_size_cells));
+ if (res != sizeof(dt_size_cells)) {
+ fprintf(stderr, "Error reading %s\n", file);
+ return -1;
+ }
+ close(fd);
+
+ /* Convert the sizes into bytes */
+ dt_size_cells *= sizeof(unsigned long);
+ dt_address_cells *= sizeof(unsigned long);
+
+ return 0;
+}
+
+#define MAXBYTES 128
+/*
+ * Reads the memory region info from the device-tree node pointed
+ * by @fd and fills the *start, *end with the boundaries of the region
+ */
+int read_memory_region_limits(int fd, unsigned long long *start,
+ unsigned long long *end)
+{
+ char buf[MAXBYTES];
+ unsigned long *p;
+ unsigned long nbytes = dt_address_cells + dt_size_cells;
+
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ fprintf(stderr, "Error in file seek\n");
+ return -1;
+ }
+ if (read(fd, buf, nbytes) != nbytes) {
+ fprintf(stderr, "Error reading the memory region info\n");
+ return -1;
+ }
+
+ p = (unsigned long*)buf;
+ if (dt_address_cells == sizeof(unsigned long)) {
+ *start = p[0];
+ p++;
+ } else if (dt_address_cells == sizeof(unsigned long long)) {
+ *start = ((unsigned long long *)p)[0];
+ p = (unsigned long long *)p + 1;
+ } else {
+ fprintf(stderr, "Unsupported value for #address-cells : %ld\n",
+ dt_address_cells);
+ return -1;
+ }
+
+ if (dt_size_cells == sizeof(unsigned long))
+ *end = *start + p[0];
+ else if (dt_size_cells == sizeof(unsigned long long))
+ *end = *start + ((unsigned long long *)p)[0];
+ else {
+ fprintf(stderr, "Unsupported value for #size-cells : %ld\n",
+ dt_size_cells);
+ return -1;
+ }
+
+ return 0;
+}
+
void arch_reuse_initrd(void)
{
reuse_initrd = 1;
@@ -182,9 +276,6 @@ static int sort_base_ranges(void)
return 0;
}
-
-#define MAXBYTES 128
-
static int realloc_memory_ranges(void)
{
size_t memory_range_len;
@@ -228,9 +319,8 @@ static int get_base_ranges(void)
char fname[256];
char buf[MAXBYTES];
DIR *dir, *dmem;
- FILE *file;
struct dirent *dentry, *mentry;
- int n;
+ int n, fd;
if ((dir = opendir(device_tree)) == NULL) {
perror(device_tree);
@@ -248,54 +338,39 @@ static int get_base_ranges(void)
return -1;
}
while ((mentry = readdir(dmem)) != NULL) {
+ unsigned long long start, end;
+
if (strcmp(mentry->d_name, "reg"))
continue;
strcat(fname, "/reg");
- if ((file = fopen(fname, "r")) == NULL) {
+ if ((fd = open(fname, O_RDONLY)) < 0) {
perror(fname);
closedir(dmem);
closedir(dir);
return -1;
}
- if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
- perror(fname);
- fclose(file);
+ if (read_memory_region_limits(fd, &start, &end) != 0) {
+ close(fd);
closedir(dmem);
closedir(dir);
return -1;
}
if (local_memory_ranges >= max_memory_ranges) {
if (realloc_memory_ranges() < 0){
- fclose(file);
+ close(fd);
break;
}
}
- if (n == sizeof(uint32_t) * 2) {
- base_memory_range[local_memory_ranges].start =
- ((uint32_t *)buf)[0];
- base_memory_range[local_memory_ranges].end =
- base_memory_range[local_memory_ranges].start +
- ((uint32_t *)buf)[1];
- }
- else if (n == sizeof(uint64_t) * 2) {
- base_memory_range[local_memory_ranges].start =
- ((uint64_t *)buf)[0];
- base_memory_range[local_memory_ranges].end =
- base_memory_range[local_memory_ranges].start +
- ((uint64_t *)buf)[1];
- }
- else {
- fprintf(stderr, "Mem node has invalid size: %d\n", n);
- return -1;
- }
+ base_memory_range[local_memory_ranges].start = start;
+ base_memory_range[local_memory_ranges].end = end;
base_memory_range[local_memory_ranges].type = RANGE_RAM;
local_memory_ranges++;
dbgprintf("%016llx-%016llx : %x\n",
base_memory_range[local_memory_ranges-1].start,
base_memory_range[local_memory_ranges-1].end,
base_memory_range[local_memory_ranges-1].type);
- fclose(file);
+ close(fd);
}
closedir(dmem);
}
@@ -572,29 +647,19 @@ static int get_devtree_details(unsigned long kexec_flags)
if (!strncmp(dentry->d_name, "memory@", 7) ||
!strcmp(dentry->d_name, "memory")) {
+ int fd;
strcat(fname, "/reg");
- if ((file = fopen(fname, "r")) == NULL) {
+ if ((fd = open(fname, O_RDONLY)) < 0) {
perror(fname);
goto error_opencdir;
}
- if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
- perror(fname);
- goto error_openfile;
- }
- if (n == sizeof(uint64_t)) {
- rmo_base = ((uint32_t *)buf)[0];
- rmo_top = rmo_base + ((uint32_t *)buf)[1];
- } else if (n == 16) {
- rmo_base = ((uint64_t *)buf)[0];
- rmo_top = rmo_base + ((uint64_t *)buf)[1];
- } else {
- fprintf(stderr, "Mem node has invalid size: %d\n", n);
+ if (read_memory_region_limits(fd, &rmo_base, &rmo_top) != 0)
goto error_openfile;
- }
+
if (rmo_top > 0x30000000UL)
rmo_top = 0x30000000UL;
- fclose(file);
+ close(fd);
closedir(cdir);
} /* memory */
@@ -778,6 +843,11 @@ int get_memory_ranges_dt(struct memory_range **range, int *ranges,
int get_memory_ranges(struct memory_range **range, int *ranges,
unsigned long kexec_flags)
{
+ int res = 0;
+
+ res = init_memory_region_info();
+ if (res != 0)
+ return res;
#ifdef WITH_GAMECUBE
return get_memory_ranges_gc(range, ranges, kexec_flags);
#else
diff --git a/kexec/arch/ppc/kexec-ppc.h b/kexec/arch/ppc/kexec-ppc.h
index 7f96be8..68728c6 100644
--- a/kexec/arch/ppc/kexec-ppc.h
+++ b/kexec/arch/ppc/kexec-ppc.h
@@ -69,6 +69,12 @@ extern unsigned long long initrd_base, initrd_size;
extern unsigned long long ramdisk_base, ramdisk_size;
extern unsigned char reuse_initrd;
extern const char *ramdisk;
+
+/* Method to parse the memory/reg nodes in device-tree */
+extern unsigned long dt_address_cells, dt_size_cells;
+extern int init_memory_region_info(void);
+extern int read_memory_region_limits(int fd, unsigned long long *start,
+ unsigned long long *end);
#define COMMAND_LINE_SIZE 512 /* from kernel */
/*fs2dt*/
void reserve(unsigned long long where, unsigned long long length);
^ permalink raw reply related
* [PATCH v2] kexec-tools: powerpc: Use the #address-cells information to parsememory/reg
From: Suzuki K. Poulose @ 2011-06-16 11:26 UTC (permalink / raw)
To: Simon Horman
Cc: linux ppc dev, Sebastian Andrzej Siewior, David Laight,
kexec@lists.infradead.org, Suzuki K. Poulose
The problem was with the mail client. Also, mistyped the "To" field in the previous
one. Resending.
ChangeLog from V1:
* Changed the interface for read_memory_region_limits to use 'int fd'
* Use sizeof(variable) for read(, instead of sizeof(type).
---
The format of memory/reg is based on the #address-cells,#size-cells. Currently,
the kexec-tools doesn't use the above values in parsing the memory/reg values.
Hence the kexec cannot handle cases where #address-cells, #size-cells are
different, (for e.g, PPC440X ).
This patch introduces a read_memory_region_limits(), which parses the
memory/reg contents based on the values of #address-cells and #size-cells.
Signed-off-by: Suzuki K. Poulose <suzuki@in.ibm.com>
---
kexec/arch/ppc/crashdump-powerpc.c | 33 ++------
kexec/arch/ppc/fs2dt.c | 14 ---
kexec/arch/ppc/kexec-ppc.c | 158 ++++++++++++++++++++++++++----------
kexec/arch/ppc/kexec-ppc.h | 6 +
4 files changed, 129 insertions(+), 82 deletions(-)
diff --git a/kexec/arch/ppc/crashdump-powerpc.c b/kexec/arch/ppc/crashdump-powerpc.c
index 1dd6485..77a01e1 100644
--- a/kexec/arch/ppc/crashdump-powerpc.c
+++ b/kexec/arch/ppc/crashdump-powerpc.c
@@ -81,7 +81,7 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges)
char fname[256];
char buf[MAXBYTES];
DIR *dir, *dmem;
- FILE *file;
+ int fd;
struct dirent *dentry, *mentry;
int i, n, crash_rng_len = 0;
unsigned long long start, end, cstart, cend;
@@ -123,17 +123,16 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges)
if (strcmp(mentry->d_name, "reg"))
continue;
strcat(fname, "/reg");
- file = fopen(fname, "r");
- if (!file) {
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
perror(fname);
closedir(dmem);
closedir(dir);
goto err;
}
- n = fread(buf, 1, MAXBYTES, file);
- if (n < 0) {
- perror(fname);
- fclose(file);
+ n = read_memory_region_limits(fd, &start, &end);
+ if (n != 0) {
+ close(fd);
closedir(dmem);
closedir(dir);
goto err;
@@ -146,24 +145,6 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges)
goto err;
}
- /*
- * FIXME: This code fails on platforms that
- * have more than one memory range specified
- * in the device-tree's /memory/reg property.
- * or where the #address-cells and #size-cells
- * are not identical.
- *
- * We should interpret the /memory/reg property
- * based on the values of the #address-cells and
- * #size-cells properites.
- */
- if (n == (sizeof(unsigned long) * 2)) {
- start = ((unsigned long *)buf)[0];
- end = start + ((unsigned long *)buf)[1];
- } else {
- start = ((unsigned long long *)buf)[0];
- end = start + ((unsigned long long *)buf)[1];
- }
if (start == 0 && end >= (BACKUP_SRC_END + 1))
start = BACKUP_SRC_END + 1;
@@ -212,7 +193,7 @@ static int get_crash_memory_ranges(struct memory_range **range, int *ranges)
= RANGE_RAM;
memory_ranges++;
}
- fclose(file);
+ close(fd);
}
closedir(dmem);
}
diff --git a/kexec/arch/ppc/fs2dt.c b/kexec/arch/ppc/fs2dt.c
index 238a3f2..733515a 100644
--- a/kexec/arch/ppc/fs2dt.c
+++ b/kexec/arch/ppc/fs2dt.c
@@ -137,21 +137,11 @@ static void add_usable_mem_property(int fd, int len)
if (strncmp(bname, "/memory@", 8) && strcmp(bname, "/memory"))
return;
- if (len < 2 * sizeof(unsigned long))
- die("unrecoverable error: not enough data for mem property\n");
- len = 2 * sizeof(unsigned long);
-
if (lseek(fd, 0, SEEK_SET) < 0)
die("unrecoverable error: error seeking in \"%s\": %s\n",
pathname, strerror(errno));
- if (read(fd, buf, len) != len)
- die("unrecoverable error: error reading \"%s\": %s\n",
- pathname, strerror(errno));
-
- if (~0ULL - buf[0] < buf[1])
- die("unrecoverable error: mem property overflow\n");
- base = buf[0];
- end = base + buf[1];
+ if (read_memory_region_limits(fd, &base, &end) != 0)
+ die("unrecoverable error: error parsing memory/reg limits\n");
for (range = 0; range < usablemem_rgns.size; range++) {
loc_base = usablemem_rgns.ranges[range].start;
diff --git a/kexec/arch/ppc/kexec-ppc.c b/kexec/arch/ppc/kexec-ppc.c
index db00b96..4e3569f 100644
--- a/kexec/arch/ppc/kexec-ppc.c
+++ b/kexec/arch/ppc/kexec-ppc.c
@@ -16,6 +16,7 @@
#include <dirent.h>
#include <stdlib.h>
#include <sys/stat.h>
+#include <fcntl.h>
#include <unistd.h>
#include "../../kexec.h"
@@ -26,6 +27,7 @@
#include "config.h"
+unsigned long dt_address_cells = 0, dt_size_cells = 0;
uint64_t rmo_top;
unsigned long long crash_base = 0, crash_size = 0;
unsigned long long initrd_base = 0, initrd_size = 0;
@@ -34,6 +36,98 @@ unsigned int rtas_base, rtas_size;
int max_memory_ranges;
const char *ramdisk;
+/*
+ * Reads the #address-cells and #size-cells on this platform.
+ * This is used to parse the memory/reg info from the device-tree
+ */
+int init_memory_region_info()
+{
+ size_t res = 0;
+ int fd;
+ char *file;
+
+ file = "/proc/device-tree/#address-cells";
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open %s\n", file);
+ return -1;
+ }
+
+ res = read(fd, &dt_address_cells, sizeof(dt_address_cells));
+ if (res != sizeof(dt_address_cells)) {
+ fprintf(stderr, "Error reading %s\n", file);
+ return -1;
+ }
+ close(fd);
+
+ file = "/proc/device-tree/#size-cells";
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open %s\n", file);
+ return -1;
+ }
+
+ res = read(fd, &dt_size_cells, sizeof(dt_size_cells));
+ if (res != sizeof(dt_size_cells)) {
+ fprintf(stderr, "Error reading %s\n", file);
+ return -1;
+ }
+ close(fd);
+
+ /* Convert the sizes into bytes */
+ dt_size_cells *= sizeof(unsigned long);
+ dt_address_cells *= sizeof(unsigned long);
+
+ return 0;
+}
+
+#define MAXBYTES 128
+/*
+ * Reads the memory region info from the device-tree node pointed
+ * by @fd and fills the *start, *end with the boundaries of the region
+ */
+int read_memory_region_limits(int fd, unsigned long long *start,
+ unsigned long long *end)
+{
+ char buf[MAXBYTES];
+ unsigned long *p;
+ unsigned long nbytes = dt_address_cells + dt_size_cells;
+
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ fprintf(stderr, "Error in file seek\n");
+ return -1;
+ }
+ if (read(fd, buf, nbytes) != nbytes) {
+ fprintf(stderr, "Error reading the memory region info\n");
+ return -1;
+ }
+
+ p = (unsigned long*)buf;
+ if (dt_address_cells == sizeof(unsigned long)) {
+ *start = p[0];
+ p++;
+ } else if (dt_address_cells == sizeof(unsigned long long)) {
+ *start = ((unsigned long long *)p)[0];
+ p = (unsigned long long *)p + 1;
+ } else {
+ fprintf(stderr, "Unsupported value for #address-cells : %ld\n",
+ dt_address_cells);
+ return -1;
+ }
+
+ if (dt_size_cells == sizeof(unsigned long))
+ *end = *start + p[0];
+ else if (dt_size_cells == sizeof(unsigned long long))
+ *end = *start + ((unsigned long long *)p)[0];
+ else {
+ fprintf(stderr, "Unsupported value for #size-cells : %ld\n",
+ dt_size_cells);
+ return -1;
+ }
+
+ return 0;
+}
+
void arch_reuse_initrd(void)
{
reuse_initrd = 1;
@@ -182,9 +276,6 @@ static int sort_base_ranges(void)
return 0;
}
-
-#define MAXBYTES 128
-
static int realloc_memory_ranges(void)
{
size_t memory_range_len;
@@ -228,9 +319,8 @@ static int get_base_ranges(void)
char fname[256];
char buf[MAXBYTES];
DIR *dir, *dmem;
- FILE *file;
struct dirent *dentry, *mentry;
- int n;
+ int n, fd;
if ((dir = opendir(device_tree)) == NULL) {
perror(device_tree);
@@ -248,54 +338,39 @@ static int get_base_ranges(void)
return -1;
}
while ((mentry = readdir(dmem)) != NULL) {
+ unsigned long long start, end;
+
if (strcmp(mentry->d_name, "reg"))
continue;
strcat(fname, "/reg");
- if ((file = fopen(fname, "r")) == NULL) {
+ if ((fd = open(fname, O_RDONLY)) < 0) {
perror(fname);
closedir(dmem);
closedir(dir);
return -1;
}
- if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
- perror(fname);
- fclose(file);
+ if (read_memory_region_limits(fd, &start, &end) != 0) {
+ close(fd);
closedir(dmem);
closedir(dir);
return -1;
}
if (local_memory_ranges >= max_memory_ranges) {
if (realloc_memory_ranges() < 0){
- fclose(file);
+ close(fd);
break;
}
}
- if (n == sizeof(uint32_t) * 2) {
- base_memory_range[local_memory_ranges].start =
- ((uint32_t *)buf)[0];
- base_memory_range[local_memory_ranges].end =
- base_memory_range[local_memory_ranges].start +
- ((uint32_t *)buf)[1];
- }
- else if (n == sizeof(uint64_t) * 2) {
- base_memory_range[local_memory_ranges].start =
- ((uint64_t *)buf)[0];
- base_memory_range[local_memory_ranges].end =
- base_memory_range[local_memory_ranges].start +
- ((uint64_t *)buf)[1];
- }
- else {
- fprintf(stderr, "Mem node has invalid size: %d\n", n);
- return -1;
- }
+ base_memory_range[local_memory_ranges].start = start;
+ base_memory_range[local_memory_ranges].end = end;
base_memory_range[local_memory_ranges].type = RANGE_RAM;
local_memory_ranges++;
dbgprintf("%016llx-%016llx : %x\n",
base_memory_range[local_memory_ranges-1].start,
base_memory_range[local_memory_ranges-1].end,
base_memory_range[local_memory_ranges-1].type);
- fclose(file);
+ close(fd);
}
closedir(dmem);
}
@@ -572,29 +647,19 @@ static int get_devtree_details(unsigned long kexec_flags)
if (!strncmp(dentry->d_name, "memory@", 7) ||
!strcmp(dentry->d_name, "memory")) {
+ int fd;
strcat(fname, "/reg");
- if ((file = fopen(fname, "r")) == NULL) {
+ if ((fd = open(fname, O_RDONLY)) < 0) {
perror(fname);
goto error_opencdir;
}
- if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
- perror(fname);
- goto error_openfile;
- }
- if (n == sizeof(uint64_t)) {
- rmo_base = ((uint32_t *)buf)[0];
- rmo_top = rmo_base + ((uint32_t *)buf)[1];
- } else if (n == 16) {
- rmo_base = ((uint64_t *)buf)[0];
- rmo_top = rmo_base + ((uint64_t *)buf)[1];
- } else {
- fprintf(stderr, "Mem node has invalid size: %d\n", n);
+ if (read_memory_region_limits(fd, &rmo_base, &rmo_top) != 0)
goto error_openfile;
- }
+
if (rmo_top > 0x30000000UL)
rmo_top = 0x30000000UL;
- fclose(file);
+ close(fd);
closedir(cdir);
} /* memory */
@@ -778,6 +843,11 @@ int get_memory_ranges_dt(struct memory_range **range, int *ranges,
int get_memory_ranges(struct memory_range **range, int *ranges,
unsigned long kexec_flags)
{
+ int res = 0;
+
+ res = init_memory_region_info();
+ if (res != 0)
+ return res;
#ifdef WITH_GAMECUBE
return get_memory_ranges_gc(range, ranges, kexec_flags);
#else
diff --git a/kexec/arch/ppc/kexec-ppc.h b/kexec/arch/ppc/kexec-ppc.h
index 7f96be8..68728c6 100644
--- a/kexec/arch/ppc/kexec-ppc.h
+++ b/kexec/arch/ppc/kexec-ppc.h
@@ -69,6 +69,12 @@ extern unsigned long long initrd_base, initrd_size;
extern unsigned long long ramdisk_base, ramdisk_size;
extern unsigned char reuse_initrd;
extern const char *ramdisk;
+
+/* Method to parse the memory/reg nodes in device-tree */
+extern unsigned long dt_address_cells, dt_size_cells;
+extern int init_memory_region_info(void);
+extern int read_memory_region_limits(int fd, unsigned long long *start,
+ unsigned long long *end);
#define COMMAND_LINE_SIZE 512 /* from kernel */
/*fs2dt*/
void reserve(unsigned long long where, unsigned long long length);
^ permalink raw reply related
* Re: libata/ipr/powerpc: regression between 2.6.39-rc4 and 2.6.39-rc5
From: Brian King @ 2011-06-16 13:28 UTC (permalink / raw)
To: Tejun Heo
Cc: jgarzik, wayneb, linux-kernel, linux-ide, mbizon,
Nishanth Aravamudan, linuxppc-dev
In-Reply-To: <20110616075114.GH8141@htj.dyndns.org>
On 06/16/2011 02:51 AM, Tejun Heo wrote:
> On Wed, Jun 15, 2011 at 04:34:17PM -0700, Nishanth Aravamudan wrote:
>>> That looks like the right thing to do. For ipr's usage of
>>> libata, we don't have the concept of a port frozen state, so this flag
>>> should really never get set. The alternate way to fix this would be to
>>> only set ATA_PFLAG_FROZEN in ata_port_alloc if ap->ops->error_handler
>>> is not NULL.
>>
>> It seemed like ipr is as you say, but I wasn't sure if it was
>> appropriate to make the change above in the common libata-scis code or
>> not. I don't want to break some other device on accident.
>>
>> Also, I tried your suggestion, but I don't think that can happen in
>> ata_port_alloc? ata_port_alloc is allocated ap itself, and it seems like
>> ap->ops typically gets set only after ata_port_alloc returns?
>
> Maybe we can test error_handler in ata_sas_port_start()?
Good point. Since libsas is converted to the new eh now, we would need to have
this test.
Thanks,
Brian
--
Brian King
Linux on Power Virtualization
IBM Linux Technology Center
^ permalink raw reply
* [RFC] Relocatable kernel for ppc44x
From: Michal Simek @ 2011-06-16 14:14 UTC (permalink / raw)
To: linuxppc-dev; +Cc: arnd, tmarri, suzuki, john.williams
Hi,
John mentioned in his email that we have some patches.
I am also sending them [patch 1/7-6/7]. It is support for non zero
boot address for ppc44x.
Patch 7/7 is relocatable support for ppc44x.
All patches are against 2.6.31.13 but it shouldn't be big problem
to port them to the latest and greatest.
We are using 256MB alignment which is fine for our purposes. But I think
it could be simple to change it to any different style.
I have tested it on QEMU system emulator with u-boot.
I have used its with vmlinux.bin.gz and DTB. Not sure about your configuration
but the best is not to relocate DTB. Address of DTB should be in 256MB boundary
to be able to access it in the first pinned 256MB tlb. I haven't tested any other
configuration but I have tried not to break anyone. Not sure if I can use r25, r26 and r21
(I need two regs for storing temp values). I look forward on your recommendation.
The idea of this patch is to find out where the kernel runs. Pinned 256MB
in asm - setup memstart_addr and kernstart_addr in ASM and not to break generic
PPC code. And also setup TLB for larger memories in mmu_mapin_ram. Have tested system till
768MB.
Thanks for your comments,
Michal
P.S.: Sorry for faults in PPC asm - I am not PPC expert.
^ permalink raw reply
* [RFC PATCH 1/7] powerpc: ppc440 remove zero physical memory base assumption
From: Michal Simek @ 2011-06-16 14:14 UTC (permalink / raw)
To: linuxppc-dev; +Cc: arnd, tmarri, suzuki, john.williams
In-Reply-To: <1308233668-24166-1-git-send-email-monstr@monstr.eu>
From: John Williams <john.williams@petalogix.com>
The macro PHYSICAL_START is available in this context, currently always with
the value zero. However, that will change in a future patchset.
For now, just remove the zero physical address start assumption in head_44x.S
where we setup the initial TLB, and in the later MMU setup where we map in
the remainder of low mem if required.
Signed-off-by: John Williams <john.williams@petalogix.com>
---
arch/powerpc/kernel/head_44x.S | 5 +++--
arch/powerpc/mm/44x_mmu.c | 2 +-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S
index 18d8a16..d80ce05 100644
--- a/arch/powerpc/kernel/head_44x.S
+++ b/arch/powerpc/kernel/head_44x.S
@@ -135,8 +135,9 @@ skpinv: addi r4,r4,1 /* Increment */
lis r3,PAGE_OFFSET@h
ori r3,r3,PAGE_OFFSET@l
- /* Kernel is at the base of RAM */
- li r4, 0 /* Load the kernel physical address */
+ /* Kernel is at PHYSICAL_START */
+ lis r4,PHYSICAL_START@h
+ ori r4,r4,PHYSICAL_START@l
/* Load the kernel PID = 0 */
li r0,0
diff --git a/arch/powerpc/mm/44x_mmu.c b/arch/powerpc/mm/44x_mmu.c
index 98052ac..4a55061 100644
--- a/arch/powerpc/mm/44x_mmu.c
+++ b/arch/powerpc/mm/44x_mmu.c
@@ -94,7 +94,7 @@ unsigned long __init mmu_mapin_ram(void)
/* Pin in enough TLBs to cover any lowmem not covered by the
* initial 256M mapping established in head_44x.S */
- for (addr = PPC_PIN_SIZE; addr < lowmem_end_addr;
+ for (addr = PHYSICAL_START + PPC_PIN_SIZE; addr < lowmem_end_addr;
addr += PPC_PIN_SIZE)
ppc44x_pin_tlb(addr + PAGE_OFFSET, addr);
--
1.5.5.6
^ permalink raw reply related
* [RFC PATCH 2/7] powerpc: Permit non-zero physical start address for PPC44x
From: Michal Simek @ 2011-06-16 14:14 UTC (permalink / raw)
To: linuxppc-dev; +Cc: arnd, tmarri, suzuki, john.williams
In-Reply-To: <1308233668-24166-2-git-send-email-monstr@monstr.eu>
From: John Williams <john.williams@petalogix.com>
The initial TLB entry is 256M, meaning that the physical base address must be
256M aligned.
Signed-off-by: John Williams <john.williams@petalogix.com>
---
arch/powerpc/Kconfig | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index d00131c..45c9683 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -845,7 +845,7 @@ config KERNEL_START
config PHYSICAL_START_BOOL
bool "Set physical address where the kernel is loaded"
- depends on ADVANCED_OPTIONS && FLATMEM && FSL_BOOKE
+ depends on ADVANCED_OPTIONS && FLATMEM && (FSL_BOOKE || 44x)
help
This gives the physical address where the kernel is loaded.
@@ -858,6 +858,7 @@ config PHYSICAL_START
config PHYSICAL_ALIGN
hex
+ default "0x10000000" if 44x
default "0x04000000" if FSL_BOOKE
help
This value puts the alignment restrictions on physical address
--
1.5.5.6
^ permalink raw reply related
* [RFC PATCH 4/7] powerpc: Let simpleboot function with non zero-based memory maps
From: Michal Simek @ 2011-06-16 14:14 UTC (permalink / raw)
To: linuxppc-dev; +Cc: arnd, tmarri, suzuki, john.williams
In-Reply-To: <1308233668-24166-4-git-send-email-monstr@monstr.eu>
From: John Williams <john.williams@petalogix.com>
It is unnecessarily restrictive to fatal() if there is physical memory at a
non-zero base address.
Signed-off-by: John Williams <john.williams@petalogix.com>
---
arch/powerpc/boot/simpleboot.c | 5 -----
1 files changed, 0 insertions(+), 5 deletions(-)
diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c
index 21cd480..910ae05 100644
--- a/arch/powerpc/boot/simpleboot.c
+++ b/arch/powerpc/boot/simpleboot.c
@@ -56,11 +56,6 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
if (size < (*na+*ns) * sizeof(u32))
fatal("cannot get memory range\n");
- /* Only interested in memory based at 0 */
- for (i = 0; i < *na; i++)
- if (*reg++ != 0)
- fatal("Memory range is not based at address 0\n");
-
/* get the memsize and trucate it to under 4G on 32 bit machines */
memsize64 = 0;
for (i = 0; i < *ns; i++)
--
1.5.5.6
^ permalink raw reply related
* [RFC PATCH 5/7] powerpc: Consider a non-zero boot address when computing the bootwrapper start
From: Michal Simek @ 2011-06-16 14:14 UTC (permalink / raw)
To: linuxppc-dev; +Cc: arnd, tmarri, suzuki, john.williams
In-Reply-To: <1308233668-24166-5-git-send-email-monstr@monstr.eu>
From: John Williams <john.williams@petalogix.com>
There's no fundamental reason the bootwrapper can't boot off a non-zero base,
we just need to make sure we account for it in the link.
Do this by adding the (up-aligned) kernel size to membase, and using that as
the link address.
Signed-off-by: John Williams <john.williams@petalogix.com>
---
arch/powerpc/boot/wrapper | 14 +++++++++-----
1 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper
index e148053..594aa02 100755
--- a/arch/powerpc/boot/wrapper
+++ b/arch/powerpc/boot/wrapper
@@ -139,9 +139,16 @@ tmp=$tmpdir/zImage.$$.o
ksection=.kernel:vmlinux.strip
isection=.kernel:initrd
-# default auto-calculate link_address to make room for the kernel
+# physical offset of kernel image
+membase=`${CROSS}objdump -p "$kernel" | grep -m 1 LOAD | awk '{print $7}'`
+
+# auto-calculate link_address to make room for the kernel
# round up kernel image size to nearest megabyte
-link_address=`${CROSS}size -x ${kernel} | grep ${kernel} | awk '{printf("0x%08x", and($4 + 0x0fffff, 0xfffe0000))}'`
+
+# don't forget to add membase for non-zero kernel boot
+membase_dec=`printf "%d" $membase`
+
+link_address=`${CROSS}size -x ${kernel} | grep ${kernel} | awk -v membase=$membase_dec '{printf("0x%08x", membase + and($4 + 0x0fffff, 0xfffe0000))}'`
case "$platform" in
pseries)
@@ -259,9 +266,6 @@ if [ -n "$version" ]; then
uboot_version="-n Linux-$version"
fi
-# physical offset of kernel image
-membase=`${CROSS}objdump -p "$kernel" | grep -m 1 LOAD | awk '{print $7}'`
-
case "$platform" in
uboot)
rm -f "$ofile"
--
1.5.5.6
^ permalink raw reply related
* [RFC PATCH 7/7] powerpc: Support RELOCATABLE kernel for PPC44x
From: Michal Simek @ 2011-06-16 14:14 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Michal Simek, arnd, tmarri, suzuki, john.williams
In-Reply-To: <1308233668-24166-7-git-send-email-monstr@monstr.eu>
Changes:
- Find out address where kernel runs
- Create the first 256MB TLB from online detected address
Limitations:
- Kernel must be aligned to 256MB
Backport:
- Changes in page.h are backported from newer kernel version
mmu_mapin_ram function has to reflect offset in memory start.
memstart_addr and kernstart_addr are setup directly from asm
code to ensure that only ppc44x is affected.
Signed-off-by: Michal Simek <monstr@monstr.eu>
---
arch/powerpc/Kconfig | 3 ++-
arch/powerpc/include/asm/page.h | 7 ++++++-
arch/powerpc/kernel/head_44x.S | 28 ++++++++++++++++++++++++++++
arch/powerpc/mm/44x_mmu.c | 6 +++++-
4 files changed, 41 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 45c9683..34c521e 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -796,7 +796,8 @@ config LOWMEM_CAM_NUM
config RELOCATABLE
bool "Build a relocatable kernel (EXPERIMENTAL)"
- depends on EXPERIMENTAL && ADVANCED_OPTIONS && FLATMEM && FSL_BOOKE
+ depends on EXPERIMENTAL && ADVANCED_OPTIONS && FLATMEM
+ depends on FSL_BOOKE || (44x && !SMP)
help
This builds a kernel image that is capable of running at the
location the kernel is loaded at (some alignment restrictions may
diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h
index 4940662..e813cc2 100644
--- a/arch/powerpc/include/asm/page.h
+++ b/arch/powerpc/include/asm/page.h
@@ -108,8 +108,13 @@ extern phys_addr_t kernstart_addr;
#define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)
#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
-#define __va(x) ((void *)((unsigned long)(x) + PAGE_OFFSET - MEMORY_START))
+#ifdef CONFIG_BOOKE
+#define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) - PHYSICAL_START + KERNELBASE))
+#define __pa(x) ((unsigned long)(x) + PHYSICAL_START - KERNELBASE)
+#else
+#define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) + PAGE_OFFSET - MEMORY_START))
#define __pa(x) ((unsigned long)(x) - PAGE_OFFSET + MEMORY_START)
+#endif
/*
* Unfortunately the PLT is in the BSS in the PPC32 ELF ABI,
diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S
index d80ce05..6a63d32 100644
--- a/arch/powerpc/kernel/head_44x.S
+++ b/arch/powerpc/kernel/head_44x.S
@@ -59,6 +59,17 @@ _ENTRY(_start);
* of abatron_pteptrs
*/
nop
+
+#ifdef CONFIG_RELOCATABLE
+ bl jump /* Find our address */
+ nop
+jump: mflr r25 /* Make it accessible */
+ /* just for and */
+ lis r26, 0xfffffff0@h
+ ori r26, r26, 0xfffffff0@l
+ and. r21, r25, r26
+#endif
+
/*
* Save parameters we are passed
*/
@@ -135,9 +146,14 @@ skpinv: addi r4,r4,1 /* Increment */
lis r3,PAGE_OFFSET@h
ori r3,r3,PAGE_OFFSET@l
+#ifdef CONFIG_RELOCATABLE
+ /* load physical address where kernel runs */
+ mr r4,r21
+#else
/* Kernel is at PHYSICAL_START */
lis r4,PHYSICAL_START@h
ori r4,r4,PHYSICAL_START@l
+#endif
/* Load the kernel PID = 0 */
li r0,0
@@ -258,6 +274,18 @@ skpinv: addi r4,r4,1 /* Increment */
mr r5,r29
mr r6,r28
mr r7,r27
+
+#ifdef CONFIG_RELOCATABLE
+ /* save kernel and memory start */
+ lis r25,kernstart_addr@h
+ ori r25,r25,kernstart_addr@l
+ stw r21,4(r25)
+
+ lis r25,memstart_addr@h
+ ori r25,r25,memstart_addr@l
+ stw r21,4(r25)
+#endif
+
bl machine_init
bl MMU_init
diff --git a/arch/powerpc/mm/44x_mmu.c b/arch/powerpc/mm/44x_mmu.c
index 4a55061..ecf4a20 100644
--- a/arch/powerpc/mm/44x_mmu.c
+++ b/arch/powerpc/mm/44x_mmu.c
@@ -91,12 +91,16 @@ void __init MMU_init_hw(void)
unsigned long __init mmu_mapin_ram(void)
{
unsigned long addr;
+ unsigned long offset = 0;
+#if defined(CONFIG_RELOCATABLE)
+ offset = memstart_addr;
+#endif
/* Pin in enough TLBs to cover any lowmem not covered by the
* initial 256M mapping established in head_44x.S */
for (addr = PHYSICAL_START + PPC_PIN_SIZE; addr < lowmem_end_addr;
addr += PPC_PIN_SIZE)
- ppc44x_pin_tlb(addr + PAGE_OFFSET, addr);
+ ppc44x_pin_tlb(addr + PAGE_OFFSET - offset, addr);
return total_lowmem;
}
--
1.5.5.6
^ permalink raw reply related
* [RFC PATCH 3/7] powerpc: simpleboot get load address from ELF instead of assuming zero
From: Michal Simek @ 2011-06-16 14:14 UTC (permalink / raw)
To: linuxppc-dev; +Cc: arnd, tmarri, suzuki, john.williams
In-Reply-To: <1308233668-24166-3-git-send-email-monstr@monstr.eu>
From: John Williams <john.williams@petalogix.com>
simpleboot current assumes that the physical load address is zero, even if
the ELF payload has a non-zero paddr.
This is a simple fix that avoids a custom pltform_ops handler in this case.
Signed-off-by: John Williams <john.williams@petalogix.com>
---
arch/powerpc/boot/elf.h | 1 +
arch/powerpc/boot/elf_util.c | 2 ++
arch/powerpc/boot/main.c | 1 +
3 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/boot/elf.h b/arch/powerpc/boot/elf.h
index 1941bc5..39af242 100644
--- a/arch/powerpc/boot/elf.h
+++ b/arch/powerpc/boot/elf.h
@@ -150,6 +150,7 @@ struct elf_info {
unsigned long loadsize;
unsigned long memsize;
unsigned long elfoffset;
+ unsigned long loadaddr;
};
int parse_elf64(void *hdr, struct elf_info *info);
int parse_elf32(void *hdr, struct elf_info *info);
diff --git a/arch/powerpc/boot/elf_util.c b/arch/powerpc/boot/elf_util.c
index 1567a0c..3aef4f0 100644
--- a/arch/powerpc/boot/elf_util.c
+++ b/arch/powerpc/boot/elf_util.c
@@ -43,6 +43,7 @@ int parse_elf64(void *hdr, struct elf_info *info)
info->loadsize = (unsigned long)elf64ph->p_filesz;
info->memsize = (unsigned long)elf64ph->p_memsz;
info->elfoffset = (unsigned long)elf64ph->p_offset;
+ info->loadaddr = (unsigned long)elf64ph->p_paddr;
return 1;
}
@@ -74,5 +75,6 @@ int parse_elf32(void *hdr, struct elf_info *info)
info->loadsize = elf32ph->p_filesz;
info->memsize = elf32ph->p_memsz;
info->elfoffset = elf32ph->p_offset;
+ info->loadaddr = elf32ph->p_paddr;
return 1;
}
diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c
index a28f021..fbbffa5 100644
--- a/arch/powerpc/boot/main.c
+++ b/arch/powerpc/boot/main.c
@@ -56,6 +56,7 @@ static struct addr_range prep_kernel(void)
if (platform_ops.vmlinux_alloc) {
addr = platform_ops.vmlinux_alloc(ei.memsize);
} else {
+ addr = ei.loadaddr;
/*
* Check if the kernel image (without bss) would overwrite the
* bootwrapper. The device tree has been moved in fdt_init()
--
1.5.5.6
^ permalink raw reply related
* [RFC PATCH 6/7] powerpc: Update the default FIT image to use the correct load/boot addresses
From: Michal Simek @ 2011-06-16 14:14 UTC (permalink / raw)
To: linuxppc-dev; +Cc: arnd, tmarri, suzuki, john.williams
In-Reply-To: <1308233668-24166-6-git-send-email-monstr@monstr.eu>
From: John Williams <john.williams@petalogix.com>
The default kernel_fdt.its hard codes zero load/start addresses, but this may
no longer be true.
As we copy the FIT tree descriptor, update these values based on the incoming
ELF payload.
Signed-off-by: John Williams <john.williams@petalogix.com>
---
arch/powerpc/boot/wrapper | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper
index 594aa02..54fbc2e 100755
--- a/arch/powerpc/boot/wrapper
+++ b/arch/powerpc/boot/wrapper
@@ -281,7 +281,11 @@ uboot-fit)
rm -f "$ofile"
#[ "$vmz" != vmlinux.bin.gz ] && mv "$vmz" "vmlinux.bin.gz"
mv "$dtb" "target.dtb"
- cp arch/powerpc/boot/kernel_fdt.its .
+ # Check the ELF file for a non-zero load/entry address
+ membase=${membase:2:8}
+ sed -e "s/load = <.*$/load = <${membase}>;/g" \
+ -e "s/entry = <.*$/entry = <${membase}>;/g" \
+ arch/powerpc/boot/kernel_fdt.its > kernel_fdt.its
mkimage -f kernel_fdt.its "$ofile"
#rm kernet_fdt.its
exit 0
--
1.5.5.6
^ permalink raw reply related
* [PATCH] libata/sas: only set FROZEN flag if new EH is supported
From: Nishanth Aravamudan @ 2011-06-16 15:28 UTC (permalink / raw)
To: Brian King
Cc: jgarzik, wayneb, linux-kernel, linux-ide, Tejun Heo, mbizon,
linuxppc-dev
In-Reply-To: <4DFA0507.3090609@linux.vnet.ibm.com>
On 16.06.2011 [08:28:39 -0500], Brian King wrote:
> On 06/16/2011 02:51 AM, Tejun Heo wrote:
> > On Wed, Jun 15, 2011 at 04:34:17PM -0700, Nishanth Aravamudan wrote:
> >>> That looks like the right thing to do. For ipr's usage of
> >>> libata, we don't have the concept of a port frozen state, so this flag
> >>> should really never get set. The alternate way to fix this would be to
> >>> only set ATA_PFLAG_FROZEN in ata_port_alloc if ap->ops->error_handler
> >>> is not NULL.
> >>
> >> It seemed like ipr is as you say, but I wasn't sure if it was
> >> appropriate to make the change above in the common libata-scis code or
> >> not. I don't want to break some other device on accident.
> >>
> >> Also, I tried your suggestion, but I don't think that can happen in
> >> ata_port_alloc? ata_port_alloc is allocated ap itself, and it seems like
> >> ap->ops typically gets set only after ata_port_alloc returns?
> >
> > Maybe we can test error_handler in ata_sas_port_start()?
>
> Good point. Since libsas is converted to the new eh now, we would need to have
> this test.
Commit 7b3a24c57d2eeda8dba9c205342b12689c4679f9 ("ahci: don't enable
port irq before handler is registered") caused a regression for CD-ROMs
attached to the IPR SATA bus on Power machines:
ata_port_alloc: ENTER
ata_port_probe: ata1: bus probe begin
ata1.00: ata_dev_read_id: ENTER
ata1.00: failed to IDENTIFY (I/O error, err_mask=0x40)
ata1.00: ata_dev_read_id: ENTER
ata1.00: failed to IDENTIFY (I/O error, err_mask=0x40)
ata1.00: limiting speed to UDMA7:PIO5
ata1.00: ata_dev_read_id: ENTER
ata1.00: failed to IDENTIFY (I/O error, err_mask=0x40)
ata1.00: disabled
ata_port_probe: ata1: bus probe end
scsi_alloc_sdev: Allocation failure during SCSI scanning, some SCSI devices might not be configured
The FROZEN flag added in that commit is only cleared by the new EH code,
which is not used by ipr. Clear this flag in the SAS code if we don't
support new EH.
Reported-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index d51f979..ebe1685 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3797,6 +3797,12 @@ EXPORT_SYMBOL_GPL(ata_sas_port_alloc);
*/
int ata_sas_port_start(struct ata_port *ap)
{
+ /*
+ * the port is marked as frozen at allocation time, but if we don't
+ * have new eh, we won't thaw it
+ */
+ if (!ap->ops->error_handler)
+ ap->pflags &= ~ATA_PFLAG_FROZEN;
return 0;
}
EXPORT_SYMBOL_GPL(ata_sas_port_start);
--
Nishanth Aravamudan <nacc@us.ibm.com>
IBM Linux Technology Center
^ permalink raw reply related
* Re: [PATCH][v3] Add support for RTC device: pt7c4338 in rtc-ds1307.c
From: Timur Tabi @ 2011-06-16 15:38 UTC (permalink / raw)
To: Priyanka Jain; +Cc: a.zummo, rtc-linux, p_gortmaker, galak, akpm, linuxppc-dev
In-Reply-To: <1308197117-3586-1-git-send-email-Priyanka.Jain@freescale.com>
Priyanka Jain wrote:
> PT7C4338 chip is being manufactured by Pericom Technology Inc.
> It is a serial real-time clock which provides:
> 1)Low-power clock/calendar.
> 2)Programmable square-wave output.
> It has 56 bytes of nonvolatile RAM.
> Its register set is same as that of rtc device: DS1307.
>
>
> Signed-off-by: Priyanka Jain <Priyanka.Jain@freescale.com>
Acked-by: Timur Tabi <timur@freescale.com>
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* [PATCH] powerpc/e500: fix breakage with fsl_rio_mcheck_exception
From: Scott Wood @ 2011-06-16 19:09 UTC (permalink / raw)
To: galak; +Cc: linuxppc-dev
The wrong MCSR bit was being used on e500mc. MCSR_BUS_RBERR only exists
on e500v1/v2. Use MCSR_LD on e500mc, and remove all MCSR checking
in fsl_rio_mcheck_exception as we now no longer call that function
if the appropriate bit in MCSR is not set.
If RIO support was enabled at compile-time, but was never probed, just
return from fsl_rio_mcheck_exception rather than dereference a NULL
pointer.
TODO: There is still a remaining, though comparitively minor, issue in
that this recovery mechanism will falsely engage if there's an unrelated
MCSR_LD event at the same time as a RIO error.
Signed-off-by: Scott Wood <scottwood@freescale.com>
---
arch/powerpc/kernel/traps.c | 2 +-
arch/powerpc/sysdev/fsl_rio.c | 33 +++++++++++++++++----------------
2 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 0ff4ab9..6414a0d 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -425,7 +425,7 @@ int machine_check_e500mc(struct pt_regs *regs)
unsigned long reason = mcsr;
int recoverable = 1;
- if (reason & MCSR_BUS_RBERR) {
+ if (reason & MCSR_LD) {
recoverable = fsl_rio_mcheck_exception(regs);
if (recoverable == 1)
goto silent_out;
diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c
index 5b206a2..b3fd081 100644
--- a/arch/powerpc/sysdev/fsl_rio.c
+++ b/arch/powerpc/sysdev/fsl_rio.c
@@ -283,23 +283,24 @@ static void __iomem *rio_regs_win;
#ifdef CONFIG_E500
int fsl_rio_mcheck_exception(struct pt_regs *regs)
{
- const struct exception_table_entry *entry = NULL;
- unsigned long reason = mfspr(SPRN_MCSR);
-
- if (reason & MCSR_BUS_RBERR) {
- reason = in_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR));
- if (reason & (RIO_LTLEDCSR_IER | RIO_LTLEDCSR_PRT)) {
- /* Check if we are prepared to handle this fault */
- entry = search_exception_tables(regs->nip);
- if (entry) {
- pr_debug("RIO: %s - MC Exception handled\n",
- __func__);
- out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR),
- 0);
- regs->msr |= MSR_RI;
- regs->nip = entry->fixup;
- return 1;
- }
+ const struct exception_table_entry *entry;
+ unsigned long reason;
+
+ if (!rio_regs_win)
+ return 0;
+
+ reason = in_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR));
+ if (reason & (RIO_LTLEDCSR_IER | RIO_LTLEDCSR_PRT)) {
+ /* Check if we are prepared to handle this fault */
+ entry = search_exception_tables(regs->nip);
+ if (entry) {
+ pr_debug("RIO: %s - MC Exception handled\n",
+ __func__);
+ out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR),
+ 0);
+ regs->msr |= MSR_RI;
+ regs->nip = entry->fixup;
+ return 1;
}
}
--
1.7.4.1
^ permalink raw reply related
* Re: [PATCH][v3] Add support for RTC device: pt7c4338 in rtc-ds1307.c
From: Wolfram Sang @ 2011-06-16 19:42 UTC (permalink / raw)
To: Timur Tabi
Cc: a.zummo, rtc-linux, p_gortmaker, Priyanka Jain, galak, akpm,
linuxppc-dev
In-Reply-To: <4DFA2363.3060004@freescale.com>
[-- Attachment #1: Type: text/plain, Size: 718 bytes --]
On Thu, Jun 16, 2011 at 10:38:11AM -0500, Timur Tabi wrote:
> Priyanka Jain wrote:
> > PT7C4338 chip is being manufactured by Pericom Technology Inc.
> > It is a serial real-time clock which provides:
> > 1)Low-power clock/calendar.
> > 2)Programmable square-wave output.
> > It has 56 bytes of nonvolatile RAM.
> > Its register set is same as that of rtc device: DS1307.
> >
> >
> > Signed-off-by: Priyanka Jain <Priyanka.Jain@freescale.com>
>
> Acked-by: Timur Tabi <timur@freescale.com>
Reviewed-by: Wolfram Sang <w.sang@pengutronix.de>
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* Re: [PATCH v2] kexec-tools: powerpc: Use the #address-cells information to parsememory/reg
From: Simon Horman @ 2011-06-16 22:00 UTC (permalink / raw)
To: Suzuki K. Poulose
Cc: kexec@lists.infradead.org, Sebastian Andrzej Siewior,
David Laight, linux ppc dev
In-Reply-To: <20110616104441.8065.46187.stgit@suzukikp.in.ibm.com>
On Thu, Jun 16, 2011 at 04:15:13PM +0530, Suzuki K. Poulose wrote:
> The format of memory/reg is based on the #address-cells,#size-cells. Currently,
> the kexec-tools doesn't use the above values in parsing the memory/reg values.
> Hence the kexec cannot handle cases where #address-cells, #size-cells are
> different, (for e.g, PPC440X ).
>
> This patch introduces a read_memory_region_limits(), which parses the
> memory/reg contents based on the values of #address-cells and #size-cells.
Thanks, applied.
^ permalink raw reply
* [PATCH 1/5] powerpc/hvsi: Move HVSI protocol definitions to a header file
From: Benjamin Herrenschmidt @ 2011-06-17 1:08 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-kernel@vger.kernel.org
This moves various HVSI protocol definitions from the hvsi.c
driver to a header file that can be used later on by a udbg
implementation
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/hvsi.h | 68 +++++++++++++++++++++++++++++++++++++++
drivers/tty/hvc/hvsi.c | 63 +-----------------------------------
2 files changed, 69 insertions(+), 62 deletions(-)
create mode 100644 arch/powerpc/include/asm/hvsi.h
diff --git a/arch/powerpc/include/asm/hvsi.h b/arch/powerpc/include/asm/hvsi.h
new file mode 100644
index 0000000..f13125a
--- /dev/null
+++ b/arch/powerpc/include/asm/hvsi.h
@@ -0,0 +1,68 @@
+#ifndef _HVSI_H
+#define _HVSI_H
+
+#define VS_DATA_PACKET_HEADER 0xff
+#define VS_CONTROL_PACKET_HEADER 0xfe
+#define VS_QUERY_PACKET_HEADER 0xfd
+#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc
+
+/* control verbs */
+#define VSV_SET_MODEM_CTL 1 /* to service processor only */
+#define VSV_MODEM_CTL_UPDATE 2 /* from service processor only */
+#define VSV_CLOSE_PROTOCOL 3
+
+/* query verbs */
+#define VSV_SEND_VERSION_NUMBER 1
+#define VSV_SEND_MODEM_CTL_STATUS 2
+
+/* yes, these masks are not consecutive. */
+#define HVSI_TSDTR 0x01
+#define HVSI_TSCD 0x20
+
+#define HVSI_MAX_OUTGOING_DATA 12
+#define HVSI_VERSION 1
+
+struct hvsi_header {
+ uint8_t type;
+ uint8_t len;
+ uint16_t seqno;
+} __attribute__((packed));
+
+struct hvsi_data {
+ uint8_t type;
+ uint8_t len;
+ uint16_t seqno;
+ uint8_t data[HVSI_MAX_OUTGOING_DATA];
+} __attribute__((packed));
+
+struct hvsi_control {
+ uint8_t type;
+ uint8_t len;
+ uint16_t seqno;
+ uint16_t verb;
+ /* optional depending on verb: */
+ uint32_t word;
+ uint32_t mask;
+} __attribute__((packed));
+
+struct hvsi_query {
+ uint8_t type;
+ uint8_t len;
+ uint16_t seqno;
+ uint16_t verb;
+} __attribute__((packed));
+
+struct hvsi_query_response {
+ uint8_t type;
+ uint8_t len;
+ uint16_t seqno;
+ uint16_t verb;
+ uint16_t query_seqno;
+ union {
+ uint8_t version;
+ uint32_t mctrl_word;
+ } u;
+} __attribute__((packed));
+
+
+#endif /* _HVSI_H */
diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c
index 8a8d637..0b35793 100644
--- a/drivers/tty/hvc/hvsi.c
+++ b/drivers/tty/hvc/hvsi.c
@@ -49,6 +49,7 @@
#include <asm/uaccess.h>
#include <asm/vio.h>
#include <asm/param.h>
+#include <asm/hvsi.h>
#define HVSI_MAJOR 229
#define HVSI_MINOR 128
@@ -109,68 +110,6 @@ enum HVSI_PROTOCOL_STATE {
};
#define HVSI_CONSOLE 0x1
-#define VS_DATA_PACKET_HEADER 0xff
-#define VS_CONTROL_PACKET_HEADER 0xfe
-#define VS_QUERY_PACKET_HEADER 0xfd
-#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc
-
-/* control verbs */
-#define VSV_SET_MODEM_CTL 1 /* to service processor only */
-#define VSV_MODEM_CTL_UPDATE 2 /* from service processor only */
-#define VSV_CLOSE_PROTOCOL 3
-
-/* query verbs */
-#define VSV_SEND_VERSION_NUMBER 1
-#define VSV_SEND_MODEM_CTL_STATUS 2
-
-/* yes, these masks are not consecutive. */
-#define HVSI_TSDTR 0x01
-#define HVSI_TSCD 0x20
-
-struct hvsi_header {
- uint8_t type;
- uint8_t len;
- uint16_t seqno;
-} __attribute__((packed));
-
-struct hvsi_data {
- uint8_t type;
- uint8_t len;
- uint16_t seqno;
- uint8_t data[HVSI_MAX_OUTGOING_DATA];
-} __attribute__((packed));
-
-struct hvsi_control {
- uint8_t type;
- uint8_t len;
- uint16_t seqno;
- uint16_t verb;
- /* optional depending on verb: */
- uint32_t word;
- uint32_t mask;
-} __attribute__((packed));
-
-struct hvsi_query {
- uint8_t type;
- uint8_t len;
- uint16_t seqno;
- uint16_t verb;
-} __attribute__((packed));
-
-struct hvsi_query_response {
- uint8_t type;
- uint8_t len;
- uint16_t seqno;
- uint16_t verb;
- uint16_t query_seqno;
- union {
- uint8_t version;
- uint32_t mctrl_word;
- } u;
-} __attribute__((packed));
-
-
-
static inline int is_console(struct hvsi_struct *hp)
{
return hp->flags & HVSI_CONSOLE;
^ permalink raw reply related
* [PATCH 2/5] powerpc/pseries: Factor HVSI header struct in packet definitions
From: Benjamin Herrenschmidt @ 2011-06-17 1:08 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-kernel@vger.kernel.org
Embed the struct hvsi_header in the various packet definitions
rather than open coding it multiple times. Will help provide
stronger type checking.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/hvsi.h | 16 ++-------
drivers/tty/hvc/hvsi.c | 66 +++++++++++++++++++-------------------
2 files changed, 37 insertions(+), 45 deletions(-)
diff --git a/arch/powerpc/include/asm/hvsi.h b/arch/powerpc/include/asm/hvsi.h
index f13125a..ab2ddd7 100644
--- a/arch/powerpc/include/asm/hvsi.h
+++ b/arch/powerpc/include/asm/hvsi.h
@@ -29,16 +29,12 @@ struct hvsi_header {
} __attribute__((packed));
struct hvsi_data {
- uint8_t type;
- uint8_t len;
- uint16_t seqno;
+ struct hvsi_header hdr;
uint8_t data[HVSI_MAX_OUTGOING_DATA];
} __attribute__((packed));
struct hvsi_control {
- uint8_t type;
- uint8_t len;
- uint16_t seqno;
+ struct hvsi_header hdr;
uint16_t verb;
/* optional depending on verb: */
uint32_t word;
@@ -46,16 +42,12 @@ struct hvsi_control {
} __attribute__((packed));
struct hvsi_query {
- uint8_t type;
- uint8_t len;
- uint16_t seqno;
+ struct hvsi_header hdr;
uint16_t verb;
} __attribute__((packed));
struct hvsi_query_response {
- uint8_t type;
- uint8_t len;
- uint16_t seqno;
+ struct hvsi_header hdr;
uint16_t verb;
uint16_t query_seqno;
union {
diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c
index 0b35793..c94e2f5 100644
--- a/drivers/tty/hvc/hvsi.c
+++ b/drivers/tty/hvc/hvsi.c
@@ -295,18 +295,18 @@ static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno)
struct hvsi_query_response packet __ALIGNED__;
int wrote;
- packet.type = VS_QUERY_RESPONSE_PACKET_HEADER;
- packet.len = sizeof(struct hvsi_query_response);
- packet.seqno = atomic_inc_return(&hp->seqno);
+ packet.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER;
+ packet.hdr.len = sizeof(struct hvsi_query_response);
+ packet.hdr.seqno = atomic_inc_return(&hp->seqno);
packet.verb = VSV_SEND_VERSION_NUMBER;
packet.u.version = HVSI_VERSION;
packet.query_seqno = query_seqno+1;
- pr_debug("%s: sending %i bytes\n", __func__, packet.len);
- dbg_dump_hex((uint8_t*)&packet, packet.len);
+ pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len);
+ dbg_dump_hex((uint8_t*)&packet, packet.hdr.len);
- wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len);
- if (wrote != packet.len) {
+ wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len);
+ if (wrote != packet.hdr.len) {
printk(KERN_ERR "hvsi%i: couldn't send query response!\n",
hp->index);
return -EIO;
@@ -321,7 +321,7 @@ static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet)
switch (hp->state) {
case HVSI_WAIT_FOR_VER_QUERY:
- hvsi_version_respond(hp, query->seqno);
+ hvsi_version_respond(hp, query->hdr.seqno);
__set_state(hp, HVSI_OPEN);
break;
default:
@@ -579,16 +579,16 @@ static int hvsi_query(struct hvsi_struct *hp, uint16_t verb)
struct hvsi_query packet __ALIGNED__;
int wrote;
- packet.type = VS_QUERY_PACKET_HEADER;
- packet.len = sizeof(struct hvsi_query);
- packet.seqno = atomic_inc_return(&hp->seqno);
+ packet.hdr.type = VS_QUERY_PACKET_HEADER;
+ packet.hdr.len = sizeof(struct hvsi_query);
+ packet.hdr.seqno = atomic_inc_return(&hp->seqno);
packet.verb = verb;
- pr_debug("%s: sending %i bytes\n", __func__, packet.len);
- dbg_dump_hex((uint8_t*)&packet, packet.len);
+ pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len);
+ dbg_dump_hex((uint8_t*)&packet, packet.hdr.len);
- wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len);
- if (wrote != packet.len) {
+ wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len);
+ if (wrote != packet.hdr.len) {
printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index,
wrote);
return -EIO;
@@ -622,20 +622,20 @@ static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl)
struct hvsi_control packet __ALIGNED__;
int wrote;
- packet.type = VS_CONTROL_PACKET_HEADER,
- packet.seqno = atomic_inc_return(&hp->seqno);
- packet.len = sizeof(struct hvsi_control);
+ packet.hdr.type = VS_CONTROL_PACKET_HEADER,
+ packet.hdr.seqno = atomic_inc_return(&hp->seqno);
+ packet.hdr.len = sizeof(struct hvsi_control);
packet.verb = VSV_SET_MODEM_CTL;
packet.mask = HVSI_TSDTR;
if (mctrl & TIOCM_DTR)
packet.word = HVSI_TSDTR;
- pr_debug("%s: sending %i bytes\n", __func__, packet.len);
- dbg_dump_hex((uint8_t*)&packet, packet.len);
+ pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len);
+ dbg_dump_hex((uint8_t*)&packet, packet.hdr.len);
- wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len);
- if (wrote != packet.len) {
+ wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len);
+ if (wrote != packet.hdr.len) {
printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index);
return -EIO;
}
@@ -705,13 +705,13 @@ static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count)
BUG_ON(count > HVSI_MAX_OUTGOING_DATA);
- packet.type = VS_DATA_PACKET_HEADER;
- packet.seqno = atomic_inc_return(&hp->seqno);
- packet.len = count + sizeof(struct hvsi_header);
+ packet.hdr.type = VS_DATA_PACKET_HEADER;
+ packet.hdr.seqno = atomic_inc_return(&hp->seqno);
+ packet.hdr.len = count + sizeof(struct hvsi_header);
memcpy(&packet.data, buf, count);
- ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len);
- if (ret == packet.len) {
+ ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len);
+ if (ret == packet.hdr.len) {
/* return the number of chars written, not the packet length */
return count;
}
@@ -722,15 +722,15 @@ static void hvsi_close_protocol(struct hvsi_struct *hp)
{
struct hvsi_control packet __ALIGNED__;
- packet.type = VS_CONTROL_PACKET_HEADER;
- packet.seqno = atomic_inc_return(&hp->seqno);
- packet.len = 6;
+ packet.hdr.type = VS_CONTROL_PACKET_HEADER;
+ packet.hdr.seqno = atomic_inc_return(&hp->seqno);
+ packet.hdr.len = 6;
packet.verb = VSV_CLOSE_PROTOCOL;
- pr_debug("%s: sending %i bytes\n", __func__, packet.len);
- dbg_dump_hex((uint8_t*)&packet, packet.len);
+ pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len);
+ dbg_dump_hex((uint8_t*)&packet, packet.hdr.len);
- hvc_put_chars(hp->vtermno, (char *)&packet, packet.len);
+ hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len);
}
static int hvsi_open(struct tty_struct *tty, struct file *filp)
^ permalink raw reply related
* [PATCH 5/5] powerpc/pseries: Move hvsi support into a library
From: Benjamin Herrenschmidt @ 2011-06-17 1:08 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-kernel@vger.kernel.org
This will allow a different backend to share it
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/hvsi.h | 34 +++
drivers/tty/hvc/Makefile | 2 +-
drivers/tty/hvc/hvc_vio.c | 405 ++-----------------------------------
drivers/tty/hvc/hvsi_lib.c | 426 +++++++++++++++++++++++++++++++++++++++
4 files changed, 482 insertions(+), 385 deletions(-)
create mode 100644 drivers/tty/hvc/hvsi_lib.c
diff --git a/arch/powerpc/include/asm/hvsi.h b/arch/powerpc/include/asm/hvsi.h
index ab2ddd7..91e0453 100644
--- a/arch/powerpc/include/asm/hvsi.h
+++ b/arch/powerpc/include/asm/hvsi.h
@@ -56,5 +56,39 @@ struct hvsi_query_response {
} u;
} __attribute__((packed));
+/* hvsi lib struct definitions */
+#define HVSI_INBUF_SIZE 255
+struct tty_struct;
+struct hvsi_priv {
+ unsigned int inbuf_len; /* data in input buffer */
+ unsigned char inbuf[HVSI_INBUF_SIZE];
+ unsigned int inbuf_cur; /* Cursor in input buffer */
+ unsigned int inbuf_pktlen; /* packet lenght from cursor */
+ atomic_t seqno; /* packet sequence number */
+ unsigned int opened:1; /* driver opened */
+ unsigned int established:1; /* protocol established */
+ unsigned int is_console:1; /* used as a kernel console device */
+ unsigned int mctrl_update:1; /* modem control updated */
+ unsigned short mctrl; /* modem control */
+ struct tty_struct *tty; /* tty structure */
+ int (*get_chars)(uint32_t termno, char *buf, int count);
+ int (*put_chars)(uint32_t termno, const char *buf, int count);
+ uint32_t termno;
+};
+
+/* hvsi lib functions */
+struct hvc_struct;
+extern void hvsi_init(struct hvsi_priv *pv,
+ int (*get_chars)(uint32_t termno, char *buf, int count),
+ int (*put_chars)(uint32_t termno, const char *buf,
+ int count),
+ int termno, int is_console);
+extern int hvsi_open(struct hvsi_priv *pv, struct hvc_struct *hp);
+extern void hvsi_close(struct hvsi_priv *pv, struct hvc_struct *hp);
+extern int hvsi_read_mctrl(struct hvsi_priv *pv);
+extern int hvsi_write_mctrl(struct hvsi_priv *pv, int dtr);
+extern void hvsi_establish(struct hvsi_priv *pv);
+extern int hvsi_get_chars(struct hvsi_priv *pv, char *buf, int count);
+extern int hvsi_put_chars(struct hvsi_priv *pv, const char *buf, int count);
#endif /* _HVSI_H */
diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile
index 69a444b..e292053 100644
--- a/drivers/tty/hvc/Makefile
+++ b/drivers/tty/hvc/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o
+obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi_lib.o
obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o
obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o
obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c
index d4e0850..ade73fa 100644
--- a/drivers/tty/hvc/hvc_vio.c
+++ b/drivers/tty/hvc/hvc_vio.c
@@ -67,22 +67,10 @@ typedef enum hv_protocol {
HV_PROTOCOL_HVSI
} hv_protocol_t;
-#define HV_INBUF_SIZE 255
-
struct hvterm_priv {
- u32 termno; /* HV term number */
- hv_protocol_t proto; /* Raw data or HVSI packets */
- unsigned int inbuf_len; /* Data in input buffer */
- unsigned char inbuf[HV_INBUF_SIZE];
- unsigned int inbuf_cur; /* Cursor in input buffer */
- unsigned int inbuf_pktlen; /* HVSI packet lenght from cursor */
- atomic_t seqno; /* HVSI packet sequence number */
- unsigned int opened:1; /* HVSI driver opened */
- unsigned int established:1; /* HVSI protocol established */
- unsigned int is_console:1; /* Used as a kernel console device */
- unsigned int mctrl_update:1; /* HVSI modem control updated */
- unsigned short mctrl; /* HVSI modem control */
- struct tty_struct *tty; /* TTY structure */
+ u32 termno; /* HV term number */
+ hv_protocol_t proto; /* Raw data or HVSI packets */
+ struct hvsi_priv hvsi; /* HVSI specific data */
};
static struct hvterm_priv *hvterm_privs[MAX_NR_HVC_CONSOLES];
@@ -139,348 +127,24 @@ static const struct hv_ops hvterm_raw_ops = {
.notifier_hangup = notifier_hangup_irq,
};
-static int hvterm_hvsi_send_packet(struct hvterm_priv *pv, struct hvsi_header *packet)
-{
- packet->seqno = atomic_inc_return(&pv->seqno);
-
- /* Assumes that always succeeds, works in practice */
- return hvc_put_chars(pv->termno, (char *)packet, packet->len);
-}
-
-static void hvterm_hvsi_start_handshake(struct hvterm_priv *pv)
-{
- struct hvsi_query q;
-
- /* Reset state */
- pv->established = 0;
- atomic_set(&pv->seqno, 0);
-
- pr_devel("HVSI@%x: Handshaking started\n", pv->termno);
-
- /* Send version query */
- q.hdr.type = VS_QUERY_PACKET_HEADER;
- q.hdr.len = sizeof(struct hvsi_query);
- q.verb = VSV_SEND_VERSION_NUMBER;
- hvterm_hvsi_send_packet(pv, &q.hdr);
-}
-
-static int hvterm_hvsi_send_close(struct hvterm_priv *pv)
-{
- struct hvsi_control ctrl;
-
- pv->established = 0;
-
- ctrl.hdr.type = VS_CONTROL_PACKET_HEADER;
- ctrl.hdr.len = sizeof(struct hvsi_control);
- ctrl.verb = VSV_CLOSE_PROTOCOL;
- return hvterm_hvsi_send_packet(pv, &ctrl.hdr);
-}
-
-static void hvterm_cd_change(struct hvterm_priv *pv, int cd)
-{
- if (cd)
- pv->mctrl |= TIOCM_CD;
- else {
- pv->mctrl &= ~TIOCM_CD;
-
- /* We copy the existing hvsi driver semantics
- * here which are to trigger a hangup when
- * we get a carrier loss.
- * Closing our connection to the server will
- * do just that.
- */
- if (!pv->is_console && pv->opened) {
- pr_devel("HVSI@%x Carrier lost, hanging up !\n",
- pv->termno);
- hvterm_hvsi_send_close(pv);
- }
- }
-}
-
-static void hvterm_hvsi_got_control(struct hvterm_priv *pv)
-{
- struct hvsi_control *pkt = (struct hvsi_control *)pv->inbuf;
-
- switch (pkt->verb) {
- case VSV_CLOSE_PROTOCOL:
- /* We restart the handshaking */
- hvterm_hvsi_start_handshake(pv);
- break;
- case VSV_MODEM_CTL_UPDATE:
- /* Transition of carrier detect */
- hvterm_cd_change(pv, pkt->word & HVSI_TSCD);
- break;
- }
-}
-
-static void hvterm_hvsi_got_query(struct hvterm_priv *pv)
-{
- struct hvsi_query *pkt = (struct hvsi_query *)pv->inbuf;
- struct hvsi_query_response r;
-
- /* We only handle version queries */
- if (pkt->verb != VSV_SEND_VERSION_NUMBER)
- return;
-
- pr_devel("HVSI@%x: Got version query, sending response...\n",
- pv->termno);
-
- /* Send version response */
- r.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER;
- r.hdr.len = sizeof(struct hvsi_query_response);
- r.verb = VSV_SEND_VERSION_NUMBER;
- r.u.version = HVSI_VERSION;
- r.query_seqno = pkt->hdr.seqno;
- hvterm_hvsi_send_packet(pv, &r.hdr);
-
- /* Assume protocol is open now */
- pv->established = 1;
-}
-
-static void hvterm_hvsi_got_response(struct hvterm_priv *pv)
-{
- struct hvsi_query_response *r = (struct hvsi_query_response *)pv->inbuf;
-
- switch(r->verb) {
- case VSV_SEND_MODEM_CTL_STATUS:
- hvterm_cd_change(pv, r->u.mctrl_word & HVSI_TSCD);
- pv->mctrl_update = 1;
- break;
- }
-}
-
-static int hvterm_hvsi_check_packet(struct hvterm_priv *pv)
-{
- u8 len, type;
-
- /* Check header validity. If it's invalid, we ditch
- * the whole buffer and hope we eventually resync
- */
- if (pv->inbuf[0] < 0xfc) {
- pv->inbuf_len = pv->inbuf_pktlen = 0;
- return 0;
- }
- type = pv->inbuf[0];
- len = pv->inbuf[1];
-
- /* Packet incomplete ? */
- if (pv->inbuf_len < len)
- return 0;
-
- pr_devel("HVSI@%x: Got packet type %x len %d bytes:\n",
- pv->termno, type, len);
-
- /* We have a packet, yay ! Handle it */
- switch(type) {
- case VS_DATA_PACKET_HEADER:
- pv->inbuf_pktlen = len - 4;
- pv->inbuf_cur = 4;
- return 1;
- case VS_CONTROL_PACKET_HEADER:
- hvterm_hvsi_got_control(pv);
- break;
- case VS_QUERY_PACKET_HEADER:
- hvterm_hvsi_got_query(pv);
- break;
- case VS_QUERY_RESPONSE_PACKET_HEADER:
- hvterm_hvsi_got_response(pv);
- break;
- }
-
- /* Swallow packet and retry */
- pv->inbuf_len -= len;
- memmove(pv->inbuf, &pv->inbuf[len], pv->inbuf_len);
- return 1;
-}
-
-static int hvterm_hvsi_get_packet(struct hvterm_priv *pv)
-{
- /* If we have room in the buffer, ask HV for more */
- if (pv->inbuf_len < HV_INBUF_SIZE)
- pv->inbuf_len += hvc_get_chars(pv->termno,
- &pv->inbuf[pv->inbuf_len],
- HV_INBUF_SIZE - pv->inbuf_len);
- /*
- * If we have at least 4 bytes in the buffer, check for
- * a full packet and retry
- */
- if (pv->inbuf_len >= 4)
- return hvterm_hvsi_check_packet(pv);
- return 0;
-}
-
static int hvterm_hvsi_get_chars(uint32_t vtermno, char *buf, int count)
{
struct hvterm_priv *pv = hvterm_privs[vtermno];
- unsigned int tries, read = 0;
if (WARN_ON(!pv))
return 0;
- /* If we aren't open, dont do anything in order to avoid races
- * with connection establishment. The hvc core will call this
- * before we have returned from notifier_add(), and we need to
- * avoid multiple users playing with the receive buffer
- */
- if (!pv->opened)
- return 0;
-
- /* We try twice, once with what data we have and once more
- * after we try to fetch some more from the hypervisor
- */
- for (tries = 1; count && tries < 2; tries++) {
- /* Consume existing data packet */
- if (pv->inbuf_pktlen) {
- unsigned int l = min(count, (int)pv->inbuf_pktlen);
- memcpy(&buf[read], &pv->inbuf[pv->inbuf_cur], l);
- pv->inbuf_cur += l;
- pv->inbuf_pktlen -= l;
- count -= l;
- read += l;
- }
- if (count == 0)
- break;
-
- /* Data packet fully consumed, move down remaning data */
- if (pv->inbuf_cur) {
- pv->inbuf_len -= pv->inbuf_cur;
- memmove(pv->inbuf, &pv->inbuf[pv->inbuf_cur], pv->inbuf_len);
- pv->inbuf_cur = 0;
- }
-
- /* Try to get another packet */
- if (hvterm_hvsi_get_packet(pv))
- tries--;
- }
- if (!pv->established) {
- pr_devel("HVSI@%x: returning -EPIPE\n", pv->termno);
- return -EPIPE;
- }
- return read;
+ return hvsi_get_chars(&pv->hvsi, buf, count);
}
static int hvterm_hvsi_put_chars(uint32_t vtermno, const char *buf, int count)
{
struct hvterm_priv *pv = hvterm_privs[vtermno];
- struct hvsi_data dp;
- int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA);
if (WARN_ON(!pv))
return 0;
- dp.hdr.type = VS_DATA_PACKET_HEADER;
- dp.hdr.len = adjcount + sizeof(struct hvsi_header);
- memcpy(dp.data, buf, adjcount);
- rc = hvterm_hvsi_send_packet(pv, &dp.hdr);
- if (rc <= 0)
- return rc;
- return adjcount;
-}
-
-static void maybe_msleep(unsigned long ms)
-{
- /* During early boot, IRQs are disabled, use mdelay */
- if (irqs_disabled())
- mdelay(ms);
- else
- msleep(ms);
-}
-
-static int hvterm_hvsi_read_mctrl(struct hvterm_priv *pv)
-{
- struct hvsi_query q;
- int rc, timeout;
-
- pr_devel("HVSI@%x: Querying modem control status...\n",
- pv->termno);
-
- pv->mctrl_update = 0;
- q.hdr.type = VS_QUERY_PACKET_HEADER;
- q.hdr.len = sizeof(struct hvsi_query);
- q.hdr.seqno = atomic_inc_return(&pv->seqno);
- q.verb = VSV_SEND_MODEM_CTL_STATUS;
- rc = hvterm_hvsi_send_packet(pv, &q.hdr);
- if (rc <= 0) {
- pr_devel("HVSI@%x: Error %d...\n", pv->termno, rc);
- return rc;
- }
-
- /* Try for up to 1s */
- for (timeout = 0; timeout < 1000; timeout++) {
- if (!pv->established)
- return -ENXIO;
- if (pv->mctrl_update)
- return 0;
- if (!hvterm_hvsi_get_packet(pv))
- maybe_msleep(1);
- }
- return -EIO;
-}
-
-static int hvterm_hvsi_write_mctrl(struct hvterm_priv *pv, int dtr)
-{
- struct hvsi_control ctrl;
-
- pr_devel("HVSI@%x: %s DTR...\n", pv->termno,
- dtr ? "Setting" : "Clearing");
-
- ctrl.hdr.type = VS_CONTROL_PACKET_HEADER,
- ctrl.hdr.len = sizeof(struct hvsi_control);
- ctrl.verb = VSV_SET_MODEM_CTL;
- ctrl.mask = HVSI_TSDTR;
- ctrl.word = dtr ? HVSI_TSDTR : 0;
- if (dtr)
- pv->mctrl |= TIOCM_DTR;
- else
- pv->mctrl &= ~TIOCM_DTR;
- return hvterm_hvsi_send_packet(pv, &ctrl.hdr);
-}
-
-static void hvterm_hvsi_establish(struct hvterm_priv *pv)
-{
- int timeout;
-
- /* Try for up to 10ms, there can be a packet to
- * start the process waiting for us...
- */
- for (timeout = 0; timeout < 10; timeout++) {
- if (pv->established)
- goto established;
- if (!hvterm_hvsi_get_packet(pv))
- maybe_msleep(1);
- }
-
- /* Failed, send a close connection packet just
- * in case
- */
- hvterm_hvsi_send_close(pv);
-
- /* Then restart handshake */
- hvterm_hvsi_start_handshake(pv);
-
- /* Try for up to 100ms */
- for (timeout = 0; timeout < 100; timeout++) {
- if (pv->established)
- goto established;
- if (!hvterm_hvsi_get_packet(pv))
- maybe_msleep(1);
- }
-
- if (!pv->established) {
- pr_devel("HVSI@%x: Timeout handshaking, giving up !\n",
- pv->termno);
- return;
- }
- established:
- /* Query modem control lines */
- hvterm_hvsi_read_mctrl(pv);
-
- /* Set our own DTR */
- hvterm_hvsi_write_mctrl(pv, 1);
-
- /* Set the opened flag so reads are allowed */
- wmb();
- pv->opened = 1;
+ return hvsi_put_chars(&pv->hvsi, buf, count);
}
static int hvterm_hvsi_open(struct hvc_struct *hp, int data)
@@ -494,46 +158,16 @@ static int hvterm_hvsi_open(struct hvc_struct *hp, int data)
if (rc)
return rc;
- /* Keep track of the tty data structure */
- pv->tty = tty_kref_get(hp->tty);
-
- hvterm_hvsi_establish(pv);
- return 0;
-}
-
-static void hvterm_hvsi_shutdown(struct hvc_struct *hp, struct hvterm_priv *pv)
-{
- unsigned long flags;
-
- if (!pv->is_console) {
- pr_devel("HVSI@%x: Not a console, tearing down\n",
- pv->termno);
-
- /* Clear opened, synchronize with khvcd */
- spin_lock_irqsave(&hp->lock, flags);
- pv->opened = 0;
- spin_unlock_irqrestore(&hp->lock, flags);
-
- /* Clear our own DTR */
- if (!pv->tty || (pv->tty->termios->c_cflag & HUPCL))
- hvterm_hvsi_write_mctrl(pv, 0);
-
- /* Tear down the connection */
- hvterm_hvsi_send_close(pv);
- }
-
- if (pv->tty)
- tty_kref_put(pv->tty);
- pv->tty = NULL;
+ return hvsi_open(&pv->hvsi, hp);
}
static void hvterm_hvsi_close(struct hvc_struct *hp, int data)
{
struct hvterm_priv *pv = hvterm_privs[hp->vtermno];
- pr_devel("HVSI@%x: close !\n", pv->termno);
+ pr_devel("HVSI@%x: do close !\n", pv->termno);
- hvterm_hvsi_shutdown(hp, pv);
+ hvsi_close(&pv->hvsi, hp);
notifier_del_irq(hp, data);
}
@@ -542,9 +176,9 @@ void hvterm_hvsi_hangup(struct hvc_struct *hp, int data)
{
struct hvterm_priv *pv = hvterm_privs[hp->vtermno];
- pr_devel("HVSI@%x: hangup !\n", pv->termno);
+ pr_devel("HVSI@%x: do hangup !\n", pv->termno);
- hvterm_hvsi_shutdown(hp, pv);
+ hvsi_close(&pv->hvsi, hp);
notifier_hangup_irq(hp, data);
}
@@ -555,7 +189,7 @@ static int hvterm_hvsi_tiocmget(struct hvc_struct *hp)
if (!pv)
return -EINVAL;
- return pv->mctrl;
+ return pv->hvsi.mctrl;
}
static int hvterm_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set,
@@ -567,9 +201,9 @@ static int hvterm_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set,
pv->termno, set, clear);
if (set & TIOCM_DTR)
- hvterm_hvsi_write_mctrl(pv, 1);
+ hvsi_write_mctrl(&pv->hvsi, 1);
else if (clear & TIOCM_DTR)
- hvterm_hvsi_write_mctrl(pv, 0);
+ hvsi_write_mctrl(&pv->hvsi, 0);
return 0;
}
@@ -633,6 +267,8 @@ static int __devinit hvc_vio_probe(struct vio_dev *vdev,
pv->termno = vdev->unit_address;
pv->proto = proto;
hvterm_privs[termno] = pv;
+ hvsi_init(&pv->hvsi, hvc_get_chars, hvc_put_chars,
+ pv->termno, 0);
}
hp = hvc_alloc(termno, vdev->irq, ops, MAX_VIO_PUT_CHARS);
@@ -770,7 +406,6 @@ void __init hvc_vio_init_early(void)
if (termno == NULL)
goto out;
hvterm_priv0.termno = *termno;
- hvterm_priv0.is_console = 1;
hvterm_privs[0] = &hvterm_priv0;
/* Check the protocol */
@@ -781,8 +416,10 @@ void __init hvc_vio_init_early(void)
else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) {
hvterm_priv0.proto = HV_PROTOCOL_HVSI;
ops = &hvterm_hvsi_ops;
+ hvsi_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars,
+ hvterm_priv0.termno, 1);
/* HVSI, perform the handshake now */
- hvterm_hvsi_establish(&hvterm_priv0);
+ hvsi_establish(&hvterm_priv0.hvsi);
} else
goto out;
udbg_putc = udbg_hvc_putc;
@@ -810,7 +447,6 @@ void __init udbg_init_debug_lpar(void)
hvterm_privs[0] = &hvterm_priv0;
hvterm_priv0.termno = 0;
hvterm_priv0.proto = HV_PROTOCOL_RAW;
- hvterm_priv0.is_console = 1;
udbg_putc = udbg_hvc_putc;
udbg_getc = udbg_hvc_getc;
udbg_getc_poll = udbg_hvc_getc_poll;
@@ -823,10 +459,11 @@ void __init udbg_init_debug_lpar_hvsi(void)
hvterm_privs[0] = &hvterm_priv0;
hvterm_priv0.termno = CONFIG_PPC_EARLY_DEBUG_HVSI_VTERMNO;
hvterm_priv0.proto = HV_PROTOCOL_HVSI;
- hvterm_priv0.is_console = 1;
udbg_putc = udbg_hvc_putc;
udbg_getc = udbg_hvc_getc;
udbg_getc_poll = udbg_hvc_getc_poll;
- hvterm_hvsi_establish(&hvterm_priv0);
+ hvsi_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars,
+ hvterm_priv0.termno, 1);
+ hvsi_establish(&hvterm_priv0.hvsi);
}
#endif /* CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI */
diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c
new file mode 100644
index 0000000..9401fcb
--- /dev/null
+++ b/drivers/tty/hvc/hvsi_lib.c
@@ -0,0 +1,426 @@
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/console.h>
+#include <asm/hvsi.h>
+
+#include "hvc_console.h"
+
+static int hvsi_send_packet(struct hvsi_priv *pv, struct hvsi_header *packet)
+{
+ packet->seqno = atomic_inc_return(&pv->seqno);
+
+ /* Assumes that always succeeds, works in practice */
+ return pv->put_chars(pv->termno, (char *)packet, packet->len);
+}
+
+static void hvsi_start_handshake(struct hvsi_priv *pv)
+{
+ struct hvsi_query q;
+
+ /* Reset state */
+ pv->established = 0;
+ atomic_set(&pv->seqno, 0);
+
+ pr_devel("HVSI@%x: Handshaking started\n", pv->termno);
+
+ /* Send version query */
+ q.hdr.type = VS_QUERY_PACKET_HEADER;
+ q.hdr.len = sizeof(struct hvsi_query);
+ q.verb = VSV_SEND_VERSION_NUMBER;
+ hvsi_send_packet(pv, &q.hdr);
+}
+
+static int hvsi_send_close(struct hvsi_priv *pv)
+{
+ struct hvsi_control ctrl;
+
+ pv->established = 0;
+
+ ctrl.hdr.type = VS_CONTROL_PACKET_HEADER;
+ ctrl.hdr.len = sizeof(struct hvsi_control);
+ ctrl.verb = VSV_CLOSE_PROTOCOL;
+ return hvsi_send_packet(pv, &ctrl.hdr);
+}
+
+static void hvsi_cd_change(struct hvsi_priv *pv, int cd)
+{
+ if (cd)
+ pv->mctrl |= TIOCM_CD;
+ else {
+ pv->mctrl &= ~TIOCM_CD;
+
+ /* We copy the existing hvsi driver semantics
+ * here which are to trigger a hangup when
+ * we get a carrier loss.
+ * Closing our connection to the server will
+ * do just that.
+ */
+ if (!pv->is_console && pv->opened) {
+ pr_devel("HVSI@%x Carrier lost, hanging up !\n",
+ pv->termno);
+ hvsi_send_close(pv);
+ }
+ }
+}
+
+static void hvsi_got_control(struct hvsi_priv *pv)
+{
+ struct hvsi_control *pkt = (struct hvsi_control *)pv->inbuf;
+
+ switch (pkt->verb) {
+ case VSV_CLOSE_PROTOCOL:
+ /* We restart the handshaking */
+ hvsi_start_handshake(pv);
+ break;
+ case VSV_MODEM_CTL_UPDATE:
+ /* Transition of carrier detect */
+ hvsi_cd_change(pv, pkt->word & HVSI_TSCD);
+ break;
+ }
+}
+
+static void hvsi_got_query(struct hvsi_priv *pv)
+{
+ struct hvsi_query *pkt = (struct hvsi_query *)pv->inbuf;
+ struct hvsi_query_response r;
+
+ /* We only handle version queries */
+ if (pkt->verb != VSV_SEND_VERSION_NUMBER)
+ return;
+
+ pr_devel("HVSI@%x: Got version query, sending response...\n",
+ pv->termno);
+
+ /* Send version response */
+ r.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER;
+ r.hdr.len = sizeof(struct hvsi_query_response);
+ r.verb = VSV_SEND_VERSION_NUMBER;
+ r.u.version = HVSI_VERSION;
+ r.query_seqno = pkt->hdr.seqno;
+ hvsi_send_packet(pv, &r.hdr);
+
+ /* Assume protocol is open now */
+ pv->established = 1;
+}
+
+static void hvsi_got_response(struct hvsi_priv *pv)
+{
+ struct hvsi_query_response *r =
+ (struct hvsi_query_response *)pv->inbuf;
+
+ switch(r->verb) {
+ case VSV_SEND_MODEM_CTL_STATUS:
+ hvsi_cd_change(pv, r->u.mctrl_word & HVSI_TSCD);
+ pv->mctrl_update = 1;
+ break;
+ }
+}
+
+static int hvsi_check_packet(struct hvsi_priv *pv)
+{
+ u8 len, type;
+
+ /* Check header validity. If it's invalid, we ditch
+ * the whole buffer and hope we eventually resync
+ */
+ if (pv->inbuf[0] < 0xfc) {
+ pv->inbuf_len = pv->inbuf_pktlen = 0;
+ return 0;
+ }
+ type = pv->inbuf[0];
+ len = pv->inbuf[1];
+
+ /* Packet incomplete ? */
+ if (pv->inbuf_len < len)
+ return 0;
+
+ pr_devel("HVSI@%x: Got packet type %x len %d bytes:\n",
+ pv->termno, type, len);
+
+ /* We have a packet, yay ! Handle it */
+ switch(type) {
+ case VS_DATA_PACKET_HEADER:
+ pv->inbuf_pktlen = len - 4;
+ pv->inbuf_cur = 4;
+ return 1;
+ case VS_CONTROL_PACKET_HEADER:
+ hvsi_got_control(pv);
+ break;
+ case VS_QUERY_PACKET_HEADER:
+ hvsi_got_query(pv);
+ break;
+ case VS_QUERY_RESPONSE_PACKET_HEADER:
+ hvsi_got_response(pv);
+ break;
+ }
+
+ /* Swallow packet and retry */
+ pv->inbuf_len -= len;
+ memmove(pv->inbuf, &pv->inbuf[len], pv->inbuf_len);
+ return 1;
+}
+
+static int hvsi_get_packet(struct hvsi_priv *pv)
+{
+ /* If we have room in the buffer, ask HV for more */
+ if (pv->inbuf_len < HVSI_INBUF_SIZE)
+ pv->inbuf_len += pv->get_chars(pv->termno,
+ &pv->inbuf[pv->inbuf_len],
+ HVSI_INBUF_SIZE - pv->inbuf_len);
+ /*
+ * If we have at least 4 bytes in the buffer, check for
+ * a full packet and retry
+ */
+ if (pv->inbuf_len >= 4)
+ return hvsi_check_packet(pv);
+ return 0;
+}
+
+int hvsi_get_chars(struct hvsi_priv *pv, char *buf, int count)
+{
+ unsigned int tries, read = 0;
+
+ if (WARN_ON(!pv))
+ return 0;
+
+ /* If we aren't open, don't do anything in order to avoid races
+ * with connection establishment. The hvc core will call this
+ * before we have returned from notifier_add(), and we need to
+ * avoid multiple users playing with the receive buffer
+ */
+ if (!pv->opened)
+ return 0;
+
+ /* We try twice, once with what data we have and once more
+ * after we try to fetch some more from the hypervisor
+ */
+ for (tries = 1; count && tries < 2; tries++) {
+ /* Consume existing data packet */
+ if (pv->inbuf_pktlen) {
+ unsigned int l = min(count, (int)pv->inbuf_pktlen);
+ memcpy(&buf[read], &pv->inbuf[pv->inbuf_cur], l);
+ pv->inbuf_cur += l;
+ pv->inbuf_pktlen -= l;
+ count -= l;
+ read += l;
+ }
+ if (count == 0)
+ break;
+
+ /* Data packet fully consumed, move down remaning data */
+ if (pv->inbuf_cur) {
+ pv->inbuf_len -= pv->inbuf_cur;
+ memmove(pv->inbuf, &pv->inbuf[pv->inbuf_cur],
+ pv->inbuf_len);
+ pv->inbuf_cur = 0;
+ }
+
+ /* Try to get another packet */
+ if (hvsi_get_packet(pv))
+ tries--;
+ }
+ if (!pv->established) {
+ pr_devel("HVSI@%x: returning -EPIPE\n", pv->termno);
+ return -EPIPE;
+ }
+ return read;
+}
+
+int hvsi_put_chars(struct hvsi_priv *pv, const char *buf, int count)
+{
+ struct hvsi_data dp;
+ int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA);
+
+ if (WARN_ON(!pv))
+ return 0;
+
+ dp.hdr.type = VS_DATA_PACKET_HEADER;
+ dp.hdr.len = adjcount + sizeof(struct hvsi_header);
+ memcpy(dp.data, buf, adjcount);
+ rc = hvsi_send_packet(pv, &dp.hdr);
+ if (rc <= 0)
+ return rc;
+ return adjcount;
+}
+
+static void maybe_msleep(unsigned long ms)
+{
+ /* During early boot, IRQs are disabled, use mdelay */
+ if (irqs_disabled())
+ mdelay(ms);
+ else
+ msleep(ms);
+}
+
+int hvsi_read_mctrl(struct hvsi_priv *pv)
+{
+ struct hvsi_query q;
+ int rc, timeout;
+
+ pr_devel("HVSI@%x: Querying modem control status...\n",
+ pv->termno);
+
+ pv->mctrl_update = 0;
+ q.hdr.type = VS_QUERY_PACKET_HEADER;
+ q.hdr.len = sizeof(struct hvsi_query);
+ q.hdr.seqno = atomic_inc_return(&pv->seqno);
+ q.verb = VSV_SEND_MODEM_CTL_STATUS;
+ rc = hvsi_send_packet(pv, &q.hdr);
+ if (rc <= 0) {
+ pr_devel("HVSI@%x: Error %d...\n", pv->termno, rc);
+ return rc;
+ }
+
+ /* Try for up to 200ms */
+ for (timeout = 0; timeout < 20; timeout++) {
+ if (!pv->established)
+ return -ENXIO;
+ if (pv->mctrl_update)
+ return 0;
+ if (!hvsi_get_packet(pv))
+ maybe_msleep(10);
+ }
+ return -EIO;
+}
+
+int hvsi_write_mctrl(struct hvsi_priv *pv, int dtr)
+{
+ struct hvsi_control ctrl;
+ unsigned short mctrl;
+
+ mctrl = pv->mctrl;
+ if (dtr)
+ mctrl |= TIOCM_DTR;
+ else
+ mctrl &= ~TIOCM_DTR;
+ if (mctrl == pv->mctrl)
+ return 0;
+ pv->mctrl = mctrl;
+
+ pr_devel("HVSI@%x: %s DTR...\n", pv->termno,
+ dtr ? "Setting" : "Clearing");
+
+ ctrl.hdr.type = VS_CONTROL_PACKET_HEADER,
+ ctrl.hdr.len = sizeof(struct hvsi_control);
+ ctrl.verb = VSV_SET_MODEM_CTL;
+ ctrl.mask = HVSI_TSDTR;
+ ctrl.word = dtr ? HVSI_TSDTR : 0;
+ return hvsi_send_packet(pv, &ctrl.hdr);
+}
+
+void hvsi_establish(struct hvsi_priv *pv)
+{
+ int timeout;
+
+ pr_devel("HVSI@%x: Establishing...\n", pv->termno);
+
+ /* Try for up to 200ms, there can be a packet to
+ * start the process waiting for us...
+ */
+ for (timeout = 0; timeout < 20; timeout++) {
+ if (pv->established)
+ goto established;
+ if (!hvsi_get_packet(pv))
+ maybe_msleep(10);
+ }
+
+ /* Failed, send a close connection packet just
+ * in case
+ */
+ pr_devel("HVSI@%x: ... sending close\n", pv->termno);
+
+ hvsi_send_close(pv);
+
+ /* Then restart handshake */
+
+ pr_devel("HVSI@%x: ... restarting handshake\n", pv->termno);
+
+ hvsi_start_handshake(pv);
+
+ pr_devel("HVSI@%x: ... waiting handshake\n", pv->termno);
+
+ /* Try for up to 200s */
+ for (timeout = 0; timeout < 20; timeout++) {
+ if (pv->established)
+ goto established;
+ if (!hvsi_get_packet(pv))
+ maybe_msleep(10);
+ }
+
+ if (!pv->established) {
+ pr_devel("HVSI@%x: Timeout handshaking, giving up !\n",
+ pv->termno);
+ return;
+ }
+ established:
+ /* Query modem control lines */
+
+ pr_devel("HVSI@%x: ... established, reading mctrl\n", pv->termno);
+
+ hvsi_read_mctrl(pv);
+
+ /* Set our own DTR */
+
+ pr_devel("HVSI@%x: ... setting mctrl\n", pv->termno);
+
+ hvsi_write_mctrl(pv, 1);
+
+ /* Set the opened flag so reads are allowed */
+ wmb();
+ pv->opened = 1;
+}
+
+int hvsi_open(struct hvsi_priv *pv, struct hvc_struct *hp)
+{
+ pr_devel("HVSI@%x: open !\n", pv->termno);
+
+ /* Keep track of the tty data structure */
+ pv->tty = tty_kref_get(hp->tty);
+
+ hvsi_establish(pv);
+
+ return 0;
+}
+
+void hvsi_close(struct hvsi_priv *pv, struct hvc_struct *hp)
+{
+ unsigned long flags;
+
+ pr_devel("HVSI@%x: close !\n", pv->termno);
+
+ if (!pv->is_console) {
+ pr_devel("HVSI@%x: Not a console, tearing down\n",
+ pv->termno);
+
+ /* Clear opened, synchronize with khvcd */
+ spin_lock_irqsave(&hp->lock, flags);
+ pv->opened = 0;
+ spin_unlock_irqrestore(&hp->lock, flags);
+
+ /* Clear our own DTR */
+ if (!pv->tty || (pv->tty->termios->c_cflag & HUPCL))
+ hvsi_write_mctrl(pv, 0);
+
+ /* Tear down the connection */
+ hvsi_send_close(pv);
+ }
+
+ if (pv->tty)
+ tty_kref_put(pv->tty);
+ pv->tty = NULL;
+}
+
+void hvsi_init(struct hvsi_priv *pv,
+ int (*get_chars)(uint32_t termno, char *buf, int count),
+ int (*put_chars)(uint32_t termno, const char *buf,
+ int count),
+ int termno, int is_console)
+{
+ memset(pv, 0, sizeof(*pv));
+ pv->get_chars = get_chars;
+ pv->put_chars = put_chars;
+ pv->termno = termno;
+ pv->is_console = is_console;
+}
^ permalink raw reply related
* [PATCH 3/5] powerpc/udbg: Register udbg console generically
From: Benjamin Herrenschmidt @ 2011-06-17 1:08 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-kernel@vger.kernel.org
When CONFIG_PPC_EARLY_DEBUG is set, call register_early_udbg_console()
early from generic code.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/kernel/udbg.c | 2 ++
arch/powerpc/platforms/pseries/lpar.c | 2 --
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c
index 23d65ab..a57e61e 100644
--- a/arch/powerpc/kernel/udbg.c
+++ b/arch/powerpc/kernel/udbg.c
@@ -68,6 +68,8 @@ void __init udbg_early_init(void)
#ifdef CONFIG_PPC_EARLY_DEBUG
console_loglevel = 10;
+
+ register_early_udbg_console();
#endif
}
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 39e6e0a..e3a96c4 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -193,8 +193,6 @@ void __init udbg_init_debug_lpar(void)
udbg_putc = udbg_putcLP;
udbg_getc = udbg_getcLP;
udbg_getc_poll = udbg_getc_pollLP;
-
- register_early_udbg_console();
}
/* returns 0 if couldn't find or use /chosen/stdout as console */
^ permalink raw reply related
* [PATCH 4/5] powerpc/pseries: Re-implement HVSI as part of hvc_vio
From: Benjamin Herrenschmidt @ 2011-06-17 1:08 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-kernel@vger.kernel.org
On pseries machines, consoles are provided by the hypervisor using
a low level get_chars/put_chars type interface. However, this is
really just a transport to the service processor which implements
them either as "raw" console (networked consoles, HMC, ...) or as
"hvsi" serial ports.
The later is a simple packet protocol on top of the raw character
interface that is supposed to convey additional "serial port" style
semantics. In practice however, all it does is provide a way to
read the CD line and set/clear our DTR line, that's it.
We currently implement the "raw" protocol as an hvc console backend
(/dev/hvcN) and the "hvsi" protocol using a separate tty driver
(/dev/hvsi0).
However this is quite impractical. The arbitrary difference between
the two type of devices has been a major source of user (and distro)
confusion. Additionally, there's an additional mini -hvsi implementation
in the pseries platform code for our low level debug console and early
boot kernel messages, which means code duplication, though that low
level variant is impractical as it's incapable of doing the initial
protocol negociation to establish the link to the FSP.
This essentially replaces the dedicated hvsi driver and the platform
udbg code completely by extending the existing hvc_vio backend used
in "raw" mode so that:
- It now supports HVSI as well
- We add support for hvc backend providing tiocm{get,set}
- It also provides a udbg interface for early debug and boot console
This is overall less code, though this will only be obvious once we
remove the old "hvsi" driver, which is still available for now. When
the old driver is enabled, the new code still kicks in for the low
level udbg console, replacing the old mini implementation in the
platform
code, it just doesn't provide the higher level "hvc" interface.
In addition to producing generally simler code, this has several
benefits
over our current situation:
- The user/distro only has to deal with /dev/hvcN for the hypervisor
console, avoiding all sort of confusion that has plagued us in the past
- The tty, kernel and low level debug console all use the same code
base which supports the full protocol establishment process, thus the
console is now available much earlier than it used to be with the
old HVSI driver. The kernel console works much earlier and udbg is
available much earlier too. Hackers can enable a hard coded very-early
debug console as well that works with HVSI (previously that was only
supported for the "raw" mode).
I've tried to keep the same semantics as hvsi relative to how I react
to things like CD changes, with some subtle differences though:
- I clear DTR on close if HUPCL is set
- Current hvsi triggers a hangup if it detects a up->down transition
on CD (you can still open a console with CD down). My new
implementation
triggers a hangup if the link to the FSP is severed, and severs it
upon
detecting a up->down transition on CD.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/Kconfig.debug | 15 +
arch/powerpc/include/asm/udbg.h | 1 +
arch/powerpc/kernel/udbg.c | 3 +
arch/powerpc/platforms/pseries/lpar.c | 189 --------
arch/powerpc/platforms/pseries/pseries.h | 3 +-
arch/powerpc/platforms/pseries/setup.c | 5 +-
drivers/tty/hvc/Kconfig | 5 +
drivers/tty/hvc/Makefile | 3 +-
drivers/tty/hvc/hvc_console.c | 23 +-
drivers/tty/hvc/hvc_console.h | 4 +
drivers/tty/hvc/hvc_vio.c | 725
++++++++++++++++++++++++++++--
11 files changed, 749 insertions(+), 227 deletions(-)
diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index e72dcf6..067cb84 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -167,6 +167,13 @@ config PPC_EARLY_DEBUG_LPAR
Select this to enable early debugging for a machine with a HVC
console on vterm 0.
+config PPC_EARLY_DEBUG_LPAR_HVSI
+ bool "LPAR HVSI Console"
+ depends on PPC_PSERIES
+ help
+ Select this to enable early debugging for a machine with a HVSI
+ console on a specified vterm.
+
config PPC_EARLY_DEBUG_G5
bool "Apple G5"
depends on PPC_PMAC64
@@ -253,6 +260,14 @@ config PPC_EARLY_DEBUG_WSP
endchoice
+config PPC_EARLY_DEBUG_HVSI_VTERMNO
+ hex "vterm number to use with early debug HVSI"
+ depends on PPC_EARLY_DEBUG_LPAR_HVSI
+ default "0x30000000"
+ help
+ You probably want 0x30000000 for your first serial port and
+ 0x30000001 for your second one
+
config PPC_EARLY_DEBUG_44x_PHYSLOW
hex "Low 32 bits of early debug UART physical address"
depends on PPC_EARLY_DEBUG_44x
diff --git a/arch/powerpc/include/asm/udbg.h
b/arch/powerpc/include/asm/udbg.h
index 58580e9..93e05d1 100644
--- a/arch/powerpc/include/asm/udbg.h
+++ b/arch/powerpc/include/asm/udbg.h
@@ -40,6 +40,7 @@ extern void udbg_adb_init_early(void);
extern void __init udbg_early_init(void);
extern void __init udbg_init_debug_lpar(void);
+extern void __init udbg_init_debug_lpar_hvsi(void);
extern void __init udbg_init_pmac_realmode(void);
extern void __init udbg_init_maple_realmode(void);
extern void __init udbg_init_pas_realmode(void);
diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c
index a57e61e..faa82c1 100644
--- a/arch/powerpc/kernel/udbg.c
+++ b/arch/powerpc/kernel/udbg.c
@@ -31,6 +31,9 @@ void __init udbg_early_init(void)
#if defined(CONFIG_PPC_EARLY_DEBUG_LPAR)
/* For LPAR machines that have an HVC console on vterm 0 */
udbg_init_debug_lpar();
+#elif defined(CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI)
+ /* For LPAR machines that have an HVSI console on vterm 0 */
+ udbg_init_debug_lpar_hvsi();
#elif defined(CONFIG_PPC_EARLY_DEBUG_G5)
/* For use on Apple G5 machines */
udbg_init_pmac_realmode();
diff --git a/arch/powerpc/platforms/pseries/lpar.c
b/arch/powerpc/platforms/pseries/lpar.c
index e3a96c4..f7205d3 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -52,195 +52,6 @@ EXPORT_SYMBOL(plpar_hcall_norets);
extern void pSeries_find_serial_port(void);
-
-static int vtermno; /* virtual terminal# for udbg */
-
-#define __ALIGNED__ __attribute__((__aligned__(sizeof(long))))
-static void udbg_hvsi_putc(char c)
-{
- /* packet's seqno isn't used anyways */
- uint8_t packet[] __ALIGNED__ = { 0xff, 5, 0, 0, c };
- int rc;
-
- if (c == '\n')
- udbg_hvsi_putc('\r');
-
- do {
- rc = plpar_put_term_char(vtermno, sizeof(packet), packet);
- } while (rc == H_BUSY);
-}
-
-static long hvsi_udbg_buf_len;
-static uint8_t hvsi_udbg_buf[256];
-
-static int udbg_hvsi_getc_poll(void)
-{
- unsigned char ch;
- int rc, i;
-
- if (hvsi_udbg_buf_len == 0) {
- rc = plpar_get_term_char(vtermno, &hvsi_udbg_buf_len, hvsi_udbg_buf);
- if (rc != H_SUCCESS || hvsi_udbg_buf[0] != 0xff) {
- /* bad read or non-data packet */
- hvsi_udbg_buf_len = 0;
- } else {
- /* remove the packet header */
- for (i = 4; i < hvsi_udbg_buf_len; i++)
- hvsi_udbg_buf[i-4] = hvsi_udbg_buf[i];
- hvsi_udbg_buf_len -= 4;
- }
- }
-
- if (hvsi_udbg_buf_len <= 0 || hvsi_udbg_buf_len > 256) {
- /* no data ready */
- hvsi_udbg_buf_len = 0;
- return -1;
- }
-
- ch = hvsi_udbg_buf[0];
- /* shift remaining data down */
- for (i = 1; i < hvsi_udbg_buf_len; i++) {
- hvsi_udbg_buf[i-1] = hvsi_udbg_buf[i];
- }
- hvsi_udbg_buf_len--;
-
- return ch;
-}
-
-static int udbg_hvsi_getc(void)
-{
- int ch;
- for (;;) {
- ch = udbg_hvsi_getc_poll();
- if (ch == -1) {
- /* This shouldn't be needed...but... */
- volatile unsigned long delay;
- for (delay=0; delay < 2000000; delay++)
- ;
- } else {
- return ch;
- }
- }
-}
-
-static void udbg_putcLP(char c)
-{
- char buf[16];
- unsigned long rc;
-
- if (c == '\n')
- udbg_putcLP('\r');
-
- buf[0] = c;
- do {
- rc = plpar_put_term_char(vtermno, 1, buf);
- } while(rc == H_BUSY);
-}
-
-/* Buffered chars getc */
-static long inbuflen;
-static long inbuf[2]; /* must be 2 longs */
-
-static int udbg_getc_pollLP(void)
-{
- /* The interface is tricky because it may return up to 16 chars.
- * We save them statically for future calls to udbg_getc().
- */
- char ch, *buf = (char *)inbuf;
- int i;
- long rc;
- if (inbuflen == 0) {
- /* get some more chars. */
- inbuflen = 0;
- rc = plpar_get_term_char(vtermno, &inbuflen, buf);
- if (rc != H_SUCCESS)
- inbuflen = 0; /* otherwise inbuflen is garbage */
- }
- if (inbuflen <= 0 || inbuflen > 16) {
- /* Catch error case as well as other oddities (corruption) */
- inbuflen = 0;
- return -1;
- }
- ch = buf[0];
- for (i = 1; i < inbuflen; i++) /* shuffle them down. */
- buf[i-1] = buf[i];
- inbuflen--;
- return ch;
-}
-
-static int udbg_getcLP(void)
-{
- int ch;
- for (;;) {
- ch = udbg_getc_pollLP();
- if (ch == -1) {
- /* This shouldn't be needed...but... */
- volatile unsigned long delay;
- for (delay=0; delay < 2000000; delay++)
- ;
- } else {
- return ch;
- }
- }
-}
-
-/* call this from early_init() for a working debug console on
- * vterm capable LPAR machines
- */
-void __init udbg_init_debug_lpar(void)
-{
- vtermno = 0;
- udbg_putc = udbg_putcLP;
- udbg_getc = udbg_getcLP;
- udbg_getc_poll = udbg_getc_pollLP;
-}
-
-/* returns 0 if couldn't find or use /chosen/stdout as console */
-void __init find_udbg_vterm(void)
-{
- struct device_node *stdout_node;
- const u32 *termno;
- const char *name;
-
- /* find the boot console from /chosen/stdout */
- if (!of_chosen)
- return;
- name = of_get_property(of_chosen, "linux,stdout-path", NULL);
- if (name == NULL)
- return;
- stdout_node = of_find_node_by_path(name);
- if (!stdout_node)
- return;
- name = of_get_property(stdout_node, "name", NULL);
- if (!name) {
- printk(KERN_WARNING "stdout node missing 'name' property!\n");
- goto out;
- }
-
- /* Check if it's a virtual terminal */
- if (strncmp(name, "vty", 3) != 0)
- goto out;
- termno = of_get_property(stdout_node, "reg", NULL);
- if (termno == NULL)
- goto out;
- vtermno = termno[0];
-
- if (of_device_is_compatible(stdout_node, "hvterm1")) {
- udbg_putc = udbg_putcLP;
- udbg_getc = udbg_getcLP;
- udbg_getc_poll = udbg_getc_pollLP;
- add_preferred_console("hvc", termno[0] & 0xff, NULL);
- } else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) {
- vtermno = termno[0];
- udbg_putc = udbg_hvsi_putc;
- udbg_getc = udbg_hvsi_getc;
- udbg_getc_poll = udbg_hvsi_getc_poll;
- add_preferred_console("hvsi", termno[0] & 0xff, NULL);
- }
-out:
- of_node_put(stdout_node);
-}
-
void vpa_init(int cpu)
{
int hwcpu = get_hard_smp_processor_id(cpu);
diff --git a/arch/powerpc/platforms/pseries/pseries.h
b/arch/powerpc/platforms/pseries/pseries.h
index e9f6d28..24c7162 100644
--- a/arch/powerpc/platforms/pseries/pseries.h
+++ b/arch/powerpc/platforms/pseries/pseries.h
@@ -47,7 +47,8 @@ extern void pSeries_final_fixup(void);
/* Poweron flag used for enabling auto ups restart */
extern unsigned long rtas_poweron_auto;
-extern void find_udbg_vterm(void);
+/* Provided by HVC VIO */
+extern void hvc_vio_init_early(void);
/* Dynamic logical Partitioning/Mobility */
extern void dlpar_free_cc_nodes(struct device_node *);
diff --git a/arch/powerpc/platforms/pseries/setup.c
b/arch/powerpc/platforms/pseries/setup.c
index 593acce..d00e529 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -512,9 +512,10 @@ static void __init pSeries_init_early(void)
{
pr_debug(" -> pSeries_init_early()\n");
+#ifdef CONFIG_HVC_CONSOLE
if (firmware_has_feature(FW_FEATURE_LPAR))
- find_udbg_vterm();
-
+ hvc_vio_init_early();
+#endif
if (firmware_has_feature(FW_FEATURE_DABR))
ppc_md.set_dabr = pseries_set_dabr;
else if (firmware_has_feature(FW_FEATURE_XDABR))
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig
index 6f2c980..e371753 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -19,6 +19,11 @@ config HVC_CONSOLE
console. This driver allows each pSeries partition to have a console
which is accessed via the HMC.
+config HVC_OLD_HVSI
+ bool "Old driver for pSeries serial port (/dev/hvsi*)"
+ depends on HVC_CONSOLE
+ default n
+
config HVC_ISERIES
bool "iSeries Hypervisor Virtual Console support"
depends on PPC_ISERIES
diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile
index 40a25d9..69a444b 100644
--- a/drivers/tty/hvc/Makefile
+++ b/drivers/tty/hvc/Makefile
@@ -1,4 +1,5 @@
-obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o
+obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o
+obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o
obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o
obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o
obj-$(CONFIG_HVC_TILE) += hvc_tile.o
diff --git a/drivers/tty/hvc/hvc_console.c
b/drivers/tty/hvc/hvc_console.c
index e9cba13..f8ff6f5 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -184,7 +184,7 @@ static struct tty_driver *hvc_console_device(struct
console *c, int *index)
}
static int __init hvc_console_setup(struct console *co, char *options)
-{
+{
if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES)
return -ENODEV;
@@ -745,6 +745,25 @@ static int khvcd(void *unused)
return 0;
}
+static int hvc_tiocmget(struct tty_struct *tty)
+{
+ struct hvc_struct *hp = tty->driver_data;
+
+ if (!hp || !hp->ops->tiocmget)
+ return -EINVAL;
+ return hp->ops->tiocmget(hp);
+}
+
+static int hvc_tiocmset(struct tty_struct *tty,
+ unsigned int set, unsigned int clear)
+{
+ struct hvc_struct *hp = tty->driver_data;
+
+ if (!hp || !hp->ops->tiocmset)
+ return -EINVAL;
+ return hp->ops->tiocmset(hp, set, clear);
+}
+
static const struct tty_operations hvc_ops = {
.open = hvc_open,
.close = hvc_close,
@@ -753,6 +772,8 @@ static const struct tty_operations hvc_ops = {
.unthrottle = hvc_unthrottle,
.write_room = hvc_write_room,
.chars_in_buffer = hvc_chars_in_buffer,
+ .tiocmget = hvc_tiocmget,
+ .tiocmset = hvc_tiocmset,
};
struct hvc_struct *hvc_alloc(uint32_t vtermno, int data,
diff --git a/drivers/tty/hvc/hvc_console.h
b/drivers/tty/hvc/hvc_console.h
index 54381eba..c335a14 100644
--- a/drivers/tty/hvc/hvc_console.h
+++ b/drivers/tty/hvc/hvc_console.h
@@ -73,6 +73,10 @@ struct hv_ops {
int (*notifier_add)(struct hvc_struct *hp, int irq);
void (*notifier_del)(struct hvc_struct *hp, int irq);
void (*notifier_hangup)(struct hvc_struct *hp, int irq);
+
+ /* tiocmget/set implementation */
+ int (*tiocmget)(struct hvc_struct *hp);
+ int (*tiocmset)(struct hvc_struct *hp, unsigned int set, unsigned int
clear);
};
/* Register a vterm and a slot index for use as a console
(console_init) */
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c
index e6eea14..d4e0850 100644
--- a/drivers/tty/hvc/hvc_vio.c
+++ b/drivers/tty/hvc/hvc_vio.c
@@ -27,15 +27,27 @@
* 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
+ *
+ * TODO:
+ *
+ * - handle error in sending hvsi protocol packets
+ * - retry nego on subsequent sends ?
*/
+#undef DEBUG
+
#include <linux/types.h>
#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/console.h>
#include <asm/hvconsole.h>
#include <asm/vio.h>
#include <asm/prom.h>
#include <asm/firmware.h>
+#include <asm/hvsi.h>
+#include <asm/udbg.h>
#include "hvc_console.h"
@@ -43,14 +55,47 @@ static const char hvc_driver_name[] = "hvc_console";
static struct vio_device_id hvc_driver_table[] __devinitdata = {
{"serial", "hvterm1"},
+#ifndef HVC_OLD_HVSI
+ {"serial", "hvterm-protocol"},
+#endif
{ "", "" }
};
MODULE_DEVICE_TABLE(vio, hvc_driver_table);
-static int filtered_get_chars(uint32_t vtermno, char *buf, int count)
+typedef enum hv_protocol {
+ HV_PROTOCOL_RAW,
+ HV_PROTOCOL_HVSI
+} hv_protocol_t;
+
+#define HV_INBUF_SIZE 255
+
+struct hvterm_priv {
+ u32 termno; /* HV term number */
+ hv_protocol_t proto; /* Raw data or HVSI packets */
+ unsigned int inbuf_len; /* Data in input buffer */
+ unsigned char inbuf[HV_INBUF_SIZE];
+ unsigned int inbuf_cur; /* Cursor in input buffer */
+ unsigned int inbuf_pktlen; /* HVSI packet lenght from cursor */
+ atomic_t seqno; /* HVSI packet sequence number */
+ unsigned int opened:1; /* HVSI driver opened */
+ unsigned int established:1; /* HVSI protocol established */
+ unsigned int is_console:1; /* Used as a kernel console device */
+ unsigned int mctrl_update:1; /* HVSI modem control updated */
+ unsigned short mctrl; /* HVSI modem control */
+ struct tty_struct *tty; /* TTY structure */
+};
+static struct hvterm_priv *hvterm_privs[MAX_NR_HVC_CONSOLES];
+
+/* For early boot console */
+static struct hvterm_priv hvterm_priv0;
+
+static int hvterm_raw_get_chars(uint32_t vtermno, char *buf, int count)
{
- unsigned long got;
- int i;
+ struct hvterm_priv *pv = hvterm_privs[vtermno];
+ unsigned long got, i;
+
+ if (WARN_ON(!pv))
+ return 0;
/*
* Vio firmware will read up to SIZE_VIO_GET_CHARS at its own
discretion
@@ -60,7 +105,7 @@ static int filtered_get_chars(uint32_t vtermno, char
*buf, int count)
if (count < SIZE_VIO_GET_CHARS)
return -EAGAIN;
- got = hvc_get_chars(vtermno, buf, count);
+ got = hvc_get_chars(pv->termno, buf, count);
/*
* Work around a HV bug where it gives us a null
@@ -70,32 +115,527 @@ static int filtered_get_chars(uint32_t vtermno,
char *buf, int count)
if (buf[i] == 0 && buf[i-1] == '\r') {
--got;
if (i < got)
- memmove(&buf[i], &buf[i+1],
- got - i);
+ memmove(&buf[i], &buf[i+1], got - i);
}
}
return got;
}
-static const struct hv_ops hvc_get_put_ops = {
- .get_chars = filtered_get_chars,
- .put_chars = hvc_put_chars,
+static int hvterm_raw_put_chars(uint32_t vtermno, const char *buf, int
count)
+{
+ struct hvterm_priv *pv = hvterm_privs[vtermno];
+
+ if (WARN_ON(!pv))
+ return 0;
+
+ return hvc_put_chars(pv->termno, buf, count);
+}
+
+static const struct hv_ops hvterm_raw_ops = {
+ .get_chars = hvterm_raw_get_chars,
+ .put_chars = hvterm_raw_put_chars,
.notifier_add = notifier_add_irq,
.notifier_del = notifier_del_irq,
.notifier_hangup = notifier_hangup_irq,
};
+static int hvterm_hvsi_send_packet(struct hvterm_priv *pv, struct
hvsi_header *packet)
+{
+ packet->seqno = atomic_inc_return(&pv->seqno);
+
+ /* Assumes that always succeeds, works in practice */
+ return hvc_put_chars(pv->termno, (char *)packet, packet->len);
+}
+
+static void hvterm_hvsi_start_handshake(struct hvterm_priv *pv)
+{
+ struct hvsi_query q;
+
+ /* Reset state */
+ pv->established = 0;
+ atomic_set(&pv->seqno, 0);
+
+ pr_devel("HVSI@%x: Handshaking started\n", pv->termno);
+
+ /* Send version query */
+ q.hdr.type = VS_QUERY_PACKET_HEADER;
+ q.hdr.len = sizeof(struct hvsi_query);
+ q.verb = VSV_SEND_VERSION_NUMBER;
+ hvterm_hvsi_send_packet(pv, &q.hdr);
+}
+
+static int hvterm_hvsi_send_close(struct hvterm_priv *pv)
+{
+ struct hvsi_control ctrl;
+
+ pv->established = 0;
+
+ ctrl.hdr.type = VS_CONTROL_PACKET_HEADER;
+ ctrl.hdr.len = sizeof(struct hvsi_control);
+ ctrl.verb = VSV_CLOSE_PROTOCOL;
+ return hvterm_hvsi_send_packet(pv, &ctrl.hdr);
+}
+
+static void hvterm_cd_change(struct hvterm_priv *pv, int cd)
+{
+ if (cd)
+ pv->mctrl |= TIOCM_CD;
+ else {
+ pv->mctrl &= ~TIOCM_CD;
+
+ /* We copy the existing hvsi driver semantics
+ * here which are to trigger a hangup when
+ * we get a carrier loss.
+ * Closing our connection to the server will
+ * do just that.
+ */
+ if (!pv->is_console && pv->opened) {
+ pr_devel("HVSI@%x Carrier lost, hanging up !\n",
+ pv->termno);
+ hvterm_hvsi_send_close(pv);
+ }
+ }
+}
+
+static void hvterm_hvsi_got_control(struct hvterm_priv *pv)
+{
+ struct hvsi_control *pkt = (struct hvsi_control *)pv->inbuf;
+
+ switch (pkt->verb) {
+ case VSV_CLOSE_PROTOCOL:
+ /* We restart the handshaking */
+ hvterm_hvsi_start_handshake(pv);
+ break;
+ case VSV_MODEM_CTL_UPDATE:
+ /* Transition of carrier detect */
+ hvterm_cd_change(pv, pkt->word & HVSI_TSCD);
+ break;
+ }
+}
+
+static void hvterm_hvsi_got_query(struct hvterm_priv *pv)
+{
+ struct hvsi_query *pkt = (struct hvsi_query *)pv->inbuf;
+ struct hvsi_query_response r;
+
+ /* We only handle version queries */
+ if (pkt->verb != VSV_SEND_VERSION_NUMBER)
+ return;
+
+ pr_devel("HVSI@%x: Got version query, sending response...\n",
+ pv->termno);
+
+ /* Send version response */
+ r.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER;
+ r.hdr.len = sizeof(struct hvsi_query_response);
+ r.verb = VSV_SEND_VERSION_NUMBER;
+ r.u.version = HVSI_VERSION;
+ r.query_seqno = pkt->hdr.seqno;
+ hvterm_hvsi_send_packet(pv, &r.hdr);
+
+ /* Assume protocol is open now */
+ pv->established = 1;
+}
+
+static void hvterm_hvsi_got_response(struct hvterm_priv *pv)
+{
+ struct hvsi_query_response *r = (struct hvsi_query_response
*)pv->inbuf;
+
+ switch(r->verb) {
+ case VSV_SEND_MODEM_CTL_STATUS:
+ hvterm_cd_change(pv, r->u.mctrl_word & HVSI_TSCD);
+ pv->mctrl_update = 1;
+ break;
+ }
+}
+
+static int hvterm_hvsi_check_packet(struct hvterm_priv *pv)
+{
+ u8 len, type;
+
+ /* Check header validity. If it's invalid, we ditch
+ * the whole buffer and hope we eventually resync
+ */
+ if (pv->inbuf[0] < 0xfc) {
+ pv->inbuf_len = pv->inbuf_pktlen = 0;
+ return 0;
+ }
+ type = pv->inbuf[0];
+ len = pv->inbuf[1];
+
+ /* Packet incomplete ? */
+ if (pv->inbuf_len < len)
+ return 0;
+
+ pr_devel("HVSI@%x: Got packet type %x len %d bytes:\n",
+ pv->termno, type, len);
+
+ /* We have a packet, yay ! Handle it */
+ switch(type) {
+ case VS_DATA_PACKET_HEADER:
+ pv->inbuf_pktlen = len - 4;
+ pv->inbuf_cur = 4;
+ return 1;
+ case VS_CONTROL_PACKET_HEADER:
+ hvterm_hvsi_got_control(pv);
+ break;
+ case VS_QUERY_PACKET_HEADER:
+ hvterm_hvsi_got_query(pv);
+ break;
+ case VS_QUERY_RESPONSE_PACKET_HEADER:
+ hvterm_hvsi_got_response(pv);
+ break;
+ }
+
+ /* Swallow packet and retry */
+ pv->inbuf_len -= len;
+ memmove(pv->inbuf, &pv->inbuf[len], pv->inbuf_len);
+ return 1;
+}
+
+static int hvterm_hvsi_get_packet(struct hvterm_priv *pv)
+{
+ /* If we have room in the buffer, ask HV for more */
+ if (pv->inbuf_len < HV_INBUF_SIZE)
+ pv->inbuf_len += hvc_get_chars(pv->termno,
+ &pv->inbuf[pv->inbuf_len],
+ HV_INBUF_SIZE - pv->inbuf_len);
+ /*
+ * If we have at least 4 bytes in the buffer, check for
+ * a full packet and retry
+ */
+ if (pv->inbuf_len >= 4)
+ return hvterm_hvsi_check_packet(pv);
+ return 0;
+}
+
+static int hvterm_hvsi_get_chars(uint32_t vtermno, char *buf, int
count)
+{
+ struct hvterm_priv *pv = hvterm_privs[vtermno];
+ unsigned int tries, read = 0;
+
+ if (WARN_ON(!pv))
+ return 0;
+
+ /* If we aren't open, dont do anything in order to avoid races
+ * with connection establishment. The hvc core will call this
+ * before we have returned from notifier_add(), and we need to
+ * avoid multiple users playing with the receive buffer
+ */
+ if (!pv->opened)
+ return 0;
+
+ /* We try twice, once with what data we have and once more
+ * after we try to fetch some more from the hypervisor
+ */
+ for (tries = 1; count && tries < 2; tries++) {
+ /* Consume existing data packet */
+ if (pv->inbuf_pktlen) {
+ unsigned int l = min(count, (int)pv->inbuf_pktlen);
+ memcpy(&buf[read], &pv->inbuf[pv->inbuf_cur], l);
+ pv->inbuf_cur += l;
+ pv->inbuf_pktlen -= l;
+ count -= l;
+ read += l;
+ }
+ if (count == 0)
+ break;
+
+ /* Data packet fully consumed, move down remaning data */
+ if (pv->inbuf_cur) {
+ pv->inbuf_len -= pv->inbuf_cur;
+ memmove(pv->inbuf, &pv->inbuf[pv->inbuf_cur], pv->inbuf_len);
+ pv->inbuf_cur = 0;
+ }
+
+ /* Try to get another packet */
+ if (hvterm_hvsi_get_packet(pv))
+ tries--;
+ }
+ if (!pv->established) {
+ pr_devel("HVSI@%x: returning -EPIPE\n", pv->termno);
+ return -EPIPE;
+ }
+ return read;
+}
+
+static int hvterm_hvsi_put_chars(uint32_t vtermno, const char *buf, int
count)
+{
+ struct hvterm_priv *pv = hvterm_privs[vtermno];
+ struct hvsi_data dp;
+ int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA);
+
+ if (WARN_ON(!pv))
+ return 0;
+
+ dp.hdr.type = VS_DATA_PACKET_HEADER;
+ dp.hdr.len = adjcount + sizeof(struct hvsi_header);
+ memcpy(dp.data, buf, adjcount);
+ rc = hvterm_hvsi_send_packet(pv, &dp.hdr);
+ if (rc <= 0)
+ return rc;
+ return adjcount;
+}
+
+static void maybe_msleep(unsigned long ms)
+{
+ /* During early boot, IRQs are disabled, use mdelay */
+ if (irqs_disabled())
+ mdelay(ms);
+ else
+ msleep(ms);
+}
+
+static int hvterm_hvsi_read_mctrl(struct hvterm_priv *pv)
+{
+ struct hvsi_query q;
+ int rc, timeout;
+
+ pr_devel("HVSI@%x: Querying modem control status...\n",
+ pv->termno);
+
+ pv->mctrl_update = 0;
+ q.hdr.type = VS_QUERY_PACKET_HEADER;
+ q.hdr.len = sizeof(struct hvsi_query);
+ q.hdr.seqno = atomic_inc_return(&pv->seqno);
+ q.verb = VSV_SEND_MODEM_CTL_STATUS;
+ rc = hvterm_hvsi_send_packet(pv, &q.hdr);
+ if (rc <= 0) {
+ pr_devel("HVSI@%x: Error %d...\n", pv->termno, rc);
+ return rc;
+ }
+
+ /* Try for up to 1s */
+ for (timeout = 0; timeout < 1000; timeout++) {
+ if (!pv->established)
+ return -ENXIO;
+ if (pv->mctrl_update)
+ return 0;
+ if (!hvterm_hvsi_get_packet(pv))
+ maybe_msleep(1);
+ }
+ return -EIO;
+}
+
+static int hvterm_hvsi_write_mctrl(struct hvterm_priv *pv, int dtr)
+{
+ struct hvsi_control ctrl;
+
+ pr_devel("HVSI@%x: %s DTR...\n", pv->termno,
+ dtr ? "Setting" : "Clearing");
+
+ ctrl.hdr.type = VS_CONTROL_PACKET_HEADER,
+ ctrl.hdr.len = sizeof(struct hvsi_control);
+ ctrl.verb = VSV_SET_MODEM_CTL;
+ ctrl.mask = HVSI_TSDTR;
+ ctrl.word = dtr ? HVSI_TSDTR : 0;
+ if (dtr)
+ pv->mctrl |= TIOCM_DTR;
+ else
+ pv->mctrl &= ~TIOCM_DTR;
+ return hvterm_hvsi_send_packet(pv, &ctrl.hdr);
+}
+
+static void hvterm_hvsi_establish(struct hvterm_priv *pv)
+{
+ int timeout;
+
+ /* Try for up to 10ms, there can be a packet to
+ * start the process waiting for us...
+ */
+ for (timeout = 0; timeout < 10; timeout++) {
+ if (pv->established)
+ goto established;
+ if (!hvterm_hvsi_get_packet(pv))
+ maybe_msleep(1);
+ }
+
+ /* Failed, send a close connection packet just
+ * in case
+ */
+ hvterm_hvsi_send_close(pv);
+
+ /* Then restart handshake */
+ hvterm_hvsi_start_handshake(pv);
+
+ /* Try for up to 100ms */
+ for (timeout = 0; timeout < 100; timeout++) {
+ if (pv->established)
+ goto established;
+ if (!hvterm_hvsi_get_packet(pv))
+ maybe_msleep(1);
+ }
+
+ if (!pv->established) {
+ pr_devel("HVSI@%x: Timeout handshaking, giving up !\n",
+ pv->termno);
+ return;
+ }
+ established:
+ /* Query modem control lines */
+ hvterm_hvsi_read_mctrl(pv);
+
+ /* Set our own DTR */
+ hvterm_hvsi_write_mctrl(pv, 1);
+
+ /* Set the opened flag so reads are allowed */
+ wmb();
+ pv->opened = 1;
+}
+
+static int hvterm_hvsi_open(struct hvc_struct *hp, int data)
+{
+ struct hvterm_priv *pv = hvterm_privs[hp->vtermno];
+ int rc;
+
+ pr_devel("HVSI@%x: open !\n", pv->termno);
+
+ rc = notifier_add_irq(hp, data);
+ if (rc)
+ return rc;
+
+ /* Keep track of the tty data structure */
+ pv->tty = tty_kref_get(hp->tty);
+
+ hvterm_hvsi_establish(pv);
+ return 0;
+}
+
+static void hvterm_hvsi_shutdown(struct hvc_struct *hp, struct
hvterm_priv *pv)
+{
+ unsigned long flags;
+
+ if (!pv->is_console) {
+ pr_devel("HVSI@%x: Not a console, tearing down\n",
+ pv->termno);
+
+ /* Clear opened, synchronize with khvcd */
+ spin_lock_irqsave(&hp->lock, flags);
+ pv->opened = 0;
+ spin_unlock_irqrestore(&hp->lock, flags);
+
+ /* Clear our own DTR */
+ if (!pv->tty || (pv->tty->termios->c_cflag & HUPCL))
+ hvterm_hvsi_write_mctrl(pv, 0);
+
+ /* Tear down the connection */
+ hvterm_hvsi_send_close(pv);
+ }
+
+ if (pv->tty)
+ tty_kref_put(pv->tty);
+ pv->tty = NULL;
+}
+
+static void hvterm_hvsi_close(struct hvc_struct *hp, int data)
+{
+ struct hvterm_priv *pv = hvterm_privs[hp->vtermno];
+
+ pr_devel("HVSI@%x: close !\n", pv->termno);
+
+ hvterm_hvsi_shutdown(hp, pv);
+
+ notifier_del_irq(hp, data);
+}
+
+void hvterm_hvsi_hangup(struct hvc_struct *hp, int data)
+{
+ struct hvterm_priv *pv = hvterm_privs[hp->vtermno];
+
+ pr_devel("HVSI@%x: hangup !\n", pv->termno);
+
+ hvterm_hvsi_shutdown(hp, pv);
+
+ notifier_hangup_irq(hp, data);
+}
+
+static int hvterm_hvsi_tiocmget(struct hvc_struct *hp)
+{
+ struct hvterm_priv *pv = hvterm_privs[hp->vtermno];
+
+ if (!pv)
+ return -EINVAL;
+ return pv->mctrl;
+}
+
+static int hvterm_hvsi_tiocmset(struct hvc_struct *hp, unsigned int
set,
+ unsigned int clear)
+{
+ struct hvterm_priv *pv = hvterm_privs[hp->vtermno];
+
+ pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n",
+ pv->termno, set, clear);
+
+ if (set & TIOCM_DTR)
+ hvterm_hvsi_write_mctrl(pv, 1);
+ else if (clear & TIOCM_DTR)
+ hvterm_hvsi_write_mctrl(pv, 0);
+
+ return 0;
+}
+
+static const struct hv_ops hvterm_hvsi_ops = {
+ .get_chars = hvterm_hvsi_get_chars,
+ .put_chars = hvterm_hvsi_put_chars,
+ .notifier_add = hvterm_hvsi_open,
+ .notifier_del = hvterm_hvsi_close,
+ .notifier_hangup = hvterm_hvsi_hangup,
+ .tiocmget = hvterm_hvsi_tiocmget,
+ .tiocmset = hvterm_hvsi_tiocmset,
+};
+
static int __devinit hvc_vio_probe(struct vio_dev *vdev,
- const struct vio_device_id *id)
+ const struct vio_device_id *id)
{
+ const struct hv_ops *ops;
struct hvc_struct *hp;
+ struct hvterm_priv *pv;
+ hv_protocol_t proto;
+ int i, termno = -1;
/* probed with invalid parameters. */
if (!vdev || !id)
return -EPERM;
- hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops,
- MAX_VIO_PUT_CHARS);
+ if (of_device_is_compatible(vdev->dev.of_node, "hvterm1")) {
+ proto = HV_PROTOCOL_RAW;
+ ops = &hvterm_raw_ops;
+ } else if (of_device_is_compatible(vdev->dev.of_node,
"hvterm-protocol")) {
+ proto = HV_PROTOCOL_HVSI;
+ ops = &hvterm_hvsi_ops;
+ } else {
+ pr_err("hvc_vio: Unkown protocol for %s\n",
vdev->dev.of_node->full_name);
+ return -ENXIO;
+ }
+
+ pr_devel("hvc_vio_probe() device %s, using %s protocol\n",
+ vdev->dev.of_node->full_name,
+ proto == HV_PROTOCOL_RAW ? "raw" : "hvsi");
+
+ /* Is it our boot one ? */
+ if (hvterm_privs[0] == &hvterm_priv0 &&
+ vdev->unit_address == hvterm_priv0.termno) {
+ pv = hvterm_privs[0];
+ termno = 0;
+ pr_devel("->boot console, using termno 0\n");
+ }
+ /* nope, allocate a new one */
+ else {
+ for (i = 0; i < MAX_NR_HVC_CONSOLES && termno < 0; i++)
+ if (!hvterm_privs[i])
+ termno = i;
+ pr_devel("->non-boot console, using termno %d\n", termno);
+ if (termno < 0)
+ return -ENODEV;
+ pv = kzalloc(sizeof(struct hvterm_priv), GFP_KERNEL);
+ if (!pv)
+ return -ENOMEM;
+ pv->termno = vdev->unit_address;
+ pv->proto = proto;
+ hvterm_privs[termno] = pv;
+ }
+
+ hp = hvc_alloc(termno, vdev->irq, ops, MAX_VIO_PUT_CHARS);
if (IS_ERR(hp))
return PTR_ERR(hp);
dev_set_drvdata(&vdev->dev, hp);
@@ -106,8 +646,16 @@ static int __devinit hvc_vio_probe(struct vio_dev
*vdev,
static int __devexit hvc_vio_remove(struct vio_dev *vdev)
{
struct hvc_struct *hp = dev_get_drvdata(&vdev->dev);
+ int rc, termno;
- return hvc_remove(hp);
+ termno = hp->vtermno;
+ rc = hvc_remove(hp);
+ if (rc == 0) {
+ if (hvterm_privs[termno] != &hvterm_priv0)
+ kfree(hvterm_privs[termno]);
+ hvterm_privs[termno] = NULL;
+ }
+ return rc;
}
static struct vio_driver hvc_vio_driver = {
@@ -140,34 +688,145 @@ static void __exit hvc_vio_exit(void)
}
module_exit(hvc_vio_exit);
-/* the device tree order defines our numbering */
-static int hvc_find_vtys(void)
+static void udbg_hvc_putc(char c)
{
- struct device_node *vty;
- int num_found = 0;
+ int count = -1;
- for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL;
- vty = of_find_node_by_name(vty, "vty")) {
- const uint32_t *vtermno;
+ if (c == '\n')
+ udbg_hvc_putc('\r');
- /* We have statically defined space for only a certain number
- * of console adapters.
- */
- if (num_found >= MAX_NR_HVC_CONSOLES) {
- of_node_put(vty);
+ do {
+ switch(hvterm_priv0.proto) {
+ case HV_PROTOCOL_RAW:
+ count = hvterm_raw_put_chars(0, &c, 1);
+ break;
+ case HV_PROTOCOL_HVSI:
+ count = hvterm_hvsi_put_chars(0, &c, 1);
break;
}
+ } while(count == 0);
+}
+
+static int udbg_hvc_getc_poll(void)
+{
+ int rc = 0;
+ char c;
- vtermno = of_get_property(vty, "reg", NULL);
- if (!vtermno)
- continue;
+ switch(hvterm_priv0.proto) {
+ case HV_PROTOCOL_RAW:
+ rc = hvterm_raw_get_chars(0, &c, 1);
+ break;
+ case HV_PROTOCOL_HVSI:
+ rc = hvterm_hvsi_get_chars(0, &c, 1);
+ break;
+ }
+ if (!rc)
+ return -1;
+ return c;
+}
- if (of_device_is_compatible(vty, "hvterm1")) {
- hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops);
- ++num_found;
+static int udbg_hvc_getc(void)
+{
+ int ch;
+ for (;;) {
+ ch = udbg_hvc_getc_poll();
+ if (ch == -1) {
+ /* This shouldn't be needed...but... */
+ volatile unsigned long delay;
+ for (delay=0; delay < 2000000; delay++)
+ ;
+ } else {
+ return ch;
}
}
+}
+
+void __init hvc_vio_init_early(void)
+{
+ struct device_node *stdout_node;
+ const u32 *termno;
+ const char *name;
+ const struct hv_ops *ops;
+
+ /* find the boot console from /chosen/stdout */
+ if (!of_chosen)
+ return;
+ name = of_get_property(of_chosen, "linux,stdout-path", NULL);
+ if (name == NULL)
+ return;
+ stdout_node = of_find_node_by_path(name);
+ if (!stdout_node)
+ return;
+ name = of_get_property(stdout_node, "name", NULL);
+ if (!name) {
+ printk(KERN_WARNING "stdout node missing 'name' property!\n");
+ goto out;
+ }
+
+ /* Check if it's a virtual terminal */
+ if (strncmp(name, "vty", 3) != 0)
+ goto out;
+ termno = of_get_property(stdout_node, "reg", NULL);
+ if (termno == NULL)
+ goto out;
+ hvterm_priv0.termno = *termno;
+ hvterm_priv0.is_console = 1;
+ hvterm_privs[0] = &hvterm_priv0;
+
+ /* Check the protocol */
+ if (of_device_is_compatible(stdout_node, "hvterm1")) {
+ hvterm_priv0.proto = HV_PROTOCOL_RAW;
+ ops = &hvterm_raw_ops;
+ }
+ else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) {
+ hvterm_priv0.proto = HV_PROTOCOL_HVSI;
+ ops = &hvterm_hvsi_ops;
+ /* HVSI, perform the handshake now */
+ hvterm_hvsi_establish(&hvterm_priv0);
+ } else
+ goto out;
+ udbg_putc = udbg_hvc_putc;
+ udbg_getc = udbg_hvc_getc;
+ udbg_getc_poll = udbg_hvc_getc_poll;
+#ifdef HVC_OLD_HVSI
+ /* When using the old HVSI driver don't register the HVC
+ * backend for HVSI, only do udbg
+ */
+ if (hvterm_priv0.proto == HV_PROTOCOL_HVSI)
+ goto out;
+#endif
+ add_preferred_console("hvc", 0, NULL);
+ hvc_instantiate(0, 0, ops);
+out:
+ of_node_put(stdout_node);
+}
+
+/* call this from early_init() for a working debug console on
+ * vterm capable LPAR machines
+ */
+#ifdef CONFIG_PPC_EARLY_DEBUG_LPAR
+void __init udbg_init_debug_lpar(void)
+{
+ hvterm_privs[0] = &hvterm_priv0;
+ hvterm_priv0.termno = 0;
+ hvterm_priv0.proto = HV_PROTOCOL_RAW;
+ hvterm_priv0.is_console = 1;
+ udbg_putc = udbg_hvc_putc;
+ udbg_getc = udbg_hvc_getc;
+ udbg_getc_poll = udbg_hvc_getc_poll;
+}
+#endif /* CONFIG_PPC_EARLY_DEBUG_LPAR */
- return num_found;
+#ifdef CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI
+void __init udbg_init_debug_lpar_hvsi(void)
+{
+ hvterm_privs[0] = &hvterm_priv0;
+ hvterm_priv0.termno = CONFIG_PPC_EARLY_DEBUG_HVSI_VTERMNO;
+ hvterm_priv0.proto = HV_PROTOCOL_HVSI;
+ hvterm_priv0.is_console = 1;
+ udbg_putc = udbg_hvc_putc;
+ udbg_getc = udbg_hvc_getc;
+ udbg_getc_poll = udbg_hvc_getc_poll;
+ hvterm_hvsi_establish(&hvterm_priv0);
}
-console_initcall(hvc_find_vtys);
+#endif /* CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI */
^ permalink raw reply related
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