mirror of
https://github.com/vincentmli/bpfire.git
synced 2026-04-25 02:12:58 +02:00
Reenabled linux-xen, added patches for Xen Kernel Version 2.6.27.31,
adjusted Xen Kernel Config.
This commit is contained in:
35
src/patches/suse-2.6.27.31/arch-symbols
Executable file
35
src/patches/suse-2.6.27.31/arch-symbols
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Generate architecture specific patch selection symbols
|
||||
|
||||
if [ "$1" = "--list" ]; then
|
||||
# List all known architectures
|
||||
echo i386 mips{,64} sparc{,64} ppc{,64} s390{,x} ia64 x86_64 alpha parisc
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "$SYMBOLS" ]; then
|
||||
if [ -n "$1" ]; then
|
||||
ARCH="$1"
|
||||
elif [ -n "$PATCH_ARCH" ]; then
|
||||
ARCH="$PATCH_ARCH"
|
||||
else
|
||||
ARCH="`arch`"
|
||||
fi
|
||||
SYMBOLS="$ARCH"
|
||||
case "$ARCH" in
|
||||
(i?86) SYMBOLS="$SYMBOLS IA32" ;;
|
||||
(mips*) SYMBOLS="$SYMBOLS MIPS" ;;
|
||||
(sparc*) SYMBOLS="$SYMBOLS SPARC" ;;
|
||||
(ppc*) SYMBOLS="$SYMBOLS PPC" ;;
|
||||
(s390*) SYMBOLS="$SYMBOLS S390" ;;
|
||||
(ia64) ;;
|
||||
(x86_64) ;;
|
||||
(alpha) ;;
|
||||
(parisc) ;;
|
||||
(*) # not a recognized architeture!
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
echo $SYMBOLS
|
||||
287
src/patches/suse-2.6.27.31/guards
Executable file
287
src/patches/suse-2.6.27.31/guards
Executable file
@@ -0,0 +1,287 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
#
|
||||
# Guards:
|
||||
#
|
||||
# +xxx include if xxx is defined
|
||||
# -xxx exclude if xxx is defined
|
||||
# +!xxx include if xxx is not defined
|
||||
# -!xxx exclude if xxx is not defined
|
||||
#
|
||||
|
||||
use FileHandle;
|
||||
use Getopt::Long;
|
||||
use strict;
|
||||
|
||||
# Prototypes
|
||||
sub files_in($$);
|
||||
sub parse($$);
|
||||
sub help();
|
||||
|
||||
#sub strip_ext($) {
|
||||
# local ($_) = @_;
|
||||
# s/\.(diff?|patch)$//;
|
||||
#}
|
||||
|
||||
#sub try_ext($) {
|
||||
# my ($path) = @_;
|
||||
# for my $p in (($path, "$path.diff", "$path.dif", "$path.patch")) {
|
||||
# return $p
|
||||
# if (-f $p);
|
||||
# }
|
||||
# return undef;
|
||||
#}
|
||||
|
||||
sub slashme($) {
|
||||
my ($dir) = @_;
|
||||
$dir =~ s#([^/])$#$&/#; # append a slash if necessary
|
||||
if ($dir eq './') {
|
||||
return '';
|
||||
} else {
|
||||
return $dir;
|
||||
}
|
||||
}
|
||||
|
||||
# Generate a list of files in a directory
|
||||
#
|
||||
sub files_in($$) {
|
||||
my ($dir, $path) = @_;
|
||||
my $dh = new FileHandle;
|
||||
my (@files, $file);
|
||||
|
||||
|
||||
opendir $dh, length("$dir$path") ? "$dir$path" : '.'
|
||||
or die "$dir$path: $!\n";
|
||||
while ($file = readdir($dh)) {
|
||||
next if $file =~ /^(\.|\.\.|\.#.*|CVS|.*~)$/;
|
||||
if (-d "$dir$path$file") {
|
||||
@files = (@files, files_in($dir, "$path$file/"));
|
||||
} else {
|
||||
#print "[$path$file]\n";
|
||||
push @files, "$path$file";
|
||||
}
|
||||
}
|
||||
closedir $dh;
|
||||
return @files;
|
||||
}
|
||||
|
||||
# Parse a configuration file
|
||||
# Callback called with ($patch, @guards) arguments
|
||||
#
|
||||
sub parse($$) {
|
||||
my ($fh, $callback) = @_;
|
||||
|
||||
my $line = "";
|
||||
|
||||
while (<$fh>) {
|
||||
chomp;
|
||||
s/(^|\s+)#.*//;
|
||||
if (s/\\$/ /) {
|
||||
$line .= $_;
|
||||
next;
|
||||
}
|
||||
$line .= $_;
|
||||
my @guards = ();
|
||||
foreach my $token (split /[\s\t\n]+/, $line) {
|
||||
next if $token eq "";
|
||||
if ($token =~ /^[-+]/) {
|
||||
push @guards, $token;
|
||||
} else {
|
||||
#print "[" . join(",", @guards) . "] $token\n";
|
||||
&$callback($token, @guards);
|
||||
}
|
||||
}
|
||||
$line = "";
|
||||
}
|
||||
}
|
||||
|
||||
# Command line options
|
||||
#
|
||||
my ($dir, $config, $default, $check, $list, $invert_match, $with_guards) =
|
||||
( '', '-', 1, 0, 0, 0, 0);
|
||||
my @path;
|
||||
|
||||
# Help text
|
||||
#
|
||||
sub help() {
|
||||
print "$0 - select from a list of files guarded by conditions\n";
|
||||
print "SYNOPSIS: $0 [--prefix=dir] [--path=dir1:dir2:...]\n" .
|
||||
" [--default=0|1] [--check|--list] [--invert-match]\n" .
|
||||
" [--with-guards] [--config=file] symbol ...\n\n" .
|
||||
" (Default values: --path='" . join(':', @path) . "', " .
|
||||
"--default=$default)\n";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
# Parse command line options
|
||||
#
|
||||
Getopt::Long::Configure ("bundling");
|
||||
eval {
|
||||
unless (GetOptions (
|
||||
'd|prefix=s' => \$dir,
|
||||
'c|config=s' => \$config,
|
||||
'C|check' => \$check,
|
||||
'l|list' => \$list,
|
||||
'w|with-guards' => \$with_guards,
|
||||
'p|path=s' => \@path,
|
||||
'D|default=i' => \$default,
|
||||
'v|invert-match' => \$invert_match,
|
||||
'h|help' => sub { help(); exit 0; })) {
|
||||
help();
|
||||
exit 1;
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
print "$@";
|
||||
help();
|
||||
exit 1;
|
||||
}
|
||||
|
||||
@path = ('.')
|
||||
unless (@path);
|
||||
@path = split(/:/, join(':', @path));
|
||||
|
||||
my $fh = ($config eq '-') ? \*STDIN : new FileHandle($config)
|
||||
or die "$config: $!\n";
|
||||
|
||||
$dir = slashme($dir);
|
||||
|
||||
if ($check) {
|
||||
# Check for duplicate files, or for files that are not referenced by
|
||||
# the specification.
|
||||
|
||||
my $problems = 0;
|
||||
my @files;
|
||||
|
||||
foreach (@path) {
|
||||
@files = (@files, files_in($dir, slashme($_)));
|
||||
}
|
||||
my %files = map { $_ => 0 } @files;
|
||||
|
||||
parse($fh, sub {
|
||||
my ($patch, @guards) = @_;
|
||||
if (exists $files{$patch}) {
|
||||
$files{$patch}++;
|
||||
} else {
|
||||
print "Not found: $dir$patch\n";
|
||||
$problems++;
|
||||
}});
|
||||
|
||||
$fh->close();
|
||||
|
||||
my ($file, $ref);
|
||||
while (($file, $ref) = each %files) {
|
||||
next if $ref == 1;
|
||||
|
||||
if ($ref == 0) {
|
||||
print "Unused: $file\n" if $ref == 0;
|
||||
$problems++;
|
||||
}
|
||||
if ($ref > 1) {
|
||||
print "Warning: multiple uses: $file\n" if $ref > 1;
|
||||
# This is not an error if the entries are mutually exclusive...
|
||||
}
|
||||
}
|
||||
exit $problems ? 1 : 0;
|
||||
|
||||
} elsif ($list) {
|
||||
parse($fh, sub {
|
||||
my ($patch, @guards) = @_;
|
||||
print join(' ', @guards), ' '
|
||||
if (@guards && $with_guards);
|
||||
print "$dir$patch\n";
|
||||
});
|
||||
} else {
|
||||
# Generate a list of patches to apply.
|
||||
|
||||
my %symbols = map { $_ => 1 } @ARGV;
|
||||
|
||||
parse($fh, sub {
|
||||
my ($patch, @guards) = @_;
|
||||
|
||||
my $selected;
|
||||
if (@guards) {
|
||||
# If the first guard is -xxx, the patch is included by default;
|
||||
# if it is +xxx, the patch is excluded by default.
|
||||
$selected = ($guards[0] =~ /^-/);
|
||||
|
||||
foreach (@guards) {
|
||||
/^([-+])(!?)(.*)?/
|
||||
or die "Bad guard '$_'\n";
|
||||
|
||||
# Check if the guard matches
|
||||
if (($2 eq '!' && !exists $symbols{$3}) ||
|
||||
($2 eq '' && ( $3 eq '' || exists $symbols{$3}))) {
|
||||
# Include or exclude
|
||||
$selected = ($1 eq '+');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# If there are no guards, use the specified default result.
|
||||
$selected = $default;
|
||||
}
|
||||
|
||||
print "$dir$patch\n"
|
||||
if $selected ^ $invert_match;
|
||||
});
|
||||
|
||||
$fh->close();
|
||||
|
||||
exit 0;
|
||||
}
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
guards - select from a list of files guarded by conditions
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
F<guards> [--prefix=F<dir>] [--path=F<dir1:dir2:...>] [--default=<0|1>]
|
||||
[--check|--list] [--invert-match] [--with-guards] [--config=<file>]
|
||||
I<symbol> ...
|
||||
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The script reads a configuration file that may contain so-called guards, file
|
||||
names, and comments, and writes those file names that satisfy all guards to
|
||||
standard output. The script takes a list of symbols as its arguments. Each line
|
||||
in the configuration file is processed separately. Lines may start with a
|
||||
number of guards. The following guards are defined:
|
||||
|
||||
=over
|
||||
|
||||
+I<xxx> Include the file(s) on this line if the symbol I<xxx> is defined.
|
||||
|
||||
-I<xxx> Exclude the file(s) on this line if the symbol I<xxx> is defined.
|
||||
|
||||
+!I<xxx> Include the file(s) on this line if the symbol I<xxx> is not defined.
|
||||
|
||||
-!I<xxx> Exclude the file(s) on this line if the symbol I<xxx> is not defined.
|
||||
|
||||
- Exclude this file. Used to avoid spurious I<--check> messages.
|
||||
|
||||
=back
|
||||
|
||||
The guards are processed left to right. The last guard that matches determines
|
||||
if the file is included. If no guard is specified, the I<--default>
|
||||
setting determines if the file is included.
|
||||
|
||||
If no configuration file is specified, the script reads from standard input.
|
||||
|
||||
The I<--check> option is used to compare the specification file against the
|
||||
file system. If files are referenced in the specification that do not exist, or
|
||||
if files are not enlisted in the specification file warnings are printed. The
|
||||
I<--path> option can be used to specify which directory or directories to scan.
|
||||
Multiple directories are eparated by a colon (C<:>) character. The
|
||||
I<--prefix> option specifies the location of the files.
|
||||
|
||||
Use I<--list> to list all files independend of any rules. Use I<--invert-match>
|
||||
to list only the excluded patches. Use I<--with-guards> to also include all
|
||||
inclusion and exclusion rules.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Andreas Gruenbacher <agruen@suse.de>, SUSE Labs
|
||||
@@ -0,0 +1,109 @@
|
||||
From: Jeff Mahoney <jeffm@suse.com>
|
||||
Subject: [PATCH] security: add ->path_permission
|
||||
|
||||
This patch adds a security_ops->path_permission hook that is identical to
|
||||
security_ops->inode_permission except that it is passed a struct path
|
||||
instead of a struct inode.
|
||||
|
||||
LSMs which don't implement it will have their ->inode_permission call
|
||||
used instead.
|
||||
|
||||
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
|
||||
---
|
||||
|
||||
include/linux/security.h | 21 +++++++++++++++++++++
|
||||
security/capability.c | 6 ++++++
|
||||
security/security.c | 9 +++++++++
|
||||
3 files changed, 36 insertions(+)
|
||||
|
||||
--- a/include/linux/security.h
|
||||
+++ b/include/linux/security.h
|
||||
@@ -592,6 +592,20 @@ static inline void security_free_mnt_opt
|
||||
* file_permission, and recheck access if anything has changed
|
||||
* since inode_permission.
|
||||
*
|
||||
+ * Security hook for path
|
||||
+ *
|
||||
+ * @path_permission:
|
||||
+ * Check permission before accessing a path. This hook is called by the
|
||||
+ * existing Linux permission function, so a security module can use it to
|
||||
+ * provide additional checking for existing Linux permission checks.
|
||||
+ * Notice that this hook is called when a file is opened (as well as many
|
||||
+ * other operations), whereas the file_security_ops permission hook is
|
||||
+ * called when the actual read/write operations are performed. This
|
||||
+ * hook is optional and if absent, inode_permission will be substituted.
|
||||
+ * @path contains the path structure to check.
|
||||
+ * @mask contains the permission mask.
|
||||
+ * Return 0 if permission is granted.
|
||||
+
|
||||
* Security hooks for task operations.
|
||||
*
|
||||
* @task_create:
|
||||
@@ -1434,6 +1448,7 @@ struct security_operations {
|
||||
struct fown_struct *fown, int sig);
|
||||
int (*file_receive) (struct file *file);
|
||||
int (*dentry_open) (struct file *file);
|
||||
+ int (*path_permission) (struct path *path, int mask);
|
||||
|
||||
int (*task_create) (unsigned long clone_flags);
|
||||
int (*task_alloc_security) (struct task_struct *p);
|
||||
@@ -1708,6 +1723,7 @@ int security_file_send_sigiotask(struct
|
||||
struct fown_struct *fown, int sig);
|
||||
int security_file_receive(struct file *file);
|
||||
int security_dentry_open(struct file *file);
|
||||
+int security_path_permission(struct path *path, int mask);
|
||||
int security_task_create(unsigned long clone_flags);
|
||||
int security_task_alloc(struct task_struct *p);
|
||||
void security_task_free(struct task_struct *p);
|
||||
@@ -2242,6 +2258,11 @@ static inline int security_dentry_open(s
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
+
|
||||
+static inline int security_path_permission(struct path *path, int mask)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
static inline int security_task_create(unsigned long clone_flags)
|
||||
{
|
||||
--- a/security/capability.c
|
||||
+++ b/security/capability.c
|
||||
@@ -343,6 +343,11 @@ static int cap_dentry_open(struct file *
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int cap_path_permission(struct path *path, int mask)
|
||||
+{
|
||||
+ return security_inode_permission(path->dentry->d_inode, mask);
|
||||
+}
|
||||
+
|
||||
static int cap_task_create(unsigned long clone_flags)
|
||||
{
|
||||
return 0;
|
||||
@@ -897,6 +902,7 @@ void security_fixup_ops(struct security_
|
||||
set_to_cap_if_null(ops, file_send_sigiotask);
|
||||
set_to_cap_if_null(ops, file_receive);
|
||||
set_to_cap_if_null(ops, dentry_open);
|
||||
+ set_to_cap_if_null(ops, path_permission);
|
||||
set_to_cap_if_null(ops, task_create);
|
||||
set_to_cap_if_null(ops, task_alloc_security);
|
||||
set_to_cap_if_null(ops, task_free_security);
|
||||
--- a/security/security.c
|
||||
+++ b/security/security.c
|
||||
@@ -612,6 +612,15 @@ int security_dentry_open(struct file *fi
|
||||
return security_ops->dentry_open(file);
|
||||
}
|
||||
|
||||
+int security_path_permission(struct path *path, int mask)
|
||||
+{
|
||||
+ struct inode *inode = path->dentry->d_inode;
|
||||
+ if (unlikely(IS_PRIVATE(inode)))
|
||||
+ return 0;
|
||||
+
|
||||
+ return security_ops->path_permission(path, mask);
|
||||
+}
|
||||
+
|
||||
int security_task_create(unsigned long clone_flags)
|
||||
{
|
||||
return security_ops->task_create(clone_flags);
|
||||
@@ -0,0 +1,78 @@
|
||||
From: Jeff Mahoney <jeffm@suse.com>
|
||||
Subject: [PATCH] apparmor: convert apparmor_inode_permission to path
|
||||
|
||||
patches.apparmor/add-security_path_permission added the ->path_permission
|
||||
call. This patch converts apparmor_inode_permission to
|
||||
apparmor_path_permission. The former is now a pass-all, which is how
|
||||
it behaved in 2.6.26 if a NULL nameidata was passed.
|
||||
|
||||
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
|
||||
---
|
||||
security/apparmor/lsm.c | 41 +++++++++++++++++++++++++++--------------
|
||||
1 file changed, 27 insertions(+), 14 deletions(-)
|
||||
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -448,21 +448,9 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
-static int apparmor_inode_permission(struct inode *inode, int mask,
|
||||
- struct nameidata *nd)
|
||||
+static int apparmor_inode_permission(struct inode *inode, int mask)
|
||||
{
|
||||
- int check = 0;
|
||||
-
|
||||
- if (!nd || nd->flags & (LOOKUP_PARENT | LOOKUP_CONTINUE))
|
||||
- return 0;
|
||||
- mask = aa_mask_permissions(mask);
|
||||
- if (S_ISDIR(inode->i_mode)) {
|
||||
- check |= AA_CHECK_DIR;
|
||||
- /* allow traverse accesses to directories */
|
||||
- mask &= ~MAY_EXEC;
|
||||
- }
|
||||
- return aa_permission("inode_permission", inode, nd->dentry, nd->mnt,
|
||||
- mask, check);
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static int apparmor_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
|
||||
@@ -656,6 +644,29 @@ static int apparmor_file_mprotect(struct
|
||||
!(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
|
||||
}
|
||||
|
||||
+static int apparmor_path_permission(struct path *path, int mask)
|
||||
+{
|
||||
+ struct inode *inode;
|
||||
+ int check = 0;
|
||||
+
|
||||
+ if (!path)
|
||||
+ return 0;
|
||||
+
|
||||
+ inode = path->dentry->d_inode;
|
||||
+
|
||||
+ mask = aa_mask_permissions(mask);
|
||||
+ if (S_ISDIR(inode->i_mode)) {
|
||||
+ check |= AA_CHECK_DIR;
|
||||
+ /* allow traverse accesses to directories */
|
||||
+ mask &= ~MAY_EXEC;
|
||||
+ if (!mask)
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ return aa_permission("inode_permission", inode, path->dentry,
|
||||
+ path->mnt, mask, check);
|
||||
+}
|
||||
+
|
||||
static int apparmor_task_alloc_security(struct task_struct *task)
|
||||
{
|
||||
return aa_clone(task);
|
||||
@@ -800,6 +811,8 @@ struct security_operations apparmor_ops
|
||||
.file_mprotect = apparmor_file_mprotect,
|
||||
.file_lock = apparmor_file_lock,
|
||||
|
||||
+ .path_permission = apparmor_path_permission,
|
||||
+
|
||||
.task_alloc_security = apparmor_task_alloc_security,
|
||||
.task_free_security = apparmor_task_free_security,
|
||||
.task_post_setuid = cap_task_post_setuid,
|
||||
@@ -0,0 +1,26 @@
|
||||
From: Jeff Mahoney <jeffm@suse.com>
|
||||
Subject: [PATCH] LSM: Export security_inode_permission for aufs
|
||||
Patch-mainline: Never
|
||||
References: 356902
|
||||
|
||||
In order for aufs to work with AppArmor, it needs to be able to call
|
||||
security_inode_permission itself.
|
||||
|
||||
This patch is a _workaround_ since the author will need to find a
|
||||
mainline-compatible solution moving forward.
|
||||
|
||||
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
|
||||
---
|
||||
security/security.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
--- a/security/security.c
|
||||
+++ b/security/security.c
|
||||
@@ -412,6 +412,7 @@ int security_inode_mknod(struct inode *d
|
||||
return 0;
|
||||
return security_ops->inode_mknod(dir, dentry, mnt, mode, dev);
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(security_inode_permission);
|
||||
|
||||
int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct vfsmount *old_mnt, struct inode *new_dir,
|
||||
@@ -0,0 +1,118 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: ACPI: video: Ignore devices that aren't present in hardware
|
||||
Patch-Mainline: queued for .28 in Len's/ak's ACPI tree
|
||||
|
||||
commit ad9ed8385ed6ec5be8da7094db911c824258ceec
|
||||
|
||||
This is a reimplemention of commit
|
||||
0119509c4fbc9adcef1472817fda295334612976
|
||||
from Matthew Garrett <mjg59@srcf.ucam.org>
|
||||
|
||||
This patch got removed because of a regression: ThinkPads with a
|
||||
Intel graphics card and an Integrated Graphics Device BIOS implementation
|
||||
stopped working.
|
||||
In fact, they only worked because the ACPI device of the discrete, the
|
||||
wrong one, got used (via int10). So ACPI functions were poking on the wrong
|
||||
hardware used which is a sever bug.
|
||||
The next patch provides support for above ThinkPads to be able to
|
||||
switch brightness via the legacy thinkpad_acpi driver and automatically
|
||||
detect when to use it.
|
||||
|
||||
Original commit message from Matthew Garrett:
|
||||
Vendors often ship machines with a choice of integrated or discrete
|
||||
graphics, and use the same DSDT for both. As a result, the ACPI video
|
||||
module will locate devices that may not exist on this specific platform.
|
||||
Attempt to determine whether the device exists or not, and abort the
|
||||
device creation if it doesn't.
|
||||
|
||||
http://bugzilla.kernel.org/show_bug.cgi?id=9614
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Signed-off-by: Andi Kleen <ak@linux.intel.com>
|
||||
|
||||
---
|
||||
drivers/acpi/glue.c | 40 ++++++++++++++++++++++++++++++++++++++++
|
||||
drivers/acpi/video.c | 7 ++++++-
|
||||
include/acpi/acpi_bus.h | 2 ++
|
||||
3 files changed, 48 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/acpi/glue.c
|
||||
+++ b/drivers/acpi/glue.c
|
||||
@@ -140,6 +140,46 @@ struct device *acpi_get_physical_device(
|
||||
|
||||
EXPORT_SYMBOL(acpi_get_physical_device);
|
||||
|
||||
+/* ToDo: When a PCI bridge is found, return the PCI device behind the bridge
|
||||
+ * This should work in general, but did not on a Lenovo T61 for the
|
||||
+ * graphics card. But this must be fixed when the PCI device is
|
||||
+ * bound and the kernel device struct is attached to the acpi device
|
||||
+ * Note: A success call will increase reference count by one
|
||||
+ * Do call put_device(dev) on the returned device then
|
||||
+ */
|
||||
+struct device *acpi_get_physical_pci_device(acpi_handle handle)
|
||||
+{
|
||||
+ struct device *dev;
|
||||
+ long long device_id;
|
||||
+ acpi_status status;
|
||||
+
|
||||
+ status =
|
||||
+ acpi_evaluate_integer(handle, "_ADR", NULL, &device_id);
|
||||
+
|
||||
+ if (ACPI_FAILURE(status))
|
||||
+ return NULL;
|
||||
+
|
||||
+ /* We need to attempt to determine whether the _ADR refers to a
|
||||
+ PCI device or not. There's no terribly good way to do this,
|
||||
+ so the best we can hope for is to assume that there'll never
|
||||
+ be a device in the host bridge */
|
||||
+ if (device_id >= 0x10000) {
|
||||
+ /* It looks like a PCI device. Does it exist? */
|
||||
+ dev = acpi_get_physical_device(handle);
|
||||
+ } else {
|
||||
+ /* It doesn't look like a PCI device. Does its parent
|
||||
+ exist? */
|
||||
+ acpi_handle phandle;
|
||||
+ if (acpi_get_parent(handle, &phandle))
|
||||
+ return NULL;
|
||||
+ dev = acpi_get_physical_device(phandle);
|
||||
+ }
|
||||
+ if (!dev)
|
||||
+ return NULL;
|
||||
+ return dev;
|
||||
+}
|
||||
+EXPORT_SYMBOL(acpi_get_physical_pci_device);
|
||||
+
|
||||
static int acpi_bind_one(struct device *dev, acpi_handle handle)
|
||||
{
|
||||
struct acpi_device *acpi_dev;
|
||||
--- a/drivers/acpi/video.c
|
||||
+++ b/drivers/acpi/video.c
|
||||
@@ -862,11 +862,16 @@ static void acpi_video_bus_find_cap(stru
|
||||
static int acpi_video_bus_check(struct acpi_video_bus *video)
|
||||
{
|
||||
acpi_status status = -ENOENT;
|
||||
-
|
||||
+ struct device *dev;
|
||||
|
||||
if (!video)
|
||||
return -EINVAL;
|
||||
|
||||
+ dev = acpi_get_physical_pci_device(video->device->handle);
|
||||
+ if (!dev)
|
||||
+ return -ENODEV;
|
||||
+ put_device(dev);
|
||||
+
|
||||
/* Since there is no HID, CID and so on for VGA driver, we have
|
||||
* to check well known required nodes.
|
||||
*/
|
||||
--- a/include/acpi/acpi_bus.h
|
||||
+++ b/include/acpi/acpi_bus.h
|
||||
@@ -373,6 +373,8 @@ struct acpi_bus_type {
|
||||
int register_acpi_bus_type(struct acpi_bus_type *);
|
||||
int unregister_acpi_bus_type(struct acpi_bus_type *);
|
||||
struct device *acpi_get_physical_device(acpi_handle);
|
||||
+struct device *acpi_get_physical_pci_device(acpi_handle);
|
||||
+
|
||||
/* helper */
|
||||
acpi_handle acpi_get_child(acpi_handle, acpi_integer);
|
||||
acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int);
|
||||
@@ -0,0 +1,489 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: [PATCH] Check for ACPI backlight support otherwise use vendor ACPI drivers
|
||||
Patch-Mainline: queued for .28 in Len's/ak's ACPI tree
|
||||
|
||||
commit f43d728731c691772ddc29e50d25c68a859935b5
|
||||
|
||||
If an ACPI graphics device supports backlight brightness functions (cmp. with
|
||||
latest ACPI spec Appendix B), let the ACPI video driver control backlight and
|
||||
switch backlight control off in vendor specific ACPI drivers (asus_acpi,
|
||||
thinkpad_acpi, eeepc, fujitsu_laptop, msi_laptop, sony_laptop, acer-wmi).
|
||||
|
||||
Currently it is possible to load above drivers and let both poke on the
|
||||
brightness HW registers, the video and vendor specific ACPI drivers -> bad.
|
||||
|
||||
This patch provides the basic support to check for BIOS capabilities before
|
||||
driver loading time. Driver specific modifications are in separate follow up
|
||||
patches.
|
||||
|
||||
acpi_backlight=vendor/video
|
||||
boot params forces video.ko or vendor specific drivers to keep its
|
||||
fingers off backlight control even it would find needed functions.
|
||||
The corresponding vendor specific driver be used then.
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Signed-off-by: Andi Kleen <ak@linux.intel.com>
|
||||
---
|
||||
Documentation/kernel-parameters.txt | 13 +
|
||||
drivers/acpi/Makefile | 5
|
||||
drivers/acpi/scan.c | 32 ----
|
||||
drivers/acpi/video.c | 28 ++-
|
||||
drivers/acpi/video_detect.c | 268 ++++++++++++++++++++++++++++++++++++
|
||||
include/linux/acpi.h | 44 +++++
|
||||
6 files changed, 347 insertions(+), 43 deletions(-)
|
||||
create mode 100644 drivers/acpi/video_detect.c
|
||||
|
||||
--- a/Documentation/kernel-parameters.txt
|
||||
+++ b/Documentation/kernel-parameters.txt
|
||||
@@ -200,6 +200,19 @@ and is between 256 and 4096 characters.
|
||||
that require a timer override, but don't have
|
||||
HPET
|
||||
|
||||
+ acpi_backlight= [HW,ACPI]
|
||||
+ acpi_backlight=vendor
|
||||
+ acpi_backlight=video
|
||||
+ If set to vendor, it enforces the use of a
|
||||
+ vendor specific ACPI driver for backlight switching
|
||||
+ (e.g. thinkpad_acpi, sony_acpi, etc.) instead
|
||||
+ of the video.ko driver.
|
||||
+
|
||||
+ acpi_display_output= [HW,ACPI]
|
||||
+ acpi_display_output=vendor
|
||||
+ acpi_display_output=video
|
||||
+ See above.
|
||||
+
|
||||
acpi.debug_layer= [HW,ACPI]
|
||||
Format: <int>
|
||||
Each bit of the <int> indicates an ACPI debug layer,
|
||||
--- a/drivers/acpi/Makefile
|
||||
+++ b/drivers/acpi/Makefile
|
||||
@@ -46,7 +46,12 @@ obj-$(CONFIG_ACPI_BUTTON) += button.o
|
||||
obj-$(CONFIG_ACPI_FAN) += fan.o
|
||||
obj-$(CONFIG_ACPI_DOCK) += dock.o
|
||||
obj-$(CONFIG_ACPI_BAY) += bay.o
|
||||
+
|
||||
obj-$(CONFIG_ACPI_VIDEO) += video.o
|
||||
+ifdef CONFIG_ACPI_VIDEO
|
||||
+obj-y += video_detect.o
|
||||
+endif
|
||||
+
|
||||
obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o
|
||||
obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o
|
||||
obj-$(CONFIG_ACPI_POWER) += power.o
|
||||
--- a/drivers/acpi/scan.c
|
||||
+++ b/drivers/acpi/scan.c
|
||||
@@ -908,36 +908,6 @@ static void acpi_device_get_busid(struct
|
||||
}
|
||||
}
|
||||
|
||||
-static int
|
||||
-acpi_video_bus_match(struct acpi_device *device)
|
||||
-{
|
||||
- acpi_handle h_dummy;
|
||||
-
|
||||
- if (!device)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- /* Since there is no HID, CID for ACPI Video drivers, we have
|
||||
- * to check well known required nodes for each feature we support.
|
||||
- */
|
||||
-
|
||||
- /* Does this device able to support video switching ? */
|
||||
- if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) &&
|
||||
- ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy)))
|
||||
- return 0;
|
||||
-
|
||||
- /* Does this device able to retrieve a video ROM ? */
|
||||
- if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy)))
|
||||
- return 0;
|
||||
-
|
||||
- /* Does this device able to configure which video head to be POSTed ? */
|
||||
- if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy)) &&
|
||||
- ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy)) &&
|
||||
- ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy)))
|
||||
- return 0;
|
||||
-
|
||||
- return -ENODEV;
|
||||
-}
|
||||
-
|
||||
/*
|
||||
* acpi_bay_match - see if a device is an ejectable driver bay
|
||||
*
|
||||
@@ -1020,7 +990,7 @@ static void acpi_device_set_id(struct ac
|
||||
will get autoloaded and the device might still match
|
||||
against another driver.
|
||||
*/
|
||||
- if (ACPI_SUCCESS(acpi_video_bus_match(device)))
|
||||
+ if (acpi_is_video_device(device))
|
||||
cid_add = ACPI_VIDEO_HID;
|
||||
else if (ACPI_SUCCESS(acpi_bay_match(device)))
|
||||
cid_add = ACPI_BAY_HID;
|
||||
--- a/drivers/acpi/video.c
|
||||
+++ b/drivers/acpi/video.c
|
||||
@@ -759,7 +759,8 @@ static void acpi_video_device_find_cap(s
|
||||
device->cap._DSS = 1;
|
||||
}
|
||||
|
||||
- max_level = acpi_video_init_brightness(device);
|
||||
+ if (acpi_video_backlight_support())
|
||||
+ max_level = acpi_video_init_brightness(device);
|
||||
|
||||
if (device->cap._BCL && device->cap._BCM && max_level > 0) {
|
||||
int result;
|
||||
@@ -805,18 +806,21 @@ static void acpi_video_device_find_cap(s
|
||||
printk(KERN_ERR PREFIX "Create sysfs link\n");
|
||||
|
||||
}
|
||||
- if (device->cap._DCS && device->cap._DSS){
|
||||
- static int count = 0;
|
||||
- char *name;
|
||||
- name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
|
||||
- if (!name)
|
||||
- return;
|
||||
- sprintf(name, "acpi_video%d", count++);
|
||||
- device->output_dev = video_output_register(name,
|
||||
- NULL, device, &acpi_output_properties);
|
||||
- kfree(name);
|
||||
+
|
||||
+ if (acpi_video_display_switch_support()) {
|
||||
+
|
||||
+ if (device->cap._DCS && device->cap._DSS) {
|
||||
+ static int count;
|
||||
+ char *name;
|
||||
+ name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
|
||||
+ if (!name)
|
||||
+ return;
|
||||
+ sprintf(name, "acpi_video%d", count++);
|
||||
+ device->output_dev = video_output_register(name,
|
||||
+ NULL, device, &acpi_output_properties);
|
||||
+ kfree(name);
|
||||
+ }
|
||||
}
|
||||
- return;
|
||||
}
|
||||
|
||||
/*
|
||||
--- /dev/null
|
||||
+++ b/drivers/acpi/video_detect.c
|
||||
@@ -0,0 +1,268 @@
|
||||
+/*
|
||||
+ * Copyright (C) 2008 SuSE Linux Products GmbH
|
||||
+ * Thomas Renninger <trenn@suse.de>
|
||||
+ *
|
||||
+ * May be copied or modified under the terms of the GNU General Public License
|
||||
+ *
|
||||
+ * video_detect.c:
|
||||
+ * Provides acpi_is_video_device() for early scanning of ACPI devices in scan.c
|
||||
+ * There a Linux specific (Spec does not provide a HID for video devices) is
|
||||
+ * assinged
|
||||
+ *
|
||||
+ * After PCI devices are glued with ACPI devices
|
||||
+ * acpi_get_physical_pci_device() can be called to identify ACPI graphics
|
||||
+ * devices for which a real graphics card is plugged in
|
||||
+ *
|
||||
+ * Now acpi_video_get_capabilities() can be called to check which
|
||||
+ * capabilities the graphics cards plugged in support. The check for general
|
||||
+ * video capabilities will be triggered by the first caller of
|
||||
+ * acpi_video_get_capabilities(NULL); which will happen when the first
|
||||
+ * backlight (or display output) switching supporting driver calls:
|
||||
+ * acpi_video_backlight_support();
|
||||
+ *
|
||||
+ * Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B)
|
||||
+ * are available, video.ko should be used to handle the device.
|
||||
+ *
|
||||
+ * Otherwise vendor specific drivers like thinkpad_acpi, asus_acpi,
|
||||
+ * sony_acpi,... can take care about backlight brightness and display output
|
||||
+ * switching.
|
||||
+ *
|
||||
+ * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m)
|
||||
+ * this file will not be compiled, acpi_video_get_capabilities() and
|
||||
+ * acpi_video_backlight_support() will always return 0 and vendor specific
|
||||
+ * drivers always can handle backlight.
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#include <linux/acpi.h>
|
||||
+#include <linux/dmi.h>
|
||||
+
|
||||
+ACPI_MODULE_NAME("video");
|
||||
+#define ACPI_VIDEO_COMPONENT 0x08000000
|
||||
+#define _COMPONENT ACPI_VIDEO_COMPONENT
|
||||
+
|
||||
+static long acpi_video_support;
|
||||
+static bool acpi_video_caps_checked;
|
||||
+
|
||||
+static acpi_status
|
||||
+acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context,
|
||||
+ void **retyurn_value)
|
||||
+{
|
||||
+ long *cap = context;
|
||||
+ acpi_handle h_dummy;
|
||||
+
|
||||
+ if (ACPI_SUCCESS(acpi_get_handle(handle, "_BCM", &h_dummy)) &&
|
||||
+ ACPI_SUCCESS(acpi_get_handle(handle, "_BCL", &h_dummy))) {
|
||||
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found generic backlight "
|
||||
+ "support\n"));
|
||||
+ *cap |= ACPI_VIDEO_BACKLIGHT;
|
||||
+ /* We have backlight support, no need to scan further */
|
||||
+ return AE_CTRL_TERMINATE;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* Returns true if the device is a video device which can be handled by
|
||||
+ * video.ko.
|
||||
+ * The device will get a Linux specific CID added in scan.c to
|
||||
+ * identify the device as an ACPI graphics device
|
||||
+ * Be aware that the graphics device may not be physically present
|
||||
+ * Use acpi_video_get_capabilities() to detect general ACPI video
|
||||
+ * capabilities of present cards
|
||||
+ */
|
||||
+long acpi_is_video_device(struct acpi_device *device)
|
||||
+{
|
||||
+ acpi_handle h_dummy;
|
||||
+ long video_caps = 0;
|
||||
+
|
||||
+ if (!device)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* Does this device able to support video switching ? */
|
||||
+ if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) &&
|
||||
+ ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy)))
|
||||
+ video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING;
|
||||
+
|
||||
+ /* Does this device able to retrieve a video ROM ? */
|
||||
+ if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy)))
|
||||
+ video_caps |= ACPI_VIDEO_ROM_AVAILABLE;
|
||||
+
|
||||
+ /* Does this device able to configure which video head to be POSTed ? */
|
||||
+ if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy)) &&
|
||||
+ ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy)) &&
|
||||
+ ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy)))
|
||||
+ video_caps |= ACPI_VIDEO_DEVICE_POSTING;
|
||||
+
|
||||
+ /* Only check for backlight functionality if one of the above hit. */
|
||||
+ if (video_caps)
|
||||
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, device->handle,
|
||||
+ ACPI_UINT32_MAX, acpi_backlight_cap_match,
|
||||
+ &video_caps, NULL);
|
||||
+
|
||||
+ return video_caps;
|
||||
+}
|
||||
+EXPORT_SYMBOL(acpi_is_video_device);
|
||||
+
|
||||
+static acpi_status
|
||||
+find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
|
||||
+{
|
||||
+ long *cap = context;
|
||||
+ struct device *dev;
|
||||
+ struct acpi_device *acpi_dev;
|
||||
+
|
||||
+ const struct acpi_device_id video_ids[] = {
|
||||
+ {ACPI_VIDEO_HID, 0},
|
||||
+ {"", 0},
|
||||
+ };
|
||||
+ if (acpi_bus_get_device(handle, &acpi_dev))
|
||||
+ return AE_OK;
|
||||
+
|
||||
+ if (!acpi_match_device_ids(acpi_dev, video_ids)) {
|
||||
+ dev = acpi_get_physical_pci_device(handle);
|
||||
+ if (!dev)
|
||||
+ return AE_OK;
|
||||
+ put_device(dev);
|
||||
+ *cap |= acpi_is_video_device(acpi_dev);
|
||||
+ }
|
||||
+ return AE_OK;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Returns the video capabilities of a specific ACPI graphics device
|
||||
+ *
|
||||
+ * if NULL is passed as argument all ACPI devices are enumerated and
|
||||
+ * all graphics capabilities of physically present devices are
|
||||
+ * summerized and returned. This is cached and done only once.
|
||||
+ */
|
||||
+long acpi_video_get_capabilities(acpi_handle graphics_handle)
|
||||
+{
|
||||
+ long caps = 0;
|
||||
+ struct acpi_device *tmp_dev;
|
||||
+ acpi_status status;
|
||||
+
|
||||
+ if (acpi_video_caps_checked && graphics_handle == NULL)
|
||||
+ return acpi_video_support;
|
||||
+
|
||||
+ if (!graphics_handle) {
|
||||
+ /* Only do the global walk through all graphics devices once */
|
||||
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
+ ACPI_UINT32_MAX, find_video,
|
||||
+ &caps, NULL);
|
||||
+ /* There might be boot param flags set already... */
|
||||
+ acpi_video_support |= caps;
|
||||
+ acpi_video_caps_checked = 1;
|
||||
+ /* Add blacklists here. Be careful to use the right *DMI* bits
|
||||
+ * to still be able to override logic via boot params, e.g.:
|
||||
+ *
|
||||
+ * if (dmi_name_in_vendors("XY")) {
|
||||
+ * acpi_video_support |=
|
||||
+ * ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR;
|
||||
+ * acpi_video_support |=
|
||||
+ * ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
|
||||
+ *}
|
||||
+ */
|
||||
+ } else {
|
||||
+ status = acpi_bus_get_device(graphics_handle, &tmp_dev);
|
||||
+ if (ACPI_FAILURE(status)) {
|
||||
+ ACPI_EXCEPTION((AE_INFO, status, "Invalid device"));
|
||||
+ return 0;
|
||||
+ }
|
||||
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, graphics_handle,
|
||||
+ ACPI_UINT32_MAX, find_video,
|
||||
+ &caps, NULL);
|
||||
+ }
|
||||
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "We have 0x%lX video support %s %s\n",
|
||||
+ graphics_handle ? caps : acpi_video_support,
|
||||
+ graphics_handle ? "on device " : "in general",
|
||||
+ graphics_handle ? acpi_device_bid(tmp_dev) : ""));
|
||||
+ return caps;
|
||||
+}
|
||||
+EXPORT_SYMBOL(acpi_video_get_capabilities);
|
||||
+
|
||||
+/* Returns true if video.ko can do backlight switching */
|
||||
+int acpi_video_backlight_support(void)
|
||||
+{
|
||||
+ /*
|
||||
+ * We must check whether the ACPI graphics device is physically plugged
|
||||
+ * in. Therefore this must be called after binding PCI and ACPI devices
|
||||
+ */
|
||||
+ if (!acpi_video_caps_checked)
|
||||
+ acpi_video_get_capabilities(NULL);
|
||||
+
|
||||
+ /* First check for boot param -> highest prio */
|
||||
+ if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR)
|
||||
+ return 0;
|
||||
+ else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO)
|
||||
+ return 1;
|
||||
+
|
||||
+ /* Then check for DMI blacklist -> second highest prio */
|
||||
+ if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VENDOR)
|
||||
+ return 0;
|
||||
+ else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VIDEO)
|
||||
+ return 1;
|
||||
+
|
||||
+ /* Then go the default way */
|
||||
+ return acpi_video_support & ACPI_VIDEO_BACKLIGHT;
|
||||
+}
|
||||
+EXPORT_SYMBOL(acpi_video_backlight_support);
|
||||
+
|
||||
+/*
|
||||
+ * Returns true if video.ko can do display output switching.
|
||||
+ * This does not work well/at all with binary graphics drivers
|
||||
+ * which disable system io ranges and do it on their own.
|
||||
+ */
|
||||
+int acpi_video_display_switch_support(void)
|
||||
+{
|
||||
+ if (!acpi_video_caps_checked)
|
||||
+ acpi_video_get_capabilities(NULL);
|
||||
+
|
||||
+ if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR)
|
||||
+ return 0;
|
||||
+ else if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO)
|
||||
+ return 1;
|
||||
+
|
||||
+ if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR)
|
||||
+ return 0;
|
||||
+ else if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO)
|
||||
+ return 1;
|
||||
+
|
||||
+ return acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING;
|
||||
+}
|
||||
+EXPORT_SYMBOL(acpi_video_display_switch_support);
|
||||
+
|
||||
+/*
|
||||
+ * Use acpi_display_output=vendor/video or acpi_backlight=vendor/video
|
||||
+ * To force that backlight or display output switching is processed by vendor
|
||||
+ * specific acpi drivers or video.ko driver.
|
||||
+ */
|
||||
+int __init acpi_backlight(char *str)
|
||||
+{
|
||||
+ if (str == NULL || *str == '\0')
|
||||
+ return 1;
|
||||
+ else {
|
||||
+ if (!strcmp("vendor", str))
|
||||
+ acpi_video_support |=
|
||||
+ ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR;
|
||||
+ if (!strcmp("video", str))
|
||||
+ acpi_video_support |=
|
||||
+ ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO;
|
||||
+ }
|
||||
+ return 1;
|
||||
+}
|
||||
+__setup("acpi_backlight=", acpi_backlight);
|
||||
+
|
||||
+int __init acpi_display_output(char *str)
|
||||
+{
|
||||
+ if (str == NULL || *str == '\0')
|
||||
+ return 1;
|
||||
+ else {
|
||||
+ if (!strcmp("vendor", str))
|
||||
+ acpi_video_support |=
|
||||
+ ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR;
|
||||
+ if (!strcmp("video", str))
|
||||
+ acpi_video_support |=
|
||||
+ ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO;
|
||||
+ }
|
||||
+ return 1;
|
||||
+}
|
||||
+__setup("acpi_display_output=", acpi_display_output);
|
||||
--- a/include/linux/acpi.h
|
||||
+++ b/include/linux/acpi.h
|
||||
@@ -202,6 +202,50 @@ extern bool wmi_has_guid(const char *gui
|
||||
|
||||
#endif /* CONFIG_ACPI_WMI */
|
||||
|
||||
+#define ACPI_VIDEO_OUTPUT_SWITCHING 0x0001
|
||||
+#define ACPI_VIDEO_DEVICE_POSTING 0x0002
|
||||
+#define ACPI_VIDEO_ROM_AVAILABLE 0x0004
|
||||
+#define ACPI_VIDEO_BACKLIGHT 0x0008
|
||||
+#define ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR 0x0010
|
||||
+#define ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO 0x0020
|
||||
+#define ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR 0x0040
|
||||
+#define ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO 0x0080
|
||||
+#define ACPI_VIDEO_BACKLIGHT_DMI_VENDOR 0x0100
|
||||
+#define ACPI_VIDEO_BACKLIGHT_DMI_VIDEO 0x0200
|
||||
+#define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR 0x0400
|
||||
+#define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO 0x0800
|
||||
+
|
||||
+#if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE)
|
||||
+
|
||||
+extern long acpi_video_get_capabilities(acpi_handle graphics_dev_handle);
|
||||
+extern long acpi_is_video_device(struct acpi_device *device);
|
||||
+extern int acpi_video_backlight_support(void);
|
||||
+extern int acpi_video_display_switch_support(void);
|
||||
+
|
||||
+#else
|
||||
+
|
||||
+static inline long acpi_video_get_capabilities(acpi_handle graphics_dev_handle)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static inline long acpi_is_video_device(struct acpi_device *device)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static inline int acpi_video_backlight_support(void)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static inline int acpi_video_display_switch_support(void)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#endif /* defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) */
|
||||
+
|
||||
extern int acpi_blacklisted(void);
|
||||
#ifdef CONFIG_DMI
|
||||
extern void acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d);
|
||||
@@ -0,0 +1,27 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: [PATCH] Acer-WMI: fingers off backlight if video.ko is serving this functionality
|
||||
Patch-Mainline: queued for .28 in Len's/ak's ACPI tree
|
||||
|
||||
commit 017f8ecd1cedb482392f0500ee3701b8c50a46f9
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Signed-off-by: Andi Kleen <ak@linux.intel.com>
|
||||
---
|
||||
drivers/misc/acer-wmi.c | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
--- a/drivers/misc/acer-wmi.c
|
||||
+++ b/drivers/misc/acer-wmi.c
|
||||
@@ -1242,6 +1242,12 @@ static int __init acer_wmi_init(void)
|
||||
|
||||
set_quirks();
|
||||
|
||||
+ if (!acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
|
||||
+ interface->capability &= ~ACER_CAP_BRIGHTNESS;
|
||||
+ printk(ACER_INFO "Brightness must be controlled by "
|
||||
+ "generic video driver\n");
|
||||
+ }
|
||||
+
|
||||
if (platform_driver_register(&acer_platform_driver)) {
|
||||
printk(ACER_ERR "Unable to register platform driver.\n");
|
||||
goto error_platform_register;
|
||||
@@ -0,0 +1,31 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: [PATCH] asus-acpi: fingers off backlight if video.ko is serving this functionality
|
||||
Patch-Mainline: queued for .28 in Len's/ak's ACPI tree
|
||||
|
||||
commit aaa47082a287d66fad32e3289e01bb9419ab18da
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Signed-off-by: Andi Kleen <ak@linux.intel.com>
|
||||
---
|
||||
drivers/misc/asus-laptop.c | 10 +++++++---
|
||||
1 file changed, 7 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/misc/asus-laptop.c
|
||||
+++ b/drivers/misc/asus-laptop.c
|
||||
@@ -1208,9 +1208,13 @@ static int __init asus_laptop_init(void)
|
||||
|
||||
dev = acpi_get_physical_device(hotk->device->handle);
|
||||
|
||||
- result = asus_backlight_init(dev);
|
||||
- if (result)
|
||||
- goto fail_backlight;
|
||||
+ if (!acpi_video_backlight_support()) {
|
||||
+ result = asus_backlight_init(dev);
|
||||
+ if (result)
|
||||
+ goto fail_backlight;
|
||||
+ } else
|
||||
+ printk(ASUS_INFO "Brightness ignored, must be controlled by "
|
||||
+ "ACPI video driver\n");
|
||||
|
||||
result = asus_led_init(dev);
|
||||
if (result)
|
||||
@@ -0,0 +1,39 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: [PATCH] compal: fingers off backlight if video.ko is serving this functionality
|
||||
Patch-Mainline: queued for .28 in Len's/ak's ACPI tree
|
||||
|
||||
commit efbc58e3236022d040e1c1f177ee5971d46b034f
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Signed-off-by: Andi Kleen <ak@linux.intel.com>
|
||||
---
|
||||
drivers/misc/compal-laptop.c | 12 +++++++-----
|
||||
1 files changed, 7 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/drivers/misc/compal-laptop.c b/drivers/misc/compal-laptop.c
|
||||
index 344b790..11003bb 100644
|
||||
--- a/drivers/misc/compal-laptop.c
|
||||
+++ b/drivers/misc/compal-laptop.c
|
||||
@@ -326,12 +326,14 @@ static int __init compal_init(void)
|
||||
|
||||
/* Register backlight stuff */
|
||||
|
||||
- compalbl_device = backlight_device_register("compal-laptop", NULL, NULL,
|
||||
- &compalbl_ops);
|
||||
- if (IS_ERR(compalbl_device))
|
||||
- return PTR_ERR(compalbl_device);
|
||||
+ if (!acpi_video_backlight_support()) {
|
||||
+ compalbl_device = backlight_device_register("compal-laptop", NULL, NULL,
|
||||
+ &compalbl_ops);
|
||||
+ if (IS_ERR(compalbl_device))
|
||||
+ return PTR_ERR(compalbl_device);
|
||||
|
||||
- compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1;
|
||||
+ compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1;
|
||||
+ }
|
||||
|
||||
ret = platform_driver_register(&compal_driver);
|
||||
if (ret)
|
||||
--
|
||||
1.5.4.5
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: [PATCH] eeepc-laptop: fingers off backlight if video.ko is serving this functionality
|
||||
commit bb7e1665304cba64b1178237c966308d06b33182
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Signed-off-by: Andi Kleen <ak@linux.intel.com>
|
||||
---
|
||||
drivers/misc/eeepc-laptop.c | 12 +++++++++---
|
||||
1 file changed, 9 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/misc/eeepc-laptop.c
|
||||
+++ b/drivers/misc/eeepc-laptop.c
|
||||
@@ -636,9 +636,15 @@ static int __init eeepc_laptop_init(void
|
||||
return -ENODEV;
|
||||
}
|
||||
dev = acpi_get_physical_device(ehotk->device->handle);
|
||||
- result = eeepc_backlight_init(dev);
|
||||
- if (result)
|
||||
- goto fail_backlight;
|
||||
+
|
||||
+ if (!acpi_video_backlight_support()) {
|
||||
+ result = eeepc_backlight_init(dev);
|
||||
+ if (result)
|
||||
+ goto fail_backlight;
|
||||
+ } else
|
||||
+ printk(EEEPC_INFO "Backlight controlled by ACPI video "
|
||||
+ "driver\n");
|
||||
+
|
||||
result = eeepc_hwmon_init(dev);
|
||||
if (result)
|
||||
goto fail_hwmon;
|
||||
@@ -0,0 +1,61 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: [PATCH] fujitsu-laptop: fingers off backlight if video.ko is serving this functionality
|
||||
Patch-Mainline: queued for .28 in Len's/ak's ACPI tree
|
||||
|
||||
commit 44cce15b053d27477980e34b1086e0c3ce642afe
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Signed-off-by: Andi Kleen <ak@linux.intel.com>
|
||||
---
|
||||
drivers/misc/fujitsu-laptop.c | 26 ++++++++++++++------------
|
||||
1 file changed, 14 insertions(+), 12 deletions(-)
|
||||
|
||||
--- a/drivers/misc/fujitsu-laptop.c
|
||||
+++ b/drivers/misc/fujitsu-laptop.c
|
||||
@@ -970,16 +970,16 @@ static int __init fujitsu_init(void)
|
||||
|
||||
/* Register backlight stuff */
|
||||
|
||||
- fujitsu->bl_device =
|
||||
- backlight_device_register("fujitsu-laptop", NULL, NULL,
|
||||
- &fujitsubl_ops);
|
||||
- if (IS_ERR(fujitsu->bl_device))
|
||||
- return PTR_ERR(fujitsu->bl_device);
|
||||
-
|
||||
- max_brightness = fujitsu->max_brightness;
|
||||
-
|
||||
- fujitsu->bl_device->props.max_brightness = max_brightness - 1;
|
||||
- fujitsu->bl_device->props.brightness = fujitsu->brightness_level;
|
||||
+ if (!acpi_video_backlight_support()) {
|
||||
+ fujitsu->bl_device =
|
||||
+ backlight_device_register("fujitsu-laptop", NULL, NULL,
|
||||
+ &fujitsubl_ops);
|
||||
+ if (IS_ERR(fujitsu->bl_device))
|
||||
+ return PTR_ERR(fujitsu->bl_device);
|
||||
+ max_brightness = fujitsu->max_brightness;
|
||||
+ fujitsu->bl_device->props.max_brightness = max_brightness - 1;
|
||||
+ fujitsu->bl_device->props.brightness = fujitsu->brightness_level;
|
||||
+ }
|
||||
|
||||
ret = platform_driver_register(&fujitsupf_driver);
|
||||
if (ret)
|
||||
@@ -1015,7 +1015,8 @@ fail_hotkey:
|
||||
|
||||
fail_backlight:
|
||||
|
||||
- backlight_device_unregister(fujitsu->bl_device);
|
||||
+ if (fujitsu->bl_device)
|
||||
+ backlight_device_unregister(fujitsu->bl_device);
|
||||
|
||||
fail_platform_device2:
|
||||
|
||||
@@ -1042,7 +1043,8 @@ static void __exit fujitsu_cleanup(void)
|
||||
&fujitsupf_attribute_group);
|
||||
platform_device_unregister(fujitsu->pf_device);
|
||||
platform_driver_unregister(&fujitsupf_driver);
|
||||
- backlight_device_unregister(fujitsu->bl_device);
|
||||
+ if (fujitsu->bl_device)
|
||||
+ backlight_device_unregister(fujitsu->bl_device);
|
||||
|
||||
acpi_bus_unregister_driver(&acpi_fujitsu_driver);
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: [PATCH] msi-laptop: fingers off backlight if video.ko is serving this functionality
|
||||
Patch-Mainline: queued for .28 in Len's/ak's ACPI tree
|
||||
|
||||
commit a8c338259a436627d2427d70dc31fac67b86b9e6
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Signed-off-by: Andi Kleen <ak@linux.intel.com>
|
||||
---
|
||||
drivers/misc/msi-laptop.c | 16 ++++++++++------
|
||||
1 files changed, 10 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/drivers/misc/msi-laptop.c b/drivers/misc/msi-laptop.c
|
||||
index de898c6..759763d 100644
|
||||
--- a/drivers/misc/msi-laptop.c
|
||||
+++ b/drivers/misc/msi-laptop.c
|
||||
@@ -347,12 +347,16 @@ static int __init msi_init(void)
|
||||
|
||||
/* Register backlight stuff */
|
||||
|
||||
- msibl_device = backlight_device_register("msi-laptop-bl", NULL, NULL,
|
||||
- &msibl_ops);
|
||||
- if (IS_ERR(msibl_device))
|
||||
- return PTR_ERR(msibl_device);
|
||||
-
|
||||
- msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1;
|
||||
+ if (acpi_video_backlight_support()) {
|
||||
+ printk(KERN_INFO "MSI: Brightness ignored, must be controlled "
|
||||
+ "by ACPI video driver\n");
|
||||
+ } else {
|
||||
+ msibl_device = backlight_device_register("msi-laptop-bl", NULL,
|
||||
+ NULL, &msibl_ops);
|
||||
+ if (IS_ERR(msibl_device))
|
||||
+ return PTR_ERR(msibl_device);
|
||||
+ msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1;
|
||||
+ }
|
||||
|
||||
ret = platform_driver_register(&msipf_driver);
|
||||
if (ret)
|
||||
--
|
||||
1.5.4.5
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: [PATCH] sony-laptop: fingers off backlight if video.ko is serving this functionality
|
||||
Patch-Mainline: queued for .28 in Len's/ak's ACPI tree
|
||||
|
||||
commit 1d38a0697573617e7f4d184207cced6cbe8d54d5
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Signed-off-by: Andi Kleen <ak@linux.intel.com>
|
||||
---
|
||||
drivers/misc/sony-laptop.c | 6 +++++-
|
||||
1 files changed, 5 insertions(+), 1 deletions(-)
|
||||
|
||||
diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c
|
||||
index 60775be..3d178ab 100644
|
||||
--- a/drivers/misc/sony-laptop.c
|
||||
+++ b/drivers/misc/sony-laptop.c
|
||||
@@ -1038,7 +1038,11 @@ static int sony_nc_add(struct acpi_device *device)
|
||||
goto outinput;
|
||||
}
|
||||
|
||||
- if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) {
|
||||
+ if (acpi_video_backlight_support()) {
|
||||
+ printk(KERN_INFO DRV_PFX "Sony: Brightness ignored, must be "
|
||||
+ "controlled by ACPI video driver\n");
|
||||
+ } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
|
||||
+ &handle))) {
|
||||
sony_backlight_device = backlight_device_register("sony", NULL,
|
||||
NULL,
|
||||
&sony_backlight_ops);
|
||||
--
|
||||
1.5.4.5
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: [PATCH] thinkpad_acpi: fingers off backlight if video.ko is serving this functionality
|
||||
Patch-Mainline: queued for .28 in Len's/ak's ACPI tree
|
||||
|
||||
commit bcca9a4a97b6e270793003d745d6f9439e1357a8
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Signed-off-by: Andi Kleen <ak@linux.intel.com>
|
||||
---
|
||||
drivers/misc/thinkpad_acpi.c | 29 +++++++++++++++++++----------
|
||||
1 file changed, 19 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/drivers/misc/thinkpad_acpi.c
|
||||
+++ b/drivers/misc/thinkpad_acpi.c
|
||||
@@ -4918,16 +4918,25 @@ static int __init brightness_init(struct
|
||||
*/
|
||||
b = tpacpi_check_std_acpi_brightness_support();
|
||||
if (b > 0) {
|
||||
- if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
|
||||
- printk(TPACPI_NOTICE
|
||||
- "Lenovo BIOS switched to ACPI backlight "
|
||||
- "control mode\n");
|
||||
- }
|
||||
- if (brightness_enable > 1) {
|
||||
- printk(TPACPI_NOTICE
|
||||
- "standard ACPI backlight interface "
|
||||
- "available, not loading native one...\n");
|
||||
- return 1;
|
||||
+
|
||||
+ if (acpi_video_backlight_support()) {
|
||||
+ if (brightness_enable > 1) {
|
||||
+ printk(TPACPI_NOTICE
|
||||
+ "Standard ACPI backlight interface "
|
||||
+ "available, not loading native one.\n");
|
||||
+ return 1;
|
||||
+ } else if (brightness_enable == 1) {
|
||||
+ printk(TPACPI_NOTICE
|
||||
+ "Backlight control force, even standard "
|
||||
+ "ACPI backlight interface available\n");
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (brightness_enable > 1) {
|
||||
+ printk(TPACPI_NOTICE
|
||||
+ "Standard ACPI backlight interface not "
|
||||
+ "available, thinkpad_acpi driver "
|
||||
+ "will take over control\n");
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
From: Jeff Mahoney <jeffm@suse.com>
|
||||
Subject: acpi: remove bay.c from makefile
|
||||
|
||||
patches.fixes/acpi-bay-remove-useless-code.patch removed drivers/acpi/bay.c.
|
||||
This patch removes the build infrastructure for it.
|
||||
|
||||
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
|
||||
---
|
||||
drivers/acpi/Kconfig | 8 --------
|
||||
drivers/acpi/Makefile | 1 -
|
||||
2 files changed, 9 deletions(-)
|
||||
|
||||
--- a/drivers/acpi/Kconfig
|
||||
+++ b/drivers/acpi/Kconfig
|
||||
@@ -162,14 +162,6 @@ config ACPI_DOCK
|
||||
help
|
||||
This driver adds support for ACPI controlled docking stations
|
||||
|
||||
-config ACPI_BAY
|
||||
- tristate "Removable Drive Bay (EXPERIMENTAL)"
|
||||
- depends on EXPERIMENTAL
|
||||
- depends on ACPI_DOCK
|
||||
- help
|
||||
- This driver adds support for ACPI controlled removable drive
|
||||
- bays such as the IBM ultrabay or the Dell Module Bay.
|
||||
-
|
||||
config ACPI_PROCESSOR
|
||||
tristate "Processor"
|
||||
select THERMAL
|
||||
--- a/drivers/acpi/Makefile
|
||||
+++ b/drivers/acpi/Makefile
|
||||
@@ -45,7 +45,6 @@ obj-$(CONFIG_ACPI_BATTERY) += battery.o
|
||||
obj-$(CONFIG_ACPI_BUTTON) += button.o
|
||||
obj-$(CONFIG_ACPI_FAN) += fan.o
|
||||
obj-$(CONFIG_ACPI_DOCK) += dock.o
|
||||
-obj-$(CONFIG_ACPI_BAY) += bay.o
|
||||
|
||||
obj-$(CONFIG_ACPI_VIDEO) += video.o
|
||||
ifdef CONFIG_ACPI_VIDEO
|
||||
@@ -0,0 +1,425 @@
|
||||
From: Shaohua Li <shaohua.li@intel.com>
|
||||
Subject: remove useless code
|
||||
Patch-mainline: submitted 2008-08-28
|
||||
References: fate#304731,bnc#401740
|
||||
|
||||
Bay driver is replaced by dock driver, remove it.
|
||||
|
||||
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
|
||||
Signed-off-by: Holger Macht <hmacht@suse.de>
|
||||
---
|
||||
|
||||
--- linux-2.6.26.orig/drivers/acpi/bay.c 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ linux-2.6.26/drivers/acpi/bay.c 1970-01-01 01:00:00.000000000 +0100
|
||||
@@ -1,411 +0,0 @@
|
||||
-/*
|
||||
- * bay.c - ACPI removable drive bay driver
|
||||
- *
|
||||
- * Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
|
||||
- *
|
||||
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
- *
|
||||
- * This program is free software; you can redistribute it and/or modify
|
||||
- * it under the terms of the GNU General Public License as published by
|
||||
- * the Free Software Foundation; either version 2 of the License, or (at
|
||||
- * your option) any later version.
|
||||
- *
|
||||
- * This program is distributed in the hope that it will be useful, but
|
||||
- * WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
- * General Public License for more details.
|
||||
- *
|
||||
- * You should have received a copy of the GNU General Public License along
|
||||
- * with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
- *
|
||||
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
- */
|
||||
-#include <linux/kernel.h>
|
||||
-#include <linux/module.h>
|
||||
-#include <linux/init.h>
|
||||
-#include <linux/types.h>
|
||||
-#include <linux/notifier.h>
|
||||
-#include <acpi/acpi_bus.h>
|
||||
-#include <acpi/acpi_drivers.h>
|
||||
-#include <linux/seq_file.h>
|
||||
-#include <asm/uaccess.h>
|
||||
-#include <linux/platform_device.h>
|
||||
-
|
||||
-ACPI_MODULE_NAME("bay");
|
||||
-MODULE_AUTHOR("Kristen Carlson Accardi");
|
||||
-MODULE_DESCRIPTION("ACPI Removable Drive Bay Driver");
|
||||
-MODULE_LICENSE("GPL");
|
||||
-#define ACPI_BAY_CLASS "bay"
|
||||
-#define ACPI_BAY_COMPONENT 0x10000000
|
||||
-#define _COMPONENT ACPI_BAY_COMPONENT
|
||||
-#define bay_dprintk(h,s) {\
|
||||
- char prefix[80] = {'\0'};\
|
||||
- struct acpi_buffer buffer = {sizeof(prefix), prefix};\
|
||||
- acpi_get_name(h, ACPI_FULL_PATHNAME, &buffer);\
|
||||
- printk(KERN_DEBUG PREFIX "%s: %s\n", prefix, s); }
|
||||
-static void bay_notify(acpi_handle handle, u32 event, void *data);
|
||||
-
|
||||
-static const struct acpi_device_id bay_device_ids[] = {
|
||||
- {"LNXIOBAY", 0},
|
||||
- {"", 0},
|
||||
-};
|
||||
-MODULE_DEVICE_TABLE(acpi, bay_device_ids);
|
||||
-
|
||||
-struct bay {
|
||||
- acpi_handle handle;
|
||||
- char *name;
|
||||
- struct list_head list;
|
||||
- struct platform_device *pdev;
|
||||
-};
|
||||
-
|
||||
-static LIST_HEAD(drive_bays);
|
||||
-
|
||||
-
|
||||
-/*****************************************************************************
|
||||
- * Drive Bay functions *
|
||||
- *****************************************************************************/
|
||||
-/**
|
||||
- * is_ejectable - see if a device is ejectable
|
||||
- * @handle: acpi handle of the device
|
||||
- *
|
||||
- * If an acpi object has a _EJ0 method, then it is ejectable
|
||||
- */
|
||||
-static int is_ejectable(acpi_handle handle)
|
||||
-{
|
||||
- acpi_status status;
|
||||
- acpi_handle tmp;
|
||||
-
|
||||
- status = acpi_get_handle(handle, "_EJ0", &tmp);
|
||||
- if (ACPI_FAILURE(status))
|
||||
- return 0;
|
||||
- return 1;
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * bay_present - see if the bay device is present
|
||||
- * @bay: the drive bay
|
||||
- *
|
||||
- * execute the _STA method.
|
||||
- */
|
||||
-static int bay_present(struct bay *bay)
|
||||
-{
|
||||
- unsigned long long sta;
|
||||
- acpi_status status;
|
||||
-
|
||||
- if (bay) {
|
||||
- status = acpi_evaluate_integer(bay->handle, "_STA", NULL, &sta);
|
||||
- if (ACPI_SUCCESS(status) && sta)
|
||||
- return 1;
|
||||
- }
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * eject_device - respond to an eject request
|
||||
- * @handle - the device to eject
|
||||
- *
|
||||
- * Call this devices _EJ0 method.
|
||||
- */
|
||||
-static void eject_device(acpi_handle handle)
|
||||
-{
|
||||
- struct acpi_object_list arg_list;
|
||||
- union acpi_object arg;
|
||||
-
|
||||
- bay_dprintk(handle, "Ejecting device");
|
||||
-
|
||||
- arg_list.count = 1;
|
||||
- arg_list.pointer = &arg;
|
||||
- arg.type = ACPI_TYPE_INTEGER;
|
||||
- arg.integer.value = 1;
|
||||
-
|
||||
- if (ACPI_FAILURE(acpi_evaluate_object(handle, "_EJ0",
|
||||
- &arg_list, NULL)))
|
||||
- pr_debug("Failed to evaluate _EJ0!\n");
|
||||
-}
|
||||
-
|
||||
-/*
|
||||
- * show_present - read method for "present" file in sysfs
|
||||
- */
|
||||
-static ssize_t show_present(struct device *dev,
|
||||
- struct device_attribute *attr, char *buf)
|
||||
-{
|
||||
- struct bay *bay = dev_get_drvdata(dev);
|
||||
- return snprintf(buf, PAGE_SIZE, "%d\n", bay_present(bay));
|
||||
-
|
||||
-}
|
||||
-static DEVICE_ATTR(present, S_IRUGO, show_present, NULL);
|
||||
-
|
||||
-/*
|
||||
- * write_eject - write method for "eject" file in sysfs
|
||||
- */
|
||||
-static ssize_t write_eject(struct device *dev, struct device_attribute *attr,
|
||||
- const char *buf, size_t count)
|
||||
-{
|
||||
- struct bay *bay = dev_get_drvdata(dev);
|
||||
-
|
||||
- if (!count)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- eject_device(bay->handle);
|
||||
- return count;
|
||||
-}
|
||||
-static DEVICE_ATTR(eject, S_IWUSR, NULL, write_eject);
|
||||
-
|
||||
-/**
|
||||
- * is_ata - see if a device is an ata device
|
||||
- * @handle: acpi handle of the device
|
||||
- *
|
||||
- * If an acpi object has one of 4 ATA ACPI methods defined,
|
||||
- * then it is an ATA device
|
||||
- */
|
||||
-static int is_ata(acpi_handle handle)
|
||||
-{
|
||||
- acpi_handle tmp;
|
||||
-
|
||||
- if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) ||
|
||||
- (ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) ||
|
||||
- (ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) ||
|
||||
- (ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp))))
|
||||
- return 1;
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * parent_is_ata(acpi_handle handle)
|
||||
- *
|
||||
- */
|
||||
-static int parent_is_ata(acpi_handle handle)
|
||||
-{
|
||||
- acpi_handle phandle;
|
||||
-
|
||||
- if (acpi_get_parent(handle, &phandle))
|
||||
- return 0;
|
||||
-
|
||||
- return is_ata(phandle);
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * is_ejectable_bay - see if a device is an ejectable drive bay
|
||||
- * @handle: acpi handle of the device
|
||||
- *
|
||||
- * If an acpi object is ejectable and has one of the ACPI ATA
|
||||
- * methods defined, then we can safely call it an ejectable
|
||||
- * drive bay
|
||||
- */
|
||||
-static int is_ejectable_bay(acpi_handle handle)
|
||||
-{
|
||||
- if ((is_ata(handle) || parent_is_ata(handle)) && is_ejectable(handle))
|
||||
- return 1;
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-#if 0
|
||||
-/**
|
||||
- * eject_removable_drive - try to eject this drive
|
||||
- * @dev : the device structure of the drive
|
||||
- *
|
||||
- * If a device is a removable drive that requires an _EJ0 method
|
||||
- * to be executed in order to safely remove from the system, do
|
||||
- * it. ATM - always returns success
|
||||
- */
|
||||
-int eject_removable_drive(struct device *dev)
|
||||
-{
|
||||
- acpi_handle handle = DEVICE_ACPI_HANDLE(dev);
|
||||
-
|
||||
- if (handle) {
|
||||
- bay_dprintk(handle, "Got device handle");
|
||||
- if (is_ejectable_bay(handle))
|
||||
- eject_device(handle);
|
||||
- } else {
|
||||
- printk("No acpi handle for device\n");
|
||||
- }
|
||||
-
|
||||
- /* should I return an error code? */
|
||||
- return 0;
|
||||
-}
|
||||
-EXPORT_SYMBOL_GPL(eject_removable_drive);
|
||||
-#endif /* 0 */
|
||||
-
|
||||
-static int acpi_bay_add_fs(struct bay *bay)
|
||||
-{
|
||||
- int ret;
|
||||
- struct device *dev = &bay->pdev->dev;
|
||||
-
|
||||
- ret = device_create_file(dev, &dev_attr_present);
|
||||
- if (ret)
|
||||
- goto add_fs_err;
|
||||
- ret = device_create_file(dev, &dev_attr_eject);
|
||||
- if (ret) {
|
||||
- device_remove_file(dev, &dev_attr_present);
|
||||
- goto add_fs_err;
|
||||
- }
|
||||
- return 0;
|
||||
-
|
||||
- add_fs_err:
|
||||
- bay_dprintk(bay->handle, "Error adding sysfs files\n");
|
||||
- return ret;
|
||||
-}
|
||||
-
|
||||
-static void acpi_bay_remove_fs(struct bay *bay)
|
||||
-{
|
||||
- struct device *dev = &bay->pdev->dev;
|
||||
-
|
||||
- /* cleanup sysfs */
|
||||
- device_remove_file(dev, &dev_attr_present);
|
||||
- device_remove_file(dev, &dev_attr_eject);
|
||||
-}
|
||||
-
|
||||
-static int bay_is_dock_device(acpi_handle handle)
|
||||
-{
|
||||
- acpi_handle parent;
|
||||
-
|
||||
- acpi_get_parent(handle, &parent);
|
||||
-
|
||||
- /* if the device or it's parent is dependent on the
|
||||
- * dock, then we are a dock device
|
||||
- */
|
||||
- return (is_dock_device(handle) || is_dock_device(parent));
|
||||
-}
|
||||
-
|
||||
-static int bay_add(acpi_handle handle, int id)
|
||||
-{
|
||||
- acpi_status status;
|
||||
- struct bay *new_bay;
|
||||
- struct platform_device *pdev;
|
||||
- struct acpi_buffer nbuffer = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
- acpi_get_name(handle, ACPI_FULL_PATHNAME, &nbuffer);
|
||||
-
|
||||
- bay_dprintk(handle, "Adding notify handler");
|
||||
-
|
||||
- /*
|
||||
- * Initialize bay device structure
|
||||
- */
|
||||
- new_bay = kzalloc(sizeof(*new_bay), GFP_ATOMIC);
|
||||
- INIT_LIST_HEAD(&new_bay->list);
|
||||
- new_bay->handle = handle;
|
||||
- new_bay->name = (char *)nbuffer.pointer;
|
||||
-
|
||||
- /* initialize platform device stuff */
|
||||
- pdev = platform_device_register_simple(ACPI_BAY_CLASS, id, NULL, 0);
|
||||
- if (IS_ERR(pdev)) {
|
||||
- printk(KERN_ERR PREFIX "Error registering bay device\n");
|
||||
- goto bay_add_err;
|
||||
- }
|
||||
- new_bay->pdev = pdev;
|
||||
- platform_set_drvdata(pdev, new_bay);
|
||||
-
|
||||
- /*
|
||||
- * we want the bay driver to be able to send uevents
|
||||
- */
|
||||
- pdev->dev.uevent_suppress = 0;
|
||||
-
|
||||
- /* register for events on this device */
|
||||
- status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
|
||||
- bay_notify, new_bay);
|
||||
- if (ACPI_FAILURE(status)) {
|
||||
- printk(KERN_INFO PREFIX "Error installing bay notify handler\n");
|
||||
- platform_device_unregister(new_bay->pdev);
|
||||
- goto bay_add_err;
|
||||
- }
|
||||
-
|
||||
- if (acpi_bay_add_fs(new_bay)) {
|
||||
- acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
|
||||
- bay_notify);
|
||||
- platform_device_unregister(new_bay->pdev);
|
||||
- goto bay_add_err;
|
||||
- }
|
||||
-
|
||||
- /* if we are on a dock station, we should register for dock
|
||||
- * notifications.
|
||||
- */
|
||||
- if (bay_is_dock_device(handle)) {
|
||||
- bay_dprintk(handle, "Is dependent on dock\n");
|
||||
- register_hotplug_dock_device(handle, bay_notify, new_bay);
|
||||
- }
|
||||
- list_add(&new_bay->list, &drive_bays);
|
||||
- printk(KERN_INFO PREFIX "Bay [%s] Added\n", new_bay->name);
|
||||
- return 0;
|
||||
-
|
||||
-bay_add_err:
|
||||
- kfree(new_bay->name);
|
||||
- kfree(new_bay);
|
||||
- return -ENODEV;
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * bay_notify - act upon an acpi bay notification
|
||||
- * @handle: the bay handle
|
||||
- * @event: the acpi event
|
||||
- * @data: our driver data struct
|
||||
- *
|
||||
- */
|
||||
-static void bay_notify(acpi_handle handle, u32 event, void *data)
|
||||
-{
|
||||
- struct bay *bay_dev = (struct bay *)data;
|
||||
- struct device *dev = &bay_dev->pdev->dev;
|
||||
- char event_string[12];
|
||||
- char *envp[] = { event_string, NULL };
|
||||
-
|
||||
- bay_dprintk(handle, "Bay event");
|
||||
- sprintf(event_string, "BAY_EVENT=%d", event);
|
||||
- kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
|
||||
-}
|
||||
-
|
||||
-static acpi_status
|
||||
-find_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
|
||||
-{
|
||||
- int *count = (int *)context;
|
||||
-
|
||||
- /*
|
||||
- * there could be more than one ejectable bay.
|
||||
- * so, just return AE_OK always so that every object
|
||||
- * will be checked.
|
||||
- */
|
||||
- if (is_ejectable_bay(handle)) {
|
||||
- bay_dprintk(handle, "found ejectable bay");
|
||||
- if (!bay_add(handle, *count))
|
||||
- (*count)++;
|
||||
- }
|
||||
- return AE_OK;
|
||||
-}
|
||||
-
|
||||
-static int __init bay_init(void)
|
||||
-{
|
||||
- int bays = 0;
|
||||
-
|
||||
- INIT_LIST_HEAD(&drive_bays);
|
||||
-
|
||||
- if (acpi_disabled)
|
||||
- return -ENODEV;
|
||||
-
|
||||
- /* look for dockable drive bays */
|
||||
- acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
- ACPI_UINT32_MAX, find_bay, &bays, NULL);
|
||||
-
|
||||
- if (!bays)
|
||||
- return -ENODEV;
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static void __exit bay_exit(void)
|
||||
-{
|
||||
- struct bay *bay, *tmp;
|
||||
-
|
||||
- list_for_each_entry_safe(bay, tmp, &drive_bays, list) {
|
||||
- if (is_dock_device(bay->handle))
|
||||
- unregister_hotplug_dock_device(bay->handle);
|
||||
- acpi_bay_remove_fs(bay);
|
||||
- acpi_remove_notify_handler(bay->handle, ACPI_SYSTEM_NOTIFY,
|
||||
- bay_notify);
|
||||
- platform_device_unregister(bay->pdev);
|
||||
- kfree(bay->name);
|
||||
- kfree(bay);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-postcore_initcall(bay_init);
|
||||
-module_exit(bay_exit);
|
||||
-
|
||||
@@ -0,0 +1,164 @@
|
||||
From: Shaohua Li <shaohua.li@intel.com>
|
||||
Subject: Fix duplicate notification handler register
|
||||
Patch-mainline: submitted 2008-08-28
|
||||
References: fate#304731,bnc#401740
|
||||
|
||||
Battery driver already registers notification handler. To avoid register
|
||||
notification handler again, the patch introduced a notifier chain in
|
||||
global system notifier handler and use it in dock driver, so we can
|
||||
avoid register notification handler.
|
||||
|
||||
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
|
||||
Signed-off-by: Holger Macht <hmacht@suse.de>
|
||||
---
|
||||
|
||||
---
|
||||
drivers/acpi/bus.c | 15 +++++++++++++++
|
||||
drivers/acpi/dock.c | 46 ++++++++++++++++++++++++----------------------
|
||||
include/acpi/acpi_bus.h | 3 +++
|
||||
3 files changed, 42 insertions(+), 22 deletions(-)
|
||||
|
||||
--- a/drivers/acpi/bus.c
|
||||
+++ b/drivers/acpi/bus.c
|
||||
@@ -496,6 +496,19 @@ static int acpi_bus_check_scope(struct a
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static BLOCKING_NOTIFIER_HEAD(acpi_bus_notify_list);
|
||||
+int register_acpi_bus_notifier(struct notifier_block *nb)
|
||||
+{
|
||||
+ return blocking_notifier_chain_register(&acpi_bus_notify_list, nb);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(register_acpi_bus_notifier);
|
||||
+
|
||||
+void unregister_acpi_bus_notifier(struct notifier_block *nb)
|
||||
+{
|
||||
+ blocking_notifier_chain_unregister(&acpi_bus_notify_list, nb);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(unregister_acpi_bus_notifier);
|
||||
+
|
||||
/**
|
||||
* acpi_bus_notify
|
||||
* ---------------
|
||||
@@ -506,6 +519,8 @@ static void acpi_bus_notify(acpi_handle
|
||||
int result = 0;
|
||||
struct acpi_device *device = NULL;
|
||||
|
||||
+ blocking_notifier_call_chain(&acpi_bus_notify_list,
|
||||
+ type, (void *)handle);
|
||||
|
||||
if (acpi_bus_get_device(handle, &device))
|
||||
return;
|
||||
--- a/drivers/acpi/dock.c
|
||||
+++ b/drivers/acpi/dock.c
|
||||
@@ -748,6 +748,28 @@ static void dock_notify(acpi_handle hand
|
||||
}
|
||||
}
|
||||
|
||||
+static int acpi_dock_notifier_call(struct notifier_block *this,
|
||||
+ unsigned long event, void *data)
|
||||
+{
|
||||
+ struct dock_station *dock_station;
|
||||
+ acpi_handle handle = (acpi_handle)data;
|
||||
+
|
||||
+ if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK
|
||||
+ && event != ACPI_NOTIFY_EJECT_REQUEST)
|
||||
+ return 0;
|
||||
+ list_for_each_entry(dock_station, &dock_stations, sibiling) {
|
||||
+ if (dock_station->handle == handle) {
|
||||
+ dock_notify(handle, event, dock_station);
|
||||
+ return 0 ;
|
||||
+ }
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct notifier_block dock_acpi_notifier = {
|
||||
+ .notifier_call = acpi_dock_notifier_call,
|
||||
+};
|
||||
+
|
||||
/**
|
||||
* find_dock_devices - find devices on the dock station
|
||||
* @handle: the handle of the device we are examining
|
||||
@@ -859,7 +881,6 @@ static DEVICE_ATTR(uid, S_IRUGO, show_do
|
||||
static int dock_add(acpi_handle handle)
|
||||
{
|
||||
int ret;
|
||||
- acpi_status status;
|
||||
struct dock_dependent_device *dd;
|
||||
struct dock_station *dock_station;
|
||||
struct platform_device *dock_device;
|
||||
@@ -954,23 +975,10 @@ static int dock_add(acpi_handle handle)
|
||||
}
|
||||
add_dock_dependent_device(dock_station, dd);
|
||||
|
||||
- /* register for dock events */
|
||||
- status = acpi_install_notify_handler(dock_station->handle,
|
||||
- ACPI_SYSTEM_NOTIFY,
|
||||
- dock_notify, dock_station);
|
||||
-
|
||||
- if (ACPI_FAILURE(status)) {
|
||||
- printk(KERN_ERR PREFIX "Error installing notify handler\n");
|
||||
- ret = -ENODEV;
|
||||
- goto dock_add_err;
|
||||
- }
|
||||
-
|
||||
dock_station_count++;
|
||||
list_add(&dock_station->sibiling, &dock_stations);
|
||||
return 0;
|
||||
|
||||
-dock_add_err:
|
||||
- kfree(dd);
|
||||
dock_add_err_unregister:
|
||||
device_remove_file(&dock_device->dev, &dev_attr_docked);
|
||||
device_remove_file(&dock_device->dev, &dev_attr_undock);
|
||||
@@ -988,7 +996,6 @@ dock_add_err_unregister:
|
||||
static int dock_remove(struct dock_station *dock_station)
|
||||
{
|
||||
struct dock_dependent_device *dd, *tmp;
|
||||
- acpi_status status;
|
||||
struct platform_device *dock_device = dock_station->dock_device;
|
||||
|
||||
if (!dock_station_count)
|
||||
@@ -999,13 +1006,6 @@ static int dock_remove(struct dock_stati
|
||||
list)
|
||||
kfree(dd);
|
||||
|
||||
- /* remove dock notify handler */
|
||||
- status = acpi_remove_notify_handler(dock_station->handle,
|
||||
- ACPI_SYSTEM_NOTIFY,
|
||||
- dock_notify);
|
||||
- if (ACPI_FAILURE(status))
|
||||
- printk(KERN_ERR "Error removing notify handler\n");
|
||||
-
|
||||
/* cleanup sysfs */
|
||||
device_remove_file(&dock_device->dev, &dev_attr_docked);
|
||||
device_remove_file(&dock_device->dev, &dev_attr_undock);
|
||||
@@ -1067,6 +1067,7 @@ static int __init dock_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+ register_acpi_bus_notifier(&dock_acpi_notifier);
|
||||
printk(KERN_INFO PREFIX "%s: %d docks/bays found\n",
|
||||
ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count);
|
||||
return 0;
|
||||
@@ -1076,6 +1077,7 @@ static void __exit dock_exit(void)
|
||||
{
|
||||
struct dock_station *dock_station;
|
||||
|
||||
+ unregister_acpi_bus_notifier(&dock_acpi_notifier);
|
||||
list_for_each_entry(dock_station, &dock_stations, sibiling)
|
||||
dock_remove(dock_station);
|
||||
}
|
||||
--- a/include/acpi/acpi_bus.h
|
||||
+++ b/include/acpi/acpi_bus.h
|
||||
@@ -327,6 +327,9 @@ int acpi_bus_get_private_data(acpi_handl
|
||||
extern int acpi_notifier_call_chain(struct acpi_device *, u32, u32);
|
||||
extern int register_acpi_notifier(struct notifier_block *);
|
||||
extern int unregister_acpi_notifier(struct notifier_block *);
|
||||
+
|
||||
+extern int register_acpi_bus_notifier(struct notifier_block *nb);
|
||||
+extern void unregister_acpi_bus_notifier(struct notifier_block *nb);
|
||||
/*
|
||||
* External Functions
|
||||
*/
|
||||
@@ -0,0 +1,57 @@
|
||||
From: Shaohua Li <shaohua.li@intel.com>
|
||||
Subject: add _LCK support for dock
|
||||
Patch-mainline: submitted 2008-08-28
|
||||
References: fate#304731,bnc#401740
|
||||
|
||||
support _LCK method, which is a optional method for hotplug
|
||||
|
||||
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
|
||||
Signed-off-by: Holger Macht <hmacht@suse.de>
|
||||
---
|
||||
|
||||
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
|
||||
index 78d27ce..7bdf93b 100644
|
||||
--- a/drivers/acpi/dock.c
|
||||
+++ b/drivers/acpi/dock.c
|
||||
@@ -452,6 +452,25 @@ static inline void complete_undock(struct dock_station *ds)
|
||||
ds->flags &= ~(DOCK_UNDOCKING);
|
||||
}
|
||||
|
||||
+static void dock_lock(struct dock_station *ds, int lock)
|
||||
+{
|
||||
+ struct acpi_object_list arg_list;
|
||||
+ union acpi_object arg;
|
||||
+ acpi_status status;
|
||||
+
|
||||
+ arg_list.count = 1;
|
||||
+ arg_list.pointer = &arg;
|
||||
+ arg.type = ACPI_TYPE_INTEGER;
|
||||
+ arg.integer.value = !!lock;
|
||||
+ status = acpi_evaluate_object(ds->handle, "_LCK", &arg_list, NULL);
|
||||
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
|
||||
+ if (lock)
|
||||
+ printk(KERN_WARNING PREFIX "Locking device failed\n");
|
||||
+ else
|
||||
+ printk(KERN_WARNING PREFIX "Unlocking device failed\n");
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* dock_in_progress - see if we are in the middle of handling a dock event
|
||||
* @ds: the dock station
|
||||
@@ -577,6 +596,7 @@ static int handle_eject_request(struct dock_station *ds, u32 event)
|
||||
|
||||
hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST);
|
||||
undock(ds);
|
||||
+ dock_lock(ds, 0);
|
||||
eject_dock(ds);
|
||||
if (dock_present(ds)) {
|
||||
printk(KERN_ERR PREFIX "Unable to undock!\n");
|
||||
@@ -617,6 +637,7 @@ static void dock_notify(acpi_handle handle, u32 event, void *data)
|
||||
hotplug_dock_devices(ds, event);
|
||||
complete_dock(ds);
|
||||
dock_event(ds, event, DOCK_EVENT);
|
||||
+ dock_lock(ds, 1);
|
||||
}
|
||||
break;
|
||||
case ACPI_NOTIFY_DEVICE_CHECK:
|
||||
@@ -0,0 +1,70 @@
|
||||
From: Shaohua Li <shaohua.li@intel.com>
|
||||
Subject: add 'type' sysfs file for dock
|
||||
Patch-mainline: submitted 2008-08-28
|
||||
References: fate#304731,bnc#401740
|
||||
|
||||
add a sysfs file to present dock type. Suggested by Holger.
|
||||
|
||||
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
|
||||
Signed-off-by: Holger Macht <hmacht@suse.de>
|
||||
---
|
||||
|
||||
---
|
||||
drivers/acpi/dock.c | 25 +++++++++++++++++++++++++
|
||||
1 file changed, 25 insertions(+)
|
||||
|
||||
--- a/drivers/acpi/dock.c
|
||||
+++ b/drivers/acpi/dock.c
|
||||
@@ -909,6 +909,26 @@ static ssize_t show_dock_uid(struct devi
|
||||
}
|
||||
static DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
|
||||
|
||||
+static ssize_t show_dock_type(struct device *dev,
|
||||
+ struct device_attribute *attr, char *buf)
|
||||
+{
|
||||
+ struct dock_station *dock_station = *((struct dock_station **)
|
||||
+ dev->platform_data);
|
||||
+ char *type;
|
||||
+
|
||||
+ if (dock_station->flags & DOCK_IS_DOCK)
|
||||
+ type = "dock_station";
|
||||
+ else if (dock_station->flags & DOCK_IS_ATA)
|
||||
+ type = "ata_bay";
|
||||
+ else if (dock_station->flags & DOCK_IS_BAT)
|
||||
+ type = "battery_bay";
|
||||
+ else
|
||||
+ type = "unknown";
|
||||
+
|
||||
+ return snprintf(buf, PAGE_SIZE, "%s\n", type);
|
||||
+}
|
||||
+static DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL);
|
||||
+
|
||||
/**
|
||||
* dock_add - add a new dock station
|
||||
* @handle: the dock station handle
|
||||
@@ -997,6 +1017,9 @@ static int dock_add(acpi_handle handle)
|
||||
dock_station = NULL;
|
||||
return ret;
|
||||
}
|
||||
+ ret = device_create_file(&dock_device->dev, &dev_attr_type);
|
||||
+ if (ret)
|
||||
+ printk(KERN_ERR"Error %d adding sysfs file\n", ret);
|
||||
|
||||
/* Find dependent devices */
|
||||
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
@@ -1018,6 +1041,7 @@ static int dock_add(acpi_handle handle)
|
||||
return 0;
|
||||
|
||||
dock_add_err_unregister:
|
||||
+ device_remove_file(&dock_device->dev, &dev_attr_type);
|
||||
device_remove_file(&dock_device->dev, &dev_attr_docked);
|
||||
device_remove_file(&dock_device->dev, &dev_attr_undock);
|
||||
device_remove_file(&dock_device->dev, &dev_attr_uid);
|
||||
@@ -1045,6 +1069,7 @@ static int dock_remove(struct dock_stati
|
||||
kfree(dd);
|
||||
|
||||
/* cleanup sysfs */
|
||||
+ device_remove_file(&dock_device->dev, &dev_attr_type);
|
||||
device_remove_file(&dock_device->dev, &dev_attr_docked);
|
||||
device_remove_file(&dock_device->dev, &dev_attr_undock);
|
||||
device_remove_file(&dock_device->dev, &dev_attr_uid);
|
||||
@@ -0,0 +1,47 @@
|
||||
From 1d672ef324e78a467603ef55aa4558cac9f895ba Mon Sep 17 00:00:00 2001
|
||||
From: Holger Macht <hmacht@suse.de>
|
||||
Date: Tue, 20 Jan 2009 12:18:24 +0100
|
||||
Subject: ACPI: dock: Don't eval _STA on every show_docked sysfs read
|
||||
|
||||
From: Holger Macht <hmacht@suse.de>
|
||||
|
||||
commit fc5a9f8841ee87d93376ada5d73117d4d6a373ea upstream.
|
||||
|
||||
Some devices trigger a DEVICE_CHECK on every evalutation of _STA. This
|
||||
can also be seen in commit 8b59560a3baf2e7c24e0fb92ea5d09eca92805db
|
||||
(ACPI: dock: avoid check _STA method). If an undock is processed, the
|
||||
dock driver sends a uevent and userspace might read the show_docked
|
||||
property in sysfs. This causes an evaluation of _STA of the particular
|
||||
device which causes the dock driver to immediately dock again.
|
||||
|
||||
In any case, evaluation of _STA (show_docked) does not necessarily mean
|
||||
that we are docked, so check with the internal device structure.
|
||||
|
||||
http://bugzilla.kernel.org/show_bug.cgi?id=12360
|
||||
|
||||
Signed-off-by: Holger Macht <hmacht@suse.de>
|
||||
Signed-off-by: Len Brown <len.brown@intel.com>
|
||||
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
|
||||
---
|
||||
drivers/acpi/dock.c | 8 +++++++-
|
||||
1 file changed, 7 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/acpi/dock.c
|
||||
+++ b/drivers/acpi/dock.c
|
||||
@@ -854,8 +854,14 @@ fdd_out:
|
||||
static ssize_t show_docked(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
- return snprintf(buf, PAGE_SIZE, "%d\n", dock_present(dock_station));
|
||||
+ struct acpi_device *tmp;
|
||||
|
||||
+ struct dock_station *dock_station = *((struct dock_station **)
|
||||
+ dev->platform_data);
|
||||
+
|
||||
+ if (ACPI_SUCCESS(acpi_bus_get_device(dock_station->handle, &tmp)))
|
||||
+ return snprintf(buf, PAGE_SIZE, "1\n");
|
||||
+ return snprintf(buf, PAGE_SIZE, "0\n");
|
||||
}
|
||||
static DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
From: Shaohua Li <shaohua.li@intel.com>
|
||||
Subject: fix eject request process
|
||||
Patch-mainline: submitted 2008-08-28
|
||||
References: fate#304731,bnc#401740
|
||||
|
||||
commit 2a7feab28d3fc060d320eaba192e49dad1079b7e introduces a bug.
|
||||
My thinkpad actually will send an eject_request and we should follow the
|
||||
eject process to finish the eject, otherwise system still thinks the bay
|
||||
is present.
|
||||
|
||||
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
|
||||
Signed-off-by: Holger Macht <hmacht@suse.de>
|
||||
---
|
||||
|
||||
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
|
||||
index 25d2161..78d27ce 100644
|
||||
--- a/drivers/acpi/dock.c
|
||||
+++ b/drivers/acpi/dock.c
|
||||
@@ -575,11 +575,6 @@ static int handle_eject_request(struct dock_station *ds, u32 event)
|
||||
*/
|
||||
dock_event(ds, event, UNDOCK_EVENT);
|
||||
|
||||
- if (!dock_present(ds)) {
|
||||
- complete_undock(ds);
|
||||
- return -ENODEV;
|
||||
- }
|
||||
-
|
||||
hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST);
|
||||
undock(ds);
|
||||
eject_dock(ds);
|
||||
@@ -0,0 +1,61 @@
|
||||
From: Shaohua Li <shaohua.li@intel.com>
|
||||
Subject: fix for bay in a dock station
|
||||
Patch-mainline: submitted 2008-08-28
|
||||
References: fate#304731,bnc#401740
|
||||
|
||||
an ATA bay can be in a dock and itself can be ejected separately. The
|
||||
patch handles such eject bay. Found by Holger.
|
||||
|
||||
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
|
||||
Signed-off-by: Holger Macht <hmacht@suse.de>
|
||||
---
|
||||
|
||||
---
|
||||
drivers/acpi/dock.c | 14 ++++++++++----
|
||||
1 file changed, 10 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/drivers/acpi/dock.c
|
||||
+++ b/drivers/acpi/dock.c
|
||||
@@ -609,6 +609,7 @@ register_hotplug_dock_device(acpi_handle
|
||||
{
|
||||
struct dock_dependent_device *dd;
|
||||
struct dock_station *dock_station;
|
||||
+ int ret = -EINVAL;
|
||||
|
||||
if (!dock_station_count)
|
||||
return -ENODEV;
|
||||
@@ -618,16 +619,21 @@ register_hotplug_dock_device(acpi_handle
|
||||
* this would include the dock station itself
|
||||
*/
|
||||
list_for_each_entry(dock_station, &dock_stations, sibiling) {
|
||||
+ /*
|
||||
+ * An ATA bay can be in a dock and itself can be ejected
|
||||
+ * seperately, so there are two 'dock stations' which need the
|
||||
+ * ops
|
||||
+ */
|
||||
dd = find_dock_dependent_device(dock_station, handle);
|
||||
if (dd) {
|
||||
dd->ops = ops;
|
||||
dd->context = context;
|
||||
dock_add_hotplug_device(dock_station, dd);
|
||||
- return 0;
|
||||
+ ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
- return -EINVAL;
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
|
||||
@@ -1076,8 +1082,8 @@ find_dock(acpi_handle handle, u32 lvl, v
|
||||
static acpi_status
|
||||
find_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
|
||||
{
|
||||
- /* If bay is in a dock, it's already handled */
|
||||
- if (is_ejectable_bay(handle) && !is_dock_device(handle))
|
||||
+ /* If bay is a dock, it's already handled */
|
||||
+ if (is_ejectable_bay(handle) && !is_dock(handle))
|
||||
dock_add(handle);
|
||||
return AE_OK;
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
From: Shaohua Li <shaohua.li@intel.com>
|
||||
Subject: fix hotplug race
|
||||
Patch-mainline: submitted 2008-08-28
|
||||
References: fate#304731,bnc#401740
|
||||
|
||||
hotplug notification handler and drivers' notification handler are all
|
||||
running in one workqueue. Before hotplug removes an acpi device, the
|
||||
device driver's notification handler is already be recorded to run just
|
||||
after global notification handler. After hotplug notification handler
|
||||
runs, acpica will notice a NULL notification handler and crash. This
|
||||
patch runs hotplug in other workqueue and wait for all acpi notication
|
||||
handlers finish. This is found in battery hotplug, but actually all
|
||||
hotplug can be affected.
|
||||
|
||||
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
|
||||
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
|
||||
Signed-off-by: Holger Macht <hmacht@suse.de>
|
||||
---
|
||||
|
||||
---
|
||||
drivers/acpi/dock.c | 25 ++++++++++++++++++++++++-
|
||||
drivers/acpi/osl.c | 46 +++++++++++++++++++++++++++++++++++++++++-----
|
||||
include/acpi/acpiosxf.h | 3 +++
|
||||
3 files changed, 68 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/acpi/dock.c
|
||||
+++ b/drivers/acpi/dock.c
|
||||
@@ -748,6 +748,20 @@ static void dock_notify(acpi_handle hand
|
||||
}
|
||||
}
|
||||
|
||||
+struct dock_data {
|
||||
+ acpi_handle handle;
|
||||
+ unsigned long event;
|
||||
+ struct dock_station *ds;
|
||||
+};
|
||||
+
|
||||
+static void acpi_dock_deferred_cb(void *context)
|
||||
+{
|
||||
+ struct dock_data *data = (struct dock_data *)context;
|
||||
+
|
||||
+ dock_notify(data->handle, data->event, data->ds);
|
||||
+ kfree(data);
|
||||
+}
|
||||
+
|
||||
static int acpi_dock_notifier_call(struct notifier_block *this,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
@@ -759,7 +773,16 @@ static int acpi_dock_notifier_call(struc
|
||||
return 0;
|
||||
list_for_each_entry(dock_station, &dock_stations, sibiling) {
|
||||
if (dock_station->handle == handle) {
|
||||
- dock_notify(handle, event, dock_station);
|
||||
+ struct dock_data *dock_data;
|
||||
+
|
||||
+ dock_data = kmalloc(sizeof(*dock_data), GFP_KERNEL);
|
||||
+ if (!dock_data)
|
||||
+ return 0;
|
||||
+ dock_data->handle = handle;
|
||||
+ dock_data->event = event;
|
||||
+ dock_data->ds = dock_station;
|
||||
+ acpi_os_hotplug_execute(acpi_dock_deferred_cb,
|
||||
+ dock_data);
|
||||
return 0 ;
|
||||
}
|
||||
}
|
||||
--- a/drivers/acpi/osl.c
|
||||
+++ b/drivers/acpi/osl.c
|
||||
@@ -705,6 +705,22 @@ static void acpi_os_execute_deferred(str
|
||||
return;
|
||||
}
|
||||
|
||||
+static void acpi_os_execute_hp_deferred(struct work_struct *work)
|
||||
+{
|
||||
+ struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work);
|
||||
+ if (!dpc) {
|
||||
+ printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ acpi_os_wait_events_complete(NULL);
|
||||
+
|
||||
+ dpc->function(dpc->context);
|
||||
+ kfree(dpc);
|
||||
+
|
||||
+ return;
|
||||
+}
|
||||
+
|
||||
/*******************************************************************************
|
||||
*
|
||||
* FUNCTION: acpi_os_execute
|
||||
@@ -720,12 +736,13 @@ static void acpi_os_execute_deferred(str
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
-acpi_status acpi_os_execute(acpi_execute_type type,
|
||||
- acpi_osd_exec_callback function, void *context)
|
||||
+static acpi_status __acpi_os_execute(acpi_execute_type type,
|
||||
+ acpi_osd_exec_callback function, void *context, int hp)
|
||||
{
|
||||
acpi_status status = AE_OK;
|
||||
struct acpi_os_dpc *dpc;
|
||||
struct workqueue_struct *queue;
|
||||
+ int ret;
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
|
||||
"Scheduling function [%p(%p)] for deferred execution.\n",
|
||||
function, context));
|
||||
@@ -749,9 +766,17 @@ acpi_status acpi_os_execute(acpi_execute
|
||||
dpc->function = function;
|
||||
dpc->context = context;
|
||||
|
||||
- INIT_WORK(&dpc->work, acpi_os_execute_deferred);
|
||||
- queue = (type == OSL_NOTIFY_HANDLER) ? kacpi_notify_wq : kacpid_wq;
|
||||
- if (!queue_work(queue, &dpc->work)) {
|
||||
+ if (!hp) {
|
||||
+ INIT_WORK(&dpc->work, acpi_os_execute_deferred);
|
||||
+ queue = (type == OSL_NOTIFY_HANDLER) ?
|
||||
+ kacpi_notify_wq : kacpid_wq;
|
||||
+ ret = queue_work(queue, &dpc->work);
|
||||
+ } else {
|
||||
+ INIT_WORK(&dpc->work, acpi_os_execute_hp_deferred);
|
||||
+ ret = schedule_work(&dpc->work);
|
||||
+ }
|
||||
+
|
||||
+ if (!ret) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
|
||||
"Call to queue_work() failed.\n"));
|
||||
status = AE_ERROR;
|
||||
@@ -760,8 +785,19 @@ acpi_status acpi_os_execute(acpi_execute
|
||||
return_ACPI_STATUS(status);
|
||||
}
|
||||
|
||||
+acpi_status acpi_os_execute(acpi_execute_type type,
|
||||
+ acpi_osd_exec_callback function, void *context)
|
||||
+{
|
||||
+ return __acpi_os_execute(type, function, context, 0);
|
||||
+}
|
||||
EXPORT_SYMBOL(acpi_os_execute);
|
||||
|
||||
+acpi_status acpi_os_hotplug_execute(acpi_osd_exec_callback function,
|
||||
+ void *context)
|
||||
+{
|
||||
+ return __acpi_os_execute(0, function, context, 1);
|
||||
+}
|
||||
+
|
||||
void acpi_os_wait_events_complete(void *context)
|
||||
{
|
||||
flush_workqueue(kacpid_wq);
|
||||
--- a/include/acpi/acpiosxf.h
|
||||
+++ b/include/acpi/acpiosxf.h
|
||||
@@ -193,6 +193,9 @@ acpi_status
|
||||
acpi_os_execute(acpi_execute_type type,
|
||||
acpi_osd_exec_callback function, void *context);
|
||||
|
||||
+acpi_status
|
||||
+acpi_os_hotplug_execute(acpi_osd_exec_callback function, void *context);
|
||||
+
|
||||
void acpi_os_wait_events_complete(void *context);
|
||||
|
||||
void acpi_os_sleep(acpi_integer milliseconds);
|
||||
@@ -0,0 +1,214 @@
|
||||
From: Shaohua Li <shaohua.li@intel.com>
|
||||
Subject: introduce .uevent for devices in dock
|
||||
Patch-mainline: submitted 2008-08-28
|
||||
References: fate#304731,bnc#401740
|
||||
|
||||
dock's uevent reported itself, not ata. It might be difficult to find an
|
||||
ata device just according to a dock. This patch introduces docking ops
|
||||
for each device in a dock. when docking, dock driver can send device
|
||||
specific uevent. This should help dock station too (not just bay)
|
||||
|
||||
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
|
||||
Signed-off-by: Holger Macht <hmacht@suse.de>
|
||||
---
|
||||
|
||||
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
|
||||
index f19f643..ac7dfef 100644
|
||||
--- a/drivers/acpi/dock.c
|
||||
+++ b/drivers/acpi/dock.c
|
||||
@@ -75,7 +75,7 @@ struct dock_dependent_device {
|
||||
struct list_head list;
|
||||
struct list_head hotplug_list;
|
||||
acpi_handle handle;
|
||||
- acpi_notify_handler handler;
|
||||
+ struct acpi_dock_ops *ops;
|
||||
void *context;
|
||||
};
|
||||
|
||||
@@ -385,8 +385,8 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event)
|
||||
* First call driver specific hotplug functions
|
||||
*/
|
||||
list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
|
||||
- if (dd->handler)
|
||||
- dd->handler(dd->handle, event, dd->context);
|
||||
+ if (dd->ops && dd->ops->handler)
|
||||
+ dd->ops->handler(dd->handle, event, dd->context);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -409,6 +409,7 @@ static void dock_event(struct dock_station *ds, u32 event, int num)
|
||||
struct device *dev = &ds->dock_device->dev;
|
||||
char event_string[13];
|
||||
char *envp[] = { event_string, NULL };
|
||||
+ struct dock_dependent_device *dd;
|
||||
|
||||
if (num == UNDOCK_EVENT)
|
||||
sprintf(event_string, "EVENT=undock");
|
||||
@@ -419,7 +420,14 @@ static void dock_event(struct dock_station *ds, u32 event, int num)
|
||||
* Indicate that the status of the dock station has
|
||||
* changed.
|
||||
*/
|
||||
- kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
|
||||
+ if (num == DOCK_EVENT)
|
||||
+ kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
|
||||
+
|
||||
+ list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
|
||||
+ if (dd->ops && dd->ops->uevent)
|
||||
+ dd->ops->uevent(dd->handle, event, dd->context);
|
||||
+ if (num != DOCK_EVENT)
|
||||
+ kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -588,7 +596,7 @@ EXPORT_SYMBOL_GPL(unregister_dock_notifier);
|
||||
/**
|
||||
* register_hotplug_dock_device - register a hotplug function
|
||||
* @handle: the handle of the device
|
||||
- * @handler: the acpi_notifier_handler to call after docking
|
||||
+ * @ops: handlers to call after docking
|
||||
* @context: device specific data
|
||||
*
|
||||
* If a driver would like to perform a hotplug operation after a dock
|
||||
@@ -596,7 +604,7 @@ EXPORT_SYMBOL_GPL(unregister_dock_notifier);
|
||||
* the dock driver after _DCK is executed.
|
||||
*/
|
||||
int
|
||||
-register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
|
||||
+register_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops,
|
||||
void *context)
|
||||
{
|
||||
struct dock_dependent_device *dd;
|
||||
@@ -612,7 +620,7 @@ register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler,
|
||||
list_for_each_entry(dock_station, &dock_stations, sibiling) {
|
||||
dd = find_dock_dependent_device(dock_station, handle);
|
||||
if (dd) {
|
||||
- dd->handler = handler;
|
||||
+ dd->ops = ops;
|
||||
dd->context = context;
|
||||
dock_add_hotplug_device(dock_station, dd);
|
||||
return 0;
|
||||
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
|
||||
index 97727be..c012307 100644
|
||||
--- a/drivers/ata/libata-acpi.c
|
||||
+++ b/drivers/ata/libata-acpi.c
|
||||
@@ -209,6 +209,46 @@ static void ata_acpi_ap_notify_dock(acpi_handle handle, u32 event, void *data)
|
||||
ata_acpi_handle_hotplug(ap, NULL, event);
|
||||
}
|
||||
|
||||
+static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev,
|
||||
+ u32 event)
|
||||
+{
|
||||
+ struct kobject *kobj = NULL;
|
||||
+ char event_string[20];
|
||||
+ char *envp[] = { event_string, NULL };
|
||||
+
|
||||
+ if (dev) {
|
||||
+ if (dev->sdev)
|
||||
+ kobj = &dev->sdev->sdev_gendev.kobj;
|
||||
+ } else
|
||||
+ kobj = &ap->dev->kobj;
|
||||
+
|
||||
+ if (kobj) {
|
||||
+ snprintf(event_string, 20, "BAY_EVENT=%d", event);
|
||||
+ kobject_uevent_env(kobj, KOBJ_CHANGE, envp);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void ata_acpi_ap_uevent(acpi_handle handle, u32 event, void *data)
|
||||
+{
|
||||
+ ata_acpi_uevent(data, NULL, event);
|
||||
+}
|
||||
+
|
||||
+static void ata_acpi_dev_uevent(acpi_handle handle, u32 event, void *data)
|
||||
+{
|
||||
+ struct ata_device *dev = data;
|
||||
+ ata_acpi_uevent(dev->link->ap, dev, event);
|
||||
+}
|
||||
+
|
||||
+static struct acpi_dock_ops ata_acpi_dev_dock_ops = {
|
||||
+ .handler = ata_acpi_dev_notify_dock,
|
||||
+ .uevent = ata_acpi_dev_uevent,
|
||||
+};
|
||||
+
|
||||
+static struct acpi_dock_ops ata_acpi_ap_dock_ops = {
|
||||
+ .handler = ata_acpi_ap_notify_dock,
|
||||
+ .uevent = ata_acpi_ap_uevent,
|
||||
+};
|
||||
+
|
||||
/**
|
||||
* ata_acpi_associate - associate ATA host with ACPI objects
|
||||
* @host: target ATA host
|
||||
@@ -244,7 +284,7 @@ void ata_acpi_associate(struct ata_host *host)
|
||||
if (ap->acpi_handle) {
|
||||
/* we might be on a docking station */
|
||||
register_hotplug_dock_device(ap->acpi_handle,
|
||||
- ata_acpi_ap_notify_dock, ap);
|
||||
+ &ata_acpi_ap_dock_ops, ap);
|
||||
}
|
||||
|
||||
for (j = 0; j < ata_link_max_devices(&ap->link); j++) {
|
||||
@@ -253,7 +293,7 @@ void ata_acpi_associate(struct ata_host *host)
|
||||
if (dev->acpi_handle) {
|
||||
/* we might be on a docking station */
|
||||
register_hotplug_dock_device(dev->acpi_handle,
|
||||
- ata_acpi_dev_notify_dock, dev);
|
||||
+ &ata_acpi_dev_dock_ops, dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
|
||||
index a3e4705..db54c5e 100644
|
||||
--- a/drivers/pci/hotplug/acpiphp_glue.c
|
||||
+++ b/drivers/pci/hotplug/acpiphp_glue.c
|
||||
@@ -169,7 +169,9 @@ static int post_dock_fixups(struct notifier_block *nb, unsigned long val,
|
||||
}
|
||||
|
||||
|
||||
-
|
||||
+static struct acpi_dock_ops acpiphp_dock_ops = {
|
||||
+ .handler = handle_hotplug_event_func,
|
||||
+};
|
||||
|
||||
/* callback routine to register each ACPI PCI slot object */
|
||||
static acpi_status
|
||||
@@ -285,7 +287,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
|
||||
*/
|
||||
newfunc->flags &= ~FUNC_HAS_EJ0;
|
||||
if (register_hotplug_dock_device(handle,
|
||||
- handle_hotplug_event_func, newfunc))
|
||||
+ &acpiphp_dock_ops, newfunc))
|
||||
dbg("failed to register dock device\n");
|
||||
|
||||
/* we need to be notified when dock events happen
|
||||
diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h
|
||||
index e5f38e5..4f5042a 100644
|
||||
--- a/include/acpi/acpi_drivers.h
|
||||
+++ b/include/acpi/acpi_drivers.h
|
||||
@@ -115,12 +115,17 @@ int acpi_processor_set_thermal_limit(acpi_handle handle, int type);
|
||||
/*--------------------------------------------------------------------------
|
||||
Dock Station
|
||||
-------------------------------------------------------------------------- */
|
||||
+struct acpi_dock_ops {
|
||||
+ acpi_notify_handler handler;
|
||||
+ acpi_notify_handler uevent;
|
||||
+};
|
||||
+
|
||||
#if defined(CONFIG_ACPI_DOCK) || defined(CONFIG_ACPI_DOCK_MODULE)
|
||||
extern int is_dock_device(acpi_handle handle);
|
||||
extern int register_dock_notifier(struct notifier_block *nb);
|
||||
extern void unregister_dock_notifier(struct notifier_block *nb);
|
||||
extern int register_hotplug_dock_device(acpi_handle handle,
|
||||
- acpi_notify_handler handler,
|
||||
+ struct acpi_dock_ops *ops,
|
||||
void *context);
|
||||
extern void unregister_hotplug_dock_device(acpi_handle handle);
|
||||
#else
|
||||
@@ -136,7 +141,7 @@ static inline void unregister_dock_notifier(struct notifier_block *nb)
|
||||
{
|
||||
}
|
||||
static inline int register_hotplug_dock_device(acpi_handle handle,
|
||||
- acpi_notify_handler handler,
|
||||
+ struct acpi_dock_ops *ops,
|
||||
void *context)
|
||||
{
|
||||
return -ENODEV;
|
||||
@@ -0,0 +1,451 @@
|
||||
From: Shaohua Li <shaohua.li@intel.com>
|
||||
Subject: makeing dock driver supports bay and battery hotplug
|
||||
Patch-mainline: submitted 2008-08-28
|
||||
References: fate#304731,bnc#401740
|
||||
|
||||
Making dock driver supports bay and battery hotplug. They are all
|
||||
regarded as dock, and unified handled.
|
||||
|
||||
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
|
||||
Signed-off-by: Holger Macht <hmacht@suse.de>
|
||||
---
|
||||
|
||||
---
|
||||
drivers/acpi/dock.c | 221 ++++++++++++++++++++++++++++++++++++++++------------
|
||||
1 file changed, 173 insertions(+), 48 deletions(-)
|
||||
|
||||
--- a/drivers/acpi/dock.c
|
||||
+++ b/drivers/acpi/dock.c
|
||||
@@ -48,7 +48,6 @@ MODULE_PARM_DESC(immediate_undock, "1 (d
|
||||
" before undocking");
|
||||
|
||||
static struct atomic_notifier_head dock_notifier_list;
|
||||
-static struct platform_device *dock_device;
|
||||
static char dock_device_name[] = "dock";
|
||||
|
||||
static const struct acpi_device_id dock_device_ids[] = {
|
||||
@@ -65,7 +64,12 @@ struct dock_station {
|
||||
struct mutex hp_lock;
|
||||
struct list_head dependent_devices;
|
||||
struct list_head hotplug_devices;
|
||||
+
|
||||
+ struct list_head sibiling;
|
||||
+ struct platform_device *dock_device;
|
||||
};
|
||||
+static LIST_HEAD(dock_stations);
|
||||
+static int dock_station_count;
|
||||
|
||||
struct dock_dependent_device {
|
||||
struct list_head list;
|
||||
@@ -77,11 +81,12 @@ struct dock_dependent_device {
|
||||
|
||||
#define DOCK_DOCKING 0x00000001
|
||||
#define DOCK_UNDOCKING 0x00000002
|
||||
+#define DOCK_IS_DOCK 0x00000010
|
||||
+#define DOCK_IS_ATA 0x00000020
|
||||
+#define DOCK_IS_BAT 0x00000040
|
||||
#define DOCK_EVENT 3
|
||||
#define UNDOCK_EVENT 2
|
||||
|
||||
-static struct dock_station *dock_station;
|
||||
-
|
||||
/*****************************************************************************
|
||||
* Dock Dependent device functions *
|
||||
*****************************************************************************/
|
||||
@@ -199,6 +204,60 @@ static int is_dock(acpi_handle handle)
|
||||
return 1;
|
||||
}
|
||||
|
||||
+static int is_ejectable(acpi_handle handle)
|
||||
+{
|
||||
+ acpi_status status;
|
||||
+ acpi_handle tmp;
|
||||
+
|
||||
+ status = acpi_get_handle(handle, "_EJ0", &tmp);
|
||||
+ if (ACPI_FAILURE(status))
|
||||
+ return 0;
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
+static int is_ata(acpi_handle handle)
|
||||
+{
|
||||
+ acpi_handle tmp;
|
||||
+
|
||||
+ if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) ||
|
||||
+ (ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) ||
|
||||
+ (ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) ||
|
||||
+ (ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp))))
|
||||
+ return 1;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int is_battery(acpi_handle handle)
|
||||
+{
|
||||
+ struct acpi_device_info *info;
|
||||
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
+ int ret = 1;
|
||||
+
|
||||
+ if (!ACPI_SUCCESS(acpi_get_object_info(handle, &buffer)))
|
||||
+ return 0;
|
||||
+ info = buffer.pointer;
|
||||
+ if (!(info->valid & ACPI_VALID_HID))
|
||||
+ ret = 0;
|
||||
+ else
|
||||
+ ret = !strcmp("PNP0C0A", info->hardware_id.value);
|
||||
+
|
||||
+ kfree(buffer.pointer);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int is_ejectable_bay(acpi_handle handle)
|
||||
+{
|
||||
+ acpi_handle phandle;
|
||||
+ if (!is_ejectable(handle))
|
||||
+ return 0;
|
||||
+ if (is_battery(handle) || is_ata(handle))
|
||||
+ return 1;
|
||||
+ if (!acpi_get_parent(handle, &phandle) && is_ata(phandle))
|
||||
+ return 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* is_dock_device - see if a device is on a dock station
|
||||
* @handle: acpi handle of the device
|
||||
@@ -209,11 +268,17 @@ static int is_dock(acpi_handle handle)
|
||||
*/
|
||||
int is_dock_device(acpi_handle handle)
|
||||
{
|
||||
- if (!dock_station)
|
||||
+ struct dock_station *dock_station;
|
||||
+
|
||||
+ if (!dock_station_count)
|
||||
return 0;
|
||||
|
||||
- if (is_dock(handle) || find_dock_dependent_device(dock_station, handle))
|
||||
+ if (is_dock(handle))
|
||||
return 1;
|
||||
+ list_for_each_entry(dock_station, &dock_stations, sibiling) {
|
||||
+ if (find_dock_dependent_device(dock_station, handle))
|
||||
+ return 1;
|
||||
+ }
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -341,7 +406,7 @@ static void hotplug_dock_devices(struct
|
||||
|
||||
static void dock_event(struct dock_station *ds, u32 event, int num)
|
||||
{
|
||||
- struct device *dev = &dock_device->dev;
|
||||
+ struct device *dev = &ds->dock_device->dev;
|
||||
char event_string[13];
|
||||
char *envp[] = { event_string, NULL };
|
||||
|
||||
@@ -414,7 +479,7 @@ static void handle_dock(struct dock_stat
|
||||
arg.type = ACPI_TYPE_INTEGER;
|
||||
arg.integer.value = dock;
|
||||
status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer);
|
||||
- if (ACPI_FAILURE(status))
|
||||
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
|
||||
printk(KERN_ERR PREFIX "%s - failed to execute _DCK\n",
|
||||
(char *)name_buffer.pointer);
|
||||
kfree(buffer.pointer);
|
||||
@@ -498,7 +563,7 @@ static int dock_in_progress(struct dock_
|
||||
*/
|
||||
int register_dock_notifier(struct notifier_block *nb)
|
||||
{
|
||||
- if (!dock_station)
|
||||
+ if (!dock_station_count)
|
||||
return -ENODEV;
|
||||
|
||||
return atomic_notifier_chain_register(&dock_notifier_list, nb);
|
||||
@@ -512,7 +577,7 @@ EXPORT_SYMBOL_GPL(register_dock_notifier
|
||||
*/
|
||||
void unregister_dock_notifier(struct notifier_block *nb)
|
||||
{
|
||||
- if (!dock_station)
|
||||
+ if (!dock_station_count)
|
||||
return;
|
||||
|
||||
atomic_notifier_chain_unregister(&dock_notifier_list, nb);
|
||||
@@ -535,20 +600,23 @@ register_hotplug_dock_device(acpi_handle
|
||||
void *context)
|
||||
{
|
||||
struct dock_dependent_device *dd;
|
||||
+ struct dock_station *dock_station;
|
||||
|
||||
- if (!dock_station)
|
||||
+ if (!dock_station_count)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* make sure this handle is for a device dependent on the dock,
|
||||
* this would include the dock station itself
|
||||
*/
|
||||
- dd = find_dock_dependent_device(dock_station, handle);
|
||||
- if (dd) {
|
||||
- dd->handler = handler;
|
||||
- dd->context = context;
|
||||
- dock_add_hotplug_device(dock_station, dd);
|
||||
- return 0;
|
||||
+ list_for_each_entry(dock_station, &dock_stations, sibiling) {
|
||||
+ dd = find_dock_dependent_device(dock_station, handle);
|
||||
+ if (dd) {
|
||||
+ dd->handler = handler;
|
||||
+ dd->context = context;
|
||||
+ dock_add_hotplug_device(dock_station, dd);
|
||||
+ return 0;
|
||||
+ }
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
@@ -563,13 +631,16 @@ EXPORT_SYMBOL_GPL(register_hotplug_dock_
|
||||
void unregister_hotplug_dock_device(acpi_handle handle)
|
||||
{
|
||||
struct dock_dependent_device *dd;
|
||||
+ struct dock_station *dock_station;
|
||||
|
||||
- if (!dock_station)
|
||||
+ if (!dock_station_count)
|
||||
return;
|
||||
|
||||
- dd = find_dock_dependent_device(dock_station, handle);
|
||||
- if (dd)
|
||||
- dock_del_hotplug_device(dock_station, dd);
|
||||
+ list_for_each_entry(dock_station, &dock_stations, sibiling) {
|
||||
+ dd = find_dock_dependent_device(dock_station, handle);
|
||||
+ if (dd)
|
||||
+ dock_del_hotplug_device(dock_station, dd);
|
||||
+ }
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
|
||||
@@ -620,9 +691,28 @@ static void dock_notify(acpi_handle hand
|
||||
{
|
||||
struct dock_station *ds = data;
|
||||
struct acpi_device *tmp;
|
||||
+ int surprise_removal = 0;
|
||||
|
||||
+ /*
|
||||
+ * According to acpi spec 3.0a, if a DEVICE_CHECK notification
|
||||
+ * is sent and _DCK is present, it is assumed to mean an undock
|
||||
+ * request.
|
||||
+ */
|
||||
+ if ((ds->flags & DOCK_IS_DOCK) && event == ACPI_NOTIFY_DEVICE_CHECK)
|
||||
+ event = ACPI_NOTIFY_EJECT_REQUEST;
|
||||
+
|
||||
+ /*
|
||||
+ * dock station: BUS_CHECK - docked or surprise removal
|
||||
+ * DEVICE_CHECK - undocked
|
||||
+ * other device: BUS_CHECK/DEVICE_CHECK - added or surprise removal
|
||||
+ *
|
||||
+ * To simplify event handling, dock dependent device handler always
|
||||
+ * get ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and
|
||||
+ * ACPI_NOTIFY_EJECT_REQUEST for removal
|
||||
+ */
|
||||
switch (event) {
|
||||
case ACPI_NOTIFY_BUS_CHECK:
|
||||
+ case ACPI_NOTIFY_DEVICE_CHECK:
|
||||
if (!dock_in_progress(ds) && acpi_bus_get_device(ds->handle,
|
||||
&tmp)) {
|
||||
begin_dock(ds);
|
||||
@@ -638,20 +728,17 @@ static void dock_notify(acpi_handle hand
|
||||
complete_dock(ds);
|
||||
dock_event(ds, event, DOCK_EVENT);
|
||||
dock_lock(ds, 1);
|
||||
+ break;
|
||||
}
|
||||
- break;
|
||||
- case ACPI_NOTIFY_DEVICE_CHECK:
|
||||
- /*
|
||||
- * According to acpi spec 3.0a, if a DEVICE_CHECK notification
|
||||
- * is sent and _DCK is present, it is assumed to mean an
|
||||
- * undock request. This notify routine will only be called
|
||||
- * for objects defining _DCK, so we will fall through to eject
|
||||
- * request here. However, we will pass an eject request through
|
||||
- * to the driver who wish to hotplug.
|
||||
- */
|
||||
+ if (dock_present(ds) || dock_in_progress(ds))
|
||||
+ break;
|
||||
+ /* This is a surprise removal */
|
||||
+ surprise_removal = 1;
|
||||
+ event = ACPI_NOTIFY_EJECT_REQUEST;
|
||||
+ /* Fall back */
|
||||
case ACPI_NOTIFY_EJECT_REQUEST:
|
||||
begin_undock(ds);
|
||||
- if (immediate_undock)
|
||||
+ if (immediate_undock || surprise_removal)
|
||||
handle_eject_request(ds, event);
|
||||
else
|
||||
dock_event(ds, event, UNDOCK_EVENT);
|
||||
@@ -718,6 +805,8 @@ static DEVICE_ATTR(docked, S_IRUGO, show
|
||||
static ssize_t show_flags(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
+ struct dock_station *dock_station = *((struct dock_station **)
|
||||
+ dev->platform_data);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
|
||||
|
||||
}
|
||||
@@ -730,6 +819,8 @@ static ssize_t write_undock(struct devic
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
+ struct dock_station *dock_station = *((struct dock_station **)
|
||||
+ dev->platform_data);
|
||||
|
||||
if (!count)
|
||||
return -EINVAL;
|
||||
@@ -747,6 +838,8 @@ static ssize_t show_dock_uid(struct devi
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
unsigned long long lbuf;
|
||||
+ struct dock_station *dock_station = *((struct dock_station **)
|
||||
+ dev->platform_data);
|
||||
acpi_status status = acpi_evaluate_integer(dock_station->handle,
|
||||
"_UID", NULL, &lbuf);
|
||||
if (ACPI_FAILURE(status))
|
||||
@@ -768,6 +861,8 @@ static int dock_add(acpi_handle handle)
|
||||
int ret;
|
||||
acpi_status status;
|
||||
struct dock_dependent_device *dd;
|
||||
+ struct dock_station *dock_station;
|
||||
+ struct platform_device *dock_device;
|
||||
|
||||
/* allocate & initialize the dock_station private data */
|
||||
dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL);
|
||||
@@ -777,22 +872,34 @@ static int dock_add(acpi_handle handle)
|
||||
dock_station->last_dock_time = jiffies - HZ;
|
||||
INIT_LIST_HEAD(&dock_station->dependent_devices);
|
||||
INIT_LIST_HEAD(&dock_station->hotplug_devices);
|
||||
+ INIT_LIST_HEAD(&dock_station->sibiling);
|
||||
spin_lock_init(&dock_station->dd_lock);
|
||||
mutex_init(&dock_station->hp_lock);
|
||||
ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
|
||||
|
||||
/* initialize platform device stuff */
|
||||
- dock_device =
|
||||
- platform_device_register_simple(dock_device_name, 0, NULL, 0);
|
||||
+ dock_station->dock_device =
|
||||
+ platform_device_register_simple(dock_device_name,
|
||||
+ dock_station_count, NULL, 0);
|
||||
+ dock_device = dock_station->dock_device;
|
||||
if (IS_ERR(dock_device)) {
|
||||
kfree(dock_station);
|
||||
dock_station = NULL;
|
||||
return PTR_ERR(dock_device);
|
||||
}
|
||||
+ platform_device_add_data(dock_device, &dock_station,
|
||||
+ sizeof(struct dock_station *));
|
||||
|
||||
/* we want the dock device to send uevents */
|
||||
dock_device->dev.uevent_suppress = 0;
|
||||
|
||||
+ if (is_dock(handle))
|
||||
+ dock_station->flags |= DOCK_IS_DOCK;
|
||||
+ if (is_ata(handle))
|
||||
+ dock_station->flags |= DOCK_IS_ATA;
|
||||
+ if (is_battery(handle))
|
||||
+ dock_station->flags |= DOCK_IS_BAT;
|
||||
+
|
||||
ret = device_create_file(&dock_device->dev, &dev_attr_docked);
|
||||
if (ret) {
|
||||
printk("Error %d adding sysfs file\n", ret);
|
||||
@@ -858,8 +965,8 @@ static int dock_add(acpi_handle handle)
|
||||
goto dock_add_err;
|
||||
}
|
||||
|
||||
- printk(KERN_INFO PREFIX "%s\n", ACPI_DOCK_DRIVER_DESCRIPTION);
|
||||
-
|
||||
+ dock_station_count++;
|
||||
+ list_add(&dock_station->sibiling, &dock_stations);
|
||||
return 0;
|
||||
|
||||
dock_add_err:
|
||||
@@ -878,12 +985,13 @@ dock_add_err_unregister:
|
||||
/**
|
||||
* dock_remove - free up resources related to the dock station
|
||||
*/
|
||||
-static int dock_remove(void)
|
||||
+static int dock_remove(struct dock_station *dock_station)
|
||||
{
|
||||
struct dock_dependent_device *dd, *tmp;
|
||||
acpi_status status;
|
||||
+ struct platform_device *dock_device = dock_station->dock_device;
|
||||
|
||||
- if (!dock_station)
|
||||
+ if (!dock_station_count)
|
||||
return 0;
|
||||
|
||||
/* remove dependent devices */
|
||||
@@ -923,41 +1031,58 @@ static int dock_remove(void)
|
||||
static acpi_status
|
||||
find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
|
||||
{
|
||||
- int *count = context;
|
||||
acpi_status status = AE_OK;
|
||||
|
||||
if (is_dock(handle)) {
|
||||
if (dock_add(handle) >= 0) {
|
||||
- (*count)++;
|
||||
status = AE_CTRL_TERMINATE;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
-static int __init dock_init(void)
|
||||
+static acpi_status
|
||||
+find_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
|
||||
{
|
||||
- int num = 0;
|
||||
-
|
||||
- dock_station = NULL;
|
||||
+ /* If bay is in a dock, it's already handled */
|
||||
+ if (is_ejectable_bay(handle) && !is_dock_device(handle))
|
||||
+ dock_add(handle);
|
||||
+ return AE_OK;
|
||||
+}
|
||||
|
||||
+static int __init dock_init(void)
|
||||
+{
|
||||
if (acpi_disabled)
|
||||
return 0;
|
||||
|
||||
/* look for a dock station */
|
||||
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
- ACPI_UINT32_MAX, find_dock, &num, NULL);
|
||||
+ ACPI_UINT32_MAX, find_dock, NULL, NULL);
|
||||
|
||||
- if (!num)
|
||||
- printk(KERN_INFO "No dock devices found.\n");
|
||||
+ /* look for bay */
|
||||
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
+ ACPI_UINT32_MAX, find_bay, NULL, NULL);
|
||||
+ if (!dock_station_count) {
|
||||
+ printk(KERN_INFO PREFIX "No dock devices found.\n");
|
||||
+ return 0;
|
||||
+ }
|
||||
|
||||
+ printk(KERN_INFO PREFIX "%s: %d docks/bays found\n",
|
||||
+ ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit dock_exit(void)
|
||||
{
|
||||
- dock_remove();
|
||||
+ struct dock_station *dock_station;
|
||||
+
|
||||
+ list_for_each_entry(dock_station, &dock_stations, sibiling)
|
||||
+ dock_remove(dock_station);
|
||||
}
|
||||
|
||||
-postcore_initcall(dock_init);
|
||||
+/*
|
||||
+ * Must be called before drivers of devices in dock, otherwise we can't know
|
||||
+ * which devices are in a dock
|
||||
+ */
|
||||
+subsys_initcall(dock_init);
|
||||
module_exit(dock_exit);
|
||||
@@ -0,0 +1,23 @@
|
||||
From: Jeff Mahoney <jeffm@suse.com>
|
||||
Subject: acpi: export acpi_os_hotplug_execute
|
||||
|
||||
The ACPI dock driver changes require acpi_os_hotplug_execute,
|
||||
which wasn't exported.
|
||||
|
||||
This patch exports it.
|
||||
|
||||
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
|
||||
---
|
||||
drivers/acpi/osl.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
--- a/drivers/acpi/osl.c
|
||||
+++ b/drivers/acpi/osl.c
|
||||
@@ -797,6 +797,7 @@ acpi_status acpi_os_hotplug_execute(acpi
|
||||
{
|
||||
return __acpi_os_execute(0, function, context, 1);
|
||||
}
|
||||
+EXPORT_SYMBOL(acpi_os_hotplug_execute);
|
||||
|
||||
void acpi_os_wait_events_complete(void *context)
|
||||
{
|
||||
@@ -0,0 +1,211 @@
|
||||
From: Shaohua Li <shaohua.li@intel.com>
|
||||
Subject: libata hotplug to align with dock driver
|
||||
Patch-mainline: submitted 2008-08-28
|
||||
References: fate#304731,bnc#401740
|
||||
|
||||
dock driver can handle ata(bay) hotplug now. dock driver already handles
|
||||
_EJ0 and _STA, so remove them. Also libata doesn't need register
|
||||
notification handler anymore.
|
||||
|
||||
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
|
||||
Signed-off-by: Holger Macht <hmacht@suse.de>
|
||||
---
|
||||
|
||||
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
|
||||
index 4b395b1..f19f643 100644
|
||||
--- a/drivers/acpi/dock.c
|
||||
+++ b/drivers/acpi/dock.c
|
||||
@@ -738,7 +738,8 @@ static void dock_notify(acpi_handle handle, u32 event, void *data)
|
||||
/* Fall back */
|
||||
case ACPI_NOTIFY_EJECT_REQUEST:
|
||||
begin_undock(ds);
|
||||
- if (immediate_undock || surprise_removal)
|
||||
+ if ((immediate_undock && !(ds->flags & DOCK_IS_ATA))
|
||||
+ || surprise_removal)
|
||||
handle_eject_request(ds, event);
|
||||
else
|
||||
dock_event(ds, event, UNDOCK_EVENT);
|
||||
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
|
||||
index 9330b79..97727be 100644
|
||||
--- a/drivers/ata/libata-acpi.c
|
||||
+++ b/drivers/ata/libata-acpi.c
|
||||
@@ -120,21 +120,6 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap)
|
||||
ap->pflags |= ATA_PFLAG_INIT_GTM_VALID;
|
||||
}
|
||||
|
||||
-static void ata_acpi_eject_device(acpi_handle handle)
|
||||
-{
|
||||
- struct acpi_object_list arg_list;
|
||||
- union acpi_object arg;
|
||||
-
|
||||
- arg_list.count = 1;
|
||||
- arg_list.pointer = &arg;
|
||||
- arg.type = ACPI_TYPE_INTEGER;
|
||||
- arg.integer.value = 1;
|
||||
-
|
||||
- if (ACPI_FAILURE(acpi_evaluate_object(handle, "_EJ0",
|
||||
- &arg_list, NULL)))
|
||||
- printk(KERN_ERR "Failed to evaluate _EJ0!\n");
|
||||
-}
|
||||
-
|
||||
/* @ap and @dev are the same as ata_acpi_handle_hotplug() */
|
||||
static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev)
|
||||
{
|
||||
@@ -157,7 +142,6 @@ static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev)
|
||||
* @ap: ATA port ACPI event occurred
|
||||
* @dev: ATA device ACPI event occurred (can be NULL)
|
||||
* @event: ACPI event which occurred
|
||||
- * @is_dock_event: boolean indicating whether the event was a dock one
|
||||
*
|
||||
* All ACPI bay / device realted events end up in this function. If
|
||||
* the event is port-wide @dev is NULL. If the event is specific to a
|
||||
@@ -171,115 +155,58 @@ static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev)
|
||||
* ACPI notify handler context. May sleep.
|
||||
*/
|
||||
static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev,
|
||||
- u32 event, int is_dock_event)
|
||||
+ u32 event)
|
||||
{
|
||||
- char event_string[12];
|
||||
- char *envp[] = { event_string, NULL };
|
||||
struct ata_eh_info *ehi = &ap->link.eh_info;
|
||||
- struct kobject *kobj = NULL;
|
||||
int wait = 0;
|
||||
unsigned long flags;
|
||||
- acpi_handle handle, tmphandle;
|
||||
- unsigned long long sta;
|
||||
- acpi_status status;
|
||||
+ acpi_handle handle;
|
||||
|
||||
- if (dev) {
|
||||
- if (dev->sdev)
|
||||
- kobj = &dev->sdev->sdev_gendev.kobj;
|
||||
+ if (dev)
|
||||
handle = dev->acpi_handle;
|
||||
- } else {
|
||||
- kobj = &ap->dev->kobj;
|
||||
+ else
|
||||
handle = ap->acpi_handle;
|
||||
- }
|
||||
-
|
||||
- status = acpi_get_handle(handle, "_EJ0", &tmphandle);
|
||||
- if (ACPI_FAILURE(status))
|
||||
- /* This device does not support hotplug */
|
||||
- return;
|
||||
-
|
||||
- if (event == ACPI_NOTIFY_BUS_CHECK ||
|
||||
- event == ACPI_NOTIFY_DEVICE_CHECK)
|
||||
- status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
|
||||
|
||||
spin_lock_irqsave(ap->lock, flags);
|
||||
-
|
||||
+ /*
|
||||
+ * When dock driver calls into the routine, it will always use
|
||||
+ * ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and
|
||||
+ * ACPI_NOTIFY_EJECT_REQUEST for remove
|
||||
+ */
|
||||
switch (event) {
|
||||
case ACPI_NOTIFY_BUS_CHECK:
|
||||
case ACPI_NOTIFY_DEVICE_CHECK:
|
||||
ata_ehi_push_desc(ehi, "ACPI event");
|
||||
|
||||
- if (ACPI_FAILURE(status)) {
|
||||
- ata_port_printk(ap, KERN_ERR,
|
||||
- "acpi: failed to determine bay status (0x%x)\n",
|
||||
- status);
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- if (sta) {
|
||||
- ata_ehi_hotplugged(ehi);
|
||||
- ata_port_freeze(ap);
|
||||
- } else {
|
||||
- /* The device has gone - unplug it */
|
||||
- ata_acpi_detach_device(ap, dev);
|
||||
- wait = 1;
|
||||
- }
|
||||
+ ata_ehi_hotplugged(ehi);
|
||||
+ ata_port_freeze(ap);
|
||||
break;
|
||||
case ACPI_NOTIFY_EJECT_REQUEST:
|
||||
ata_ehi_push_desc(ehi, "ACPI event");
|
||||
|
||||
- if (!is_dock_event)
|
||||
- break;
|
||||
-
|
||||
- /* undock event - immediate unplug */
|
||||
ata_acpi_detach_device(ap, dev);
|
||||
wait = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
- /* make sure kobj doesn't go away while ap->lock is released */
|
||||
- kobject_get(kobj);
|
||||
-
|
||||
spin_unlock_irqrestore(ap->lock, flags);
|
||||
|
||||
- if (wait) {
|
||||
+ if (wait)
|
||||
ata_port_wait_eh(ap);
|
||||
- ata_acpi_eject_device(handle);
|
||||
- }
|
||||
-
|
||||
- if (kobj && !is_dock_event) {
|
||||
- sprintf(event_string, "BAY_EVENT=%d", event);
|
||||
- kobject_uevent_env(kobj, KOBJ_CHANGE, envp);
|
||||
- }
|
||||
-
|
||||
- kobject_put(kobj);
|
||||
}
|
||||
|
||||
static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data)
|
||||
{
|
||||
struct ata_device *dev = data;
|
||||
|
||||
- ata_acpi_handle_hotplug(dev->link->ap, dev, event, 1);
|
||||
+ ata_acpi_handle_hotplug(dev->link->ap, dev, event);
|
||||
}
|
||||
|
||||
static void ata_acpi_ap_notify_dock(acpi_handle handle, u32 event, void *data)
|
||||
{
|
||||
struct ata_port *ap = data;
|
||||
|
||||
- ata_acpi_handle_hotplug(ap, NULL, event, 1);
|
||||
-}
|
||||
-
|
||||
-static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data)
|
||||
-{
|
||||
- struct ata_device *dev = data;
|
||||
-
|
||||
- ata_acpi_handle_hotplug(dev->link->ap, dev, event, 0);
|
||||
-}
|
||||
-
|
||||
-static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data)
|
||||
-{
|
||||
- struct ata_port *ap = data;
|
||||
-
|
||||
- ata_acpi_handle_hotplug(ap, NULL, event, 0);
|
||||
+ ata_acpi_handle_hotplug(ap, NULL, event);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,9 +242,6 @@ void ata_acpi_associate(struct ata_host *host)
|
||||
ata_acpi_associate_ide_port(ap);
|
||||
|
||||
if (ap->acpi_handle) {
|
||||
- acpi_install_notify_handler(ap->acpi_handle,
|
||||
- ACPI_SYSTEM_NOTIFY,
|
||||
- ata_acpi_ap_notify, ap);
|
||||
/* we might be on a docking station */
|
||||
register_hotplug_dock_device(ap->acpi_handle,
|
||||
ata_acpi_ap_notify_dock, ap);
|
||||
@@ -327,9 +251,6 @@ void ata_acpi_associate(struct ata_host *host)
|
||||
struct ata_device *dev = &ap->link.device[j];
|
||||
|
||||
if (dev->acpi_handle) {
|
||||
- acpi_install_notify_handler(dev->acpi_handle,
|
||||
- ACPI_SYSTEM_NOTIFY,
|
||||
- ata_acpi_dev_notify, dev);
|
||||
/* we might be on a docking station */
|
||||
register_hotplug_dock_device(dev->acpi_handle,
|
||||
ata_acpi_dev_notify_dock, dev);
|
||||
@@ -0,0 +1,206 @@
|
||||
From: Myron Stowe <myron.stowe@hp.com>
|
||||
Subject: ACPI: Behave uniquely based on processor declaration definition type
|
||||
References: bnc#440062
|
||||
Patch-Mainline: yes
|
||||
Commit-ID: b26e9286fb438eb78bcdb68b67a3dbb8bc539125
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
|
||||
Associating a Local SAPIC with a processor object is dependent upon the
|
||||
processor object's definition type. CPUs declared as "Processor" should
|
||||
use the Local SAPIC's 'processor_id', and CPUs declared as "Device"
|
||||
should use the 'uid'. Note that for "Processor" declarations, even if a
|
||||
'_UID' child object exists, it has no bearing with respect to mapping
|
||||
Local SAPICs (see section 5.2.11.13 - Local SAPIC Structure; "Advanced
|
||||
Configuration and Power Interface Specification", Revision 3.0b).
|
||||
|
||||
This patch changes the lsapic mapping logic to rely on the distinction of
|
||||
how the processor object was declared - the mapping can't just try both
|
||||
types of matches regardless of declaration type and rely on one failing
|
||||
as is currently being done.
|
||||
|
||||
Signed-off-by: Myron Stowe <myron.stowe@hp.com>
|
||||
Signed-off-by: Len Brown <len.brown@intel.com>
|
||||
|
||||
---
|
||||
drivers/acpi/processor_core.c | 78 +++++++++++++++++++++++-------------------
|
||||
1 file changed, 44 insertions(+), 34 deletions(-)
|
||||
|
||||
Index: linux-2.6.27/drivers/acpi/processor_core.c
|
||||
===================================================================
|
||||
--- linux-2.6.27.orig/drivers/acpi/processor_core.c
|
||||
+++ linux-2.6.27/drivers/acpi/processor_core.c
|
||||
@@ -410,7 +410,7 @@ static int acpi_processor_remove_fs(stru
|
||||
/* Use the acpiid in MADT to map cpus in case of SMP */
|
||||
|
||||
#ifndef CONFIG_SMP
|
||||
-static int get_cpu_id(acpi_handle handle, u32 acpi_id) {return -1;}
|
||||
+static int get_cpu_id(acpi_handle handle, int type, u32 acpi_id) { return -1; }
|
||||
#else
|
||||
|
||||
static struct acpi_table_madt *madt;
|
||||
@@ -429,27 +429,35 @@ static int map_lapic_id(struct acpi_subt
|
||||
}
|
||||
|
||||
static int map_lsapic_id(struct acpi_subtable_header *entry,
|
||||
- u32 acpi_id, int *apic_id)
|
||||
+ int device_declaration, u32 acpi_id, int *apic_id)
|
||||
{
|
||||
struct acpi_madt_local_sapic *lsapic =
|
||||
(struct acpi_madt_local_sapic *)entry;
|
||||
+ u32 tmp = (lsapic->id << 8) | lsapic->eid;
|
||||
+
|
||||
/* Only check enabled APICs*/
|
||||
- if (lsapic->lapic_flags & ACPI_MADT_ENABLED) {
|
||||
- /* First check against id */
|
||||
- if (lsapic->processor_id == acpi_id) {
|
||||
- *apic_id = (lsapic->id << 8) | lsapic->eid;
|
||||
- return 1;
|
||||
- /* Check against optional uid */
|
||||
- } else if (entry->length >= 16 &&
|
||||
- lsapic->uid == acpi_id) {
|
||||
- *apic_id = lsapic->uid;
|
||||
- return 1;
|
||||
- }
|
||||
- }
|
||||
+ if (!(lsapic->lapic_flags & ACPI_MADT_ENABLED))
|
||||
+ return 0;
|
||||
+
|
||||
+ /* Device statement declaration type */
|
||||
+ if (device_declaration) {
|
||||
+ if (entry->length < 16)
|
||||
+ printk(KERN_ERR PREFIX
|
||||
+ "Invalid LSAPIC with Device type processor (SAPIC ID %#x)\n",
|
||||
+ tmp);
|
||||
+ else if (lsapic->uid == acpi_id)
|
||||
+ goto found;
|
||||
+ /* Processor statement declaration type */
|
||||
+ } else if (lsapic->processor_id == acpi_id)
|
||||
+ goto found;
|
||||
+
|
||||
return 0;
|
||||
+found:
|
||||
+ *apic_id = tmp;
|
||||
+ return 1;
|
||||
}
|
||||
|
||||
-static int map_madt_entry(u32 acpi_id)
|
||||
+static int map_madt_entry(int type, u32 acpi_id)
|
||||
{
|
||||
unsigned long madt_end, entry;
|
||||
int apic_id = -1;
|
||||
@@ -470,7 +478,7 @@ static int map_madt_entry(u32 acpi_id)
|
||||
if (map_lapic_id(header, acpi_id, &apic_id))
|
||||
break;
|
||||
} else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) {
|
||||
- if (map_lsapic_id(header, acpi_id, &apic_id))
|
||||
+ if (map_lsapic_id(header, type, acpi_id, &apic_id))
|
||||
break;
|
||||
}
|
||||
entry += header->length;
|
||||
@@ -478,7 +486,7 @@ static int map_madt_entry(u32 acpi_id)
|
||||
return apic_id;
|
||||
}
|
||||
|
||||
-static int map_mat_entry(acpi_handle handle, u32 acpi_id)
|
||||
+static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id)
|
||||
{
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
@@ -501,7 +509,7 @@ static int map_mat_entry(acpi_handle han
|
||||
if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) {
|
||||
map_lapic_id(header, acpi_id, &apic_id);
|
||||
} else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) {
|
||||
- map_lsapic_id(header, acpi_id, &apic_id);
|
||||
+ map_lsapic_id(header, type, acpi_id, &apic_id);
|
||||
}
|
||||
|
||||
exit:
|
||||
@@ -510,14 +518,14 @@ exit:
|
||||
return apic_id;
|
||||
}
|
||||
|
||||
-static int get_cpu_id(acpi_handle handle, u32 acpi_id)
|
||||
+static int get_cpu_id(acpi_handle handle, int type, u32 acpi_id)
|
||||
{
|
||||
int i;
|
||||
int apic_id = -1;
|
||||
|
||||
- apic_id = map_mat_entry(handle, acpi_id);
|
||||
+ apic_id = map_mat_entry(handle, type, acpi_id);
|
||||
if (apic_id == -1)
|
||||
- apic_id = map_madt_entry(acpi_id);
|
||||
+ apic_id = map_madt_entry(type, acpi_id);
|
||||
if (apic_id == -1)
|
||||
return apic_id;
|
||||
|
||||
@@ -533,15 +541,16 @@ static int get_cpu_id(acpi_handle handle
|
||||
Driver Interface
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
-static int acpi_processor_get_info(struct acpi_processor *pr, unsigned has_uid)
|
||||
+static int acpi_processor_get_info(struct acpi_device *device)
|
||||
{
|
||||
acpi_status status = 0;
|
||||
union acpi_object object = { 0 };
|
||||
struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
|
||||
- int cpu_index;
|
||||
+ struct acpi_processor *pr;
|
||||
+ int cpu_index, device_declaration = 0;
|
||||
static int cpu0_initialized;
|
||||
|
||||
-
|
||||
+ pr = acpi_driver_data(device);
|
||||
if (!pr)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -562,22 +571,23 @@ static int acpi_processor_get_info(struc
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||
"No bus mastering arbitration control\n"));
|
||||
|
||||
- /* Check if it is a Device with HID and UID */
|
||||
- if (has_uid) {
|
||||
+ if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_HID)) {
|
||||
+ /*
|
||||
+ * Declared with "Device" statement; match _UID.
|
||||
+ * Note that we don't handle string _UIDs yet.
|
||||
+ */
|
||||
unsigned long long value;
|
||||
status = acpi_evaluate_integer(pr->handle, METHOD_NAME__UID,
|
||||
NULL, &value);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
- printk(KERN_ERR PREFIX "Evaluating processor _UID\n");
|
||||
+ printk(KERN_ERR PREFIX
|
||||
+ "Evaluating processor _UID [%#x]\n", status);
|
||||
return -ENODEV;
|
||||
}
|
||||
+ device_declaration = 1;
|
||||
pr->acpi_id = value;
|
||||
} else {
|
||||
- /*
|
||||
- * Evalute the processor object. Note that it is common on SMP to
|
||||
- * have the first (boot) processor with a valid PBLK address while
|
||||
- * all others have a NULL address.
|
||||
- */
|
||||
+ /* Declared with "Processor" statement; match ProcessorID */
|
||||
status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_ERR PREFIX "Evaluating processor object\n");
|
||||
@@ -590,7 +600,7 @@ static int acpi_processor_get_info(struc
|
||||
*/
|
||||
pr->acpi_id = object.processor.proc_id;
|
||||
}
|
||||
- cpu_index = get_cpu_id(pr->handle, pr->acpi_id);
|
||||
+ cpu_index = get_cpu_id(pr->handle, device_declaration, pr->acpi_id);
|
||||
|
||||
/* Handle UP system running SMP kernel, with no LAPIC in MADT */
|
||||
if (!cpu0_initialized && (cpu_index == -1) &&
|
||||
@@ -662,7 +672,7 @@ static int __cpuinit acpi_processor_star
|
||||
|
||||
pr = acpi_driver_data(device);
|
||||
|
||||
- result = acpi_processor_get_info(pr, device->flags.unique_id);
|
||||
+ result = acpi_processor_get_info(device);
|
||||
if (result) {
|
||||
/* Processor is physically not present */
|
||||
return 0;
|
||||
@@ -0,0 +1,58 @@
|
||||
From: Myron Stowe <myron.stowe@hp.com>
|
||||
Subject: ACPI: Disambiguate processor declaration type
|
||||
References: bnc#440062
|
||||
Patch-Mainline: yes
|
||||
Commit-ID: ad93a765c1834db031b5bf1c2baf2a50d0462ca4
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
|
||||
Declaring processors in ACPI namespace can be done using either a
|
||||
"Processor" definition or a "Device" definition (see section 8.4 -
|
||||
Declaring Processors; "Advanced Configuration and Power Interface
|
||||
Specification", Revision 3.0b). Currently the two processor
|
||||
declaration types are conflated.
|
||||
|
||||
This patch disambiguates the processor declaration's definition type
|
||||
enabling subsequent code to behave uniquely based explicitly on the
|
||||
declaration's type.
|
||||
|
||||
Signed-off-by: Myron Stowe <myron.stowe@hp.com>
|
||||
Signed-off-by: Len Brown <len.brown@intel.com>
|
||||
|
||||
---
|
||||
drivers/acpi/processor_core.c | 1 +
|
||||
drivers/acpi/scan.c | 2 +-
|
||||
include/acpi/acpi_drivers.h | 1 +
|
||||
3 files changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/acpi/processor_core.c
|
||||
+++ b/drivers/acpi/processor_core.c
|
||||
@@ -89,6 +89,7 @@ static int acpi_processor_handle_eject(s
|
||||
|
||||
|
||||
static const struct acpi_device_id processor_device_ids[] = {
|
||||
+ {ACPI_PROCESSOR_OBJECT_HID, 0},
|
||||
{ACPI_PROCESSOR_HID, 0},
|
||||
{"", 0},
|
||||
};
|
||||
--- a/drivers/acpi/scan.c
|
||||
+++ b/drivers/acpi/scan.c
|
||||
@@ -1002,7 +1002,7 @@ static void acpi_device_set_id(struct ac
|
||||
hid = ACPI_POWER_HID;
|
||||
break;
|
||||
case ACPI_BUS_TYPE_PROCESSOR:
|
||||
- hid = ACPI_PROCESSOR_HID;
|
||||
+ hid = ACPI_PROCESSOR_OBJECT_HID;
|
||||
break;
|
||||
case ACPI_BUS_TYPE_SYSTEM:
|
||||
hid = ACPI_SYSTEM_HID;
|
||||
--- a/include/acpi/acpi_drivers.h
|
||||
+++ b/include/acpi/acpi_drivers.h
|
||||
@@ -41,6 +41,7 @@
|
||||
*/
|
||||
|
||||
#define ACPI_POWER_HID "LNXPOWER"
|
||||
+#define ACPI_PROCESSOR_OBJECT_HID "ACPI_CPU"
|
||||
#define ACPI_PROCESSOR_HID "ACPI0007"
|
||||
#define ACPI_SYSTEM_HID "LNXSYSTM"
|
||||
#define ACPI_THERMAL_HID "LNXTHERM"
|
||||
@@ -0,0 +1,67 @@
|
||||
From: Alexey Starikovskiy <astarikovskiy@suse.de>
|
||||
Subject: ACPI: EC: Don't degrade to poll mode at storm automatically.
|
||||
References: bnc#446142
|
||||
Patch-Mainline: no
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
|
||||
Not all users of semi-broken EC devices want to degrade to poll mode, so
|
||||
give them right to choose.
|
||||
|
||||
Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
|
||||
---
|
||||
|
||||
Documentation/kernel-parameters.txt | 5 +++++
|
||||
drivers/acpi/ec.c | 15 +++++++++++++++
|
||||
2 files changed, 20 insertions(+)
|
||||
|
||||
|
||||
--- a/Documentation/kernel-parameters.txt
|
||||
+++ b/Documentation/kernel-parameters.txt
|
||||
@@ -706,6 +706,11 @@ and is between 256 and 4096 characters.
|
||||
|
||||
eata= [HW,SCSI]
|
||||
|
||||
+ ec_intr= [HW,ACPI] ACPI Embedded Controller interrupt mode
|
||||
+ Format: <int>
|
||||
+ 0: polling mode
|
||||
+ non-0: interrupt mode (default)
|
||||
+
|
||||
edd= [EDD]
|
||||
Format: {"off" | "on" | "skip[mbr]"}
|
||||
|
||||
--- a/drivers/acpi/ec.c
|
||||
+++ b/drivers/acpi/ec.c
|
||||
@@ -121,6 +121,8 @@ static struct acpi_ec {
|
||||
spinlock_t curr_lock;
|
||||
} *boot_ec, *first_ec;
|
||||
|
||||
+int acpi_ec_intr = 1; /* Default is interrupt mode */
|
||||
+
|
||||
/*
|
||||
* Some Asus system have exchanged ECDT data/command IO addresses.
|
||||
*/
|
||||
@@ -902,6 +904,8 @@ static int ec_install_handlers(struct ac
|
||||
&acpi_ec_gpe_handler, ec);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
+ if (!acpi_ec_intr)
|
||||
+ set_bit(EC_FLAGS_NO_GPE, &ec->flags);
|
||||
acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME);
|
||||
acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR);
|
||||
status = acpi_install_address_space_handler(ec->handle,
|
||||
@@ -1095,3 +1099,14 @@ static void __exit acpi_ec_exit(void)
|
||||
return;
|
||||
}
|
||||
#endif /* 0 */
|
||||
+
|
||||
+static int __init acpi_ec_set_intr_mode(char *str)
|
||||
+{
|
||||
+ if (!get_option(&str, &acpi_ec_intr)) {
|
||||
+ acpi_ec_intr = 0;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
+__setup("ec_intr=", acpi_ec_set_intr_mode);
|
||||
@@ -0,0 +1,29 @@
|
||||
From: Myron Stowe <myron.stowe@hp.com>
|
||||
Subject: ACPI: 80 column adherence and spelling fix (no functional change)
|
||||
References: bnc#440062
|
||||
Patch-Mainline: yes
|
||||
Commit-ID: 5b53ed69158eeff115004f246193d07a083445f6
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
|
||||
Signed-off-by: Myron Stowe <myron.stowe@hp.com>
|
||||
Signed-off-by: Len Brown <len.brown@intel.com>
|
||||
|
||||
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
|
||||
index bc332fc..b57b1f0 100644
|
||||
--- a/drivers/acpi/processor_core.c
|
||||
+++ b/drivers/acpi/processor_core.c
|
||||
@@ -595,9 +595,10 @@ static int acpi_processor_get_info(struct acpi_device *device)
|
||||
}
|
||||
|
||||
/*
|
||||
- * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP.
|
||||
- * >>> 'acpi_get_processor_id(acpi_id, &id)' in arch/xxx/acpi.c
|
||||
- */
|
||||
+ * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP.
|
||||
+ * >>> 'acpi_get_processor_id(acpi_id, &id)' in
|
||||
+ * arch/xxx/acpi.c
|
||||
+ */
|
||||
pr->acpi_id = object.processor.proc_id;
|
||||
}
|
||||
cpu_index = get_cpu_id(pr->handle, device_declaration, pr->acpi_id);
|
||||
@@ -0,0 +1,58 @@
|
||||
From: Kurt Garloff <garloff@suse.de>
|
||||
Subject: Use SRAT table rev to use 8bit or 16/32bit PXM fields (ia64)
|
||||
References: bnc#503038
|
||||
|
||||
In SRAT v1, we had 8bit proximity domain (PXM) fields; SRAT v2 provides
|
||||
32bits for these. The new fields were reserved before.
|
||||
According to the ACPI spec, the OS must disregrard reserved fields.
|
||||
|
||||
ia64 did handle the PXM fields almost consistently, but depending on
|
||||
sgi's sn2 platform. This patch leaves the sn2 logic in, but does also
|
||||
use 16/32 bits for PXM if the SRAT has rev 2 or higher.
|
||||
|
||||
The patch also adds __init to the two pxm accessor functions, as they
|
||||
access __initdata now and are called from an __init function only anyway.
|
||||
|
||||
Note that the code only uses 16 bits for the PXM field in the processor
|
||||
proximity field; the patch does not address this as 16 bits are more than
|
||||
enough.
|
||||
|
||||
This is patch 3/3.
|
||||
|
||||
Signed-off-by: Kurt Garloff <garloff@suse.de>
|
||||
|
||||
---
|
||||
arch/ia64/kernel/acpi.c | 10 ++++++----
|
||||
1 file changed, 6 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/arch/ia64/kernel/acpi.c
|
||||
+++ b/arch/ia64/kernel/acpi.c
|
||||
@@ -430,22 +430,24 @@ static u32 __devinitdata pxm_flag[PXM_FL
|
||||
static struct acpi_table_slit __initdata *slit_table;
|
||||
cpumask_t early_cpu_possible_map = CPU_MASK_NONE;
|
||||
|
||||
-static int get_processor_proximity_domain(struct acpi_srat_cpu_affinity *pa)
|
||||
+static int __init
|
||||
+get_processor_proximity_domain(struct acpi_srat_cpu_affinity *pa)
|
||||
{
|
||||
int pxm;
|
||||
|
||||
pxm = pa->proximity_domain_lo;
|
||||
- if (ia64_platform_is("sn2"))
|
||||
+ if (ia64_platform_is("sn2") || acpi_srat_revision >= 2)
|
||||
pxm += pa->proximity_domain_hi[0] << 8;
|
||||
return pxm;
|
||||
}
|
||||
|
||||
-static int get_memory_proximity_domain(struct acpi_srat_mem_affinity *ma)
|
||||
+static int __init
|
||||
+get_memory_proximity_domain(struct acpi_srat_mem_affinity *ma)
|
||||
{
|
||||
int pxm;
|
||||
|
||||
pxm = ma->proximity_domain;
|
||||
- if (!ia64_platform_is("sn2"))
|
||||
+ if (!ia64_platform_is("sn2") && acpi_srat_revision <= 1)
|
||||
pxm &= 0xff;
|
||||
|
||||
return pxm;
|
||||
@@ -0,0 +1,51 @@
|
||||
From: Kurt Garloff <garloff@suse.de>
|
||||
Subject: Store SRAT table revision
|
||||
References: bnc#503038
|
||||
|
||||
In SRAT v1, we had 8bit proximity domain (PXM) fields; SRAT v2 provides
|
||||
32bits for these. The new fields were reserved before.
|
||||
According to the ACPI spec, the OS must disregrard reserved fields.
|
||||
In order to know whether or not, we must know what version the SRAT
|
||||
table has.
|
||||
|
||||
This patch stores the SRAT table revision for later consumption
|
||||
by arch specific __init functions.
|
||||
|
||||
This is patch 1/3.
|
||||
|
||||
Signed-off-by: Kurt Garloff <garloff@suse.de>
|
||||
|
||||
---
|
||||
drivers/acpi/numa.c | 3 +++
|
||||
include/acpi/acpi_numa.h | 1 +
|
||||
2 files changed, 4 insertions(+)
|
||||
|
||||
--- a/drivers/acpi/numa.c
|
||||
+++ b/drivers/acpi/numa.c
|
||||
@@ -43,6 +43,8 @@ static int pxm_to_node_map[MAX_PXM_DOMAI
|
||||
static int node_to_pxm_map[MAX_NUMNODES]
|
||||
= { [0 ... MAX_NUMNODES - 1] = PXM_INVAL };
|
||||
|
||||
+unsigned char acpi_srat_revision __initdata;
|
||||
+
|
||||
int pxm_to_node(int pxm)
|
||||
{
|
||||
if (pxm < 0)
|
||||
@@ -225,6 +227,7 @@ static int __init acpi_parse_srat(struct
|
||||
return -EINVAL;
|
||||
|
||||
srat = (struct acpi_table_srat *)table;
|
||||
+ acpi_srat_revision = srat->header.revision;
|
||||
|
||||
return 0;
|
||||
}
|
||||
--- a/include/acpi/acpi_numa.h
|
||||
+++ b/include/acpi/acpi_numa.h
|
||||
@@ -15,6 +15,7 @@ extern int pxm_to_node(int);
|
||||
extern int node_to_pxm(int);
|
||||
extern void __acpi_map_pxm_to_node(int, int);
|
||||
extern int acpi_map_pxm_to_node(int);
|
||||
+extern unsigned char acpi_srat_revision;
|
||||
|
||||
#endif /* CONFIG_ACPI_NUMA */
|
||||
#endif /* __ACP_NUMA_H */
|
||||
@@ -0,0 +1,41 @@
|
||||
From: Kurt Garloff <garloff@suse.de>
|
||||
Subject: Use SRAT table rev to use 8bit or 32bit PXM fields (x86-64)
|
||||
References: bnc#503038
|
||||
|
||||
In SRAT v1, we had 8bit proximity domain (PXM) fields; SRAT v2 provides
|
||||
32bits for these. The new fields were reserved before.
|
||||
According to the ACPI spec, the OS must disregrard reserved fields.
|
||||
|
||||
x86-64 was rather inconsistent prior to this patch; it used 8 bits
|
||||
for the pxm field in cpu_affinity, but 32 bits in mem_affinity.
|
||||
This patch makes it consistent: Either use 8 bits consistently (SRAT
|
||||
rev 1 or lower) or 32 bits (SRAT rev 2 or higher).
|
||||
|
||||
This is patch 2/3.
|
||||
|
||||
Signed-off-by: Kurt Garloff <garloff@suse.de>
|
||||
|
||||
---
|
||||
arch/x86/mm/srat_64.c | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
--- a/arch/x86/mm/srat_64.c
|
||||
+++ b/arch/x86/mm/srat_64.c
|
||||
@@ -133,6 +133,8 @@ acpi_numa_processor_affinity_init(struct
|
||||
if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0)
|
||||
return;
|
||||
pxm = pa->proximity_domain_lo;
|
||||
+ if (acpi_srat_revision >= 2)
|
||||
+ pxm |= *((unsigned int*)pa->proximity_domain_hi) << 8;
|
||||
node = setup_node(pxm);
|
||||
if (node < 0) {
|
||||
printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm);
|
||||
@@ -243,6 +245,8 @@ acpi_numa_memory_affinity_init(struct ac
|
||||
start = ma->base_address;
|
||||
end = start + ma->length;
|
||||
pxm = ma->proximity_domain;
|
||||
+ if (acpi_srat_revision <= 1)
|
||||
+ pxm &= 0xff;
|
||||
node = setup_node(pxm);
|
||||
if (node < 0) {
|
||||
printk(KERN_ERR "SRAT: Too many proximity domains.\n");
|
||||
@@ -0,0 +1,104 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: Avoid critical temp shutdowns on specific ThinkPad T4x(p) and R40
|
||||
References: https://bugzilla.novell.com/show_bug.cgi?id=333043
|
||||
|
||||
---
|
||||
drivers/acpi/thermal.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 73 insertions(+)
|
||||
|
||||
--- a/drivers/acpi/thermal.c
|
||||
+++ b/drivers/acpi/thermal.c
|
||||
@@ -42,6 +42,7 @@
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/reboot.h>
|
||||
+#include <linux/dmi.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
@@ -1640,6 +1641,66 @@ static int acpi_thermal_get_info(struct
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static struct dmi_system_id thermal_psv_dmi_table[] = {
|
||||
+ {
|
||||
+ .ident = "IBM ThinkPad T41",
|
||||
+ .matches = {
|
||||
+ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
+ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad T41"),
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .ident = "IBM ThinkPad T42",
|
||||
+ .matches = {
|
||||
+ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
+ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad T42"),
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .ident = "IBM ThinkPad T43",
|
||||
+ .matches = {
|
||||
+ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
+ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad T43"),
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .ident = "IBM ThinkPad T41p",
|
||||
+ .matches = {
|
||||
+ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
+ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad T41p"),
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .ident = "IBM ThinkPad T42p",
|
||||
+ .matches = {
|
||||
+ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
+ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad T42p"),
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .ident = "IBM ThinkPad T43p",
|
||||
+ .matches = {
|
||||
+ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
+ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad T43p"),
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .ident = "IBM ThinkPad R40",
|
||||
+ .matches = {
|
||||
+ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
+ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad R40"),
|
||||
+ },
|
||||
+ },
|
||||
+ {
|
||||
+ .ident = "IBM ThinkPad R50p",
|
||||
+ .matches = {
|
||||
+ DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
+ DMI_MATCH(DMI_PRODUCT_VERSION,"ThinkPad R50p"),
|
||||
+ },
|
||||
+ },
|
||||
+ {},
|
||||
+};
|
||||
+
|
||||
static int acpi_thermal_add(struct acpi_device *device)
|
||||
{
|
||||
int result = 0;
|
||||
@@ -1670,6 +1731,18 @@ static int acpi_thermal_add(struct acpi_
|
||||
if (result)
|
||||
goto free_memory;
|
||||
|
||||
+ if (dmi_check_system(thermal_psv_dmi_table)) {
|
||||
+ if (tz->trips.passive.flags.valid &&
|
||||
+ tz->trips.passive.temperature > CELSIUS_TO_KELVIN(85)) {
|
||||
+ printk (KERN_INFO "Adjust passive trip point from %lu"
|
||||
+ " to %lu\n",
|
||||
+ KELVIN_TO_CELSIUS(tz->trips.passive.temperature),
|
||||
+ KELVIN_TO_CELSIUS(tz->trips.passive.temperature - 150));
|
||||
+ tz->trips.passive.temperature -= 150;
|
||||
+ acpi_thermal_set_polling(tz, 5);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
result = acpi_thermal_add_fs(device);
|
||||
if (result)
|
||||
goto unregister_thermal_zone;
|
||||
@@ -0,0 +1,96 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: Introduce acpi_root_table=rsdt boot param and dmi list to force rsdt
|
||||
Patch-mainline: not yet
|
||||
References: http://bugzilla.kernel.org/show_bug.cgi?id=8246
|
||||
|
||||
This one is part of a patch series:
|
||||
acpi_thinkpad_introduce_acpi_root_table_boot_param.patch
|
||||
acpi_thinkpad_introduce_acpica_rsdt_global_variable.patch
|
||||
acpi_thinkpad_remove_R40e_c-state_blacklist.patch
|
||||
|
||||
Blacklist R40e, R51e and T40, T40p, T41, T41p, T42, T42p, R50 and R50p
|
||||
ThinkPads to use the RSDT instead of the XSDT.
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Tested-by: Mark Doughty <me@markdoughty.co.uk>
|
||||
CC: Yakui Zhao <yakui.zhao@intel.com>
|
||||
|
||||
---
|
||||
Documentation/kernel-parameters.txt | 5 ++++
|
||||
drivers/acpi/tables.c | 37 ++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 42 insertions(+)
|
||||
|
||||
--- a/Documentation/kernel-parameters.txt
|
||||
+++ b/Documentation/kernel-parameters.txt
|
||||
@@ -237,6 +237,11 @@ and is between 256 and 4096 characters.
|
||||
to assume that this machine's pmtimer latches its value
|
||||
and always returns good values.
|
||||
|
||||
+ acpi_root_table= [X86,ACPI]
|
||||
+ { rsdt }
|
||||
+ rsdt: Take RSDT address for fetching
|
||||
+ ACPI tables (instead of XSDT)
|
||||
+
|
||||
agp= [AGP]
|
||||
{ off | try_unsupported }
|
||||
off: disable AGP support
|
||||
--- a/drivers/acpi/tables.c
|
||||
+++ b/drivers/acpi/tables.c
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bootmem.h>
|
||||
+#include <linux/dmi.h>
|
||||
|
||||
#define PREFIX "ACPI: "
|
||||
|
||||
@@ -282,6 +283,37 @@ static void __init check_multiple_madt(v
|
||||
return;
|
||||
}
|
||||
|
||||
+static struct dmi_system_id __initdata acpi_rsdt_dmi_table[] = {
|
||||
+ {
|
||||
+ .ident = "ThinkPad ", /* R40e, broken C-states */
|
||||
+ .matches = {
|
||||
+ DMI_MATCH(DMI_BIOS_VENDOR, "IBM"),
|
||||
+ DMI_MATCH(DMI_BIOS_VERSION, "1SET")},
|
||||
+ },
|
||||
+ {
|
||||
+ .ident = "ThinkPad ", /* R50e, slow booting */
|
||||
+ .matches = {
|
||||
+ DMI_MATCH(DMI_BIOS_VENDOR, "IBM"),
|
||||
+ DMI_MATCH(DMI_BIOS_VERSION, "1WET")},
|
||||
+ },
|
||||
+ {
|
||||
+ .ident = "ThinkPad ", /* T40, T40p, T41, T41p, T42, T42p
|
||||
+ R50, R50p */
|
||||
+ .matches = {
|
||||
+ DMI_MATCH(DMI_BIOS_VENDOR, "IBM"),
|
||||
+ DMI_MATCH(DMI_BIOS_VERSION, "1RET")},
|
||||
+ },
|
||||
+ {}
|
||||
+};
|
||||
+
|
||||
+static int __init acpi_force_rsdt(char *opt)
|
||||
+{
|
||||
+ if (!strcmp(opt, "rsdt"))
|
||||
+ acpi_gbl_force_rsdt = 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+early_param("acpi_root_table", acpi_force_rsdt);
|
||||
+
|
||||
/*
|
||||
* acpi_table_init()
|
||||
*
|
||||
@@ -295,6 +327,11 @@ int __init acpi_table_init(void)
|
||||
{
|
||||
acpi_status status;
|
||||
|
||||
+ if (dmi_check_system(acpi_rsdt_dmi_table))
|
||||
+ acpi_gbl_force_rsdt = 1;
|
||||
+ if (acpi_gbl_force_rsdt)
|
||||
+ printk(KERN_INFO "Using RSDT as ACPI root table\n");
|
||||
+
|
||||
status = acpi_initialize_tables(initial_tables, ACPI_MAX_TABLES, 0);
|
||||
if (ACPI_FAILURE(status))
|
||||
return 1;
|
||||
@@ -0,0 +1,53 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: ACPICA: Add acpi_gbl_force_rsdt variable
|
||||
Patch-mainline: not yet
|
||||
References: http://bugzilla.kernel.org/show_bug.cgi?id=8246
|
||||
|
||||
This one is part of a patch series:
|
||||
acpi_thinkpad_introduce_acpi_root_table_boot_param.patch
|
||||
acpi_thinkpad_introduce_acpica_rsdt_global_variable.patch
|
||||
acpi_thinkpad_remove_R40e_c-state_blacklist.patch
|
||||
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Tested-by: Mark Doughty <me@markdoughty.co.uk>
|
||||
|
||||
|
||||
---
|
||||
drivers/acpi/tables/tbutils.c | 3 ++-
|
||||
drivers/acpi/utilities/utglobal.c | 1 +
|
||||
include/acpi/acglobal.h | 1 +
|
||||
3 files changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/acpi/tables/tbutils.c
|
||||
+++ b/drivers/acpi/tables/tbutils.c
|
||||
@@ -420,7 +420,8 @@ acpi_tb_parse_root_table(acpi_physical_a
|
||||
|
||||
/* Differentiate between RSDT and XSDT root tables */
|
||||
|
||||
- if (rsdp->revision > 1 && rsdp->xsdt_physical_address) {
|
||||
+ if (rsdp->revision > 1 && rsdp->xsdt_physical_address
|
||||
+ && !acpi_gbl_force_rsdt) {
|
||||
/*
|
||||
* Root table is an XSDT (64-bit physical addresses). We must use the
|
||||
* XSDT if the revision is > 1 and the XSDT pointer is present, as per
|
||||
--- a/drivers/acpi/utilities/utglobal.c
|
||||
+++ b/drivers/acpi/utilities/utglobal.c
|
||||
@@ -76,6 +76,7 @@ u8 acpi_gbl_method_executing = FALSE;
|
||||
/* System flags */
|
||||
|
||||
u32 acpi_gbl_startup_flags = 0;
|
||||
+int acpi_gbl_force_rsdt = 0;
|
||||
|
||||
/* System starts uninitialized */
|
||||
|
||||
--- a/include/acpi/acglobal.h
|
||||
+++ b/include/acpi/acglobal.h
|
||||
@@ -246,6 +246,7 @@ ACPI_EXTERN u8 acpi_gbl_system_awake_and
|
||||
|
||||
extern u8 acpi_gbl_shutdown;
|
||||
extern u32 acpi_gbl_startup_flags;
|
||||
+extern int acpi_gbl_force_rsdt;
|
||||
extern const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT];
|
||||
extern const char *acpi_gbl_highest_dstate_names[4];
|
||||
extern const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES];
|
||||
@@ -0,0 +1,98 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: Remove R40e c-state blacklist
|
||||
Patch-mainline: not yet
|
||||
References: http://bugzilla.kernel.org/show_bug.cgi?id=8246
|
||||
|
||||
This one is part of a patch series:
|
||||
acpi_thinkpad_introduce_acpi_root_table_boot_param.patch
|
||||
acpi_thinkpad_introduce_acpica_rsdt_global_variable.patch
|
||||
acpi_thinkpad_remove_R40e_c-state_blacklist.patch
|
||||
|
||||
|
||||
The FADT pointed to through XSDT is wrong on this (and similar)
|
||||
machines.
|
||||
The HW addresses to switch C-states are coming from the FADT.
|
||||
When using the FADT pointed to in the RSDT the info is correct.
|
||||
Previous patches blacklist this machine to use the right FADT and
|
||||
C-states finally work fine.
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Tested-by: Mark Doughty <me@markdoughty.co.uk>
|
||||
|
||||
Remove R40e c-state blacklist
|
||||
|
||||
The FADT pointed to through XSDT is wrong on this (and similar) machines.
|
||||
The HW addresses to switch C-states are coming from the FADT.
|
||||
When using the FADT pointed to in the RSDT the info is correct.
|
||||
Previous patches blacklist this machine to use the right FADT and
|
||||
C-states finally work fine.
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Tested-by: Mark Doughty <me@markdoughty.co.uk>
|
||||
CC: Yakui Zhao <yakui.zhao@intel.com>
|
||||
|
||||
|
||||
---
|
||||
drivers/acpi/processor_idle.c | 51 ------------------------------------------
|
||||
1 file changed, 51 deletions(-)
|
||||
|
||||
--- a/drivers/acpi/processor_idle.c
|
||||
+++ b/drivers/acpi/processor_idle.c
|
||||
@@ -127,57 +127,6 @@ static int set_max_cstate(const struct d
|
||||
/* Actually this shouldn't be __cpuinitdata, would be better to fix the
|
||||
callers to only run once -AK */
|
||||
static struct dmi_system_id __cpuinitdata processor_power_dmi_table[] = {
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET70WW")}, (void *)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET60WW")}, (void *)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET43WW") }, (void*)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET45WW") }, (void*)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET47WW") }, (void*)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET50WW") }, (void*)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET52WW") }, (void*)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET55WW") }, (void*)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET56WW") }, (void*)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET59WW") }, (void*)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET60WW") }, (void*)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET61WW") }, (void*)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET62WW") }, (void*)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET64WW") }, (void*)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET65WW") }, (void*)1},
|
||||
- { set_max_cstate, "IBM ThinkPad R40e", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"1SET68WW") }, (void*)1},
|
||||
- { set_max_cstate, "Medion 41700", {
|
||||
- DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
|
||||
- DMI_MATCH(DMI_BIOS_VERSION,"R01-A1J")}, (void *)1},
|
||||
{ set_max_cstate, "Clevo 5600D", {
|
||||
DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
|
||||
DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307")},
|
||||
@@ -0,0 +1,56 @@
|
||||
From: Thomas Renninger <trenn@suse.de>
|
||||
Subject: Do not use video backlight switching for Lenovo ThinkPads
|
||||
Patch-Mainline: never
|
||||
|
||||
Mainline will go the IGD driver way which is too new and untested for
|
||||
SLE11.
|
||||
|
||||
---
|
||||
drivers/acpi/video.c | 17 ++++++++++++++---
|
||||
drivers/acpi/video_detect.c | 2 +-
|
||||
2 files changed, 15 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/drivers/acpi/video.c
|
||||
+++ b/drivers/acpi/video.c
|
||||
@@ -731,7 +731,7 @@ static void acpi_video_device_find_cap(s
|
||||
{
|
||||
acpi_handle h_dummy1;
|
||||
u32 max_level = 0;
|
||||
-
|
||||
+ unsigned long acpi_video_support;
|
||||
|
||||
memset(&device->cap, 0, sizeof(device->cap));
|
||||
|
||||
@@ -759,8 +759,19 @@ static void acpi_video_device_find_cap(s
|
||||
device->cap._DSS = 1;
|
||||
}
|
||||
|
||||
- if (acpi_video_backlight_support())
|
||||
- max_level = acpi_video_init_brightness(device);
|
||||
+ acpi_video_support = acpi_video_backlight_support();
|
||||
+ if (acpi_video_support) {
|
||||
+ /*
|
||||
+ * Ugly SLE11 hack to let thinkpad_acpi handle brightness on
|
||||
+ * ThinkPad IGD devices
|
||||
+ */
|
||||
+ if (dmi_name_in_vendors("LENOVO") &&
|
||||
+ (acpi_video_support & ACPI_VIDEO_IGD))
|
||||
+ brightness_switch_enabled = 0;
|
||||
+ else
|
||||
+ max_level = acpi_video_init_brightness(device);
|
||||
+ } else
|
||||
+ brightness_switch_enabled = 0;
|
||||
|
||||
if (device->cap._BCL && device->cap._BCM && max_level > 0) {
|
||||
int result;
|
||||
--- a/drivers/acpi/video_detect.c
|
||||
+++ b/drivers/acpi/video_detect.c
|
||||
@@ -217,7 +217,7 @@ int acpi_video_backlight_support(void)
|
||||
return 1;
|
||||
|
||||
/* Then go the default way */
|
||||
- return acpi_video_support & ACPI_VIDEO_BACKLIGHT;
|
||||
+ return acpi_video_support & (ACPI_VIDEO_BACKLIGHT | ACPI_VIDEO_IGD);
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_video_backlight_support);
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
From: Bob Moore <robert.moore@intel.com>
|
||||
Subject: ACPICA: x2APIC support: changes for MADT and SRAT ACPI tables
|
||||
References: fate 303948 and fate 303984
|
||||
Patch-Mainline: in 2.6.28
|
||||
Commit-ID: 1d7cc03049f7c9c5cced9208a39316c5245ef314
|
||||
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
|
||||
Support for the x2APIC. There are 2 new subtables for the MADT and
|
||||
one new subtable for the SRAT. Includes disassembler and acpisrc
|
||||
support. Data from the Intel 64 Architecture x2APIC Specification,
|
||||
June 2008.
|
||||
|
||||
Signed-off-by: Bob Moore <robert.moore@intel.com>
|
||||
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
|
||||
Signed-off-by: Andi Kleen <ak@linux.intel.com>
|
||||
Signed-off-by: Len Brown <len.brown@intel.com>
|
||||
|
||||
diff --git a/include/acpi/acdisasm.h b/include/acpi/acdisasm.h
|
||||
index f53faca..0c1ed38 100644
|
||||
--- a/include/acpi/acdisasm.h
|
||||
+++ b/include/acpi/acdisasm.h
|
||||
@@ -186,6 +186,8 @@ extern struct acpi_dmtable_info acpi_dm_table_info_madt5[];
|
||||
extern struct acpi_dmtable_info acpi_dm_table_info_madt6[];
|
||||
extern struct acpi_dmtable_info acpi_dm_table_info_madt7[];
|
||||
extern struct acpi_dmtable_info acpi_dm_table_info_madt8[];
|
||||
+extern struct acpi_dmtable_info acpi_dm_table_info_madt9[];
|
||||
+extern struct acpi_dmtable_info acpi_dm_table_info_madt10[];
|
||||
extern struct acpi_dmtable_info acpi_dm_table_info_madt_hdr[];
|
||||
extern struct acpi_dmtable_info acpi_dm_table_info_mcfg[];
|
||||
extern struct acpi_dmtable_info acpi_dm_table_info_mcfg0[];
|
||||
@@ -197,8 +199,10 @@ extern struct acpi_dmtable_info acpi_dm_table_info_slit[];
|
||||
extern struct acpi_dmtable_info acpi_dm_table_info_spcr[];
|
||||
extern struct acpi_dmtable_info acpi_dm_table_info_spmi[];
|
||||
extern struct acpi_dmtable_info acpi_dm_table_info_srat[];
|
||||
+extern struct acpi_dmtable_info acpi_dm_table_info_srat_hdr[];
|
||||
extern struct acpi_dmtable_info acpi_dm_table_info_srat0[];
|
||||
extern struct acpi_dmtable_info acpi_dm_table_info_srat1[];
|
||||
+extern struct acpi_dmtable_info acpi_dm_table_info_srat2[];
|
||||
extern struct acpi_dmtable_info acpi_dm_table_info_tcpa[];
|
||||
extern struct acpi_dmtable_info acpi_dm_table_info_wdrt[];
|
||||
|
||||
diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h
|
||||
index d38f9be..63f5b4c 100644
|
||||
--- a/include/acpi/actbl1.h
|
||||
+++ b/include/acpi/actbl1.h
|
||||
@@ -908,7 +908,9 @@ enum acpi_madt_type {
|
||||
ACPI_MADT_TYPE_IO_SAPIC = 6,
|
||||
ACPI_MADT_TYPE_LOCAL_SAPIC = 7,
|
||||
ACPI_MADT_TYPE_INTERRUPT_SOURCE = 8,
|
||||
- ACPI_MADT_TYPE_RESERVED = 9 /* 9 and greater are reserved */
|
||||
+ ACPI_MADT_TYPE_LOCAL_X2APIC = 9,
|
||||
+ ACPI_MADT_TYPE_LOCAL_X2APIC_NMI = 10,
|
||||
+ ACPI_MADT_TYPE_RESERVED = 11 /* 11 and greater are reserved */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1009,6 +1011,26 @@ struct acpi_madt_interrupt_source {
|
||||
|
||||
#define ACPI_MADT_CPEI_OVERRIDE (1)
|
||||
|
||||
+/* 9: Processor Local X2_APIC (07/2008) */
|
||||
+
|
||||
+struct acpi_madt_local_x2apic {
|
||||
+ struct acpi_subtable_header header;
|
||||
+ u16 reserved; /* Reserved - must be zero */
|
||||
+ u32 local_apic_id; /* Processor X2_APIC ID */
|
||||
+ u32 lapic_flags;
|
||||
+ u32 uid; /* Extended X2_APIC processor ID */
|
||||
+};
|
||||
+
|
||||
+/* 10: Local X2APIC NMI (07/2008) */
|
||||
+
|
||||
+struct acpi_madt_local_x2apic_nmi {
|
||||
+ struct acpi_subtable_header header;
|
||||
+ u16 inti_flags;
|
||||
+ u32 uid; /* Processor X2_APIC ID */
|
||||
+ u8 lint; /* LINTn to which NMI is connected */
|
||||
+ u8 reserved[3];
|
||||
+};
|
||||
+
|
||||
/*
|
||||
* Common flags fields for MADT subtables
|
||||
*/
|
||||
@@ -1150,10 +1172,15 @@ struct acpi_table_srat {
|
||||
enum acpi_srat_type {
|
||||
ACPI_SRAT_TYPE_CPU_AFFINITY = 0,
|
||||
ACPI_SRAT_TYPE_MEMORY_AFFINITY = 1,
|
||||
- ACPI_SRAT_TYPE_RESERVED = 2
|
||||
+ ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY = 2,
|
||||
+ ACPI_SRAT_TYPE_RESERVED = 3 /* 3 and greater are reserved */
|
||||
};
|
||||
|
||||
-/* SRAT sub-tables */
|
||||
+/*
|
||||
+ * SRAT Sub-tables, correspond to Type in struct acpi_subtable_header
|
||||
+ */
|
||||
+
|
||||
+/* 0: Processor Local APIC/SAPIC Affinity */
|
||||
|
||||
struct acpi_srat_cpu_affinity {
|
||||
struct acpi_subtable_header header;
|
||||
@@ -1165,9 +1192,7 @@ struct acpi_srat_cpu_affinity {
|
||||
u32 reserved; /* Reserved, must be zero */
|
||||
};
|
||||
|
||||
-/* Flags */
|
||||
-
|
||||
-#define ACPI_SRAT_CPU_ENABLED (1) /* 00: Use affinity structure */
|
||||
+/* 1: Memory Affinity */
|
||||
|
||||
struct acpi_srat_mem_affinity {
|
||||
struct acpi_subtable_header header;
|
||||
@@ -1186,6 +1211,20 @@ struct acpi_srat_mem_affinity {
|
||||
#define ACPI_SRAT_MEM_HOT_PLUGGABLE (1<<1) /* 01: Memory region is hot pluggable */
|
||||
#define ACPI_SRAT_MEM_NON_VOLATILE (1<<2) /* 02: Memory region is non-volatile */
|
||||
|
||||
+/* 2: Processor Local X2_APIC Affinity (07/2008) */
|
||||
+
|
||||
+struct acpi_srat_x2apic_cpu_affinity {
|
||||
+ struct acpi_subtable_header header;
|
||||
+ u16 reserved; /* Reserved, must be zero */
|
||||
+ u32 proximity_domain;
|
||||
+ u32 apic_id;
|
||||
+ u32 flags;
|
||||
+};
|
||||
+
|
||||
+/* Flags for struct acpi_srat_cpu_affinity and struct acpi_srat_x2apic_cpu_affinity */
|
||||
+
|
||||
+#define ACPI_SRAT_CPU_ENABLED (1) /* 00: Use affinity structure */
|
||||
+
|
||||
/*******************************************************************************
|
||||
*
|
||||
* TCPA - Trusted Computing Platform Alliance table
|
||||
@@ -0,0 +1,335 @@
|
||||
From: Jean Delvare <jdelvare@suse.de>
|
||||
Subject: Check for ACPI resource conflicts in hwmon drivers.
|
||||
Patch-mainline: 2.6.24-rc3-mm1
|
||||
|
||||
I've included all Super-I/O and PCI drivers.
|
||||
|
||||
I've voluntarily left out:
|
||||
* Laptop specific and vendor-specific drivers: if they conflicted
|
||||
on any system, this would pretty much mean that they conflict on all
|
||||
systems, and we would know by now.
|
||||
* Legacy ISA drivers (lm78 and w83781d): they only support chips found
|
||||
on old designs were ACPI either wasn't supported or didn't deal with
|
||||
thermal management.
|
||||
* Drivers accessing the I/O resources indirectly (e.g. through SMBus):
|
||||
the check will be added where it belongs, i.e. in the bus drivers.
|
||||
|
||||
Signed-off-by: Jean Delvare <jdelvare@suse.de>
|
||||
Signed-off-by: Thomas Renninger <trenn@suse.de>
|
||||
Cc: "Mark M. Hoffman" <mhoffman@lightlink.com>
|
||||
Cc: Len Brown <lenb@kernel.org>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
---
|
||||
|
||||
drivers/hwmon/dme1737.c | 5 +++++
|
||||
drivers/hwmon/f71805f.c | 5 +++++
|
||||
drivers/hwmon/f71882fg.c | 5 +++++
|
||||
drivers/hwmon/it87.c | 5 +++++
|
||||
drivers/hwmon/pc87360.c | 6 ++++++
|
||||
drivers/hwmon/pc87427.c | 5 +++++
|
||||
drivers/hwmon/sis5595.c | 5 +++++
|
||||
drivers/hwmon/smsc47b397.c | 5 +++++
|
||||
drivers/hwmon/smsc47m1.c | 5 +++++
|
||||
drivers/hwmon/via686a.c | 5 +++++
|
||||
drivers/hwmon/vt1211.c | 5 +++++
|
||||
drivers/hwmon/vt8231.c | 5 +++++
|
||||
drivers/hwmon/w83627ehf.c | 6 ++++++
|
||||
drivers/hwmon/w83627hf.c | 5 +++++
|
||||
14 files changed, 72 insertions(+)
|
||||
|
||||
--- a/drivers/hwmon/dme1737.c
|
||||
+++ b/drivers/hwmon/dme1737.c
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/* ISA device, if found */
|
||||
@@ -2372,6 +2373,10 @@ static int __init dme1737_isa_device_add
|
||||
};
|
||||
int err;
|
||||
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ goto exit;
|
||||
+
|
||||
if (!(pdev = platform_device_alloc("dme1737", addr))) {
|
||||
printk(KERN_ERR "dme1737: Failed to allocate device.\n");
|
||||
err = -ENOMEM;
|
||||
--- a/drivers/hwmon/f71805f.c
|
||||
+++ b/drivers/hwmon/f71805f.c
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/ioport.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static unsigned short force_id;
|
||||
@@ -1455,6 +1456,10 @@ static int __init f71805f_device_add(uns
|
||||
}
|
||||
|
||||
res.name = pdev->name;
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ goto exit_device_put;
|
||||
+
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR DRVNAME ": Device resource addition failed "
|
||||
--- a/drivers/hwmon/f71882fg.c
|
||||
+++ b/drivers/hwmon/f71882fg.c
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRVNAME "f71882fg"
|
||||
@@ -892,6 +893,10 @@ static int __init f71882fg_device_add(un
|
||||
return -ENOMEM;
|
||||
|
||||
res.name = f71882fg_pdev->name;
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
err = platform_device_add_resources(f71882fg_pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR DRVNAME ": Device resource addition failed\n");
|
||||
--- a/drivers/hwmon/it87.c
|
||||
+++ b/drivers/hwmon/it87.c
|
||||
@@ -48,6 +48,7 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/dmi.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRVNAME "it87"
|
||||
@@ -1535,6 +1536,10 @@ static int __init it87_device_add(unsign
|
||||
};
|
||||
int err;
|
||||
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ goto exit;
|
||||
+
|
||||
pdev = platform_device_alloc(DRVNAME, address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
--- a/drivers/hwmon/pc87360.c
|
||||
+++ b/drivers/hwmon/pc87360.c
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static u8 devid;
|
||||
@@ -1425,6 +1426,11 @@ static int __init pc87360_device_add(uns
|
||||
continue;
|
||||
res.start = extra_isa[i];
|
||||
res.end = extra_isa[i] + PC87360_EXTENT - 1;
|
||||
+
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ goto exit_device_put;
|
||||
+
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR "pc87360: Device resource[%d] "
|
||||
--- a/drivers/hwmon/pc87427.c
|
||||
+++ b/drivers/hwmon/pc87427.c
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/ioport.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static unsigned short force_id;
|
||||
@@ -524,6 +525,10 @@ static int __init pc87427_device_add(uns
|
||||
};
|
||||
int err;
|
||||
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ goto exit;
|
||||
+
|
||||
pdev = platform_device_alloc(DRVNAME, address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
--- a/drivers/hwmon/sis5595.c
|
||||
+++ b/drivers/hwmon/sis5595.c
|
||||
@@ -62,6 +62,7 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
|
||||
@@ -727,6 +728,10 @@ static int __devinit sis5595_device_add(
|
||||
};
|
||||
int err;
|
||||
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ goto exit;
|
||||
+
|
||||
pdev = platform_device_alloc("sis5595", address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
--- a/drivers/hwmon/smsc47b397.c
|
||||
+++ b/drivers/hwmon/smsc47b397.c
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static unsigned short force_id;
|
||||
@@ -303,6 +304,10 @@ static int __init smsc47b397_device_add(
|
||||
};
|
||||
int err;
|
||||
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ goto exit;
|
||||
+
|
||||
pdev = platform_device_alloc(DRVNAME, address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
--- a/drivers/hwmon/smsc47m1.c
|
||||
+++ b/drivers/hwmon/smsc47m1.c
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static unsigned short force_id;
|
||||
@@ -716,6 +717,10 @@ static int __init smsc47m1_device_add(un
|
||||
};
|
||||
int err;
|
||||
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ goto exit;
|
||||
+
|
||||
pdev = platform_device_alloc(DRVNAME, address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
--- a/drivers/hwmon/via686a.c
|
||||
+++ b/drivers/hwmon/via686a.c
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
|
||||
@@ -783,6 +784,10 @@ static int __devinit via686a_device_add(
|
||||
};
|
||||
int err;
|
||||
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ goto exit;
|
||||
+
|
||||
pdev = platform_device_alloc("via686a", address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
--- a/drivers/hwmon/vt1211.c
|
||||
+++ b/drivers/hwmon/vt1211.c
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/ioport.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static int uch_config = -1;
|
||||
@@ -1259,6 +1260,10 @@ static int __init vt1211_device_add(unsi
|
||||
}
|
||||
|
||||
res.name = pdev->name;
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ goto EXIT;
|
||||
+
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR DRVNAME ": Device resource addition failed "
|
||||
--- a/drivers/hwmon/vt8231.c
|
||||
+++ b/drivers/hwmon/vt8231.c
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static int force_addr;
|
||||
@@ -894,6 +895,10 @@ static int __devinit vt8231_device_add(u
|
||||
};
|
||||
int err;
|
||||
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ goto exit;
|
||||
+
|
||||
pdev = platform_device_alloc("vt8231", address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
--- a/drivers/hwmon/w83627ehf.c
|
||||
+++ b/drivers/hwmon/w83627ehf.c
|
||||
@@ -48,6 +48,7 @@
|
||||
#include <linux/hwmon-vid.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
#include "lm75.h"
|
||||
|
||||
@@ -1544,6 +1545,11 @@ static int __init sensors_w83627ehf_init
|
||||
res.start = address + IOREGION_OFFSET;
|
||||
res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
|
||||
res.flags = IORESOURCE_IO;
|
||||
+
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ goto exit;
|
||||
+
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
printk(KERN_ERR DRVNAME ": Device resource addition failed "
|
||||
--- a/drivers/hwmon/w83627hf.c
|
||||
+++ b/drivers/hwmon/w83627hf.c
|
||||
@@ -50,6 +50,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/ioport.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <asm/io.h>
|
||||
#include "lm75.h"
|
||||
|
||||
@@ -1793,6 +1794,10 @@ static int __init w83627hf_device_add(un
|
||||
};
|
||||
int err;
|
||||
|
||||
+ err = acpi_check_resource_conflict(&res);
|
||||
+ if (err)
|
||||
+ goto exit;
|
||||
+
|
||||
pdev = platform_device_alloc(DRVNAME, address);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
@@ -0,0 +1,38 @@
|
||||
From: schwab@suse.de
|
||||
Subject: Fix msr check in compat_sys_swapcontext
|
||||
References: 441498
|
||||
|
||||
The new context may not be 16-byte aligned, so the real address of the
|
||||
mcontext structure should be read from the uc_regs pointer instead of
|
||||
directly using the (unaligned) uc_mcontext field.
|
||||
|
||||
Signed-off-by: Andreas Schwab <schwab@suse.de>
|
||||
|
||||
---
|
||||
---
|
||||
arch/powerpc/kernel/signal_32.c | 14 +++++++++++---
|
||||
1 file changed, 11 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/kernel/signal_32.c
|
||||
+++ b/arch/powerpc/kernel/signal_32.c
|
||||
@@ -941,9 +941,17 @@ long sys_swapcontext(struct ucontext __u
|
||||
#ifdef CONFIG_PPC64
|
||||
unsigned long new_msr = 0;
|
||||
|
||||
- if (new_ctx &&
|
||||
- get_user(new_msr, &new_ctx->uc_mcontext.mc_gregs[PT_MSR]))
|
||||
- return -EFAULT;
|
||||
+ if (new_ctx) {
|
||||
+ struct mcontext __user *mcp;
|
||||
+ u32 cmcp;
|
||||
+
|
||||
+ /* Get pointer to the real mcontext. */
|
||||
+ if (get_user(cmcp, &new_ctx->uc_regs))
|
||||
+ return -EFAULT;
|
||||
+ mcp = (struct mcontext __user *)(u64)cmcp;
|
||||
+ if (get_user(new_msr, &mcp->mc_gregs[PT_MSR]))
|
||||
+ return -EFAULT;
|
||||
+ }
|
||||
/*
|
||||
* Check that the context is not smaller than the original
|
||||
* size (with VMX but without VSX)
|
||||
43
src/patches/suse-2.6.27.31/patches.arch/disable-apic-error
Normal file
43
src/patches/suse-2.6.27.31/patches.arch/disable-apic-error
Normal file
@@ -0,0 +1,43 @@
|
||||
From: ak@suse.de
|
||||
Subject: Disable APIC error printing
|
||||
References: 156576
|
||||
Patch-mainline: not planned
|
||||
|
||||
ATI chipsets currently do this all the time. It's probably
|
||||
mostly harmless
|
||||
|
||||
We keep it enabled in mainline to make sure the (hardware?) bug
|
||||
is tracked down, but don't bother in the distribution kernels
|
||||
with it.
|
||||
|
||||
---
|
||||
arch/x86/kernel/apic_32.c | 2 ++
|
||||
arch/x86/kernel/apic_64.c | 2 ++
|
||||
2 files changed, 4 insertions(+)
|
||||
|
||||
--- a/arch/x86/kernel/apic_32.c
|
||||
+++ b/arch/x86/kernel/apic_32.c
|
||||
@@ -1316,8 +1316,10 @@ void smp_error_interrupt(struct pt_regs
|
||||
6: Received illegal vector
|
||||
7: Illegal register address
|
||||
*/
|
||||
+#if 0
|
||||
printk(KERN_DEBUG "APIC error on CPU%d: %02lx(%02lx)\n",
|
||||
smp_processor_id(), v , v1);
|
||||
+#endif
|
||||
irq_exit();
|
||||
}
|
||||
|
||||
--- a/arch/x86/kernel/apic_64.c
|
||||
+++ b/arch/x86/kernel/apic_64.c
|
||||
@@ -998,8 +998,10 @@ asmlinkage void smp_error_interrupt(void
|
||||
6: Received illegal vector
|
||||
7: Illegal register address
|
||||
*/
|
||||
+#if 0
|
||||
printk(KERN_DEBUG "APIC error on CPU%d: %02x(%02x)\n",
|
||||
smp_processor_id(), v , v1);
|
||||
+#endif
|
||||
irq_exit();
|
||||
}
|
||||
|
||||
30
src/patches/suse-2.6.27.31/patches.arch/ia64-cpu_disable-fix
Normal file
30
src/patches/suse-2.6.27.31/patches.arch/ia64-cpu_disable-fix
Normal file
@@ -0,0 +1,30 @@
|
||||
From: Alex Chiang <achiang@hp.com>
|
||||
Subject: IA64: first clear CPU from online map, then fixup IRQs.
|
||||
References: bnc#386714
|
||||
|
||||
Acked-by: Raymund Will <rw@suse.de>
|
||||
|
||||
---
|
||||
arch/ia64/kernel/smpboot.c | 8 +++-----
|
||||
1 file changed, 3 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/arch/ia64/kernel/smpboot.c
|
||||
+++ b/arch/ia64/kernel/smpboot.c
|
||||
@@ -741,14 +741,12 @@ int __cpu_disable(void)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
- if (migrate_platform_irqs(cpu)) {
|
||||
- cpu_set(cpu, cpu_online_map);
|
||||
- return (-EBUSY);
|
||||
- }
|
||||
+ if (migrate_platform_irqs(cpu))
|
||||
+ return -EBUSY;
|
||||
|
||||
remove_siblinginfo(cpu);
|
||||
- fixup_irqs();
|
||||
cpu_clear(cpu, cpu_online_map);
|
||||
+ fixup_irqs();
|
||||
local_flush_tlb_all();
|
||||
cpu_clear(cpu, cpu_callin_map);
|
||||
return 0;
|
||||
598
src/patches/suse-2.6.27.31/patches.arch/ia64-page-migration
Normal file
598
src/patches/suse-2.6.27.31/patches.arch/ia64-page-migration
Normal file
@@ -0,0 +1,598 @@
|
||||
From: Russ Anderson <rja@sgi.com>
|
||||
Subject: ia64: Call migration code on correctable errors v8
|
||||
References: 415829
|
||||
Acked-by: schwab@suse.de
|
||||
|
||||
Migrate data off pages with correctable memory errors. This patch is the
|
||||
ia64 specific piece. It connects the CPE handler to the page migration
|
||||
code. It is implemented as a kernel loadable module, similar to the mca
|
||||
recovery code (mca_recovery.ko). This allows the feature to be turned off
|
||||
by uninstalling the module.
|
||||
|
||||
|
||||
Signed-off-by: Russ Anderson <rja@sgi.com>
|
||||
|
||||
---
|
||||
arch/ia64/Kconfig | 9
|
||||
arch/ia64/include/asm/mca.h | 6
|
||||
arch/ia64/include/asm/page.h | 1
|
||||
arch/ia64/kernel/Makefile | 1
|
||||
arch/ia64/kernel/cpe_migrate.c | 434 +++++++++++++++++++++++++++++++++++++++++
|
||||
arch/ia64/kernel/mca.c | 37 +++
|
||||
6 files changed, 487 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/ia64/include/asm/mca.h
|
||||
+++ b/arch/ia64/include/asm/mca.h
|
||||
@@ -137,6 +137,7 @@ extern unsigned long __per_cpu_mca[NR_CP
|
||||
|
||||
extern int cpe_vector;
|
||||
extern int ia64_cpe_irq;
|
||||
+extern int cpe_poll_enabled;
|
||||
extern void ia64_mca_init(void);
|
||||
extern void ia64_mca_cpu_init(void *);
|
||||
extern void ia64_os_mca_dispatch(void);
|
||||
@@ -150,10 +151,15 @@ extern void ia64_slave_init_handler(void
|
||||
extern void ia64_mca_cmc_vector_setup(void);
|
||||
extern int ia64_reg_MCA_extension(int (*fn)(void *, struct ia64_sal_os_state *));
|
||||
extern void ia64_unreg_MCA_extension(void);
|
||||
+extern int ia64_reg_CE_extension(int (*fn)(void *));
|
||||
+extern void ia64_unreg_CE_extension(void);
|
||||
extern u64 ia64_get_rnat(u64 *);
|
||||
extern void ia64_mca_printk(const char * fmt, ...)
|
||||
__attribute__ ((format (printf, 1, 2)));
|
||||
|
||||
+extern struct list_head badpagelist;
|
||||
+extern unsigned int total_badpages;
|
||||
+
|
||||
struct ia64_mca_notify_die {
|
||||
struct ia64_sal_os_state *sos;
|
||||
int *monarch_cpu;
|
||||
--- a/arch/ia64/include/asm/page.h
|
||||
+++ b/arch/ia64/include/asm/page.h
|
||||
@@ -121,6 +121,7 @@ extern unsigned long max_low_pfn;
|
||||
#endif
|
||||
|
||||
#define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT)
|
||||
+#define phys_to_page(kaddr) (pfn_to_page(kaddr >> PAGE_SHIFT))
|
||||
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
|
||||
#define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)
|
||||
|
||||
--- a/arch/ia64/Kconfig
|
||||
+++ b/arch/ia64/Kconfig
|
||||
@@ -470,6 +470,15 @@ config COMPAT_FOR_U64_ALIGNMENT
|
||||
config IA64_MCA_RECOVERY
|
||||
tristate "MCA recovery from errors other than TLB."
|
||||
|
||||
+config IA64_CPE_MIGRATE
|
||||
+ tristate "Migrate data off pages with correctable errors"
|
||||
+ default m
|
||||
+ help
|
||||
+ Migrate data off pages with correctable memory errors. Selecting
|
||||
+ Y will build this functionality into the kernel. Selecting M will
|
||||
+ build this functionality as a kernel loadable module. Installing
|
||||
+ the module will turn on the functionality.
|
||||
+
|
||||
config PERFMON
|
||||
bool "Performance monitor support"
|
||||
help
|
||||
--- /dev/null
|
||||
+++ b/arch/ia64/kernel/cpe_migrate.c
|
||||
@@ -0,0 +1,434 @@
|
||||
+/*
|
||||
+ * File: cpe_migrate.c
|
||||
+ * Purpose: Migrate data from physical pages with excessive correctable
|
||||
+ * errors to new physical pages. Keep the old pages on a discard
|
||||
+ * list.
|
||||
+ *
|
||||
+ * Copyright (C) 2008 SGI - Silicon Graphics Inc.
|
||||
+ * Copyright (C) 2008 Russ Anderson <rja@sgi.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/sysdev.h>
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/sched.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/smp.h>
|
||||
+#include <linux/workqueue.h>
|
||||
+#include <linux/mm.h>
|
||||
+#include <linux/swap.h>
|
||||
+#include <linux/vmalloc.h>
|
||||
+#include <linux/migrate.h>
|
||||
+#include <linux/page-isolation.h>
|
||||
+#include <linux/memcontrol.h>
|
||||
+#include <linux/kobject.h>
|
||||
+
|
||||
+#include <asm/page.h>
|
||||
+#include <asm/system.h>
|
||||
+#include <asm/sn/sn_cpuid.h>
|
||||
+#include <asm/mca.h>
|
||||
+
|
||||
+#define BADRAM_BASENAME "badram"
|
||||
+#define CE_HISTORY_LENGTH 30
|
||||
+
|
||||
+struct cpe_info {
|
||||
+ u64 paddr;
|
||||
+ u16 node;
|
||||
+};
|
||||
+static struct cpe_info cpe[CE_HISTORY_LENGTH];
|
||||
+
|
||||
+static int cpe_polling_enabled = 1;
|
||||
+static int cpe_head;
|
||||
+static int cpe_tail;
|
||||
+static int work_scheduled;
|
||||
+static int mstat_cannot_isolate;
|
||||
+static int mstat_failed_to_discard;
|
||||
+static int mstat_already_marked;
|
||||
+static int mstat_already_on_list;
|
||||
+
|
||||
+DEFINE_SPINLOCK(cpe_migrate_lock);
|
||||
+
|
||||
+static void
|
||||
+get_physical_address(void *buffer, u64 *paddr, u16 *node)
|
||||
+{
|
||||
+ sal_log_record_header_t *rh;
|
||||
+ sal_log_mem_dev_err_info_t *mdei;
|
||||
+ ia64_err_rec_t *err_rec;
|
||||
+ sal_log_platform_err_info_t *plat_err;
|
||||
+ efi_guid_t guid;
|
||||
+
|
||||
+ err_rec = buffer;
|
||||
+ rh = &err_rec->sal_elog_header;
|
||||
+ *paddr = 0;
|
||||
+ *node = 0;
|
||||
+
|
||||
+ /*
|
||||
+ * Make sure it is a corrected error.
|
||||
+ */
|
||||
+ if (rh->severity != sal_log_severity_corrected)
|
||||
+ return;
|
||||
+
|
||||
+ plat_err = (sal_log_platform_err_info_t *)&err_rec->proc_err;
|
||||
+
|
||||
+ guid = plat_err->mem_dev_err.header.guid;
|
||||
+ if (efi_guidcmp(guid, SAL_PLAT_MEM_DEV_ERR_SECT_GUID) == 0) {
|
||||
+ /*
|
||||
+ * Memory cpe
|
||||
+ */
|
||||
+ mdei = &plat_err->mem_dev_err;
|
||||
+ if (mdei->valid.oem_data) {
|
||||
+ if (mdei->valid.physical_addr)
|
||||
+ *paddr = mdei->physical_addr;
|
||||
+
|
||||
+ if (mdei->valid.node) {
|
||||
+ if (ia64_platform_is("sn2"))
|
||||
+ *node = nasid_to_cnodeid(mdei->node);
|
||||
+ else
|
||||
+ *node = mdei->node;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static struct page *
|
||||
+alloc_migrate_page(struct page *ignored, unsigned long node, int **x)
|
||||
+{
|
||||
+
|
||||
+ return alloc_pages_node(node, GFP_HIGHUSER_MOVABLE, 0);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+validate_paddr_page(u64 paddr)
|
||||
+{
|
||||
+ struct page *page;
|
||||
+
|
||||
+ if (!paddr)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!ia64_phys_addr_valid(paddr))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!pfn_valid(paddr >> PAGE_SHIFT))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ page = phys_to_page(paddr);
|
||||
+ if (PageMemError(page))
|
||||
+ mstat_already_marked++;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+ia64_mca_cpe_move_page(u64 paddr, u32 node)
|
||||
+{
|
||||
+ LIST_HEAD(pagelist);
|
||||
+ struct page *page;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = validate_paddr_page(paddr);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ /*
|
||||
+ * convert physical address to page number
|
||||
+ */
|
||||
+ page = phys_to_page(paddr);
|
||||
+
|
||||
+ migrate_prep();
|
||||
+ ret = isolate_lru_page(page, &pagelist);
|
||||
+ if (ret) {
|
||||
+ mstat_cannot_isolate++;
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ SetPageMemError(page); /* Mark the page as bad */
|
||||
+ ret = migrate_pages(&pagelist, alloc_migrate_page, node);
|
||||
+ if (ret == 0) {
|
||||
+ total_badpages++;
|
||||
+ list_add_tail(&page->lru, &badpagelist);
|
||||
+ } else {
|
||||
+ mstat_failed_to_discard++;
|
||||
+ /*
|
||||
+ * The page failed to migrate and is not on the bad page list.
|
||||
+ * Clearing the error bit will allow another attempt to migrate
|
||||
+ * if it gets another correctable error.
|
||||
+ */
|
||||
+ ClearPageMemError(page);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * ia64_mca_cpe_migrate
|
||||
+ * The worker that does the actual migration. It pulls a
|
||||
+ * physical address off the list and calls the migration code.
|
||||
+ */
|
||||
+static void
|
||||
+ia64_mca_cpe_migrate(struct work_struct *unused)
|
||||
+{
|
||||
+ int ret;
|
||||
+ u64 paddr;
|
||||
+ u16 node;
|
||||
+
|
||||
+ do {
|
||||
+ paddr = cpe[cpe_tail].paddr;
|
||||
+ if (paddr) {
|
||||
+ /*
|
||||
+ * There is a valid entry that needs processing.
|
||||
+ */
|
||||
+ node = cpe[cpe_tail].node;
|
||||
+
|
||||
+ ret = ia64_mca_cpe_move_page(paddr, node);
|
||||
+ if (ret <= 0)
|
||||
+ /*
|
||||
+ * Even though the return status is negative,
|
||||
+ * clear the entry. If the same address has
|
||||
+ * another CPE it will be re-added to the list.
|
||||
+ */
|
||||
+ cpe[cpe_tail].paddr = 0;
|
||||
+
|
||||
+ }
|
||||
+ if (++cpe_tail >= CE_HISTORY_LENGTH)
|
||||
+ cpe_tail = 0;
|
||||
+
|
||||
+ } while (cpe_tail != cpe_head);
|
||||
+ work_scheduled = 0;
|
||||
+}
|
||||
+
|
||||
+static DECLARE_WORK(cpe_enable_work, ia64_mca_cpe_migrate);
|
||||
+DEFINE_SPINLOCK(cpe_list_lock);
|
||||
+
|
||||
+/*
|
||||
+ * cpe_setup_migrate
|
||||
+ * Get the physical address out of the CPE record, add it
|
||||
+ * to the list of addresses to migrate (if not already on),
|
||||
+ * and schedule the back end worker task. This is called
|
||||
+ * in interrupt context so cannot directly call the migration
|
||||
+ * code.
|
||||
+ *
|
||||
+ * Inputs
|
||||
+ * rec The CPE record
|
||||
+ * Outputs
|
||||
+ * 1 on Success, -1 on failure
|
||||
+ */
|
||||
+static int
|
||||
+cpe_setup_migrate(void *rec)
|
||||
+{
|
||||
+ u64 paddr;
|
||||
+ u16 node;
|
||||
+ /* int head, tail; */
|
||||
+ int i, ret;
|
||||
+
|
||||
+ if (!rec)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ get_physical_address(rec, &paddr, &node);
|
||||
+ ret = validate_paddr_page(paddr);
|
||||
+ if (ret < 0)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if ((cpe_head != cpe_tail) || (cpe[cpe_head].paddr != 0))
|
||||
+ /*
|
||||
+ * List not empty
|
||||
+ */
|
||||
+ for (i = 0; i < CE_HISTORY_LENGTH; i++) {
|
||||
+ if (PAGE_ALIGN(cpe[i].paddr) == PAGE_ALIGN(paddr)) {
|
||||
+ mstat_already_on_list++;
|
||||
+ return 1; /* already on the list */
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!spin_trylock(&cpe_list_lock)) {
|
||||
+ /*
|
||||
+ * Someone else has the lock. To avoid spinning in interrupt
|
||||
+ * handler context, bail.
|
||||
+ */
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ if (cpe[cpe_head].paddr == 0) {
|
||||
+ cpe[cpe_head].node = node;
|
||||
+ cpe[cpe_head].paddr = paddr;
|
||||
+
|
||||
+ if (++cpe_head >= CE_HISTORY_LENGTH)
|
||||
+ cpe_head = 0;
|
||||
+ }
|
||||
+ spin_unlock(&cpe_list_lock);
|
||||
+
|
||||
+ if (!work_scheduled) {
|
||||
+ work_scheduled = 1;
|
||||
+ schedule_work(&cpe_enable_work);
|
||||
+ }
|
||||
+
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * =============================================================================
|
||||
+ */
|
||||
+
|
||||
+/*
|
||||
+ * free_one_bad_page
|
||||
+ * Free one page from the list of bad pages.
|
||||
+ */
|
||||
+static int
|
||||
+free_one_bad_page(unsigned long paddr)
|
||||
+{
|
||||
+ LIST_HEAD(pagelist);
|
||||
+ struct page *page, *page2, *target;
|
||||
+
|
||||
+ /*
|
||||
+ * Verify page address
|
||||
+ */
|
||||
+ target = phys_to_page(paddr);
|
||||
+ list_for_each_entry_safe(page, page2, &badpagelist, lru) {
|
||||
+ if (page != target)
|
||||
+ continue;
|
||||
+
|
||||
+ ClearPageMemError(page); /* Mark the page as good */
|
||||
+ total_badpages--;
|
||||
+ list_move_tail(&page->lru, &pagelist);
|
||||
+ putback_lru_pages(&pagelist);
|
||||
+ break;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * free_all_bad_pages
|
||||
+ * Free all of the pages on the bad pages list.
|
||||
+ */
|
||||
+static int
|
||||
+free_all_bad_pages(void)
|
||||
+{
|
||||
+ struct page *page, *page2;
|
||||
+
|
||||
+ list_for_each_entry_safe(page, page2, &badpagelist, lru) {
|
||||
+ ClearPageMemError(page); /* Mark the page as good */
|
||||
+ total_badpages--;
|
||||
+ }
|
||||
+ putback_lru_pages(&badpagelist);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#define OPT_LEN 16
|
||||
+
|
||||
+static ssize_t
|
||||
+badpage_store(struct kobject *kobj,
|
||||
+ struct kobj_attribute *attr, const char *buf, size_t count)
|
||||
+{
|
||||
+ char optstr[OPT_LEN];
|
||||
+ unsigned long opt;
|
||||
+ int len = OPT_LEN;
|
||||
+ int err;
|
||||
+
|
||||
+ if (count < len)
|
||||
+ len = count;
|
||||
+
|
||||
+ strlcpy(optstr, buf, len);
|
||||
+
|
||||
+ err = strict_strtoul(optstr, 16, &opt);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ if (opt == 0)
|
||||
+ free_all_bad_pages();
|
||||
+ else
|
||||
+ free_one_bad_page(opt);
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * badpage_show
|
||||
+ * Display the number, size, and addresses of all the pages on the
|
||||
+ * bad page list.
|
||||
+ *
|
||||
+ * Note that sysfs provides buf of PAGE_SIZE length. bufend tracks
|
||||
+ * the remaining space in buf to avoid overflowing.
|
||||
+ */
|
||||
+static ssize_t
|
||||
+badpage_show(struct kobject *kobj,
|
||||
+ struct kobj_attribute *attr, char *buf)
|
||||
+
|
||||
+{
|
||||
+ struct page *page, *page2;
|
||||
+ int i = 0, cnt = 0;
|
||||
+ char *bufend = buf + PAGE_SIZE;
|
||||
+
|
||||
+ cnt = snprintf(buf, bufend - (buf + cnt),
|
||||
+ "Memory marked bad: %d kB\n"
|
||||
+ "Pages marked bad: %d\n"
|
||||
+ "Unable to isolate on LRU: %d\n"
|
||||
+ "Unable to migrate: %d\n"
|
||||
+ "Already marked bad: %d\n"
|
||||
+ "Already on list: %d\n"
|
||||
+ "List of bad physical pages\n",
|
||||
+ total_badpages << (PAGE_SHIFT - 10), total_badpages,
|
||||
+ mstat_cannot_isolate, mstat_failed_to_discard,
|
||||
+ mstat_already_marked, mstat_already_on_list
|
||||
+ );
|
||||
+
|
||||
+ list_for_each_entry_safe(page, page2, &badpagelist, lru) {
|
||||
+ if (bufend - (buf + cnt) < 20)
|
||||
+ break; /* Avoid overflowing the buffer */
|
||||
+ cnt += snprintf(buf + cnt, bufend - (buf + cnt),
|
||||
+ " 0x%011lx", page_to_phys(page));
|
||||
+ if (!(++i % 5))
|
||||
+ cnt += snprintf(buf + cnt, bufend - (buf + cnt), "\n");
|
||||
+ }
|
||||
+ cnt += snprintf(buf + cnt, bufend - (buf + cnt), "\n");
|
||||
+
|
||||
+ return cnt;
|
||||
+}
|
||||
+
|
||||
+static struct kobj_attribute badram_attr = {
|
||||
+ .attr = {
|
||||
+ .name = "badram",
|
||||
+ .mode = S_IWUSR | S_IRUGO,
|
||||
+ },
|
||||
+ .show = badpage_show,
|
||||
+ .store = badpage_store,
|
||||
+};
|
||||
+
|
||||
+static int __init
|
||||
+cpe_migrate_external_handler_init(void)
|
||||
+{
|
||||
+ int error;
|
||||
+
|
||||
+ error = sysfs_create_file(kernel_kobj, &badram_attr.attr);
|
||||
+ if (error)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /*
|
||||
+ * register external ce handler
|
||||
+ */
|
||||
+ if (ia64_reg_CE_extension(cpe_setup_migrate)) {
|
||||
+ printk(KERN_ERR "ia64_reg_CE_extension failed.\n");
|
||||
+ return -EFAULT;
|
||||
+ }
|
||||
+ cpe_poll_enabled = cpe_polling_enabled;
|
||||
+
|
||||
+ printk(KERN_INFO "Registered badram Driver\n");
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void __exit
|
||||
+cpe_migrate_external_handler_exit(void)
|
||||
+{
|
||||
+ /* unregister external mca handlers */
|
||||
+ ia64_unreg_CE_extension();
|
||||
+
|
||||
+ sysfs_remove_file(kernel_kobj, &badram_attr.attr);
|
||||
+}
|
||||
+
|
||||
+module_init(cpe_migrate_external_handler_init);
|
||||
+module_exit(cpe_migrate_external_handler_exit);
|
||||
+
|
||||
+module_param(cpe_polling_enabled, int, 0644);
|
||||
+MODULE_PARM_DESC(cpe_polling_enabled,
|
||||
+ "Enable polling with migration");
|
||||
+
|
||||
+MODULE_AUTHOR("Russ Anderson <rja@sgi.com>");
|
||||
+MODULE_DESCRIPTION("ia64 Corrected Error page migration driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
--- a/arch/ia64/kernel/Makefile
|
||||
+++ b/arch/ia64/kernel/Makefile
|
||||
@@ -27,6 +27,7 @@ obj-$(CONFIG_PERFMON) += perfmon_defaul
|
||||
obj-$(CONFIG_IA64_CYCLONE) += cyclone.o
|
||||
obj-$(CONFIG_CPU_FREQ) += cpufreq/
|
||||
obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o
|
||||
+obj-$(CONFIG_IA64_CPE_MIGRATE) += cpe_migrate.o
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o jprobes.o
|
||||
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o crash.o
|
||||
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
|
||||
--- a/arch/ia64/kernel/mca.c
|
||||
+++ b/arch/ia64/kernel/mca.c
|
||||
@@ -68,6 +68,9 @@
|
||||
*
|
||||
* 2007-04-27 Russ Anderson <rja@sgi.com>
|
||||
* Support multiple cpus going through OS_MCA in the same event.
|
||||
+ *
|
||||
+ * 2008-04-22 Russ Anderson <rja@sgi.com>
|
||||
+ * Migrate data off pages with correctable memory errors.
|
||||
*/
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/types.h>
|
||||
@@ -163,7 +166,14 @@ static int cmc_polling_enabled = 1;
|
||||
* but encounters problems retrieving CPE logs. This should only be
|
||||
* necessary for debugging.
|
||||
*/
|
||||
-static int cpe_poll_enabled = 1;
|
||||
+int cpe_poll_enabled = 1;
|
||||
+EXPORT_SYMBOL(cpe_poll_enabled);
|
||||
+
|
||||
+unsigned int total_badpages;
|
||||
+EXPORT_SYMBOL(total_badpages);
|
||||
+
|
||||
+LIST_HEAD(badpagelist);
|
||||
+EXPORT_SYMBOL(badpagelist);
|
||||
|
||||
extern void salinfo_log_wakeup(int type, u8 *buffer, u64 size, int irqsafe);
|
||||
|
||||
@@ -523,6 +533,28 @@ int mca_recover_range(unsigned long addr
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mca_recover_range);
|
||||
|
||||
+/* Function pointer to Corrected Error memory migration driver */
|
||||
+int (*ia64_mca_ce_extension)(void *);
|
||||
+
|
||||
+int
|
||||
+ia64_reg_CE_extension(int (*fn)(void *))
|
||||
+{
|
||||
+ if (ia64_mca_ce_extension)
|
||||
+ return 1;
|
||||
+
|
||||
+ ia64_mca_ce_extension = fn;
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(ia64_reg_CE_extension);
|
||||
+
|
||||
+void
|
||||
+ia64_unreg_CE_extension(void)
|
||||
+{
|
||||
+ if (ia64_mca_ce_extension)
|
||||
+ ia64_mca_ce_extension = NULL;
|
||||
+}
|
||||
+EXPORT_SYMBOL(ia64_unreg_CE_extension);
|
||||
+
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
int cpe_vector = -1;
|
||||
@@ -534,6 +566,7 @@ ia64_mca_cpe_int_handler (int cpe_irq, v
|
||||
static unsigned long cpe_history[CPE_HISTORY_LENGTH];
|
||||
static int index;
|
||||
static DEFINE_SPINLOCK(cpe_history_lock);
|
||||
+ int recover;
|
||||
|
||||
IA64_MCA_DEBUG("%s: received interrupt vector = %#x on CPU %d\n",
|
||||
__func__, cpe_irq, smp_processor_id());
|
||||
@@ -580,6 +613,8 @@ ia64_mca_cpe_int_handler (int cpe_irq, v
|
||||
out:
|
||||
/* Get the CPE error record and log it */
|
||||
ia64_mca_log_sal_error_record(SAL_INFO_TYPE_CPE);
|
||||
+ recover = (ia64_mca_ce_extension && ia64_mca_ce_extension(
|
||||
+ IA64_LOG_CURR_BUFFER(SAL_INFO_TYPE_CPE)));
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
161
src/patches/suse-2.6.27.31/patches.arch/ia64-page-migration.fix
Normal file
161
src/patches/suse-2.6.27.31/patches.arch/ia64-page-migration.fix
Normal file
@@ -0,0 +1,161 @@
|
||||
From: Russ Anderson <rja@sgi.com>
|
||||
Subject: ia64: cpe_migrate.ko causes deadlock.
|
||||
References: bnc#464676
|
||||
|
||||
schedule_on_each_cpu() deadlocks when called from an event thread.
|
||||
Change cpe_migrate to use a kthread to avoid the problem.
|
||||
|
||||
Signed-off-by: Russ Anderson <rja@sgi.com>
|
||||
Acked-by: Raymund Will <rw@suse.de>
|
||||
|
||||
---
|
||||
arch/ia64/kernel/cpe_migrate.c | 72 +++++++++++++++++++++++++++++++----------
|
||||
1 file changed, 56 insertions(+), 16 deletions(-)
|
||||
|
||||
Index: linux/arch/ia64/kernel/cpe_migrate.c
|
||||
===================================================================
|
||||
--- linux.orig/arch/ia64/kernel/cpe_migrate.c 2009-01-09 11:37:47.130269369 -0600
|
||||
+++ linux/arch/ia64/kernel/cpe_migrate.c 2009-01-09 11:44:43.658280930 -0600
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <linux/page-isolation.h>
|
||||
#include <linux/memcontrol.h>
|
||||
#include <linux/kobject.h>
|
||||
+#include <linux/kthread.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/system.h>
|
||||
@@ -40,12 +41,15 @@ static struct cpe_info cpe[CE_HISTORY_LE
|
||||
static int cpe_polling_enabled = 1;
|
||||
static int cpe_head;
|
||||
static int cpe_tail;
|
||||
-static int work_scheduled;
|
||||
static int mstat_cannot_isolate;
|
||||
static int mstat_failed_to_discard;
|
||||
static int mstat_already_marked;
|
||||
static int mstat_already_on_list;
|
||||
|
||||
+/* IRQ handler notifies this wait queue on receipt of an IRQ */
|
||||
+DECLARE_WAIT_QUEUE_HEAD(cpe_activate_IRQ_wq);
|
||||
+static DECLARE_COMPLETION(kthread_cpe_migrated_exited);
|
||||
+int cpe_active;
|
||||
DEFINE_SPINLOCK(cpe_migrate_lock);
|
||||
|
||||
static void
|
||||
@@ -159,12 +163,12 @@ ia64_mca_cpe_move_page(u64 paddr, u32 no
|
||||
}
|
||||
|
||||
/*
|
||||
- * ia64_mca_cpe_migrate
|
||||
- * The worker that does the actual migration. It pulls a
|
||||
- * physical address off the list and calls the migration code.
|
||||
+ * cpe_process_queue
|
||||
+ * Pulls the physical address off the list and calls the migration code.
|
||||
+ * Will process all the addresses on the list.
|
||||
*/
|
||||
-static void
|
||||
-ia64_mca_cpe_migrate(struct work_struct *unused)
|
||||
+void
|
||||
+cpe_process_queue(void)
|
||||
{
|
||||
int ret;
|
||||
u64 paddr;
|
||||
@@ -192,10 +196,36 @@ ia64_mca_cpe_migrate(struct work_struct
|
||||
cpe_tail = 0;
|
||||
|
||||
} while (cpe_tail != cpe_head);
|
||||
- work_scheduled = 0;
|
||||
+ return;
|
||||
+}
|
||||
+
|
||||
+inline int
|
||||
+cpe_list_empty(void)
|
||||
+{
|
||||
+ return (cpe_head == cpe_tail) && (!cpe[cpe_head].paddr);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * kthread_cpe_migrate
|
||||
+ * kthread_cpe_migrate is created at module load time and lives
|
||||
+ * until the module is removed. When not active, it will sleep.
|
||||
+ */
|
||||
+static int
|
||||
+kthread_cpe_migrate(void *ignore)
|
||||
+{
|
||||
+ while (cpe_active) {
|
||||
+ /*
|
||||
+ * wait for work
|
||||
+ */
|
||||
+ (void)wait_event_interruptible(cpe_activate_IRQ_wq,
|
||||
+ (!cpe_list_empty() ||
|
||||
+ !cpe_active));
|
||||
+ cpe_process_queue(); /* process work */
|
||||
+ }
|
||||
+ complete(&kthread_cpe_migrated_exited);
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
-static DECLARE_WORK(cpe_enable_work, ia64_mca_cpe_migrate);
|
||||
DEFINE_SPINLOCK(cpe_list_lock);
|
||||
|
||||
/*
|
||||
@@ -227,10 +257,7 @@ cpe_setup_migrate(void *rec)
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
- if ((cpe_head != cpe_tail) || (cpe[cpe_head].paddr != 0))
|
||||
- /*
|
||||
- * List not empty
|
||||
- */
|
||||
+ if (!cpe_list_empty())
|
||||
for (i = 0; i < CE_HISTORY_LENGTH; i++) {
|
||||
if (PAGE_ALIGN(cpe[i].paddr) == PAGE_ALIGN(paddr)) {
|
||||
mstat_already_on_list++;
|
||||
@@ -255,10 +282,7 @@ cpe_setup_migrate(void *rec)
|
||||
}
|
||||
spin_unlock(&cpe_list_lock);
|
||||
|
||||
- if (!work_scheduled) {
|
||||
- work_scheduled = 1;
|
||||
- schedule_work(&cpe_enable_work);
|
||||
- }
|
||||
+ wake_up_interruptible(&cpe_activate_IRQ_wq);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -395,12 +419,23 @@ static int __init
|
||||
cpe_migrate_external_handler_init(void)
|
||||
{
|
||||
int error;
|
||||
+ struct task_struct *kthread;
|
||||
|
||||
error = sysfs_create_file(kernel_kobj, &badram_attr.attr);
|
||||
if (error)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
+ * set up the kthread
|
||||
+ */
|
||||
+ cpe_active = 1;
|
||||
+ kthread = kthread_run(kthread_cpe_migrate, NULL, "cpe_migrate");
|
||||
+ if (IS_ERR(kthread)) {
|
||||
+ complete(&kthread_cpe_migrated_exited);
|
||||
+ return -EFAULT;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
* register external ce handler
|
||||
*/
|
||||
if (ia64_reg_CE_extension(cpe_setup_migrate)) {
|
||||
@@ -418,6 +453,11 @@ cpe_migrate_external_handler_exit(void)
|
||||
{
|
||||
/* unregister external mca handlers */
|
||||
ia64_unreg_CE_extension();
|
||||
+
|
||||
+ /* Stop kthread */
|
||||
+ cpe_active = 0; /* tell kthread_cpe_migrate to exit */
|
||||
+ wake_up_interruptible(&cpe_activate_IRQ_wq);
|
||||
+ wait_for_completion(&kthread_cpe_migrated_exited);
|
||||
|
||||
sysfs_remove_file(kernel_kobj, &badram_attr.attr);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
From: Petr Tesarik <ptesarik@suse.cz>
|
||||
Subject: [ia64] re-enable interrupts when waiting for a rwlock
|
||||
References: bnc#387784
|
||||
Mainline: no
|
||||
|
||||
Re-enable interrupts for _read_lock_irqsave() and _write_lock_irqsave()
|
||||
while waiting for the lock if interrupts were enabled in the caller.
|
||||
|
||||
Signed-off-by: Petr Tesarik <ptesarik@suse.cz>
|
||||
|
||||
---
|
||||
arch/ia64/include/asm/spinlock.h | 49 ++++++++++++++++++++++++++++++++++-----
|
||||
1 file changed, 43 insertions(+), 6 deletions(-)
|
||||
|
||||
--- linux-2.6.26.orig/arch/ia64/include/asm/spinlock.h 2008-09-26 13:02:50.000000000 +0200
|
||||
+++ linux-2.6.26/arch/ia64/include/asm/spinlock.h 2008-09-26 15:54:11.000000000 +0200
|
||||
@@ -120,6 +120,35 @@ do { \
|
||||
#define __raw_read_can_lock(rw) (*(volatile int *)(rw) >= 0)
|
||||
#define __raw_write_can_lock(rw) (*(volatile int *)(rw) == 0)
|
||||
|
||||
+#ifdef ASM_SUPPORTED
|
||||
+#define __raw_read_lock_flags(rw, flags) \
|
||||
+do { \
|
||||
+ __asm__ __volatile__ ( \
|
||||
+ "tbit.nz p6,p0 = %1,%2\n" \
|
||||
+ "br.few 3f\n" \
|
||||
+ "1:\n" \
|
||||
+ "fetchadd4.rel r2 = [%0],-1;;\n" \
|
||||
+ "(p6) ssm psr.i\n" \
|
||||
+ "2:\n" \
|
||||
+ "hint @pause\n" \
|
||||
+ "ld4 r2 = [%0];;\n" \
|
||||
+ "cmp4.lt p7,p0 = r2,r0\n" \
|
||||
+ "(p7) br.cond.spnt.few 2b\n" \
|
||||
+ "(p6) rsm psr.i;;\n" \
|
||||
+ "3:\n" \
|
||||
+ "fetchadd4.acq r2 = [%0],1;;\n" \
|
||||
+ "cmp4.lt p7,p0 = r2,r0\n" \
|
||||
+ "(p7) br.cond.spnt.few 1b\n" \
|
||||
+ :: "r"(rw), "r"(flags), "i"(IA64_PSR_I_BIT) \
|
||||
+ : "p6", "p7", "r2", "memory"); \
|
||||
+} while(0)
|
||||
+
|
||||
+#define __raw_read_lock(lock) __raw_read_lock_flags(lock, 0)
|
||||
+
|
||||
+#else /* !ASM_SUPPORTED */
|
||||
+
|
||||
+#define __raw_read_lock_flags(rw, flags) __raw_read_lock(rw)
|
||||
+
|
||||
#define __raw_read_lock(rw) \
|
||||
do { \
|
||||
raw_rwlock_t *__read_lock_ptr = (rw); \
|
||||
@@ -131,6 +160,8 @@ do { \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
+#endif /* !ASM_SUPPORTED */
|
||||
+
|
||||
#define __raw_read_unlock(rw) \
|
||||
do { \
|
||||
raw_rwlock_t *__read_lock_ptr = (rw); \
|
||||
@@ -138,21 +169,28 @@ do { \
|
||||
} while (0)
|
||||
|
||||
#ifdef ASM_SUPPORTED
|
||||
-#define __raw_write_lock(rw) \
|
||||
+#define __raw_write_lock_flags(rw, flags) \
|
||||
do { \
|
||||
__asm__ __volatile__ ( \
|
||||
"mov ar.ccv = r0\n" \
|
||||
+ "tbit.nz p6,p0 = %1,%2\n" \
|
||||
"dep r29 = -1, r0, 31, 1;;\n" \
|
||||
"1:\n" \
|
||||
+ "(p6) ssm psr.i\n" \
|
||||
+ "2:\n" \
|
||||
"ld4 r2 = [%0];;\n" \
|
||||
"cmp4.eq p0,p7 = r0,r2\n" \
|
||||
- "(p7) br.cond.spnt.few 1b \n" \
|
||||
+ "(p7) br.cond.spnt.few 2b \n" \
|
||||
+ "(p6) rsm psr.i;;\n" \
|
||||
"cmpxchg4.acq r2 = [%0], r29, ar.ccv;;\n" \
|
||||
"cmp4.eq p0,p7 = r0, r2\n" \
|
||||
"(p7) br.cond.spnt.few 1b;;\n" \
|
||||
- :: "r"(rw) : "ar.ccv", "p7", "r2", "r29", "memory"); \
|
||||
+ :: "r"(rw), "r"(flags), "i"(IA64_PSR_I_BIT) \
|
||||
+ : "ar.ccv", "p6", "p7", "r2", "r29", "memory"); \
|
||||
} while(0)
|
||||
|
||||
+#define __raw_write_lock(rw) __raw_write_lock_flags(rw, 0)
|
||||
+
|
||||
#define __raw_write_trylock(rw) \
|
||||
({ \
|
||||
register long result; \
|
||||
@@ -174,6 +212,8 @@ static inline void __raw_write_unlock(ra
|
||||
|
||||
#else /* !ASM_SUPPORTED */
|
||||
|
||||
+#define __raw_write_lock_flags(l, flags) __raw_write_lock(l)
|
||||
+
|
||||
#define __raw_write_lock(l) \
|
||||
({ \
|
||||
__u64 ia64_val, ia64_set_val = ia64_dep_mi(-1, 0, 31, 1); \
|
||||
@@ -213,9 +253,6 @@ static inline int __raw_read_trylock(raw
|
||||
return (u32)ia64_cmpxchg4_acq((__u32 *)(x), new.word, old.word) == old.word;
|
||||
}
|
||||
|
||||
-#define __raw_read_lock_flags(lock, flags) __raw_read_lock(lock)
|
||||
-#define __raw_write_lock_flags(lock, flags) __raw_write_lock(lock)
|
||||
-
|
||||
#define _raw_spin_relax(lock) cpu_relax()
|
||||
#define _raw_read_relax(lock) cpu_relax()
|
||||
#define _raw_write_relax(lock) cpu_relax()
|
||||
@@ -0,0 +1,49 @@
|
||||
From: Dimitri Sivanich <sivanich@sgi.com>
|
||||
Date: Wed, 15 Apr 2009 15:56:25 +0000 (-0500)
|
||||
Subject: ia64: smp_flush_tlb_mm() should only send IPI's to cpus in cpu_vm_mask
|
||||
Patch-mainline: 2.6.30-rc3
|
||||
Git-commit: edb91dc01a216e84b78721b71a06db1e0db141b7
|
||||
References: bnc#497807
|
||||
|
||||
[IA64] smp_flush_tlb_mm() should only send IPI's to cpus in cpu_vm_mask
|
||||
|
||||
Having flush_tlb_mm->smp_flush_tlb_mm() send an IPI to every cpu
|
||||
on the system is occasionally triggering spin_lock contention in
|
||||
generic_smp_call_function_interrupt().
|
||||
|
||||
Follow x86 arch's lead and only sends IPIs to the cpus in mm->cpu_vm_mask.
|
||||
|
||||
Experiments with this change have shown significant improvement in this
|
||||
contention issue.
|
||||
|
||||
Signed-off-by: Dimitri Sivanich <sivanich@sgi.com>
|
||||
Signed-off-by: Tony Luck <tony.luck@intel.com>
|
||||
Acked-by: Jeff Mahoney <jeffm@suse.com>
|
||||
---
|
||||
|
||||
arch/ia64/kernel/smp.c | 13 +++++--------
|
||||
1 file changed, 5 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/arch/ia64/kernel/smp.c
|
||||
+++ b/arch/ia64/kernel/smp.c
|
||||
@@ -300,15 +300,12 @@ smp_flush_tlb_mm (struct mm_struct *mm)
|
||||
return;
|
||||
}
|
||||
|
||||
+ smp_call_function_mask(mm->cpu_vm_mask,
|
||||
+ (void (*)(void *))local_finish_flush_tlb_mm, mm, 1);
|
||||
+ local_irq_disable();
|
||||
+ local_finish_flush_tlb_mm(mm);
|
||||
+ local_irq_enable();
|
||||
preempt_enable();
|
||||
- /*
|
||||
- * We could optimize this further by using mm->cpu_vm_mask to track which CPUs
|
||||
- * have been running in the address space. It's not clear that this is worth the
|
||||
- * trouble though: to avoid races, we have to raise the IPI on the target CPU
|
||||
- * anyhow, and once a CPU is interrupted, the cost of local_flush_tlb_all() is
|
||||
- * rather trivial.
|
||||
- */
|
||||
- on_each_cpu((void (*)(void *))local_finish_flush_tlb_mm, mm, 1);
|
||||
}
|
||||
|
||||
void arch_send_call_function_single_ipi(int cpu)
|
||||
64
src/patches/suse-2.6.27.31/patches.arch/ia64-sn-BTE_MAX_XFER
Normal file
64
src/patches/suse-2.6.27.31/patches.arch/ia64-sn-BTE_MAX_XFER
Normal file
@@ -0,0 +1,64 @@
|
||||
Date: Wed, 4 Feb 2009 10:47:37 -0600
|
||||
From: Robin Holt <holt@sgi.com>
|
||||
Subject: bte_copy of BTE_MAX_XFER trips BUG_ON -V2
|
||||
References: bnc#472894
|
||||
|
||||
BTE_MAX_XFER is wrong. It is one greater than the number of cache
|
||||
lines the BTE is actually able to transfer. If you request a transfer
|
||||
of exactly BTE_MAX_XFER size, you trip a very cryptic BUG_ON() which
|
||||
should certainly be made more clear.
|
||||
|
||||
This patch fixes that constant and also cleans up the BUG_ON()s in
|
||||
arch/ia64/sn/kernel/bte.c to test one condition per line.
|
||||
|
||||
Signed-off-by: Robin Holt <holt@sgi.com>
|
||||
Acked-by: Raymund Will <rw@suse.de>
|
||||
|
||||
---
|
||||
|
||||
Changes since -V1:
|
||||
Base the BTE_LEN_MASK upon a 1UL instead of 1 for correctness.
|
||||
|
||||
|
||||
arch/ia64/include/asm/sn/bte.h | 4 ++--
|
||||
arch/ia64/sn/kernel/bte.c | 7 ++++---
|
||||
2 files changed, 6 insertions(+), 5 deletions(-)
|
||||
|
||||
Index: bte_fixup/arch/ia64/include/asm/sn/bte.h
|
||||
===================================================================
|
||||
--- bte_fixup.orig/arch/ia64/include/asm/sn/bte.h 2009-02-04 10:37:29.594750841 -0600
|
||||
+++ bte_fixup/arch/ia64/include/asm/sn/bte.h 2009-02-04 10:40:32.495791608 -0600
|
||||
@@ -38,8 +38,8 @@
|
||||
|
||||
/* BTE status register only supports 16 bits for length field */
|
||||
#define BTE_LEN_BITS (16)
|
||||
-#define BTE_LEN_MASK ((1 << BTE_LEN_BITS) - 1)
|
||||
-#define BTE_MAX_XFER ((1 << BTE_LEN_BITS) * L1_CACHE_BYTES)
|
||||
+#define BTE_LEN_MASK ((1UL << BTE_LEN_BITS) - 1)
|
||||
+#define BTE_MAX_XFER (BTE_LEN_MASK << L1_CACHE_SHIFT)
|
||||
|
||||
|
||||
/* Define hardware */
|
||||
Index: bte_fixup/arch/ia64/sn/kernel/bte.c
|
||||
===================================================================
|
||||
--- bte_fixup.orig/arch/ia64/sn/kernel/bte.c 2009-02-04 10:37:30.026753839 -0600
|
||||
+++ bte_fixup/arch/ia64/sn/kernel/bte.c 2009-02-04 10:40:11.711701203 -0600
|
||||
@@ -97,9 +97,10 @@ bte_result_t bte_copy(u64 src, u64 dest,
|
||||
return BTE_SUCCESS;
|
||||
}
|
||||
|
||||
- BUG_ON((len & L1_CACHE_MASK) ||
|
||||
- (src & L1_CACHE_MASK) || (dest & L1_CACHE_MASK));
|
||||
- BUG_ON(!(len < ((BTE_LEN_MASK + 1) << L1_CACHE_SHIFT)));
|
||||
+ BUG_ON(len & L1_CACHE_MASK);
|
||||
+ BUG_ON(src & L1_CACHE_MASK);
|
||||
+ BUG_ON(dest & L1_CACHE_MASK);
|
||||
+ BUG_ON(len > BTE_MAX_XFER);
|
||||
|
||||
/*
|
||||
* Start with interface corresponding to cpu number
|
||||
--
|
||||
To unsubscribe from this list: send the line "unsubscribe linux-ia64" in
|
||||
the body of a message to majordomo@vger.kernel.org
|
||||
More majordomo info at http://vger.kernel.org/majordomo-info.html
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
From: Jeremy Higdon <jeremy@sgi.com>
|
||||
Subject: ia64: sn: fix pci attribute propagation bug
|
||||
Patch-mainline: 2.6.29
|
||||
References: bnc#480591
|
||||
|
||||
Customer backups were no longer able to complete within 24 hours after
|
||||
upgrading from SLES10 SP1 to SLES10 SP2. Further investigation demonstrated
|
||||
tape streaming bandwidth reduced when the scsi controller is operating at PCI
|
||||
66 mhz. Operating both ports simultaneously caused the reduced bandwidth to
|
||||
reduce in half to each tape drive. If the controller is operating at PCI-X
|
||||
100mhz, there is no reduction in the tape performance.
|
||||
|
||||
Performance degradation is noticeable with a single tape drive writing highly
|
||||
compressible data and the controller operating in PCI-66. This only affects
|
||||
PCI mode, not PCIx.
|
||||
|
||||
kotd SP2 baseline: 4194304000 bytes (4.2 GB) copied, 70.7747 seconds, 59.3 MB/s
|
||||
kotd SP1 baseline: 4194304000 bytes (4.2 GB) copied, 26.7403 seconds, 157 MB/s
|
||||
|
||||
The culprit patch is:
|
||||
patches.fixes/fix-ia64-sn-msi-support
|
||||
|
||||
The patch is a backport of the following upstream patch:
|
||||
|
||||
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=83821d3f558dc651e555d62182ed0c95651f41a6
|
||||
|
||||
The fact that the patch is sn2 specific, and that the problem shows up only in
|
||||
PCI mode, would easily explain how the performance regression managed to go
|
||||
unnoticed for this long.
|
||||
|
||||
The problem is that DMA attributes are not getting set on 64bit DMA addresses
|
||||
on our PIC ASIC based PCI busses (i.e. O350 and O3000 systems). This only
|
||||
affects devices running in PCI mode, not PCIx mode. The result is anything
|
||||
from poor performance to data corruption -- so this is a mustfix type problem.
|
||||
|
||||
This is a _regression_ in SLES10SP2 introduced _accidentally_ by the patch
|
||||
patches.fixes/fix-ia64-sn-msi-support
|
||||
That implemented MSI support on IA64 SN2 systems.
|
||||
|
||||
The broken code is located in the changes to the pcibr_dmatrans_direct64()
|
||||
routine in arch/ia64/sn/pci/pcibr/pcibr_dma.c.
|
||||
|
||||
The bug is also present in upstream code which means that SLES11 too suffers
|
||||
from the same problem: git commit that introduced the bug is
|
||||
83821d3f558dc651e555d62182ed0c95651f41a6
|
||||
|
||||
This patch resolves the problem.
|
||||
|
||||
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
|
||||
|
||||
---
|
||||
arch/ia64/sn/pci/pcibr/pcibr_dma.c | 7 +++----
|
||||
1 file changed, 3 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/arch/ia64/sn/pci/pcibr/pcibr_dma.c
|
||||
+++ b/arch/ia64/sn/pci/pcibr/pcibr_dma.c
|
||||
@@ -135,11 +135,10 @@ pcibr_dmatrans_direct64(struct pcidev_in
|
||||
if (SN_DMA_ADDRTYPE(dma_flags) == SN_DMA_ADDR_PHYS)
|
||||
pci_addr = IS_PIC_SOFT(pcibus_info) ?
|
||||
PHYS_TO_DMA(paddr) :
|
||||
- PHYS_TO_TIODMA(paddr) | dma_attributes;
|
||||
+ PHYS_TO_TIODMA(paddr);
|
||||
else
|
||||
- pci_addr = IS_PIC_SOFT(pcibus_info) ?
|
||||
- paddr :
|
||||
- paddr | dma_attributes;
|
||||
+ pci_addr = paddr;
|
||||
+ pci_addr |= dma_attributes;
|
||||
|
||||
/* Handle Bus mode */
|
||||
if (IS_PCIX(pcibus_info))
|
||||
@@ -0,0 +1,192 @@
|
||||
From: John Keller <jpk@sgi.com>
|
||||
Date: Mon, 24 Nov 2008 22:47:17 +0000 (-0600)
|
||||
Subject: [IA64] SN specific version of dma_get_required_mask()
|
||||
Patch-mainline: 2.6.29-rc2
|
||||
Git-commit: 175add1981e53d22caba8f42d5f924a4de507b6c
|
||||
References: bnc#529369
|
||||
|
||||
[IA64] SN specific version of dma_get_required_mask()
|
||||
|
||||
Create a platform specific version of dma_get_required_mask()
|
||||
for ia64 SN Altix. All SN Altix platforms support 64 bit DMA
|
||||
addressing regardless of the size of system memory.
|
||||
Create an ia64 machvec for dma_get_required_mask, with the
|
||||
SN version unconditionally returning DMA_64BIT_MASK.
|
||||
|
||||
Signed-off-by: John Keller <jpk@sgi.com>
|
||||
Signed-off-by: Tony Luck <tony.luck@intel.com>
|
||||
Acked-by: Jeff Mahoney <jeffm@suse.com>
|
||||
---
|
||||
Documentation/DMA-API.txt | 9 ++++-----
|
||||
arch/ia64/include/asm/dma-mapping.h | 2 ++
|
||||
arch/ia64/include/asm/machvec.h | 7 +++++++
|
||||
arch/ia64/include/asm/machvec_init.h | 1 +
|
||||
arch/ia64/include/asm/machvec_sn2.h | 2 ++
|
||||
arch/ia64/pci/pci.c | 27 +++++++++++++++++++++++++++
|
||||
arch/ia64/sn/pci/pci_dma.c | 6 ++++++
|
||||
7 files changed, 49 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/Documentation/DMA-API.txt
|
||||
+++ b/Documentation/DMA-API.txt
|
||||
@@ -170,16 +170,15 @@ Returns: 0 if successful and a negative
|
||||
u64
|
||||
dma_get_required_mask(struct device *dev)
|
||||
|
||||
-After setting the mask with dma_set_mask(), this API returns the
|
||||
-actual mask (within that already set) that the platform actually
|
||||
-requires to operate efficiently. Usually this means the returned mask
|
||||
+This API returns the mask that the platform requires to
|
||||
+operate efficiently. Usually this means the returned mask
|
||||
is the minimum required to cover all of memory. Examining the
|
||||
required mask gives drivers with variable descriptor sizes the
|
||||
opportunity to use smaller descriptors as necessary.
|
||||
|
||||
Requesting the required mask does not alter the current mask. If you
|
||||
-wish to take advantage of it, you should issue another dma_set_mask()
|
||||
-call to lower the mask again.
|
||||
+wish to take advantage of it, you should issue a dma_set_mask()
|
||||
+call to set the mask to the value returned.
|
||||
|
||||
|
||||
Part Id - Streaming DMA mappings
|
||||
--- a/arch/ia64/include/asm/dma-mapping.h
|
||||
+++ b/arch/ia64/include/asm/dma-mapping.h
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <asm/machvec.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
+#define ARCH_HAS_DMA_GET_REQUIRED_MASK
|
||||
+
|
||||
#define dma_alloc_coherent platform_dma_alloc_coherent
|
||||
/* coherent mem. is cheap */
|
||||
static inline void *
|
||||
--- a/arch/ia64/include/asm/machvec.h
|
||||
+++ b/arch/ia64/include/asm/machvec.h
|
||||
@@ -61,6 +61,7 @@ typedef dma_addr_t ia64_mv_dma_map_singl
|
||||
typedef void ia64_mv_dma_unmap_single_attrs (struct device *, dma_addr_t, size_t, int, struct dma_attrs *);
|
||||
typedef int ia64_mv_dma_map_sg_attrs (struct device *, struct scatterlist *, int, int, struct dma_attrs *);
|
||||
typedef void ia64_mv_dma_unmap_sg_attrs (struct device *, struct scatterlist *, int, int, struct dma_attrs *);
|
||||
+typedef u64 ia64_mv_dma_get_required_mask (struct device *);
|
||||
|
||||
/*
|
||||
* WARNING: The legacy I/O space is _architected_. Platforms are
|
||||
@@ -154,6 +155,7 @@ extern void machvec_tlb_migrate_finish (
|
||||
# define platform_dma_sync_sg_for_device ia64_mv.dma_sync_sg_for_device
|
||||
# define platform_dma_mapping_error ia64_mv.dma_mapping_error
|
||||
# define platform_dma_supported ia64_mv.dma_supported
|
||||
+# define platform_dma_get_required_mask ia64_mv.dma_get_required_mask
|
||||
# define platform_irq_to_vector ia64_mv.irq_to_vector
|
||||
# define platform_local_vector_to_irq ia64_mv.local_vector_to_irq
|
||||
# define platform_pci_get_legacy_mem ia64_mv.pci_get_legacy_mem
|
||||
@@ -208,6 +210,7 @@ struct ia64_machine_vector {
|
||||
ia64_mv_dma_sync_sg_for_device *dma_sync_sg_for_device;
|
||||
ia64_mv_dma_mapping_error *dma_mapping_error;
|
||||
ia64_mv_dma_supported *dma_supported;
|
||||
+ ia64_mv_dma_get_required_mask *dma_get_required_mask;
|
||||
ia64_mv_irq_to_vector *irq_to_vector;
|
||||
ia64_mv_local_vector_to_irq *local_vector_to_irq;
|
||||
ia64_mv_pci_get_legacy_mem_t *pci_get_legacy_mem;
|
||||
@@ -258,6 +261,7 @@ struct ia64_machine_vector {
|
||||
platform_dma_sync_sg_for_device, \
|
||||
platform_dma_mapping_error, \
|
||||
platform_dma_supported, \
|
||||
+ platform_dma_get_required_mask, \
|
||||
platform_irq_to_vector, \
|
||||
platform_local_vector_to_irq, \
|
||||
platform_pci_get_legacy_mem, \
|
||||
@@ -382,6 +386,9 @@ extern ia64_mv_dma_supported swiotlb_dm
|
||||
#ifndef platform_dma_supported
|
||||
# define platform_dma_supported swiotlb_dma_supported
|
||||
#endif
|
||||
+#ifndef platform_dma_get_required_mask
|
||||
+# define platform_dma_get_required_mask ia64_dma_get_required_mask
|
||||
+#endif
|
||||
#ifndef platform_irq_to_vector
|
||||
# define platform_irq_to_vector __ia64_irq_to_vector
|
||||
#endif
|
||||
--- a/arch/ia64/include/asm/machvec_init.h
|
||||
+++ b/arch/ia64/include/asm/machvec_init.h
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
extern ia64_mv_send_ipi_t ia64_send_ipi;
|
||||
extern ia64_mv_global_tlb_purge_t ia64_global_tlb_purge;
|
||||
+extern ia64_mv_dma_get_required_mask ia64_dma_get_required_mask;
|
||||
extern ia64_mv_irq_to_vector __ia64_irq_to_vector;
|
||||
extern ia64_mv_local_vector_to_irq __ia64_local_vector_to_irq;
|
||||
extern ia64_mv_pci_get_legacy_mem_t ia64_pci_get_legacy_mem;
|
||||
--- a/arch/ia64/include/asm/machvec_sn2.h
|
||||
+++ b/arch/ia64/include/asm/machvec_sn2.h
|
||||
@@ -67,6 +67,7 @@ extern ia64_mv_dma_sync_single_for_devic
|
||||
extern ia64_mv_dma_sync_sg_for_device sn_dma_sync_sg_for_device;
|
||||
extern ia64_mv_dma_mapping_error sn_dma_mapping_error;
|
||||
extern ia64_mv_dma_supported sn_dma_supported;
|
||||
+extern ia64_mv_dma_get_required_mask sn_dma_get_required_mask;
|
||||
extern ia64_mv_migrate_t sn_migrate;
|
||||
extern ia64_mv_kernel_launch_event_t sn_kernel_launch_event;
|
||||
extern ia64_mv_setup_msi_irq_t sn_setup_msi_irq;
|
||||
@@ -123,6 +124,7 @@ extern ia64_mv_pci_fixup_bus_t sn_pci_f
|
||||
#define platform_dma_sync_sg_for_device sn_dma_sync_sg_for_device
|
||||
#define platform_dma_mapping_error sn_dma_mapping_error
|
||||
#define platform_dma_supported sn_dma_supported
|
||||
+#define platform_dma_get_required_mask sn_dma_get_required_mask
|
||||
#define platform_migrate sn_migrate
|
||||
#define platform_kernel_launch_event sn_kernel_launch_event
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
--- a/arch/ia64/pci/pci.c
|
||||
+++ b/arch/ia64/pci/pci.c
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
+#include <linux/bootmem.h>
|
||||
|
||||
#include <asm/machvec.h>
|
||||
#include <asm/page.h>
|
||||
@@ -743,6 +744,32 @@ static void __init set_pci_cacheline_siz
|
||||
pci_cache_line_size = (1 << cci.pcci_line_size) / 4;
|
||||
}
|
||||
|
||||
+u64 ia64_dma_get_required_mask(struct device *dev)
|
||||
+{
|
||||
+ u32 low_totalram = ((max_pfn - 1) << PAGE_SHIFT);
|
||||
+ u32 high_totalram = ((max_pfn - 1) >> (32 - PAGE_SHIFT));
|
||||
+ u64 mask;
|
||||
+
|
||||
+ if (!high_totalram) {
|
||||
+ /* convert to mask just covering totalram */
|
||||
+ low_totalram = (1 << (fls(low_totalram) - 1));
|
||||
+ low_totalram += low_totalram - 1;
|
||||
+ mask = low_totalram;
|
||||
+ } else {
|
||||
+ high_totalram = (1 << (fls(high_totalram) - 1));
|
||||
+ high_totalram += high_totalram - 1;
|
||||
+ mask = (((u64)high_totalram) << 32) + 0xffffffff;
|
||||
+ }
|
||||
+ return mask;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(ia64_dma_get_required_mask);
|
||||
+
|
||||
+u64 dma_get_required_mask(struct device *dev)
|
||||
+{
|
||||
+ return platform_dma_get_required_mask(dev);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(dma_get_required_mask);
|
||||
+
|
||||
static int __init pcibios_init(void)
|
||||
{
|
||||
set_pci_cacheline_size();
|
||||
--- a/arch/ia64/sn/pci/pci_dma.c
|
||||
+++ b/arch/ia64/sn/pci/pci_dma.c
|
||||
@@ -356,6 +356,12 @@ int sn_dma_mapping_error(struct device *
|
||||
}
|
||||
EXPORT_SYMBOL(sn_dma_mapping_error);
|
||||
|
||||
+u64 sn_dma_get_required_mask(struct device *dev)
|
||||
+{
|
||||
+ return DMA_64BIT_MASK;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(sn_dma_get_required_mask);
|
||||
+
|
||||
char *sn_pci_get_legacy_mem(struct pci_bus *bus)
|
||||
{
|
||||
if (!SN_PCIBUS_BUSSOFT(bus))
|
||||
150
src/patches/suse-2.6.27.31/patches.arch/mm-avoid-bad-page-on-lru
Normal file
150
src/patches/suse-2.6.27.31/patches.arch/mm-avoid-bad-page-on-lru
Normal file
@@ -0,0 +1,150 @@
|
||||
From: Russ Anderson <rja@sgi.com>
|
||||
Subject: mm: Avoid putting a bad page back on the LRU v8
|
||||
References: 415829
|
||||
Acked-by: schwab@suse.de
|
||||
|
||||
Prevent a page with a physical memory error from being placed back
|
||||
on the LRU. A new page flag (PG_memerror) is added if
|
||||
CONFIG_PAGEFLAGS_EXTENDED is defined.
|
||||
|
||||
Version 8 change: Removed hot path check for pages with memory
|
||||
errors on the free list.
|
||||
|
||||
Signed-off-by: Russ Anderson <rja@sgi.com>
|
||||
Reviewed-by: Christoph Lameter <cl@linux-foundation.org>
|
||||
|
||||
---
|
||||
include/linux/page-flags.h | 15 ++++++++++++++-
|
||||
mm/migrate.c | 36 +++++++++++++++++++++++++++++++++++-
|
||||
2 files changed, 49 insertions(+), 2 deletions(-)
|
||||
|
||||
Index: linux/mm/migrate.c
|
||||
===================================================================
|
||||
--- linux.orig/mm/migrate.c 2008-07-29 13:18:23.000000000 -0500
|
||||
+++ linux/mm/migrate.c 2008-07-29 13:21:03.000000000 -0500
|
||||
@@ -65,6 +65,7 @@ int isolate_lru_page(struct page *page,
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
+EXPORT_SYMBOL(isolate_lru_page);
|
||||
|
||||
/*
|
||||
* migrate_prep() needs to be called before we start compiling a list of pages
|
||||
@@ -82,6 +83,7 @@ int migrate_prep(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
+EXPORT_SYMBOL(migrate_prep);
|
||||
|
||||
static inline void move_to_lru(struct page *page)
|
||||
{
|
||||
@@ -116,6 +118,7 @@ int putback_lru_pages(struct list_head *
|
||||
}
|
||||
return count;
|
||||
}
|
||||
+EXPORT_SYMBOL(putback_lru_pages);
|
||||
|
||||
/*
|
||||
* Restore a potential migration pte to a working pte entry
|
||||
@@ -741,7 +744,26 @@ unlock:
|
||||
* restored.
|
||||
*/
|
||||
list_del(&page->lru);
|
||||
- move_to_lru(page);
|
||||
+ if (PageMemError(page)) {
|
||||
+ if (rc == 0)
|
||||
+ /*
|
||||
+ * A page with a memory error that has
|
||||
+ * been migrated will not be moved to
|
||||
+ * the LRU.
|
||||
+ */
|
||||
+ goto move_newpage;
|
||||
+ else
|
||||
+ /*
|
||||
+ * The page failed to migrate and will not
|
||||
+ * be added to the bad page list. Clearing
|
||||
+ * the error bit will allow another attempt
|
||||
+ * to migrate if it gets another correctable
|
||||
+ * error.
|
||||
+ */
|
||||
+ ClearPageMemError(page);
|
||||
+ }
|
||||
+
|
||||
+ move_to_lru(page);
|
||||
}
|
||||
|
||||
move_newpage:
|
||||
@@ -813,6 +835,17 @@ int migrate_pages(struct list_head *from
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+ if (rc != 0)
|
||||
+ list_for_each_entry_safe(page, page2, from, lru)
|
||||
+ if (PageMemError(page))
|
||||
+ /*
|
||||
+ * The page failed to migrate. Clearing
|
||||
+ * the error bit will allow another attempt
|
||||
+ * to migrate if it gets another correctable
|
||||
+ * error.
|
||||
+ */
|
||||
+ ClearPageMemError(page);
|
||||
rc = 0;
|
||||
out:
|
||||
if (!swapwrite)
|
||||
@@ -825,6 +858,7 @@ out:
|
||||
|
||||
return nr_failed + retry;
|
||||
}
|
||||
+EXPORT_SYMBOL(migrate_pages);
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
/*
|
||||
Index: linux/include/linux/page-flags.h
|
||||
===================================================================
|
||||
--- linux.orig/include/linux/page-flags.h 2008-07-29 13:18:23.000000000 -0500
|
||||
+++ linux/include/linux/page-flags.h 2008-07-29 13:21:03.000000000 -0500
|
||||
@@ -84,6 +84,7 @@ enum pageflags {
|
||||
PG_private, /* If pagecache, has fs-private data */
|
||||
PG_writeback, /* Page is under writeback */
|
||||
#ifdef CONFIG_PAGEFLAGS_EXTENDED
|
||||
+ PG_memerror, /* Page has a physical memory error */
|
||||
PG_head, /* A head page */
|
||||
PG_tail, /* A tail page */
|
||||
#else
|
||||
@@ -147,15 +148,21 @@ static inline int TestSetPage##uname(str
|
||||
static inline int TestClearPage##uname(struct page *page) \
|
||||
{ return test_and_clear_bit(PG_##lname, &page->flags); }
|
||||
|
||||
+#define PAGEFLAGMASK(uname, lname) \
|
||||
+static inline int PAGEMASK_##uname(void) \
|
||||
+ { return (1 << PG_##lname); }
|
||||
|
||||
#define PAGEFLAG(uname, lname) TESTPAGEFLAG(uname, lname) \
|
||||
- SETPAGEFLAG(uname, lname) CLEARPAGEFLAG(uname, lname)
|
||||
+ SETPAGEFLAG(uname, lname) CLEARPAGEFLAG(uname, lname) \
|
||||
+ PAGEFLAGMASK(uname, lname)
|
||||
|
||||
#define __PAGEFLAG(uname, lname) TESTPAGEFLAG(uname, lname) \
|
||||
__SETPAGEFLAG(uname, lname) __CLEARPAGEFLAG(uname, lname)
|
||||
|
||||
#define PAGEFLAG_FALSE(uname) \
|
||||
static inline int Page##uname(struct page *page) \
|
||||
+ { return 0; } \
|
||||
+static inline int PAGEMASK_##uname(void) \
|
||||
{ return 0; }
|
||||
|
||||
#define TESTSCFLAG(uname, lname) \
|
||||
@@ -325,6 +332,12 @@ static inline void __ClearPageTail(struc
|
||||
}
|
||||
|
||||
#endif /* !PAGEFLAGS_EXTENDED */
|
||||
+
|
||||
+#ifdef CONFIG_PAGEFLAGS_EXTENDED
|
||||
+PAGEFLAG(MemError, memerror)
|
||||
+#else
|
||||
+PAGEFLAG_FALSE(MemError)
|
||||
+#endif
|
||||
|
||||
#define PAGE_FLAGS (1 << PG_lru | 1 << PG_private | 1 << PG_locked | \
|
||||
1 << PG_buddy | 1 << PG_writeback | \
|
||||
@@ -0,0 +1,119 @@
|
||||
From 14f966e79445015cd89d0fa0ceb6b33702e951b6 Mon Sep 17 00:00:00 2001
|
||||
From: Robert Jennings <rcj@linux.vnet.ibm.com>
|
||||
Date: Wed, 15 Apr 2009 05:55:32 +0000
|
||||
Subject: powerpc/pseries: CMO unused page hinting
|
||||
Patch-mainline: 2.6.31
|
||||
References: bnc#495091
|
||||
|
||||
From: Robert Jennings <rcj@linux.vnet.ibm.com>
|
||||
|
||||
commit 14f966e79445015cd89d0fa0ceb6b33702e951b6 upstream.
|
||||
|
||||
Adds support for the "unused" page hint which can be used in shared
|
||||
memory partitions to flag pages not in use, which will then be stolen
|
||||
before active pages by the hypervisor when memory needs to be moved to
|
||||
LPARs in need of additional memory. Failure to mark pages as 'unused'
|
||||
makes the LPAR slower to give up unused memory to other partitions.
|
||||
|
||||
This adds the kernel parameter 'cmo_free_hint' to disable this
|
||||
functionality.
|
||||
|
||||
Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
|
||||
Signed-off-by: Robert Jennings <rcj@linux.vnet.ibm.com>
|
||||
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
||||
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
|
||||
---
|
||||
Documentation/kernel-parameters.txt | 7 ++++
|
||||
arch/powerpc/include/asm/page.h | 5 +++
|
||||
arch/powerpc/platforms/pseries/lpar.c | 52 ++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 64 insertions(+)
|
||||
|
||||
--- a/Documentation/kernel-parameters.txt
|
||||
+++ b/Documentation/kernel-parameters.txt
|
||||
@@ -458,6 +458,13 @@ and is between 256 and 4096 characters.
|
||||
Also note the kernel might malfunction if you disable
|
||||
some critical bits.
|
||||
|
||||
+ cmo_free_hint= [PPC] Format: { yes | no }
|
||||
+ Specify whether pages are marked as being inactive
|
||||
+ when they are freed. This is used in CMO environments
|
||||
+ to determine OS memory pressure for page stealing by
|
||||
+ a hypervisor.
|
||||
+ Default: yes
|
||||
+
|
||||
code_bytes [IA32/X86_64] How many bytes of object code to print
|
||||
in an oops report.
|
||||
Range: 0 - 8192
|
||||
--- a/arch/powerpc/include/asm/page.h
|
||||
+++ b/arch/powerpc/include/asm/page.h
|
||||
@@ -215,6 +215,11 @@ extern void copy_user_page(void *to, voi
|
||||
struct page *p);
|
||||
extern int page_is_ram(unsigned long pfn);
|
||||
|
||||
+#ifdef CONFIG_PPC_SMLPAR
|
||||
+void arch_free_page(struct page *page, int order);
|
||||
+#define HAVE_ARCH_FREE_PAGE
|
||||
+#endif
|
||||
+
|
||||
struct vm_area_struct;
|
||||
|
||||
typedef struct page *pgtable_t;
|
||||
--- a/arch/powerpc/platforms/pseries/lpar.c
|
||||
+++ b/arch/powerpc/platforms/pseries/lpar.c
|
||||
@@ -609,3 +609,55 @@ void __init hpte_init_lpar(void)
|
||||
ppc_md.flush_hash_range = pSeries_lpar_flush_hash_range;
|
||||
ppc_md.hpte_clear_all = pSeries_lpar_hptab_clear;
|
||||
}
|
||||
+
|
||||
+#ifdef CONFIG_PPC_SMLPAR
|
||||
+#define CMO_FREE_HINT_DEFAULT 1
|
||||
+static int cmo_free_hint_flag = CMO_FREE_HINT_DEFAULT;
|
||||
+
|
||||
+static int __init cmo_free_hint(char *str)
|
||||
+{
|
||||
+ char *parm;
|
||||
+ parm = strstrip(str);
|
||||
+
|
||||
+ if (strcasecmp(parm, "no") == 0 || strcasecmp(parm, "off") == 0) {
|
||||
+ printk(KERN_INFO "cmo_free_hint: CMO free page hinting is not active.\n");
|
||||
+ cmo_free_hint_flag = 0;
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ cmo_free_hint_flag = 1;
|
||||
+ printk(KERN_INFO "cmo_free_hint: CMO free page hinting is active.\n");
|
||||
+
|
||||
+ if (strcasecmp(parm, "yes") == 0 || strcasecmp(parm, "on") == 0)
|
||||
+ return 1;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+__setup("cmo_free_hint=", cmo_free_hint);
|
||||
+
|
||||
+static void pSeries_set_page_state(struct page *page, int order,
|
||||
+ unsigned long state)
|
||||
+{
|
||||
+ int i, j;
|
||||
+ unsigned long cmo_page_sz, addr;
|
||||
+
|
||||
+ cmo_page_sz = cmo_get_page_size();
|
||||
+ addr = __pa((unsigned long)page_address(page));
|
||||
+
|
||||
+ for (i = 0; i < (1 << order); i++, addr += PAGE_SIZE) {
|
||||
+ for (j = 0; j < PAGE_SIZE; j += cmo_page_sz)
|
||||
+ plpar_hcall_norets(H_PAGE_INIT, state, addr + j, 0);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void arch_free_page(struct page *page, int order)
|
||||
+{
|
||||
+ if (!cmo_free_hint_flag || !firmware_has_feature(FW_FEATURE_CMO))
|
||||
+ return;
|
||||
+
|
||||
+ pSeries_set_page_state(page, order, H_PAGE_SET_UNUSED);
|
||||
+}
|
||||
+EXPORT_SYMBOL(arch_free_page);
|
||||
+
|
||||
+#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
From: Michael Neuling <mikey@neuling.org>
|
||||
Subject: change giveup_fpu/altivec to disable VSX for current
|
||||
Patch-mainline: 2.6.30
|
||||
References: bnc#492324
|
||||
|
||||
[PATCH] powerpc: change giveup_fpu/altivec to disable VSX for current
|
||||
|
||||
When we call giveup_fpu, we need to need to turn off VSX in current.
|
||||
If we don't, on return to current it may execute a VSX instruction
|
||||
(before the next FP), and not have it's register state refreshed
|
||||
correctly from the thread_struct. Ditto for altivec.
|
||||
|
||||
This caused a bug where an unaligned lfs or stfs (which calls
|
||||
giveup_fpu so it can use the FPRs) to return to userspace with FP off
|
||||
but VSX on. Then if a VSX instruction is executed, before another FP
|
||||
instruction, it will proceed without another exception and hence have
|
||||
the incorrect register state for VSX registers 0-31.
|
||||
|
||||
lfs unaligned <- alignment exception turns FP off but leaves VSX on
|
||||
|
||||
VSX instruction <- no exception since VSX on, hence we get the
|
||||
wrong VSX register values for VSX registers 0-31
|
||||
(overlapping the FPRs)
|
||||
|
||||
Signed-off-by: Michael Neuling <mikey@neuling.org>
|
||||
Acked-by: duwe@suse.de
|
||||
|
||||
---
|
||||
arch/powerpc/kernel/fpu.S | 5 +++++
|
||||
arch/powerpc/kernel/misc_64.S | 8 ++++++++
|
||||
2 files changed, 13 insertions(+)
|
||||
|
||||
Index: clone3/arch/powerpc/kernel/fpu.S
|
||||
===================================================================
|
||||
--- clone3.orig/arch/powerpc/kernel/fpu.S
|
||||
+++ clone3/arch/powerpc/kernel/fpu.S
|
||||
@@ -145,6 +145,11 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX)
|
||||
beq 1f
|
||||
PPC_LL r4,_MSR-STACK_FRAME_OVERHEAD(r5)
|
||||
li r3,MSR_FP|MSR_FE0|MSR_FE1
|
||||
+#ifdef CONFIG_VSX
|
||||
+BEGIN_FTR_SECTION
|
||||
+ oris r3,r3,MSR_VSX@h
|
||||
+END_FTR_SECTION_IFSET(CPU_FTR_VSX)
|
||||
+#endif
|
||||
andc r4,r4,r3 /* disable FP for previous task */
|
||||
PPC_STL r4,_MSR-STACK_FRAME_OVERHEAD(r5)
|
||||
1:
|
||||
Index: clone3/arch/powerpc/kernel/misc_64.S
|
||||
===================================================================
|
||||
--- clone3.orig/arch/powerpc/kernel/misc_64.S
|
||||
+++ clone3/arch/powerpc/kernel/misc_64.S
|
||||
@@ -493,7 +493,15 @@ _GLOBAL(giveup_altivec)
|
||||
stvx vr0,r4,r3
|
||||
beq 1f
|
||||
ld r4,_MSR-STACK_FRAME_OVERHEAD(r5)
|
||||
+#ifdef CONFIG_VSX
|
||||
+BEGIN_FTR_SECTION
|
||||
+ lis r3,(MSR_VEC|MSR_VSX)@h
|
||||
+FTR_SECTION_ELSE
|
||||
+ lis r3,MSR_VEC@h
|
||||
+ALT_FTR_SECTION_END_IFSET(CPU_FTR_VSX)
|
||||
+#else
|
||||
lis r3,MSR_VEC@h
|
||||
+#endif
|
||||
andc r4,r4,r3 /* disable FP for previous task */
|
||||
std r4,_MSR-STACK_FRAME_OVERHEAD(r5)
|
||||
1:
|
||||
@@ -0,0 +1,50 @@
|
||||
Subject: Fix GDB watchpoints on Cell
|
||||
From: Arnd Bergmann <arnd@arndb.de>
|
||||
References: 456405 - LTC50396
|
||||
|
||||
An earlier patch from Jens Osterkamp attempted to fix GDB
|
||||
watchpoints by enabling the DABRX register at boot time.
|
||||
Unfortunately, this did not work on SMP setups, where
|
||||
secondary CPUs were still using the power-on DABRX value.
|
||||
|
||||
This introduces the same change for secondary CPUs on cell
|
||||
as well.
|
||||
|
||||
Reported-by: Ulrich Weigand <Ulrich.Weigand@de.ibm.com>
|
||||
Tested-by: Ulrich Weigand <Ulrich.Weigand@de.ibm.com>
|
||||
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
|
||||
Signed-off-by: Paul Mackerras <paulus@samba.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
arch/powerpc/platforms/cell/smp.c | 9 +++++++--
|
||||
1 file changed, 7 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/platforms/cell/smp.c
|
||||
+++ b/arch/powerpc/platforms/cell/smp.c
|
||||
@@ -129,10 +129,15 @@ static int __init smp_iic_probe(void)
|
||||
return cpus_weight(cpu_possible_map);
|
||||
}
|
||||
|
||||
-static void __devinit smp_iic_setup_cpu(int cpu)
|
||||
+static void __devinit smp_cell_setup_cpu(int cpu)
|
||||
{
|
||||
if (cpu != boot_cpuid)
|
||||
iic_setup_cpu();
|
||||
+
|
||||
+ /*
|
||||
+ * change default DABRX to allow user watchpoints
|
||||
+ */
|
||||
+ mtspr(SPRN_DABRX, DABRX_KERNEL | DABRX_USER);
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(timebase_lock);
|
||||
@@ -192,7 +197,7 @@ static struct smp_ops_t bpa_iic_smp_ops
|
||||
.message_pass = smp_iic_message_pass,
|
||||
.probe = smp_iic_probe,
|
||||
.kick_cpu = smp_cell_kick_cpu,
|
||||
- .setup_cpu = smp_iic_setup_cpu,
|
||||
+ .setup_cpu = smp_cell_setup_cpu,
|
||||
.cpu_bootable = smp_cell_cpu_bootable,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,513 @@
|
||||
From: Tony Breeds <tony@bakeyournoodle.com>
|
||||
Subject: [PATCH] powerpc: Improve resolution of VDSO clock_gettime
|
||||
References: 439908 - LTC49499
|
||||
|
||||
Currently the clock_gettime implementation in the VDSO produces a
|
||||
result with microsecond resolution for the cases that are handled
|
||||
without a system call, i.e. CLOCK_REALTIME and CLOCK_MONOTONIC. The
|
||||
nanoseconds field of the result is obtained by computing a
|
||||
microseconds value and multiplying by 1000.
|
||||
|
||||
This changes the code in the VDSO to do the computation for
|
||||
clock_gettime with nanosecond resolution. That means that the
|
||||
resolution of the result will ultimately depend on the timebase
|
||||
frequency.
|
||||
|
||||
Because the timestamp in the VDSO datapage (stamp_xsec, the real time
|
||||
corresponding to the timebase count in tb_orig_stamp) is in units of
|
||||
2^-20 seconds, it doesn't have sufficient resolution for computing a
|
||||
result with nanosecond resolution. Therefore this adds a copy of
|
||||
xtime to the VDSO datapage and updates it in update_gtod() along with
|
||||
the other time-related fields.
|
||||
|
||||
Signed-off-by: Paul Mackerras <paulus@samba.org>
|
||||
Signed-off-by: Tony Breeds <tony@bakeyournoodle.com>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
arch/powerpc/include/asm/vdso_datapage.h | 3
|
||||
arch/powerpc/kernel/asm-offsets.c | 1
|
||||
arch/powerpc/kernel/time.c | 1
|
||||
arch/powerpc/kernel/vdso32/gettimeofday.S | 196 ++++++++++++++++++------------
|
||||
arch/powerpc/kernel/vdso64/gettimeofday.S | 143 +++++++++++----------
|
||||
5 files changed, 205 insertions(+), 139 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/include/asm/vdso_datapage.h
|
||||
+++ b/arch/powerpc/include/asm/vdso_datapage.h
|
||||
@@ -39,6 +39,7 @@
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <linux/unistd.h>
|
||||
+#include <linux/time.h>
|
||||
|
||||
#define SYSCALL_MAP_SIZE ((__NR_syscalls + 31) / 32)
|
||||
|
||||
@@ -83,6 +84,7 @@ struct vdso_data {
|
||||
__u32 icache_log_block_size; /* L1 i-cache log block size */
|
||||
__s32 wtom_clock_sec; /* Wall to monotonic clock */
|
||||
__s32 wtom_clock_nsec;
|
||||
+ struct timespec stamp_xtime; /* xtime value for tb_orig_stamp */
|
||||
__u32 syscall_map_64[SYSCALL_MAP_SIZE]; /* map of syscalls */
|
||||
__u32 syscall_map_32[SYSCALL_MAP_SIZE]; /* map of syscalls */
|
||||
};
|
||||
@@ -102,6 +104,7 @@ struct vdso_data {
|
||||
__u32 tz_dsttime; /* Type of dst correction 0x5C */
|
||||
__s32 wtom_clock_sec; /* Wall to monotonic clock */
|
||||
__s32 wtom_clock_nsec;
|
||||
+ struct timespec stamp_xtime; /* xtime value for tb_orig_stamp */
|
||||
__u32 syscall_map_32[SYSCALL_MAP_SIZE]; /* map of syscalls */
|
||||
__u32 dcache_block_size; /* L1 d-cache block size */
|
||||
__u32 icache_block_size; /* L1 i-cache block size */
|
||||
--- a/arch/powerpc/kernel/asm-offsets.c
|
||||
+++ b/arch/powerpc/kernel/asm-offsets.c
|
||||
@@ -304,6 +304,7 @@ int main(void)
|
||||
DEFINE(CFG_SYSCALL_MAP32, offsetof(struct vdso_data, syscall_map_32));
|
||||
DEFINE(WTOM_CLOCK_SEC, offsetof(struct vdso_data, wtom_clock_sec));
|
||||
DEFINE(WTOM_CLOCK_NSEC, offsetof(struct vdso_data, wtom_clock_nsec));
|
||||
+ DEFINE(STAMP_XTIME, offsetof(struct vdso_data, stamp_xtime));
|
||||
DEFINE(CFG_ICACHE_BLOCKSZ, offsetof(struct vdso_data, icache_block_size));
|
||||
DEFINE(CFG_DCACHE_BLOCKSZ, offsetof(struct vdso_data, dcache_block_size));
|
||||
DEFINE(CFG_ICACHE_LOGBLOCKSZ, offsetof(struct vdso_data, icache_log_block_size));
|
||||
--- a/arch/powerpc/kernel/time.c
|
||||
+++ b/arch/powerpc/kernel/time.c
|
||||
@@ -456,6 +456,7 @@ static inline void update_gtod(u64 new_t
|
||||
vdso_data->tb_to_xs = new_tb_to_xs;
|
||||
vdso_data->wtom_clock_sec = wall_to_monotonic.tv_sec;
|
||||
vdso_data->wtom_clock_nsec = wall_to_monotonic.tv_nsec;
|
||||
+ vdso_data->stamp_xtime = xtime;
|
||||
smp_wmb();
|
||||
++(vdso_data->tb_update_count);
|
||||
}
|
||||
--- a/arch/powerpc/kernel/vdso32/gettimeofday.S
|
||||
+++ b/arch/powerpc/kernel/vdso32/gettimeofday.S
|
||||
@@ -16,6 +16,13 @@
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
+/* Offset for the low 32-bit part of a field of long type */
|
||||
+#ifdef CONFIG_PPC64
|
||||
+#define LOPART 4
|
||||
+#else
|
||||
+#define LOPART 0
|
||||
+#endif
|
||||
+
|
||||
.text
|
||||
/*
|
||||
* Exact prototype of gettimeofday
|
||||
@@ -90,101 +97,53 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime)
|
||||
|
||||
mflr r12 /* r12 saves lr */
|
||||
.cfi_register lr,r12
|
||||
- mr r10,r3 /* r10 saves id */
|
||||
mr r11,r4 /* r11 saves tp */
|
||||
bl __get_datapage@local /* get data page */
|
||||
mr r9,r3 /* datapage ptr in r9 */
|
||||
- beq cr1,50f /* if monotonic -> jump there */
|
||||
-
|
||||
- /*
|
||||
- * CLOCK_REALTIME
|
||||
- */
|
||||
-
|
||||
- bl __do_get_xsec@local /* get xsec from tb & kernel */
|
||||
- bne- 98f /* out of line -> do syscall */
|
||||
-
|
||||
- /* seconds are xsec >> 20 */
|
||||
- rlwinm r5,r4,12,20,31
|
||||
- rlwimi r5,r3,12,0,19
|
||||
- stw r5,TSPC32_TV_SEC(r11)
|
||||
|
||||
- /* get remaining xsec and convert to nsec. we scale
|
||||
- * up remaining xsec by 12 bits and get the top 32 bits
|
||||
- * of the multiplication, then we multiply by 1000
|
||||
- */
|
||||
- rlwinm r5,r4,12,0,19
|
||||
- lis r6,1000000@h
|
||||
- ori r6,r6,1000000@l
|
||||
- mulhwu r5,r5,r6
|
||||
- mulli r5,r5,1000
|
||||
- stw r5,TSPC32_TV_NSEC(r11)
|
||||
- mtlr r12
|
||||
- crclr cr0*4+so
|
||||
- li r3,0
|
||||
- blr
|
||||
+50: bl __do_get_tspec@local /* get sec/nsec from tb & kernel */
|
||||
+ bne cr1,80f /* not monotonic -> all done */
|
||||
|
||||
/*
|
||||
* CLOCK_MONOTONIC
|
||||
*/
|
||||
|
||||
-50: bl __do_get_xsec@local /* get xsec from tb & kernel */
|
||||
- bne- 98f /* out of line -> do syscall */
|
||||
-
|
||||
- /* seconds are xsec >> 20 */
|
||||
- rlwinm r6,r4,12,20,31
|
||||
- rlwimi r6,r3,12,0,19
|
||||
-
|
||||
- /* get remaining xsec and convert to nsec. we scale
|
||||
- * up remaining xsec by 12 bits and get the top 32 bits
|
||||
- * of the multiplication, then we multiply by 1000
|
||||
- */
|
||||
- rlwinm r7,r4,12,0,19
|
||||
- lis r5,1000000@h
|
||||
- ori r5,r5,1000000@l
|
||||
- mulhwu r7,r7,r5
|
||||
- mulli r7,r7,1000
|
||||
-
|
||||
/* now we must fixup using wall to monotonic. We need to snapshot
|
||||
* that value and do the counter trick again. Fortunately, we still
|
||||
* have the counter value in r8 that was returned by __do_get_xsec.
|
||||
- * At this point, r6,r7 contain our sec/nsec values, r3,r4 and r5
|
||||
- * can be used
|
||||
+ * At this point, r3,r4 contain our sec/nsec values, r5 and r6
|
||||
+ * can be used, r7 contains NSEC_PER_SEC.
|
||||
*/
|
||||
|
||||
- lwz r3,WTOM_CLOCK_SEC(r9)
|
||||
- lwz r4,WTOM_CLOCK_NSEC(r9)
|
||||
+ lwz r5,WTOM_CLOCK_SEC(r9)
|
||||
+ lwz r6,WTOM_CLOCK_NSEC(r9)
|
||||
|
||||
- /* We now have our result in r3,r4. We create a fake dependency
|
||||
- * on that result and re-check the counter
|
||||
+ /* We now have our offset in r5,r6. We create a fake dependency
|
||||
+ * on that value and re-check the counter
|
||||
*/
|
||||
- or r5,r4,r3
|
||||
- xor r0,r5,r5
|
||||
+ or r0,r6,r5
|
||||
+ xor r0,r0,r0
|
||||
add r9,r9,r0
|
||||
-#ifdef CONFIG_PPC64
|
||||
- lwz r0,(CFG_TB_UPDATE_COUNT+4)(r9)
|
||||
-#else
|
||||
- lwz r0,(CFG_TB_UPDATE_COUNT)(r9)
|
||||
-#endif
|
||||
+ lwz r0,(CFG_TB_UPDATE_COUNT+LOPART)(r9)
|
||||
cmpl cr0,r8,r0 /* check if updated */
|
||||
bne- 50b
|
||||
|
||||
- /* Calculate and store result. Note that this mimmics the C code,
|
||||
+ /* Calculate and store result. Note that this mimics the C code,
|
||||
* which may cause funny results if nsec goes negative... is that
|
||||
* possible at all ?
|
||||
*/
|
||||
- add r3,r3,r6
|
||||
- add r4,r4,r7
|
||||
- lis r5,NSEC_PER_SEC@h
|
||||
- ori r5,r5,NSEC_PER_SEC@l
|
||||
- cmpl cr0,r4,r5
|
||||
- cmpli cr1,r4,0
|
||||
+ add r3,r3,r5
|
||||
+ add r4,r4,r6
|
||||
+ cmpw cr0,r4,r7
|
||||
+ cmpwi cr1,r4,0
|
||||
blt 1f
|
||||
- subf r4,r5,r4
|
||||
+ subf r4,r7,r4
|
||||
addi r3,r3,1
|
||||
-1: bge cr1,1f
|
||||
+1: bge cr1,80f
|
||||
addi r3,r3,-1
|
||||
- add r4,r4,r5
|
||||
-1: stw r3,TSPC32_TV_SEC(r11)
|
||||
+ add r4,r4,r7
|
||||
+
|
||||
+80: stw r3,TSPC32_TV_SEC(r11)
|
||||
stw r4,TSPC32_TV_NSEC(r11)
|
||||
|
||||
mtlr r12
|
||||
@@ -195,10 +154,6 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime)
|
||||
/*
|
||||
* syscall fallback
|
||||
*/
|
||||
-98:
|
||||
- mtlr r12
|
||||
- mr r3,r10
|
||||
- mr r4,r11
|
||||
99:
|
||||
li r0,__NR_clock_gettime
|
||||
sc
|
||||
@@ -322,3 +277,98 @@ __do_get_xsec:
|
||||
*/
|
||||
3: blr
|
||||
.cfi_endproc
|
||||
+
|
||||
+/*
|
||||
+ * This is the core of clock_gettime(), it returns the current
|
||||
+ * time in seconds and nanoseconds in r3 and r4.
|
||||
+ * It expects the datapage ptr in r9 and doesn't clobber it.
|
||||
+ * It clobbers r0, r5, r6, r10 and returns NSEC_PER_SEC in r7.
|
||||
+ * On return, r8 contains the counter value that can be reused.
|
||||
+ * This clobbers cr0 but not any other cr field.
|
||||
+ */
|
||||
+__do_get_tspec:
|
||||
+ .cfi_startproc
|
||||
+ /* Check for update count & load values. We use the low
|
||||
+ * order 32 bits of the update count
|
||||
+ */
|
||||
+1: lwz r8,(CFG_TB_UPDATE_COUNT+LOPART)(r9)
|
||||
+ andi. r0,r8,1 /* pending update ? loop */
|
||||
+ bne- 1b
|
||||
+ xor r0,r8,r8 /* create dependency */
|
||||
+ add r9,r9,r0
|
||||
+
|
||||
+ /* Load orig stamp (offset to TB) */
|
||||
+ lwz r5,CFG_TB_ORIG_STAMP(r9)
|
||||
+ lwz r6,(CFG_TB_ORIG_STAMP+4)(r9)
|
||||
+
|
||||
+ /* Get a stable TB value */
|
||||
+2: mftbu r3
|
||||
+ mftbl r4
|
||||
+ mftbu r0
|
||||
+ cmpl cr0,r3,r0
|
||||
+ bne- 2b
|
||||
+
|
||||
+ /* Subtract tb orig stamp and shift left 12 bits.
|
||||
+ */
|
||||
+ subfc r7,r6,r4
|
||||
+ subfe r0,r5,r3
|
||||
+ slwi r0,r0,12
|
||||
+ rlwimi. r0,r7,12,20,31
|
||||
+ slwi r7,r7,12
|
||||
+
|
||||
+ /* Load scale factor & do multiplication */
|
||||
+ lwz r5,CFG_TB_TO_XS(r9) /* load values */
|
||||
+ lwz r6,(CFG_TB_TO_XS+4)(r9)
|
||||
+ mulhwu r3,r7,r6
|
||||
+ mullw r10,r7,r5
|
||||
+ mulhwu r4,r7,r5
|
||||
+ addc r10,r3,r10
|
||||
+ li r3,0
|
||||
+
|
||||
+ beq+ 4f /* skip high part computation if 0 */
|
||||
+ mulhwu r3,r0,r5
|
||||
+ mullw r7,r0,r5
|
||||
+ mulhwu r5,r0,r6
|
||||
+ mullw r6,r0,r6
|
||||
+ adde r4,r4,r7
|
||||
+ addze r3,r3
|
||||
+ addc r4,r4,r5
|
||||
+ addze r3,r3
|
||||
+ addc r10,r10,r6
|
||||
+
|
||||
+4: addze r4,r4 /* add in carry */
|
||||
+ lis r7,NSEC_PER_SEC@h
|
||||
+ ori r7,r7,NSEC_PER_SEC@l
|
||||
+ mulhwu r4,r4,r7 /* convert to nanoseconds */
|
||||
+
|
||||
+ /* At this point, we have seconds & nanoseconds since the xtime
|
||||
+ * stamp in r3+CA and r4. Load & add the xtime stamp.
|
||||
+ */
|
||||
+#ifdef CONFIG_PPC64
|
||||
+ lwz r5,STAMP_XTIME+TSPC64_TV_SEC+LOPART(r9)
|
||||
+ lwz r6,STAMP_XTIME+TSPC64_TV_NSEC+LOPART(r9)
|
||||
+#else
|
||||
+ lwz r5,STAMP_XTIME+TSPC32_TV_SEC(r9)
|
||||
+ lwz r6,STAMP_XTIME+TSPC32_TV_NSEC(r9)
|
||||
+#endif
|
||||
+ add r4,r4,r6
|
||||
+ adde r3,r3,r5
|
||||
+
|
||||
+ /* We now have our result in r3,r4. We create a fake dependency
|
||||
+ * on that result and re-check the counter
|
||||
+ */
|
||||
+ or r6,r4,r3
|
||||
+ xor r0,r6,r6
|
||||
+ add r9,r9,r0
|
||||
+ lwz r0,(CFG_TB_UPDATE_COUNT+LOPART)(r9)
|
||||
+ cmpl cr0,r8,r0 /* check if updated */
|
||||
+ bne- 1b
|
||||
+
|
||||
+ /* check for nanosecond overflow and adjust if necessary */
|
||||
+ cmpw r4,r7
|
||||
+ bltlr /* all done if no overflow */
|
||||
+ subf r4,r7,r4 /* adjust if overflow */
|
||||
+ addi r3,r3,1
|
||||
+
|
||||
+ blr
|
||||
+ .cfi_endproc
|
||||
--- a/arch/powerpc/kernel/vdso64/gettimeofday.S
|
||||
+++ b/arch/powerpc/kernel/vdso64/gettimeofday.S
|
||||
@@ -75,90 +75,49 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime)
|
||||
|
||||
mflr r12 /* r12 saves lr */
|
||||
.cfi_register lr,r12
|
||||
- mr r10,r3 /* r10 saves id */
|
||||
mr r11,r4 /* r11 saves tp */
|
||||
bl V_LOCAL_FUNC(__get_datapage) /* get data page */
|
||||
- beq cr1,50f /* if monotonic -> jump there */
|
||||
-
|
||||
- /*
|
||||
- * CLOCK_REALTIME
|
||||
- */
|
||||
-
|
||||
- bl V_LOCAL_FUNC(__do_get_xsec) /* get xsec from tb & kernel */
|
||||
-
|
||||
- lis r7,15 /* r7 = 1000000 = USEC_PER_SEC */
|
||||
- ori r7,r7,16960
|
||||
- rldicl r5,r4,44,20 /* r5 = sec = xsec / XSEC_PER_SEC */
|
||||
- rldicr r6,r5,20,43 /* r6 = sec * XSEC_PER_SEC */
|
||||
- std r5,TSPC64_TV_SEC(r11) /* store sec in tv */
|
||||
- subf r0,r6,r4 /* r0 = xsec = (xsec - r6) */
|
||||
- mulld r0,r0,r7 /* usec = (xsec * USEC_PER_SEC) /
|
||||
- * XSEC_PER_SEC
|
||||
- */
|
||||
- rldicl r0,r0,44,20
|
||||
- mulli r0,r0,1000 /* nsec = usec * 1000 */
|
||||
- std r0,TSPC64_TV_NSEC(r11) /* store nsec in tp */
|
||||
-
|
||||
- mtlr r12
|
||||
- crclr cr0*4+so
|
||||
- li r3,0
|
||||
- blr
|
||||
+50: bl V_LOCAL_FUNC(__do_get_tspec) /* get time from tb & kernel */
|
||||
+ bne cr1,80f /* if not monotonic, all done */
|
||||
|
||||
/*
|
||||
* CLOCK_MONOTONIC
|
||||
*/
|
||||
|
||||
-50: bl V_LOCAL_FUNC(__do_get_xsec) /* get xsec from tb & kernel */
|
||||
-
|
||||
- lis r7,15 /* r7 = 1000000 = USEC_PER_SEC */
|
||||
- ori r7,r7,16960
|
||||
- rldicl r5,r4,44,20 /* r5 = sec = xsec / XSEC_PER_SEC */
|
||||
- rldicr r6,r5,20,43 /* r6 = sec * XSEC_PER_SEC */
|
||||
- subf r0,r6,r4 /* r0 = xsec = (xsec - r6) */
|
||||
- mulld r0,r0,r7 /* usec = (xsec * USEC_PER_SEC) /
|
||||
- * XSEC_PER_SEC
|
||||
- */
|
||||
- rldicl r6,r0,44,20
|
||||
- mulli r6,r6,1000 /* nsec = usec * 1000 */
|
||||
-
|
||||
/* now we must fixup using wall to monotonic. We need to snapshot
|
||||
* that value and do the counter trick again. Fortunately, we still
|
||||
- * have the counter value in r8 that was returned by __do_get_xsec.
|
||||
- * At this point, r5,r6 contain our sec/nsec values.
|
||||
- * can be used
|
||||
+ * have the counter value in r8 that was returned by __do_get_tspec.
|
||||
+ * At this point, r4,r5 contain our sec/nsec values.
|
||||
*/
|
||||
|
||||
- lwa r4,WTOM_CLOCK_SEC(r3)
|
||||
- lwa r7,WTOM_CLOCK_NSEC(r3)
|
||||
+ lwa r6,WTOM_CLOCK_SEC(r3)
|
||||
+ lwa r9,WTOM_CLOCK_NSEC(r3)
|
||||
|
||||
- /* We now have our result in r4,r7. We create a fake dependency
|
||||
+ /* We now have our result in r6,r9. We create a fake dependency
|
||||
* on that result and re-check the counter
|
||||
*/
|
||||
- or r9,r4,r7
|
||||
- xor r0,r9,r9
|
||||
+ or r0,r6,r9
|
||||
+ xor r0,r0,r0
|
||||
add r3,r3,r0
|
||||
ld r0,CFG_TB_UPDATE_COUNT(r3)
|
||||
cmpld cr0,r0,r8 /* check if updated */
|
||||
bne- 50b
|
||||
|
||||
- /* Calculate and store result. Note that this mimmics the C code,
|
||||
- * which may cause funny results if nsec goes negative... is that
|
||||
- * possible at all ?
|
||||
- */
|
||||
- add r4,r4,r5
|
||||
- add r7,r7,r6
|
||||
- lis r9,NSEC_PER_SEC@h
|
||||
- ori r9,r9,NSEC_PER_SEC@l
|
||||
- cmpl cr0,r7,r9
|
||||
- cmpli cr1,r7,0
|
||||
+ /* Add wall->monotonic offset and check for overflow or underflow.
|
||||
+ */
|
||||
+ add r4,r4,r6
|
||||
+ add r5,r5,r9
|
||||
+ cmpd cr0,r5,r7
|
||||
+ cmpdi cr1,r5,0
|
||||
blt 1f
|
||||
- subf r7,r9,r7
|
||||
+ subf r5,r7,r5
|
||||
addi r4,r4,1
|
||||
-1: bge cr1,1f
|
||||
+1: bge cr1,80f
|
||||
addi r4,r4,-1
|
||||
- add r7,r7,r9
|
||||
-1: std r4,TSPC64_TV_SEC(r11)
|
||||
- std r7,TSPC64_TV_NSEC(r11)
|
||||
+ add r5,r5,r7
|
||||
+
|
||||
+80: std r4,TSPC64_TV_SEC(r11)
|
||||
+ std r5,TSPC64_TV_NSEC(r11)
|
||||
|
||||
mtlr r12
|
||||
crclr cr0*4+so
|
||||
@@ -168,10 +127,6 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime)
|
||||
/*
|
||||
* syscall fallback
|
||||
*/
|
||||
-98:
|
||||
- mtlr r12
|
||||
- mr r3,r10
|
||||
- mr r4,r11
|
||||
99:
|
||||
li r0,__NR_clock_gettime
|
||||
sc
|
||||
@@ -253,3 +208,59 @@ V_FUNCTION_BEGIN(__do_get_xsec)
|
||||
blr
|
||||
.cfi_endproc
|
||||
V_FUNCTION_END(__do_get_xsec)
|
||||
+
|
||||
+/*
|
||||
+ * This is the core of clock_gettime(), it returns the current
|
||||
+ * time in seconds and nanoseconds in r4 and r5.
|
||||
+ * It expects the datapage ptr in r3 and doesn't clobber it.
|
||||
+ * It clobbers r0 and r6 and returns NSEC_PER_SEC in r7.
|
||||
+ * On return, r8 contains the counter value that can be reused.
|
||||
+ * This clobbers cr0 but not any other cr field.
|
||||
+ */
|
||||
+V_FUNCTION_BEGIN(__do_get_tspec)
|
||||
+ .cfi_startproc
|
||||
+ /* check for update count & load values */
|
||||
+1: ld r8,CFG_TB_UPDATE_COUNT(r3)
|
||||
+ andi. r0,r8,1 /* pending update ? loop */
|
||||
+ bne- 1b
|
||||
+ xor r0,r8,r8 /* create dependency */
|
||||
+ add r3,r3,r0
|
||||
+
|
||||
+ /* Get TB & offset it. We use the MFTB macro which will generate
|
||||
+ * workaround code for Cell.
|
||||
+ */
|
||||
+ MFTB(r7)
|
||||
+ ld r9,CFG_TB_ORIG_STAMP(r3)
|
||||
+ subf r7,r9,r7
|
||||
+
|
||||
+ /* Scale result */
|
||||
+ ld r5,CFG_TB_TO_XS(r3)
|
||||
+ sldi r7,r7,12 /* compute time since stamp_xtime */
|
||||
+ mulhdu r6,r7,r5 /* in units of 2^-32 seconds */
|
||||
+
|
||||
+ /* Add stamp since epoch */
|
||||
+ ld r4,STAMP_XTIME+TSPC64_TV_SEC(r3)
|
||||
+ ld r5,STAMP_XTIME+TSPC64_TV_NSEC(r3)
|
||||
+ or r0,r4,r5
|
||||
+ or r0,r0,r6
|
||||
+ xor r0,r0,r0
|
||||
+ add r3,r3,r0
|
||||
+ ld r0,CFG_TB_UPDATE_COUNT(r3)
|
||||
+ cmpld r0,r8 /* check if updated */
|
||||
+ bne- 1b /* reload if so */
|
||||
+
|
||||
+ /* convert to seconds & nanoseconds and add to stamp */
|
||||
+ lis r7,NSEC_PER_SEC@h
|
||||
+ ori r7,r7,NSEC_PER_SEC@l
|
||||
+ mulhwu r0,r6,r7 /* compute nanoseconds and */
|
||||
+ srdi r6,r6,32 /* seconds since stamp_xtime */
|
||||
+ clrldi r0,r0,32
|
||||
+ add r5,r5,r0 /* add nanoseconds together */
|
||||
+ cmpd r5,r7 /* overflow? */
|
||||
+ add r4,r4,r6
|
||||
+ bltlr /* all done if no overflow */
|
||||
+ subf r5,r7,r5 /* if overflow, adjust */
|
||||
+ addi r4,r4,1
|
||||
+ blr
|
||||
+ .cfi_endproc
|
||||
+V_FUNCTION_END(__do_get_tspec)
|
||||
@@ -0,0 +1,755 @@
|
||||
Subject: Efika ATA DMA
|
||||
From: Matt Sealey <matt@genesi-usa.com>
|
||||
References: 445856
|
||||
|
||||
Enables UDMA operation for ATA disks on Efika, meaning faster (up to
|
||||
~33MB/s, from ~1.2MB/s) access and lower (5-10% from 40-80%) CPU usage.
|
||||
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
arch/powerpc/sysdev/bestcomm/ata.c | 3
|
||||
arch/powerpc/sysdev/bestcomm/ata.h | 2
|
||||
arch/powerpc/sysdev/bestcomm/bestcomm.c | 7
|
||||
arch/powerpc/sysdev/bestcomm/bestcomm.h | 33 +-
|
||||
arch/powerpc/sysdev/bestcomm/bestcomm_priv.h | 20 +
|
||||
drivers/ata/pata_mpc52xx.c | 440 +++++++++++++++++++++++++--
|
||||
6 files changed, 473 insertions(+), 32 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/sysdev/bestcomm/ata.c
|
||||
+++ b/arch/powerpc/sysdev/bestcomm/ata.c
|
||||
@@ -61,6 +61,9 @@ bcom_ata_init(int queue_len, int maxbufs
|
||||
struct bcom_ata_var *var;
|
||||
struct bcom_ata_inc *inc;
|
||||
|
||||
+ /* Prefetch breaks ATA DMA. Turn it off for ATA DMA */
|
||||
+ bcom_disable_prefetch();
|
||||
+
|
||||
tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_ata_bd), 0);
|
||||
if (!tsk)
|
||||
return NULL;
|
||||
--- a/arch/powerpc/sysdev/bestcomm/ata.h
|
||||
+++ b/arch/powerpc/sysdev/bestcomm/ata.h
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
struct bcom_ata_bd {
|
||||
u32 status;
|
||||
- u32 dst_pa;
|
||||
u32 src_pa;
|
||||
+ u32 dst_pa;
|
||||
};
|
||||
|
||||
extern struct bcom_task *
|
||||
--- a/arch/powerpc/sysdev/bestcomm/bestcomm.c
|
||||
+++ b/arch/powerpc/sysdev/bestcomm/bestcomm.c
|
||||
@@ -279,7 +279,6 @@ bcom_engine_init(void)
|
||||
int task;
|
||||
phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa;
|
||||
unsigned int tdt_size, ctx_size, var_size, fdt_size;
|
||||
- u16 regval;
|
||||
|
||||
/* Allocate & clear SRAM zones for FDT, TDTs, contexts and vars/incs */
|
||||
tdt_size = BCOM_MAX_TASKS * sizeof(struct bcom_tdt);
|
||||
@@ -331,10 +330,8 @@ bcom_engine_init(void)
|
||||
out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS);
|
||||
|
||||
/* Disable COMM Bus Prefetch on the original 5200; it's broken */
|
||||
- if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR) {
|
||||
- regval = in_be16(&bcom_eng->regs->PtdCntrl);
|
||||
- out_be16(&bcom_eng->regs->PtdCntrl, regval | 1);
|
||||
- }
|
||||
+ if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR)
|
||||
+ bcom_disable_prefetch();
|
||||
|
||||
/* Init lock */
|
||||
spin_lock_init(&bcom_eng->lock);
|
||||
--- a/arch/powerpc/sysdev/bestcomm/bestcomm.h
|
||||
+++ b/arch/powerpc/sysdev/bestcomm/bestcomm.h
|
||||
@@ -140,15 +140,29 @@ bcom_queue_full(struct bcom_task *tsk)
|
||||
}
|
||||
|
||||
/**
|
||||
+ * bcom_get_bd - Get a BD from the queue
|
||||
+ * @tsk: The BestComm task structure
|
||||
+ * index: Index of the BD to fetch
|
||||
+ */
|
||||
+static inline struct bcom_bd
|
||||
+*bcom_get_bd(struct bcom_task *tsk, unsigned int index)
|
||||
+{
|
||||
+ return tsk->bd + index * tsk->bd_size;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
* bcom_buffer_done - Checks if a BestComm
|
||||
* @tsk: The BestComm task structure
|
||||
*/
|
||||
static inline int
|
||||
bcom_buffer_done(struct bcom_task *tsk)
|
||||
{
|
||||
+ struct bcom_bd *bd;
|
||||
if (bcom_queue_empty(tsk))
|
||||
return 0;
|
||||
- return !(tsk->bd[tsk->outdex].status & BCOM_BD_READY);
|
||||
+
|
||||
+ bd = bcom_get_bd(tsk, tsk->outdex);
|
||||
+ return !(bd->status & BCOM_BD_READY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,16 +174,21 @@ bcom_buffer_done(struct bcom_task *tsk)
|
||||
static inline struct bcom_bd *
|
||||
bcom_prepare_next_buffer(struct bcom_task *tsk)
|
||||
{
|
||||
- tsk->bd[tsk->index].status = 0; /* cleanup last status */
|
||||
- return &tsk->bd[tsk->index];
|
||||
+ struct bcom_bd *bd;
|
||||
+
|
||||
+ bd = bcom_get_bd(tsk, tsk->index);
|
||||
+ bd->status = 0; /* cleanup last status */
|
||||
+ return bd;
|
||||
}
|
||||
|
||||
static inline void
|
||||
bcom_submit_next_buffer(struct bcom_task *tsk, void *cookie)
|
||||
{
|
||||
+ struct bcom_bd *bd = bcom_get_bd(tsk, tsk->index);
|
||||
+
|
||||
tsk->cookie[tsk->index] = cookie;
|
||||
mb(); /* ensure the bd is really up-to-date */
|
||||
- tsk->bd[tsk->index].status |= BCOM_BD_READY;
|
||||
+ bd->status |= BCOM_BD_READY;
|
||||
tsk->index = _bcom_next_index(tsk);
|
||||
if (tsk->flags & BCOM_FLAGS_ENABLE_TASK)
|
||||
bcom_enable(tsk);
|
||||
@@ -179,10 +198,12 @@ static inline void *
|
||||
bcom_retrieve_buffer(struct bcom_task *tsk, u32 *p_status, struct bcom_bd **p_bd)
|
||||
{
|
||||
void *cookie = tsk->cookie[tsk->outdex];
|
||||
+ struct bcom_bd *bd = bcom_get_bd(tsk, tsk->outdex);
|
||||
+
|
||||
if (p_status)
|
||||
- *p_status = tsk->bd[tsk->outdex].status;
|
||||
+ *p_status = bd->status;
|
||||
if (p_bd)
|
||||
- *p_bd = &tsk->bd[tsk->outdex];
|
||||
+ *p_bd = bd;
|
||||
tsk->outdex = _bcom_next_outdex(tsk);
|
||||
return cookie;
|
||||
}
|
||||
--- a/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
|
||||
+++ b/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
|
||||
@@ -198,8 +198,8 @@ struct bcom_task_header {
|
||||
#define BCOM_IPR_SCTMR_1 2
|
||||
#define BCOM_IPR_FEC_RX 6
|
||||
#define BCOM_IPR_FEC_TX 5
|
||||
-#define BCOM_IPR_ATA_RX 4
|
||||
-#define BCOM_IPR_ATA_TX 3
|
||||
+#define BCOM_IPR_ATA_RX 7
|
||||
+#define BCOM_IPR_ATA_TX 7
|
||||
#define BCOM_IPR_SCPCI_RX 2
|
||||
#define BCOM_IPR_SCPCI_TX 2
|
||||
#define BCOM_IPR_PSC3_RX 2
|
||||
@@ -241,6 +241,22 @@ extern void bcom_set_initiator(int task,
|
||||
|
||||
#define TASK_ENABLE 0x8000
|
||||
|
||||
+/**
|
||||
+ * bcom_disable_prefetch - Hook to disable bus prefetching
|
||||
+ *
|
||||
+ * ATA DMA and the original MPC5200 need this due to silicon bugs. At the
|
||||
+ * moment disabling prefetch is a one-way street. There is no mechanism
|
||||
+ * in place to turn prefetch back on after it has been disabled. There is
|
||||
+ * no reason it couldn't be done, it would just be more complex to implement.
|
||||
+ */
|
||||
+static inline void bcom_disable_prefetch(void)
|
||||
+{
|
||||
+ u16 regval;
|
||||
+
|
||||
+ regval = in_be16(&bcom_eng->regs->PtdCntrl);
|
||||
+ out_be16(&bcom_eng->regs->PtdCntrl, regval | 1);
|
||||
+};
|
||||
+
|
||||
static inline void
|
||||
bcom_enable_task(int task)
|
||||
{
|
||||
--- a/drivers/ata/pata_mpc52xx.c
|
||||
+++ b/drivers/ata/pata_mpc52xx.c
|
||||
@@ -6,6 +6,9 @@
|
||||
* Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
|
||||
* Copyright (C) 2003 Mipsys - Benjamin Herrenschmidt
|
||||
*
|
||||
+ * UDMA support based on patches by Freescale (Bernard Kuhn, John Rigby),
|
||||
+ * Domen Puncer and Tim Yamin.
|
||||
+ *
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
@@ -18,27 +21,46 @@
|
||||
#include <linux/libata.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
+#include <asm/cacheflush.h>
|
||||
#include <asm/types.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/mpc52xx.h>
|
||||
|
||||
+#include <sysdev/bestcomm/bestcomm.h>
|
||||
+#include <sysdev/bestcomm/bestcomm_priv.h>
|
||||
+#include <sysdev/bestcomm/ata.h>
|
||||
|
||||
#define DRV_NAME "mpc52xx_ata"
|
||||
#define DRV_VERSION "0.1.2"
|
||||
|
||||
-
|
||||
/* Private structures used by the driver */
|
||||
struct mpc52xx_ata_timings {
|
||||
u32 pio1;
|
||||
u32 pio2;
|
||||
+ u32 mdma1;
|
||||
+ u32 mdma2;
|
||||
+ u32 udma1;
|
||||
+ u32 udma2;
|
||||
+ u32 udma3;
|
||||
+ u32 udma4;
|
||||
+ u32 udma5;
|
||||
+ int using_udma;
|
||||
};
|
||||
|
||||
struct mpc52xx_ata_priv {
|
||||
unsigned int ipb_period;
|
||||
struct mpc52xx_ata __iomem * ata_regs;
|
||||
+ phys_addr_t ata_regs_pa;
|
||||
int ata_irq;
|
||||
struct mpc52xx_ata_timings timings[2];
|
||||
int csel;
|
||||
+
|
||||
+ /* DMA */
|
||||
+ struct bcom_task *dmatsk;
|
||||
+ const struct udmaspec *udmaspec;
|
||||
+ const struct mdmaspec *mdmaspec;
|
||||
+ int mpc52xx_ata_dma_last_write;
|
||||
+ int waiting_for_dma;
|
||||
};
|
||||
|
||||
|
||||
@@ -53,6 +75,107 @@ static const int ataspec_ta[5] = { 35
|
||||
|
||||
#define CALC_CLKCYC(c,v) ((((v)+(c)-1)/(c)))
|
||||
|
||||
+/* ======================================================================== */
|
||||
+
|
||||
+/* ATAPI-4 MDMA specs (in clocks) */
|
||||
+struct mdmaspec {
|
||||
+ u32 t0M;
|
||||
+ u32 td;
|
||||
+ u32 th;
|
||||
+ u32 tj;
|
||||
+ u32 tkw;
|
||||
+ u32 tm;
|
||||
+ u32 tn;
|
||||
+};
|
||||
+
|
||||
+static const struct mdmaspec mdmaspec66[3] = {
|
||||
+ { .t0M = 32, .td = 15, .th = 2, .tj = 2, .tkw = 15, .tm = 4, .tn = 1 },
|
||||
+ { .t0M = 10, .td = 6, .th = 1, .tj = 1, .tkw = 4, .tm = 2, .tn = 1 },
|
||||
+ { .t0M = 8, .td = 5, .th = 1, .tj = 1, .tkw = 2, .tm = 2, .tn = 1 },
|
||||
+};
|
||||
+
|
||||
+static const struct mdmaspec mdmaspec132[3] = {
|
||||
+ { .t0M = 64, .td = 29, .th = 3, .tj = 3, .tkw = 29, .tm = 7, .tn = 2 },
|
||||
+ { .t0M = 20, .td = 11, .th = 2, .tj = 1, .tkw = 7, .tm = 4, .tn = 1 },
|
||||
+ { .t0M = 16, .td = 10, .th = 2, .tj = 1, .tkw = 4, .tm = 4, .tn = 1 },
|
||||
+};
|
||||
+
|
||||
+/* ATAPI-4 UDMA specs (in clocks) */
|
||||
+struct udmaspec {
|
||||
+ u32 tcyc;
|
||||
+ u32 t2cyc;
|
||||
+ u32 tds;
|
||||
+ u32 tdh;
|
||||
+ u32 tdvs;
|
||||
+ u32 tdvh;
|
||||
+ u32 tfs;
|
||||
+ u32 tli;
|
||||
+ u32 tmli;
|
||||
+ u32 taz;
|
||||
+ u32 tzah;
|
||||
+ u32 tenv;
|
||||
+ u32 tsr;
|
||||
+ u32 trfs;
|
||||
+ u32 trp;
|
||||
+ u32 tack;
|
||||
+ u32 tss;
|
||||
+};
|
||||
+
|
||||
+static const struct udmaspec udmaspec66[6] = {
|
||||
+ { .tcyc = 8, .t2cyc = 16, .tds = 1, .tdh = 1, .tdvs = 5, .tdvh = 1,
|
||||
+ .tfs = 16, .tli = 10, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
|
||||
+ .tsr = 3, .trfs = 5, .trp = 11, .tack = 2, .tss = 4,
|
||||
+ },
|
||||
+ { .tcyc = 5, .t2cyc = 11, .tds = 1, .tdh = 1, .tdvs = 4, .tdvh = 1,
|
||||
+ .tfs = 14, .tli = 10, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
|
||||
+ .tsr = 2, .trfs = 5, .trp = 9, .tack = 2, .tss = 4,
|
||||
+ },
|
||||
+ { .tcyc = 4, .t2cyc = 8, .tds = 1, .tdh = 1, .tdvs = 3, .tdvh = 1,
|
||||
+ .tfs = 12, .tli = 10, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
|
||||
+ .tsr = 2, .trfs = 4, .trp = 7, .tack = 2, .tss = 4,
|
||||
+ },
|
||||
+ { .tcyc = 3, .t2cyc = 6, .tds = 1, .tdh = 1, .tdvs = 2, .tdvh = 1,
|
||||
+ .tfs = 9, .tli = 7, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
|
||||
+ .tsr = 2, .trfs = 4, .trp = 7, .tack = 2, .tss = 4,
|
||||
+ },
|
||||
+ { .tcyc = 2, .t2cyc = 4, .tds = 1, .tdh = 1, .tdvs = 1, .tdvh = 1,
|
||||
+ .tfs = 8, .tli = 8, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
|
||||
+ .tsr = 2, .trfs = 4, .trp = 7, .tack = 2, .tss = 4,
|
||||
+ },
|
||||
+ { .tcyc = 2, .t2cyc = 2, .tds = 1, .tdh = 1, .tdvs = 1, .tdvh = 1,
|
||||
+ .tfs = 6, .tli = 5, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
|
||||
+ .tsr = 2, .trfs = 4, .trp = 6, .tack = 2, .tss = 4,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const struct udmaspec udmaspec132[6] = {
|
||||
+ { .tcyc = 15, .t2cyc = 31, .tds = 2, .tdh = 1, .tdvs = 10, .tdvh = 1,
|
||||
+ .tfs = 30, .tli = 20, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
|
||||
+ .tsr = 7, .trfs = 10, .trp = 22, .tack = 3, .tss = 7,
|
||||
+ },
|
||||
+ { .tcyc = 10, .t2cyc = 21, .tds = 2, .tdh = 1, .tdvs = 7, .tdvh = 1,
|
||||
+ .tfs = 27, .tli = 20, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
|
||||
+ .tsr = 4, .trfs = 10, .trp = 17, .tack = 3, .tss = 7,
|
||||
+ },
|
||||
+ { .tcyc = 6, .t2cyc = 12, .tds = 1, .tdh = 1, .tdvs = 5, .tdvh = 1,
|
||||
+ .tfs = 23, .tli = 20, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
|
||||
+ .tsr = 3, .trfs = 8, .trp = 14, .tack = 3, .tss = 7,
|
||||
+ },
|
||||
+ { .tcyc = 7, .t2cyc = 12, .tds = 1, .tdh = 1, .tdvs = 3, .tdvh = 1,
|
||||
+ .tfs = 15, .tli = 13, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
|
||||
+ .tsr = 3, .trfs = 8, .trp = 14, .tack = 3, .tss = 7,
|
||||
+ },
|
||||
+ { .tcyc = 2, .t2cyc = 5, .tds = 0, .tdh = 0, .tdvs = 1, .tdvh = 1,
|
||||
+ .tfs = 16, .tli = 14, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
|
||||
+ .tsr = 2, .trfs = 7, .trp = 13, .tack = 2, .tss = 6,
|
||||
+ },
|
||||
+ { .tcyc = 3, .t2cyc = 6, .tds = 1, .tdh = 1, .tdvs = 1, .tdvh = 1,
|
||||
+ .tfs = 12, .tli = 10, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
|
||||
+ .tsr = 3, .trfs = 7, .trp = 12, .tack = 3, .tss = 7,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+/* ======================================================================== */
|
||||
|
||||
/* Bit definitions inside the registers */
|
||||
#define MPC52xx_ATA_HOSTCONF_SMR 0x80000000UL /* State machine reset */
|
||||
@@ -66,6 +189,7 @@ static const int ataspec_ta[5] = { 35
|
||||
#define MPC52xx_ATA_HOSTSTAT_WERR 0x01000000UL /* Write Error */
|
||||
|
||||
#define MPC52xx_ATA_FIFOSTAT_EMPTY 0x01 /* FIFO Empty */
|
||||
+#define MPC52xx_ATA_FIFOSTAT_ERROR 0x40 /* FIFO Error */
|
||||
|
||||
#define MPC52xx_ATA_DMAMODE_WRITE 0x01 /* Write DMA */
|
||||
#define MPC52xx_ATA_DMAMODE_READ 0x02 /* Read DMA */
|
||||
@@ -75,6 +199,8 @@ static const int ataspec_ta[5] = { 35
|
||||
#define MPC52xx_ATA_DMAMODE_FR 0x20 /* FIFO Reset */
|
||||
#define MPC52xx_ATA_DMAMODE_HUT 0x40 /* Host UDMA burst terminate */
|
||||
|
||||
+#define MAX_DMA_BUFFERS 128
|
||||
+#define MAX_DMA_BUFFER_SIZE 0x20000u
|
||||
|
||||
/* Structure of the hardware registers */
|
||||
struct mpc52xx_ata {
|
||||
@@ -140,7 +266,6 @@ struct mpc52xx_ata {
|
||||
|
||||
|
||||
/* MPC52xx low level hw control */
|
||||
-
|
||||
static int
|
||||
mpc52xx_ata_compute_pio_timings(struct mpc52xx_ata_priv *priv, int dev, int pio)
|
||||
{
|
||||
@@ -165,6 +290,42 @@ mpc52xx_ata_compute_pio_timings(struct m
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int
|
||||
+mpc52xx_ata_compute_mdma_timings(struct mpc52xx_ata_priv *priv, int dev,
|
||||
+ int speed)
|
||||
+{
|
||||
+ struct mpc52xx_ata_timings *t = &priv->timings[dev];
|
||||
+ const struct mdmaspec *s = &priv->mdmaspec[speed];
|
||||
+
|
||||
+ if (speed < 0 || speed > 2)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ t->mdma1 = (s->t0M << 24) | (s->td << 16) | (s->tkw << 8) | (s->tm);
|
||||
+ t->mdma2 = (s->th << 24) | (s->tj << 16) | (s->tn << 8);
|
||||
+ t->using_udma = 0;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mpc52xx_ata_compute_udma_timings(struct mpc52xx_ata_priv *priv, int dev, int speed)
|
||||
+{
|
||||
+ struct mpc52xx_ata_timings *t = &priv->timings[dev];
|
||||
+ const struct udmaspec *s = &priv->udmaspec[speed];
|
||||
+
|
||||
+ if (speed < 0 || speed > 2)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ t->udma1 = (s->t2cyc << 24) | (s->tcyc << 16) | (s->tds << 8) | s->tdh;
|
||||
+ t->udma2 = (s->tdvs << 24) | (s->tdvh << 16) | (s->tfs << 8) | s->tli;
|
||||
+ t->udma3 = (s->tmli << 24) | (s->taz << 16) | (s->tenv << 8) | s->tsr;
|
||||
+ t->udma4 = (s->tss << 24) | (s->trfs << 16) | (s->trp << 8) | s->tack;
|
||||
+ t->udma5 = (s->tzah << 24);
|
||||
+ t->using_udma = 1;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static void
|
||||
mpc52xx_ata_apply_timings(struct mpc52xx_ata_priv *priv, int device)
|
||||
{
|
||||
@@ -173,14 +334,13 @@ mpc52xx_ata_apply_timings(struct mpc52xx
|
||||
|
||||
out_be32(®s->pio1, timing->pio1);
|
||||
out_be32(®s->pio2, timing->pio2);
|
||||
- out_be32(®s->mdma1, 0);
|
||||
- out_be32(®s->mdma2, 0);
|
||||
- out_be32(®s->udma1, 0);
|
||||
- out_be32(®s->udma2, 0);
|
||||
- out_be32(®s->udma3, 0);
|
||||
- out_be32(®s->udma4, 0);
|
||||
- out_be32(®s->udma5, 0);
|
||||
-
|
||||
+ out_be32(®s->mdma1, timing->mdma1);
|
||||
+ out_be32(®s->mdma2, timing->mdma2);
|
||||
+ out_be32(®s->udma1, timing->udma1);
|
||||
+ out_be32(®s->udma2, timing->udma2);
|
||||
+ out_be32(®s->udma3, timing->udma3);
|
||||
+ out_be32(®s->udma4, timing->udma4);
|
||||
+ out_be32(®s->udma5, timing->udma5);
|
||||
priv->csel = device;
|
||||
}
|
||||
|
||||
@@ -244,6 +404,31 @@ mpc52xx_ata_set_piomode(struct ata_port
|
||||
|
||||
mpc52xx_ata_apply_timings(priv, adev->devno);
|
||||
}
|
||||
+
|
||||
+static void
|
||||
+mpc52xx_ata_set_dmamode(struct ata_port *ap, struct ata_device *adev)
|
||||
+{
|
||||
+ struct mpc52xx_ata_priv *priv = ap->host->private_data;
|
||||
+ int rv;
|
||||
+
|
||||
+ if (adev->dma_mode >= XFER_UDMA_0) {
|
||||
+ int dma = adev->dma_mode - XFER_UDMA_0;
|
||||
+ rv = mpc52xx_ata_compute_udma_timings(priv, adev->devno, dma);
|
||||
+ } else {
|
||||
+ int dma = adev->dma_mode - XFER_MW_DMA_0;
|
||||
+ rv = mpc52xx_ata_compute_mdma_timings(priv, adev->devno, dma);
|
||||
+ }
|
||||
+
|
||||
+ if (rv) {
|
||||
+ dev_alert(ap->dev,
|
||||
+ "Trying to select invalid DMA mode %d\n",
|
||||
+ adev->dma_mode);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ mpc52xx_ata_apply_timings(priv, adev->devno);
|
||||
+}
|
||||
+
|
||||
static void
|
||||
mpc52xx_ata_dev_select(struct ata_port *ap, unsigned int device)
|
||||
{
|
||||
@@ -255,6 +440,172 @@ mpc52xx_ata_dev_select(struct ata_port *
|
||||
ata_sff_dev_select(ap,device);
|
||||
}
|
||||
|
||||
+static int
|
||||
+mpc52xx_ata_build_dmatable(struct ata_queued_cmd *qc)
|
||||
+{
|
||||
+ struct ata_port *ap = qc->ap;
|
||||
+ struct mpc52xx_ata_priv *priv = ap->host->private_data;
|
||||
+ struct bcom_ata_bd *bd;
|
||||
+ unsigned int read = !(qc->tf.flags & ATA_TFLAG_WRITE), si;
|
||||
+ struct scatterlist *sg;
|
||||
+ int count = 0;
|
||||
+
|
||||
+ if (read)
|
||||
+ bcom_ata_rx_prepare(priv->dmatsk);
|
||||
+ else
|
||||
+ bcom_ata_tx_prepare(priv->dmatsk);
|
||||
+
|
||||
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
|
||||
+ dma_addr_t cur_addr = sg_dma_address(sg);
|
||||
+ u32 cur_len = sg_dma_len(sg);
|
||||
+
|
||||
+ while (cur_len) {
|
||||
+ unsigned int tc = min(cur_len, MAX_DMA_BUFFER_SIZE);
|
||||
+ bd = (struct bcom_ata_bd *)
|
||||
+ bcom_prepare_next_buffer(priv->dmatsk);
|
||||
+
|
||||
+ if (read) {
|
||||
+ bd->status = tc;
|
||||
+ bd->src_pa = (__force u32) priv->ata_regs_pa +
|
||||
+ offsetof(struct mpc52xx_ata, fifo_data);
|
||||
+ bd->dst_pa = (__force u32) cur_addr;
|
||||
+ } else {
|
||||
+ bd->status = tc;
|
||||
+ bd->src_pa = (__force u32) cur_addr;
|
||||
+ bd->dst_pa = (__force u32) priv->ata_regs_pa +
|
||||
+ offsetof(struct mpc52xx_ata, fifo_data);
|
||||
+ }
|
||||
+
|
||||
+ bcom_submit_next_buffer(priv->dmatsk, NULL);
|
||||
+
|
||||
+ cur_addr += tc;
|
||||
+ cur_len -= tc;
|
||||
+ count++;
|
||||
+
|
||||
+ if (count > MAX_DMA_BUFFERS) {
|
||||
+ dev_alert(ap->dev, "dma table"
|
||||
+ "too small\n");
|
||||
+ goto use_pio_instead;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return 1;
|
||||
+
|
||||
+ use_pio_instead:
|
||||
+ bcom_ata_reset_bd(priv->dmatsk);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+mpc52xx_bmdma_setup(struct ata_queued_cmd *qc)
|
||||
+{
|
||||
+ struct ata_port *ap = qc->ap;
|
||||
+ struct mpc52xx_ata_priv *priv = ap->host->private_data;
|
||||
+ struct mpc52xx_ata __iomem *regs = priv->ata_regs;
|
||||
+
|
||||
+ unsigned int read = !(qc->tf.flags & ATA_TFLAG_WRITE);
|
||||
+ u8 dma_mode;
|
||||
+
|
||||
+ if (!mpc52xx_ata_build_dmatable(qc))
|
||||
+ dev_alert(ap->dev, "%s: %i, return 1?\n",
|
||||
+ __func__, __LINE__);
|
||||
+
|
||||
+ /* Check FIFO is OK... */
|
||||
+ if (in_8(&priv->ata_regs->fifo_status) & MPC52xx_ATA_FIFOSTAT_ERROR)
|
||||
+ dev_alert(ap->dev, "%s: FIFO error detected: 0x%02x!\n",
|
||||
+ __func__, in_8(&priv->ata_regs->fifo_status));
|
||||
+
|
||||
+ if (read) {
|
||||
+ dma_mode = MPC52xx_ATA_DMAMODE_IE | MPC52xx_ATA_DMAMODE_READ |
|
||||
+ MPC52xx_ATA_DMAMODE_FE;
|
||||
+
|
||||
+ /* Setup FIFO if direction changed */
|
||||
+ if (priv->mpc52xx_ata_dma_last_write != 0) {
|
||||
+ priv->mpc52xx_ata_dma_last_write = 0;
|
||||
+
|
||||
+ /* Configure FIFO with granularity to 7 */
|
||||
+ out_8(®s->fifo_control, 7);
|
||||
+ out_be16(®s->fifo_alarm, 128);
|
||||
+
|
||||
+ /* Set FIFO Reset bit (FR) */
|
||||
+ out_8(®s->dma_mode, MPC52xx_ATA_DMAMODE_FR);
|
||||
+ }
|
||||
+ } else {
|
||||
+ dma_mode = MPC52xx_ATA_DMAMODE_IE | MPC52xx_ATA_DMAMODE_WRITE;
|
||||
+
|
||||
+ /* Setup FIFO if direction changed */
|
||||
+ if (priv->mpc52xx_ata_dma_last_write != 1) {
|
||||
+ priv->mpc52xx_ata_dma_last_write = 1;
|
||||
+
|
||||
+ /* Configure FIFO with granularity to 4 */
|
||||
+ out_8(®s->fifo_control, 4);
|
||||
+ out_be16(®s->fifo_alarm, 128);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (priv->timings[qc->dev->devno].using_udma)
|
||||
+ dma_mode |= MPC52xx_ATA_DMAMODE_UDMA;
|
||||
+
|
||||
+ out_8(®s->dma_mode, dma_mode);
|
||||
+ priv->waiting_for_dma = ATA_DMA_ACTIVE;
|
||||
+
|
||||
+ ata_wait_idle(ap);
|
||||
+ ap->ops->sff_exec_command(ap, &qc->tf);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+mpc52xx_bmdma_start(struct ata_queued_cmd *qc)
|
||||
+{
|
||||
+ struct ata_port *ap = qc->ap;
|
||||
+ struct mpc52xx_ata_priv *priv = ap->host->private_data;
|
||||
+
|
||||
+ bcom_set_task_auto_start(priv->dmatsk->tasknum, priv->dmatsk->tasknum);
|
||||
+ bcom_enable(priv->dmatsk);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+mpc52xx_bmdma_stop(struct ata_queued_cmd *qc)
|
||||
+{
|
||||
+ struct ata_port *ap = qc->ap;
|
||||
+ struct mpc52xx_ata_priv *priv = ap->host->private_data;
|
||||
+
|
||||
+ bcom_disable(priv->dmatsk);
|
||||
+ bcom_ata_reset_bd(priv->dmatsk);
|
||||
+ priv->waiting_for_dma = 0;
|
||||
+
|
||||
+ /* Check FIFO is OK... */
|
||||
+ if (in_8(&priv->ata_regs->fifo_status) & MPC52xx_ATA_FIFOSTAT_ERROR)
|
||||
+ dev_alert(ap->dev, "%s: FIFO error detected: 0x%02x!\n",
|
||||
+ __func__, in_8(&priv->ata_regs->fifo_status));
|
||||
+}
|
||||
+
|
||||
+static u8
|
||||
+mpc52xx_bmdma_status(struct ata_port *ap)
|
||||
+{
|
||||
+ struct mpc52xx_ata_priv *priv = ap->host->private_data;
|
||||
+
|
||||
+ /* Check FIFO is OK... */
|
||||
+ if (in_8(&priv->ata_regs->fifo_status) & MPC52xx_ATA_FIFOSTAT_ERROR) {
|
||||
+ dev_alert(ap->dev, "%s: FIFO error detected: 0x%02x!\n",
|
||||
+ __func__, in_8(&priv->ata_regs->fifo_status));
|
||||
+ return priv->waiting_for_dma | ATA_DMA_ERR;
|
||||
+ }
|
||||
+
|
||||
+ return priv->waiting_for_dma;
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t
|
||||
+mpc52xx_ata_task_irq(int irq, void *vpriv)
|
||||
+{
|
||||
+ struct mpc52xx_ata_priv *priv = vpriv;
|
||||
+ while (bcom_buffer_done(priv->dmatsk))
|
||||
+ bcom_retrieve_buffer(priv->dmatsk, NULL, NULL);
|
||||
+
|
||||
+ priv->waiting_for_dma |= ATA_DMA_INTR;
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
static struct scsi_host_template mpc52xx_ata_sht = {
|
||||
ATA_PIO_SHT(DRV_NAME),
|
||||
};
|
||||
@@ -262,14 +613,18 @@ static struct scsi_host_template mpc52xx
|
||||
static struct ata_port_operations mpc52xx_ata_port_ops = {
|
||||
.inherits = &ata_sff_port_ops,
|
||||
.sff_dev_select = mpc52xx_ata_dev_select,
|
||||
- .cable_detect = ata_cable_40wire,
|
||||
.set_piomode = mpc52xx_ata_set_piomode,
|
||||
- .post_internal_cmd = ATA_OP_NULL,
|
||||
+ .set_dmamode = mpc52xx_ata_set_dmamode,
|
||||
+ .bmdma_setup = mpc52xx_bmdma_setup,
|
||||
+ .bmdma_start = mpc52xx_bmdma_start,
|
||||
+ .bmdma_stop = mpc52xx_bmdma_stop,
|
||||
+ .bmdma_status = mpc52xx_bmdma_status,
|
||||
+ .qc_prep = ata_noop_qc_prep,
|
||||
};
|
||||
|
||||
static int __devinit
|
||||
mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv,
|
||||
- unsigned long raw_ata_regs)
|
||||
+ unsigned long raw_ata_regs, int mwdma_mask, int udma_mask)
|
||||
{
|
||||
struct ata_host *host;
|
||||
struct ata_port *ap;
|
||||
@@ -281,9 +636,9 @@ mpc52xx_ata_init_one(struct device *dev,
|
||||
|
||||
ap = host->ports[0];
|
||||
ap->flags |= ATA_FLAG_SLAVE_POSS;
|
||||
- ap->pio_mask = 0x1f; /* Up to PIO4 */
|
||||
- ap->mwdma_mask = 0x00; /* No MWDMA */
|
||||
- ap->udma_mask = 0x00; /* No UDMA */
|
||||
+ ap->pio_mask = ATA_PIO4;
|
||||
+ ap->mwdma_mask = mwdma_mask;
|
||||
+ ap->udma_mask = udma_mask;
|
||||
ap->ops = &mpc52xx_ata_port_ops;
|
||||
host->private_data = priv;
|
||||
|
||||
@@ -333,7 +688,10 @@ mpc52xx_ata_probe(struct of_device *op,
|
||||
int ata_irq;
|
||||
struct mpc52xx_ata __iomem *ata_regs;
|
||||
struct mpc52xx_ata_priv *priv;
|
||||
- int rv;
|
||||
+ int rv, ret, task_irq;
|
||||
+ int mwdma_mask = 0, udma_mask = 0;
|
||||
+ const __be32 *prop;
|
||||
+ int proplen;
|
||||
|
||||
/* Get ipb frequency */
|
||||
ipb_freq = mpc52xx_find_ipb_freq(op->node);
|
||||
@@ -351,6 +709,27 @@ mpc52xx_ata_probe(struct of_device *op,
|
||||
return rv;
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * By default, all DMA modes are disabled for the MPC5200. Some
|
||||
+ * boards don't have the required signals routed to make DMA work.
|
||||
+ * Also, the MPC5200B has a silicon bug that causes data corruption
|
||||
+ * with UDMA if it is used at the same time as the LocalPlus bus.
|
||||
+ *
|
||||
+ * Instead of trying to guess what modes are usable, check the
|
||||
+ * ATA device tree node to find out what DMA modes work on the board.
|
||||
+ * UDMA/MWDMA modes can also be forced by adding "libata.force=<mode>"
|
||||
+ * to the kernel boot parameters.
|
||||
+ *
|
||||
+ * The MPC5200 ATA controller supports MWDMA modes 0, 1 and 2 and
|
||||
+ * UDMA modes 0, 1 and 2.
|
||||
+ */
|
||||
+ prop = of_get_property(op->node, "mwdma-mode", &proplen);
|
||||
+ if ((prop) && (proplen >= 4))
|
||||
+ mwdma_mask = 0x7 & ((1 << (*prop + 1)) - 1);
|
||||
+ prop = of_get_property(op->node, "udma-mode", &proplen);
|
||||
+ if ((prop) && (proplen >= 4))
|
||||
+ udma_mask = 0x7 & ((1 << (*prop + 1)) - 1);
|
||||
+
|
||||
ata_irq = irq_of_parse_and_map(op->node, 0);
|
||||
if (ata_irq == NO_IRQ) {
|
||||
printk(KERN_ERR DRV_NAME ": "
|
||||
@@ -389,8 +768,32 @@ mpc52xx_ata_probe(struct of_device *op,
|
||||
|
||||
priv->ipb_period = 1000000000 / (ipb_freq / 1000);
|
||||
priv->ata_regs = ata_regs;
|
||||
+ priv->ata_regs_pa = res_mem.start;
|
||||
priv->ata_irq = ata_irq;
|
||||
priv->csel = -1;
|
||||
+ priv->mpc52xx_ata_dma_last_write = -1;
|
||||
+
|
||||
+ if (ipb_freq/1000000 == 66) {
|
||||
+ priv->mdmaspec = mdmaspec66;
|
||||
+ priv->udmaspec = udmaspec66;
|
||||
+ } else {
|
||||
+ priv->mdmaspec = mdmaspec132;
|
||||
+ priv->udmaspec = udmaspec132;
|
||||
+ }
|
||||
+
|
||||
+ priv->dmatsk = bcom_ata_init(MAX_DMA_BUFFERS, MAX_DMA_BUFFER_SIZE);
|
||||
+ if (!priv->dmatsk) {
|
||||
+ dev_err(&op->dev, "bestcomm initialization failed\n");
|
||||
+ rv = -ENOMEM;
|
||||
+ goto err;
|
||||
+ }
|
||||
+
|
||||
+ task_irq = bcom_get_task_irq(priv->dmatsk);
|
||||
+ ret = request_irq(task_irq, &mpc52xx_ata_task_irq, IRQF_DISABLED,
|
||||
+ "ATA task", priv);
|
||||
+ if (ret)
|
||||
+ dev_alert(&op->dev, "request_irq failed with: "
|
||||
+ "%i\n", ret);
|
||||
|
||||
/* Init the hw */
|
||||
rv = mpc52xx_ata_hw_init(priv);
|
||||
@@ -400,7 +803,8 @@ mpc52xx_ata_probe(struct of_device *op,
|
||||
}
|
||||
|
||||
/* Register ourselves to libata */
|
||||
- rv = mpc52xx_ata_init_one(&op->dev, priv, res_mem.start);
|
||||
+ rv = mpc52xx_ata_init_one(&op->dev, priv, res_mem.start,
|
||||
+ mwdma_mask, udma_mask);
|
||||
if (rv) {
|
||||
printk(KERN_ERR DRV_NAME ": "
|
||||
"Error while registering to ATA layer\n");
|
||||
@@ -0,0 +1,892 @@
|
||||
From: Juergen Beisert <jbe@pengutronix.de>
|
||||
Subject: RFC: MPC5200 PSC AC97 driver
|
||||
|
||||
if someone is interested: Here the full patch to get sound support for
|
||||
MPC5200b and a current 2.6.25 kernel.
|
||||
|
||||
Acked-by: Olaf Hering <olh@novell.com>
|
||||
|
||||
---
|
||||
arch/powerpc/include/asm/mpc52xx_psc.h | 4
|
||||
arch/powerpc/kernel/prom_init.c | 3
|
||||
sound/ppc/Kconfig | 15
|
||||
sound/ppc/Makefile | 2
|
||||
sound/ppc/mpc52xx_ac97.c | 807 +++++++++++++++++++++++++++++++++
|
||||
5 files changed, 831 insertions(+)
|
||||
|
||||
--- a/arch/powerpc/include/asm/mpc52xx_psc.h
|
||||
+++ b/arch/powerpc/include/asm/mpc52xx_psc.h
|
||||
@@ -28,6 +28,10 @@
|
||||
#define MPC52xx_PSC_MAXNUM 6
|
||||
|
||||
/* Programmable Serial Controller (PSC) status register bits */
|
||||
+#define MPC52xx_PSC_SR_UNEX_RX 0x0001
|
||||
+#define MPC52xx_PSC_SR_DATA_VAL 0x0002
|
||||
+#define MPC52xx_PSC_SR_DATA_OVR 0x0004
|
||||
+#define MPC52xx_PSC_SR_CMDSEND 0x0008
|
||||
#define MPC52xx_PSC_SR_CDE 0x0080
|
||||
#define MPC52xx_PSC_SR_RXRDY 0x0100
|
||||
#define MPC52xx_PSC_SR_RXFULL 0x0200
|
||||
--- a/arch/powerpc/kernel/prom_init.c
|
||||
+++ b/arch/powerpc/kernel/prom_init.c
|
||||
@@ -2189,6 +2189,7 @@ static void __init fixup_device_tree_efi
|
||||
|
||||
static void __init fixup_device_tree_efika(void)
|
||||
{
|
||||
+ int sound_cell[1] = { 1 };
|
||||
int sound_irq[3] = { 2, 2, 0 };
|
||||
int bcomm_irq[3*16] = { 3,0,0, 3,1,0, 3,2,0, 3,3,0,
|
||||
3,4,0, 3,5,0, 3,6,0, 3,7,0,
|
||||
@@ -2244,6 +2245,8 @@ static void __init fixup_device_tree_efi
|
||||
prom_printf("Adding sound interrupts property\n");
|
||||
prom_setprop(node, "/builtin/sound", "interrupts",
|
||||
sound_irq, sizeof(sound_irq));
|
||||
+ prom_setprop(node, "/builtin/sound", "cell-index",
|
||||
+ sound_cell, sizeof(sound_cell));
|
||||
}
|
||||
}
|
||||
|
||||
--- a/sound/ppc/Kconfig
|
||||
+++ b/sound/ppc/Kconfig
|
||||
@@ -48,4 +48,19 @@ config SND_PS3_DEFAULT_START_DELAY
|
||||
depends on SND_PS3
|
||||
default "2000"
|
||||
|
||||
+
|
||||
+# ALSA ppc drivers
|
||||
+
|
||||
+menu "ALSA PPC devices"
|
||||
+ depends on SND!=n && PPC
|
||||
+
|
||||
+config SND_PPC_MPC52xx_AC97
|
||||
+ tristate "Freescale MPC52xx AC97 interface support"
|
||||
+ depends on SND && PPC_MPC52xx
|
||||
+ select SND_AC97_CODEC
|
||||
+ help
|
||||
+ Say Y or M if you want to support any AC97 codec attached to
|
||||
+ the Freescqle MPC52xx AC97 interface.
|
||||
+endmenu
|
||||
+
|
||||
endif # SND_PPC
|
||||
--- a/sound/ppc/Makefile
|
||||
+++ b/sound/ppc/Makefile
|
||||
@@ -4,7 +4,9 @@
|
||||
#
|
||||
|
||||
snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o
|
||||
+snd-mpc52xx-ac97-objs := mpc52xx_ac97.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
|
||||
obj-$(CONFIG_SND_PS3) += snd_ps3.o
|
||||
+obj-$(CONFIG_SND_PPC_MPC52xx_AC97) += snd-mpc52xx-ac97.o
|
||||
--- /dev/null
|
||||
+++ b/sound/ppc/mpc52xx_ac97.c
|
||||
@@ -0,0 +1,807 @@
|
||||
+/*
|
||||
+ * Driver for the PSC of the Freescale MPC52xx configured as AC97 interface
|
||||
+ *
|
||||
+ *
|
||||
+ * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
|
||||
+ *
|
||||
+ * This file is licensed under the terms of the GNU General Public License
|
||||
+ * version 2. This program is licensed "as is" without any warranty of any
|
||||
+ * kind, whether express or implied.
|
||||
+ */
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+
|
||||
+#include <sound/core.h>
|
||||
+#include <sound/initval.h>
|
||||
+#include <sound/pcm.h>
|
||||
+#include <sound/pcm_params.h>
|
||||
+#include <sound/ac97_codec.h>
|
||||
+
|
||||
+#include <asm/of_platform.h>
|
||||
+#include <linux/dma-mapping.h>
|
||||
+#include <asm/mpc52xx_psc.h>
|
||||
+
|
||||
+#include <sysdev/bestcomm/bestcomm.h>
|
||||
+#include <sysdev/bestcomm/gen_bd.h>
|
||||
+
|
||||
+
|
||||
+#define DRV_NAME "mpc52xx-psc-ac97"
|
||||
+
|
||||
+
|
||||
+/* ======================================================================== */
|
||||
+/* Structs / Defines */
|
||||
+/* ======================================================================== */
|
||||
+
|
||||
+/* Private structure */
|
||||
+struct mpc52xx_ac97_priv {
|
||||
+ struct device *dev;
|
||||
+ resource_size_t mem_start;
|
||||
+ resource_size_t mem_len;
|
||||
+ int irq;
|
||||
+ struct mpc52xx_psc __iomem *psc;
|
||||
+ struct mpc52xx_psc_fifo __iomem *fifo;
|
||||
+
|
||||
+ struct bcom_task *tsk_tx;
|
||||
+ spinlock_t dma_lock;
|
||||
+
|
||||
+ struct snd_card *card;
|
||||
+ struct snd_pcm *pcm;
|
||||
+ struct snd_ac97 *ac97;
|
||||
+
|
||||
+ struct snd_pcm_substream *substream_playback;
|
||||
+
|
||||
+ int period_byte_size;
|
||||
+ u32 period_start, period_end, period_next_p;
|
||||
+};
|
||||
+
|
||||
+/* Register bit definition (AC97 mode specific) */
|
||||
+#define PSC_AC97_SLOT_BIT(n) (1<<(12-n))
|
||||
+#define PSC_AC97_SLOTS_XMIT_SHIFT 16
|
||||
+#define PSC_AC97_SLOTS_RECV_SHIFT 0
|
||||
+
|
||||
+/* Bestcomm options */
|
||||
+#define AC97_TX_NUM_BD 32
|
||||
+#define AC97_RX_NUM_BD 32
|
||||
+
|
||||
+static int mpc52xx_ac97_tx_fill(struct mpc52xx_ac97_priv *priv)
|
||||
+{
|
||||
+ struct snd_pcm_runtime *rt;
|
||||
+
|
||||
+ u32 dma_data_ptr;
|
||||
+
|
||||
+ rt = priv->substream_playback->runtime;
|
||||
+
|
||||
+ dma_data_ptr = virt_to_phys(rt->dma_area);
|
||||
+
|
||||
+ priv->period_byte_size = frames_to_bytes(rt, rt->period_size);
|
||||
+ priv->period_start = dma_data_ptr;
|
||||
+ priv->period_end = dma_data_ptr + priv->period_byte_size * rt->periods;
|
||||
+ priv->period_next_p = dma_data_ptr;
|
||||
+
|
||||
+ spin_lock(&priv->dma_lock);
|
||||
+ while (!bcom_queue_full(priv->tsk_tx)) {
|
||||
+ struct bcom_gen_bd *bd;
|
||||
+
|
||||
+ /* Submit a new one */
|
||||
+ bd = (struct bcom_gen_bd *) bcom_prepare_next_buffer(priv->tsk_tx);
|
||||
+ bd->status = priv->period_byte_size;
|
||||
+ bd->buf_pa = priv->period_next_p;
|
||||
+ bcom_submit_next_buffer(priv->tsk_tx, NULL);
|
||||
+
|
||||
+ /* Next pointer */
|
||||
+ priv->period_next_p += priv->period_byte_size;
|
||||
+ if (priv->period_next_p >= priv->period_end)
|
||||
+ priv->period_next_p = priv->period_start;
|
||||
+ }
|
||||
+ spin_unlock(&priv->dma_lock);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/* ======================================================================== */
|
||||
+/* ISR routine */
|
||||
+/* ======================================================================== */
|
||||
+
|
||||
+static irqreturn_t mpc52xx_ac97_tx_irq(int irq, void *dev_id)
|
||||
+{
|
||||
+ struct mpc52xx_ac97_priv *priv = dev_id;
|
||||
+ struct snd_pcm_runtime *rt;
|
||||
+ struct bcom_gen_bd *bd;
|
||||
+
|
||||
+ rt = priv->substream_playback->runtime;
|
||||
+
|
||||
+ if (!bcom_buffer_done(priv->tsk_tx)) {
|
||||
+ dev_dbg(priv->dev, "tx mismatch? Check correct output PSC\n");
|
||||
+ bcom_disable(priv->tsk_tx);
|
||||
+ }
|
||||
+
|
||||
+ spin_lock(&priv->dma_lock);
|
||||
+ while (bcom_buffer_done(priv->tsk_tx)) {
|
||||
+ /* Get the buffer back */
|
||||
+ bcom_retrieve_buffer(priv->tsk_tx, NULL, NULL);
|
||||
+
|
||||
+ /* Submit a new one */
|
||||
+ bd = (struct bcom_gen_bd *) bcom_prepare_next_buffer(priv->tsk_tx);
|
||||
+ bd->status = priv->period_byte_size;
|
||||
+ bd->buf_pa = priv->period_next_p;
|
||||
+ bcom_submit_next_buffer(priv->tsk_tx, NULL);
|
||||
+ bcom_enable(priv->tsk_tx);
|
||||
+
|
||||
+ /* Next pointer */
|
||||
+ priv->period_next_p += priv->period_byte_size;
|
||||
+ if (priv->period_next_p >= priv->period_end)
|
||||
+ priv->period_next_p = priv->period_start;
|
||||
+ }
|
||||
+ spin_unlock(&priv->dma_lock);
|
||||
+
|
||||
+ snd_pcm_period_elapsed(priv->substream_playback);
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static irqreturn_t mpc52xx_ac97_irq(int irq, void *dev_id)
|
||||
+{
|
||||
+ struct mpc52xx_ac97_priv *priv = dev_id;
|
||||
+
|
||||
+ static int icnt = 0;
|
||||
+
|
||||
+#if 1
|
||||
+ /* Anti Crash during dev ;) */
|
||||
+ if ((icnt++) > 5000)
|
||||
+ out_be16(&priv->psc->mpc52xx_psc_imr, 0);
|
||||
+#endif
|
||||
+
|
||||
+ /* Print statuts */
|
||||
+ dev_dbg(priv->dev, "isr: %04x", in_be16(&priv->psc->mpc52xx_psc_imr));
|
||||
+ out_8(&priv->psc->command,MPC52xx_PSC_RST_ERR_STAT);
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+/* ======================================================================== */
|
||||
+/* PCM interface */
|
||||
+/* ======================================================================== */
|
||||
+
|
||||
+/* HW desc */
|
||||
+
|
||||
+static struct snd_pcm_hardware mpc52xx_ac97_hw = {
|
||||
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
+ SNDRV_PCM_INFO_MMAP |
|
||||
+ SNDRV_PCM_INFO_MMAP_VALID,
|
||||
+ .formats = SNDRV_PCM_FMTBIT_S32_BE,
|
||||
+ .rates = SNDRV_PCM_RATE_8000_48000,
|
||||
+ .rate_min = 8000,
|
||||
+ .rate_max = 48000,
|
||||
+ .channels_min = 1,
|
||||
+ .channels_max = 2, /* Support for more ? */
|
||||
+ .buffer_bytes_max = 1024*1024,
|
||||
+ .period_bytes_min = 512,
|
||||
+ .period_bytes_max = 16*1024,
|
||||
+ .periods_min = 8,
|
||||
+ .periods_max = 1024,
|
||||
+ .fifo_size = 512,
|
||||
+};
|
||||
+
|
||||
+
|
||||
+/* Playback */
|
||||
+
|
||||
+static int mpc52xx_ac97_playback_open(struct snd_pcm_substream *substream)
|
||||
+{
|
||||
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
|
||||
+
|
||||
+ dev_dbg(priv->dev, "mpc52xx_ac97_playback_open(%p)\n", substream);
|
||||
+
|
||||
+ substream->runtime->hw = mpc52xx_ac97_hw;
|
||||
+
|
||||
+ priv->substream_playback = substream;
|
||||
+
|
||||
+ return 0; /* FIXME */
|
||||
+}
|
||||
+
|
||||
+static int mpc52xx_ac97_playback_close(struct snd_pcm_substream *substream)
|
||||
+{
|
||||
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
|
||||
+ dev_dbg(priv->dev, "mpc52xx_ac97_playback_close(%p)\n", substream);
|
||||
+ priv->substream_playback = NULL;
|
||||
+ return 0; /* FIXME */
|
||||
+}
|
||||
+
|
||||
+static int mpc52xx_ac97_playback_prepare(struct snd_pcm_substream *substream)
|
||||
+{
|
||||
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
|
||||
+
|
||||
+ dev_dbg(priv->dev, "mpc52xx_ac97_playback_prepare(%p)\n", substream);
|
||||
+
|
||||
+ /* FIXME, need a spinlock to protect access */
|
||||
+ if (substream->runtime->channels == 1)
|
||||
+ out_be32(&priv->psc->ac97_slots, 0x01000000);
|
||||
+ else
|
||||
+ out_be32(&priv->psc->ac97_slots, 0x03000000);
|
||||
+
|
||||
+ snd_ac97_set_rate(priv->ac97, AC97_PCM_FRONT_DAC_RATE,
|
||||
+ substream->runtime->rate);
|
||||
+
|
||||
+ return 0; /* FIXME */
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/* Capture */
|
||||
+
|
||||
+static int mpc52xx_ac97_capture_open(struct snd_pcm_substream *substream)
|
||||
+{
|
||||
+/* struct mpc52xx_ac97_priv *priv = substream->pcm->private_data; */
|
||||
+ return 0; /* FIXME */
|
||||
+}
|
||||
+
|
||||
+static int mpc52xx_ac97_capture_close(struct snd_pcm_substream *substream)
|
||||
+{
|
||||
+/* struct mpc52xx_ac97_priv *priv = substream->pcm->private_data; */
|
||||
+ return 0; /* FIXME */
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mpc52xx_ac97_capture_prepare(struct snd_pcm_substream *substream)
|
||||
+{
|
||||
+/* struct mpc52xx_ac97_priv *priv = substream->pcm->private_data; */
|
||||
+ return 0; /* FIXME */
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/* Common */
|
||||
+
|
||||
+static int mpc52xx_ac97_hw_params(struct snd_pcm_substream *substream,
|
||||
+ struct snd_pcm_hw_params *params)
|
||||
+{
|
||||
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
|
||||
+ int rv;
|
||||
+
|
||||
+ dev_dbg(priv->dev, "mpc52xx_ac97_hw_params(%p)\n", substream);
|
||||
+
|
||||
+ rv = snd_pcm_lib_malloc_pages(substream,
|
||||
+ params_buffer_bytes(params));
|
||||
+ if (rv < 0) {
|
||||
+ printk(KERN_ERR "hw params failes\n"); /* FIXME */
|
||||
+ return rv;
|
||||
+ }
|
||||
+
|
||||
+ dev_dbg(priv->dev, "%d %d %d\n", params_buffer_bytes(params),
|
||||
+ params_period_bytes(params), params_periods(params));
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mpc52xx_ac97_hw_free(struct snd_pcm_substream *substream)
|
||||
+{
|
||||
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
|
||||
+
|
||||
+ dev_dbg(priv->dev, "mpc52xx_ac97_hw_free(%p)\n", substream);
|
||||
+
|
||||
+ return snd_pcm_lib_free_pages(substream);
|
||||
+}
|
||||
+
|
||||
+static int mpc52xx_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
+{
|
||||
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
|
||||
+ int rv = 0;
|
||||
+
|
||||
+ dev_dbg(priv->dev, "mpc52xx_ac97_trigger(%p,%d)\n", substream, cmd);
|
||||
+
|
||||
+ switch (cmd) {
|
||||
+ case SNDRV_PCM_TRIGGER_START:
|
||||
+ /* Enable TX taks */
|
||||
+ bcom_gen_bd_tx_reset(priv->tsk_tx);
|
||||
+ mpc52xx_ac97_tx_fill(priv);
|
||||
+ bcom_enable(priv->tsk_tx);
|
||||
+/*
|
||||
+ out_be16(&priv->psc->mpc52xx_psc_imr, 0x0800); // 0x0100
|
||||
+ out_be16(&priv->psc->mpc52xx_psc_imr, 0x0100); // 0x0100
|
||||
+*/
|
||||
+ /* FIXME: Shouldn't we check for overrun too ? */
|
||||
+ /* also, shouldn't we just activate TX here ? */
|
||||
+
|
||||
+ break;
|
||||
+
|
||||
+ case SNDRV_PCM_TRIGGER_STOP:
|
||||
+ /* Disable TX task */
|
||||
+ bcom_disable(priv->tsk_tx);
|
||||
+ out_be16(&priv->psc->mpc52xx_psc_imr, 0x0000); // 0x0100
|
||||
+
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ rv = -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* FIXME */
|
||||
+ return rv;
|
||||
+}
|
||||
+
|
||||
+static snd_pcm_uframes_t mpc52xx_ac97_pointer(struct snd_pcm_substream *substream)
|
||||
+{
|
||||
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
+ struct mpc52xx_ac97_priv *priv = substream->pcm->private_data;
|
||||
+ u32 count;
|
||||
+
|
||||
+ count = priv->tsk_tx->bd[priv->tsk_tx->outdex].data[0] - priv->period_start;
|
||||
+
|
||||
+ return bytes_to_frames(runtime, count);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/* Ops */
|
||||
+
|
||||
+static struct snd_pcm_ops mpc52xx_ac97_playback_ops = {
|
||||
+ .open = mpc52xx_ac97_playback_open,
|
||||
+ .close = mpc52xx_ac97_playback_close,
|
||||
+ .ioctl = snd_pcm_lib_ioctl,
|
||||
+ .hw_params = mpc52xx_ac97_hw_params,
|
||||
+ .hw_free = mpc52xx_ac97_hw_free,
|
||||
+ .prepare = mpc52xx_ac97_playback_prepare,
|
||||
+ .trigger = mpc52xx_ac97_trigger,
|
||||
+ .pointer = mpc52xx_ac97_pointer,
|
||||
+};
|
||||
+
|
||||
+static struct snd_pcm_ops mpc52xx_ac97_capture_ops = {
|
||||
+ .open = mpc52xx_ac97_capture_open,
|
||||
+ .close = mpc52xx_ac97_capture_close,
|
||||
+ .ioctl = snd_pcm_lib_ioctl,
|
||||
+ .hw_params = mpc52xx_ac97_hw_params,
|
||||
+ .hw_free = mpc52xx_ac97_hw_free,
|
||||
+ .prepare = mpc52xx_ac97_capture_prepare,
|
||||
+ .trigger = mpc52xx_ac97_trigger,
|
||||
+ .pointer = mpc52xx_ac97_pointer,
|
||||
+};
|
||||
+
|
||||
+
|
||||
+/* ======================================================================== */
|
||||
+/* AC97 Bus interface */
|
||||
+/* ======================================================================== */
|
||||
+
|
||||
+static unsigned short mpc52xx_ac97_bus_read(struct snd_ac97 *ac97,
|
||||
+ unsigned short reg)
|
||||
+{
|
||||
+ struct mpc52xx_ac97_priv *priv = ac97->private_data;
|
||||
+ int timeout;
|
||||
+ unsigned int val;
|
||||
+
|
||||
+ /* Wait for it to be ready */
|
||||
+ timeout = 1000;
|
||||
+ while ((--timeout) && (in_be16(&priv->psc->mpc52xx_psc_status) &
|
||||
+ MPC52xx_PSC_SR_CMDSEND) )
|
||||
+ udelay(10);
|
||||
+
|
||||
+ if (!timeout) {
|
||||
+ printk(KERN_ERR DRV_NAME ": timeout on ac97 bus (rdy)\n");
|
||||
+ return 0xffff;
|
||||
+ }
|
||||
+
|
||||
+ /* Do the read */
|
||||
+ out_be32(&priv->psc->ac97_cmd, (1<<31) | ((reg & 0x7f) << 24));
|
||||
+
|
||||
+ /* Wait for the answer */
|
||||
+ timeout = 1000;
|
||||
+ while ((--timeout) && !(in_be16(&priv->psc->mpc52xx_psc_status) &
|
||||
+ MPC52xx_PSC_SR_DATA_VAL) )
|
||||
+ udelay(10);
|
||||
+
|
||||
+ if (!timeout) {
|
||||
+ printk(KERN_ERR DRV_NAME ": timeout on ac97 read (val)\n");
|
||||
+ return 0xffff;
|
||||
+ }
|
||||
+
|
||||
+ /* Get the data */
|
||||
+ val = in_be32(&priv->psc->ac97_data);
|
||||
+ if ( ((val>>24) & 0x7f) != reg ) {
|
||||
+ printk(KERN_ERR DRV_NAME ": reg echo error on ac97 read\n");
|
||||
+ return 0xffff;
|
||||
+ }
|
||||
+ val = (val >> 8) & 0xffff;
|
||||
+
|
||||
+ return (unsigned short) val;
|
||||
+}
|
||||
+
|
||||
+static void mpc52xx_ac97_bus_write(struct snd_ac97 *ac97,
|
||||
+ unsigned short reg, unsigned short val)
|
||||
+{
|
||||
+ struct mpc52xx_ac97_priv *priv = ac97->private_data;
|
||||
+ int timeout;
|
||||
+
|
||||
+ /* Wait for it to be ready */
|
||||
+ timeout = 1000;
|
||||
+ while ((--timeout) && (in_be16(&priv->psc->mpc52xx_psc_status) &
|
||||
+ MPC52xx_PSC_SR_CMDSEND) )
|
||||
+ udelay(10);
|
||||
+
|
||||
+ if (!timeout) {
|
||||
+ printk(KERN_ERR DRV_NAME ": timeout on ac97 write\n");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /* Write data */
|
||||
+ out_be32(&priv->psc->ac97_cmd, ((reg & 0x7f) << 24) | (val << 8));
|
||||
+}
|
||||
+
|
||||
+static void mpc52xx_ac97_bus_reset(struct snd_ac97 *ac97)
|
||||
+{
|
||||
+ struct mpc52xx_ac97_priv *priv = ac97->private_data;
|
||||
+
|
||||
+ dev_dbg(priv->dev, "ac97 codec reset\n");
|
||||
+
|
||||
+ /* Do a cold reset */
|
||||
+ /*
|
||||
+ * Note: This could interfere with some external AC97 mixers, as it
|
||||
+ * could switch them into test mode, when SYNC or SDATA_OUT are not
|
||||
+ * low while RES is low!
|
||||
+ */
|
||||
+ out_8(&priv->psc->op1, 0x02);
|
||||
+ udelay(10);
|
||||
+ out_8(&priv->psc->op0, 0x02);
|
||||
+ udelay(50);
|
||||
+
|
||||
+ /* PSC recover from cold reset (cfr user manual, not sure if useful) */
|
||||
+ out_be32(&priv->psc->sicr, in_be32(&priv->psc->sicr));
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static struct snd_ac97_bus_ops mpc52xx_ac97_bus_ops = {
|
||||
+ .read = mpc52xx_ac97_bus_read,
|
||||
+ .write = mpc52xx_ac97_bus_write,
|
||||
+ .reset = mpc52xx_ac97_bus_reset,
|
||||
+};
|
||||
+
|
||||
+
|
||||
+/* ======================================================================== */
|
||||
+/* Sound driver setup */
|
||||
+/* ======================================================================== */
|
||||
+
|
||||
+static int mpc52xx_ac97_setup_pcm(struct mpc52xx_ac97_priv *priv)
|
||||
+{
|
||||
+ int rv;
|
||||
+
|
||||
+ rv = snd_pcm_new(priv->card, DRV_NAME "-pcm", 0, 1, 1, &priv->pcm);
|
||||
+ if (rv) {
|
||||
+ dev_dbg(priv->dev, "%s: snd_pcm_new failed\n", DRV_NAME);
|
||||
+ return rv;
|
||||
+ }
|
||||
+
|
||||
+ rv = snd_pcm_lib_preallocate_pages_for_all(priv->pcm,
|
||||
+ SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL),
|
||||
+ 128*1024, 128*1024);
|
||||
+ if (rv) {
|
||||
+ dev_dbg(priv->dev,
|
||||
+ "%s: snd_pcm_lib_preallocate_pages_for_all failed\n",
|
||||
+ DRV_NAME);
|
||||
+ return rv;
|
||||
+ }
|
||||
+
|
||||
+ snd_pcm_set_ops(priv->pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
||||
+ &mpc52xx_ac97_playback_ops);
|
||||
+ snd_pcm_set_ops(priv->pcm, SNDRV_PCM_STREAM_CAPTURE,
|
||||
+ &mpc52xx_ac97_capture_ops);
|
||||
+
|
||||
+ priv->pcm->private_data = priv;
|
||||
+ priv->pcm->info_flags = 0;
|
||||
+
|
||||
+ strcpy(priv->pcm->name, "Freescale MPC52xx PSC-AC97 PCM");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mpc52xx_ac97_setup_mixer(struct mpc52xx_ac97_priv *priv)
|
||||
+{
|
||||
+ struct snd_ac97_bus *ac97_bus;
|
||||
+ struct snd_ac97_template ac97_template;
|
||||
+ int rv;
|
||||
+
|
||||
+ rv = snd_ac97_bus(priv->card, 0, &mpc52xx_ac97_bus_ops, NULL, &ac97_bus);
|
||||
+ if (rv) {
|
||||
+ printk(KERN_ERR DRV_NAME ": snd_ac97_bus failed\n");
|
||||
+ return rv;
|
||||
+ }
|
||||
+
|
||||
+ memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
|
||||
+ ac97_template.private_data = priv;
|
||||
+
|
||||
+ rv = snd_ac97_mixer(ac97_bus, &ac97_template, &priv->ac97);
|
||||
+ if (rv) {
|
||||
+ printk(KERN_ERR DRV_NAME ": snd_ac97_mixer failed\n");
|
||||
+ return rv;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mpc52xx_ac97_hwinit(struct mpc52xx_ac97_priv *priv)
|
||||
+{
|
||||
+ /* Reset everything first by safety */
|
||||
+ out_8(&priv->psc->command,MPC52xx_PSC_RST_RX);
|
||||
+ out_8(&priv->psc->command,MPC52xx_PSC_RST_TX);
|
||||
+ out_8(&priv->psc->command,MPC52xx_PSC_RST_ERR_STAT);
|
||||
+
|
||||
+ /* Do a cold reset of codec */
|
||||
+ /*
|
||||
+ * Note: This could interfere with some external AC97 mixers, as it
|
||||
+ * could switch them into test mode, when SYNC or SDATA_OUT are not
|
||||
+ * low while RES is low!
|
||||
+ */
|
||||
+ out_8(&priv->psc->op1, 0x02);
|
||||
+ udelay(10);
|
||||
+ out_8(&priv->psc->op0, 0x02);
|
||||
+ udelay(50);
|
||||
+
|
||||
+ /* Configure AC97 enhanced mode */
|
||||
+ out_be32(&priv->psc->sicr, 0x03010000);
|
||||
+
|
||||
+ /* No slots active */
|
||||
+ out_be32(&priv->psc->ac97_slots, 0x00000000);
|
||||
+
|
||||
+ /* No IRQ */
|
||||
+ out_be16(&priv->psc->mpc52xx_psc_imr, 0x0000);
|
||||
+
|
||||
+ /* FIFO levels */
|
||||
+ out_8(&priv->fifo->rfcntl, 0x07);
|
||||
+ out_8(&priv->fifo->tfcntl, 0x07);
|
||||
+ out_be16(&priv->fifo->rfalarm, 0x80);
|
||||
+ out_be16(&priv->fifo->tfalarm, 0x80);
|
||||
+
|
||||
+ /* Go */
|
||||
+ out_8(&priv->psc->command,MPC52xx_PSC_TX_ENABLE);
|
||||
+ out_8(&priv->psc->command,MPC52xx_PSC_RX_ENABLE);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mpc52xx_ac97_hwshutdown(struct mpc52xx_ac97_priv *priv)
|
||||
+{
|
||||
+ /* No IRQ */
|
||||
+ out_be16(&priv->psc->mpc52xx_psc_imr, 0x0000);
|
||||
+
|
||||
+ /* Disable TB & RX */
|
||||
+ out_8(&priv->psc->command,MPC52xx_PSC_RST_RX);
|
||||
+ out_8(&priv->psc->command,MPC52xx_PSC_RST_TX);
|
||||
+
|
||||
+ /* FIXME : Reset or put codec in low power ? */
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* ======================================================================== */
|
||||
+/* OF Platform Driver */
|
||||
+/* ======================================================================== */
|
||||
+
|
||||
+static int __devinit
|
||||
+mpc52xx_ac97_probe(struct of_device *op, const struct of_device_id *match)
|
||||
+{
|
||||
+ struct device_node *dn = op->node;
|
||||
+ struct mpc52xx_ac97_priv *priv;
|
||||
+ struct snd_card *card;
|
||||
+ struct resource res;
|
||||
+ int tx_initiator;
|
||||
+ int rv;
|
||||
+ const unsigned int *devno;
|
||||
+
|
||||
+ dev_dbg(&op->dev, "probing MPC52xx PSC AC97 driver\n");
|
||||
+
|
||||
+ /* Get card structure */
|
||||
+ rv = -ENOMEM;
|
||||
+ card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
|
||||
+ THIS_MODULE, sizeof(struct mpc52xx_ac97_priv));
|
||||
+ if (!card)
|
||||
+ goto err_early;
|
||||
+
|
||||
+ priv = card->private_data;
|
||||
+
|
||||
+ /* Init our private structure */
|
||||
+ priv->card = card;
|
||||
+ priv->dev = &op->dev;
|
||||
+
|
||||
+ /* Get resources (mem,irq,...) */
|
||||
+ rv = of_address_to_resource(dn, 0, &res);
|
||||
+ if (rv)
|
||||
+ goto err_early;
|
||||
+
|
||||
+ priv->mem_start = res.start;
|
||||
+ priv->mem_len = res.end - res.start + 1;
|
||||
+
|
||||
+ if (!request_mem_region(priv->mem_start, priv->mem_len, DRV_NAME)) {
|
||||
+ dev_err(&op->dev, "%s: request_mem_region failed\n", DRV_NAME);
|
||||
+ rv = -EBUSY;
|
||||
+ goto err_early;
|
||||
+ }
|
||||
+
|
||||
+ priv->psc = ioremap(priv->mem_start, priv->mem_len);
|
||||
+ if (!priv->psc) {
|
||||
+ dev_err(&op->dev, "%s: ioremap failed\n", DRV_NAME);
|
||||
+ rv = -ENOMEM;
|
||||
+ goto err_iomap;
|
||||
+ }
|
||||
+ /* the fifo starts right after psc ends */
|
||||
+ priv->fifo = (struct mpc52xx_psc_fifo*)&priv->psc[1]; /* FIXME */
|
||||
+
|
||||
+ priv->irq = irq_of_parse_and_map(dn, 0);
|
||||
+ if (priv->irq == NO_IRQ) {
|
||||
+ dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n",
|
||||
+ DRV_NAME);
|
||||
+ rv = -EBUSY;
|
||||
+ goto err_irqmap;
|
||||
+ }
|
||||
+
|
||||
+ /* Setup Bestcomm tasks */
|
||||
+ spin_lock_init(&priv->dma_lock);
|
||||
+
|
||||
+ /*
|
||||
+ * PSC1 or PSC2 can be configured for AC97 usage. Select the right
|
||||
+ * channel, to let the BCOMM unit does its job correctly.
|
||||
+ */
|
||||
+ devno = of_get_property(dn, "cell-index", NULL);
|
||||
+ switch (*devno) {
|
||||
+ case 0: /* PSC1 */
|
||||
+ tx_initiator = 14;
|
||||
+ break;
|
||||
+ case 1: /* PSC2 */
|
||||
+ tx_initiator = 12;
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_dbg(priv->dev, "Unknown PSC unit for AC97 usage!\n");
|
||||
+ rv = -ENODEV;
|
||||
+ goto err_irq;
|
||||
+ }
|
||||
+
|
||||
+ priv->tsk_tx = bcom_gen_bd_tx_init(AC97_TX_NUM_BD,
|
||||
+ priv->mem_start + sizeof(struct mpc52xx_psc) +
|
||||
+ offsetof(struct mpc52xx_psc_fifo, tfdata),
|
||||
+ tx_initiator,
|
||||
+ 2); /* ipr : FIXME */
|
||||
+ if (!priv->tsk_tx) {
|
||||
+ dev_err(&op->dev, "%s: bcom_gen_bd_tx_init failed\n",
|
||||
+ DRV_NAME);
|
||||
+ rv = -ENOMEM;
|
||||
+ goto err_bcomm;
|
||||
+ }
|
||||
+
|
||||
+ /* Low level HW Init */
|
||||
+ mpc52xx_ac97_hwinit(priv);
|
||||
+
|
||||
+ /* Request IRQ now that we're 'stable' */
|
||||
+ rv = request_irq(priv->irq, mpc52xx_ac97_irq, 0, DRV_NAME, priv);
|
||||
+ if (rv < 0) {
|
||||
+ dev_err(&op->dev, "%s: request_irq failed\n", DRV_NAME);
|
||||
+ goto err_irqreq;
|
||||
+ }
|
||||
+
|
||||
+ rv = request_irq(bcom_get_task_irq(priv->tsk_tx),
|
||||
+ mpc52xx_ac97_tx_irq, 0, DRV_NAME "_tx", priv);
|
||||
+ if (rv < 0) {
|
||||
+ dev_err(&op->dev, "%s: request_irq failed\n", DRV_NAME);
|
||||
+ goto err_txirqreq;
|
||||
+ }
|
||||
+
|
||||
+ /* Prepare sound stuff */
|
||||
+ rv = mpc52xx_ac97_setup_mixer(priv);
|
||||
+ if (rv)
|
||||
+ goto err_late;
|
||||
+
|
||||
+ rv = mpc52xx_ac97_setup_pcm(priv);
|
||||
+ if (rv)
|
||||
+ goto err_late;
|
||||
+
|
||||
+ /* Finally register the card */
|
||||
+ snprintf(card->shortname, sizeof(card->shortname), DRV_NAME);
|
||||
+ snprintf(card->longname, sizeof(card->longname),
|
||||
+ "Freescale MPC52xx PSC-AC97 (%s)", card->mixername);
|
||||
+
|
||||
+ rv = snd_card_register(card);
|
||||
+ if (rv) {
|
||||
+ dev_err(&op->dev, "%s: snd_card_register failed\n", DRV_NAME);
|
||||
+ goto err_late;
|
||||
+ }
|
||||
+
|
||||
+ dev_set_drvdata(&op->dev, priv);
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_late:
|
||||
+ free_irq(bcom_get_task_irq(priv->tsk_tx), priv);
|
||||
+err_txirqreq:
|
||||
+ free_irq(priv->irq, priv);
|
||||
+err_irqreq:
|
||||
+ bcom_gen_bd_tx_release(priv->tsk_tx);
|
||||
+err_bcomm:
|
||||
+ mpc52xx_ac97_hwshutdown(priv);
|
||||
+err_irq:
|
||||
+ irq_dispose_mapping(priv->irq);
|
||||
+err_irqmap:
|
||||
+ iounmap(priv->psc);
|
||||
+err_iomap:
|
||||
+ release_mem_region(priv->mem_start, priv->mem_len);
|
||||
+err_early:
|
||||
+ if (card)
|
||||
+ snd_card_free(card);
|
||||
+ return rv;
|
||||
+}
|
||||
+
|
||||
+static int mpc52xx_ac97_remove(struct of_device *op)
|
||||
+{
|
||||
+ struct mpc52xx_ac97_priv *priv;
|
||||
+
|
||||
+ dev_dbg(&op->dev, "removing MPC52xx PSC AC97 driver\n");
|
||||
+
|
||||
+ priv = dev_get_drvdata(&op->dev);
|
||||
+ if (priv) {
|
||||
+ /* Sound subsys shutdown */
|
||||
+ snd_card_free(priv->card);
|
||||
+
|
||||
+ /* Low level HW shutdown */
|
||||
+ mpc52xx_ac97_hwshutdown(priv);
|
||||
+
|
||||
+ /* Release bestcomm tasks */
|
||||
+ free_irq(bcom_get_task_irq(priv->tsk_tx), priv);
|
||||
+ bcom_gen_bd_tx_release(priv->tsk_tx);
|
||||
+
|
||||
+ /* Release resources */
|
||||
+ iounmap(priv->psc);
|
||||
+ free_irq(priv->irq, priv);
|
||||
+ irq_dispose_mapping(priv->irq);
|
||||
+ release_mem_region(priv->mem_start, priv->mem_len);
|
||||
+ }
|
||||
+
|
||||
+ dev_set_drvdata(&op->dev, NULL);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static struct of_device_id mpc52xx_ac97_of_match[] = {
|
||||
+ {
|
||||
+ .type = "sound",
|
||||
+ .compatible = "mpc5200b-psc-ac97", /* B only for now */
|
||||
+ }, { }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, mpc52xx_ac97_of_match);
|
||||
+static struct of_platform_driver mpc52xx_ac97_of_driver = {
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .name = DRV_NAME,
|
||||
+ .match_table = mpc52xx_ac97_of_match,
|
||||
+ .probe = mpc52xx_ac97_probe,
|
||||
+ .remove = mpc52xx_ac97_remove,
|
||||
+ .driver = {
|
||||
+ .name = DRV_NAME,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+/* ======================================================================== */
|
||||
+/* Module */
|
||||
+/* ======================================================================== */
|
||||
+
|
||||
+static int __init mpc52xx_ac97_init(void)
|
||||
+{
|
||||
+ int rv;
|
||||
+
|
||||
+ printk(KERN_INFO "Sound: MPC52xx PSC AC97 driver\n");
|
||||
+
|
||||
+ rv = of_register_platform_driver(&mpc52xx_ac97_of_driver);
|
||||
+ if (rv) {
|
||||
+ printk(KERN_ERR DRV_NAME ": "
|
||||
+ "of_register_platform_driver failed (%i)\n", rv);
|
||||
+ return rv;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void __exit mpc52xx_ac97_exit(void)
|
||||
+{
|
||||
+ of_unregister_platform_driver(&mpc52xx_ac97_of_driver);
|
||||
+}
|
||||
+
|
||||
+module_init(mpc52xx_ac97_init);
|
||||
+module_exit(mpc52xx_ac97_exit);
|
||||
+
|
||||
+MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
|
||||
+MODULE_DESCRIPTION(DRV_NAME ": Freescale MPC52xx PSC AC97 driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+
|
||||
@@ -0,0 +1,83 @@
|
||||
Subject: [PATCH] autodetect serial console on efika
|
||||
From: Olaf Hering <olh@suse.de>
|
||||
|
||||
Efika boards have to be booted with console=ttyPSC0 unless there is a
|
||||
graphics card plugged in. Detect if the firmware stdout is the serial
|
||||
connector.
|
||||
|
||||
---
|
||||
arch/powerpc/platforms/52xx/efika.c | 50 ++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 50 insertions(+)
|
||||
|
||||
--- a/arch/powerpc/platforms/52xx/efika.c
|
||||
+++ b/arch/powerpc/platforms/52xx/efika.c
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <linux/utsrelease.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/of.h>
|
||||
+#include <linux/console.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/machdep.h>
|
||||
@@ -211,12 +212,61 @@ static int __init efika_probe(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Per default, input/output-device points to the keyboard/screen
|
||||
+ * If no card is installed, the built-in serial port is used as a fallback.
|
||||
+ * But unfortunately, the firmware does not connect /chosen/{stdin,stdout}
|
||||
+ * the the built-in serial node. Instead, a /failsafe node is created.
|
||||
+ * More advanced hardware configurations cant be detected,
|
||||
+ * boot with console=xyz123 to point the kernel to the correct device
|
||||
+ */
|
||||
+static void __init efika_init_early(void)
|
||||
+{
|
||||
+#ifdef CONFIG_SERIAL_MPC52xx
|
||||
+ struct device_node *stdout_node;
|
||||
+ const char *property;
|
||||
+
|
||||
+ if (strstr(cmd_line, "console="))
|
||||
+ return;
|
||||
+ /* find the boot console from /chosen/stdout */
|
||||
+ if (!of_chosen)
|
||||
+ return;
|
||||
+ property = of_get_property(of_chosen, "linux,stdout-path", NULL);
|
||||
+ if (!property)
|
||||
+ return;
|
||||
+ stdout_node = of_find_node_by_path(property);
|
||||
+ if (!stdout_node)
|
||||
+ return;
|
||||
+ property = of_get_property(stdout_node, "device_type", NULL);
|
||||
+ if (property && strcmp(property, "serial") == 0) {
|
||||
+ /*
|
||||
+ * The 9pin connector is either /failsafe or /builtin/serial.
|
||||
+ * The optional graphics card has also type 'serial' in VGA mode.
|
||||
+ */
|
||||
+ property = of_get_property(stdout_node, "name", NULL);
|
||||
+ if (property) {
|
||||
+ if (strcmp(property, "failsafe") == 0)
|
||||
+ add_preferred_console("ttyPSC", 0, NULL);
|
||||
+ else {
|
||||
+ if (strcmp(property, "serial") == 0) {
|
||||
+ property = of_get_property(stdout_node, "model", NULL);
|
||||
+ if (property && strcmp(property, "mpc5200-serial") == 0)
|
||||
+ add_preferred_console("ttyPSC", 0, NULL);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ of_node_put(stdout_node);
|
||||
+#endif
|
||||
+}
|
||||
+
|
||||
define_machine(efika)
|
||||
{
|
||||
.name = EFIKA_PLATFORM_NAME,
|
||||
.probe = efika_probe,
|
||||
.setup_arch = efika_setup_arch,
|
||||
.init = mpc52xx_declare_of_platform_devices,
|
||||
+ .init_early = efika_init_early,
|
||||
.show_cpuinfo = efika_show_cpuinfo,
|
||||
.init_IRQ = mpc52xx_init_irq,
|
||||
.get_irq = mpc52xx_get_irq,
|
||||
@@ -0,0 +1,25 @@
|
||||
Subject: Don't emulate mr. instructions
|
||||
From: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
|
||||
References: 459387 - LTC49903
|
||||
|
||||
Currently emulate_step() emulates mr. instructions without updating cr0
|
||||
and this can be disastrous. Don't emulate mr.
|
||||
|
||||
Signed-off-by: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
arch/powerpc/lib/sstep.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/arch/powerpc/lib/sstep.c
|
||||
+++ b/arch/powerpc/lib/sstep.c
|
||||
@@ -172,6 +172,8 @@ int __kprobes emulate_step(struct pt_reg
|
||||
}
|
||||
break;
|
||||
case 0x378: /* orx */
|
||||
+ if (instr & 1)
|
||||
+ break;
|
||||
rs = (instr >> 21) & 0x1f;
|
||||
rb = (instr >> 11) & 0x1f;
|
||||
if (rs == rb) { /* mr */
|
||||
@@ -0,0 +1,33 @@
|
||||
From: Takashi Iwai <tiwai@suse.de>
|
||||
Subject: [PATCH] Fix build_error without CONFIG_PPC_83xx
|
||||
Patch-mainline:
|
||||
References:
|
||||
|
||||
fsl_deep_sleep() is defined only with CONFIG_PPC_83xx although
|
||||
CONFIG_IPIC is set for CONFIG_PPC_MPC512x, too.
|
||||
|
||||
Signed-off-by: Takashi Iwai <tiwai@suse.de>
|
||||
|
||||
---
|
||||
---
|
||||
arch/powerpc/sysdev/ipic.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/arch/powerpc/sysdev/ipic.c
|
||||
+++ b/arch/powerpc/sysdev/ipic.c
|
||||
@@ -920,6 +920,7 @@ static int ipic_suspend(struct sys_devic
|
||||
ipic_saved_state.sermr = ipic_read(ipic->regs, IPIC_SERMR);
|
||||
ipic_saved_state.sercr = ipic_read(ipic->regs, IPIC_SERCR);
|
||||
|
||||
+#ifdef CONFIG_PPC_83xx
|
||||
if (fsl_deep_sleep()) {
|
||||
/* In deep sleep, make sure there can be no
|
||||
* pending interrupts, as this can cause
|
||||
@@ -930,6 +931,7 @@ static int ipic_suspend(struct sys_devic
|
||||
ipic_write(ipic->regs, IPIC_SEMSR, 0);
|
||||
ipic_write(ipic->regs, IPIC_SERMR, 0);
|
||||
}
|
||||
+#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
Subject: fix booting with memoryless nodes
|
||||
From: haveblue@us.ibm.com
|
||||
References: 443280 - LTC49675
|
||||
|
||||
I've reproduced this on 2.6.27.7. I'm pretty sure it is caused by this
|
||||
patch:
|
||||
|
||||
http://git.kernel.org/gitweb.cgi?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=8f64e1f2d1e09267ac926e15090fd505c1c0cbcb
|
||||
|
||||
The problem is that Jon took a loop which was (in psuedocode):
|
||||
|
||||
for_each_node(nid)
|
||||
NODE_DATA(nid) = careful_alloc(nid);
|
||||
setup_bootmem(nid);
|
||||
reserve_node_bootmem(nid);
|
||||
|
||||
and broke it up into:
|
||||
|
||||
for_each_node(nid)
|
||||
NODE_DATA(nid) = careful_alloc(nid);
|
||||
setup_bootmem(nid);
|
||||
for_each_node(nid)
|
||||
reserve_node_bootmem(nid);
|
||||
|
||||
The issue comes in when the 'careful_alloc()' is called on a node with
|
||||
no memory. It falls back to using bootmem from a previously-initialized
|
||||
node. But, bootmem has not yet been reserved when Jon's patch is
|
||||
applied. It gives back bogus memory (0xc000000000000000) and pukes
|
||||
later in boot.
|
||||
|
||||
The following patch collapses the loop back together. It also breaks
|
||||
the mark_reserved_regions_for_nid() code out into a function and adds
|
||||
some comments. I think a huge part of introducing this bug is because
|
||||
for loop was too long and hard to read.
|
||||
|
||||
The actual bug fix here is the:
|
||||
|
||||
+ if (end_pfn <= node->node_start_pfn ||
|
||||
+ start_pfn >= node_end_pfn)
|
||||
+ continue;
|
||||
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
arch/powerpc/mm/numa.c | 130 ++++++++++++++++++++++++++++++-------------------
|
||||
1 file changed, 82 insertions(+), 48 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/mm/numa.c
|
||||
+++ b/arch/powerpc/mm/numa.c
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/lmb.h>
|
||||
#include <linux/of.h>
|
||||
+#include <linux/pfn.h>
|
||||
#include <asm/sparsemem.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/system.h>
|
||||
@@ -867,10 +868,75 @@ static struct notifier_block __cpuinitda
|
||||
.priority = 1 /* Must run before sched domains notifier. */
|
||||
};
|
||||
|
||||
+static void mark_reserved_regions_for_nid(int nid)
|
||||
+{
|
||||
+ struct pglist_data *node = NODE_DATA(nid);
|
||||
+ int i;
|
||||
+
|
||||
+ dbg("mark_reserved_regions_for_nid(%d) NODE_DATA: %p\n", nid, node);
|
||||
+ for (i = 0; i < lmb.reserved.cnt; i++) {
|
||||
+ unsigned long physbase = lmb.reserved.region[i].base;
|
||||
+ unsigned long size = lmb.reserved.region[i].size;
|
||||
+ unsigned long start_pfn = physbase >> PAGE_SHIFT;
|
||||
+ unsigned long end_pfn = PFN_UP(physbase + size);
|
||||
+ struct node_active_region node_ar;
|
||||
+ unsigned long node_end_pfn = node->node_start_pfn +
|
||||
+ node->node_spanned_pages;
|
||||
+
|
||||
+ /*
|
||||
+ * Check to make sure that this lmb.reserved area is
|
||||
+ * within the bounds of the node that we care about.
|
||||
+ * Checking the nid of the start and end points is not
|
||||
+ * sufficient because the reserved area could span the
|
||||
+ * entire node.
|
||||
+ */
|
||||
+ if (end_pfn <= node->node_start_pfn ||
|
||||
+ start_pfn >= node_end_pfn)
|
||||
+ continue;
|
||||
+
|
||||
+ get_node_active_region(start_pfn, &node_ar);
|
||||
+ while (start_pfn < end_pfn &&
|
||||
+ node_ar.start_pfn < node_ar.end_pfn) {
|
||||
+ unsigned long reserve_size = size;
|
||||
+ /*
|
||||
+ * if reserved region extends past active region
|
||||
+ * then trim size to active region
|
||||
+ */
|
||||
+ if (end_pfn > node_ar.end_pfn)
|
||||
+ reserve_size = (node_ar.end_pfn << PAGE_SHIFT)
|
||||
+ - physbase;
|
||||
+ /*
|
||||
+ * Only worry about *this* node, others may not
|
||||
+ * yet have valid NODE_DATA().
|
||||
+ */
|
||||
+ if (node_ar.nid == nid)
|
||||
+ reserve_bootmem_node(NODE_DATA(node_ar.nid),
|
||||
+ physbase, reserve_size,
|
||||
+ BOOTMEM_DEFAULT);
|
||||
+ /*
|
||||
+ * if reserved region is contained in the active region
|
||||
+ * then done.
|
||||
+ */
|
||||
+ if (end_pfn <= node_ar.end_pfn)
|
||||
+ break;
|
||||
+
|
||||
+ /*
|
||||
+ * reserved region extends past the active region
|
||||
+ * get next active region that contains this
|
||||
+ * reserved region
|
||||
+ */
|
||||
+ start_pfn = node_ar.end_pfn;
|
||||
+ physbase = start_pfn << PAGE_SHIFT;
|
||||
+ size = size - reserve_size;
|
||||
+ get_node_active_region(start_pfn, &node_ar);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+
|
||||
void __init do_init_bootmem(void)
|
||||
{
|
||||
int nid;
|
||||
- unsigned int i;
|
||||
|
||||
min_low_pfn = 0;
|
||||
max_low_pfn = lmb_end_of_DRAM() >> PAGE_SHIFT;
|
||||
@@ -890,9 +956,16 @@ void __init do_init_bootmem(void)
|
||||
unsigned long bootmem_paddr;
|
||||
unsigned long bootmap_pages;
|
||||
|
||||
+ dbg("node %d is online\n", nid);
|
||||
get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
|
||||
|
||||
- /* Allocate the node structure node local if possible */
|
||||
+ /*
|
||||
+ * Allocate the node structure node local if possible
|
||||
+ *
|
||||
+ * Be careful moving this around, as it relies on all
|
||||
+ * previous nodes' bootmem to be initialized and have
|
||||
+ * all reserved areas marked.
|
||||
+ */
|
||||
NODE_DATA(nid) = careful_allocation(nid,
|
||||
sizeof(struct pglist_data),
|
||||
SMP_CACHE_BYTES, end_pfn);
|
||||
@@ -924,53 +997,14 @@ void __init do_init_bootmem(void)
|
||||
start_pfn, end_pfn);
|
||||
|
||||
free_bootmem_with_active_regions(nid, end_pfn);
|
||||
- }
|
||||
-
|
||||
- /* Mark reserved regions */
|
||||
- for (i = 0; i < lmb.reserved.cnt; i++) {
|
||||
- unsigned long physbase = lmb.reserved.region[i].base;
|
||||
- unsigned long size = lmb.reserved.region[i].size;
|
||||
- unsigned long start_pfn = physbase >> PAGE_SHIFT;
|
||||
- unsigned long end_pfn = ((physbase + size) >> PAGE_SHIFT);
|
||||
- struct node_active_region node_ar;
|
||||
-
|
||||
- get_node_active_region(start_pfn, &node_ar);
|
||||
- while (start_pfn < end_pfn &&
|
||||
- node_ar.start_pfn < node_ar.end_pfn) {
|
||||
- unsigned long reserve_size = size;
|
||||
- /*
|
||||
- * if reserved region extends past active region
|
||||
- * then trim size to active region
|
||||
- */
|
||||
- if (end_pfn > node_ar.end_pfn)
|
||||
- reserve_size = (node_ar.end_pfn << PAGE_SHIFT)
|
||||
- - (start_pfn << PAGE_SHIFT);
|
||||
- dbg("reserve_bootmem %lx %lx nid=%d\n", physbase,
|
||||
- reserve_size, node_ar.nid);
|
||||
- reserve_bootmem_node(NODE_DATA(node_ar.nid), physbase,
|
||||
- reserve_size, BOOTMEM_DEFAULT);
|
||||
- /*
|
||||
- * if reserved region is contained in the active region
|
||||
- * then done.
|
||||
- */
|
||||
- if (end_pfn <= node_ar.end_pfn)
|
||||
- break;
|
||||
-
|
||||
- /*
|
||||
- * reserved region extends past the active region
|
||||
- * get next active region that contains this
|
||||
- * reserved region
|
||||
- */
|
||||
- start_pfn = node_ar.end_pfn;
|
||||
- physbase = start_pfn << PAGE_SHIFT;
|
||||
- size = size - reserve_size;
|
||||
- get_node_active_region(start_pfn, &node_ar);
|
||||
- }
|
||||
-
|
||||
- }
|
||||
-
|
||||
- for_each_online_node(nid)
|
||||
+ /*
|
||||
+ * Be very careful about moving this around. Future
|
||||
+ * calls to careful_allocation() depend on this getting
|
||||
+ * done correctly.
|
||||
+ */
|
||||
+ mark_reserved_regions_for_nid(nid);
|
||||
sparse_memory_present_with_active_regions(nid);
|
||||
+ }
|
||||
}
|
||||
|
||||
void __init paging_init(void)
|
||||
30
src/patches/suse-2.6.27.31/patches.arch/ppc-of-irq-map.patch
Normal file
30
src/patches/suse-2.6.27.31/patches.arch/ppc-of-irq-map.patch
Normal file
@@ -0,0 +1,30 @@
|
||||
Subject: fix IRQ assignment if interrupts property is missing
|
||||
From: Adhemerval Zanella Neto <azanella@br.ibm.com>
|
||||
References: 446610 - LTC50006
|
||||
|
||||
This patch fix the IRQ assign.
|
||||
If the code can not get "interrupts" property from PCI OF node,
|
||||
it falls back to standard OF parsing.
|
||||
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
arch/powerpc/kernel/prom_parse.c | 7 +++++--
|
||||
1 file changed, 5 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/kernel/prom_parse.c
|
||||
+++ b/arch/powerpc/kernel/prom_parse.c
|
||||
@@ -250,8 +250,11 @@ int of_irq_map_pci(struct pci_dev *pdev,
|
||||
* parsing
|
||||
*/
|
||||
dn = pci_device_to_OF_node(pdev);
|
||||
- if (dn)
|
||||
- return of_irq_map_one(dn, 0, out_irq);
|
||||
+ if (dn) {
|
||||
+ rc = of_irq_map_one(dn, 0, out_irq);
|
||||
+ if (!rc)
|
||||
+ return rc;
|
||||
+ }
|
||||
|
||||
/* Ok, we don't, time to have fun. Let's start by building up an
|
||||
* interrupt spec. we assume #interrupt-cells is 1, which is standard
|
||||
@@ -0,0 +1,513 @@
|
||||
Subject: [PATCH] powerpc/oprofile: Fix mutex locking for cell spu-oprofile
|
||||
From: Carl Love <cel@us.ibm.com>
|
||||
References: 422501 - LTC47617
|
||||
|
||||
The issue is the SPU code is not holding the kernel mutex lock while
|
||||
adding samples to the kernel buffer.
|
||||
|
||||
This patch creates per SPU buffers to hold the data. Data
|
||||
is added to the buffers from in interrupt context. The data
|
||||
is periodically pushed to the kernel buffer via a new Oprofile
|
||||
function oprofile_put_buff(). The oprofile_put_buff() function
|
||||
is called via a work queue enabling the funtion to acquire the
|
||||
mutex lock.
|
||||
|
||||
The existing user controls for adjusting the per CPU buffer
|
||||
size is used to control the size of the per SPU buffers.
|
||||
Similarly, overflows of the SPU buffers are reported by
|
||||
incrementing the per CPU buffer stats. This eliminates the
|
||||
need to have architecture specific controls for the per SPU
|
||||
buffers which is not acceptable to the OProfile user tool
|
||||
maintainer.
|
||||
|
||||
The export of the oprofile add_event_entry() is removed as it
|
||||
is no longer needed given this patch.
|
||||
|
||||
Note, this patch has not addressed the issue of indexing arrays
|
||||
by the spu number. This still needs to be fixed as the spu
|
||||
numbering is not guarenteed to be 0 to max_num_spus-1.
|
||||
|
||||
Signed-off-by: Carl Love <carll@us.ibm.com>
|
||||
Signed-off-by: Maynard Johnson <maynardj@us.ibm.com>
|
||||
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
|
||||
Acked-by: Acked-by: Robert Richter <robert.richter@amd.com>
|
||||
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
arch/powerpc/oprofile/cell/pr_util.h | 13 +
|
||||
arch/powerpc/oprofile/cell/spu_profiler.c | 4
|
||||
arch/powerpc/oprofile/cell/spu_task_sync.c | 236 +++++++++++++++++++++++++----
|
||||
drivers/oprofile/buffer_sync.c | 24 ++
|
||||
drivers/oprofile/cpu_buffer.c | 15 +
|
||||
drivers/oprofile/event_buffer.h | 7
|
||||
include/linux/oprofile.h | 16 +
|
||||
7 files changed, 279 insertions(+), 36 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/oprofile/cell/pr_util.h
|
||||
+++ b/arch/powerpc/oprofile/cell/pr_util.h
|
||||
@@ -24,6 +24,11 @@
|
||||
#define SKIP_GENERIC_SYNC 0
|
||||
#define SYNC_START_ERROR -1
|
||||
#define DO_GENERIC_SYNC 1
|
||||
+#define SPUS_PER_NODE 8
|
||||
+#define DEFAULT_TIMER_EXPIRE (HZ / 10)
|
||||
+
|
||||
+extern struct delayed_work spu_work;
|
||||
+extern int spu_prof_running;
|
||||
|
||||
struct spu_overlay_info { /* map of sections within an SPU overlay */
|
||||
unsigned int vma; /* SPU virtual memory address from elf */
|
||||
@@ -62,6 +67,14 @@ struct vma_to_fileoffset_map { /* map of
|
||||
|
||||
};
|
||||
|
||||
+struct spu_buffer {
|
||||
+ int last_guard_val;
|
||||
+ int ctx_sw_seen;
|
||||
+ unsigned long *buff;
|
||||
+ unsigned int head, tail;
|
||||
+};
|
||||
+
|
||||
+
|
||||
/* The three functions below are for maintaining and accessing
|
||||
* the vma-to-fileoffset map.
|
||||
*/
|
||||
--- a/arch/powerpc/oprofile/cell/spu_profiler.c
|
||||
+++ b/arch/powerpc/oprofile/cell/spu_profiler.c
|
||||
@@ -24,12 +24,11 @@
|
||||
|
||||
static u32 *samples;
|
||||
|
||||
-static int spu_prof_running;
|
||||
+int spu_prof_running;
|
||||
static unsigned int profiling_interval;
|
||||
|
||||
#define NUM_SPU_BITS_TRBUF 16
|
||||
#define SPUS_PER_TB_ENTRY 4
|
||||
-#define SPUS_PER_NODE 8
|
||||
|
||||
#define SPU_PC_MASK 0xFFFF
|
||||
|
||||
@@ -209,6 +208,7 @@ int start_spu_profiling(unsigned int cyc
|
||||
|
||||
spu_prof_running = 1;
|
||||
hrtimer_start(&timer, kt, HRTIMER_MODE_REL);
|
||||
+ schedule_delayed_work(&spu_work, DEFAULT_TIMER_EXPIRE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
--- a/arch/powerpc/oprofile/cell/spu_task_sync.c
|
||||
+++ b/arch/powerpc/oprofile/cell/spu_task_sync.c
|
||||
@@ -35,7 +35,102 @@ static DEFINE_SPINLOCK(buffer_lock);
|
||||
static DEFINE_SPINLOCK(cache_lock);
|
||||
static int num_spu_nodes;
|
||||
int spu_prof_num_nodes;
|
||||
-int last_guard_val[MAX_NUMNODES * 8];
|
||||
+
|
||||
+struct spu_buffer spu_buff[MAX_NUMNODES * SPUS_PER_NODE];
|
||||
+struct delayed_work spu_work;
|
||||
+static unsigned max_spu_buff;
|
||||
+
|
||||
+static void spu_buff_add(unsigned long int value, int spu)
|
||||
+{
|
||||
+ /* spu buff is a circular buffer. Add entries to the
|
||||
+ * head. Head is the index to store the next value.
|
||||
+ * The buffer is full when there is one available entry
|
||||
+ * in the queue, i.e. head and tail can't be equal.
|
||||
+ * That way we can tell the difference between the
|
||||
+ * buffer being full versus empty.
|
||||
+ *
|
||||
+ * ASSUPTION: the buffer_lock is held when this function
|
||||
+ * is called to lock the buffer, head and tail.
|
||||
+ */
|
||||
+ int full = 1;
|
||||
+
|
||||
+ if (spu_buff[spu].head >= spu_buff[spu].tail) {
|
||||
+ if ((spu_buff[spu].head - spu_buff[spu].tail)
|
||||
+ < (max_spu_buff - 1))
|
||||
+ full = 0;
|
||||
+
|
||||
+ } else if (spu_buff[spu].tail > spu_buff[spu].head) {
|
||||
+ if ((spu_buff[spu].tail - spu_buff[spu].head)
|
||||
+ > 1)
|
||||
+ full = 0;
|
||||
+ }
|
||||
+
|
||||
+ if (!full) {
|
||||
+ spu_buff[spu].buff[spu_buff[spu].head] = value;
|
||||
+ spu_buff[spu].head++;
|
||||
+
|
||||
+ if (spu_buff[spu].head >= max_spu_buff)
|
||||
+ spu_buff[spu].head = 0;
|
||||
+ } else {
|
||||
+ /* From the user's perspective make the SPU buffer
|
||||
+ * size management/overflow look like we are using
|
||||
+ * per cpu buffers. The user uses the same
|
||||
+ * per cpu parameter to adjust the SPU buffer size.
|
||||
+ * Increment the sample_lost_overflow to inform
|
||||
+ * the user the buffer size needs to be increased.
|
||||
+ */
|
||||
+ oprofile_cpu_buffer_inc_smpl_lost();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/* This function copies the per SPU buffers to the
|
||||
+ * OProfile kernel buffer.
|
||||
+ */
|
||||
+void sync_spu_buff(void)
|
||||
+{
|
||||
+ int spu;
|
||||
+ unsigned long flags;
|
||||
+ int curr_head;
|
||||
+
|
||||
+ for (spu = 0; spu < num_spu_nodes; spu++) {
|
||||
+ /* In case there was an issue and the buffer didn't
|
||||
+ * get created skip it.
|
||||
+ */
|
||||
+ if (spu_buff[spu].buff == NULL)
|
||||
+ continue;
|
||||
+
|
||||
+ /* Hold the lock to make sure the head/tail
|
||||
+ * doesn't change while spu_buff_add() is
|
||||
+ * deciding if the buffer is full or not.
|
||||
+ * Being a little paranoid.
|
||||
+ */
|
||||
+ spin_lock_irqsave(&buffer_lock, flags);
|
||||
+ curr_head = spu_buff[spu].head;
|
||||
+ spin_unlock_irqrestore(&buffer_lock, flags);
|
||||
+
|
||||
+ /* Transfer the current contents to the kernel buffer.
|
||||
+ * data can still be added to the head of the buffer.
|
||||
+ */
|
||||
+ oprofile_put_buff(spu_buff[spu].buff,
|
||||
+ spu_buff[spu].tail,
|
||||
+ curr_head, max_spu_buff);
|
||||
+
|
||||
+ spin_lock_irqsave(&buffer_lock, flags);
|
||||
+ spu_buff[spu].tail = curr_head;
|
||||
+ spin_unlock_irqrestore(&buffer_lock, flags);
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
+
|
||||
+static void wq_sync_spu_buff(struct work_struct *work)
|
||||
+{
|
||||
+ /* move data from spu buffers to kernel buffer */
|
||||
+ sync_spu_buff();
|
||||
+
|
||||
+ /* only reschedule if profiling is not done */
|
||||
+ if (spu_prof_running)
|
||||
+ schedule_delayed_work(&spu_work, DEFAULT_TIMER_EXPIRE);
|
||||
+}
|
||||
|
||||
/* Container for caching information about an active SPU task. */
|
||||
struct cached_info {
|
||||
@@ -305,14 +400,21 @@ static int process_context_switch(struct
|
||||
|
||||
/* Record context info in event buffer */
|
||||
spin_lock_irqsave(&buffer_lock, flags);
|
||||
- add_event_entry(ESCAPE_CODE);
|
||||
- add_event_entry(SPU_CTX_SWITCH_CODE);
|
||||
- add_event_entry(spu->number);
|
||||
- add_event_entry(spu->pid);
|
||||
- add_event_entry(spu->tgid);
|
||||
- add_event_entry(app_dcookie);
|
||||
- add_event_entry(spu_cookie);
|
||||
- add_event_entry(offset);
|
||||
+ spu_buff_add(ESCAPE_CODE, spu->number);
|
||||
+ spu_buff_add(SPU_CTX_SWITCH_CODE, spu->number);
|
||||
+ spu_buff_add(spu->number, spu->number);
|
||||
+ spu_buff_add(spu->pid, spu->number);
|
||||
+ spu_buff_add(spu->tgid, spu->number);
|
||||
+ spu_buff_add(app_dcookie, spu->number);
|
||||
+ spu_buff_add(spu_cookie, spu->number);
|
||||
+ spu_buff_add(offset, spu->number);
|
||||
+
|
||||
+ /* Set flag to indicate SPU PC data can now be written out. If
|
||||
+ * the SPU program counter data is seen before an SPU context
|
||||
+ * record is seen, the postprocessing will fail.
|
||||
+ */
|
||||
+ spu_buff[spu->number].ctx_sw_seen = 1;
|
||||
+
|
||||
spin_unlock_irqrestore(&buffer_lock, flags);
|
||||
smp_wmb(); /* insure spu event buffer updates are written */
|
||||
/* don't want entries intermingled... */
|
||||
@@ -360,6 +462,47 @@ static int number_of_online_nodes(void)
|
||||
return nodes;
|
||||
}
|
||||
|
||||
+static int oprofile_spu_buff_create(void)
|
||||
+{
|
||||
+ int spu;
|
||||
+
|
||||
+ max_spu_buff = oprofile_get_cpu_buffer_size();
|
||||
+
|
||||
+ for (spu = 0; spu < num_spu_nodes; spu++) {
|
||||
+ /* create circular buffers to store the data in.
|
||||
+ * use locks to manage accessing the buffers
|
||||
+ */
|
||||
+ spu_buff[spu].head = 0;
|
||||
+ spu_buff[spu].tail = 0;
|
||||
+
|
||||
+ /*
|
||||
+ * Create a buffer for each SPU. Can't reliably
|
||||
+ * create a single buffer for all spus due to not
|
||||
+ * enough contiguous kernel memory.
|
||||
+ */
|
||||
+
|
||||
+ spu_buff[spu].buff = kzalloc((max_spu_buff
|
||||
+ * sizeof(unsigned long)),
|
||||
+ GFP_KERNEL);
|
||||
+
|
||||
+ if (!spu_buff[spu].buff) {
|
||||
+ printk(KERN_ERR "SPU_PROF: "
|
||||
+ "%s, line %d: oprofile_spu_buff_create "
|
||||
+ "failed to allocate spu buffer %d.\n",
|
||||
+ __func__, __LINE__, spu);
|
||||
+
|
||||
+ /* release the spu buffers that have been allocated */
|
||||
+ while (spu >= 0) {
|
||||
+ kfree(spu_buff[spu].buff);
|
||||
+ spu_buff[spu].buff = 0;
|
||||
+ spu--;
|
||||
+ }
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
/* The main purpose of this function is to synchronize
|
||||
* OProfile with SPUFS by registering to be notified of
|
||||
* SPU task switches.
|
||||
@@ -372,20 +515,35 @@ static int number_of_online_nodes(void)
|
||||
*/
|
||||
int spu_sync_start(void)
|
||||
{
|
||||
- int k;
|
||||
+ int spu;
|
||||
int ret = SKIP_GENERIC_SYNC;
|
||||
int register_ret;
|
||||
unsigned long flags = 0;
|
||||
|
||||
spu_prof_num_nodes = number_of_online_nodes();
|
||||
num_spu_nodes = spu_prof_num_nodes * 8;
|
||||
+ INIT_DELAYED_WORK(&spu_work, wq_sync_spu_buff);
|
||||
+
|
||||
+ /* create buffer for storing the SPU data to put in
|
||||
+ * the kernel buffer.
|
||||
+ */
|
||||
+ ret = oprofile_spu_buff_create();
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
|
||||
spin_lock_irqsave(&buffer_lock, flags);
|
||||
- add_event_entry(ESCAPE_CODE);
|
||||
- add_event_entry(SPU_PROFILING_CODE);
|
||||
- add_event_entry(num_spu_nodes);
|
||||
+ for (spu = 0; spu < num_spu_nodes; spu++) {
|
||||
+ spu_buff_add(ESCAPE_CODE, spu);
|
||||
+ spu_buff_add(SPU_PROFILING_CODE, spu);
|
||||
+ spu_buff_add(num_spu_nodes, spu);
|
||||
+ }
|
||||
spin_unlock_irqrestore(&buffer_lock, flags);
|
||||
|
||||
+ for (spu = 0; spu < num_spu_nodes; spu++) {
|
||||
+ spu_buff[spu].ctx_sw_seen = 0;
|
||||
+ spu_buff[spu].last_guard_val = 0;
|
||||
+ }
|
||||
+
|
||||
/* Register for SPU events */
|
||||
register_ret = spu_switch_event_register(&spu_active);
|
||||
if (register_ret) {
|
||||
@@ -393,8 +551,6 @@ int spu_sync_start(void)
|
||||
goto out;
|
||||
}
|
||||
|
||||
- for (k = 0; k < (MAX_NUMNODES * 8); k++)
|
||||
- last_guard_val[k] = 0;
|
||||
pr_debug("spu_sync_start -- running.\n");
|
||||
out:
|
||||
return ret;
|
||||
@@ -446,13 +602,20 @@ void spu_sync_buffer(int spu_num, unsign
|
||||
* use. We need to discard samples taken during the time
|
||||
* period which an overlay occurs (i.e., guard value changes).
|
||||
*/
|
||||
- if (grd_val && grd_val != last_guard_val[spu_num]) {
|
||||
- last_guard_val[spu_num] = grd_val;
|
||||
+ if (grd_val && grd_val != spu_buff[spu_num].last_guard_val) {
|
||||
+ spu_buff[spu_num].last_guard_val = grd_val;
|
||||
/* Drop the rest of the samples. */
|
||||
break;
|
||||
}
|
||||
|
||||
- add_event_entry(file_offset | spu_num_shifted);
|
||||
+ /* We must ensure that the SPU context switch has been written
|
||||
+ * out before samples for the SPU. Otherwise, the SPU context
|
||||
+ * information is not available and the postprocessing of the
|
||||
+ * SPU PC will fail with no available anonymous map information.
|
||||
+ */
|
||||
+ if (spu_buff[spu_num].ctx_sw_seen)
|
||||
+ spu_buff_add((file_offset | spu_num_shifted),
|
||||
+ spu_num);
|
||||
}
|
||||
spin_unlock(&buffer_lock);
|
||||
out:
|
||||
@@ -463,20 +626,41 @@ out:
|
||||
int spu_sync_stop(void)
|
||||
{
|
||||
unsigned long flags = 0;
|
||||
- int ret = spu_switch_event_unregister(&spu_active);
|
||||
- if (ret) {
|
||||
+ int ret;
|
||||
+ int k;
|
||||
+
|
||||
+ ret = spu_switch_event_unregister(&spu_active);
|
||||
+
|
||||
+ if (ret)
|
||||
printk(KERN_ERR "SPU_PROF: "
|
||||
- "%s, line %d: spu_switch_event_unregister returned %d\n",
|
||||
- __func__, __LINE__, ret);
|
||||
- goto out;
|
||||
- }
|
||||
+ "%s, line %d: spu_switch_event_unregister " \
|
||||
+ "returned %d\n",
|
||||
+ __func__, __LINE__, ret);
|
||||
+
|
||||
+ /* flush any remaining data in the per SPU buffers */
|
||||
+ sync_spu_buff();
|
||||
|
||||
spin_lock_irqsave(&cache_lock, flags);
|
||||
ret = release_cached_info(RELEASE_ALL);
|
||||
spin_unlock_irqrestore(&cache_lock, flags);
|
||||
-out:
|
||||
+
|
||||
+ /* remove scheduled work queue item rather then waiting
|
||||
+ * for every queued entry to execute. Then flush pending
|
||||
+ * system wide buffer to event buffer.
|
||||
+ */
|
||||
+ cancel_delayed_work(&spu_work);
|
||||
+
|
||||
+ for (k = 0; k < num_spu_nodes; k++) {
|
||||
+ spu_buff[k].ctx_sw_seen = 0;
|
||||
+
|
||||
+ /*
|
||||
+ * spu_sys_buff will be null if there was a problem
|
||||
+ * allocating the buffer. Only delete if it exists.
|
||||
+ */
|
||||
+ kfree(spu_buff[k].buff);
|
||||
+ spu_buff[k].buff = 0;
|
||||
+ }
|
||||
pr_debug("spu_sync_stop -- done.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
-
|
||||
--- a/drivers/oprofile/buffer_sync.c
|
||||
+++ b/drivers/oprofile/buffer_sync.c
|
||||
@@ -551,3 +551,27 @@ void sync_buffer(int cpu)
|
||||
|
||||
mutex_unlock(&buffer_mutex);
|
||||
}
|
||||
+
|
||||
+/* The function can be used to add a buffer worth of data directly to
|
||||
+ * the kernel buffer. The buffer is assumed to be a circular buffer.
|
||||
+ * Take the entries from index start and end at index end, wrapping
|
||||
+ * at max_entries.
|
||||
+ */
|
||||
+void oprofile_put_buff(unsigned long *buf, unsigned int start,
|
||||
+ unsigned int stop, unsigned int max)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ i = start;
|
||||
+
|
||||
+ mutex_lock(&buffer_mutex);
|
||||
+ while (i != stop) {
|
||||
+ add_event_entry(buf[i++]);
|
||||
+
|
||||
+ if (i >= max)
|
||||
+ i = 0;
|
||||
+ }
|
||||
+
|
||||
+ mutex_unlock(&buffer_mutex);
|
||||
+}
|
||||
+
|
||||
--- a/drivers/oprofile/cpu_buffer.c
|
||||
+++ b/drivers/oprofile/cpu_buffer.c
|
||||
@@ -37,13 +37,26 @@ static int work_enabled;
|
||||
void free_cpu_buffers(void)
|
||||
{
|
||||
int i;
|
||||
-
|
||||
+
|
||||
for_each_online_cpu(i) {
|
||||
vfree(per_cpu(cpu_buffer, i).buffer);
|
||||
per_cpu(cpu_buffer, i).buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
+unsigned long oprofile_get_cpu_buffer_size(void)
|
||||
+{
|
||||
+ return fs_cpu_buffer_size;
|
||||
+}
|
||||
+
|
||||
+void oprofile_cpu_buffer_inc_smpl_lost(void)
|
||||
+{
|
||||
+ struct oprofile_cpu_buffer *cpu_buf
|
||||
+ = &__get_cpu_var(cpu_buffer);
|
||||
+
|
||||
+ cpu_buf->sample_lost_overflow++;
|
||||
+}
|
||||
+
|
||||
int alloc_cpu_buffers(void)
|
||||
{
|
||||
int i;
|
||||
--- a/drivers/oprofile/event_buffer.h
|
||||
+++ b/drivers/oprofile/event_buffer.h
|
||||
@@ -17,6 +17,13 @@ int alloc_event_buffer(void);
|
||||
|
||||
void free_event_buffer(void);
|
||||
|
||||
+/**
|
||||
+ * Add data to the event buffer.
|
||||
+ * The data passed is free-form, but typically consists of
|
||||
+ * file offsets, dcookies, context information, and ESCAPE codes.
|
||||
+ */
|
||||
+void add_event_entry(unsigned long data);
|
||||
+
|
||||
/* wake up the process sleeping on the event file */
|
||||
void wake_up_buffer_waiter(void);
|
||||
|
||||
--- a/include/linux/oprofile.h
|
||||
+++ b/include/linux/oprofile.h
|
||||
@@ -84,13 +84,6 @@ int oprofile_arch_init(struct oprofile_o
|
||||
void oprofile_arch_exit(void);
|
||||
|
||||
/**
|
||||
- * Add data to the event buffer.
|
||||
- * The data passed is free-form, but typically consists of
|
||||
- * file offsets, dcookies, context information, and ESCAPE codes.
|
||||
- */
|
||||
-void add_event_entry(unsigned long data);
|
||||
-
|
||||
-/**
|
||||
* Add a sample. This may be called from any context. Pass
|
||||
* smp_processor_id() as cpu.
|
||||
*/
|
||||
@@ -160,5 +153,14 @@ int oprofilefs_ulong_from_user(unsigned
|
||||
|
||||
/** lock for read/write safety */
|
||||
extern spinlock_t oprofilefs_lock;
|
||||
+
|
||||
+/**
|
||||
+ * Add the contents of a circular buffer to the event buffer.
|
||||
+ */
|
||||
+void oprofile_put_buff(unsigned long *buf, unsigned int start,
|
||||
+ unsigned int stop, unsigned int max);
|
||||
+
|
||||
+unsigned long oprofile_get_cpu_buffer_size(void);
|
||||
+void oprofile_cpu_buffer_inc_smpl_lost(void);
|
||||
|
||||
#endif /* OPROFILE_H */
|
||||
@@ -0,0 +1,64 @@
|
||||
Subject: Incorrect local array size in activate spu profiling function
|
||||
From: Carl Love <carll@us.ibm.com>
|
||||
References: 439553 - LTC48925
|
||||
|
||||
Updated the patch to address comments by Michael Ellerman.
|
||||
Specifically, changed upper limit in for loop to
|
||||
ARRAY_SIZE() macro and added a check to make sure the
|
||||
number of events specified by the user, which is used as
|
||||
the max for indexing various arrays, is no bigger then the
|
||||
declared size of the arrays.
|
||||
|
||||
The size of the pm_signal_local array should be equal to the
|
||||
number of SPUs being configured in the array. Currently, the
|
||||
array is of size 4 (NR_PHYS_CTRS) but being indexed by a for
|
||||
loop from 0 to 7 (NUM_SPUS_PER_NODE).
|
||||
|
||||
Signed-off-by: Carl Love <carll@us.ibm.com>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
arch/powerpc/oprofile/op_model_cell.c | 13 ++++++++++---
|
||||
1 file changed, 10 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/oprofile/op_model_cell.c
|
||||
+++ b/arch/powerpc/oprofile/op_model_cell.c
|
||||
@@ -582,6 +582,13 @@ static int cell_reg_setup(struct op_coun
|
||||
|
||||
num_counters = num_ctrs;
|
||||
|
||||
+ if (unlikely(num_ctrs > NR_PHYS_CTRS)) {
|
||||
+ printk(KERN_ERR
|
||||
+ "%s: Oprofile, number of specified events " \
|
||||
+ "exceeds number of physical counters\n",
|
||||
+ __func__);
|
||||
+ return -EIO;
|
||||
+ }
|
||||
pm_regs.group_control = 0;
|
||||
pm_regs.debug_bus_control = 0;
|
||||
|
||||
@@ -830,13 +837,13 @@ static int calculate_lfsr(int n)
|
||||
static int pm_rtas_activate_spu_profiling(u32 node)
|
||||
{
|
||||
int ret, i;
|
||||
- struct pm_signal pm_signal_local[NR_PHYS_CTRS];
|
||||
+ struct pm_signal pm_signal_local[NUM_SPUS_PER_NODE];
|
||||
|
||||
/*
|
||||
* Set up the rtas call to configure the debug bus to
|
||||
* route the SPU PCs. Setup the pm_signal for each SPU
|
||||
*/
|
||||
- for (i = 0; i < NUM_SPUS_PER_NODE; i++) {
|
||||
+ for (i = 0; i < ARRAY_SIZE(pm_signal_local); i++) {
|
||||
pm_signal_local[i].cpu = node;
|
||||
pm_signal_local[i].signal_group = 41;
|
||||
/* spu i on word (i/2) */
|
||||
@@ -848,7 +855,7 @@ static int pm_rtas_activate_spu_profilin
|
||||
|
||||
ret = rtas_ibm_cbe_perftools(SUBFUNC_ACTIVATE,
|
||||
PASSTHRU_ENABLE, pm_signal_local,
|
||||
- (NUM_SPUS_PER_NODE
|
||||
+ (ARRAY_SIZE(pm_signal_local)
|
||||
* sizeof(struct pm_signal)));
|
||||
|
||||
if (unlikely(ret)) {
|
||||
240
src/patches/suse-2.6.27.31/patches.arch/ppc-optimize-sync.patch
Normal file
240
src/patches/suse-2.6.27.31/patches.arch/ppc-optimize-sync.patch
Normal file
@@ -0,0 +1,240 @@
|
||||
Subject: Optimise smp_{r,w}mb and mutex
|
||||
From: Nick Piggin <npiggin@suse.de>
|
||||
References: 471222 - LTC51356
|
||||
|
||||
powerpc: Optimise smp_wmb
|
||||
|
||||
Change 2d1b2027626d5151fff8ef7c06ca8e7876a1a510 ("powerpc: Fixup
|
||||
lwsync at runtime") removed __SUBARCH_HAS_LWSYNC, causing smp_wmb to
|
||||
revert back to eieio for all CPUs. This restores the behaviour
|
||||
intorduced in 74f0609526afddd88bef40b651da24f3167b10b2 ("powerpc:
|
||||
Optimise smp_wmb on 64-bit processors").
|
||||
|
||||
powerpc: Optimise smp_rmb
|
||||
|
||||
After commit 598056d5af8fef1dbe8f96f5c2b641a528184e5a ("[POWERPC] Fix
|
||||
rmb to order cacheable vs. noncacheable"), rmb() becomes a sync
|
||||
instruction, which is needed to order cacheable vs noncacheable loads.
|
||||
However smp_rmb() is #defined to rmb(), and smp_rmb() can be an
|
||||
lwsync.
|
||||
|
||||
This restores smp_rmb() performance by using lwsync there and updates
|
||||
the comments.
|
||||
|
||||
powerpc: Optimise mutex
|
||||
|
||||
This implements an optimised mutex fastpath for powerpc, making use of
|
||||
acquire and release barrier semantics. This takes the mutex
|
||||
lock+unlock benchmark from 203 to 173 cycles on a G5.
|
||||
|
||||
Signed-off-by: Nick Piggin <npiggin@suse.de>
|
||||
Signed-off-by: Paul Mackerras <paulus@samba.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
arch/powerpc/include/asm/mutex.h | 135 ++++++++++++++++++++++++++++++++++++--
|
||||
arch/powerpc/include/asm/synch.h | 4 +
|
||||
arch/powerpc/include/asm/system.h | 24 +++---
|
||||
3 files changed, 147 insertions(+), 16 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/include/asm/mutex.h
|
||||
+++ b/arch/powerpc/include/asm/mutex.h
|
||||
@@ -1,9 +1,134 @@
|
||||
/*
|
||||
- * Pull in the generic implementation for the mutex fastpath.
|
||||
+ * Optimised mutex implementation of include/asm-generic/mutex-dec.h algorithm
|
||||
+ */
|
||||
+#ifndef _ASM_POWERPC_MUTEX_H
|
||||
+#define _ASM_POWERPC_MUTEX_H
|
||||
+
|
||||
+static inline int __mutex_cmpxchg_lock(atomic_t *v, int old, int new)
|
||||
+{
|
||||
+ int t;
|
||||
+
|
||||
+ __asm__ __volatile__ (
|
||||
+"1: lwarx %0,0,%1 # mutex trylock\n\
|
||||
+ cmpw 0,%0,%2\n\
|
||||
+ bne- 2f\n"
|
||||
+ PPC405_ERR77(0,%1)
|
||||
+" stwcx. %3,0,%1\n\
|
||||
+ bne- 1b"
|
||||
+ ISYNC_ON_SMP
|
||||
+ "\n\
|
||||
+2:"
|
||||
+ : "=&r" (t)
|
||||
+ : "r" (&v->counter), "r" (old), "r" (new)
|
||||
+ : "cc", "memory");
|
||||
+
|
||||
+ return t;
|
||||
+}
|
||||
+
|
||||
+static inline int __mutex_dec_return_lock(atomic_t *v)
|
||||
+{
|
||||
+ int t;
|
||||
+
|
||||
+ __asm__ __volatile__(
|
||||
+"1: lwarx %0,0,%1 # mutex lock\n\
|
||||
+ addic %0,%0,-1\n"
|
||||
+ PPC405_ERR77(0,%1)
|
||||
+" stwcx. %0,0,%1\n\
|
||||
+ bne- 1b"
|
||||
+ ISYNC_ON_SMP
|
||||
+ : "=&r" (t)
|
||||
+ : "r" (&v->counter)
|
||||
+ : "cc", "memory");
|
||||
+
|
||||
+ return t;
|
||||
+}
|
||||
+
|
||||
+static inline int __mutex_inc_return_unlock(atomic_t *v)
|
||||
+{
|
||||
+ int t;
|
||||
+
|
||||
+ __asm__ __volatile__(
|
||||
+ LWSYNC_ON_SMP
|
||||
+"1: lwarx %0,0,%1 # mutex unlock\n\
|
||||
+ addic %0,%0,1\n"
|
||||
+ PPC405_ERR77(0,%1)
|
||||
+" stwcx. %0,0,%1 \n\
|
||||
+ bne- 1b"
|
||||
+ : "=&r" (t)
|
||||
+ : "r" (&v->counter)
|
||||
+ : "cc", "memory");
|
||||
+
|
||||
+ return t;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * __mutex_fastpath_lock - try to take the lock by moving the count
|
||||
+ * from 1 to a 0 value
|
||||
+ * @count: pointer of type atomic_t
|
||||
+ * @fail_fn: function to call if the original value was not 1
|
||||
+ *
|
||||
+ * Change the count from 1 to a value lower than 1, and call <fail_fn> if
|
||||
+ * it wasn't 1 originally. This function MUST leave the value lower than
|
||||
+ * 1 even when the "1" assertion wasn't true.
|
||||
+ */
|
||||
+static inline void
|
||||
+__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
|
||||
+{
|
||||
+ if (unlikely(__mutex_dec_return_lock(count) < 0))
|
||||
+ fail_fn(count);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * __mutex_fastpath_lock_retval - try to take the lock by moving the count
|
||||
+ * from 1 to a 0 value
|
||||
+ * @count: pointer of type atomic_t
|
||||
+ * @fail_fn: function to call if the original value was not 1
|
||||
+ *
|
||||
+ * Change the count from 1 to a value lower than 1, and call <fail_fn> if
|
||||
+ * it wasn't 1 originally. This function returns 0 if the fastpath succeeds,
|
||||
+ * or anything the slow path function returns.
|
||||
+ */
|
||||
+static inline int
|
||||
+__mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *))
|
||||
+{
|
||||
+ if (unlikely(__mutex_dec_return_lock(count) < 0))
|
||||
+ return fail_fn(count);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * __mutex_fastpath_unlock - try to promote the count from 0 to 1
|
||||
+ * @count: pointer of type atomic_t
|
||||
+ * @fail_fn: function to call if the original value was not 0
|
||||
+ *
|
||||
+ * Try to promote the count from 0 to 1. If it wasn't 0, call <fail_fn>.
|
||||
+ * In the failure case, this function is allowed to either set the value to
|
||||
+ * 1, or to set it to a value lower than 1.
|
||||
+ */
|
||||
+static inline void
|
||||
+__mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
|
||||
+{
|
||||
+ if (unlikely(__mutex_inc_return_unlock(count) <= 0))
|
||||
+ fail_fn(count);
|
||||
+}
|
||||
+
|
||||
+#define __mutex_slowpath_needs_to_unlock() 1
|
||||
+
|
||||
+/**
|
||||
+ * __mutex_fastpath_trylock - try to acquire the mutex, without waiting
|
||||
+ *
|
||||
+ * @count: pointer of type atomic_t
|
||||
+ * @fail_fn: fallback function
|
||||
*
|
||||
- * TODO: implement optimized primitives instead, or leave the generic
|
||||
- * implementation in place, or pick the atomic_xchg() based generic
|
||||
- * implementation. (see asm-generic/mutex-xchg.h for details)
|
||||
+ * Change the count from 1 to 0, and return 1 (success), or if the count
|
||||
+ * was not 1, then return 0 (failure).
|
||||
*/
|
||||
+static inline int
|
||||
+__mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *))
|
||||
+{
|
||||
+ if (likely(__mutex_cmpxchg_lock(count, 1, 0) == 1))
|
||||
+ return 1;
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
-#include <asm-generic/mutex-dec.h>
|
||||
+#endif
|
||||
--- a/arch/powerpc/include/asm/synch.h
|
||||
+++ b/arch/powerpc/include/asm/synch.h
|
||||
@@ -5,6 +5,10 @@
|
||||
#include <linux/stringify.h>
|
||||
#include <asm/feature-fixups.h>
|
||||
|
||||
+#if defined(__powerpc64__) || defined(CONFIG_PPC_E500MC)
|
||||
+#define __SUBARCH_HAS_LWSYNC
|
||||
+#endif
|
||||
+
|
||||
#ifndef __ASSEMBLY__
|
||||
extern unsigned int __start___lwsync_fixup, __stop___lwsync_fixup;
|
||||
extern void do_lwsync_fixups(unsigned long value, void *fixup_start,
|
||||
--- a/arch/powerpc/include/asm/system.h
|
||||
+++ b/arch/powerpc/include/asm/system.h
|
||||
@@ -23,15 +23,17 @@
|
||||
* read_barrier_depends() prevents data-dependent loads being reordered
|
||||
* across this point (nop on PPC).
|
||||
*
|
||||
- * We have to use the sync instructions for mb(), since lwsync doesn't
|
||||
- * order loads with respect to previous stores. Lwsync is fine for
|
||||
- * rmb(), though. Note that rmb() actually uses a sync on 32-bit
|
||||
- * architectures.
|
||||
+ * *mb() variants without smp_ prefix must order all types of memory
|
||||
+ * operations with one another. sync is the only instruction sufficient
|
||||
+ * to do this.
|
||||
*
|
||||
- * For wmb(), we use sync since wmb is used in drivers to order
|
||||
- * stores to system memory with respect to writes to the device.
|
||||
- * However, smp_wmb() can be a lighter-weight lwsync or eieio barrier
|
||||
- * on SMP since it is only used to order updates to system memory.
|
||||
+ * For the smp_ barriers, ordering is for cacheable memory operations
|
||||
+ * only. We have to use the sync instruction for smp_mb(), since lwsync
|
||||
+ * doesn't order loads with respect to previous stores. Lwsync can be
|
||||
+ * used for smp_rmb() and smp_wmb().
|
||||
+ *
|
||||
+ * However, on CPUs that don't support lwsync, lwsync actually maps to a
|
||||
+ * heavy-weight sync, so smp_wmb() can be a lighter-weight eieio.
|
||||
*/
|
||||
#define mb() __asm__ __volatile__ ("sync" : : : "memory")
|
||||
#define rmb() __asm__ __volatile__ ("sync" : : : "memory")
|
||||
@@ -45,14 +47,14 @@
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
#ifdef __SUBARCH_HAS_LWSYNC
|
||||
-# define SMPWMB lwsync
|
||||
+# define SMPWMB LWSYNC
|
||||
#else
|
||||
# define SMPWMB eieio
|
||||
#endif
|
||||
|
||||
#define smp_mb() mb()
|
||||
-#define smp_rmb() rmb()
|
||||
-#define smp_wmb() __asm__ __volatile__ (__stringify(SMPWMB) : : :"memory")
|
||||
+#define smp_rmb() __asm__ __volatile__ (stringify_in_c(LWSYNC) : : :"memory")
|
||||
+#define smp_wmb() __asm__ __volatile__ (stringify_in_c(SMPWMB) : : :"memory")
|
||||
#define smp_read_barrier_depends() read_barrier_depends()
|
||||
#else
|
||||
#define smp_mb() barrier()
|
||||
@@ -0,0 +1,178 @@
|
||||
Subject: Fix DLPAR
|
||||
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
||||
References: 439491
|
||||
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
arch/powerpc/include/asm/pci.h | 2
|
||||
arch/powerpc/kernel/pci-common.c | 110 ++++++++++++++---------------
|
||||
arch/powerpc/platforms/pseries/pci_dlpar.c | 2
|
||||
3 files changed, 59 insertions(+), 55 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/include/asm/pci.h
|
||||
+++ b/arch/powerpc/include/asm/pci.h
|
||||
@@ -196,6 +196,8 @@ extern void pcibios_setup_new_device(str
|
||||
|
||||
extern void pcibios_claim_one_bus(struct pci_bus *b);
|
||||
|
||||
+extern void pcibios_allocate_bus_resources(struct pci_bus *bus);
|
||||
+
|
||||
extern void pcibios_resource_survey(void);
|
||||
|
||||
extern struct pci_controller *init_phb_dynamic(struct device_node *dn);
|
||||
--- a/arch/powerpc/kernel/pci-common.c
|
||||
+++ b/arch/powerpc/kernel/pci-common.c
|
||||
@@ -986,69 +986,66 @@ static int __init reparent_resources(str
|
||||
* as well.
|
||||
*/
|
||||
|
||||
-static void __init pcibios_allocate_bus_resources(struct list_head *bus_list)
|
||||
+void pcibios_allocate_bus_resources(struct pci_bus *bus)
|
||||
{
|
||||
- struct pci_bus *bus;
|
||||
+ struct pci_bus *b;
|
||||
int i;
|
||||
struct resource *res, *pr;
|
||||
|
||||
- /* Depth-First Search on bus tree */
|
||||
- list_for_each_entry(bus, bus_list, node) {
|
||||
- for (i = 0; i < PCI_BUS_NUM_RESOURCES; ++i) {
|
||||
- if ((res = bus->resource[i]) == NULL || !res->flags
|
||||
- || res->start > res->end)
|
||||
- continue;
|
||||
- if (bus->parent == NULL)
|
||||
- pr = (res->flags & IORESOURCE_IO) ?
|
||||
- &ioport_resource : &iomem_resource;
|
||||
- else {
|
||||
- /* Don't bother with non-root busses when
|
||||
- * re-assigning all resources. We clear the
|
||||
- * resource flags as if they were colliding
|
||||
- * and as such ensure proper re-allocation
|
||||
- * later.
|
||||
+ for (i = 0; i < PCI_BUS_NUM_RESOURCES; ++i) {
|
||||
+ if ((res = bus->resource[i]) == NULL || !res->flags
|
||||
+ || res->start > res->end)
|
||||
+ continue;
|
||||
+ if (bus->parent == NULL)
|
||||
+ pr = (res->flags & IORESOURCE_IO) ?
|
||||
+ &ioport_resource : &iomem_resource;
|
||||
+ else {
|
||||
+ /* Don't bother with non-root busses when
|
||||
+ * re-assigning all resources. We clear the
|
||||
+ * resource flags as if they were colliding
|
||||
+ * and as such ensure proper re-allocation
|
||||
+ * later.
|
||||
+ */
|
||||
+ if (ppc_pci_flags & PPC_PCI_REASSIGN_ALL_RSRC)
|
||||
+ goto clear_resource;
|
||||
+ pr = pci_find_parent_resource(bus->self, res);
|
||||
+ if (pr == res) {
|
||||
+ /* this happens when the generic PCI
|
||||
+ * code (wrongly) decides that this
|
||||
+ * bridge is transparent -- paulus
|
||||
*/
|
||||
- if (ppc_pci_flags & PPC_PCI_REASSIGN_ALL_RSRC)
|
||||
- goto clear_resource;
|
||||
- pr = pci_find_parent_resource(bus->self, res);
|
||||
- if (pr == res) {
|
||||
- /* this happens when the generic PCI
|
||||
- * code (wrongly) decides that this
|
||||
- * bridge is transparent -- paulus
|
||||
- */
|
||||
- continue;
|
||||
- }
|
||||
+ continue;
|
||||
}
|
||||
+ }
|
||||
|
||||
- DBG("PCI: %s (bus %d) bridge rsrc %d: %016llx-%016llx "
|
||||
- "[0x%x], parent %p (%s)\n",
|
||||
- bus->self ? pci_name(bus->self) : "PHB",
|
||||
- bus->number, i,
|
||||
- (unsigned long long)res->start,
|
||||
- (unsigned long long)res->end,
|
||||
- (unsigned int)res->flags,
|
||||
- pr, (pr && pr->name) ? pr->name : "nil");
|
||||
-
|
||||
- if (pr && !(pr->flags & IORESOURCE_UNSET)) {
|
||||
- if (request_resource(pr, res) == 0)
|
||||
- continue;
|
||||
- /*
|
||||
- * Must be a conflict with an existing entry.
|
||||
- * Move that entry (or entries) under the
|
||||
- * bridge resource and try again.
|
||||
- */
|
||||
- if (reparent_resources(pr, res) == 0)
|
||||
- continue;
|
||||
- }
|
||||
- printk(KERN_WARNING
|
||||
- "PCI: Cannot allocate resource region "
|
||||
- "%d of PCI bridge %d, will remap\n",
|
||||
- i, bus->number);
|
||||
-clear_resource:
|
||||
- res->flags = 0;
|
||||
+ DBG("PCI: %s (bus %d) bridge rsrc %d: %016llx-%016llx "
|
||||
+ "[0x%x], parent %p (%s)\n",
|
||||
+ bus->self ? pci_name(bus->self) : "PHB",
|
||||
+ bus->number, i,
|
||||
+ (unsigned long long)res->start,
|
||||
+ (unsigned long long)res->end,
|
||||
+ (unsigned int)res->flags,
|
||||
+ pr, (pr && pr->name) ? pr->name : "nil");
|
||||
+
|
||||
+ if (pr && !(pr->flags & IORESOURCE_UNSET)) {
|
||||
+ if (request_resource(pr, res) == 0)
|
||||
+ continue;
|
||||
+ /*
|
||||
+ * Must be a conflict with an existing entry.
|
||||
+ * Move that entry (or entries) under the
|
||||
+ * bridge resource and try again.
|
||||
+ */
|
||||
+ if (reparent_resources(pr, res) == 0)
|
||||
+ continue;
|
||||
}
|
||||
- pcibios_allocate_bus_resources(&bus->children);
|
||||
+ printk(KERN_WARNING "PCI: Cannot allocate resource region "
|
||||
+ "%d of PCI bridge %d, will remap\n", i, bus->number);
|
||||
+clear_resource:
|
||||
+ res->flags = 0;
|
||||
}
|
||||
+
|
||||
+ list_for_each_entry(b, &bus->children, node)
|
||||
+ pcibios_allocate_bus_resources(b);
|
||||
}
|
||||
|
||||
static inline void __devinit alloc_resource(struct pci_dev *dev, int idx)
|
||||
@@ -1119,10 +1116,13 @@ static void __init pcibios_allocate_reso
|
||||
|
||||
void __init pcibios_resource_survey(void)
|
||||
{
|
||||
+ struct pci_bus *b;
|
||||
+
|
||||
/* Allocate and assign resources. If we re-assign everything, then
|
||||
* we skip the allocate phase
|
||||
*/
|
||||
- pcibios_allocate_bus_resources(&pci_root_buses);
|
||||
+ list_for_each_entry(b, &pci_root_buses, node)
|
||||
+ pcibios_allocate_bus_resources(b);
|
||||
|
||||
if (!(ppc_pci_flags & PPC_PCI_REASSIGN_ALL_RSRC)) {
|
||||
pcibios_allocate_resources(0);
|
||||
--- a/arch/powerpc/platforms/pseries/pci_dlpar.c
|
||||
+++ b/arch/powerpc/platforms/pseries/pci_dlpar.c
|
||||
@@ -189,6 +189,7 @@ struct pci_controller * __devinit init_p
|
||||
{
|
||||
struct pci_controller *phb;
|
||||
int primary;
|
||||
+ struct pci_bus *b;
|
||||
|
||||
primary = list_empty(&hose_list);
|
||||
phb = pcibios_alloc_controller(dn);
|
||||
@@ -203,6 +204,7 @@ struct pci_controller * __devinit init_p
|
||||
eeh_add_device_tree_early(dn);
|
||||
|
||||
scan_phb(phb);
|
||||
+ pcibios_allocate_bus_resources(phb->bus);
|
||||
pcibios_fixup_new_pci_devices(phb->bus);
|
||||
pci_bus_add_devices(phb->bus);
|
||||
eeh_add_device_tree_late(phb->bus);
|
||||
@@ -0,0 +1,19 @@
|
||||
From: olh@suse.de
|
||||
Subject: force speed to fix autodetection on pegasos2
|
||||
Patch-mainline: never
|
||||
|
||||
---
|
||||
arch/powerpc/platforms/chrp/setup.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/powerpc/platforms/chrp/setup.c
|
||||
+++ b/arch/powerpc/platforms/chrp/setup.c
|
||||
@@ -295,7 +295,7 @@ static void chrp_init_early(void)
|
||||
if (!property)
|
||||
goto out_put;
|
||||
if (!strcmp(property, "failsafe") || !strcmp(property, "serial"))
|
||||
- add_preferred_console("ttyS", 0, NULL);
|
||||
+ add_preferred_console("ttyS", 0, "115200");
|
||||
out_put:
|
||||
of_node_put(node);
|
||||
}
|
||||
@@ -0,0 +1,834 @@
|
||||
Subject: Fix DLPAR
|
||||
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
||||
References: 439491
|
||||
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
arch/powerpc/include/asm/pci-bridge.h | 4
|
||||
arch/powerpc/include/asm/pci.h | 10 +
|
||||
arch/powerpc/kernel/pci-common.c | 124 ++++++++++++----------
|
||||
arch/powerpc/kernel/pci_32.c | 6 -
|
||||
arch/powerpc/kernel/pci_64.c | 27 +++-
|
||||
arch/powerpc/kernel/rtas_pci.c | 48 --------
|
||||
arch/powerpc/platforms/pseries/eeh.c | 44 ++++---
|
||||
arch/powerpc/platforms/pseries/pci_dlpar.c | 163 +++++++++++++++--------------
|
||||
drivers/pci/hotplug/rpadlpar_core.c | 49 +++++---
|
||||
drivers/pci/hotplug/rpaphp_slot.c | 4
|
||||
10 files changed, 245 insertions(+), 234 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/include/asm/pci-bridge.h
|
||||
+++ b/arch/powerpc/include/asm/pci-bridge.h
|
||||
@@ -234,9 +234,7 @@ extern void pcibios_remove_pci_devices(s
|
||||
|
||||
/** Discover new pci devices under this bus, and add them */
|
||||
extern void pcibios_add_pci_devices(struct pci_bus *bus);
|
||||
-extern void pcibios_fixup_new_pci_devices(struct pci_bus *bus);
|
||||
-
|
||||
-extern int pcibios_remove_root_bus(struct pci_controller *phb);
|
||||
+extern void pcibios_finish_adding_new_bus(struct pci_bus *bus);
|
||||
|
||||
static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus)
|
||||
{
|
||||
--- a/arch/powerpc/include/asm/pci.h
|
||||
+++ b/arch/powerpc/include/asm/pci.h
|
||||
@@ -201,6 +201,7 @@ extern void pcibios_allocate_bus_resourc
|
||||
extern void pcibios_resource_survey(void);
|
||||
|
||||
extern struct pci_controller *init_phb_dynamic(struct device_node *dn);
|
||||
+extern int remove_phb_dynamic(struct pci_controller *phb);
|
||||
|
||||
extern struct pci_dev *of_create_pci_dev(struct device_node *node,
|
||||
struct pci_bus *bus, int devfn);
|
||||
@@ -208,7 +209,8 @@ extern struct pci_dev *of_create_pci_dev
|
||||
extern void of_scan_pci_bridge(struct device_node *node,
|
||||
struct pci_dev *dev);
|
||||
|
||||
-extern void of_scan_bus(struct device_node *node, struct pci_bus *bus);
|
||||
+extern void of_scan_bus(struct device_node *node, struct pci_bus *bus,
|
||||
+ int rescan_existing);
|
||||
|
||||
extern int pci_read_irq_line(struct pci_dev *dev);
|
||||
|
||||
@@ -223,8 +225,10 @@ extern void pci_resource_to_user(const s
|
||||
const struct resource *rsrc,
|
||||
resource_size_t *start, resource_size_t *end);
|
||||
|
||||
-extern void pcibios_do_bus_setup(struct pci_bus *bus);
|
||||
-extern void pcibios_fixup_of_probed_bus(struct pci_bus *bus);
|
||||
+extern void pcibios_do_bus_setup_self(struct pci_bus *bus);
|
||||
+extern void pcibios_do_bus_setup_devices(struct pci_bus *bus);
|
||||
+extern void pcibios_fixup_bus_self(struct pci_bus *bus);
|
||||
+extern void pcibios_fixup_bus_devices(struct pci_bus *bus);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* __ASM_POWERPC_PCI_H */
|
||||
--- a/arch/powerpc/kernel/pci_32.c
|
||||
+++ b/arch/powerpc/kernel/pci_32.c
|
||||
@@ -418,7 +418,7 @@ static int __init pcibios_init(void)
|
||||
|
||||
subsys_initcall(pcibios_init);
|
||||
|
||||
-void __devinit pcibios_do_bus_setup(struct pci_bus *bus)
|
||||
+void __devinit pcibios_do_bus_setup_self(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
|
||||
unsigned long io_offset;
|
||||
@@ -459,6 +459,10 @@ void __devinit pcibios_do_bus_setup(stru
|
||||
}
|
||||
}
|
||||
|
||||
+void __devinit pcibios_do_bus_setup_devices(struct pci_bus *bus)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
/* the next one is stolen from the alpha port... */
|
||||
void __init
|
||||
pcibios_update_irq(struct pci_dev *dev, int irq)
|
||||
--- a/arch/powerpc/kernel/pci_64.c
|
||||
+++ b/arch/powerpc/kernel/pci_64.c
|
||||
@@ -225,14 +225,16 @@ struct pci_dev *of_create_pci_dev(struct
|
||||
EXPORT_SYMBOL(of_create_pci_dev);
|
||||
|
||||
void __devinit of_scan_bus(struct device_node *node,
|
||||
- struct pci_bus *bus)
|
||||
+ struct pci_bus *bus,
|
||||
+ int rescan_existing)
|
||||
{
|
||||
struct device_node *child;
|
||||
const u32 *reg;
|
||||
int reglen, devfn;
|
||||
struct pci_dev *dev;
|
||||
|
||||
- DBG("of_scan_bus(%s) bus no %d... \n", node->full_name, bus->number);
|
||||
+ DBG("of_scan_bus(%s) %s bus no %d... \n", node->full_name,
|
||||
+ rescan_existing ? "existing" : "new", bus->number);
|
||||
|
||||
/* Scan direct children */
|
||||
for_each_child_of_node(node, child) {
|
||||
@@ -249,8 +251,12 @@ void __devinit of_scan_bus(struct device
|
||||
DBG(" dev header type: %x\n", dev->hdr_type);
|
||||
}
|
||||
|
||||
- /* Ally all fixups */
|
||||
- pcibios_fixup_of_probed_bus(bus);
|
||||
+ /* Apply all fixups necessary. We don't fixup the bus "self"
|
||||
+ * for an existing bridge that is being rescanned
|
||||
+ */
|
||||
+ if (!rescan_existing)
|
||||
+ pcibios_fixup_bus_self(bus);
|
||||
+ pcibios_fixup_bus_devices(bus);
|
||||
|
||||
/* Now scan child busses */
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
@@ -346,7 +352,7 @@ void __devinit of_scan_pci_bridge(struct
|
||||
DBG(" probe mode: %d\n", mode);
|
||||
|
||||
if (mode == PCI_PROBE_DEVTREE)
|
||||
- of_scan_bus(node, bus);
|
||||
+ of_scan_bus(node, bus, 0);
|
||||
else if (mode == PCI_PROBE_NORMAL)
|
||||
pci_scan_child_bus(bus);
|
||||
}
|
||||
@@ -396,7 +402,7 @@ void __devinit scan_phb(struct pci_contr
|
||||
DBG(" probe mode: %d\n", mode);
|
||||
if (mode == PCI_PROBE_DEVTREE) {
|
||||
bus->subordinate = hose->last_busno;
|
||||
- of_scan_bus(node, bus);
|
||||
+ of_scan_bus(node, bus, 0);
|
||||
}
|
||||
|
||||
if (mode == PCI_PROBE_NORMAL)
|
||||
@@ -568,12 +574,15 @@ void __devinit pcibios_setup_new_device(
|
||||
}
|
||||
EXPORT_SYMBOL(pcibios_setup_new_device);
|
||||
|
||||
-void __devinit pcibios_do_bus_setup(struct pci_bus *bus)
|
||||
+void __devinit pcibios_do_bus_setup_self(struct pci_bus *bus)
|
||||
{
|
||||
- struct pci_dev *dev;
|
||||
-
|
||||
if (ppc_md.pci_dma_bus_setup)
|
||||
ppc_md.pci_dma_bus_setup(bus);
|
||||
+}
|
||||
+
|
||||
+void __devinit pcibios_do_bus_setup_devices(struct pci_bus *bus)
|
||||
+{
|
||||
+ struct pci_dev *dev;
|
||||
|
||||
list_for_each_entry(dev, &bus->devices, bus_list)
|
||||
pcibios_setup_new_device(dev);
|
||||
--- a/arch/powerpc/kernel/pci-common.c
|
||||
+++ b/arch/powerpc/kernel/pci-common.c
|
||||
@@ -789,63 +789,78 @@ static void __devinit pcibios_fixup_reso
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources);
|
||||
|
||||
-static void __devinit __pcibios_fixup_bus(struct pci_bus *bus)
|
||||
+void __devinit pcibios_fixup_bus_self(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_controller *hose = pci_bus_to_host(bus);
|
||||
struct pci_dev *dev = bus->self;
|
||||
+ struct resource *res;
|
||||
+ int i;
|
||||
|
||||
- pr_debug("PCI: Fixup bus %d (%s)\n", bus->number, dev ? pci_name(dev) : "PHB");
|
||||
+ pr_debug("PCI: Fixup bus resources %d (%s)\n",
|
||||
+ bus->number, dev ? pci_name(dev) : "PHB");
|
||||
|
||||
/* Fixup PCI<->PCI bridges. Host bridges are handled separately, for
|
||||
* now differently between 32 and 64 bits.
|
||||
*/
|
||||
- if (dev != NULL) {
|
||||
- struct resource *res;
|
||||
- int i;
|
||||
-
|
||||
- for (i = 0; i < PCI_BUS_NUM_RESOURCES; ++i) {
|
||||
- if ((res = bus->resource[i]) == NULL)
|
||||
- continue;
|
||||
- if (!res->flags)
|
||||
- continue;
|
||||
- if (i >= 3 && bus->self->transparent)
|
||||
- continue;
|
||||
- /* On PowerMac, Apple leaves bridge windows open over
|
||||
- * an inaccessible region of memory space (0...fffff)
|
||||
- * which is somewhat bogus, but that's what they think
|
||||
- * means disabled...
|
||||
- *
|
||||
- * We clear those to force them to be reallocated later
|
||||
- *
|
||||
- * We detect such regions by the fact that the base is
|
||||
- * equal to the pci_mem_offset of the host bridge and
|
||||
- * their size is smaller than 1M.
|
||||
- */
|
||||
- if (res->flags & IORESOURCE_MEM &&
|
||||
- res->start == hose->pci_mem_offset &&
|
||||
- res->end < 0x100000) {
|
||||
- printk(KERN_INFO
|
||||
- "PCI: Closing bogus Apple Firmware"
|
||||
- " region %d on bus 0x%02x\n",
|
||||
- i, bus->number);
|
||||
- res->flags = 0;
|
||||
- continue;
|
||||
- }
|
||||
+ if (dev == NULL)
|
||||
+ goto host_bridge;
|
||||
|
||||
- pr_debug("PCI:%s Bus rsrc %d %016llx-%016llx [%x] fixup...\n",
|
||||
- pci_name(dev), i,
|
||||
- (unsigned long long)res->start,\
|
||||
- (unsigned long long)res->end,
|
||||
- (unsigned int)res->flags);
|
||||
+ for (i = 0; i < PCI_BUS_NUM_RESOURCES; ++i) {
|
||||
+ if ((res = bus->resource[i]) == NULL)
|
||||
+ continue;
|
||||
+ if (!res->flags)
|
||||
+ continue;
|
||||
+ if (i >= 3 && bus->self->transparent)
|
||||
+ continue;
|
||||
|
||||
- fixup_resource(res, dev);
|
||||
+ /* On PowerMac, Apple leaves bridge windows open over
|
||||
+ * an inaccessible region of memory space (0...fffff)
|
||||
+ * which is somewhat bogus, but that's what they think
|
||||
+ * means disabled...
|
||||
+ *
|
||||
+ * We clear those to force them to be reallocated later
|
||||
+ *
|
||||
+ * We detect such regions by the fact that the base is
|
||||
+ * equal to the pci_mem_offset of the host bridge and
|
||||
+ * their size is smaller than 1M.
|
||||
+ */
|
||||
+ if (res->flags & IORESOURCE_MEM &&
|
||||
+ res->start == hose->pci_mem_offset &&
|
||||
+ res->end < 0x100000) {
|
||||
+ printk(KERN_INFO
|
||||
+ "PCI: Closing bogus Apple Firmware"
|
||||
+ " region %d on bus 0x%02x\n",
|
||||
+ i, bus->number);
|
||||
+ res->flags = 0;
|
||||
+ continue;
|
||||
}
|
||||
+
|
||||
+ pr_debug("PCI:%s Bus rsrc %d %016llx-%016llx [%x] fixup...\n",
|
||||
+ pci_name(dev), i,
|
||||
+ (unsigned long long)res->start,\
|
||||
+ (unsigned long long)res->end,
|
||||
+ (unsigned int)res->flags);
|
||||
+
|
||||
+ fixup_resource(res, dev);
|
||||
}
|
||||
|
||||
+host_bridge:
|
||||
+
|
||||
+ /* Additional setup that is different between 32 and 64 bits for now */
|
||||
+ pcibios_do_bus_setup_self(bus);
|
||||
+}
|
||||
+
|
||||
+void __devinit pcibios_fixup_bus_devices(struct pci_bus *bus)
|
||||
+{
|
||||
+ struct pci_dev *dev = bus->self;
|
||||
+
|
||||
+ pr_debug("PCI: Fixup bus devices %d (%s)\n",
|
||||
+ bus->number, dev ? pci_name(dev) : "PHB");
|
||||
+
|
||||
/* Additional setup that is different between 32 and 64 bits for now */
|
||||
- pcibios_do_bus_setup(bus);
|
||||
+ pcibios_do_bus_setup_devices(bus);
|
||||
|
||||
- /* Platform specific bus fixups */
|
||||
+ /* Platform specific bus fixups (XXX Get rid of these !) */
|
||||
if (ppc_md.pcibios_fixup_bus)
|
||||
ppc_md.pcibios_fixup_bus(bus);
|
||||
|
||||
@@ -864,19 +879,11 @@ void __devinit pcibios_fixup_bus(struct
|
||||
*/
|
||||
if (bus->self != NULL)
|
||||
pci_read_bridge_bases(bus);
|
||||
- __pcibios_fixup_bus(bus);
|
||||
+ pcibios_fixup_bus_self(bus);
|
||||
+ pcibios_fixup_bus_devices(bus);
|
||||
}
|
||||
EXPORT_SYMBOL(pcibios_fixup_bus);
|
||||
|
||||
-/* When building a bus from the OF tree rather than probing, we need a
|
||||
- * slightly different version of the fixup which doesn't read the
|
||||
- * bridge bases using config space accesses
|
||||
- */
|
||||
-void __devinit pcibios_fixup_of_probed_bus(struct pci_bus *bus)
|
||||
-{
|
||||
- __pcibios_fixup_bus(bus);
|
||||
-}
|
||||
-
|
||||
static int skip_isa_ioresource_align(struct pci_dev *dev)
|
||||
{
|
||||
if ((ppc_pci_flags & PPC_PCI_CAN_SKIP_ISA_ALIGN) &&
|
||||
@@ -992,9 +999,12 @@ void pcibios_allocate_bus_resources(stru
|
||||
int i;
|
||||
struct resource *res, *pr;
|
||||
|
||||
+ DBG("PCI: Allocating bus resources for %04x:%02x...\n",
|
||||
+ pci_domain_nr(bus), bus->number);
|
||||
+
|
||||
for (i = 0; i < PCI_BUS_NUM_RESOURCES; ++i) {
|
||||
if ((res = bus->resource[i]) == NULL || !res->flags
|
||||
- || res->start > res->end)
|
||||
+ || res->start > res->end || res->parent)
|
||||
continue;
|
||||
if (bus->parent == NULL)
|
||||
pr = (res->flags & IORESOURCE_IO) ?
|
||||
@@ -1047,6 +1057,7 @@ clear_resource:
|
||||
list_for_each_entry(b, &bus->children, node)
|
||||
pcibios_allocate_bus_resources(b);
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(pcibios_allocate_bus_resources);
|
||||
|
||||
static inline void __devinit alloc_resource(struct pci_dev *dev, int idx)
|
||||
{
|
||||
@@ -1157,6 +1168,13 @@ void __devinit pcibios_claim_one_bus(str
|
||||
|
||||
if (r->parent || !r->start || !r->flags)
|
||||
continue;
|
||||
+
|
||||
+ DBG("PCI: Claiming %s: Resource %d: %016llx..%016llx [%x]\n",
|
||||
+ pci_name(dev), i,
|
||||
+ (unsigned long long)r->start,
|
||||
+ (unsigned long long)r->end,
|
||||
+ (unsigned int)r->flags);
|
||||
+
|
||||
pci_claim_resource(dev, i);
|
||||
}
|
||||
}
|
||||
--- a/arch/powerpc/kernel/rtas_pci.c
|
||||
+++ b/arch/powerpc/kernel/rtas_pci.c
|
||||
@@ -301,51 +301,3 @@ void __init find_and_init_phbs(void)
|
||||
#endif /* CONFIG_PPC32 */
|
||||
}
|
||||
}
|
||||
-
|
||||
-/* RPA-specific bits for removing PHBs */
|
||||
-int pcibios_remove_root_bus(struct pci_controller *phb)
|
||||
-{
|
||||
- struct pci_bus *b = phb->bus;
|
||||
- struct resource *res;
|
||||
- int rc, i;
|
||||
-
|
||||
- res = b->resource[0];
|
||||
- if (!res->flags) {
|
||||
- printk(KERN_ERR "%s: no IO resource for PHB %s\n", __func__,
|
||||
- b->name);
|
||||
- return 1;
|
||||
- }
|
||||
-
|
||||
- rc = pcibios_unmap_io_space(b);
|
||||
- if (rc) {
|
||||
- printk(KERN_ERR "%s: failed to unmap IO on bus %s\n",
|
||||
- __func__, b->name);
|
||||
- return 1;
|
||||
- }
|
||||
-
|
||||
- if (release_resource(res)) {
|
||||
- printk(KERN_ERR "%s: failed to release IO on bus %s\n",
|
||||
- __func__, b->name);
|
||||
- return 1;
|
||||
- }
|
||||
-
|
||||
- for (i = 1; i < 3; ++i) {
|
||||
- res = b->resource[i];
|
||||
- if (!res->flags && i == 0) {
|
||||
- printk(KERN_ERR "%s: no MEM resource for PHB %s\n",
|
||||
- __func__, b->name);
|
||||
- return 1;
|
||||
- }
|
||||
- if (res->flags && release_resource(res)) {
|
||||
- printk(KERN_ERR
|
||||
- "%s: failed to release IO %d on bus %s\n",
|
||||
- __func__, i, b->name);
|
||||
- return 1;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- pcibios_free_controller(phb);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-EXPORT_SYMBOL(pcibios_remove_root_bus);
|
||||
--- a/arch/powerpc/platforms/pseries/eeh.c
|
||||
+++ b/arch/powerpc/platforms/pseries/eeh.c
|
||||
@@ -21,6 +21,8 @@
|
||||
* Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com>
|
||||
*/
|
||||
|
||||
+#undef DEBUG
|
||||
+
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
@@ -488,10 +490,8 @@ int eeh_dn_check_failure(struct device_n
|
||||
if (!(pdn->eeh_mode & EEH_MODE_SUPPORTED) ||
|
||||
pdn->eeh_mode & EEH_MODE_NOCHECK) {
|
||||
ignored_check++;
|
||||
-#ifdef DEBUG
|
||||
- printk ("EEH:ignored check (%x) for %s %s\n",
|
||||
- pdn->eeh_mode, pci_name (dev), dn->full_name);
|
||||
-#endif
|
||||
+ pr_debug("EEH: Ignored check (%x) for %s %s\n",
|
||||
+ pdn->eeh_mode, pci_name (dev), dn->full_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1014,10 +1014,9 @@ static void *early_enable_eeh(struct dev
|
||||
eeh_subsystem_enabled = 1;
|
||||
pdn->eeh_mode |= EEH_MODE_SUPPORTED;
|
||||
|
||||
-#ifdef DEBUG
|
||||
- printk(KERN_DEBUG "EEH: %s: eeh enabled, config=%x pe_config=%x\n",
|
||||
- dn->full_name, pdn->eeh_config_addr, pdn->eeh_pe_config_addr);
|
||||
-#endif
|
||||
+ pr_debug("EEH: %s: eeh enabled, config=%x pe_config=%x\n",
|
||||
+ dn->full_name, pdn->eeh_config_addr,
|
||||
+ pdn->eeh_pe_config_addr);
|
||||
} else {
|
||||
|
||||
/* This device doesn't support EEH, but it may have an
|
||||
@@ -1161,13 +1160,17 @@ static void eeh_add_device_late(struct p
|
||||
if (!dev || !eeh_subsystem_enabled)
|
||||
return;
|
||||
|
||||
-#ifdef DEBUG
|
||||
- printk(KERN_DEBUG "EEH: adding device %s\n", pci_name(dev));
|
||||
-#endif
|
||||
+ pr_debug("EEH: Adding device %s\n", pci_name(dev));
|
||||
|
||||
- pci_dev_get (dev);
|
||||
dn = pci_device_to_OF_node(dev);
|
||||
pdn = PCI_DN(dn);
|
||||
+ if (pdn->pcidev == dev) {
|
||||
+ pr_debug("EEH: Already referenced !\n");
|
||||
+ return;
|
||||
+ }
|
||||
+ WARN_ON(pdn->pcidev);
|
||||
+
|
||||
+ pci_dev_get (dev);
|
||||
pdn->pcidev = dev;
|
||||
|
||||
pci_addr_cache_insert_device(dev);
|
||||
@@ -1206,17 +1209,18 @@ static void eeh_remove_device(struct pci
|
||||
return;
|
||||
|
||||
/* Unregister the device with the EEH/PCI address search system */
|
||||
-#ifdef DEBUG
|
||||
- printk(KERN_DEBUG "EEH: remove device %s\n", pci_name(dev));
|
||||
-#endif
|
||||
- pci_addr_cache_remove_device(dev);
|
||||
- eeh_sysfs_remove_device(dev);
|
||||
+ pr_debug("EEH: Removing device %s\n", pci_name(dev));
|
||||
|
||||
dn = pci_device_to_OF_node(dev);
|
||||
- if (PCI_DN(dn)->pcidev) {
|
||||
- PCI_DN(dn)->pcidev = NULL;
|
||||
- pci_dev_put (dev);
|
||||
+ if (PCI_DN(dn)->pcidev == NULL) {
|
||||
+ pr_debug("EEH: Not referenced !\n");
|
||||
+ return;
|
||||
}
|
||||
+ PCI_DN(dn)->pcidev = NULL;
|
||||
+ pci_dev_put (dev);
|
||||
+
|
||||
+ pci_addr_cache_remove_device(dev);
|
||||
+ eeh_sysfs_remove_device(dev);
|
||||
}
|
||||
|
||||
void eeh_remove_bus_device(struct pci_dev *dev)
|
||||
--- a/arch/powerpc/platforms/pseries/pci_dlpar.c
|
||||
+++ b/arch/powerpc/platforms/pseries/pci_dlpar.c
|
||||
@@ -25,6 +25,8 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
+#undef DEBUG
|
||||
+
|
||||
#include <linux/pci.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/ppc-pci.h>
|
||||
@@ -69,73 +71,41 @@ EXPORT_SYMBOL_GPL(pcibios_find_pci_bus);
|
||||
* Remove all of the PCI devices under this bus both from the
|
||||
* linux pci device tree, and from the powerpc EEH address cache.
|
||||
*/
|
||||
-void
|
||||
-pcibios_remove_pci_devices(struct pci_bus *bus)
|
||||
+void pcibios_remove_pci_devices(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_dev *dev, *tmp;
|
||||
+ struct pci_bus *child_bus;
|
||||
|
||||
+ /* First go down child busses */
|
||||
+ list_for_each_entry(child_bus, &bus->children, node)
|
||||
+ pcibios_remove_pci_devices(child_bus);
|
||||
+
|
||||
+ pr_debug("PCI: Removing devices on bus %04x:%02x\n",
|
||||
+ pci_domain_nr(bus), bus->number);
|
||||
list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
|
||||
+ pr_debug(" * Removing %s...\n", pci_name(dev));
|
||||
eeh_remove_bus_device(dev);
|
||||
pci_remove_bus_device(dev);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
|
||||
|
||||
-/* Must be called before pci_bus_add_devices */
|
||||
-void
|
||||
-pcibios_fixup_new_pci_devices(struct pci_bus *bus)
|
||||
-{
|
||||
- struct pci_dev *dev;
|
||||
-
|
||||
- list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
- /* Skip already-added devices */
|
||||
- if (!dev->is_added) {
|
||||
- int i;
|
||||
-
|
||||
- /* Fill device archdata and setup iommu table */
|
||||
- pcibios_setup_new_device(dev);
|
||||
-
|
||||
- pci_read_irq_line(dev);
|
||||
- for (i = 0; i < PCI_NUM_RESOURCES; i++) {
|
||||
- struct resource *r = &dev->resource[i];
|
||||
-
|
||||
- if (r->parent || !r->start || !r->flags)
|
||||
- continue;
|
||||
- pci_claim_resource(dev, i);
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-EXPORT_SYMBOL_GPL(pcibios_fixup_new_pci_devices);
|
||||
-
|
||||
-static int
|
||||
-pcibios_pci_config_bridge(struct pci_dev *dev)
|
||||
+void pcibios_finish_adding_new_bus(struct pci_bus *bus)
|
||||
{
|
||||
- u8 sec_busno;
|
||||
- struct pci_bus *child_bus;
|
||||
-
|
||||
- /* Get busno of downstream bus */
|
||||
- pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno);
|
||||
+ pr_debug("PCI: Finishing adding hotplug bus %04x:%02x\n",
|
||||
+ pci_domain_nr(bus), bus->number);
|
||||
|
||||
- /* Add to children of PCI bridge dev->bus */
|
||||
- child_bus = pci_add_new_bus(dev->bus, dev, sec_busno);
|
||||
- if (!child_bus) {
|
||||
- printk (KERN_ERR "%s: could not add second bus\n", __func__);
|
||||
- return -EIO;
|
||||
- }
|
||||
- sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number);
|
||||
-
|
||||
- pci_scan_child_bus(child_bus);
|
||||
+ /* Allocate bus and devices resources */
|
||||
+ pcibios_allocate_bus_resources(bus);
|
||||
+ pcibios_claim_one_bus(bus);
|
||||
|
||||
- /* Fixup new pci devices */
|
||||
- pcibios_fixup_new_pci_devices(child_bus);
|
||||
+ /* Add new devices to global lists. Register in proc, sysfs. */
|
||||
+ pci_bus_add_devices(bus);
|
||||
|
||||
- /* Make the discovered devices available */
|
||||
- pci_bus_add_devices(child_bus);
|
||||
-
|
||||
- eeh_add_device_tree_late(child_bus);
|
||||
- return 0;
|
||||
+ /* Fixup EEH */
|
||||
+ eeh_add_device_tree_late(bus);
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(pcibios_finish_adding_new_bus);
|
||||
|
||||
/**
|
||||
* pcibios_add_pci_devices - adds new pci devices to bus
|
||||
@@ -147,10 +117,9 @@ pcibios_pci_config_bridge(struct pci_dev
|
||||
* is how this routine differs from other, similar pcibios
|
||||
* routines.)
|
||||
*/
|
||||
-void
|
||||
-pcibios_add_pci_devices(struct pci_bus * bus)
|
||||
+void pcibios_add_pci_devices(struct pci_bus * bus)
|
||||
{
|
||||
- int slotno, num, mode;
|
||||
+ int slotno, num, mode, pass, max;
|
||||
struct pci_dev *dev;
|
||||
struct device_node *dn = pci_bus_to_OF_node(bus);
|
||||
|
||||
@@ -162,25 +131,24 @@ pcibios_add_pci_devices(struct pci_bus *
|
||||
|
||||
if (mode == PCI_PROBE_DEVTREE) {
|
||||
/* use ofdt-based probe */
|
||||
- of_scan_bus(dn, bus);
|
||||
- if (!list_empty(&bus->devices)) {
|
||||
- pcibios_fixup_new_pci_devices(bus);
|
||||
- pci_bus_add_devices(bus);
|
||||
- eeh_add_device_tree_late(bus);
|
||||
- }
|
||||
+ of_scan_bus(dn, bus, 1);
|
||||
+ if (!list_empty(&bus->devices))
|
||||
+ pcibios_finish_adding_new_bus(bus);
|
||||
} else if (mode == PCI_PROBE_NORMAL) {
|
||||
/* use legacy probe */
|
||||
slotno = PCI_SLOT(PCI_DN(dn->child)->devfn);
|
||||
num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
|
||||
- if (num) {
|
||||
- pcibios_fixup_new_pci_devices(bus);
|
||||
- pci_bus_add_devices(bus);
|
||||
- eeh_add_device_tree_late(bus);
|
||||
+ if (!num)
|
||||
+ return;
|
||||
+ pcibios_fixup_bus_devices(bus);
|
||||
+ max = bus->secondary;
|
||||
+ for (pass=0; pass < 2; pass++)
|
||||
+ list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
|
||||
+ dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
|
||||
+ max = pci_scan_bridge(bus, dev, max, pass);
|
||||
}
|
||||
-
|
||||
- list_for_each_entry(dev, &bus->devices, bus_list)
|
||||
- if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
|
||||
- pcibios_pci_config_bridge(dev);
|
||||
+ pcibios_finish_adding_new_bus(bus);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcibios_add_pci_devices);
|
||||
@@ -189,7 +157,8 @@ struct pci_controller * __devinit init_p
|
||||
{
|
||||
struct pci_controller *phb;
|
||||
int primary;
|
||||
- struct pci_bus *b;
|
||||
+
|
||||
+ pr_debug("PCI: Initializing new hotplug PHB %s\n", dn->full_name);
|
||||
|
||||
primary = list_empty(&hose_list);
|
||||
phb = pcibios_alloc_controller(dn);
|
||||
@@ -204,11 +173,57 @@ struct pci_controller * __devinit init_p
|
||||
eeh_add_device_tree_early(dn);
|
||||
|
||||
scan_phb(phb);
|
||||
- pcibios_allocate_bus_resources(phb->bus);
|
||||
- pcibios_fixup_new_pci_devices(phb->bus);
|
||||
- pci_bus_add_devices(phb->bus);
|
||||
- eeh_add_device_tree_late(phb->bus);
|
||||
+ pcibios_finish_adding_new_bus(phb->bus);
|
||||
|
||||
return phb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(init_phb_dynamic);
|
||||
+
|
||||
+
|
||||
+
|
||||
+
|
||||
+/* RPA-specific bits for removing PHBs */
|
||||
+int remove_phb_dynamic(struct pci_controller *phb)
|
||||
+{
|
||||
+ struct pci_bus *b = phb->bus;
|
||||
+ struct resource *res;
|
||||
+ int rc, i;
|
||||
+
|
||||
+ pr_debug("PCI: Removing PHB %04x:%02x... \n", pci_domain_nr(b), b->number);
|
||||
+
|
||||
+ /* We -know- there aren't any child devices anymore at this stage
|
||||
+ * and thus, we can safely unmap the IO space as it's not in use
|
||||
+ */
|
||||
+ res = &phb->io_resource;
|
||||
+ if (res->flags & IORESOURCE_IO) {
|
||||
+ rc = pcibios_unmap_io_space(b);
|
||||
+ if (rc) {
|
||||
+ printk(KERN_ERR "%s: failed to unmap IO on bus %s\n",
|
||||
+ __func__, b->name);
|
||||
+ return 1;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Unregister the bridge device from sysfs and remove the PCI bus */
|
||||
+ device_unregister(b->bridge);
|
||||
+ phb->bus = NULL;
|
||||
+ pci_remove_bus(b);
|
||||
+
|
||||
+ /* Now release the IO resource */
|
||||
+ if (res->flags & IORESOURCE_IO)
|
||||
+ release_resource(res);
|
||||
+
|
||||
+ /* Release memory resources */
|
||||
+ for (i = 0; i < 3; ++i) {
|
||||
+ res = &phb->mem_resources[i];
|
||||
+ if (!(res->flags & IORESOURCE_MEM))
|
||||
+ continue;
|
||||
+ release_resource(res);
|
||||
+ }
|
||||
+
|
||||
+ /* Free pci_controller data structure */
|
||||
+ pcibios_free_controller(phb);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(remove_phb_dynamic);
|
||||
--- a/drivers/pci/hotplug/rpadlpar_core.c
|
||||
+++ b/drivers/pci/hotplug/rpadlpar_core.c
|
||||
@@ -14,6 +14,8 @@
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
+#undef DEBUG
|
||||
+
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
@@ -155,16 +157,15 @@ static void dlpar_pci_add_bus(struct dev
|
||||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
|
||||
of_scan_pci_bridge(dn, dev);
|
||||
|
||||
- pcibios_fixup_new_pci_devices(dev->subordinate);
|
||||
-
|
||||
- /* Claim new bus resources */
|
||||
- pcibios_claim_one_bus(dev->bus);
|
||||
-
|
||||
/* Map IO space for child bus, which may or may not succeed */
|
||||
pcibios_map_io_space(dev->subordinate);
|
||||
|
||||
- /* Add new devices to global lists. Register in proc, sysfs. */
|
||||
- pci_bus_add_devices(phb->bus);
|
||||
+ /* Finish adding it : resource allocation, adding devices, etc...
|
||||
+ * Note that we need to perform the finish pass on the -parent-
|
||||
+ * bus of the EADS bridge so the bridge device itself gets
|
||||
+ * properly added
|
||||
+ */
|
||||
+ pcibios_finish_adding_new_bus(phb->bus);
|
||||
}
|
||||
|
||||
static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
|
||||
@@ -206,22 +207,19 @@ static int dlpar_add_pci_slot(char *drc_
|
||||
static int dlpar_remove_root_bus(struct pci_controller *phb)
|
||||
{
|
||||
struct pci_bus *phb_bus;
|
||||
- int rc;
|
||||
|
||||
phb_bus = phb->bus;
|
||||
+ pr_debug("PCI: -> removing root bus %04x:%02x\n",
|
||||
+ pci_domain_nr(phb_bus), phb_bus->number);
|
||||
+
|
||||
+ /* We cannot to remove a root bus that has children */
|
||||
if (!(list_empty(&phb_bus->children) &&
|
||||
list_empty(&phb_bus->devices))) {
|
||||
+ pr_debug("PCI: PHB removal failed, bus not empty !\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
- rc = pcibios_remove_root_bus(phb);
|
||||
- if (rc)
|
||||
- return -EIO;
|
||||
-
|
||||
- device_unregister(phb_bus->bridge);
|
||||
- pci_remove_bus(phb_bus);
|
||||
-
|
||||
- return 0;
|
||||
+ return remove_phb_dynamic(phb);
|
||||
}
|
||||
|
||||
static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
|
||||
@@ -233,6 +231,8 @@ static int dlpar_remove_phb(char *drc_na
|
||||
if (!pcibios_find_pci_bus(dn))
|
||||
return -EINVAL;
|
||||
|
||||
+ pr_debug("PCI: Removing PHB %s...\n", dn->full_name);
|
||||
+
|
||||
/* If pci slot is hotplugable, use hotplug to remove it */
|
||||
slot = find_php_slot(dn);
|
||||
if (slot) {
|
||||
@@ -378,25 +378,36 @@ int dlpar_remove_pci_slot(char *drc_name
|
||||
if (!bus)
|
||||
return -EINVAL;
|
||||
|
||||
- /* If pci slot is hotplugable, use hotplug to remove it */
|
||||
+ pr_debug("PCI: Removing PCI slot below EADS bridge %s\n",
|
||||
+ bus->self ? pci_name(bus->self) : "<!PHB!>");
|
||||
+
|
||||
+ /* If pci slot is hotplugable, remove hotplug data structures */
|
||||
slot = find_php_slot(dn);
|
||||
if (slot) {
|
||||
+ pr_debug("PCI: Removing hotplug slot for %04x:%02x...\n",
|
||||
+ pci_domain_nr(bus), bus->number);
|
||||
if (rpaphp_deregister_slot(slot)) {
|
||||
printk(KERN_ERR
|
||||
"%s: unable to remove hotplug slot %s\n",
|
||||
__func__, drc_name);
|
||||
return -EIO;
|
||||
}
|
||||
- } else
|
||||
- pcibios_remove_pci_devices(bus);
|
||||
+ }
|
||||
+
|
||||
+ /* Remove all devices below slot */
|
||||
+ pcibios_remove_pci_devices(bus);
|
||||
|
||||
+ /* Unmap PCI IO space */
|
||||
if (pcibios_unmap_io_space(bus)) {
|
||||
printk(KERN_ERR "%s: failed to unmap bus range\n",
|
||||
__func__);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
+ /* Remove the EADS bridge device itself */
|
||||
BUG_ON(!bus->self);
|
||||
+ pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self));
|
||||
+ eeh_remove_bus_device(bus->self);
|
||||
pci_remove_bus_device(bus->self);
|
||||
return 0;
|
||||
}
|
||||
--- a/drivers/pci/hotplug/rpaphp_slot.c
|
||||
+++ b/drivers/pci/hotplug/rpaphp_slot.c
|
||||
@@ -145,9 +145,5 @@ int rpaphp_register_slot(struct slot *sl
|
||||
list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
|
||||
info("Slot [%s] registered\n", slot->name);
|
||||
return 0;
|
||||
-
|
||||
-sysfs_fail:
|
||||
- pci_hp_deregister(php_slot);
|
||||
- return retval;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
Subject: powerpc/pci: Fix unmapping of IO space on 64-bit
|
||||
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
||||
References: 439491
|
||||
|
||||
A typo/thinko made us pass the wrong argument to __flush_hash_table_range
|
||||
when unplugging bridges, thus not flushing all the translations for
|
||||
the IO space on unplug.
|
||||
|
||||
This causes the hypervisor to refuse unplugging slots.
|
||||
|
||||
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
arch/powerpc/kernel/pci_64.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/powerpc/kernel/pci_64.c
|
||||
+++ b/arch/powerpc/kernel/pci_64.c
|
||||
@@ -455,7 +455,7 @@ int pcibios_unmap_io_space(struct pci_bu
|
||||
pci_name(bus->self));
|
||||
|
||||
__flush_hash_table_range(&init_mm, res->start + _IO_BASE,
|
||||
- res->end - res->start + 1);
|
||||
+ res->end + _IO_BASE + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
From: unknown@suse.de
|
||||
Subject: some crazy ppc patch
|
||||
|
||||
add prom=nodisplay
|
||||
avoid crash in firmware on IBM B50 when OF stdout is on serial.
|
||||
|
||||
0 > boot scsi/sd@4:1,yaboot |
|
||||
yaboot starting: loaded at 00200000 00222530 (0/0/00c1a078; sp: 00efffd0)
|
||||
brokenfirmware did not claim executable memory, fixed it myself
|
||||
Config file 'yaboot.cnf' read, 213 bytes
|
||||
|
||||
Welcome to yaboot version 10.1.22-r945.SuSE
|
||||
booted from '/pci@80000000/scsi@10/sd@4:1,yaboot'
|
||||
Enter "help" to get some basic usage information
|
||||
boot:
|
||||
* linux
|
||||
boot: linux 3
|
||||
Please wait, loading kernel...
|
||||
Allocated 00600000 bytes for executable @ 02000000
|
||||
Elf32 kernel loaded...
|
||||
Loading ramdisk...
|
||||
ramdisk loaded 0030e057 @ 04100000
|
||||
OF stdout device is: /pci@80000000/isa@b/serial@i3f8
|
||||
command line: root=/dev/system/root xmon=on sysrq=1 quiet panic=12 3
|
||||
memory layout at init:
|
||||
memory_limit : 00000000 (16 MB aligned)
|
||||
alloc_bottom : 0440f000
|
||||
alloc_top : 30000000
|
||||
alloc_top_hi : 40000000
|
||||
rmo_top : 30000000
|
||||
ram_top : 40000000
|
||||
Looking for displays
|
||||
found display : /pci@80000000/display@16, opening ...
|
||||
Unexpected Firmware Error:
|
||||
DEFAULT CATCH!, code=fff00300 at %SRR0: 00c18ccc %SRR1: 00003030
|
||||
ok
|
||||
0 > reset-all
|
||||
|
||||
|
||||
---
|
||||
arch/powerpc/kernel/prom_init.c | 12 ++++++++++--
|
||||
1 file changed, 10 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/kernel/prom_init.c
|
||||
+++ b/arch/powerpc/kernel/prom_init.c
|
||||
@@ -172,6 +172,7 @@ static unsigned long __initdata dt_strin
|
||||
|
||||
static unsigned long __initdata prom_initrd_start, prom_initrd_end;
|
||||
|
||||
+static int __initdata prom_no_display;
|
||||
#ifdef CONFIG_PPC64
|
||||
static int __initdata prom_iommu_force_on;
|
||||
static int __initdata prom_iommu_off;
|
||||
@@ -555,9 +556,7 @@ unsigned long prom_memparse(const char *
|
||||
static void __init early_cmdline_parse(void)
|
||||
{
|
||||
struct prom_t *_prom = &RELOC(prom);
|
||||
-#ifdef CONFIG_PPC64
|
||||
const char *opt;
|
||||
-#endif
|
||||
char *p;
|
||||
int l = 0;
|
||||
|
||||
@@ -572,6 +571,14 @@ static void __init early_cmdline_parse(v
|
||||
#endif /* CONFIG_CMDLINE */
|
||||
prom_printf("command line: %s\n", RELOC(prom_cmd_line));
|
||||
|
||||
+ opt = strstr(RELOC(prom_cmd_line), RELOC("prom="));
|
||||
+ if (opt) {
|
||||
+ opt += 5;
|
||||
+ while (*opt && *opt == ' ')
|
||||
+ opt++;
|
||||
+ if (!strncmp(opt, RELOC("nodisplay"), 9))
|
||||
+ RELOC(prom_no_display) = 1;
|
||||
+ }
|
||||
#ifdef CONFIG_PPC64
|
||||
opt = strstr(RELOC(prom_cmd_line), RELOC("iommu="));
|
||||
if (opt) {
|
||||
@@ -2400,6 +2407,7 @@ unsigned long __init prom_init(unsigned
|
||||
/*
|
||||
* Initialize display devices
|
||||
*/
|
||||
+ if (RELOC(prom_no_display) == 0)
|
||||
prom_check_displays();
|
||||
|
||||
#ifdef CONFIG_PPC64
|
||||
@@ -0,0 +1,83 @@
|
||||
Subject: ps3: Add passthru support for non-audio streams
|
||||
From: Takashi Iwai tiwai@suse.de Mon Oct 20 08:05:10 2008 +0200
|
||||
Date: Mon Oct 20 08:05:15 2008 +0200:
|
||||
Git: 64931a4be03dbc49bd50d10d211592cf98b523bb
|
||||
|
||||
Add support for the channel status bit setting so that non-PCM
|
||||
data stream can be sent (i.e. pass-through) via SPDIF/HDMI.
|
||||
|
||||
Signed-off-by: Masakazu Mokuno <mokuno@sm.sony.co.jp>
|
||||
Acked-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
|
||||
Signed-off-by: Takashi Iwai <tiwai@suse.de>
|
||||
Signed-off-by: Stefan Assmann <sassmann@suse.de>
|
||||
|
||||
diff --git a/arch/powerpc/include/asm/ps3av.h b/arch/powerpc/include/asm/ps3av.h
|
||||
index d30bde2..5aa22cf 100644
|
||||
--- a/arch/powerpc/include/asm/ps3av.h
|
||||
+++ b/arch/powerpc/include/asm/ps3av.h
|
||||
@@ -678,6 +678,8 @@ struct ps3av_pkt_avb_param {
|
||||
u8 buf[PS3AV_PKT_AVB_PARAM_MAX_BUF_SIZE];
|
||||
};
|
||||
|
||||
+/* channel status */
|
||||
+extern u8 ps3av_mode_cs_info[];
|
||||
|
||||
/** command status **/
|
||||
#define PS3AV_STATUS_SUCCESS 0x0000 /* success */
|
||||
diff --git a/drivers/ps3/ps3av_cmd.c b/drivers/ps3/ps3av_cmd.c
|
||||
index 7f880c2..11eb503 100644
|
||||
--- a/drivers/ps3/ps3av_cmd.c
|
||||
+++ b/drivers/ps3/ps3av_cmd.c
|
||||
@@ -660,9 +660,10 @@ u32 ps3av_cmd_set_av_audio_param(void *p, u32 port,
|
||||
}
|
||||
|
||||
/* default cs val */
|
||||
-static const u8 ps3av_mode_cs_info[] = {
|
||||
+u8 ps3av_mode_cs_info[] = {
|
||||
0x00, 0x09, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00
|
||||
};
|
||||
+EXPORT_SYMBOL_GPL(ps3av_mode_cs_info);
|
||||
|
||||
#define CS_44 0x00
|
||||
#define CS_48 0x02
|
||||
@@ -677,7 +678,7 @@ void ps3av_cmd_set_audio_mode(struct ps3av_pkt_audio_mode *audio, u32 avport,
|
||||
u32 ch, u32 fs, u32 word_bits, u32 format,
|
||||
u32 source)
|
||||
{
|
||||
- int spdif_through, spdif_bitstream;
|
||||
+ int spdif_through;
|
||||
int i;
|
||||
|
||||
if (!(ch | fs | format | word_bits | source)) {
|
||||
@@ -687,7 +688,6 @@ void ps3av_cmd_set_audio_mode(struct ps3av_pkt_audio_mode *audio, u32 avport,
|
||||
format = PS3AV_CMD_AUDIO_FORMAT_PCM;
|
||||
source = PS3AV_CMD_AUDIO_SOURCE_SERIAL;
|
||||
}
|
||||
- spdif_through = spdif_bitstream = 0; /* XXX not supported */
|
||||
|
||||
/* audio mode */
|
||||
memset(audio, 0, sizeof(*audio));
|
||||
@@ -777,16 +777,17 @@ void ps3av_cmd_set_audio_mode(struct ps3av_pkt_audio_mode *audio, u32 avport,
|
||||
break;
|
||||
}
|
||||
|
||||
+ /* non-audio bit */
|
||||
+ spdif_through = audio->audio_cs_info[0] & 0x02;
|
||||
+
|
||||
/* pass through setting */
|
||||
if (spdif_through &&
|
||||
(avport == PS3AV_CMD_AVPORT_SPDIF_0 ||
|
||||
- avport == PS3AV_CMD_AVPORT_SPDIF_1)) {
|
||||
+ avport == PS3AV_CMD_AVPORT_SPDIF_1 ||
|
||||
+ avport == PS3AV_CMD_AVPORT_HDMI_0 ||
|
||||
+ avport == PS3AV_CMD_AVPORT_HDMI_1)) {
|
||||
audio->audio_word_bits = PS3AV_CMD_AUDIO_WORD_BITS_16;
|
||||
- audio->audio_source = PS3AV_CMD_AUDIO_SOURCE_SPDIF;
|
||||
- if (spdif_bitstream) {
|
||||
- audio->audio_format = PS3AV_CMD_AUDIO_FORMAT_BITSTREAM;
|
||||
- audio->audio_cs_info[0] |= CS_BIT;
|
||||
- }
|
||||
+ audio->audio_format = PS3AV_CMD_AUDIO_FORMAT_BITSTREAM;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
Subject: ps3: Add ps3av_audio_mute_analog()
|
||||
From: Masakazu Mokuno mokuno@sm.sony.co.jp Mon Oct 20 08:03:33 2008 +0200
|
||||
Date: Mon Oct 20 08:04:59 2008 +0200:
|
||||
Git: 756ba83ee370fbf62643777e7ba4a4f05932f6fb
|
||||
|
||||
Add support for muting the analog output so that it does not
|
||||
play noises while non-PCM data is played.
|
||||
|
||||
Signed-off-by: Masakazu Mokuno <mokuno@sm.sony.co.jp>
|
||||
Signed-off-by: Takashi Iwai <tiwai@suse.de>
|
||||
Signed-off-by: Stefan Assmann <sassmann@suse.de>
|
||||
|
||||
diff --git a/arch/powerpc/include/asm/ps3av.h b/arch/powerpc/include/asm/ps3av.h
|
||||
index fda9871..d30bde2 100644
|
||||
--- a/arch/powerpc/include/asm/ps3av.h
|
||||
+++ b/arch/powerpc/include/asm/ps3av.h
|
||||
@@ -735,6 +735,7 @@ extern int ps3av_get_mode(void);
|
||||
extern int ps3av_video_mode2res(u32, u32 *, u32 *);
|
||||
extern int ps3av_video_mute(int);
|
||||
extern int ps3av_audio_mute(int);
|
||||
+extern int ps3av_audio_mute_analog(int);
|
||||
extern int ps3av_dev_open(void);
|
||||
extern int ps3av_dev_close(void);
|
||||
extern void ps3av_register_flip_ctl(void (*flip_ctl)(int on, void *data),
|
||||
diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c
|
||||
index 6f2f90e..06848b2 100644
|
||||
--- a/drivers/ps3/ps3av.c
|
||||
+++ b/drivers/ps3/ps3av.c
|
||||
@@ -915,6 +915,22 @@ int ps3av_video_mute(int mute)
|
||||
|
||||
EXPORT_SYMBOL_GPL(ps3av_video_mute);
|
||||
|
||||
+/* mute analog output only */
|
||||
+int ps3av_audio_mute_analog(int mute)
|
||||
+{
|
||||
+ int i, res;
|
||||
+
|
||||
+ for (i = 0; i < ps3av->av_hw_conf.num_of_avmulti; i++) {
|
||||
+ res = ps3av_cmd_av_audio_mute(1,
|
||||
+ &ps3av->av_port[i + ps3av->av_hw_conf.num_of_hdmi],
|
||||
+ mute);
|
||||
+ if (res < 0)
|
||||
+ return -1;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(ps3av_audio_mute_analog);
|
||||
+
|
||||
int ps3av_audio_mute(int mute)
|
||||
{
|
||||
return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON
|
||||
@@ -0,0 +1,166 @@
|
||||
Subject: ALSA: ps3: Add support for SPDIF/HDMI passthru
|
||||
From: Takashi Iwai tiwai@suse.de Mon Oct 20 08:06:39 2008 +0200
|
||||
Date: Mon Oct 20 08:06:39 2008 +0200:
|
||||
Git: 1ee2a322b058f6399dc900603f9ebb392037ff77
|
||||
|
||||
Add support for SPDIF/HDMI pass-through support of PS3 audio driver.
|
||||
|
||||
Signed-off-by: Masakazu Mokuno <mokuno@sm.sony.co.jp>
|
||||
Signed-off-by: Takashi Iwai <tiwai@suse.de>
|
||||
Signed-off-by: Stefan Assmann <sassmann@suse.de>
|
||||
|
||||
diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
|
||||
index 20d0e32..8f9e385 100644
|
||||
--- a/sound/ppc/snd_ps3.c
|
||||
+++ b/sound/ppc/snd_ps3.c
|
||||
@@ -666,6 +666,7 @@ static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card)
|
||||
card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
|
||||
card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM;
|
||||
card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL;
|
||||
+ memcpy(card->avs.avs_cs_info, ps3av_mode_cs_info, 8);
|
||||
|
||||
ret = snd_ps3_change_avsetting(card);
|
||||
|
||||
@@ -685,6 +686,7 @@ static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
|
||||
struct snd_ps3_avsetting_info avs;
|
||||
+ int ret;
|
||||
|
||||
avs = card->avs;
|
||||
|
||||
@@ -729,19 +731,92 @@ static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream)
|
||||
return 1;
|
||||
}
|
||||
|
||||
- if ((card->avs.avs_audio_width != avs.avs_audio_width) ||
|
||||
- (card->avs.avs_audio_rate != avs.avs_audio_rate)) {
|
||||
- card->avs = avs;
|
||||
- snd_ps3_change_avsetting(card);
|
||||
+ memcpy(avs.avs_cs_info, ps3av_mode_cs_info, 8);
|
||||
|
||||
+ if (memcmp(&card->avs, &avs, sizeof(avs))) {
|
||||
pr_debug("%s: after freq=%d width=%d\n", __func__,
|
||||
card->avs.avs_audio_rate, card->avs.avs_audio_width);
|
||||
|
||||
- return 0;
|
||||
+ card->avs = avs;
|
||||
+ snd_ps3_change_avsetting(card);
|
||||
+ ret = 0;
|
||||
} else
|
||||
+ ret = 1;
|
||||
+
|
||||
+ /* check CS non-audio bit and mute accordingly */
|
||||
+ if (avs.avs_cs_info[0] & 0x02)
|
||||
+ ps3av_audio_mute_analog(1); /* mute if non-audio */
|
||||
+ else
|
||||
+ ps3av_audio_mute_analog(0);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * SPDIF status bits controls
|
||||
+ */
|
||||
+static int snd_ps3_spdif_mask_info(struct snd_kcontrol *kcontrol,
|
||||
+ struct snd_ctl_elem_info *uinfo)
|
||||
+{
|
||||
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
+ uinfo->count = 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* FIXME: ps3av_set_audio_mode() assumes only consumer mode */
|
||||
+static int snd_ps3_spdif_cmask_get(struct snd_kcontrol *kcontrol,
|
||||
+ struct snd_ctl_elem_value *ucontrol)
|
||||
+{
|
||||
+ memset(ucontrol->value.iec958.status, 0xff, 8);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int snd_ps3_spdif_pmask_get(struct snd_kcontrol *kcontrol,
|
||||
+ struct snd_ctl_elem_value *ucontrol)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int snd_ps3_spdif_default_get(struct snd_kcontrol *kcontrol,
|
||||
+ struct snd_ctl_elem_value *ucontrol)
|
||||
+{
|
||||
+ memcpy(ucontrol->value.iec958.status, ps3av_mode_cs_info, 8);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int snd_ps3_spdif_default_put(struct snd_kcontrol *kcontrol,
|
||||
+ struct snd_ctl_elem_value *ucontrol)
|
||||
+{
|
||||
+ if (memcmp(ps3av_mode_cs_info, ucontrol->value.iec958.status, 8)) {
|
||||
+ memcpy(ps3av_mode_cs_info, ucontrol->value.iec958.status, 8);
|
||||
return 1;
|
||||
+ }
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
+static struct snd_kcontrol_new spdif_ctls[] = {
|
||||
+ {
|
||||
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
|
||||
+ .info = snd_ps3_spdif_mask_info,
|
||||
+ .get = snd_ps3_spdif_cmask_get,
|
||||
+ },
|
||||
+ {
|
||||
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
|
||||
+ .info = snd_ps3_spdif_mask_info,
|
||||
+ .get = snd_ps3_spdif_pmask_get,
|
||||
+ },
|
||||
+ {
|
||||
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
|
||||
+ .info = snd_ps3_spdif_mask_info,
|
||||
+ .get = snd_ps3_spdif_default_get,
|
||||
+ .put = snd_ps3_spdif_default_put,
|
||||
+ },
|
||||
+};
|
||||
|
||||
|
||||
static int snd_ps3_map_mmio(void)
|
||||
@@ -842,7 +917,7 @@ static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start)
|
||||
|
||||
static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
|
||||
{
|
||||
- int ret;
|
||||
+ int i, ret;
|
||||
u64 lpar_addr, lpar_size;
|
||||
|
||||
BUG_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1));
|
||||
@@ -903,6 +978,15 @@ static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
|
||||
strcpy(the_card.card->driver, "PS3");
|
||||
strcpy(the_card.card->shortname, "PS3");
|
||||
strcpy(the_card.card->longname, "PS3 sound");
|
||||
+
|
||||
+ /* create control elements */
|
||||
+ for (i = 0; i < ARRAY_SIZE(spdif_ctls); i++) {
|
||||
+ ret = snd_ctl_add(the_card.card,
|
||||
+ snd_ctl_new1(&spdif_ctls[i], &the_card));
|
||||
+ if (ret < 0)
|
||||
+ goto clean_card;
|
||||
+ }
|
||||
+
|
||||
/* create PCM devices instance */
|
||||
/* NOTE:this driver works assuming pcm:substream = 1:1 */
|
||||
ret = snd_pcm_new(the_card.card,
|
||||
diff --git a/sound/ppc/snd_ps3.h b/sound/ppc/snd_ps3.h
|
||||
index 4b7e6fb..326fb29 100644
|
||||
--- a/sound/ppc/snd_ps3.h
|
||||
+++ b/sound/ppc/snd_ps3.h
|
||||
@@ -51,6 +51,7 @@ struct snd_ps3_avsetting_info {
|
||||
uint32_t avs_audio_width;
|
||||
uint32_t avs_audio_format; /* fixed */
|
||||
uint32_t avs_audio_source; /* fixed */
|
||||
+ unsigned char avs_cs_info[8];
|
||||
};
|
||||
/*
|
||||
* PS3 audio 'card' instance
|
||||
@@ -0,0 +1,164 @@
|
||||
From: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
|
||||
Subject: Introduce ps3_gpu_mutex
|
||||
|
||||
Introduce ps3_gpu_mutex to synchronizes GPU-related operations, like:
|
||||
- invoking the L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT command using the
|
||||
lv1_gpu_context_attribute() hypervisor call,
|
||||
- handling the PS3AV_CID_AVB_PARAM packet in the PS3 A/V Settings driver.
|
||||
|
||||
Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
|
||||
Signed-off-by: Stefan Assmann <sassmann@suse.de>
|
||||
---
|
||||
arch/powerpc/include/asm/ps3.h | 3 +++
|
||||
arch/powerpc/include/asm/ps3av.h | 3 ---
|
||||
arch/powerpc/platforms/ps3/setup.c | 4 ++++
|
||||
drivers/ps3/ps3av.c | 20 --------------------
|
||||
drivers/ps3/ps3av_cmd.c | 4 ++--
|
||||
drivers/video/ps3fb.c | 17 +++++------------
|
||||
6 files changed, 14 insertions(+), 37 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/include/asm/ps3.h
|
||||
+++ b/arch/powerpc/include/asm/ps3.h
|
||||
@@ -516,4 +516,7 @@ void ps3_sync_irq(int node);
|
||||
u32 ps3_get_hw_thread_id(int cpu);
|
||||
u64 ps3_get_spe_id(void *arg);
|
||||
|
||||
+/* mutex synchronizing GPU accesses and video mode changes */
|
||||
+extern struct mutex ps3_gpu_mutex;
|
||||
+
|
||||
#endif
|
||||
--- a/arch/powerpc/include/asm/ps3av.h
|
||||
+++ b/arch/powerpc/include/asm/ps3av.h
|
||||
@@ -740,8 +740,5 @@ extern int ps3av_audio_mute(int);
|
||||
extern int ps3av_audio_mute_analog(int);
|
||||
extern int ps3av_dev_open(void);
|
||||
extern int ps3av_dev_close(void);
|
||||
-extern void ps3av_register_flip_ctl(void (*flip_ctl)(int on, void *data),
|
||||
- void *flip_data);
|
||||
-extern void ps3av_flip_ctl(int on);
|
||||
|
||||
#endif /* _ASM_POWERPC_PS3AV_H_ */
|
||||
--- a/arch/powerpc/platforms/ps3/setup.c
|
||||
+++ b/arch/powerpc/platforms/ps3/setup.c
|
||||
@@ -42,6 +42,10 @@
|
||||
#define DBG pr_debug
|
||||
#endif
|
||||
|
||||
+/* mutex synchronizing GPU accesses and video mode changes */
|
||||
+DEFINE_MUTEX(ps3_gpu_mutex);
|
||||
+EXPORT_SYMBOL_GPL(ps3_gpu_mutex);
|
||||
+
|
||||
#if !defined(CONFIG_SMP)
|
||||
static void smp_send_stop(void) {}
|
||||
#endif
|
||||
--- a/drivers/ps3/ps3av.c
|
||||
+++ b/drivers/ps3/ps3av.c
|
||||
@@ -59,8 +59,6 @@ static struct ps3av {
|
||||
struct ps3av_reply_hdr reply_hdr;
|
||||
u8 raw[PS3AV_BUF_SIZE];
|
||||
} recv_buf;
|
||||
- void (*flip_ctl)(int on, void *data);
|
||||
- void *flip_data;
|
||||
} *ps3av;
|
||||
|
||||
/* color space */
|
||||
@@ -939,24 +937,6 @@ int ps3av_audio_mute(int mute)
|
||||
|
||||
EXPORT_SYMBOL_GPL(ps3av_audio_mute);
|
||||
|
||||
-void ps3av_register_flip_ctl(void (*flip_ctl)(int on, void *data),
|
||||
- void *flip_data)
|
||||
-{
|
||||
- mutex_lock(&ps3av->mutex);
|
||||
- ps3av->flip_ctl = flip_ctl;
|
||||
- ps3av->flip_data = flip_data;
|
||||
- mutex_unlock(&ps3av->mutex);
|
||||
-}
|
||||
-EXPORT_SYMBOL_GPL(ps3av_register_flip_ctl);
|
||||
-
|
||||
-void ps3av_flip_ctl(int on)
|
||||
-{
|
||||
- mutex_lock(&ps3av->mutex);
|
||||
- if (ps3av->flip_ctl)
|
||||
- ps3av->flip_ctl(on, ps3av->flip_data);
|
||||
- mutex_unlock(&ps3av->mutex);
|
||||
-}
|
||||
-
|
||||
static int ps3av_probe(struct ps3_system_bus_device *dev)
|
||||
{
|
||||
int res;
|
||||
--- a/drivers/ps3/ps3av_cmd.c
|
||||
+++ b/drivers/ps3/ps3av_cmd.c
|
||||
@@ -864,7 +864,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt
|
||||
{
|
||||
int res;
|
||||
|
||||
- ps3av_flip_ctl(0); /* flip off */
|
||||
+ mutex_lock(&ps3_gpu_mutex);
|
||||
|
||||
/* avb packet */
|
||||
res = ps3av_do_pkt(PS3AV_CID_AVB_PARAM, send_len, sizeof(*avb),
|
||||
@@ -878,7 +878,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt
|
||||
res);
|
||||
|
||||
out:
|
||||
- ps3av_flip_ctl(1); /* flip on */
|
||||
+ mutex_unlock(&ps3_gpu_mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
--- a/drivers/video/ps3fb.c
|
||||
+++ b/drivers/video/ps3fb.c
|
||||
@@ -460,12 +460,16 @@ static void ps3fb_sync_image(struct devi
|
||||
line_length |= (u64)src_line_length << 32;
|
||||
|
||||
src_offset += GPU_FB_START;
|
||||
+
|
||||
+ mutex_lock(&ps3_gpu_mutex);
|
||||
status = lv1_gpu_context_attribute(ps3fb.context_handle,
|
||||
L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
|
||||
dst_offset, GPU_IOIF + src_offset,
|
||||
L1GPU_FB_BLIT_WAIT_FOR_COMPLETION |
|
||||
(width << 16) | height,
|
||||
line_length);
|
||||
+ mutex_unlock(&ps3_gpu_mutex);
|
||||
+
|
||||
if (status)
|
||||
dev_err(dev,
|
||||
"%s: lv1_gpu_context_attribute FB_BLIT failed: %d\n",
|
||||
@@ -784,15 +788,6 @@ static int ps3fb_wait_for_vsync(u32 crtc
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void ps3fb_flip_ctl(int on, void *data)
|
||||
-{
|
||||
- struct ps3fb_priv *priv = data;
|
||||
- if (on)
|
||||
- atomic_dec_if_positive(&priv->ext_flip);
|
||||
- else
|
||||
- atomic_inc(&priv->ext_flip);
|
||||
-}
|
||||
-
|
||||
|
||||
/*
|
||||
* ioctl
|
||||
@@ -1228,7 +1223,6 @@ static int __devinit ps3fb_probe(struct
|
||||
}
|
||||
|
||||
ps3fb.task = task;
|
||||
- ps3av_register_flip_ctl(ps3fb_flip_ctl, &ps3fb);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1258,10 +1252,9 @@ static int ps3fb_shutdown(struct ps3_sys
|
||||
|
||||
dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);
|
||||
|
||||
- ps3fb_flip_ctl(0, &ps3fb); /* flip off */
|
||||
+ atomic_inc(&ps3fb.ext_flip); /* flip off */
|
||||
ps3fb.dinfo->irq.mask = 0;
|
||||
|
||||
- ps3av_register_flip_ctl(NULL, NULL);
|
||||
if (ps3fb.task) {
|
||||
struct task_struct *task = ps3fb.task;
|
||||
ps3fb.task = NULL;
|
||||
@@ -0,0 +1,912 @@
|
||||
From: Jim Paris <jim@jtan.com>
|
||||
Subject: Add ps3vram driver, which exposes unused video RAM on the PS3 as a MTD
|
||||
|
||||
Add ps3vram driver, which exposes unused video RAM on the PS3 as a MTD
|
||||
device suitable for storage or swap. Fast data transfer is achieved
|
||||
using a local cache in system RAM and DMA transfers via the GPU.
|
||||
|
||||
Signed-off-by: Vivien Chappelier <vivien.chappelier@free.fr>
|
||||
CC: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
|
||||
CC: Geoff Levand <geoffrey.levand@am.sony.com>
|
||||
Signed-off-by: Jim Paris <jim@jtan.com>
|
||||
Signed-off-by: Stefan Assmann <sassmann@suse.de>
|
||||
---
|
||||
This version has been updated to work with PS3 firmware 2.50, and
|
||||
to use ps3_gpu_mutex.
|
||||
|
||||
Please consider for 2.6.29.
|
||||
|
||||
MAINTAINERS | 6
|
||||
arch/powerpc/include/asm/ps3.h | 1
|
||||
arch/powerpc/platforms/ps3/device-init.c | 37 +
|
||||
drivers/mtd/devices/Kconfig | 7
|
||||
drivers/mtd/devices/Makefile | 1
|
||||
MAINTAINERS | 6
|
||||
arch/powerpc/include/asm/ps3.h | 1
|
||||
arch/powerpc/platforms/ps3/device-init.c | 37 +
|
||||
drivers/mtd/devices/Kconfig | 7
|
||||
drivers/mtd/devices/Makefile | 1
|
||||
drivers/mtd/devices/ps3vram.c | 776 +++++++++++++++++++++++++++++++
|
||||
6 files changed, 828 insertions(+)
|
||||
create mode 100644 drivers/mtd/devices/ps3vram.c
|
||||
|
||||
--- a/arch/powerpc/include/asm/ps3.h
|
||||
+++ b/arch/powerpc/include/asm/ps3.h
|
||||
@@ -340,6 +340,7 @@ enum ps3_system_bus_device_type {
|
||||
enum ps3_match_sub_id {
|
||||
/* for PS3_MATCH_ID_GRAPHICS */
|
||||
PS3_MATCH_SUB_ID_FB = 1,
|
||||
+ PS3_MATCH_SUB_ID_RAMDISK = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
--- a/arch/powerpc/platforms/ps3/device-init.c
|
||||
+++ b/arch/powerpc/platforms/ps3/device-init.c
|
||||
@@ -499,6 +499,41 @@ static int __init ps3_register_graphics_
|
||||
return result;
|
||||
}
|
||||
|
||||
+static int __init ps3_register_ramdisk_device(void)
|
||||
+{
|
||||
+ int result;
|
||||
+ struct layout {
|
||||
+ struct ps3_system_bus_device dev;
|
||||
+ } *p;
|
||||
+
|
||||
+ pr_debug(" -> %s:%d\n", __func__, __LINE__);
|
||||
+
|
||||
+ p = kzalloc(sizeof(struct layout), GFP_KERNEL);
|
||||
+
|
||||
+ if (!p)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ p->dev.match_id = PS3_MATCH_ID_GRAPHICS;
|
||||
+ p->dev.match_sub_id = PS3_MATCH_SUB_ID_RAMDISK;
|
||||
+ p->dev.dev_type = PS3_DEVICE_TYPE_IOC0;
|
||||
+
|
||||
+ result = ps3_system_bus_device_register(&p->dev);
|
||||
+
|
||||
+ if (result) {
|
||||
+ pr_debug("%s:%d ps3_system_bus_device_register failed\n",
|
||||
+ __func__, __LINE__);
|
||||
+ goto fail_device_register;
|
||||
+ }
|
||||
+
|
||||
+ pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
+ return 0;
|
||||
+
|
||||
+fail_device_register:
|
||||
+ kfree(p);
|
||||
+ pr_debug(" <- %s:%d failed\n", __func__, __LINE__);
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* ps3_setup_dynamic_device - Setup a dynamic device from the repository
|
||||
*/
|
||||
@@ -927,6 +962,8 @@ static int __init ps3_register_devices(v
|
||||
|
||||
ps3_register_lpm_devices();
|
||||
|
||||
+ ps3_register_ramdisk_device();
|
||||
+
|
||||
pr_debug(" <- %s:%d\n", __func__, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
--- a/drivers/mtd/devices/Kconfig
|
||||
+++ b/drivers/mtd/devices/Kconfig
|
||||
@@ -99,6 +99,13 @@ config MTD_PHRAM
|
||||
doesn't have access to, memory beyond the mem=xxx limit, nvram,
|
||||
memory on the video card, etc...
|
||||
|
||||
+config MTD_PS3VRAM
|
||||
+ tristate "PS3 video RAM"
|
||||
+ depends on FB_PS3
|
||||
+ help
|
||||
+ This driver allows you to use excess PS3 video RAM as volatile
|
||||
+ storage or system swap.
|
||||
+
|
||||
config MTD_LART
|
||||
tristate "28F160xx flash driver for LART"
|
||||
depends on SA1100_LART
|
||||
--- a/drivers/mtd/devices/Makefile
|
||||
+++ b/drivers/mtd/devices/Makefile
|
||||
@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o
|
||||
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
|
||||
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
|
||||
obj-$(CONFIG_MTD_M25P80) += m25p80.o
|
||||
+obj-$(CONFIG_MTD_PS3VRAM) += ps3vram.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/mtd/devices/ps3vram.c
|
||||
@@ -0,0 +1,776 @@
|
||||
+/**
|
||||
+ * ps3vram - Use extra PS3 video ram as MTD block device.
|
||||
+ *
|
||||
+ * Copyright (c) 2007-2008 Jim Paris <jim@jtan.com>
|
||||
+ * Added support RSX DMA Vivien Chappelier <vivien.chappelier@free.fr>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/list.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/moduleparam.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/version.h>
|
||||
+#include <linux/gfp.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/mtd/mtd.h>
|
||||
+
|
||||
+#include <asm/lv1call.h>
|
||||
+#include <asm/ps3.h>
|
||||
+
|
||||
+#define DEVICE_NAME "ps3vram"
|
||||
+
|
||||
+#define XDR_BUF_SIZE (2 * 1024 * 1024) /* XDR buffer (must be 1MB aligned) */
|
||||
+#define XDR_IOIF 0x0c000000
|
||||
+
|
||||
+#define FIFO_BASE XDR_IOIF
|
||||
+#define FIFO_SIZE (64 * 1024)
|
||||
+
|
||||
+#define DMA_PAGE_SIZE (4 * 1024)
|
||||
+
|
||||
+#define CACHE_PAGE_SIZE (256 * 1024)
|
||||
+#define CACHE_PAGE_COUNT ((XDR_BUF_SIZE - FIFO_SIZE) / CACHE_PAGE_SIZE)
|
||||
+
|
||||
+#define CACHE_OFFSET CACHE_PAGE_SIZE
|
||||
+#define FIFO_OFFSET 0
|
||||
+
|
||||
+#define CTRL_PUT 0x10
|
||||
+#define CTRL_GET 0x11
|
||||
+#define CTRL_TOP 0x15
|
||||
+
|
||||
+#define UPLOAD_SUBCH 1
|
||||
+#define DOWNLOAD_SUBCH 2
|
||||
+
|
||||
+#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c
|
||||
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104
|
||||
+
|
||||
+#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601
|
||||
+
|
||||
+struct mtd_info ps3vram_mtd;
|
||||
+
|
||||
+#define CACHE_PAGE_PRESENT 1
|
||||
+#define CACHE_PAGE_DIRTY 2
|
||||
+
|
||||
+#define dbg(fmt, args...) \
|
||||
+ pr_debug("%s:%d " fmt "\n", __func__, __LINE__, ## args)
|
||||
+
|
||||
+struct ps3vram_tag {
|
||||
+ unsigned int address;
|
||||
+ unsigned int flags;
|
||||
+};
|
||||
+
|
||||
+struct ps3vram_cache {
|
||||
+ unsigned int page_count;
|
||||
+ unsigned int page_size;
|
||||
+ struct ps3vram_tag *tags;
|
||||
+};
|
||||
+
|
||||
+struct ps3vram_priv {
|
||||
+ uint64_t memory_handle;
|
||||
+ uint64_t context_handle;
|
||||
+ uint8_t *base;
|
||||
+ uint32_t *ctrl;
|
||||
+ uint32_t *reports;
|
||||
+ uint8_t *xdr_buf;
|
||||
+
|
||||
+ uint32_t *fifo_base;
|
||||
+ uint32_t *fifo_ptr;
|
||||
+
|
||||
+ struct ps3vram_cache cache;
|
||||
+
|
||||
+ /* Used to serialize cache/DMA operations */
|
||||
+ struct mutex lock;
|
||||
+};
|
||||
+
|
||||
+#define DMA_NOTIFIER_HANDLE_BASE 0x66604200 /* first DMA notifier handle */
|
||||
+#define DMA_NOTIFIER_OFFSET_BASE 0x1000 /* first DMA notifier offset */
|
||||
+#define DMA_NOTIFIER_SIZE 0x40
|
||||
+
|
||||
+#define NUM_NOTIFIERS 16
|
||||
+
|
||||
+#define NOTIFIER 7 /* notifier used for completion report */
|
||||
+
|
||||
+/* A trailing '-' means to subtract off ps3fb_videomemory.size */
|
||||
+char *size = "256M-";
|
||||
+module_param(size, charp, 0);
|
||||
+MODULE_PARM_DESC(size, "memory size");
|
||||
+
|
||||
+static inline uint32_t *ps3vram_get_notifier(uint32_t *reports, int notifier)
|
||||
+{
|
||||
+ return (void *) reports +
|
||||
+ DMA_NOTIFIER_OFFSET_BASE +
|
||||
+ DMA_NOTIFIER_SIZE * notifier;
|
||||
+}
|
||||
+
|
||||
+static void ps3vram_notifier_reset(struct mtd_info *mtd)
|
||||
+{
|
||||
+ int i;
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+ uint32_t *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
|
||||
+ for (i = 0; i < 4; i++)
|
||||
+ notify[i] = 0xffffffff;
|
||||
+}
|
||||
+
|
||||
+static int ps3vram_notifier_wait(struct mtd_info *mtd, int timeout_ms)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+ uint32_t *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
|
||||
+
|
||||
+ timeout_ms *= 1000;
|
||||
+
|
||||
+ do {
|
||||
+ if (notify[3] == 0)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (timeout_ms)
|
||||
+ udelay(1);
|
||||
+ } while (timeout_ms--);
|
||||
+
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
+static void ps3vram_dump_ring(struct mtd_info *mtd)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+ uint32_t *fifo;
|
||||
+
|
||||
+ pr_info("PUT = %08x GET = %08x\n", priv->ctrl[CTRL_PUT],
|
||||
+ priv->ctrl[CTRL_GET]);
|
||||
+ for (fifo = priv->fifo_base; fifo < priv->fifo_ptr; fifo++)
|
||||
+ pr_info("%p: %08x\n", fifo, *fifo);
|
||||
+}
|
||||
+
|
||||
+static void ps3vram_dump_reports(struct mtd_info *mtd)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < NUM_NOTIFIERS; i++) {
|
||||
+ uint32_t *n = ps3vram_get_notifier(priv->reports, i);
|
||||
+ pr_info("%p: %08x\n", n, *n);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void ps3vram_init_ring(struct mtd_info *mtd)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+
|
||||
+ priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
|
||||
+ priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET;
|
||||
+}
|
||||
+
|
||||
+static int ps3vram_wait_ring(struct mtd_info *mtd, int timeout)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+
|
||||
+ /* wait until setup commands are processed */
|
||||
+ timeout *= 1000;
|
||||
+ while (--timeout) {
|
||||
+ if (priv->ctrl[CTRL_PUT] == priv->ctrl[CTRL_GET])
|
||||
+ break;
|
||||
+ udelay(1);
|
||||
+ }
|
||||
+ if (timeout == 0) {
|
||||
+ pr_err("FIFO timeout (%08x/%08x/%08x)\n", priv->ctrl[CTRL_PUT],
|
||||
+ priv->ctrl[CTRL_GET], priv->ctrl[CTRL_TOP]);
|
||||
+ return -ETIMEDOUT;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static inline void ps3vram_out_ring(struct ps3vram_priv *priv, uint32_t data)
|
||||
+{
|
||||
+ *(priv->fifo_ptr)++ = data;
|
||||
+}
|
||||
+
|
||||
+static inline void ps3vram_begin_ring(struct ps3vram_priv *priv, uint32_t chan,
|
||||
+ uint32_t tag, uint32_t size)
|
||||
+{
|
||||
+ ps3vram_out_ring(priv, (size << 18) | (chan << 13) | tag);
|
||||
+}
|
||||
+
|
||||
+static void ps3vram_rewind_ring(struct mtd_info *mtd)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+ u64 status;
|
||||
+
|
||||
+ ps3vram_out_ring(priv, 0x20000000 | (FIFO_BASE + FIFO_OFFSET));
|
||||
+
|
||||
+ priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET;
|
||||
+
|
||||
+ /* asking the HV for a blit will kick the fifo */
|
||||
+ status = lv1_gpu_context_attribute(priv->context_handle,
|
||||
+ L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
|
||||
+ 0, 0, 0, 0);
|
||||
+ if (status)
|
||||
+ pr_err("ps3vram: lv1_gpu_context_attribute FB_BLIT failed\n");
|
||||
+
|
||||
+ priv->fifo_ptr = priv->fifo_base;
|
||||
+}
|
||||
+
|
||||
+static void ps3vram_fire_ring(struct mtd_info *mtd)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+ u64 status;
|
||||
+
|
||||
+ mutex_lock(&ps3_gpu_mutex);
|
||||
+
|
||||
+ priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET +
|
||||
+ (priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t);
|
||||
+
|
||||
+ /* asking the HV for a blit will kick the fifo */
|
||||
+ status = lv1_gpu_context_attribute(priv->context_handle,
|
||||
+ L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
|
||||
+ 0, 0, 0, 0);
|
||||
+ if (status)
|
||||
+ pr_err("ps3vram: lv1_gpu_context_attribute FB_BLIT failed\n");
|
||||
+
|
||||
+ if ((priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t) >
|
||||
+ FIFO_SIZE - 1024) {
|
||||
+ dbg("fifo full, rewinding");
|
||||
+ ps3vram_wait_ring(mtd, 200);
|
||||
+ ps3vram_rewind_ring(mtd);
|
||||
+ }
|
||||
+
|
||||
+ mutex_unlock(&ps3_gpu_mutex);
|
||||
+}
|
||||
+
|
||||
+static void ps3vram_bind(struct mtd_info *mtd)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+
|
||||
+ ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0, 1);
|
||||
+ ps3vram_out_ring(priv, 0x31337303);
|
||||
+ ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x180, 3);
|
||||
+ ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER);
|
||||
+ ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */
|
||||
+ ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */
|
||||
+
|
||||
+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0, 1);
|
||||
+ ps3vram_out_ring(priv, 0x3137c0de);
|
||||
+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x180, 3);
|
||||
+ ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER);
|
||||
+ ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */
|
||||
+ ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */
|
||||
+
|
||||
+ ps3vram_fire_ring(mtd);
|
||||
+}
|
||||
+
|
||||
+static int ps3vram_upload(struct mtd_info *mtd, unsigned int src_offset,
|
||||
+ unsigned int dst_offset, int len, int count)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+
|
||||
+ ps3vram_begin_ring(priv, UPLOAD_SUBCH,
|
||||
+ NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
|
||||
+ ps3vram_out_ring(priv, XDR_IOIF + src_offset);
|
||||
+ ps3vram_out_ring(priv, dst_offset);
|
||||
+ ps3vram_out_ring(priv, len);
|
||||
+ ps3vram_out_ring(priv, len);
|
||||
+ ps3vram_out_ring(priv, len);
|
||||
+ ps3vram_out_ring(priv, count);
|
||||
+ ps3vram_out_ring(priv, (1 << 8) | 1);
|
||||
+ ps3vram_out_ring(priv, 0);
|
||||
+
|
||||
+ ps3vram_notifier_reset(mtd);
|
||||
+ ps3vram_begin_ring(priv, UPLOAD_SUBCH,
|
||||
+ NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
|
||||
+ ps3vram_out_ring(priv, 0);
|
||||
+ ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x100, 1);
|
||||
+ ps3vram_out_ring(priv, 0);
|
||||
+ ps3vram_fire_ring(mtd);
|
||||
+ if (ps3vram_notifier_wait(mtd, 200) < 0) {
|
||||
+ pr_err("notifier timeout\n");
|
||||
+ ps3vram_dump_ring(mtd);
|
||||
+ ps3vram_dump_reports(mtd);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int ps3vram_download(struct mtd_info *mtd, unsigned int src_offset,
|
||||
+ unsigned int dst_offset, int len, int count)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+
|
||||
+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH,
|
||||
+ NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
|
||||
+ ps3vram_out_ring(priv, src_offset);
|
||||
+ ps3vram_out_ring(priv, XDR_IOIF + dst_offset);
|
||||
+ ps3vram_out_ring(priv, len);
|
||||
+ ps3vram_out_ring(priv, len);
|
||||
+ ps3vram_out_ring(priv, len);
|
||||
+ ps3vram_out_ring(priv, count);
|
||||
+ ps3vram_out_ring(priv, (1 << 8) | 1);
|
||||
+ ps3vram_out_ring(priv, 0);
|
||||
+
|
||||
+ ps3vram_notifier_reset(mtd);
|
||||
+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH,
|
||||
+ NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
|
||||
+ ps3vram_out_ring(priv, 0);
|
||||
+ ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x100, 1);
|
||||
+ ps3vram_out_ring(priv, 0);
|
||||
+ ps3vram_fire_ring(mtd);
|
||||
+ if (ps3vram_notifier_wait(mtd, 200) < 0) {
|
||||
+ pr_err("notifier timeout\n");
|
||||
+ ps3vram_dump_ring(mtd);
|
||||
+ ps3vram_dump_reports(mtd);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void ps3vram_cache_evict(struct mtd_info *mtd, int entry)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+ struct ps3vram_cache *cache = &priv->cache;
|
||||
+
|
||||
+ if (cache->tags[entry].flags & CACHE_PAGE_DIRTY) {
|
||||
+ dbg("flushing %d : 0x%08x", entry, cache->tags[entry].address);
|
||||
+ if (ps3vram_upload(mtd,
|
||||
+ CACHE_OFFSET + entry * cache->page_size,
|
||||
+ cache->tags[entry].address,
|
||||
+ DMA_PAGE_SIZE,
|
||||
+ cache->page_size / DMA_PAGE_SIZE) < 0) {
|
||||
+ pr_err("failed to upload from 0x%x to 0x%x size 0x%x\n",
|
||||
+ entry * cache->page_size,
|
||||
+ cache->tags[entry].address,
|
||||
+ cache->page_size);
|
||||
+ }
|
||||
+ cache->tags[entry].flags &= ~CACHE_PAGE_DIRTY;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void ps3vram_cache_load(struct mtd_info *mtd, int entry,
|
||||
+ unsigned int address)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+ struct ps3vram_cache *cache = &priv->cache;
|
||||
+
|
||||
+ dbg("fetching %d : 0x%08x", entry, address);
|
||||
+ if (ps3vram_download(mtd,
|
||||
+ address,
|
||||
+ CACHE_OFFSET + entry * cache->page_size,
|
||||
+ DMA_PAGE_SIZE,
|
||||
+ cache->page_size / DMA_PAGE_SIZE) < 0) {
|
||||
+ pr_err("failed to download from 0x%x to 0x%x size 0x%x\n",
|
||||
+ address,
|
||||
+ entry * cache->page_size,
|
||||
+ cache->page_size);
|
||||
+ }
|
||||
+
|
||||
+ cache->tags[entry].address = address;
|
||||
+ cache->tags[entry].flags |= CACHE_PAGE_PRESENT;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static void ps3vram_cache_flush(struct mtd_info *mtd)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+ struct ps3vram_cache *cache = &priv->cache;
|
||||
+ int i;
|
||||
+
|
||||
+ dbg("FLUSH");
|
||||
+ for (i = 0; i < cache->page_count; i++) {
|
||||
+ ps3vram_cache_evict(mtd, i);
|
||||
+ cache->tags[i].flags = 0;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static unsigned int ps3vram_cache_match(struct mtd_info *mtd, loff_t address)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+ struct ps3vram_cache *cache = &priv->cache;
|
||||
+ unsigned int base;
|
||||
+ unsigned int offset;
|
||||
+ int i;
|
||||
+ static int counter;
|
||||
+
|
||||
+ offset = (unsigned int) (address & (cache->page_size - 1));
|
||||
+ base = (unsigned int) (address - offset);
|
||||
+
|
||||
+ /* fully associative check */
|
||||
+ for (i = 0; i < cache->page_count; i++) {
|
||||
+ if ((cache->tags[i].flags & CACHE_PAGE_PRESENT) &&
|
||||
+ cache->tags[i].address == base) {
|
||||
+ dbg("found entry %d : 0x%08x",
|
||||
+ i, cache->tags[i].address);
|
||||
+ return i;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* choose a random entry */
|
||||
+ i = (jiffies + (counter++)) % cache->page_count;
|
||||
+ dbg("using cache entry %d", i);
|
||||
+
|
||||
+ ps3vram_cache_evict(mtd, i);
|
||||
+ ps3vram_cache_load(mtd, i, base);
|
||||
+
|
||||
+ return i;
|
||||
+}
|
||||
+
|
||||
+static int ps3vram_cache_init(struct mtd_info *mtd)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+
|
||||
+ pr_info("creating cache: %d entries, %d bytes pages\n",
|
||||
+ CACHE_PAGE_COUNT, CACHE_PAGE_SIZE);
|
||||
+
|
||||
+ priv->cache.page_count = CACHE_PAGE_COUNT;
|
||||
+ priv->cache.page_size = CACHE_PAGE_SIZE;
|
||||
+ priv->cache.tags = kzalloc(sizeof(struct ps3vram_tag) *
|
||||
+ CACHE_PAGE_COUNT, GFP_KERNEL);
|
||||
+ if (priv->cache.tags == NULL) {
|
||||
+ pr_err("could not allocate cache tags\n");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void ps3vram_cache_cleanup(struct mtd_info *mtd)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+
|
||||
+ ps3vram_cache_flush(mtd);
|
||||
+ kfree(priv->cache.tags);
|
||||
+}
|
||||
+
|
||||
+static int ps3vram_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+
|
||||
+ if (instr->addr + instr->len > mtd->size)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ mutex_lock(&priv->lock);
|
||||
+
|
||||
+ ps3vram_cache_flush(mtd);
|
||||
+
|
||||
+ /* Set bytes to 0xFF */
|
||||
+ memset(priv->base + instr->addr, 0xFF, instr->len);
|
||||
+
|
||||
+ mutex_unlock(&priv->lock);
|
||||
+
|
||||
+ instr->state = MTD_ERASE_DONE;
|
||||
+ mtd_erase_callback(instr);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static int ps3vram_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
+ size_t *retlen, u_char *buf)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+ unsigned int cached, count;
|
||||
+
|
||||
+ dbg("from = 0x%08x len = 0x%zx", (unsigned int) from, len);
|
||||
+
|
||||
+ if (from >= mtd->size)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (len > mtd->size - from)
|
||||
+ len = mtd->size - from;
|
||||
+
|
||||
+ /* Copy from vram to buf */
|
||||
+ count = len;
|
||||
+ while (count) {
|
||||
+ unsigned int offset, avail;
|
||||
+ unsigned int entry;
|
||||
+
|
||||
+ offset = (unsigned int) (from & (priv->cache.page_size - 1));
|
||||
+ avail = priv->cache.page_size - offset;
|
||||
+
|
||||
+ mutex_lock(&priv->lock);
|
||||
+
|
||||
+ entry = ps3vram_cache_match(mtd, from);
|
||||
+ cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
|
||||
+
|
||||
+ dbg("from=%08x cached=%08x offset=%08x avail=%08x count=%08x",
|
||||
+ (unsigned)from, cached, offset, avail, count);
|
||||
+
|
||||
+ if (avail > count)
|
||||
+ avail = count;
|
||||
+ memcpy(buf, priv->xdr_buf + cached, avail);
|
||||
+
|
||||
+ mutex_unlock(&priv->lock);
|
||||
+
|
||||
+ buf += avail;
|
||||
+ count -= avail;
|
||||
+ from += avail;
|
||||
+ }
|
||||
+
|
||||
+ *retlen = len;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int ps3vram_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
+ size_t *retlen, const u_char *buf)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv = mtd->priv;
|
||||
+ unsigned int cached, count;
|
||||
+
|
||||
+ if (to >= mtd->size)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (len > mtd->size - to)
|
||||
+ len = mtd->size - to;
|
||||
+
|
||||
+ /* Copy from buf to vram */
|
||||
+ count = len;
|
||||
+ while (count) {
|
||||
+ unsigned int offset, avail;
|
||||
+ unsigned int entry;
|
||||
+
|
||||
+ offset = (unsigned int) (to & (priv->cache.page_size - 1));
|
||||
+ avail = priv->cache.page_size - offset;
|
||||
+
|
||||
+ mutex_lock(&priv->lock);
|
||||
+
|
||||
+ entry = ps3vram_cache_match(mtd, to);
|
||||
+ cached = CACHE_OFFSET + entry * priv->cache.page_size + offset;
|
||||
+
|
||||
+ dbg("to=%08x cached=%08x offset=%08x avail=%08x count=%08x",
|
||||
+ (unsigned) to, cached, offset, avail, count);
|
||||
+
|
||||
+ if (avail > count)
|
||||
+ avail = count;
|
||||
+ memcpy(priv->xdr_buf + cached, buf, avail);
|
||||
+
|
||||
+ priv->cache.tags[entry].flags |= CACHE_PAGE_DIRTY;
|
||||
+
|
||||
+ mutex_unlock(&priv->lock);
|
||||
+
|
||||
+ buf += avail;
|
||||
+ count -= avail;
|
||||
+ to += avail;
|
||||
+ }
|
||||
+
|
||||
+ *retlen = len;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv;
|
||||
+ uint64_t status;
|
||||
+ uint64_t ddr_lpar, ctrl_lpar, info_lpar, reports_lpar;
|
||||
+ int64_t ddr_size;
|
||||
+ uint64_t reports_size;
|
||||
+ int ret = -ENOMEM;
|
||||
+ char *rest;
|
||||
+
|
||||
+ ret = -EIO;
|
||||
+ ps3vram_mtd.priv = kzalloc(sizeof(struct ps3vram_priv), GFP_KERNEL);
|
||||
+ if (!ps3vram_mtd.priv)
|
||||
+ goto out;
|
||||
+ priv = ps3vram_mtd.priv;
|
||||
+
|
||||
+ mutex_init(&priv->lock);
|
||||
+
|
||||
+ /* Allocate XDR buffer (1MB aligned) */
|
||||
+ priv->xdr_buf = (uint8_t *) __get_free_pages(GFP_KERNEL,
|
||||
+ get_order(XDR_BUF_SIZE));
|
||||
+ if (priv->xdr_buf == NULL) {
|
||||
+ pr_err("ps3vram: could not allocate XDR buffer\n");
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out_free_priv;
|
||||
+ }
|
||||
+
|
||||
+ /* Put FIFO at begginning of XDR buffer */
|
||||
+ priv->fifo_base = (uint32_t *) (priv->xdr_buf + FIFO_OFFSET);
|
||||
+ priv->fifo_ptr = priv->fifo_base;
|
||||
+
|
||||
+ /* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */
|
||||
+ if (ps3_open_hv_device(dev)) {
|
||||
+ pr_err("ps3vram: ps3_open_hv_device failed\n");
|
||||
+ ret = -EAGAIN;
|
||||
+ goto out_close_gpu;
|
||||
+ }
|
||||
+
|
||||
+ /* Request memory */
|
||||
+ status = -1;
|
||||
+ ddr_size = memparse(size, &rest);
|
||||
+ if (*rest == '-')
|
||||
+ ddr_size -= ps3fb_videomemory.size;
|
||||
+ ddr_size = ALIGN(ddr_size, 1024*1024);
|
||||
+ if (ddr_size <= 0) {
|
||||
+ printk(KERN_ERR "ps3vram: specified size is too small\n");
|
||||
+ ret = -EINVAL;
|
||||
+ goto out_close_gpu;
|
||||
+ }
|
||||
+
|
||||
+ while (ddr_size > 0) {
|
||||
+ status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0,
|
||||
+ &priv->memory_handle,
|
||||
+ &ddr_lpar);
|
||||
+ if (status == 0)
|
||||
+ break;
|
||||
+ ddr_size -= 1024*1024;
|
||||
+ }
|
||||
+ if (status != 0 || ddr_size <= 0) {
|
||||
+ pr_err("ps3vram: lv1_gpu_memory_allocate failed\n");
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out_free_xdr_buf;
|
||||
+ }
|
||||
+ pr_info("ps3vram: allocated %u MiB of DDR memory\n",
|
||||
+ (unsigned int) (ddr_size / 1024 / 1024));
|
||||
+
|
||||
+ /* Request context */
|
||||
+ status = lv1_gpu_context_allocate(priv->memory_handle,
|
||||
+ 0,
|
||||
+ &priv->context_handle,
|
||||
+ &ctrl_lpar,
|
||||
+ &info_lpar,
|
||||
+ &reports_lpar,
|
||||
+ &reports_size);
|
||||
+ if (status) {
|
||||
+ pr_err("ps3vram: lv1_gpu_context_allocate failed\n");
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out_free_memory;
|
||||
+ }
|
||||
+
|
||||
+ /* Map XDR buffer to RSX */
|
||||
+ status = lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF,
|
||||
+ ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)),
|
||||
+ XDR_BUF_SIZE, 0);
|
||||
+ if (status) {
|
||||
+ pr_err("ps3vram: lv1_gpu_context_iomap failed\n");
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out_free_context;
|
||||
+ }
|
||||
+
|
||||
+ priv->base = ioremap(ddr_lpar, ddr_size);
|
||||
+ if (!priv->base) {
|
||||
+ pr_err("ps3vram: ioremap failed\n");
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out_free_context;
|
||||
+ }
|
||||
+
|
||||
+ priv->ctrl = ioremap(ctrl_lpar, 64 * 1024);
|
||||
+ if (!priv->ctrl) {
|
||||
+ pr_err("ps3vram: ioremap failed\n");
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out_unmap_vram;
|
||||
+ }
|
||||
+
|
||||
+ priv->reports = ioremap(reports_lpar, reports_size);
|
||||
+ if (!priv->reports) {
|
||||
+ pr_err("ps3vram: ioremap failed\n");
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out_unmap_ctrl;
|
||||
+ }
|
||||
+
|
||||
+ mutex_lock(&ps3_gpu_mutex);
|
||||
+ ps3vram_init_ring(&ps3vram_mtd);
|
||||
+ mutex_unlock(&ps3_gpu_mutex);
|
||||
+
|
||||
+ ps3vram_mtd.name = "ps3vram";
|
||||
+ ps3vram_mtd.size = ddr_size;
|
||||
+ ps3vram_mtd.flags = MTD_CAP_RAM;
|
||||
+ ps3vram_mtd.erase = ps3vram_erase;
|
||||
+ ps3vram_mtd.point = NULL;
|
||||
+ ps3vram_mtd.unpoint = NULL;
|
||||
+ ps3vram_mtd.read = ps3vram_read;
|
||||
+ ps3vram_mtd.write = ps3vram_write;
|
||||
+ ps3vram_mtd.owner = THIS_MODULE;
|
||||
+ ps3vram_mtd.type = MTD_RAM;
|
||||
+ ps3vram_mtd.erasesize = CACHE_PAGE_SIZE;
|
||||
+ ps3vram_mtd.writesize = 1;
|
||||
+
|
||||
+ ps3vram_bind(&ps3vram_mtd);
|
||||
+
|
||||
+ mutex_lock(&ps3_gpu_mutex);
|
||||
+ ret = ps3vram_wait_ring(&ps3vram_mtd, 100);
|
||||
+ mutex_unlock(&ps3_gpu_mutex);
|
||||
+ if (ret < 0) {
|
||||
+ pr_err("failed to initialize channels\n");
|
||||
+ ret = -ETIMEDOUT;
|
||||
+ goto out_unmap_reports;
|
||||
+ }
|
||||
+
|
||||
+ ps3vram_cache_init(&ps3vram_mtd);
|
||||
+
|
||||
+ if (add_mtd_device(&ps3vram_mtd)) {
|
||||
+ pr_err("ps3vram: failed to register device\n");
|
||||
+ ret = -EAGAIN;
|
||||
+ goto out_cache_cleanup;
|
||||
+ }
|
||||
+
|
||||
+ pr_info("ps3vram mtd device registered, %lu bytes\n", ddr_size);
|
||||
+ return 0;
|
||||
+
|
||||
+out_cache_cleanup:
|
||||
+ ps3vram_cache_cleanup(&ps3vram_mtd);
|
||||
+out_unmap_reports:
|
||||
+ iounmap(priv->reports);
|
||||
+out_unmap_ctrl:
|
||||
+ iounmap(priv->ctrl);
|
||||
+out_unmap_vram:
|
||||
+ iounmap(priv->base);
|
||||
+out_free_context:
|
||||
+ lv1_gpu_context_free(priv->context_handle);
|
||||
+out_free_memory:
|
||||
+ lv1_gpu_memory_free(priv->memory_handle);
|
||||
+out_close_gpu:
|
||||
+ ps3_close_hv_device(dev);
|
||||
+out_free_xdr_buf:
|
||||
+ free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
|
||||
+out_free_priv:
|
||||
+ kfree(ps3vram_mtd.priv);
|
||||
+ ps3vram_mtd.priv = NULL;
|
||||
+out:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int ps3vram_shutdown(struct ps3_system_bus_device *dev)
|
||||
+{
|
||||
+ struct ps3vram_priv *priv;
|
||||
+
|
||||
+ priv = ps3vram_mtd.priv;
|
||||
+
|
||||
+ del_mtd_device(&ps3vram_mtd);
|
||||
+ ps3vram_cache_cleanup(&ps3vram_mtd);
|
||||
+ iounmap(priv->reports);
|
||||
+ iounmap(priv->ctrl);
|
||||
+ iounmap(priv->base);
|
||||
+ lv1_gpu_context_free(priv->context_handle);
|
||||
+ lv1_gpu_memory_free(priv->memory_handle);
|
||||
+ ps3_close_hv_device(dev);
|
||||
+ free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
|
||||
+ kfree(priv);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct ps3_system_bus_driver ps3vram_driver = {
|
||||
+ .match_id = PS3_MATCH_ID_GRAPHICS,
|
||||
+ .match_sub_id = PS3_MATCH_SUB_ID_RAMDISK,
|
||||
+ .core.name = DEVICE_NAME,
|
||||
+ .core.owner = THIS_MODULE,
|
||||
+ .probe = ps3vram_probe,
|
||||
+ .remove = ps3vram_shutdown,
|
||||
+ .shutdown = ps3vram_shutdown,
|
||||
+};
|
||||
+
|
||||
+static int __init ps3vram_init(void)
|
||||
+{
|
||||
+ return ps3_system_bus_driver_register(&ps3vram_driver);
|
||||
+}
|
||||
+
|
||||
+static void __exit ps3vram_exit(void)
|
||||
+{
|
||||
+ ps3_system_bus_driver_unregister(&ps3vram_driver);
|
||||
+}
|
||||
+
|
||||
+module_init(ps3vram_init);
|
||||
+module_exit(ps3vram_exit);
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("Jim Paris <jim@jtan.com>");
|
||||
+MODULE_DESCRIPTION("MTD driver for PS3 video RAM");
|
||||
--- a/MAINTAINERS
|
||||
+++ b/MAINTAINERS
|
||||
@@ -3353,6 +3353,12 @@ L: linuxppc-dev@ozlabs.org
|
||||
L: cbe-oss-dev@ozlabs.org
|
||||
S: Supported
|
||||
|
||||
+PS3VRAM DRIVER
|
||||
+P: Jim Paris
|
||||
+M: jim@jtan.com
|
||||
+L: cbe-oss-dev@ozlabs.org
|
||||
+S: Maintained
|
||||
+
|
||||
PVRUSB2 VIDEO4LINUX DRIVER
|
||||
P: Mike Isely
|
||||
M: isely@pobox.com
|
||||
@@ -0,0 +1,57 @@
|
||||
Subject: Unable to Use Small BSR register on Power LPAR
|
||||
From: Sonny Rao <sonnyrao@us.ibm.com>
|
||||
References: 443673 - LTC49749
|
||||
|
||||
Fix the BSR driver to allow small BSR devices on a 64k page kernel.
|
||||
Previously the driver would reject the mmap since the size was smaller
|
||||
than PAGESIZE. This patch adds a check for this case and uses remap_4k_pfn().
|
||||
|
||||
Also, take out code to set vm_flags, as the remap_pfn functions will
|
||||
do this for us.
|
||||
|
||||
Signed-off-by: Sonny Rao <sonnyrao@us.ibm.com>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
drivers/char/bsr.c | 20 ++++++++++++++------
|
||||
1 file changed, 14 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/char/bsr.c
|
||||
+++ b/drivers/char/bsr.c
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mm.h>
|
||||
+#include <asm/pgtable.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/*
|
||||
@@ -115,15 +116,22 @@ static int bsr_mmap(struct file *filp, s
|
||||
{
|
||||
unsigned long size = vma->vm_end - vma->vm_start;
|
||||
struct bsr_dev *dev = filp->private_data;
|
||||
+ int ret;
|
||||
|
||||
- if (size > dev->bsr_len || (size & (PAGE_SIZE-1)))
|
||||
- return -EINVAL;
|
||||
-
|
||||
- vma->vm_flags |= (VM_IO | VM_DONTEXPAND);
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
|
||||
- if (io_remap_pfn_range(vma, vma->vm_start, dev->bsr_addr >> PAGE_SHIFT,
|
||||
- size, vma->vm_page_prot))
|
||||
+ /* check for the case of a small BSR device and map one 4k page for it*/
|
||||
+ if (dev->bsr_len < PAGE_SIZE && size == PAGE_SIZE)
|
||||
+ ret = remap_4k_pfn(vma, vma->vm_start, dev->bsr_addr >> 12,
|
||||
+ vma->vm_page_prot);
|
||||
+ else if (size <= dev->bsr_len)
|
||||
+ ret = io_remap_pfn_range(vma, vma->vm_start,
|
||||
+ dev->bsr_addr >> PAGE_SHIFT,
|
||||
+ size, vma->vm_page_prot);
|
||||
+ else
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (ret)
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
@@ -0,0 +1,206 @@
|
||||
Subject: Add support for multiple BSR nodes in the device tree.
|
||||
From: Sonny Rao <sonnyrao@us.ibm.com>
|
||||
References: 443665 - LTC49817
|
||||
|
||||
Previously, the BSR driver only supported a single OF node describing
|
||||
a BSR. Apparently when an LPAR is set to use "all system resources"
|
||||
the BSR appears as a single node, but when it is handed out in pieces,
|
||||
each 8 byte piece gets its own node. So, keep a list of bsr devices
|
||||
instead of the array and include all nodes.
|
||||
|
||||
Also, be more inclusive of what BSR devices we accept by only checking
|
||||
compatibility and not the device name property (which might change in
|
||||
the future versions of BSR).
|
||||
|
||||
Signed-off-by: Sonny Rao <sonnyrao@us.ibm.com>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
drivers/char/bsr.c | 84 ++++++++++++++++++++++++++++++++++-------------------
|
||||
1 file changed, 55 insertions(+), 29 deletions(-)
|
||||
|
||||
--- a/drivers/char/bsr.c
|
||||
+++ b/drivers/char/bsr.c
|
||||
@@ -61,6 +61,8 @@ struct bsr_dev {
|
||||
unsigned bsr_num; /* bsr id number for its type */
|
||||
int bsr_minor;
|
||||
|
||||
+ struct list_head bsr_list;
|
||||
+
|
||||
dev_t bsr_dev;
|
||||
struct cdev bsr_cdev;
|
||||
struct device *bsr_device;
|
||||
@@ -68,8 +70,8 @@ struct bsr_dev {
|
||||
|
||||
};
|
||||
|
||||
-static unsigned num_bsr_devs;
|
||||
-static struct bsr_dev *bsr_devs;
|
||||
+static unsigned total_bsr_devs;
|
||||
+static struct list_head bsr_devs = LIST_HEAD_INIT(bsr_devs);
|
||||
static struct class *bsr_class;
|
||||
static int bsr_major;
|
||||
|
||||
@@ -154,24 +156,25 @@ const static struct file_operations bsr_
|
||||
|
||||
static void bsr_cleanup_devs(void)
|
||||
{
|
||||
- int i;
|
||||
- for (i=0 ; i < num_bsr_devs; i++) {
|
||||
- struct bsr_dev *cur = bsr_devs + i;
|
||||
+ struct bsr_dev *cur, *n;
|
||||
+
|
||||
+ list_for_each_entry_safe(cur, n, &bsr_devs, bsr_list) {
|
||||
if (cur->bsr_device) {
|
||||
cdev_del(&cur->bsr_cdev);
|
||||
device_del(cur->bsr_device);
|
||||
}
|
||||
+ list_del(&cur->bsr_list);
|
||||
+ kfree(cur);
|
||||
}
|
||||
-
|
||||
- kfree(bsr_devs);
|
||||
}
|
||||
|
||||
-static int bsr_create_devs(struct device_node *bn)
|
||||
+static int bsr_add_node(struct device_node *bn)
|
||||
{
|
||||
- int bsr_stride_len, bsr_bytes_len;
|
||||
+ int bsr_stride_len, bsr_bytes_len, num_bsr_devs;
|
||||
const u32 *bsr_stride;
|
||||
const u32 *bsr_bytes;
|
||||
unsigned i;
|
||||
+ int ret = -ENODEV;
|
||||
|
||||
bsr_stride = of_get_property(bn, "ibm,lock-stride", &bsr_stride_len);
|
||||
bsr_bytes = of_get_property(bn, "ibm,#lock-bytes", &bsr_bytes_len);
|
||||
@@ -179,35 +182,36 @@ static int bsr_create_devs(struct device
|
||||
if (!bsr_stride || !bsr_bytes ||
|
||||
(bsr_stride_len != bsr_bytes_len)) {
|
||||
printk(KERN_ERR "bsr of-node has missing/incorrect property\n");
|
||||
- return -ENODEV;
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
num_bsr_devs = bsr_bytes_len / sizeof(u32);
|
||||
|
||||
- /* only a warning, its informational since we'll fail and exit */
|
||||
- WARN_ON(num_bsr_devs > BSR_MAX_DEVS);
|
||||
-
|
||||
- bsr_devs = kzalloc(sizeof(struct bsr_dev) * num_bsr_devs, GFP_KERNEL);
|
||||
- if (!bsr_devs)
|
||||
- return -ENOMEM;
|
||||
-
|
||||
for (i = 0 ; i < num_bsr_devs; i++) {
|
||||
- struct bsr_dev *cur = bsr_devs + i;
|
||||
+ struct bsr_dev *cur = kzalloc(sizeof(struct bsr_dev),
|
||||
+ GFP_KERNEL);
|
||||
struct resource res;
|
||||
int result;
|
||||
|
||||
+ if (!cur) {
|
||||
+ printk(KERN_ERR "Unable to alloc bsr dev\n");
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out_err;
|
||||
+ }
|
||||
+
|
||||
result = of_address_to_resource(bn, i, &res);
|
||||
if (result < 0) {
|
||||
- printk(KERN_ERR "bsr of-node has invalid reg property\n");
|
||||
- goto out_err;
|
||||
+ printk(KERN_ERR "bsr of-node has invalid reg property, skipping\n");
|
||||
+ kfree(cur);
|
||||
+ continue;
|
||||
}
|
||||
|
||||
- cur->bsr_minor = i;
|
||||
+ cur->bsr_minor = i + total_bsr_devs;
|
||||
cur->bsr_addr = res.start;
|
||||
cur->bsr_len = res.end - res.start + 1;
|
||||
cur->bsr_bytes = bsr_bytes[i];
|
||||
cur->bsr_stride = bsr_stride[i];
|
||||
- cur->bsr_dev = MKDEV(bsr_major, i);
|
||||
+ cur->bsr_dev = MKDEV(bsr_major, i + total_bsr_devs);
|
||||
|
||||
switch(cur->bsr_bytes) {
|
||||
case 8:
|
||||
@@ -228,14 +232,15 @@ static int bsr_create_devs(struct device
|
||||
}
|
||||
|
||||
cur->bsr_num = bsr_types[cur->bsr_type];
|
||||
- bsr_types[cur->bsr_type] = cur->bsr_num + 1;
|
||||
snprintf(cur->bsr_name, 32, "bsr%d_%d",
|
||||
cur->bsr_bytes, cur->bsr_num);
|
||||
|
||||
cdev_init(&cur->bsr_cdev, &bsr_fops);
|
||||
result = cdev_add(&cur->bsr_cdev, cur->bsr_dev, 1);
|
||||
- if (result)
|
||||
+ if (result) {
|
||||
+ kfree(cur);
|
||||
goto out_err;
|
||||
+ }
|
||||
|
||||
cur->bsr_device = device_create_drvdata(bsr_class, NULL,
|
||||
cur->bsr_dev,
|
||||
@@ -244,16 +249,37 @@ static int bsr_create_devs(struct device
|
||||
printk(KERN_ERR "device_create failed for %s\n",
|
||||
cur->bsr_name);
|
||||
cdev_del(&cur->bsr_cdev);
|
||||
+ kfree(cur);
|
||||
goto out_err;
|
||||
}
|
||||
+
|
||||
+ bsr_types[cur->bsr_type] = cur->bsr_num + 1;
|
||||
+ list_add_tail(&cur->bsr_list, &bsr_devs);
|
||||
}
|
||||
|
||||
+ total_bsr_devs += num_bsr_devs;
|
||||
+
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
|
||||
bsr_cleanup_devs();
|
||||
- return -ENODEV;
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int bsr_create_devs(struct device_node *bn)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ while (bn) {
|
||||
+ ret = bsr_add_node(bn);
|
||||
+ if (ret) {
|
||||
+ of_node_put(bn);
|
||||
+ return ret;
|
||||
+ }
|
||||
+ bn = of_find_compatible_node(bn, NULL, "ibm,bsr");
|
||||
+ }
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static int __init bsr_init(void)
|
||||
@@ -263,7 +289,7 @@ static int __init bsr_init(void)
|
||||
int ret = -ENODEV;
|
||||
int result;
|
||||
|
||||
- np = of_find_compatible_node(NULL, "ibm,bsr", "ibm,bsr");
|
||||
+ np = of_find_compatible_node(NULL, NULL, "ibm,bsr");
|
||||
if (!np)
|
||||
goto out_err;
|
||||
|
||||
@@ -281,10 +307,10 @@ static int __init bsr_init(void)
|
||||
goto out_err_2;
|
||||
}
|
||||
|
||||
- if ((ret = bsr_create_devs(np)) < 0)
|
||||
+ if ((ret = bsr_create_devs(np)) < 0) {
|
||||
+ np = NULL;
|
||||
goto out_err_3;
|
||||
-
|
||||
- of_node_put(np);
|
||||
+ }
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
Subject: powerpc: Update page in counter for CMM
|
||||
From: Brian King <brking@linux.vnet.ibm.com>
|
||||
References: 445540 - LTC49942
|
||||
|
||||
A new field has been added to the VPA as a method for
|
||||
the client OS to communicate to firmware the number of
|
||||
page ins it is performing when running collaborative
|
||||
memory overcommit. The hypervisor will use this information
|
||||
to better determine if a partition is experiencing memory
|
||||
pressure and needs more memory allocated to it.
|
||||
|
||||
Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
|
||||
Signed-off-by: Robert Jennings <rcjenn@linux.vnet.ibm.com>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
|
||||
arch/powerpc/include/asm/lppaca.h | 3 ++-
|
||||
arch/powerpc/kernel/paca.c | 1 +
|
||||
arch/powerpc/mm/fault.c | 12 ++++++++++--
|
||||
3 files changed, 13 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/include/asm/lppaca.h
|
||||
+++ b/arch/powerpc/include/asm/lppaca.h
|
||||
@@ -133,7 +133,8 @@ struct lppaca {
|
||||
//=============================================================================
|
||||
// CACHE_LINE_4-5 0x0180 - 0x027F Contains PMC interrupt data
|
||||
//=============================================================================
|
||||
- u8 pmc_save_area[256]; // PMC interrupt Area x00-xFF
|
||||
+ u32 page_ins; // CMO Hint - # page ins by OS x00-x04
|
||||
+ u8 pmc_save_area[252]; // PMC interrupt Area x04-xFF
|
||||
} __attribute__((__aligned__(0x400)));
|
||||
|
||||
extern struct lppaca lppaca[];
|
||||
--- a/arch/powerpc/kernel/paca.c
|
||||
+++ b/arch/powerpc/kernel/paca.c
|
||||
@@ -36,6 +36,7 @@ struct lppaca lppaca[] = {
|
||||
.end_of_quantum = 0xfffffffffffffffful,
|
||||
.slb_count = 64,
|
||||
.vmxregs_in_use = 0,
|
||||
+ .page_ins = 0,
|
||||
},
|
||||
};
|
||||
|
||||
--- a/arch/powerpc/mm/fault.c
|
||||
+++ b/arch/powerpc/mm/fault.c
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/kdebug.h>
|
||||
|
||||
+#include <asm/firmware.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/mmu.h>
|
||||
@@ -318,9 +319,16 @@ good_area:
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
}
|
||||
- if (ret & VM_FAULT_MAJOR)
|
||||
+ if (ret & VM_FAULT_MAJOR) {
|
||||
current->maj_flt++;
|
||||
- else
|
||||
+#ifdef CONFIG_PPC_SMLPAR
|
||||
+ if (firmware_has_feature(FW_FEATURE_CMO)) {
|
||||
+ preempt_disable();
|
||||
+ get_lppaca()->page_ins += (1 << PAGE_FACTOR);
|
||||
+ preempt_enable();
|
||||
+ }
|
||||
+#endif
|
||||
+ } else
|
||||
current->min_flt++;
|
||||
up_read(&mm->mmap_sem);
|
||||
return 0;
|
||||
@@ -0,0 +1,60 @@
|
||||
Subject: Update default_server during migrate_irqs_away
|
||||
From: Milton Miller <miltonm@bga.com>
|
||||
References: 460566 - LTC50723
|
||||
|
||||
Currently, every time we determine which irq server to use, we check if
|
||||
default_server, which is the id of the bootcpu, is still online. But
|
||||
default_server is a hardware cpu, not the logical cpu id needed to index
|
||||
cpu_online_map.
|
||||
|
||||
Since the default server can only go offline during a cpu hotplug event,
|
||||
explicitly check the default server and choose the new one when we move
|
||||
irqs away from the cpu being offlined.
|
||||
|
||||
This has the added benefit of only needing the boot_cpuid to be updated
|
||||
and not relying on the cpu being marked offline during migrate_irqs_away.
|
||||
|
||||
Also, since xics_update_irq_servers only reads device tree information, we
|
||||
can call it before xics_init_host in xics_init_IRQ and then default_server
|
||||
will always be valid when we can reach get_irq_server via the host ops.
|
||||
|
||||
Signed-off-by: Milton Miller <miltonm@bga.com>
|
||||
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
arch/powerpc/platforms/pseries/xics.c | 9 +++++----
|
||||
1 file changed, 5 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/platforms/pseries/xics.c
|
||||
+++ b/arch/powerpc/platforms/pseries/xics.c
|
||||
@@ -208,9 +208,6 @@ static int get_irq_server(unsigned int v
|
||||
cpumask_t cpumask = irq_desc[virq].affinity;
|
||||
cpumask_t tmp = CPU_MASK_NONE;
|
||||
|
||||
- if (! cpu_isset(default_server, cpu_online_map))
|
||||
- xics_update_irq_servers();
|
||||
-
|
||||
if (!distribute_irqs)
|
||||
return default_server;
|
||||
|
||||
@@ -659,8 +656,8 @@ void __init xics_init_IRQ(void)
|
||||
if (found == 0)
|
||||
return;
|
||||
|
||||
- xics_init_host();
|
||||
xics_update_irq_servers();
|
||||
+ xics_init_host();
|
||||
|
||||
if (firmware_has_feature(FW_FEATURE_LPAR))
|
||||
ppc_md.get_irq = xics_get_irq_lpar;
|
||||
@@ -753,6 +750,10 @@ void xics_migrate_irqs_away(void)
|
||||
int cpu = smp_processor_id(), hw_cpu = hard_smp_processor_id();
|
||||
unsigned int irq, virq;
|
||||
|
||||
+ /* If we used to be the default server, move to the new "boot_cpuid" */
|
||||
+ if (hw_cpu == default_server)
|
||||
+ xics_update_irq_servers();
|
||||
+
|
||||
/* Reject any interrupt that was queued to us... */
|
||||
xics_set_cpu_priority(0);
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
Subject: Fix partition migration hang under load
|
||||
From: Brian King <brking@linux.vnet.ibm.com>
|
||||
References: 470563 - LTC51153
|
||||
|
||||
While testing partition migration with heavy CPU load using
|
||||
shared processors, it was observed that sometimes the migration
|
||||
would never complete and would appear to hang. Currently, the
|
||||
migration code assumes that if H_SUCCESS is returned from the H_JOIN
|
||||
then the migration is complete and the processor is waking up on
|
||||
the target system. If there was an outstanding PROD to the processor
|
||||
when the H_JOIN is called, however, it will return H_SUCCESS on the source
|
||||
system, causing the migration to hang, or in some scenarios cause
|
||||
the kernel to crash on the complete call waking the caller
|
||||
of rtas_percpu_suspend_me.
|
||||
|
||||
Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
|
||||
arch/powerpc/kernel/rtas.c | 10 ++++++++--
|
||||
1 file changed, 8 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/kernel/rtas.c
|
||||
+++ b/arch/powerpc/kernel/rtas.c
|
||||
@@ -46,6 +46,7 @@ EXPORT_SYMBOL(rtas);
|
||||
|
||||
struct rtas_suspend_me_data {
|
||||
atomic_t working; /* number of cpus accessing this struct */
|
||||
+ atomic_t done;
|
||||
int token; /* ibm,suspend-me */
|
||||
int error;
|
||||
struct completion *complete; /* wait on this until working == 0 */
|
||||
@@ -663,7 +664,7 @@ static int ibm_suspend_me_token = RTAS_U
|
||||
#ifdef CONFIG_PPC_PSERIES
|
||||
static void rtas_percpu_suspend_me(void *info)
|
||||
{
|
||||
- long rc;
|
||||
+ long rc = H_SUCCESS;
|
||||
unsigned long msr_save;
|
||||
int cpu;
|
||||
struct rtas_suspend_me_data *data =
|
||||
@@ -675,7 +676,8 @@ static void rtas_percpu_suspend_me(void
|
||||
msr_save = mfmsr();
|
||||
mtmsr(msr_save & ~(MSR_EE));
|
||||
|
||||
- rc = plpar_hcall_norets(H_JOIN);
|
||||
+ while (rc == H_SUCCESS && !atomic_read(&data->done))
|
||||
+ rc = plpar_hcall_norets(H_JOIN);
|
||||
|
||||
mtmsr(msr_save);
|
||||
|
||||
@@ -698,6 +700,9 @@ static void rtas_percpu_suspend_me(void
|
||||
smp_processor_id(), rc);
|
||||
data->error = rc;
|
||||
}
|
||||
+
|
||||
+ atomic_set(&data->done, 1);
|
||||
+
|
||||
/* This cpu did the suspend or got an error; in either case,
|
||||
* we need to prod all other other cpus out of join state.
|
||||
* Extra prods are harmless.
|
||||
@@ -740,6 +745,7 @@ static int rtas_ibm_suspend_me(struct rt
|
||||
}
|
||||
|
||||
atomic_set(&data.working, 0);
|
||||
+ atomic_set(&data.done, 0);
|
||||
data.token = rtas_token("ibm,suspend-me");
|
||||
data.error = 0;
|
||||
data.complete = &done;
|
||||
@@ -0,0 +1,46 @@
|
||||
Subject: Bug 435181 - DMEM add caused kernel opps after ehca rejected request
|
||||
From: olh@suse.de
|
||||
References: 435181
|
||||
|
||||
---
|
||||
arch/powerpc/platforms/pseries/hotplug-memory.c | 12 ++++++++++++
|
||||
mm/memory_hotplug.c | 4 ++--
|
||||
2 files changed, 14 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/platforms/pseries/hotplug-memory.c
|
||||
+++ b/arch/powerpc/platforms/pseries/hotplug-memory.c
|
||||
@@ -22,6 +22,18 @@ static int pseries_remove_lmb(unsigned l
|
||||
int ret;
|
||||
|
||||
start_pfn = base >> PAGE_SHIFT;
|
||||
+
|
||||
+ if (!pfn_valid(start_pfn)) {
|
||||
+ /*
|
||||
+ * Failing hotplug memory add will end up calling
|
||||
+ * remove device node to clean up. Since its already
|
||||
+ * added to lmbs, we need to remove it and pretend
|
||||
+ * success.
|
||||
+ */
|
||||
+ lmb_remove(base, lmb_size);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
zone = page_zone(pfn_to_page(start_pfn));
|
||||
|
||||
/*
|
||||
--- a/mm/memory_hotplug.c
|
||||
+++ b/mm/memory_hotplug.c
|
||||
@@ -323,11 +323,11 @@ int __remove_pages(struct zone *zone, un
|
||||
BUG_ON(phys_start_pfn & ~PAGE_SECTION_MASK);
|
||||
BUG_ON(nr_pages % PAGES_PER_SECTION);
|
||||
|
||||
- release_mem_region(phys_start_pfn << PAGE_SHIFT, nr_pages * PAGE_SIZE);
|
||||
-
|
||||
sections_to_remove = nr_pages / PAGES_PER_SECTION;
|
||||
for (i = 0; i < sections_to_remove; i++) {
|
||||
unsigned long pfn = phys_start_pfn + i*PAGES_PER_SECTION;
|
||||
+ release_mem_region(pfn << PAGE_SHIFT,
|
||||
+ PAGES_PER_SECTION << PAGE_SHIFT);
|
||||
ret = __remove_section(zone, __pfn_to_section(pfn));
|
||||
if (ret)
|
||||
break;
|
||||
@@ -0,0 +1,32 @@
|
||||
Subject: Bug 431380 – UPT-LTE: mem dlpar remove generates oops(.pseries_remove_lmb+0x28/0xb8)
|
||||
From: Nathan Fontenot <nfont@austin.ibm.com>
|
||||
References: 431380 - LTC48430
|
||||
|
||||
|
||||
Testing hotplug memory remove has revealed that we can oops in
|
||||
pseries_lmb_remove(). The incorrect shift causes a NULL pointer
|
||||
dereference in the page_zone() inline routine.
|
||||
|
||||
I have only been able to reproduce the oops on kernels with large pages
|
||||
enabled.
|
||||
|
||||
Tested on Power5 and Power6 with and without large pages enabled.
|
||||
|
||||
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
arch/powerpc/platforms/pseries/hotplug-memory.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/powerpc/platforms/pseries/hotplug-memory.c
|
||||
+++ b/arch/powerpc/platforms/pseries/hotplug-memory.c
|
||||
@@ -21,7 +21,7 @@ static int pseries_remove_lmb(unsigned l
|
||||
struct zone *zone;
|
||||
int ret;
|
||||
|
||||
- start_pfn = base >> PFN_SECTION_SHIFT;
|
||||
+ start_pfn = base >> PAGE_SHIFT;
|
||||
zone = page_zone(pfn_to_page(start_pfn));
|
||||
|
||||
/*
|
||||
17
src/patches/suse-2.6.27.31/patches.arch/ppc-select
Normal file
17
src/patches/suse-2.6.27.31/patches.arch/ppc-select
Normal file
@@ -0,0 +1,17 @@
|
||||
From: Paul Mackerras <paulus@samba.org>
|
||||
Subject: Fix wrong error code from ppc32 select syscall
|
||||
Acked-by: schwab@suse.de
|
||||
|
||||
See <http://ozlabs.org/pipermail/linuxppc-dev/2008-October/063678.html>.
|
||||
|
||||
--- linux-2.6.27/arch/powerpc/include/asm/systbl.h.~1~ 2008-11-10 23:10:37.000000000 +0100
|
||||
+++ linux-2.6.27/arch/powerpc/include/asm/systbl.h 2008-11-11 11:39:06.000000000 +0100
|
||||
@@ -145,7 +145,7 @@ SYSCALL_SPU(setfsuid)
|
||||
SYSCALL_SPU(setfsgid)
|
||||
SYSCALL_SPU(llseek)
|
||||
COMPAT_SYS_SPU(getdents)
|
||||
-SYSX_SPU(sys_select,ppc32_select,ppc_select)
|
||||
+SYSX_SPU(sys_select,ppc32_select,sys_select)
|
||||
SYSCALL_SPU(flock)
|
||||
SYSCALL_SPU(msync)
|
||||
COMPAT_SYS_SPU(readv)
|
||||
@@ -0,0 +1,36 @@
|
||||
Subject: use inc_nlink
|
||||
From: Jeremy Kerr <jk@ozlabs.org>
|
||||
References: 447133 - LTC50070
|
||||
|
||||
Style change: use inc_nlink instead of incrementing i_nlink directly
|
||||
|
||||
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
arch/powerpc/platforms/cell/spufs/inode.c | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/platforms/cell/spufs/inode.c
|
||||
+++ b/arch/powerpc/platforms/cell/spufs/inode.c
|
||||
@@ -298,8 +298,8 @@ spufs_mkdir(struct inode *dir, struct de
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
dget(dentry);
|
||||
- dir->i_nlink++;
|
||||
- dentry->d_inode->i_nlink++;
|
||||
+ inc_nlink(dir);
|
||||
+ inc_nlink(dentry->d_inode);
|
||||
goto out;
|
||||
|
||||
out_free_ctx:
|
||||
@@ -540,8 +540,8 @@ spufs_mkgang(struct inode *dir, struct d
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
- dir->i_nlink++;
|
||||
- dentry->d_inode->i_nlink++;
|
||||
+ inc_nlink(dir);
|
||||
+ inc_nlink(dentry->d_inode);
|
||||
return ret;
|
||||
|
||||
out_iput:
|
||||
@@ -0,0 +1,83 @@
|
||||
Subject: Only enable logging on open(), prevent multiple openers
|
||||
From: Jeremy Kerr <jk@ozlabs.org>
|
||||
References: 447133 - LTC50070
|
||||
|
||||
Currently, sputrace will start logging to the event buffer before the
|
||||
log buffer has been open()ed. This results in a heap of "lost samples"
|
||||
warnings if the sputrace file hasn't yet been opened.
|
||||
|
||||
Since the buffer is reset on open() anyway, there's no need to enable
|
||||
logging when no-one has opened the log.
|
||||
|
||||
Because open clears the log, make it return EBUSY for mutliple open
|
||||
calls.
|
||||
|
||||
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
arch/powerpc/platforms/cell/spufs/sputrace.c | 32 ++++++++++++++++++++++++---
|
||||
1 file changed, 29 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/platforms/cell/spufs/sputrace.c
|
||||
+++ b/arch/powerpc/platforms/cell/spufs/sputrace.c
|
||||
@@ -40,6 +40,7 @@ static DECLARE_WAIT_QUEUE_HEAD(sputrace_
|
||||
static ktime_t sputrace_start;
|
||||
static unsigned long sputrace_head, sputrace_tail;
|
||||
static struct sputrace *sputrace_log;
|
||||
+static int sputrace_logging;
|
||||
|
||||
static int sputrace_used(void)
|
||||
{
|
||||
@@ -109,24 +110,49 @@ static ssize_t sputrace_read(struct file
|
||||
|
||||
static int sputrace_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
+ int rc;
|
||||
+
|
||||
spin_lock(&sputrace_lock);
|
||||
+ if (sputrace_logging) {
|
||||
+ rc = -EBUSY;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ sputrace_logging = 1;
|
||||
sputrace_head = sputrace_tail = 0;
|
||||
sputrace_start = ktime_get();
|
||||
+ rc = 0;
|
||||
+
|
||||
+out:
|
||||
spin_unlock(&sputrace_lock);
|
||||
+ return rc;
|
||||
+}
|
||||
|
||||
+static int sputrace_release(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ spin_lock(&sputrace_lock);
|
||||
+ sputrace_logging = 0;
|
||||
+ spin_unlock(&sputrace_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations sputrace_fops = {
|
||||
- .owner = THIS_MODULE,
|
||||
- .open = sputrace_open,
|
||||
- .read = sputrace_read,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .open = sputrace_open,
|
||||
+ .read = sputrace_read,
|
||||
+ .release = sputrace_release,
|
||||
};
|
||||
|
||||
static void sputrace_log_item(const char *name, struct spu_context *ctx,
|
||||
struct spu *spu)
|
||||
{
|
||||
spin_lock(&sputrace_lock);
|
||||
+
|
||||
+ if (!sputrace_logging) {
|
||||
+ spin_unlock(&sputrace_lock);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
if (sputrace_avail() > 1) {
|
||||
struct sputrace *t = sputrace_log + sputrace_head;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Subject: Don't block until the read buffer is full
|
||||
From: Jeremy Kerr <jk@ozlabs.org>
|
||||
References: 447133 - LTC50070
|
||||
|
||||
Currently, read() on the sputrace buffer will only return data when
|
||||
the user buffer is exhausted. This may mean that we never see the
|
||||
end of the event log, unless we read() with exactly the right-sized
|
||||
buffer.
|
||||
|
||||
This change makes sputrace_read not block if we have data ready to
|
||||
return.
|
||||
|
||||
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
arch/powerpc/platforms/cell/spufs/sputrace.c | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
--- a/arch/powerpc/platforms/cell/spufs/sputrace.c
|
||||
+++ b/arch/powerpc/platforms/cell/spufs/sputrace.c
|
||||
@@ -80,6 +80,11 @@ static ssize_t sputrace_read(struct file
|
||||
char tbuf[128];
|
||||
int width;
|
||||
|
||||
+ /* If we have data ready to return, don't block waiting
|
||||
+ * for more */
|
||||
+ if (cnt > 0 && sputrace_used() == 0)
|
||||
+ break;
|
||||
+
|
||||
error = wait_event_interruptible(sputrace_wait,
|
||||
sputrace_used() > 0);
|
||||
if (error)
|
||||
@@ -0,0 +1,264 @@
|
||||
Subject: Use state_mutex for switch_log locking, and prevent multiple openers
|
||||
From: Jeremy Kerr <jk@ozlabs.org>
|
||||
References: 447133 - LTC50070
|
||||
|
||||
Currently, we use ctx->mapping_lock and ctx->switch_log->lock for the
|
||||
context switch log. The mapping lock only prevents concurrent open()s,
|
||||
so we require the switch_lock->lock for reads.
|
||||
|
||||
Since writes to the switch log buffer occur on context switches, we're
|
||||
better off synchronising with the state_mutex, which is held during a
|
||||
switch. Since we're serialised througout the buffer reads and writes,
|
||||
we can use the state mutex to protect open and release too, and
|
||||
can now kfree() the log buffer on release. This allows us to perform
|
||||
the switch log notify without taking any extra locks.
|
||||
|
||||
Because the buffer is only present while the file is open, we can use
|
||||
it to prevent multiple simultaneous openers.
|
||||
|
||||
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
arch/powerpc/platforms/cell/spufs/file.c | 133 ++++++++++++++++++------------
|
||||
arch/powerpc/platforms/cell/spufs/run.c | 3
|
||||
arch/powerpc/platforms/cell/spufs/spufs.h | 1
|
||||
3 files changed, 81 insertions(+), 56 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/platforms/cell/spufs/file.c
|
||||
+++ b/arch/powerpc/platforms/cell/spufs/file.c
|
||||
@@ -2429,38 +2429,48 @@ static inline int spufs_switch_log_avail
|
||||
static int spufs_switch_log_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
|
||||
+ int rc;
|
||||
+
|
||||
+ rc = spu_acquire(ctx);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
|
||||
- /*
|
||||
- * We (ab-)use the mapping_lock here because it serves the similar
|
||||
- * purpose for synchronizing open/close elsewhere. Maybe it should
|
||||
- * be renamed eventually.
|
||||
- */
|
||||
- mutex_lock(&ctx->mapping_lock);
|
||||
if (ctx->switch_log) {
|
||||
- spin_lock(&ctx->switch_log->lock);
|
||||
- ctx->switch_log->head = 0;
|
||||
- ctx->switch_log->tail = 0;
|
||||
- spin_unlock(&ctx->switch_log->lock);
|
||||
- } else {
|
||||
- /*
|
||||
- * We allocate the switch log data structures on first open.
|
||||
- * They will never be free because we assume a context will
|
||||
- * be traced until it goes away.
|
||||
- */
|
||||
- ctx->switch_log = kzalloc(sizeof(struct switch_log) +
|
||||
- SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry),
|
||||
- GFP_KERNEL);
|
||||
- if (!ctx->switch_log)
|
||||
- goto out;
|
||||
- spin_lock_init(&ctx->switch_log->lock);
|
||||
- init_waitqueue_head(&ctx->switch_log->wait);
|
||||
+ rc = -EBUSY;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ ctx->switch_log = kzalloc(sizeof(struct switch_log) +
|
||||
+ SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry),
|
||||
+ GFP_KERNEL);
|
||||
+
|
||||
+ if (!ctx->switch_log) {
|
||||
+ rc = -ENOMEM;
|
||||
+ goto out;
|
||||
}
|
||||
- mutex_unlock(&ctx->mapping_lock);
|
||||
+
|
||||
+ init_waitqueue_head(&ctx->switch_log->wait);
|
||||
+ rc = 0;
|
||||
+
|
||||
+out:
|
||||
+ spu_release(ctx);
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
+static int spufs_switch_log_release(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
|
||||
+ int rc;
|
||||
+
|
||||
+ rc = spu_acquire(ctx);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ kfree(ctx->switch_log);
|
||||
+ ctx->switch_log = NULL;
|
||||
+ spu_release(ctx);
|
||||
|
||||
return 0;
|
||||
- out:
|
||||
- mutex_unlock(&ctx->mapping_lock);
|
||||
- return -ENOMEM;
|
||||
}
|
||||
|
||||
static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n)
|
||||
@@ -2488,42 +2498,46 @@ static ssize_t spufs_switch_log_read(str
|
||||
if (!buf || len < 0)
|
||||
return -EINVAL;
|
||||
|
||||
+ error = spu_acquire(ctx);
|
||||
+ if (error)
|
||||
+ return error;
|
||||
+
|
||||
while (cnt < len) {
|
||||
char tbuf[128];
|
||||
int width;
|
||||
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
- if (spufs_switch_log_used(ctx) <= 0)
|
||||
- return cnt ? cnt : -EAGAIN;
|
||||
+ if (spufs_switch_log_used(ctx) == 0) {
|
||||
+ error = -EAGAIN;
|
||||
+ break;
|
||||
+ }
|
||||
} else {
|
||||
- /* Wait for data in buffer */
|
||||
- error = wait_event_interruptible(ctx->switch_log->wait,
|
||||
+ /* spufs_wait will drop the mutex and re-acquire,
|
||||
+ * but since we're in read(), the file cannot be
|
||||
+ * _released (and so ctx->switch_log is stable).
|
||||
+ */
|
||||
+ error = spufs_wait(ctx->switch_log->wait,
|
||||
spufs_switch_log_used(ctx) > 0);
|
||||
+
|
||||
+ /* On error, spufs_wait returns without the
|
||||
+ * state mutex held */
|
||||
if (error)
|
||||
- break;
|
||||
+ return error;
|
||||
}
|
||||
|
||||
- spin_lock(&ctx->switch_log->lock);
|
||||
- if (ctx->switch_log->head == ctx->switch_log->tail) {
|
||||
- /* multiple readers race? */
|
||||
- spin_unlock(&ctx->switch_log->lock);
|
||||
+ /* We may have had entries read from underneath us while we
|
||||
+ * dropped the mutex in spufs_wait, so re-check */
|
||||
+ if (ctx->switch_log->head == ctx->switch_log->tail)
|
||||
continue;
|
||||
- }
|
||||
|
||||
width = switch_log_sprint(ctx, tbuf, sizeof(tbuf));
|
||||
- if (width < len) {
|
||||
+ if (width < len)
|
||||
ctx->switch_log->tail =
|
||||
(ctx->switch_log->tail + 1) %
|
||||
SWITCH_LOG_BUFSIZE;
|
||||
- }
|
||||
-
|
||||
- spin_unlock(&ctx->switch_log->lock);
|
||||
-
|
||||
- /*
|
||||
- * If the record is greater than space available return
|
||||
- * partial buffer (so far)
|
||||
- */
|
||||
- if (width >= len)
|
||||
+ else
|
||||
+ /* If the record is greater than space available return
|
||||
+ * partial buffer (so far) */
|
||||
break;
|
||||
|
||||
error = copy_to_user(buf + cnt, tbuf, width);
|
||||
@@ -2532,6 +2546,8 @@ static ssize_t spufs_switch_log_read(str
|
||||
cnt += width;
|
||||
}
|
||||
|
||||
+ spu_release(ctx);
|
||||
+
|
||||
return cnt == 0 ? error : cnt;
|
||||
}
|
||||
|
||||
@@ -2540,29 +2556,41 @@ static unsigned int spufs_switch_log_pol
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
|
||||
unsigned int mask = 0;
|
||||
+ int rc;
|
||||
|
||||
poll_wait(file, &ctx->switch_log->wait, wait);
|
||||
|
||||
+ rc = spu_acquire(ctx);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
if (spufs_switch_log_used(ctx) > 0)
|
||||
mask |= POLLIN;
|
||||
|
||||
+ spu_release(ctx);
|
||||
+
|
||||
return mask;
|
||||
}
|
||||
|
||||
static const struct file_operations spufs_switch_log_fops = {
|
||||
- .owner = THIS_MODULE,
|
||||
- .open = spufs_switch_log_open,
|
||||
- .read = spufs_switch_log_read,
|
||||
- .poll = spufs_switch_log_poll,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .open = spufs_switch_log_open,
|
||||
+ .read = spufs_switch_log_read,
|
||||
+ .poll = spufs_switch_log_poll,
|
||||
+ .release = spufs_switch_log_release,
|
||||
};
|
||||
|
||||
+/**
|
||||
+ * Log a context switch event to a switch log reader.
|
||||
+ *
|
||||
+ * Must be called with ctx->state_mutex held.
|
||||
+ */
|
||||
void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx,
|
||||
u32 type, u32 val)
|
||||
{
|
||||
if (!ctx->switch_log)
|
||||
return;
|
||||
|
||||
- spin_lock(&ctx->switch_log->lock);
|
||||
if (spufs_switch_log_avail(ctx) > 1) {
|
||||
struct switch_log_entry *p;
|
||||
|
||||
@@ -2576,7 +2604,6 @@ void spu_switch_log_notify(struct spu *s
|
||||
ctx->switch_log->head =
|
||||
(ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE;
|
||||
}
|
||||
- spin_unlock(&ctx->switch_log->lock);
|
||||
|
||||
wake_up(&ctx->switch_log->wait);
|
||||
}
|
||||
--- a/arch/powerpc/platforms/cell/spufs/run.c
|
||||
+++ b/arch/powerpc/platforms/cell/spufs/run.c
|
||||
@@ -249,6 +249,7 @@ static int spu_run_fini(struct spu_conte
|
||||
|
||||
spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED);
|
||||
clear_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags);
|
||||
+ spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, *status);
|
||||
spu_release(ctx);
|
||||
|
||||
if (signal_pending(current))
|
||||
@@ -417,8 +418,6 @@ long spufs_run_spu(struct spu_context *c
|
||||
ret = spu_run_fini(ctx, npc, &status);
|
||||
spu_yield(ctx);
|
||||
|
||||
- spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, status);
|
||||
-
|
||||
if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
|
||||
(((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100))
|
||||
ctx->stats.libassist++;
|
||||
--- a/arch/powerpc/platforms/cell/spufs/spufs.h
|
||||
+++ b/arch/powerpc/platforms/cell/spufs/spufs.h
|
||||
@@ -65,7 +65,6 @@ enum {
|
||||
};
|
||||
|
||||
struct switch_log {
|
||||
- spinlock_t lock;
|
||||
wait_queue_head_t wait;
|
||||
unsigned long head;
|
||||
unsigned long tail;
|
||||
@@ -0,0 +1,78 @@
|
||||
Subject: Don't require full buffer in switch_log read
|
||||
From: Jeremy Kerr <jk@ozlabs.org>
|
||||
References: 447133 - LTC50070
|
||||
|
||||
Currently, read() on the sputrace log will block until the read buffer
|
||||
is full. This makes it difficult to retrieve the end of the buffer, as
|
||||
the user will need to read with the right-sized buffer.
|
||||
|
||||
In a similar method as 91553a1b5e0df006a3573a88d98ee7cd48a3818a, this
|
||||
change makes the switch_log return if there has already been data
|
||||
read.
|
||||
|
||||
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
arch/powerpc/platforms/cell/spufs/file.c | 46 ++++++++++++++++++-------------
|
||||
1 file changed, 27 insertions(+), 19 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/platforms/cell/spufs/file.c
|
||||
+++ b/arch/powerpc/platforms/cell/spufs/file.c
|
||||
@@ -2506,30 +2506,38 @@ static ssize_t spufs_switch_log_read(str
|
||||
char tbuf[128];
|
||||
int width;
|
||||
|
||||
- if (file->f_flags & O_NONBLOCK) {
|
||||
- if (spufs_switch_log_used(ctx) == 0) {
|
||||
+ if (spufs_switch_log_used(ctx) == 0) {
|
||||
+ if (cnt > 0) {
|
||||
+ /* If there's data ready to go, we can
|
||||
+ * just return straight away */
|
||||
+ break;
|
||||
+
|
||||
+ } else if (file->f_flags & O_NONBLOCK) {
|
||||
error = -EAGAIN;
|
||||
break;
|
||||
+
|
||||
+ } else {
|
||||
+ /* spufs_wait will drop the mutex and
|
||||
+ * re-acquire, but since we're in read(), the
|
||||
+ * file cannot be _released (and so
|
||||
+ * ctx->switch_log is stable).
|
||||
+ */
|
||||
+ error = spufs_wait(ctx->switch_log->wait,
|
||||
+ spufs_switch_log_used(ctx) > 0);
|
||||
+
|
||||
+ /* On error, spufs_wait returns without the
|
||||
+ * state mutex held */
|
||||
+ if (error)
|
||||
+ return error;
|
||||
+
|
||||
+ /* We may have had entries read from underneath
|
||||
+ * us while we dropped the mutex in spufs_wait,
|
||||
+ * so re-check */
|
||||
+ if (spufs_switch_log_used(ctx) == 0)
|
||||
+ continue;
|
||||
}
|
||||
- } else {
|
||||
- /* spufs_wait will drop the mutex and re-acquire,
|
||||
- * but since we're in read(), the file cannot be
|
||||
- * _released (and so ctx->switch_log is stable).
|
||||
- */
|
||||
- error = spufs_wait(ctx->switch_log->wait,
|
||||
- spufs_switch_log_used(ctx) > 0);
|
||||
-
|
||||
- /* On error, spufs_wait returns without the
|
||||
- * state mutex held */
|
||||
- if (error)
|
||||
- return error;
|
||||
}
|
||||
|
||||
- /* We may have had entries read from underneath us while we
|
||||
- * dropped the mutex in spufs_wait, so re-check */
|
||||
- if (ctx->switch_log->head == ctx->switch_log->tail)
|
||||
- continue;
|
||||
-
|
||||
width = switch_log_sprint(ctx, tbuf, sizeof(tbuf));
|
||||
if (width < len)
|
||||
ctx->switch_log->tail =
|
||||
@@ -0,0 +1,32 @@
|
||||
Subject: Don't spu_acquire_saved unnecessarily in regs read
|
||||
From: Jeremy Kerr <jk@ozlabs.org>
|
||||
References: 447133 - LTC50070
|
||||
|
||||
With most file readers (eg cat, dd), reading a context's regs file will
|
||||
result in two reads: the first to read the data, and the second to
|
||||
return EOF. Because each read performs a spu_acquire_saved, we end up
|
||||
descheduling and re-scheduling the context twice.
|
||||
|
||||
This change does a simple check to see if we'd return EOF before
|
||||
calling spu_acquire_saved(), saving the extra schedule operation.
|
||||
|
||||
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
arch/powerpc/platforms/cell/spufs/file.c | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
--- a/arch/powerpc/platforms/cell/spufs/file.c
|
||||
+++ b/arch/powerpc/platforms/cell/spufs/file.c
|
||||
@@ -551,6 +551,11 @@ spufs_regs_read(struct file *file, char
|
||||
int ret;
|
||||
struct spu_context *ctx = file->private_data;
|
||||
|
||||
+ /* pre-check for file position: if we'd return EOF, there's no point
|
||||
+ * causing a deschedule */
|
||||
+ if (*pos >= sizeof(ctx->csa.lscsa->gprs))
|
||||
+ return 0;
|
||||
+
|
||||
ret = spu_acquire_saved(ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -0,0 +1,31 @@
|
||||
Subject: Use kmalloc rather than kzalloc for switch log buffer
|
||||
From: Jeremy Kerr <jk@ozlabs.org>
|
||||
References: 447133 - LTC50070
|
||||
|
||||
No need to zero the entire buffer, just the head and tail indices.
|
||||
|
||||
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
arch/powerpc/platforms/cell/spufs/file.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/powerpc/platforms/cell/spufs/file.c
|
||||
+++ b/arch/powerpc/platforms/cell/spufs/file.c
|
||||
@@ -2445,7 +2445,7 @@ static int spufs_switch_log_open(struct
|
||||
goto out;
|
||||
}
|
||||
|
||||
- ctx->switch_log = kzalloc(sizeof(struct switch_log) +
|
||||
+ ctx->switch_log = kmalloc(sizeof(struct switch_log) +
|
||||
SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry),
|
||||
GFP_KERNEL);
|
||||
|
||||
@@ -2454,6 +2454,7 @@ static int spufs_switch_log_open(struct
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ ctx->switch_log->head = ctx->switch_log->tail = 0;
|
||||
init_waitqueue_head(&ctx->switch_log->wait);
|
||||
rc = 0;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
Subject: Improve search of node for contexts with SPU affinity
|
||||
From: Andre Detsch <adetsch@br.ibm.com>
|
||||
References: 447133 - LTC50070
|
||||
|
||||
This patch improves redability of the code responsible for trying to find
|
||||
a node with enough SPUs not committed to other affinity gangs.
|
||||
|
||||
An additional check is also added, to avoid taking into account gangs that
|
||||
have no SPU affinity.
|
||||
|
||||
Signed-off-by: Andre Detsch <adetsch@br.ibm.com>
|
||||
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
arch/powerpc/platforms/cell/spufs/sched.c | 19 +++++++++++++------
|
||||
1 file changed, 13 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/platforms/cell/spufs/sched.c
|
||||
+++ b/arch/powerpc/platforms/cell/spufs/sched.c
|
||||
@@ -312,6 +312,15 @@ static struct spu *aff_ref_location(stru
|
||||
*/
|
||||
node = cpu_to_node(raw_smp_processor_id());
|
||||
for (n = 0; n < MAX_NUMNODES; n++, node++) {
|
||||
+ /*
|
||||
+ * "available_spus" counts how many spus are not potentially
|
||||
+ * going to be used by other affinity gangs whose reference
|
||||
+ * context is already in place. Although this code seeks to
|
||||
+ * avoid having affinity gangs with a summed amount of
|
||||
+ * contexts bigger than the amount of spus in the node,
|
||||
+ * this may happen sporadically. In this case, available_spus
|
||||
+ * becomes negative, which is harmless.
|
||||
+ */
|
||||
int available_spus;
|
||||
|
||||
node = (node < MAX_NUMNODES) ? node : 0;
|
||||
@@ -321,12 +330,10 @@ static struct spu *aff_ref_location(stru
|
||||
available_spus = 0;
|
||||
mutex_lock(&cbe_spu_info[node].list_mutex);
|
||||
list_for_each_entry(spu, &cbe_spu_info[node].spus, cbe_list) {
|
||||
- if (spu->ctx && spu->ctx->gang
|
||||
- && spu->ctx->aff_offset == 0)
|
||||
- available_spus -=
|
||||
- (spu->ctx->gang->contexts - 1);
|
||||
- else
|
||||
- available_spus++;
|
||||
+ if (spu->ctx && spu->ctx->gang && !spu->ctx->aff_offset
|
||||
+ && spu->ctx->gang->aff_ref_spu)
|
||||
+ available_spus -= spu->ctx->gang->contexts;
|
||||
+ available_spus++;
|
||||
}
|
||||
if (available_spus < ctx->gang->contexts) {
|
||||
mutex_unlock(&cbe_spu_info[node].list_mutex);
|
||||
@@ -0,0 +1,28 @@
|
||||
Subject: Explain conditional decrement of aff_sched_count
|
||||
From: Andre Detsch <adetsch@br.ibm.com>
|
||||
References: 447133 - LTC50070
|
||||
|
||||
This patch adds a comment to clarify why atomic_dec_if_positive is being used
|
||||
to decrement gang's aff_sched_count on SPU context unbind.
|
||||
|
||||
Signed-off-by: Andre Detsch <adetsch@br.ibm.com>
|
||||
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
---
|
||||
arch/powerpc/platforms/cell/spufs/sched.c | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
--- a/arch/powerpc/platforms/cell/spufs/sched.c
|
||||
+++ b/arch/powerpc/platforms/cell/spufs/sched.c
|
||||
@@ -444,6 +444,11 @@ static void spu_unbind_context(struct sp
|
||||
atomic_dec(&cbe_spu_info[spu->node].reserved_spus);
|
||||
|
||||
if (ctx->gang)
|
||||
+ /*
|
||||
+ * If ctx->gang->aff_sched_count is positive, SPU affinity is
|
||||
+ * being considered in this gang. Using atomic_dec_if_positive
|
||||
+ * allow us to skip an explicit check for affinity in this gang
|
||||
+ */
|
||||
atomic_dec_if_positive(&ctx->gang->aff_sched_count);
|
||||
|
||||
spu_switch_notify(spu, NULL);
|
||||
@@ -0,0 +1,48 @@
|
||||
Subject: Fix system calls on Cell entered with XER.SO=1
|
||||
From: Paul Mackerras <paulus@samba.org>
|
||||
References: 456406 - LTC50395
|
||||
|
||||
|
||||
It turns out that on Cell, on a kernel with CONFIG_VIRT_CPU_ACCOUNTING=y,
|
||||
if a program sets the SO (summary overflow) bit in the XER and
|
||||
then does a system call, the SO bit in CR0 will be set on return
|
||||
regardless of whether the system call detected an error. Since CR0.SO
|
||||
is used as the error indication from the system call, this means that
|
||||
all system calls appear to fail.
|
||||
|
||||
The reason is that the workaround for the timebase bug on Cell uses a
|
||||
compare instruction. With CONFIG_VIRT_CPU_ACCOUNTING = y, the
|
||||
ACCOUNT_CPU_USER_ENTRY macro reads the timebase, so we end up doing a
|
||||
compare instruction, which copies XER.SO to CR0.SO. Since we were
|
||||
doing this in the system call entry patch after clearing CR0.SO but
|
||||
before saving the CR, this meant that the saved CR image had CR0.SO
|
||||
set if XER.SO was set on entry.
|
||||
|
||||
This fixes it by moving the clearing of CR0.SO to after the
|
||||
ACCOUNT_CPU_USER_ENTRY call in the system call entry path.
|
||||
|
||||
Signed-off-by: Paul Mackerras <paulus@samba.org>
|
||||
Acked-by: Arnd Bergmann <arnd@arndb.de>
|
||||
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
arch/powerpc/kernel/entry_64.S | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/powerpc/kernel/entry_64.S
|
||||
+++ b/arch/powerpc/kernel/entry_64.S
|
||||
@@ -57,12 +57,12 @@ system_call_common:
|
||||
beq- 1f
|
||||
ld r1,PACAKSAVE(r13)
|
||||
1: std r10,0(r1)
|
||||
- crclr so
|
||||
std r11,_NIP(r1)
|
||||
std r12,_MSR(r1)
|
||||
std r0,GPR0(r1)
|
||||
std r10,GPR1(r1)
|
||||
ACCOUNT_CPU_USER_ENTRY(r10, r11)
|
||||
+ crclr so
|
||||
std r2,GPR2(r1)
|
||||
std r3,GPR3(r1)
|
||||
std r4,GPR4(r1)
|
||||
@@ -0,0 +1,61 @@
|
||||
Subject: Check for valid hugepage size in hugetlb_get_unmapped_area
|
||||
From: Brian King <brking@linux.vnet.ibm.com>
|
||||
References: 456433 - LTC50170
|
||||
|
||||
It looks like most of the hugetlb code is doing the correct thing if
|
||||
hugepages are not supported, but the mmap code is not. If we get into
|
||||
the mmap code when hugepages are not supported, such as in an LPAR
|
||||
which is running Active Memory Sharing, we can oops the kernel. This
|
||||
patch fixes the oops being seen in this path.
|
||||
|
||||
ops: Kernel access of bad area, sig: 11 [#1]
|
||||
SMP NR_CPUS=1024 NUMA pSeries
|
||||
Modules linked in: nfs(N) lockd(N) nfs_acl(N) sunrpc(N) ipv6(N) fuse(N) loop(N)
|
||||
dm_mod(N) sg(N) ibmveth(N) sd_mod(N) crc_t10dif(N) ibmvscsic(N)
|
||||
scsi_transport_srp(N) scsi_tgt(N) scsi_mod(N)
|
||||
Supported: No
|
||||
NIP: c000000000038d60 LR: c00000000003945c CTR: c0000000000393f0
|
||||
REGS: c000000077e7b830 TRAP: 0300 Tainted: G
|
||||
(2.6.27.5-bz50170-2-ppc64)
|
||||
MSR: 8000000000009032 <EE,ME,IR,DR> CR: 44000448 XER: 20000001
|
||||
DAR: c000002000af90a8, DSISR: 0000000040000000
|
||||
TASK = c00000007c1b8600[4019] 'hugemmap01' THREAD: c000000077e78000 CPU: 6
|
||||
GPR00: 0000001fffffffe0 c000000077e7bab0 c0000000009a4e78 0000000000000000
|
||||
GPR04: 0000000000010000 0000000000000001 00000000ffffffff 0000000000000001
|
||||
GPR08: 0000000000000000 c000000000af90c8 0000000000000001 0000000000000000
|
||||
GPR12: 000000000000003f c000000000a73880 0000000000000000 0000000000000000
|
||||
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000010000
|
||||
GPR20: 0000000000000000 0000000000000003 0000000000010000 0000000000000001
|
||||
GPR24: 0000000000000003 0000000000000000 0000000000000001 ffffffffffffffb5
|
||||
GPR28: c000000077ca2e80 0000000000000000 c00000000092af78 0000000000010000
|
||||
NIP [c000000000038d60] .slice_get_unmapped_area+0x6c/0x4e0
|
||||
LR [c00000000003945c] .hugetlb_get_unmapped_area+0x6c/0x80
|
||||
Call Trace:
|
||||
[c000000077e7bbc0] [c00000000003945c] .hugetlb_get_unmapped_area+0x6c/0x80
|
||||
[c000000077e7bc30] [c000000000107e30] .get_unmapped_area+0x64/0xd8
|
||||
[c000000077e7bcb0] [c00000000010b140] .do_mmap_pgoff+0x140/0x420
|
||||
[c000000077e7bd80] [c00000000000bf5c] .sys_mmap+0xc4/0x140
|
||||
[c000000077e7be30] [c0000000000086b4] syscall_exit+0x0/0x40
|
||||
Instruction dump:
|
||||
fac1ffb0 fae1ffb8 fb01ffc0 fb21ffc8 fb41ffd0 fb61ffd8 fb81ffe0 fbc1fff0
|
||||
fbe1fff8 f821fef1 f8c10158 f8e10160 <7d49002e> f9010168 e92d01b0 eb4902b0
|
||||
|
||||
Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
|
||||
Signed-off-by: Olaf Hering <olh@suse.de>
|
||||
|
||||
---
|
||||
arch/powerpc/mm/hugetlbpage.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
--- a/arch/powerpc/mm/hugetlbpage.c
|
||||
+++ b/arch/powerpc/mm/hugetlbpage.c
|
||||
@@ -500,6 +500,9 @@ unsigned long hugetlb_get_unmapped_area(
|
||||
{
|
||||
struct hstate *hstate = hstate_file(file);
|
||||
int mmu_psize = shift_to_mmu_psize(huge_page_shift(hstate));
|
||||
+
|
||||
+ if (!mmu_huge_psizes[mmu_psize])
|
||||
+ return -EINVAL;
|
||||
return slice_get_unmapped_area(addr, len, flags, mmu_psize, 1, 0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
From: Olaf Hering <olh@novell.com>
|
||||
Subject: [PATCH] poweroc: vio modalias
|
||||
|
||||
Acked-by: Olaf Hering <olh@novell.com>
|
||||
---
|
||||
arch/powerpc/kernel/vio.c | 15 +++++++++++++++
|
||||
1 file changed, 15 insertions(+)
|
||||
|
||||
--- a/arch/powerpc/kernel/vio.c
|
||||
+++ b/arch/powerpc/kernel/vio.c
|
||||
@@ -1315,9 +1315,24 @@ static ssize_t devspec_show(struct devic
|
||||
return sprintf(buf, "%s\n", of_node ? of_node->full_name : "none");
|
||||
}
|
||||
|
||||
+static ssize_t modalias_show (struct device *dev, struct device_attribute *attr,
|
||||
+ char *buf)
|
||||
+{
|
||||
+ struct device_node *of_node = dev->archdata.of_node;
|
||||
+ const char *compat;
|
||||
+ int i = 0;
|
||||
+
|
||||
+ if (of_node) {
|
||||
+ compat = of_get_property(of_node, "compatible", &i);
|
||||
+ i = sprintf (buf, "vio:T%sS%s\n", of_node->type, compat);
|
||||
+ }
|
||||
+ return i;
|
||||
+}
|
||||
+
|
||||
static struct device_attribute vio_dev_attrs[] = {
|
||||
__ATTR_RO(name),
|
||||
__ATTR_RO(devspec),
|
||||
+ __ATTR_RO(modalias),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
From: Milton Miller <miltonm@bga.com>
|
||||
Subject: powerpc/xics: EOI unmapped irqs after disabling them
|
||||
Date: Fri Oct 10 01:56:23 2008 +0000
|
||||
X-Git-Tag: v2.6.28-rc1~569^2~27
|
||||
X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=8767e9badca7cdf0adc2564d7524092d47ababf3
|
||||
Patch-mainline: v2.6.28-rc1
|
||||
References: bnc#459065
|
||||
|
||||
When reciving an irq vector that does not have a linux mapping, the kernel
|
||||
prints a message and calls RTAS to disable the irq source. Previously
|
||||
the kernel did not EOI the interrupt, causing the source to think it is
|
||||
still being processed by software. While this does add an additional
|
||||
layer of protection against interrupt storms had RTAS failed to disable
|
||||
the source, it also prevents the interrupt from working when a driver
|
||||
later enables it. (We could alternatively send an EOI on startup, but
|
||||
that strategy would likely fail on an emulated xics.)
|
||||
|
||||
All interrupts should be disabled when the kernel starts, but this can
|
||||
be observed if a driver does not shutdown an interrupt in its reboot
|
||||
hook before starting a new kernel with kexec.
|
||||
|
||||
Michael reports this can be reproduced trivially by banging the keyboard
|
||||
while kexec'ing on a P5 LPAR: even though the hvc_console driver request's
|
||||
the console irq later in boot, the console is non-functional because
|
||||
we're receiving no console interrupts.
|
||||
|
||||
Reported-By: Michael Ellerman
|
||||
Signed-off-by: Milton Miller <miltonm@bga.com>
|
||||
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
||||
Acked-by: Petr Tesarik <ptesarik@suse.cz>
|
||||
|
||||
---
|
||||
arch/powerpc/platforms/pseries/xics.c | 53 ++++++++++++++++++++++++++--------
|
||||
1 file changed, 41 insertions(+), 12 deletions(-)
|
||||
|
||||
--- linux-2.6.27-SLE11_BRANCH.orig/arch/powerpc/platforms/pseries/xics.c 2009-04-16 10:33:47.000000000 +0200
|
||||
+++ linux-2.6.27-SLE11_BRANCH/arch/powerpc/platforms/pseries/xics.c 2009-04-16 10:52:29.000000000 +0200
|
||||
@@ -335,32 +335,61 @@ static void xics_eoi_lpar(unsigned int v
|
||||
lpar_xirr_info_set((0xff << 24) | irq);
|
||||
}
|
||||
|
||||
-static inline unsigned int xics_remap_irq(unsigned int vec)
|
||||
+static inline unsigned int xics_xirr_vector(unsigned int xirr)
|
||||
{
|
||||
- unsigned int irq;
|
||||
+ /*
|
||||
+ * The top byte is the old cppr, to be restored on EOI.
|
||||
+ * The remaining 24 bits are the vector.
|
||||
+ */
|
||||
+ return xirr & 0x00ffffff;
|
||||
+}
|
||||
|
||||
- vec &= 0x00ffffff;
|
||||
+static void xics_mask_unknown_vec(unsigned int vec)
|
||||
+{
|
||||
+ printk(KERN_ERR "Interrupt %u (real) is invalid, disabling it.\n", vec);
|
||||
+ xics_mask_real_irq(vec);
|
||||
+}
|
||||
+
|
||||
+static unsigned int xics_get_irq_direct(void)
|
||||
+{
|
||||
+ unsigned int xirr = direct_xirr_info_get();
|
||||
+ unsigned int vec = xics_xirr_vector(xirr);
|
||||
+ unsigned int irq;
|
||||
|
||||
if (vec == XICS_IRQ_SPURIOUS)
|
||||
return NO_IRQ;
|
||||
+
|
||||
irq = irq_radix_revmap(xics_host, vec);
|
||||
if (likely(irq != NO_IRQ))
|
||||
return irq;
|
||||
|
||||
- printk(KERN_ERR "Interrupt %u (real) is invalid,"
|
||||
- " disabling it.\n", vec);
|
||||
- xics_mask_real_irq(vec);
|
||||
- return NO_IRQ;
|
||||
-}
|
||||
+ /* We don't have a linux mapping, so have rtas mask it. */
|
||||
+ xics_mask_unknown_vec(vec);
|
||||
|
||||
-static unsigned int xics_get_irq_direct(void)
|
||||
-{
|
||||
- return xics_remap_irq(direct_xirr_info_get());
|
||||
+ /* We might learn about it later, so EOI it */
|
||||
+ direct_xirr_info_set(xirr);
|
||||
+ return NO_IRQ;
|
||||
}
|
||||
|
||||
static unsigned int xics_get_irq_lpar(void)
|
||||
{
|
||||
- return xics_remap_irq(lpar_xirr_info_get());
|
||||
+ unsigned int xirr = lpar_xirr_info_get();
|
||||
+ unsigned int vec = xics_xirr_vector(xirr);
|
||||
+ unsigned int irq;
|
||||
+
|
||||
+ if (vec == XICS_IRQ_SPURIOUS)
|
||||
+ return NO_IRQ;
|
||||
+
|
||||
+ irq = irq_radix_revmap(xics_host, vec);
|
||||
+ if (likely(irq != NO_IRQ))
|
||||
+ return irq;
|
||||
+
|
||||
+ /* We don't have a linux mapping, so have RTAS mask it. */
|
||||
+ xics_mask_unknown_vec(vec);
|
||||
+
|
||||
+ /* We might learn about it later, so EOI it */
|
||||
+ lpar_xirr_info_set(xirr);
|
||||
+ return NO_IRQ;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
@@ -0,0 +1,63 @@
|
||||
From: Michael Neuling <mikey@neuling.org>
|
||||
Subject: powerpc: Fix ptrace compat wrapper for FPU register access
|
||||
Patch-mainline: 2.6.30-rc4
|
||||
Git-commit: bc826666e4252f78d2b144af3b7d699ff5efce0a
|
||||
References: bnc#496027
|
||||
|
||||
powerpc: Fix ptrace compat wrapper for FPU register access
|
||||
|
||||
The ptrace compat wrapper mishandles access to the fpu registers. The
|
||||
PTRACE_PEEKUSR and PTRACE_POKEUSR requests miscalculate the index into
|
||||
the fpr array due to the broken FPINDEX macro. The
|
||||
PPC_PTRACE_PEEKUSR_3264 request needs to use the same formula that the
|
||||
native ptrace interface uses when operating on the register number (as
|
||||
opposed to the 4-byte offset). The PPC_PTRACE_POKEUSR_3264 request
|
||||
didn't take TS_FPRWIDTH into account.
|
||||
|
||||
Signed-off-by: Andreas Schwab <schwab@linux-m68k.org>
|
||||
Signed-off-by: Michael Neuling <mikey@neuling.org>
|
||||
Signed-off-by: Paul Mackerras <paulus@samba.org>
|
||||
Acked-by: Jeff Mahoney <jeffm@suse.com>
|
||||
---
|
||||
arch/powerpc/kernel/ptrace32.c | 15 +++++++++++----
|
||||
1 file changed, 11 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/kernel/ptrace32.c
|
||||
+++ b/arch/powerpc/kernel/ptrace32.c
|
||||
@@ -70,7 +70,8 @@ static long compat_ptrace_old(struct tas
|
||||
/* Macros to workout the correct index for the FPR in the thread struct */
|
||||
#define FPRNUMBER(i) (((i) - PT_FPR0) >> 1)
|
||||
#define FPRHALF(i) (((i) - PT_FPR0) & 1)
|
||||
-#define FPRINDEX(i) TS_FPRWIDTH * FPRNUMBER(i) + FPRHALF(i)
|
||||
+#define FPRINDEX(i) TS_FPRWIDTH * FPRNUMBER(i) * 2 + FPRHALF(i)
|
||||
+#define FPRINDEX_3264(i) (TS_FPRWIDTH * ((i) - PT_FPR0))
|
||||
|
||||
static int compat_ptrace_getsiginfo(struct task_struct *child, compat_siginfo_t __user *data)
|
||||
{
|
||||
@@ -192,8 +193,9 @@ long compat_arch_ptrace(struct task_stru
|
||||
CHECK_FULL_REGS(child->thread.regs);
|
||||
if (numReg >= PT_FPR0) {
|
||||
flush_fp_to_thread(child);
|
||||
- tmp = ((unsigned long int *)child->thread.fpr)
|
||||
- [FPRINDEX(numReg)];
|
||||
+ /* get 64 bit FPR */
|
||||
+ tmp = ((u64 *)child->thread.fpr)
|
||||
+ [FPRINDEX_3264(numReg)];
|
||||
} else { /* register within PT_REGS struct */
|
||||
tmp = ptrace_get_reg(child, numReg);
|
||||
}
|
||||
@@ -286,8 +288,13 @@ long compat_arch_ptrace(struct task_stru
|
||||
freg = (freg & 0xfffffffful) | (data << 32);
|
||||
ret = ptrace_put_reg(child, numReg, freg);
|
||||
} else {
|
||||
+ u64 *tmp;
|
||||
flush_fp_to_thread(child);
|
||||
- ((unsigned int *)child->thread.regs)[index] = data;
|
||||
+ /* get 64 bit FPR ... */
|
||||
+ tmp = &(((u64 *)child->thread.fpr)
|
||||
+ [FPRINDEX_3264(numReg)]);
|
||||
+ /* ... write the 32 bit part we want */
|
||||
+ ((u32 *)tmp)[index % 2] = data;
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
145
src/patches/suse-2.6.27.31/patches.arch/ppc64-eeh-msix-irq
Normal file
145
src/patches/suse-2.6.27.31/patches.arch/ppc64-eeh-msix-irq
Normal file
@@ -0,0 +1,145 @@
|
||||
From: Mike Mason <mmlnx@us.ibm.com>
|
||||
Date: Tue, 10 Feb 2009 11:12:21 +0000 (+0000)
|
||||
Subject: powerpc/eeh: Only disable/enable LSI interrupts in EEH
|
||||
Patch-mainline: 2.6.30-rc1
|
||||
Git-commit: 8535ef05a6904429ce72671c3035dbf05e6d5edf
|
||||
References: bnc#509497
|
||||
|
||||
powerpc/eeh: Only disable/enable LSI interrupts in EEH
|
||||
|
||||
The EEH code disables and enables interrupts during the
|
||||
device recovery process. This is unnecessary for MSI
|
||||
and MSI-X interrupts because they are effectively disabled
|
||||
by the DMA Stopped state when an EEH error occurs. The
|
||||
current code is also incorrect for MSI-X interrupts. It
|
||||
doesn't take into account that MSI-X interrupts are tracked
|
||||
in a different way than LSI/MSI interrupts. This patch
|
||||
ensures only LSI interrupts are disabled/enabled.
|
||||
|
||||
Signed-off-by: Mike Mason <mmlnx@us.ibm.com>
|
||||
Acked-by: Linas Vepstas <linasvepstas@gmail.com>
|
||||
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
||||
Acked-by: Jeff Mahoney <jeffm@suse.com>
|
||||
---
|
||||
|
||||
arch/powerpc/platforms/pseries/eeh_driver.c | 68 ++++++++++++++++++----------
|
||||
1 file changed, 45 insertions(+), 23 deletions(-)
|
||||
|
||||
--- a/arch/powerpc/platforms/pseries/eeh_driver.c
|
||||
+++ b/arch/powerpc/platforms/pseries/eeh_driver.c
|
||||
@@ -79,6 +79,40 @@ static int irq_in_use(unsigned int irq)
|
||||
return rc;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * eeh_disable_irq - disable interrupt for the recovering device
|
||||
+ */
|
||||
+static void eeh_disable_irq(struct pci_dev *dev)
|
||||
+{
|
||||
+ struct device_node *dn = pci_device_to_OF_node(dev);
|
||||
+
|
||||
+ /* Don't disable MSI and MSI-X interrupts. They are
|
||||
+ * effectively disabled by the DMA Stopped state
|
||||
+ * when an EEH error occurs.
|
||||
+ */
|
||||
+ if (dev->msi_enabled || dev->msix_enabled)
|
||||
+ return;
|
||||
+
|
||||
+ if (!irq_in_use(dev->irq))
|
||||
+ return;
|
||||
+
|
||||
+ PCI_DN(dn)->eeh_mode |= EEH_MODE_IRQ_DISABLED;
|
||||
+ disable_irq_nosync(dev->irq);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * eeh_enable_irq - enable interrupt for the recovering device
|
||||
+ */
|
||||
+static void eeh_enable_irq(struct pci_dev *dev)
|
||||
+{
|
||||
+ struct device_node *dn = pci_device_to_OF_node(dev);
|
||||
+
|
||||
+ if ((PCI_DN(dn)->eeh_mode) & EEH_MODE_IRQ_DISABLED) {
|
||||
+ PCI_DN(dn)->eeh_mode &= ~EEH_MODE_IRQ_DISABLED;
|
||||
+ enable_irq(dev->irq);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/* ------------------------------------------------------- */
|
||||
/**
|
||||
* eeh_report_error - report pci error to each device driver
|
||||
@@ -98,11 +132,8 @@ static void eeh_report_error(struct pci_
|
||||
if (!driver)
|
||||
return;
|
||||
|
||||
- if (irq_in_use (dev->irq)) {
|
||||
- struct device_node *dn = pci_device_to_OF_node(dev);
|
||||
- PCI_DN(dn)->eeh_mode |= EEH_MODE_IRQ_DISABLED;
|
||||
- disable_irq_nosync(dev->irq);
|
||||
- }
|
||||
+ eeh_disable_irq(dev);
|
||||
+
|
||||
if (!driver->err_handler ||
|
||||
!driver->err_handler->error_detected)
|
||||
return;
|
||||
@@ -147,17 +178,14 @@ static void eeh_report_reset(struct pci_
|
||||
{
|
||||
enum pci_ers_result rc, *res = userdata;
|
||||
struct pci_driver *driver = dev->driver;
|
||||
- struct device_node *dn = pci_device_to_OF_node(dev);
|
||||
|
||||
if (!driver)
|
||||
return;
|
||||
|
||||
dev->error_state = pci_channel_io_normal;
|
||||
|
||||
- if ((PCI_DN(dn)->eeh_mode) & EEH_MODE_IRQ_DISABLED) {
|
||||
- PCI_DN(dn)->eeh_mode &= ~EEH_MODE_IRQ_DISABLED;
|
||||
- enable_irq(dev->irq);
|
||||
- }
|
||||
+ eeh_enable_irq(dev);
|
||||
+
|
||||
if (!driver->err_handler ||
|
||||
!driver->err_handler->slot_reset)
|
||||
return;
|
||||
@@ -176,17 +204,14 @@ static void eeh_report_reset(struct pci_
|
||||
static void eeh_report_resume(struct pci_dev *dev, void *userdata)
|
||||
{
|
||||
struct pci_driver *driver = dev->driver;
|
||||
- struct device_node *dn = pci_device_to_OF_node(dev);
|
||||
|
||||
dev->error_state = pci_channel_io_normal;
|
||||
|
||||
if (!driver)
|
||||
return;
|
||||
|
||||
- if ((PCI_DN(dn)->eeh_mode) & EEH_MODE_IRQ_DISABLED) {
|
||||
- PCI_DN(dn)->eeh_mode &= ~EEH_MODE_IRQ_DISABLED;
|
||||
- enable_irq(dev->irq);
|
||||
- }
|
||||
+ eeh_enable_irq(dev);
|
||||
+
|
||||
if (!driver->err_handler ||
|
||||
!driver->err_handler->resume)
|
||||
return;
|
||||
@@ -210,15 +235,12 @@ static void eeh_report_failure(struct pc
|
||||
if (!driver)
|
||||
return;
|
||||
|
||||
- if (irq_in_use (dev->irq)) {
|
||||
- struct device_node *dn = pci_device_to_OF_node(dev);
|
||||
- PCI_DN(dn)->eeh_mode |= EEH_MODE_IRQ_DISABLED;
|
||||
- disable_irq_nosync(dev->irq);
|
||||
- }
|
||||
- if (!driver->err_handler)
|
||||
- return;
|
||||
- if (!driver->err_handler->error_detected)
|
||||
+ eeh_disable_irq(dev);
|
||||
+
|
||||
+ if (!driver->err_handler ||
|
||||
+ !driver->err_handler->error_detected)
|
||||
return;
|
||||
+
|
||||
driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user