diff -uNr linux-2.4.22/CHANGES linux-2.4.22-ben2/CHANGES
--- linux-2.4.22/CHANGES 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/CHANGES 2003-08-31 11:40:07.000000000 +0200
@@ -0,0 +1,11 @@
+2.4.22-ben2 : - Remove a hack on the MachineCheck exception handler that seemed
+ to actually cause a problem on some machines
+ - Turn legacy serial driver into a module (used by pcmcia modems)
+ instead of built-in in pmac defconfig
+ - Export a couple of symbols needed by recent DRI
+ - Hopefully close a few possible SMP races along with making the
+ IO ops more robust vs. store reordering.
+ - Fix build without CONFIG_PMAC_PBOOK
+
+2.4.22-ben1 : First release
+
diff -uNr linux-2.4.22/Documentation/Configure.help linux-2.4.22-ben2/Documentation/Configure.help
--- linux-2.4.22/Documentation/Configure.help 2003-08-25 13:44:39.000000000 +0200
+++ linux-2.4.22-ben2/Documentation/Configure.help 2003-08-31 11:41:33.000000000 +0200
@@ -27170,6 +27170,29 @@
If unsure, say N.
+CONFIG_CPU_FREQ
+ Clock scaling allows you to change the clock speed of CPUs on the
+ fly. This is a nice method to save battery power on notebooks,
+ because the lower the clock speed, the less power the CPU consumes.
+
+ For more information, take a look at linux/Documentation/cpufreq or
+ at
+
+ If in doubt, say N.
+
+CONFIG_CPU_FREQ_24_API
+ This enables the /proc/sys/cpu/ sysctl interface for controlling
+ CPUFreq, as known from the 2.4.-kernel patches for CPUFreq. Note
+ that some drivers do not support this interface or offer less
+ functionality.
+
+ If you say N here, you'll be able to control CPUFreq using the
+ new /proc/cpufreq interface.
+
+ For details, take a look at linux/Documentation/cpufreq.
+
+ If in doubt, say N.
+
NatSemi SCx200 support
CONFIG_SCx200
This provides basic support for the National Semiconductor SCx200
diff -uNr linux-2.4.22/Documentation/cpufreq linux-2.4.22-ben2/Documentation/cpufreq
--- linux-2.4.22/Documentation/cpufreq 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/Documentation/cpufreq 2003-08-31 11:39:58.000000000 +0200
@@ -0,0 +1,361 @@
+ CPU frequency and voltage scaling code in the Linux(TM) kernel
+
+
+ L i n u x C P U F r e q
+
+
+
+
+ Dominik Brodowski
+ David Kimdon
+
+
+
+ Clock scaling allows you to change the clock speed of the CPUs on the
+ fly. This is a nice method to save battery power, because the lower
+ the clock speed, the less power the CPU consumes.
+
+
+
+Contents:
+---------
+1. Supported architectures
+2. User interface
+2.1 /proc/cpufreq interface [2.6]
+2.2. /proc/sys/cpu/ interface [2.4]
+3. CPUFreq core and interfaces
+3.1 General information
+3.2 CPUFreq notifiers
+3.3 CPUFreq architecture drivers
+4. Mailing list and Links
+
+
+
+1. Supported architectures
+==========================
+
+ARM:
+ ARM Integrator, SA 1100, SA1110
+--------------------------------
+ This driver will be ported to new CPUFreq core soon, so
+ far it will not work.
+
+
+AMD Elan:
+ SC400, SC410
+--------------------------------
+ You need to specify the highest allowed CPU frequency as
+ a module parameter ("max_freq") or as boot parameter
+ ("elanfreq="). Else the available speed range will be
+ limited to the speed at which the CPU runs while this
+ module is loaded.
+
+
+VIA Cyrix Longhaul:
+ VIA Samuel/CyrixIII, VIA Cyrix Samuel/C3,
+ VIA Cyrix Ezra, VIA Cyrix Ezra-T
+--------------------------------
+ If you do not want to scale the Front Side Bus or voltage,
+ pass the module parameter "dont_scale_fsb 1" or
+ "dont_scale_voltage 1". Additionally, it is advised that
+ you pass the current Front Side Bus speed (in MHz) to
+ this module as module parameter "current_fsb", e.g.
+ "current_fsb 133" for a Front Side Bus speed of 133 MHz.
+
+
+Intel SpeedStep:
+ certain mobile Intel Pentium III (Coppermine), and all mobile
+ Intel Pentium III-M (Tualatin) and mobile Intel Pentium 4 P4-Ms.
+--------------------------------
+ Unfortunately only modern Intel ICH2-M and ICH3-M chipsets are
+ supported.
+
+
+P4 CPU Clock Modulation:
+ Intel Pentium 4 Xeon processors
+---------------------------------
+ Note that you can only switch the speed of two logical CPUs at
+ once - but each phyiscal CPU may have different throttling levels.
+
+
+PowerNow! K6:
+ mobile AMD K6-2+ / mobile K6-3+:
+--------------------------------
+ No known issues.
+
+
+Transmeta Crusoe Longrun:
+ Transmeta Crusoe processors:
+--------------------------------
+ Does not work with the 2.4. /proc/sys/cpu/ interface.
+
+
+
+2. User Interface
+=================
+
+2.1 /proc/cpufreq interface [2.6]
+***********************************
+
+Starting in the patches for kernel 2.5.33, CPUFreq uses a "policy"
+interface /proc/cpufreq.
+
+When you "cat" this file, you'll find something like:
+
+--
+ minimum CPU frequency - maximum CPU frequency - policy
+CPU 0 1200000 ( 75%) - 1600000 (100%) - performance
+--
+
+This means the current policy allows this CPU to be run anywhere
+between 1.2 GHz (the value is in kHz) and 1.6 GHz with an eye towards
+performance.
+
+To change the policy, "echo" the desired new policy into
+/proc/cpufreq. Use one of the following formats:
+
+cpu_nr:min_freq:max_freq:policy
+cpu_nr%min_freq%max_freq%policy
+min_freq:max_freq:policy
+min_freq%max_freq%policy
+
+with cpu_nr being the CPU which shall be affected, min_freq and
+max_freq the lower and upper limit of the CPU core frequency in kHz,
+and policy either "performance" or "powersave".
+A few examples:
+
+root@notebook:#echo -n "0:0:0:powersave" > /proc/cpufreq
+ sets the CPU #0 to the lowest supported frequency.
+
+root@notebook:#echo -n "1%100%100%performance" > /proc/cpufreq
+ sets the CPU #1 to the highest supported frequency.
+
+root@notebook:#echo -n "1000000:2000000:performance" > /proc/cpufreq
+ to set the frequency of all CPUs between 1 GHz and 2 GHz and to
+ the policy "performance".
+
+Please note that the values you "echo" into /proc/cpufreq are
+validated first, and may be limited by hardware or thermal
+considerations. Because of this, a read from /proc/cpufreq might
+differ from what was written into it.
+
+
+When you read /proc/cpufreq for the first time after a CPUFreq driver
+has been initialized, you'll see the "default policy" for this
+driver. If this does not suit your needs, you can pass a boot
+parameter to the cpufreq core. Use the following syntax for this:
+ "cpufreq=min_freq:max_freq:policy", i.e. you may not chose a
+specific CPU and you need to specify the limits in kHz and not in
+per cent.
+
+
+2.2 /proc/cpufreq interface [2.4]
+***********************************
+
+Previsiously (and still available as a config option), CPUFreq used
+a "sysctl" interface which is located in
+ /proc/sys/cpu/0/
+ /proc/sys/cpu/1/ ... (SMP only)
+
+In these directories, you will find three files of importance for
+CPUFreq: speed-max, speed-min and speed:
+
+speed shows the current CPU frequency in kHz,
+speed-min the minimum supported CPU frequency, and
+speed-max the maximum supported CPU frequency.
+
+
+To change the CPU frequency, "echo" the desired CPU frequency (in kHz)
+to speed. For example, to set the CPU speed to the lowest/highest
+allowed frequency do:
+
+root@notebook:# cat /proc/sys/cpu/0/speed-min > /proc/sys/cpu/0/speed
+root@notebook:# cat /proc/sys/cpu/0/speed-max > /proc/sys/cpu/0/speed
+
+
+
+3. CPUFreq core and interfaces
+===============================
+
+3.1 General information
+*************************
+
+The CPUFreq core code is located in linux/kernel/cpufreq.c. This
+cpufreq code offers a standardized interface for the CPUFreq
+architecture drivers (those pieces of code that do actual
+frequency transitions), as well as to "notifiers". These are device
+drivers or other part of the kernel that need to be informed of
+policy changes (like thermal modules like ACPI) or of all
+frequency changes (like timing code) or even need to force certain
+speed limits (like LCD drivers on ARM architecture). Additionally, the
+kernel "constant" loops_per_jiffy is updated on frequency changes
+here.
+
+
+3.2 CPUFreq notifiers
+***********************
+
+CPUFreq notifiers conform to the standard kernel notifier interface.
+See linux/include/linux/notifier.h for details on notifiers.
+
+There are two different CPUFreq notifiers - policy notifiers and
+transition notifiers.
+
+
+3.2.1 CPUFreq policy notifiers
+******************************
+
+These are notified when a new policy is intended to be set. Each
+CPUFreq policy notifier is called three times for a policy transition:
+
+1.) During CPUFREQ_ADJUST all CPUFreq notifiers may change the limit if
+ they see a need for this - may it be thermal considerations or
+ hardware limitations.
+
+2.) During CPUFREQ_INCOMPATIBLE only changes may be done in order to avoid
+ hardware failure.
+
+3.) And during CPUFREQ_NOTIFY all notifiers are informed of the new policy
+ - if two hardware drivers failed to agree on a new policy before this
+ stage, the incompatible hardware shall be shut down, and the user
+ informed of this.
+
+The phase is specified in the second argument to the notifier.
+
+The third argument, a void *pointer, points to a struct cpufreq_policy
+consisting of five values: cpu, min, max, policy and max_cpu_freq. Min
+and max are the lower and upper frequencies (in kHz) of the new
+policy, policy the new policy, cpu the number of the affected CPU or
+CPUFREQ_ALL_CPUS for all CPUs; and max_cpu_freq the maximum supported
+CPU frequency. This value is given for informational purposes only.
+
+
+3.2.2 CPUFreq transition notifiers
+**********************************
+
+These are notified twice when the CPUfreq driver switches the CPU core
+frequency and this change has any external implications.
+
+The second argument specifies the phase - CPUFREQ_PRECHANGE or
+CPUFREQ_POSTCHANGE.
+
+The third argument is a struct cpufreq_freqs with the following
+values:
+cpu - number of the affected CPU or CPUFREQ_ALL_CPUS
+old - old frequency
+new - new frequency
+
+
+3.3 CPUFreq architecture drivers
+**********************************
+
+CPUFreq architecture drivers are the pieces of kernel code that
+actually perform CPU frequency transitions. These need to be
+initialized separately (separate initcalls), and may be
+modularized. They interact with the CPUFreq core in the following way:
+
+cpufreq_register()
+------------------
+cpufreq_register registers an arch driver to the CPUFreq core. Please
+note that only one arch driver may be registered at any time. -EBUSY
+is returned when an arch driver is already registered. The argument to
+cpufreq_register, struct cpufreq_driver *driver, is described later.
+
+cpufreq_unregister()
+--------------------
+cpufreq_unregister unregisters an arch driver, e.g. on module
+unloading. Please note that there is no check done that this is called
+from the driver which actually registered itself to the core, so
+please only call this function when you are sure the arch driver got
+registered correctly before.
+
+cpufreq_notify_transition()
+---------------------------
+On "dumb" hardware where only fixed frequency can be set, the driver
+must call cpufreq_notify_transition() once before, and once after the
+actual transition.
+
+struct cpufreq_driver
+---------------------
+On initialization, the arch driver is supposed to pass a pointer
+to a struct cpufreq_driver *cpufreq_driver consisting of the following
+entries:
+
+cpufreq_verify_t verify: This is a pointer to a function with the
+ following definition:
+ void verify_function (struct cpufreq_policy *policy).
+ This function must verify the new policy is within the limits
+ supported by the CPU, and at least one supported CPU is within
+ this range. It may be useful to use cpufreq.h /
+ cpufreq_verify_within_limits for this.
+
+cpufreq_setpolicy_t setpolicy: This is a pointer to a function with
+ the following definition:
+ void setpolicy_function (struct cpufreq_policy *policy).
+ This function must set the CPU to the new policy. If it is a
+ "dumb" CPU which only allows fixed frequencies to be set, it
+ shall set it to the lowest within the limit for
+ CPUFREQ_POLICY_POWERSAVE, and to the highest for
+ CPUFREQ_POLICY_PERFORMANCE. Once CONFIG_CPU_FREQ_DYNAMIC is
+ implemented, it can use a dynamic method to adjust the speed
+ between the lower and upper limit.
+
+struct cpufreq_policy *policy: This is an array of NR_CPUS struct
+ cpufreq_policies, containing the current policies set for these
+ CPUs. Note that policy[0].max_cpu_freq must contain the
+ absolute maximum CPU frequency supported by _all_ CPUs.
+
+In case the driver is expected to run with the 2.4.-style API
+(/proc/sys/cpu/.../), two more values must be passed
+#ifdef CONFIG_CPU_FREQ_24_API
+ unsigned int cpu_min_freq;
+ unsigned int cpu_cur_freq[NR_CPUS];
+#endif
+ with cpu_min_freq being the minimum CPU frequency supported by
+ the CPUs; and the entries in cpu_cur_freq reflecting the
+ current speed of the appropriate CPU.
+
+Some Requirements to CPUFreq architecture drivers
+-------------------------------------------------
+* Only call cpufreq_register() when the ability to switch CPU
+ frequencies is _verified_ or can't be missing
+* cpufreq_unregister() may only be called if cpufreq_register() has
+ been successfully(!) called before.
+* kfree() the struct cpufreq_driver only after the call to
+ cpufreq_unregister(), unless cpufreq_register() failed.
+* Be aware that there is currently no error management in the
+ setpolicy() code in the CPUFreq core. So only call yourself a
+ cpufreq_driver if you are really a working cpufreq_driver!
+
+
+
+4. Mailing list and Links
+*************************
+
+
+Mailing List
+------------
+There is a CPU frequency changing CVS commit and general list where
+you can report bugs, problems or submit patches. To post a message,
+send an email to cpufreq@www.linux.org.uk, to subscribe go to
+http://www.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the
+mailing list are available to subscribers at
+http://www.linux.org.uk/mailman/private/cpufreq/.
+
+
+Links
+-----
+the FTP archives:
+* ftp://ftp.linux.org.uk/pub/linux/cpufreq/
+
+how to access the CVS repository:
+* http://cvs.arm.linux.org.uk/
+
+the CPUFreq Mailing list:
+* http://www.linux.org.uk/mailman/listinfo/cpufreq
+
+Clock and voltage scaling for the SA-1100:
+* http://www.lart.tudelft.nl/projects/scaling
+
+CPUFreq project homepage
+* http://www.brodo.de/cpufreq/
diff -uNr linux-2.4.22/Documentation/laptop-mode.sh linux-2.4.22-ben2/Documentation/laptop-mode.sh
--- linux-2.4.22/Documentation/laptop-mode.sh 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/Documentation/laptop-mode.sh 2003-08-31 11:38:48.000000000 +0200
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# start of stop laptop mode, best run by a power management daemon when
+# ac gets connected/disconnected from a laptop
+#
+# FIXME: assumes HZ == 100
+
+# age time, in seconds. should be put into a sysconfig file
+MAX_AGE=600
+
+# kernel default dirty buffer age
+DEF_AGE=30
+DEF_UPDATE=5
+
+if [ ! -w /proc/sys/vm/laptop_mode ]; then
+ echo "Kernel is not patched with laptop_mode patch"
+ exit 1
+fi
+
+case "$1" in
+ start)
+ AGE=$((100*$MAX_AGE))
+ echo -n "Starting laptop mode"
+ echo "1" > /proc/sys/vm/laptop_mode
+ echo "30 500 0 0 $AGE $AGE 60 20 0" > /proc/sys/vm/bdflush
+ echo "."
+ ;;
+ stop)
+ U_AGE=$((100*$DEF_UPDATE))
+ B_AGE=$((100*$DEF_AGE))
+ echo -n "Stopping laptop mode"
+ echo "0" > /proc/sys/vm/laptop_mode
+ echo "30 500 0 0 $U_AGE $B_AGE 60 20 0" > /proc/sys/vm/bdflush
+ echo "."
+ ;;
+ *)
+ echo "$0 {start|stop}"
+ ;;
+
+esac
+
+exit 0
diff -uNr linux-2.4.22/Documentation/laptop-mode.txt linux-2.4.22-ben2/Documentation/laptop-mode.txt
--- linux-2.4.22/Documentation/laptop-mode.txt 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/Documentation/laptop-mode.txt 2003-08-31 11:38:51.000000000 +0200
@@ -0,0 +1,72 @@
+Laptop mode
+===========
+
+This small doc describes the 2.4 laptop mode patch.
+
+Last updated 2003-05-25, Jens Axboe
+
+Introduction
+------------
+
+A few properties of the Linux vm makes it virtually impossible to attempt
+to spin down the hard drive in a laptop for a longer period of time (more
+than a handful of seconds). This means you are lucky if you can even reach
+the break even point with regards to power consumption, let alone expect any
+decrease.
+
+One problem is the age time of dirty buffers. Linux uses 30 seconds per
+default, so if you dirty any data then flusing of that data will commence
+at most 30 seconds from then. Another is the journal commit interval of
+journalled file systems such as ext3, which is 5 seconds on a stock kernel.
+Both of these are tweakable either from proc/sysctl or as mount options
+though, and thus partly solvable from user space.
+
+The kernel update daemon (kupdated) also runs at specific intervals, flushing
+old dirty data out. Default is every 5 seconds, this too can be tweaked
+from sysctl.
+
+So what does the laptop mode patch do? It attempts to fully utilize the
+hard drive once it has been spun up, flushing the old dirty data out to
+disk. Instead of flushing just the expired data, it will clean everything.
+When a read causes the disk to spin up, we kick off this flushing after
+a few seconds. This means that once the disk spins down again, everything
+is up to date. That allows longer dirty data and journal expire times.
+
+It follows that you have to set long expire times to get long spin downs.
+This means you could potentially loose 10 minutes worth of data, if you
+set a 10 minute expire count instead of just 30 seconds worth. The biggest
+risk here is undoubtedly running out of battery.
+
+Settings
+--------
+
+The main knob is /proc/sys/vm/laptop mode. Setting that to 1 switches the
+vm (and block layer) to laptop mode. Leaving it to 0 makes the kernel work
+like before. When in laptop mode, you also want to extend the intervals
+desribed above. See the laptop-mode.sh script for how to do that.
+
+It can happen that the disk still keeps spinning up and you don't quite
+know why or what causes it. The laptop mode patch has a little helper for
+that as well, /proc/sys/vm/block-dump. When set to 1, it will dump info to
+the kernel message buffer about what process caused the io. Be very careful
+when playing with this setting, it is advisable to shut down syslog first!
+
+Result
+------
+
+Using the laptop-mode.sh script with its default settings, I get the full
+10 minutes worth of drive spin down. Provided your work load is cached,
+the disk will only spin up every 10 minutes (well actually, 9 minutes and 55
+seconds due to the 5 second delay in flushing dirty data after the last read
+completes). I can't tell you exactly how much extra battery life you will
+gain in laptop mode, it will vary greatly on the laptop and workload in
+question. The only way to know for sure is to try it out. Getting 10% extra
+battery life is not unrealistic.
+
+Notes
+-----
+
+Patch only changes journal expire time for ext3. reiserfs uses a hardwire
+value, should be trivial to adapt though (basically just make it call
+get_buffer_flushtime() and uses that). I have not looked at other
+journalling file systems, I'll happily accept patches to rectify that!
diff -uNr linux-2.4.22/MAINTAINERS linux-2.4.22-ben2/MAINTAINERS
--- linux-2.4.22/MAINTAINERS 2003-08-25 13:44:39.000000000 +0200
+++ linux-2.4.22-ben2/MAINTAINERS 2003-08-31 11:38:57.000000000 +0200
@@ -759,6 +759,12 @@
L: linux-kernel@vger.kernel.org
S: Maintained
+HFSPLUS FILESYSTEM
+P: Brad Boyer
+M: flar@allandria.com
+W: http://sourceforge.net/projects/linux-hfsplus/
+S: Maintained
+
HGA FRAMEBUFFER DRIVER
P: Ferenc Bakonyi
M: fero@drama.obuda.kando.hu
@@ -1309,7 +1315,7 @@
NVIDIA (RIVA) FRAMEBUFFER DRIVER
P: Ani Joshi
M: ajoshi@kernel.crashing.org
-L: linux-nvidia@lists.surfsouth.com
+L: linux-fbdev-devel@lists.sourceforge.net
S: Maintained
OLYMPIC NETWORK DRIVER
diff -uNr linux-2.4.22/Makefile linux-2.4.22-ben2/Makefile
--- linux-2.4.22/Makefile 2003-08-25 13:44:44.000000000 +0200
+++ linux-2.4.22-ben2/Makefile 2003-08-31 11:41:44.000000000 +0200
@@ -1,7 +1,7 @@
VERSION = 2
PATCHLEVEL = 4
SUBLEVEL = 22
-EXTRAVERSION =
+EXTRAVERSION = -ben2
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
diff -uNr linux-2.4.22/README.BENH linux-2.4.22-ben2/README.BENH
--- linux-2.4.22/README.BENH 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/README.BENH 2003-08-31 11:38:52.000000000 +0200
@@ -0,0 +1,23 @@
+Welcome to my PowerMac tree.
+
+In addition to the standard feature set of kernel.org's 2.4.22, this
+tree includes small fixes that didn't make it into 2.4.21, including all
+of what was present as of 2.4.21-ben3. Some of the major additions are:
+
+ - Laptop mode patch (Jens Axboe). See Documentation/laptop_mode.sh script
+ - Andrea Arcangeli's silent-stack-overflow patch
+ - CPU Frequency switching support on some laptops
+ - Support for UniNorth AGP in the agpgart driver (though it's strongly
+ recommended that you use Michel's Danzer DRM module for that to work
+ properly)
+ - Support for blinking the laptop LED on internal HD activity
+ (Jens and me)
+ - Improved support for lba48 capable disks (Jens Axboe)
+ - Updated rivafb with support for more cards & eMac
+ - Updated sungem driver, supports more chips & recent PHYs
+ - Updated dmasound driver to support tumbler & snapper
+ - Add reporting of OF device path of IDE interfaces in /proc/ide
+ - Fixes for CompactFlash cards
+ - Fixes to vmlinux.coff oldworld wrapper
+ - Better TB sync code for 2 CPU machines from Samuel Rydth
+
diff -uNr linux-2.4.22/arch/alpha/mm/fault.c linux-2.4.22-ben2/arch/alpha/mm/fault.c
--- linux-2.4.22/arch/alpha/mm/fault.c 2002-11-29 00:53:08.000000000 +0100
+++ linux-2.4.22-ben2/arch/alpha/mm/fault.c 2003-08-31 11:38:47.000000000 +0200
@@ -124,7 +124,7 @@
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- if (expand_stack(vma, address))
+ if (expand_stack(vma, address, NULL))
goto bad_area;
/*
* Ok, we have a good vm_area for this memory access, so
diff -uNr linux-2.4.22/arch/arm/mm/fault-common.c linux-2.4.22-ben2/arch/arm/mm/fault-common.c
--- linux-2.4.22/arch/arm/mm/fault-common.c 2003-08-25 13:44:39.000000000 +0200
+++ linux-2.4.22-ben2/arch/arm/mm/fault-common.c 2003-08-31 11:42:37.000000000 +0200
@@ -254,7 +254,7 @@
goto survive;
check_stack:
- if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr))
+ if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr, NULL))
goto good_area;
out:
return fault;
diff -uNr linux-2.4.22/arch/cris/mm/fault.c linux-2.4.22-ben2/arch/cris/mm/fault.c
--- linux-2.4.22/arch/cris/mm/fault.c 2003-08-25 13:44:39.000000000 +0200
+++ linux-2.4.22-ben2/arch/cris/mm/fault.c 2003-08-31 11:41:07.000000000 +0200
@@ -323,7 +323,7 @@
if (address + PAGE_SIZE < rdusp())
goto bad_area;
}
- if (expand_stack(vma, address))
+ if (expand_stack(vma, address, NULL))
goto bad_area;
/*
diff -uNr linux-2.4.22/arch/i386/mm/fault.c linux-2.4.22-ben2/arch/i386/mm/fault.c
--- linux-2.4.22/arch/i386/mm/fault.c 2002-11-29 00:53:09.000000000 +0100
+++ linux-2.4.22-ben2/arch/i386/mm/fault.c 2003-08-31 11:40:24.000000000 +0200
@@ -32,7 +32,7 @@
*/
int __verify_write(const void * addr, unsigned long size)
{
- struct vm_area_struct * vma;
+ struct vm_area_struct * vma, * prev_vma;
unsigned long start = (unsigned long) addr;
if (!size)
@@ -78,7 +78,8 @@
check_stack:
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- if (expand_stack(vma, start) == 0)
+ find_vma_prev(current->mm, start, &prev_vma);
+ if (expand_stack(vma, start, prev_vma) == 0)
goto good_area;
bad_area:
@@ -141,7 +142,7 @@
{
struct task_struct *tsk;
struct mm_struct *mm;
- struct vm_area_struct * vma;
+ struct vm_area_struct * vma, * prev_vma;
unsigned long address;
unsigned long page;
unsigned long fixup;
@@ -202,7 +203,8 @@
if (address + 32 < regs->esp)
goto bad_area;
}
- if (expand_stack(vma, address))
+ find_vma_prev(mm, address, &prev_vma);
+ if (expand_stack(vma, address, prev_vma))
goto bad_area;
/*
* Ok, we have a good vm_area for this memory access, so
diff -uNr linux-2.4.22/arch/ia64/mm/fault.c linux-2.4.22-ben2/arch/ia64/mm/fault.c
--- linux-2.4.22/arch/ia64/mm/fault.c 2003-08-25 13:44:39.000000000 +0200
+++ linux-2.4.22-ben2/arch/ia64/mm/fault.c 2003-08-31 11:38:47.000000000 +0200
@@ -159,7 +159,7 @@
if (rgn_index(address) != rgn_index(vma->vm_start)
|| rgn_offset(address) >= RGN_MAP_LIMIT)
goto bad_area;
- if (expand_stack(vma, address))
+ if (expand_stack(vma, address, NULL))
goto bad_area;
} else {
vma = prev_vma;
diff -uNr linux-2.4.22/arch/m68k/mm/fault.c linux-2.4.22-ben2/arch/m68k/mm/fault.c
--- linux-2.4.22/arch/m68k/mm/fault.c 2002-11-29 00:53:09.000000000 +0100
+++ linux-2.4.22-ben2/arch/m68k/mm/fault.c 2003-08-31 11:40:10.000000000 +0200
@@ -120,7 +120,7 @@
if (address + 256 < rdusp())
goto map_err;
}
- if (expand_stack(vma, address))
+ if (expand_stack(vma, address, NULL))
goto map_err;
/*
diff -uNr linux-2.4.22/arch/mips/mm/fault.c linux-2.4.22-ben2/arch/mips/mm/fault.c
--- linux-2.4.22/arch/mips/mm/fault.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/mips/mm/fault.c 2003-08-31 11:40:20.000000000 +0200
@@ -114,7 +114,7 @@
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- if (expand_stack(vma, address))
+ if (expand_stack(vma, address, NULL))
goto bad_area;
/*
* Ok, we have a good vm_area for this memory access, so
diff -uNr linux-2.4.22/arch/mips64/mm/fault.c linux-2.4.22-ben2/arch/mips64/mm/fault.c
--- linux-2.4.22/arch/mips64/mm/fault.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/mips64/mm/fault.c 2003-08-31 11:42:22.000000000 +0200
@@ -137,7 +137,7 @@
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- if (expand_stack(vma, address))
+ if (expand_stack(vma, address, NULL))
goto bad_area;
/*
* Ok, we have a good vm_area for this memory access, so
diff -uNr linux-2.4.22/arch/ppc/boot/pmac/coffmain.c linux-2.4.22-ben2/arch/ppc/boot/pmac/coffmain.c
--- linux-2.4.22/arch/ppc/boot/pmac/coffmain.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/boot/pmac/coffmain.c 2003-08-31 11:41:05.000000000 +0200
@@ -18,6 +18,7 @@
extern char _start, _end;
extern char *claim(unsigned, unsigned, unsigned);
+extern int map(unsigned, unsigned, unsigned);
extern char image_data[], initrd_data[];
extern int initrd_len, image_len;
extern int getprop(void *, const char *, void *, int);
@@ -34,16 +35,19 @@
char *begin_avail, *end_avail;
char *avail_high;
-#define RAM_START 0
-#define RAM_END (RAM_START + 0x800000) /* only 8M mapped with BATs */
-
-#define PROG_START RAM_START
-#define PROG_SIZE 0x00400000
-
#define SCRATCH_SIZE (128 << 10)
static char heap[SCRATCH_SIZE];
+//static unsigned long ram_start = 0;
+//static unsigned long ram_end = 0x800000;
+//static unsigned long prog_start = 0;
+//static unsigned long prog_size = 0x380000;
+
+static unsigned long ram_start = 0;
+static unsigned long ram_end = 0x1000000;
+static unsigned long prog_start = 0x800000;
+static unsigned long prog_size = 0x800000;
typedef void (*kernel_start_t)(int, int, void *);
void boot(int a1, int a2, void *prom)
@@ -54,32 +58,34 @@
unsigned initrd_start, initrd_size;
printf("coffboot starting: loaded at 0x%p\n", &_start);
- setup_bats(RAM_START);
+ setup_bats(ram_start);
initrd_size = (char *)(&__ramdisk_end) - (char *)(&__ramdisk_begin);
if (initrd_size) {
- initrd_start = (RAM_END - initrd_size) & ~0xFFF;
+ initrd_start = (ram_end - initrd_size) & ~0xFFF;
a1 = initrd_start;
a2 = initrd_size;
- claim(initrd_start, RAM_END - initrd_start, 0);
+ claim(initrd_start, ram_end - initrd_start, 0);
printf("initial ramdisk moving 0x%x <- 0x%p (%x bytes)\n\r",
initrd_start, (char *)(&__ramdisk_begin), initrd_size);
memcpy((char *)initrd_start, (char *)(&__ramdisk_begin), initrd_size);
+ prog_size = initrd_start - prog_start;
} else
a2 = 0xdeadbeef;
im = (char *)(&__image_begin);
len = (char *)(&__image_end) - (char *)(&__image_begin);
/* claim 4MB starting at 0 */
- claim(0, PROG_SIZE, 0);
- dst = (void *) RAM_START;
+ claim(prog_start, prog_size, 0);
+ map(prog_start, prog_start, prog_size);
+ dst = (void *) prog_start;
if (im[0] == 0x1f && im[1] == 0x8b) {
/* set up scratch space */
begin_avail = avail_high = avail_ram = heap;
end_avail = heap + sizeof(heap);
printf("heap at 0x%p\n", avail_ram);
printf("gunzipping (0x%p <- 0x%p:0x%p)...", dst, im, im+len);
- gunzip(dst, PROG_SIZE, im, &len);
+ gunzip(dst, prog_size, im, &len);
printf("done %u bytes\n", len);
printf("%u bytes of heap consumed, max in use %u\n",
avail_high - begin_avail, heap_max);
@@ -89,9 +95,9 @@
flush_cache(dst, len);
make_bi_recs(((unsigned long) dst + len), "coffboot", _MACH_Pmac,
- (PROG_START + PROG_SIZE));
+ (prog_start + prog_size));
- sa = (unsigned long)PROG_START;
+ sa = (unsigned long)prog_start;
printf("start address = 0x%x\n", sa);
(*(kernel_start_t)sa)(a1, a2, prom);
diff -uNr linux-2.4.22/arch/ppc/boot/pmac/misc.S linux-2.4.22-ben2/arch/ppc/boot/pmac/misc.S
--- linux-2.4.22/arch/ppc/boot/pmac/misc.S 2003-06-13 16:51:31.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/boot/pmac/misc.S 2003-08-31 11:38:53.000000000 +0200
@@ -9,7 +9,7 @@
.text
/*
- * Use the BAT3 registers to map the 1st 8MB of RAM to
+ * Use the BAT2 & 3 registers to map the 1st 16MB of RAM to
* the address given as the 1st argument.
*/
.globl setup_bats
@@ -22,6 +22,10 @@
mtibatl 3,0 /* invalidate BAT first */
ori 3,3,4 /* set up BAT registers for 601 */
li 4,0x7f
+ mtibatu 2,3
+ mtibatl 2,4
+ oris 3,3,0x80
+ oris 4,4,0x80
mtibatu 3,3
mtibatl 3,4
b 5f
@@ -29,6 +33,12 @@
mtibatu 3,0
ori 3,3,0xff /* set up BAT registers for 604 */
li 4,2
+ mtdbatl 2,4
+ mtdbatu 2,3
+ mtibatl 2,4
+ mtibatu 2,3
+ oris 3,3,0x80
+ oris 4,4,0x80
mtdbatl 3,4
mtdbatu 3,3
mtibatl 3,4
diff -uNr linux-2.4.22/arch/ppc/boot/pmac/start.c linux-2.4.22-ben2/arch/ppc/boot/pmac/start.c
--- linux-2.4.22/arch/ppc/boot/pmac/start.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/boot/pmac/start.c 2003-08-31 11:40:15.000000000 +0200
@@ -11,12 +11,13 @@
extern int strlen(const char *s);
extern void boot(int a1, int a2, void *prom);
-int (*prom)(void *args);
+int (*prom)(void *);
void *chosen_handle;
void *stdin;
void *stdout;
void *stderr;
+void *prom_mmu;
void exit(void);
void *finddevice(const char *name);
@@ -35,6 +36,8 @@
stderr = stdout;
if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4)
exit();
+ if (getprop(chosen_handle, "mmu", &prom_mmu, sizeof(prom_mmu)) != 4)
+ exit();
boot(a1, a2, promptr);
for (;;)
@@ -195,6 +198,41 @@
}
int
+map(unsigned int phys, unsigned int virt, unsigned int size)
+{
+ struct prom_args {
+ char *service;
+ int nargs;
+ int nret;
+ char *method;
+ void *mmu_ihandle;
+ int misc;
+ unsigned int phys;
+ unsigned int virt;
+ unsigned int size;
+ int ret0;
+ int ret1;
+ } args;
+
+ if (prom_mmu == 0) {
+ printk("map() called, no MMU found\n");
+ return -1;
+ }
+ args.service = "call-method";
+ args.nargs = 6;
+ args.nret = 2;
+ args.method = "map";
+ args.mmu_ihandle = prom_mmu;
+ args.misc = -1;
+ args.phys = phys;
+ args.virt = virt;
+ args.size = size;
+ (*prom)(&args);
+
+ return (int)args.ret0;
+}
+
+int
getprop(void *phandle, const char *name, void *buf, int buflen)
{
struct prom_args {
@@ -330,7 +368,7 @@
}
int
-printf(char *fmt, ...)
+printf(const char *fmt, ...)
{
va_list args;
int n;
diff -uNr linux-2.4.22/arch/ppc/config.in linux-2.4.22-ben2/arch/ppc/config.in
--- linux-2.4.22/arch/ppc/config.in 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/config.in 2003-08-31 11:38:44.000000000 +0200
@@ -53,6 +53,17 @@
define_bool CONFIG_PPC_STD_MMU y
fi
+bool 'CPU Frequency scaling' CONFIG_CPU_FREQ
+if [ "$CONFIG_CPU_FREQ" = "y" ]; then
+ bool ' /proc/sys/cpu/ interface (2.4.)' CONFIG_CPU_FREQ_24_API
+ if [ "$CONFIG_CPU_FREQ_24_API" = "n" ]; then
+ define_bool CONFIG_CPU_FREQ_26_API y
+ fi
+ if [ "$CONFIG_ADB_PMU" = "y" ]; then
+ bool " Support for Apple PowerBooks" CONFIG_CPU_FREQ_PMAC
+ fi
+fi
+
if [ "$CONFIG_8260" = "y" ]; then
define_bool CONFIG_SERIAL_CONSOLE y
bool 'Support for EST8260' CONFIG_EST8260
diff -uNr linux-2.4.22/arch/ppc/configs/briq_defconfig linux-2.4.22-ben2/arch/ppc/configs/briq_defconfig
--- linux-2.4.22/arch/ppc/configs/briq_defconfig 2003-06-13 16:51:31.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/configs/briq_defconfig 2003-08-31 11:39:48.000000000 +0200
@@ -1,5 +1,5 @@
#
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
#
# CONFIG_UID16 is not set
# CONFIG_RWSEM_GENERIC_SPINLOCK is not set
@@ -30,6 +30,7 @@
# CONFIG_8xx is not set
# CONFIG_8260 is not set
CONFIG_PPC_STD_MMU=y
+# CONFIG_CPU_FREQ is not set
CONFIG_ALL_PPC=y
# CONFIG_APUS is not set
# CONFIG_SPRUCE is not set
@@ -191,10 +192,6 @@
# CONFIG_KHTTPD is not set
# CONFIG_ATM is not set
# CONFIG_VLAN_8021Q is not set
-
-#
-#
-#
# CONFIG_IPX is not set
CONFIG_ATALK=m
@@ -232,10 +229,6 @@
# IDE, ATA and ATAPI Block devices
#
CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
# CONFIG_BLK_DEV_HD_IDE is not set
# CONFIG_BLK_DEV_HD is not set
CONFIG_BLK_DEV_IDEDISK=y
@@ -247,10 +240,6 @@
CONFIG_BLK_DEV_IDEFLOPPY=y
CONFIG_BLK_DEV_IDESCSI=y
# CONFIG_IDE_TASK_IOCTL is not set
-
-#
-# IDE chipset support/bugfixes
-#
# CONFIG_BLK_DEV_CMD640 is not set
# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
# CONFIG_BLK_DEV_ISAPNP is not set
@@ -588,14 +577,6 @@
# Joysticks
#
# CONFIG_INPUT_GAMEPORT is not set
-
-#
-# Input core support is needed for gameports
-#
-
-#
-# Input core support is needed for joysticks
-#
# CONFIG_QIC02_TAPE is not set
# CONFIG_IPMI_HANDLER is not set
# CONFIG_IPMI_PANIC_EVENT is not set
diff -uNr linux-2.4.22/arch/ppc/configs/pmac_defconfig linux-2.4.22-ben2/arch/ppc/configs/pmac_defconfig
--- linux-2.4.22/arch/ppc/configs/pmac_defconfig 2003-06-13 16:51:31.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/configs/pmac_defconfig 2003-08-31 11:38:45.000000000 +0200
@@ -1,5 +1,5 @@
#
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
#
# CONFIG_UID16 is not set
# CONFIG_RWSEM_GENERIC_SPINLOCK is not set
@@ -24,29 +24,32 @@
CONFIG_PPC=y
CONFIG_PPC32=y
CONFIG_6xx=y
-# CONFIG_4xx is not set
+# CONFIG_40x is not set
# CONFIG_POWER3 is not set
# CONFIG_POWER4 is not set
# CONFIG_8xx is not set
# CONFIG_8260 is not set
CONFIG_PPC_STD_MMU=y
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_24_API is not set
+CONFIG_CPU_FREQ_26_API=y
+CONFIG_CPU_FREQ_PMAC=y
CONFIG_ALL_PPC=y
# CONFIG_APUS is not set
# CONFIG_SPRUCE is not set
# CONFIG_LOPEC is not set
+# CONFIG_PPLUS is not set
# CONFIG_PAL4 is not set
# CONFIG_GEMINI is not set
# CONFIG_SMP is not set
CONFIG_ALTIVEC=y
-CONFIG_TAU=y
-# CONFIG_TAU_INT is not set
-# CONFIG_TAU_AVERAGE is not set
+# CONFIG_TAU is not set
CONFIG_PPC_ISATIMER=y
#
# General setup
#
-# CONFIG_HIGHMEM is not set
+CONFIG_HIGHMEM=y
# CONFIG_ISA is not set
# CONFIG_EISA is not set
# CONFIG_SBUS is not set
@@ -76,10 +79,11 @@
# Parallel port support
#
# CONFIG_PARPORT is not set
-CONFIG_GEN_RTC=y
+# CONFIG_GEN_RTC is not set
+CONFIG_PPC_RTC=y
CONFIG_PPC601_SYNC_FIX=y
CONFIG_PROC_DEVICETREE=y
-CONFIG_PPC_RTAS=y
+# CONFIG_PPC_RTAS is not set
# CONFIG_PREP_RESIDUAL is not set
# CONFIG_PROC_PREPRESIDUAL is not set
CONFIG_PPCBUG_NVRAM=y
@@ -99,7 +103,7 @@
#
# Block devices
#
-CONFIG_BLK_DEV_FD=m
+# CONFIG_BLK_DEV_FD is not set
# CONFIG_BLK_DEV_XD is not set
# CONFIG_PARIDE is not set
# CONFIG_BLK_CPQ_DA is not set
@@ -152,10 +156,10 @@
#
CONFIG_IP_NF_CONNTRACK=m
CONFIG_IP_NF_FTP=m
-CONFIG_IP_NF_AMANDA=m
-CONFIG_IP_NF_TFTP=m
+# CONFIG_IP_NF_AMANDA is not set
+# CONFIG_IP_NF_TFTP is not set
CONFIG_IP_NF_IRC=m
-# CONFIG_IP_NF_QUEUE is not set
+CONFIG_IP_NF_QUEUE=m
CONFIG_IP_NF_IPTABLES=m
CONFIG_IP_NF_MATCH_LIMIT=m
CONFIG_IP_NF_MATCH_MAC=m
@@ -163,6 +167,7 @@
CONFIG_IP_NF_MATCH_MARK=m
CONFIG_IP_NF_MATCH_MULTIPORT=m
CONFIG_IP_NF_MATCH_TOS=m
+# CONFIG_IP_NF_MATCH_RECENT is not set
CONFIG_IP_NF_MATCH_ECN=m
CONFIG_IP_NF_MATCH_DSCP=m
CONFIG_IP_NF_MATCH_AH_ESP=m
@@ -181,29 +186,34 @@
CONFIG_IP_NF_NAT_NEEDED=y
CONFIG_IP_NF_TARGET_MASQUERADE=m
CONFIG_IP_NF_TARGET_REDIRECT=m
-CONFIG_IP_NF_NAT_AMANDA=m
# CONFIG_IP_NF_NAT_LOCAL is not set
CONFIG_IP_NF_NAT_SNMP_BASIC=m
CONFIG_IP_NF_NAT_IRC=m
CONFIG_IP_NF_NAT_FTP=m
-CONFIG_IP_NF_NAT_TFTP=m
-# CONFIG_IP_NF_MANGLE is not set
-# CONFIG_IP_NF_TARGET_LOG is not set
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_LOG=m
CONFIG_IP_NF_TARGET_ULOG=m
CONFIG_IP_NF_TARGET_TCPMSS=m
CONFIG_IP_NF_ARPTABLES=m
CONFIG_IP_NF_ARPFILTER=m
+# CONFIG_IP_NF_ARP_MANGLE is not set
CONFIG_IP_NF_COMPAT_IPCHAINS=m
CONFIG_IP_NF_NAT_NEEDED=y
# CONFIG_IP_NF_COMPAT_IPFWADM is not set
# CONFIG_IPV6 is not set
# CONFIG_KHTTPD is not set
-# CONFIG_ATM is not set
+CONFIG_ATM=y
+CONFIG_ATM_CLIP=y
+CONFIG_ATM_CLIP_NO_ICMP=y
+CONFIG_ATM_LANE=m
+CONFIG_ATM_MPOA=m
+CONFIG_ATM_BR2684=m
+# CONFIG_ATM_BR2684_IPFILTER is not set
# CONFIG_VLAN_8021Q is not set
-
-#
-#
-#
# CONFIG_IPX is not set
CONFIG_ATALK=m
@@ -241,10 +251,6 @@
# IDE, ATA and ATAPI Block devices
#
CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
# CONFIG_BLK_DEV_HD_IDE is not set
# CONFIG_BLK_DEV_HD is not set
CONFIG_BLK_DEV_IDEDISK=y
@@ -255,11 +261,7 @@
# CONFIG_BLK_DEV_IDETAPE is not set
CONFIG_BLK_DEV_IDEFLOPPY=y
CONFIG_BLK_DEV_IDESCSI=y
-# CONFIG_IDE_TASK_IOCTL is not set
-
-#
-# IDE chipset support/bugfixes
-#
+CONFIG_IDE_TASK_IOCTL=y
# CONFIG_BLK_DEV_CMD640 is not set
# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
# CONFIG_BLK_DEV_ISAPNP is not set
@@ -272,9 +274,9 @@
CONFIG_IDEDMA_PCI_AUTO=y
# CONFIG_IDEDMA_ONLYDISK is not set
CONFIG_BLK_DEV_IDEDMA=y
-# CONFIG_IDEDMA_PCI_WIP is not set
+CONFIG_IDEDMA_PCI_WIP=y
# CONFIG_BLK_DEV_ADMA100 is not set
-# CONFIG_BLK_DEV_AEC62XX is not set
+CONFIG_BLK_DEV_AEC62XX=y
# CONFIG_BLK_DEV_ALI15X3 is not set
# CONFIG_WDC_ALI15X3 is not set
# CONFIG_BLK_DEV_AMD74XX is not set
@@ -289,10 +291,10 @@
# CONFIG_BLK_DEV_PIIX is not set
# CONFIG_BLK_DEV_NS87415 is not set
# CONFIG_BLK_DEV_OPTI621 is not set
-# CONFIG_BLK_DEV_PDC202XX_OLD is not set
-# CONFIG_PDC202XX_BURST is not set
-CONFIG_BLK_DEV_PDC202XX_NEW=m
-# CONFIG_PDC202XX_FORCE is not set
+CONFIG_BLK_DEV_PDC202XX_OLD=y
+CONFIG_PDC202XX_BURST=y
+CONFIG_BLK_DEV_PDC202XX_NEW=y
+CONFIG_PDC202XX_FORCE=y
# CONFIG_BLK_DEV_RZ1000 is not set
# CONFIG_BLK_DEV_SC1200 is not set
# CONFIG_BLK_DEV_SVWKS is not set
@@ -303,8 +305,10 @@
# CONFIG_BLK_DEV_VIA82CXXX is not set
CONFIG_BLK_DEV_SL82C105=y
CONFIG_BLK_DEV_IDE_PMAC=y
+CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST=y
CONFIG_BLK_DEV_IDEDMA_PMAC=y
CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO=y
+CONFIG_PMU_HD_BLINK=y
CONFIG_BLK_DEV_IDEDMA=y
CONFIG_BLK_DEV_IDEPCI=y
# CONFIG_IDE_CHIPSETS is not set
@@ -322,10 +326,6 @@
# SCSI support
#
CONFIG_SCSI=y
-
-#
-# SCSI support type (disk, tape, CD-ROM)
-#
CONFIG_BLK_DEV_SD=y
CONFIG_SD_EXTRA_DEVS=40
CONFIG_CHR_DEV_ST=y
@@ -334,10 +334,6 @@
CONFIG_BLK_DEV_SR_VENDOR=y
CONFIG_SR_EXTRA_DEVS=2
CONFIG_CHR_DEV_SG=y
-
-#
-# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
-#
# CONFIG_SCSI_DEBUG_QUEUES is not set
# CONFIG_SCSI_MULTI_LUN is not set
CONFIG_SCSI_CONSTANTS=y
@@ -353,7 +349,7 @@
# CONFIG_SCSI_AHA1542 is not set
# CONFIG_SCSI_AHA1740 is not set
# CONFIG_SCSI_AACRAID is not set
-CONFIG_SCSI_AIC7XXX=m
+CONFIG_SCSI_AIC7XXX=y
CONFIG_AIC7XXX_CMDS_PER_DEVICE=253
CONFIG_AIC7XXX_RESET_DELAY_MS=15000
# CONFIG_AIC7XXX_PROBE_EISA_VL is not set
@@ -362,10 +358,6 @@
CONFIG_AIC7XXX_DEBUG_MASK=0
# CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set
# CONFIG_SCSI_AIC79XX is not set
-CONFIG_SCSI_AIC7XXX_OLD=m
-# CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT is not set
-CONFIG_AIC7XXX_OLD_CMDS_PER_DEVICE=8
-CONFIG_AIC7XXX_OLD_PROC_STATS=y
# CONFIG_SCSI_DPT_I2O is not set
CONFIG_SCSI_ADVANSYS=m
# CONFIG_SCSI_IN2000 is not set
@@ -407,7 +399,7 @@
# CONFIG_SCSI_DEBUG is not set
CONFIG_SCSI_MESH=y
CONFIG_SCSI_MESH_SYNC_RATE=5
-CONFIG_SCSI_MESH_RESET_DELAY_MS=500
+CONFIG_SCSI_MESH_RESET_DELAY_MS=4000
CONFIG_SCSI_MAC53C94=y
#
@@ -419,27 +411,16 @@
# IEEE 1394 (FireWire) support (EXPERIMENTAL)
#
CONFIG_IEEE1394=m
-
-#
-# Device Drivers
-#
-
-#
-# Texas Instruments PCILynx requires I2C bit-banging
-#
CONFIG_IEEE1394_OHCI1394=m
-
-#
-# Protocol Drivers
-#
CONFIG_IEEE1394_VIDEO1394=m
CONFIG_IEEE1394_SBP2=m
# CONFIG_IEEE1394_SBP2_PHYS_DMA is not set
CONFIG_IEEE1394_ETH1394=m
-# CONFIG_IEEE1394_DV1394 is not set
+CONFIG_IEEE1394_DV1394=m
CONFIG_IEEE1394_RAWIO=m
# CONFIG_IEEE1394_CMP is not set
# CONFIG_IEEE1394_VERBOSEDEBUG is not set
+CONFIG_IEEE1394_OUI_DB=y
#
# Network device support
@@ -453,7 +434,7 @@
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
-# CONFIG_TUN is not set
+CONFIG_TUN=m
# CONFIG_ETHERTAP is not set
#
@@ -480,6 +461,7 @@
# CONFIG_AMD8111_ETH is not set
# CONFIG_ADAPTEC_STARFIRE is not set
# CONFIG_APRICOT is not set
+# CONFIG_B44 is not set
# CONFIG_CS89x0 is not set
CONFIG_TULIP=y
# CONFIG_TULIP_MWI is not set
@@ -507,7 +489,6 @@
# CONFIG_SUNDANCE is not set
# CONFIG_SUNDANCE_MMIO is not set
# CONFIG_TLAN is not set
-# CONFIG_TC35815 is not set
# CONFIG_VIA_RHINE is not set
# CONFIG_VIA_RHINE_MMIO is not set
# CONFIG_WINBOND_840 is not set
@@ -537,6 +518,7 @@
CONFIG_PPP_DEFLATE=y
CONFIG_PPP_BSDCOMP=m
# CONFIG_PPPOE is not set
+# CONFIG_PPPOATM is not set
# CONFIG_SLIP is not set
#
@@ -554,10 +536,7 @@
CONFIG_APPLE_AIRPORT=m
# CONFIG_PLX_HERMES is not set
CONFIG_PCI_HERMES=m
-
-#
-# Wireless Pcmcia cards support
-#
+# CONFIG_TMD_HERMES is not set
CONFIG_PCMCIA_HERMES=m
# CONFIG_AIRO_CS is not set
CONFIG_NET_WIRELESS=y
@@ -598,6 +577,22 @@
# CONFIG_AIRONET4500_CS is not set
#
+# ATM drivers
+#
+# CONFIG_ATM_TCP is not set
+# CONFIG_ATM_LANAI is not set
+# CONFIG_ATM_ENI is not set
+# CONFIG_ATM_FIRESTREAM is not set
+# CONFIG_ATM_ZATM is not set
+# CONFIG_ATM_NICSTAR is not set
+# CONFIG_ATM_IDT77252 is not set
+# CONFIG_ATM_AMBASSADOR is not set
+# CONFIG_ATM_HORIZON is not set
+# CONFIG_ATM_IA is not set
+# CONFIG_ATM_FORE200E_MAYBE is not set
+# CONFIG_ATM_HE is not set
+
+#
# Amateur Radio support
#
# CONFIG_HAMRADIO is not set
@@ -606,18 +601,10 @@
# IrDA (infrared) support
#
CONFIG_IRDA=m
-
-#
-# IrDA protocols
-#
CONFIG_IRLAN=m
CONFIG_IRNET=m
CONFIG_IRCOMM=m
-# CONFIG_IRDA_ULTRA is not set
-
-#
-# IrDA options
-#
+CONFIG_IRDA_ULTRA=y
CONFIG_IRDA_CACHE_LAST_LSAP=y
CONFIG_IRDA_FAST_RR=y
# CONFIG_IRDA_DEBUG is not set
@@ -625,21 +612,9 @@
#
# Infrared-port device drivers
#
-
-#
-# SIR device drivers
-#
CONFIG_IRTTY_SIR=m
-# CONFIG_IRPORT_SIR is not set
-
-#
-# Dongle support
-#
+CONFIG_IRPORT_SIR=m
# CONFIG_DONGLE is not set
-
-#
-# FIR device drivers
-#
# CONFIG_USB_IRDA is not set
# CONFIG_NSC_FIR is not set
# CONFIG_WINBOND_FIR is not set
@@ -652,7 +627,67 @@
#
# ISDN subsystem
#
-# CONFIG_ISDN is not set
+CONFIG_ISDN=m
+CONFIG_ISDN_BOOL=y
+CONFIG_ISDN_PPP=y
+# CONFIG_IPPP_FILTER is not set
+CONFIG_ISDN_PPP_VJ=y
+CONFIG_ISDN_MPP=y
+CONFIG_ISDN_PPP_BSDCOMP=m
+# CONFIG_ISDN_AUDIO is not set
+
+#
+# ISDN feature submodules
+#
+# CONFIG_ISDN_DRV_LOOP is not set
+# CONFIG_ISDN_DIVERSION is not set
+
+#
+# Passive ISDN cards
+#
+CONFIG_ISDN_DRV_HISAX=m
+CONFIG_ISDN_HISAX=y
+# CONFIG_HISAX_EURO is not set
+# CONFIG_HISAX_1TR6 is not set
+# CONFIG_HISAX_NI1 is not set
+CONFIG_HISAX_MAX_CARDS=8
+# CONFIG_HISAX_TELESPCI is not set
+# CONFIG_HISAX_S0BOX is not set
+# CONFIG_HISAX_FRITZPCI is not set
+# CONFIG_HISAX_AVM_A1_PCMCIA is not set
+# CONFIG_HISAX_ELSA is not set
+# CONFIG_HISAX_DIEHLDIVA is not set
+# CONFIG_HISAX_SEDLBAUER is not set
+# CONFIG_HISAX_NETJET is not set
+# CONFIG_HISAX_NETJET_U is not set
+# CONFIG_HISAX_NICCY is not set
+# CONFIG_HISAX_BKM_A4T is not set
+# CONFIG_HISAX_SCT_QUADRO is not set
+CONFIG_HISAX_GAZEL=y
+# CONFIG_HISAX_HFC_PCI is not set
+# CONFIG_HISAX_W6692 is not set
+# CONFIG_HISAX_HFC_SX is not set
+# CONFIG_HISAX_ENTERNOW_PCI is not set
+# CONFIG_HISAX_DEBUG is not set
+# CONFIG_HISAX_SEDLBAUER_CS is not set
+# CONFIG_HISAX_ELSA_CS is not set
+# CONFIG_HISAX_AVM_A1_CS is not set
+CONFIG_HISAX_ST5481=m
+# CONFIG_HISAX_FRITZ_PCIPNP is not set
+# CONFIG_USB_AUERISDN is not set
+
+#
+# Active ISDN cards
+#
+# CONFIG_ISDN_DRV_ICN is not set
+# CONFIG_ISDN_DRV_PCBIT is not set
+# CONFIG_ISDN_DRV_SC is not set
+# CONFIG_ISDN_DRV_ACT2000 is not set
+# CONFIG_ISDN_DRV_EICON is not set
+# CONFIG_ISDN_DRV_TPAM is not set
+# CONFIG_ISDN_CAPI is not set
+# CONFIG_HYSDN is not set
+# CONFIG_HYSDN_CAPI is not set
#
# Old CD-ROM drivers (not SCSI, not IDE)
@@ -669,7 +704,7 @@
#
CONFIG_FB=y
CONFIG_DUMMY_CONSOLE=y
-# CONFIG_FB_RIVA is not set
+CONFIG_FB_RIVA=y
# CONFIG_FB_CLGEN is not set
# CONFIG_FB_PM2 is not set
# CONFIG_FB_PM3 is not set
@@ -682,16 +717,7 @@
CONFIG_FB_IMSTT=y
# CONFIG_FB_S3TRIO is not set
# CONFIG_FB_VGA16 is not set
-CONFIG_FB_MATROX=y
-CONFIG_FB_MATROX_MILLENIUM=y
-CONFIG_FB_MATROX_MYSTIQUE=y
-# CONFIG_FB_MATROX_G450 is not set
-CONFIG_FB_MATROX_G100A=y
-CONFIG_FB_MATROX_G100=y
-# CONFIG_FB_MATROX_I2C is not set
-# CONFIG_FB_MATROX_MAVEN is not set
-CONFIG_FB_MATROX_PROC=y
-# CONFIG_FB_MATROX_MULTIHEAD is not set
+# CONFIG_FB_MATROX is not set
CONFIG_FB_ATY=y
CONFIG_FB_ATY_GX=y
CONFIG_FB_ATY_CT=y
@@ -742,11 +768,11 @@
CONFIG_PMAC_BACKLIGHT=y
CONFIG_MAC_FLOPPY=y
CONFIG_MAC_SERIAL=y
-# CONFIG_SERIAL_CONSOLE is not set
+CONFIG_SERIAL_CONSOLE=y
CONFIG_ADB=y
CONFIG_ADB_MACIO=y
CONFIG_INPUT_ADBHID=y
-CONFIG_MAC_ADBKEYCODES=y
+# CONFIG_MAC_ADBKEYCODES is not set
CONFIG_MAC_EMUMOUSEBTN=y
CONFIG_MAC_HID=y
# CONFIG_ANSLCD is not set
@@ -761,16 +787,17 @@
# CONFIG_SERIAL_NONSTANDARD is not set
CONFIG_UNIX98_PTYS=y
CONFIG_UNIX98_PTY_COUNT=256
+# CONFIG_BRIQ_PANEL is not set
#
# I2C support
#
-CONFIG_I2C=m
+CONFIG_I2C=y
# CONFIG_I2C_ALGOBIT is not set
# CONFIG_I2C_ALGOPCF is not set
-CONFIG_I2C_KEYWEST=m
-CONFIG_I2C_CHARDEV=m
-CONFIG_I2C_PROC=m
+CONFIG_I2C_KEYWEST=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_PROC=y
#
# Mice
@@ -796,10 +823,6 @@
# CONFIG_INPUT_EMU10K1 is not set
# CONFIG_INPUT_SERIO is not set
# CONFIG_INPUT_SERPORT is not set
-
-#
-# Joysticks
-#
# CONFIG_INPUT_ANALOG is not set
# CONFIG_INPUT_A3D is not set
# CONFIG_INPUT_ADI is not set
@@ -842,24 +865,61 @@
# Ftape, the floppy tape device driver
#
# CONFIG_FTAPE is not set
-# CONFIG_AGP is not set
+CONFIG_AGP=m
+# CONFIG_AGP_INTEL is not set
+# CONFIG_AGP_I810 is not set
+# CONFIG_AGP_VIA is not set
+# CONFIG_AGP_AMD is not set
+# CONFIG_AGP_AMD_8151 is not set
+# CONFIG_AGP_SIS is not set
+# CONFIG_AGP_ALI is not set
+# CONFIG_AGP_SWORKS is not set
+CONFIG_AGP_UNINORTH=y
# CONFIG_DRM is not set
#
# PCMCIA character devices
#
-# CONFIG_PCMCIA_SERIAL_CS is not set
+CONFIG_PCMCIA_SERIAL_CS=m
# CONFIG_SYNCLINK_CS is not set
#
# Multimedia devices
#
-# CONFIG_VIDEO_DEV is not set
+CONFIG_VIDEO_DEV=m
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_PROC_FS is not set
+# CONFIG_I2C_PARPORT is not set
+# CONFIG_VIDEO_BT848 is not set
+# CONFIG_VIDEO_PMS is not set
+CONFIG_VIDEO_PLANB=m
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_TUNER_3036 is not set
+# CONFIG_VIDEO_STRADIS is not set
+# CONFIG_VIDEO_ZORAN is not set
+# CONFIG_VIDEO_ZORAN_BUZ is not set
+# CONFIG_VIDEO_ZORAN_DC10 is not set
+# CONFIG_VIDEO_ZORAN_LML33 is not set
+# CONFIG_VIDEO_ZR36120 is not set
+# CONFIG_VIDEO_MEYE is not set
+
+#
+# Radio Adapters
+#
+# CONFIG_RADIO_GEMTEK_PCI is not set
+# CONFIG_RADIO_MAXIRADIO is not set
+# CONFIG_RADIO_MAESTRO is not set
+# CONFIG_RADIO_MIROPCM20 is not set
#
# File systems
#
# CONFIG_QUOTA is not set
+# CONFIG_QFMT_V2 is not set
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
# CONFIG_REISERFS_FS is not set
@@ -869,6 +929,7 @@
# CONFIG_ADFS_FS_RW is not set
# CONFIG_AFFS_FS is not set
CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
# CONFIG_BEFS_FS is not set
# CONFIG_BEFS_DEBUG is not set
# CONFIG_BFS_FS is not set
@@ -897,7 +958,7 @@
# CONFIG_NTFS_RW is not set
# CONFIG_HPFS_FS is not set
CONFIG_PROC_FS=y
-CONFIG_DEVFS_FS=y
+# CONFIG_DEVFS_FS is not set
# CONFIG_DEVFS_MOUNT is not set
# CONFIG_DEVFS_DEBUG is not set
CONFIG_DEVPTS_FS=y
@@ -918,6 +979,7 @@
# CONFIG_INTERMEZZO_FS is not set
CONFIG_NFS_FS=y
# CONFIG_NFS_V3 is not set
+# CONFIG_NFS_DIRECTIO is not set
# CONFIG_ROOT_NFS is not set
CONFIG_NFSD=y
# CONFIG_NFSD_V3 is not set
@@ -963,7 +1025,7 @@
# Native Language Support
#
CONFIG_NLS_DEFAULT="iso8859-1"
-# CONFIG_NLS_CODEPAGE_437 is not set
+CONFIG_NLS_CODEPAGE_437=m
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
# CONFIG_NLS_CODEPAGE_850 is not set
@@ -999,7 +1061,7 @@
# CONFIG_NLS_ISO8859_15 is not set
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
-# CONFIG_NLS_UTF8 is not set
+CONFIG_NLS_UTF8=m
#
# Sound
@@ -1007,8 +1069,6 @@
CONFIG_SOUND=m
CONFIG_DMASOUND_PMAC=m
CONFIG_DMASOUND=m
-CONFIG_I2C=m
-CONFIG_I2C_KEYWEST=m
# CONFIG_SOUND_ALI5455 is not set
# CONFIG_SOUND_BT878 is not set
# CONFIG_SOUND_CMPCI is not set
@@ -1038,82 +1098,56 @@
#
CONFIG_USB=y
# CONFIG_USB_DEBUG is not set
-
-#
-# Miscellaneous USB options
-#
CONFIG_USB_DEVICEFS=y
# CONFIG_USB_BANDWIDTH is not set
-
-#
-# USB Host Controller Drivers
-#
# CONFIG_USB_EHCI_HCD is not set
# CONFIG_USB_UHCI is not set
# CONFIG_USB_UHCI_ALT is not set
CONFIG_USB_OHCI=y
-
-#
-# USB Device Class drivers
-#
-# CONFIG_USB_AUDIO is not set
+CONFIG_USB_AUDIO=m
# CONFIG_USB_EMI26 is not set
# CONFIG_USB_BLUETOOTH is not set
# CONFIG_USB_MIDI is not set
-# CONFIG_USB_STORAGE is not set
-# CONFIG_USB_STORAGE_DEBUG is not set
-# CONFIG_USB_STORAGE_DATAFAB is not set
-# CONFIG_USB_STORAGE_FREECOM is not set
-# CONFIG_USB_STORAGE_ISD200 is not set
-# CONFIG_USB_STORAGE_DPCM is not set
-# CONFIG_USB_STORAGE_HP8200e is not set
-# CONFIG_USB_STORAGE_SDDR09 is not set
-# CONFIG_USB_STORAGE_SDDR55 is not set
-# CONFIG_USB_STORAGE_JUMPSHOT is not set
+CONFIG_USB_STORAGE=m
+CONFIG_USB_STORAGE_DEBUG=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_ISD200=y
+CONFIG_USB_STORAGE_DPCM=y
+CONFIG_USB_STORAGE_HP8200e=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
CONFIG_USB_ACM=m
CONFIG_USB_PRINTER=m
-
-#
-# USB Human Interface Devices (HID)
-#
CONFIG_USB_HID=y
CONFIG_USB_HIDINPUT=y
-# CONFIG_USB_HIDDEV is not set
+CONFIG_USB_HIDDEV=y
# CONFIG_USB_AIPTEK is not set
# CONFIG_USB_WACOM is not set
# CONFIG_USB_KBTAB is not set
# CONFIG_USB_POWERMATE is not set
-
-#
-# USB Imaging devices
-#
# CONFIG_USB_DC2XX is not set
# CONFIG_USB_MDC800 is not set
CONFIG_USB_SCANNER=m
# CONFIG_USB_MICROTEK is not set
# CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-# Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+CONFIG_USB_OV511=m
+CONFIG_USB_PWC=m
+# CONFIG_USB_SE401 is not set
+# CONFIG_USB_STV680 is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_DSBR is not set
+# CONFIG_USB_DABUSB is not set
# CONFIG_USB_PEGASUS is not set
# CONFIG_USB_RTL8150 is not set
# CONFIG_USB_KAWETH is not set
# CONFIG_USB_CATC is not set
-# CONFIG_USB_CDCETHER is not set
+# CONFIG_USB_AX8817X is not set
+CONFIG_USB_CDCETHER=m
# CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
# CONFIG_USB_USS720 is not set
#
@@ -1132,8 +1166,20 @@
# CONFIG_USB_SERIAL_IR is not set
# CONFIG_USB_SERIAL_EDGEPORT is not set
# CONFIG_USB_SERIAL_EDGEPORT_TI is not set
-# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
-# CONFIG_USB_SERIAL_KEYSPAN is not set
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+CONFIG_USB_SERIAL_KEYSPAN_USA28=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19=y
+CONFIG_USB_SERIAL_KEYSPAN_USA18X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y
+CONFIG_USB_SERIAL_KEYSPAN_MPR=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y
# CONFIG_USB_SERIAL_MCT_U232 is not set
# CONFIG_USB_SERIAL_KLSI is not set
# CONFIG_USB_SERIAL_KOBIL_SCT is not set
@@ -1141,15 +1187,12 @@
# CONFIG_USB_SERIAL_CYBERJACK is not set
# CONFIG_USB_SERIAL_XIRCOM is not set
# CONFIG_USB_SERIAL_OMNINET is not set
-
-#
-# USB Miscellaneous drivers
-#
# CONFIG_USB_RIO500 is not set
# CONFIG_USB_AUERSWALD is not set
# CONFIG_USB_TIGL is not set
# CONFIG_USB_BRLVGER is not set
# CONFIG_USB_LCD is not set
+# CONFIG_USB_SPEEDTOUCH is not set
#
# Bluetooth support
@@ -1157,8 +1200,14 @@
# CONFIG_BLUEZ is not set
#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
# Library routines
#
+# CONFIG_CRC32 is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
@@ -1171,7 +1220,6 @@
# CONFIG_DEBUG_SLAB is not set
# CONFIG_DEBUG_WAITQ is not set
# CONFIG_KGDB is not set
-CONFIG_XMON=y
+# CONFIG_XMON is not set
# CONFIG_BDI_SWITCH is not set
-# CONFIG_MORE_COMPILE_OPTIONS is not set
CONFIG_BOOTX_TEXT=y
diff -uNr linux-2.4.22/arch/ppc/kernel/Makefile linux-2.4.22-ben2/arch/ppc/kernel/Makefile
--- linux-2.4.22/arch/ppc/kernel/Makefile 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/Makefile 2003-08-31 11:41:46.000000000 +0200
@@ -45,7 +45,7 @@
obj-$(CONFIG_PCI) += pci-dma.o
obj-$(CONFIG_KGDB) += ppc-stub.o
obj-$(CONFIG_PPCBUG_NVRAM) += prep_nvram.o
-obj-$(CONFIG_SMP) += smp.o
+obj-$(CONFIG_SMP) += smp.o smp-tbsync.o
obj-$(CONFIG_TAU) += temp.o
ifeq ($(CONFIG_SERIAL)$(CONFIG_GEN550),yy)
obj-$(CONFIG_KGDB) += gen550_kgdb.o gen550_dbg.o
diff -uNr linux-2.4.22/arch/ppc/kernel/btext.c linux-2.4.22-ben2/arch/ppc/kernel/btext.c
--- linux-2.4.22/arch/ppc/kernel/btext.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/btext.c 2003-08-31 11:40:56.000000000 +0200
@@ -42,22 +42,14 @@
static unsigned char vga_font[cmapsz];
int boot_text_mapped;
+int force_printk_to_btext;
boot_infos_t disp_bi;
extern char *klimit;
-/*
- * Powermac can use btext_* after boot for xmon,
- * chrp only uses it during early boot.
- */
-#ifdef CONFIG_XMON
-#define BTEXT __pmac
-#define BTDATA __pmacdata
-#else
-#define BTEXT __init
-#define BTDATA __initdata
-#endif /* CONFIG_XMON */
+#define BTEXT
+#define BTDATA
/*
* This is called only when we are booted via BootX.
diff -uNr linux-2.4.22/arch/ppc/kernel/entry.S linux-2.4.22-ben2/arch/ppc/kernel/entry.S
--- linux-2.4.22/arch/ppc/kernel/entry.S 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/entry.S 2003-08-31 11:39:10.000000000 +0200
@@ -258,6 +258,12 @@
addi r1,r1,INT_FRAME_SIZE
blr
+ .globl syscall_direct_return
+syscall_direct_return:
+ addi r1,r3,-STACK_FRAME_OVERHEAD
+ /* XXX should check syscall tracing here */
+ b ret_from_except
+
.globl ret_from_fork
ret_from_fork:
bl schedule_tail
diff -uNr linux-2.4.22/arch/ppc/kernel/head.S linux-2.4.22-ben2/arch/ppc/kernel/head.S
--- linux-2.4.22/arch/ppc/kernel/head.S 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/head.S 2003-08-31 11:39:29.000000000 +0200
@@ -316,10 +316,6 @@
#endif
/* Machine check */
-BEGIN_FTR_SECTION
- DSSALL
- sync
-END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
STD_EXCEPTION(0x200, MachineCheck, MachineCheckException)
/* Data access exception. */
@@ -983,7 +979,9 @@
/* enable use of AltiVec after return */
oris r23,r23,MSR_VEC@h
mfspr r5,SPRG3 /* current task's THREAD (phys) */
+ li r4,1
li r20,THREAD_VSCR
+ stw r4,THREAD_USED_VR(r5)
LVX(vr0,r20,r5)
MTVSCR(vr0)
REST_32VR(0,r20,r5)
diff -uNr linux-2.4.22/arch/ppc/kernel/irq.c linux-2.4.22-ben2/arch/ppc/kernel/irq.c
--- linux-2.4.22/arch/ppc/kernel/irq.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/irq.c 2003-08-31 11:39:07.000000000 +0200
@@ -1,4 +1,4 @@
-/*
+ /*
* arch/ppc/kernel/irq.c
*
* Derived from arch/i386/kernel/irq.c
@@ -75,6 +75,10 @@
unsigned long ppc_lost_interrupts[NR_MASK_WORDS];
atomic_t ppc_n_lost_interrupts;
+#ifdef CONFIG_DEBUG_SPINLOCK
+int debug_long_irqlock;
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
/* nasty hack for shared irq's since we need to do kmalloc calls but
* can't very early in the boot when we need to do a request irq.
* this needs to be removed.
@@ -578,6 +582,24 @@
unsigned long *stack;
int cpu = smp_processor_id();
+#ifdef CONFIG_XMON
+ xmon_printf("\n%s, CPU %d:\n", str, cpu);
+ xmon_printf("irq: %d [%d %d]\n",
+ atomic_read(&global_irq_count),
+ local_irq_count(0),
+ local_irq_count(1));
+ xmon_printf("bh: %d [%d %d]\n",
+ atomic_read(&global_bh_count),
+ local_bh_count(0),
+ local_bh_count(1));
+ stack = (unsigned long *) &str;
+ for (i = 40; i ; i--) {
+ unsigned long x = *++stack;
+ if (x > (unsigned long) &init_task_union && x < (unsigned long) &vsprintf) {
+ xmon_printf("<[%08lx]> ", x);
+ }
+ }
+#endif /* CONFIG_XMON */
printk("\n%s, CPU %d:\n", str, cpu);
printk("irq: %d [%d %d]\n",
atomic_read(&global_irq_count),
@@ -662,6 +684,7 @@
*/
void synchronize_bh(void)
{
+ smp_mb();
if (atomic_read(&global_bh_count) && !in_interrupt())
wait_on_bh();
}
@@ -675,6 +698,7 @@
*/
void synchronize_irq(void)
{
+ smp_mb();
if (atomic_read(&global_irq_count)) {
/* Stupid approach */
cli();
@@ -694,6 +718,9 @@
do {
do {
if (loops-- == 0) {
+#ifdef CONFIG_XMON
+ xmon_printf("get_irqlock(%d) waiting, global_irq_holder=%d\n", cpu, global_irq_holder);
+#endif
printk("get_irqlock(%d) waiting, global_irq_holder=%d\n", cpu, global_irq_holder);
#ifdef CONFIG_XMON
xmon(0);
diff -uNr linux-2.4.22/arch/ppc/kernel/m8xx_setup.c linux-2.4.22-ben2/arch/ppc/kernel/m8xx_setup.c
--- linux-2.4.22/arch/ppc/kernel/m8xx_setup.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/m8xx_setup.c 2003-08-31 11:40:09.000000000 +0200
@@ -43,6 +43,7 @@
#include
#include
#include
+#include
#include "ppc8xx_pic.h"
diff -uNr linux-2.4.22/arch/ppc/kernel/misc.S linux-2.4.22-ben2/arch/ppc/kernel/misc.S
--- linux-2.4.22/arch/ppc/kernel/misc.S 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/misc.S 2003-08-31 11:39:09.000000000 +0200
@@ -200,6 +200,59 @@
mr r4,r24
bctr
+#ifdef CONFIG_CPU_FREQ_PMAC
+
+/* This gets called by via-pmu.c to switch the PLL selection
+ * on 750fx CPU.
+ */
+_GLOBAL(low_choose_750fx_pll)
+ /* Clear MSR:EE */
+ mfmsr r7
+ rlwinm r0,r7,0,17,15
+ mtmsr r0
+
+ /* If switching to PLL1, disable HID0:BTIC */
+ cmpli cr0,r3,0
+ beq 1f
+ mfspr r5,HID0
+ rlwinm r5,r5,0,27,25
+ sync
+ mtspr HID0,r5
+ isync
+ sync
+
+1:
+ /* Calc new HID1 value */
+ mfspr r4,SPRN_HID1 /* Build a HID1:PS bit from parameter */
+ rlwinm r5,r3,16,15,15 /* Clear out HID1:PS from value read */
+ rlwinm r4,r4,0,16,14 /* Could have I used rlwimi here ? */
+ or r4,r4,r5
+ mtspr SPRN_HID1,r4
+
+ /* Store new HID1 image */
+ lwz r6,PROCESSOR(r2)
+ slwi r6,r6,2
+ addis r6,r6,nap_save_hid1@ha
+ stw r4,nap_save_hid1@l(r6)
+
+ /* If switching to PLL0, enable HID0:BTIC */
+ cmpli cr0,r3,0
+ bne 1f
+ mfspr r5,HID0
+ ori r5,r5,HID0_BTIC
+ sync
+ mtspr HID0,r5
+ isync
+ sync
+
+1:
+ /* Return */
+ mtmsr r7
+ blr
+
+#endif /* CONFIG_CPU_FREQ_PMAC */
+
+
/* void __save_flags_ptr(unsigned long *flags) */
_GLOBAL(__save_flags_ptr)
mfmsr r4
@@ -1243,6 +1296,24 @@
.long sys_ni_syscall /* reserved for sys_io_getevents */
.long sys_ni_syscall /* 230 reserved for sys_io_submit */
.long sys_ni_syscall /* reserved for sys_io_cancel */
+ .long sys_ni_syscall /* reserved for sys_set_tid_address */
+ .long sys_ni_syscall /* reserved for sys_fadvise64 */
+ .long sys_ni_syscall /* reserved for sys_exit_group */
+ .long sys_ni_syscall /* 235 reserved for sys_lookup_dcookie */
+ .long sys_ni_syscall /* reserved for sys_epoll_create */
+ .long sys_ni_syscall /* reserved for sys_epoll_ctl */
+ .long sys_ni_syscall /* reserved for sys_epoll_wait */
+ .long sys_ni_syscall /* reserved for sys_remap_file_pages */
+ .long sys_ni_syscall /* 240 reserved for sys_timer_create */
+ .long sys_ni_syscall /* reserved for sys_timer_settime */
+ .long sys_ni_syscall /* reserved for sys_timer_gettime */
+ .long sys_ni_syscall /* reserved for sys_timer_getoverrun */
+ .long sys_ni_syscall /* reserved for sys_timer_delete */
+ .long sys_ni_syscall /* 245 reserved for sys_clock_settime */
+ .long sys_ni_syscall /* reserved for sys_clock_gettime */
+ .long sys_ni_syscall /* reserved for sys_clock_getres */
+ .long sys_ni_syscall /* reserved for sys_clock_nanosleep */
+ .long sys_swapcontext
.rept NR_syscalls-(.-sys_call_table)/4
.long sys_ni_syscall
diff -uNr linux-2.4.22/arch/ppc/kernel/mk_defs.c linux-2.4.22-ben2/arch/ppc/kernel/mk_defs.c
--- linux-2.4.22/arch/ppc/kernel/mk_defs.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/mk_defs.c 2003-08-31 11:40:23.000000000 +0200
@@ -56,6 +56,7 @@
DEFINE(THREAD_VR0, offsetof(struct thread_struct, vr[0]));
DEFINE(THREAD_VRSAVE, offsetof(struct thread_struct, vrsave));
DEFINE(THREAD_VSCR, offsetof(struct thread_struct, vscr));
+ DEFINE(THREAD_USED_VR, offsetof(struct thread_struct, used_vr));
#endif /* CONFIG_ALTIVEC */
#ifdef CONFIG_4xx
DEFINE(THREAD_DBCR0, offsetof(struct thread_struct, dbcr0));
diff -uNr linux-2.4.22/arch/ppc/kernel/open_pic.c linux-2.4.22-ben2/arch/ppc/kernel/open_pic.c
--- linux-2.4.22/arch/ppc/kernel/open_pic.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/open_pic.c 2003-08-31 11:42:01.000000000 +0200
@@ -870,6 +870,17 @@
static u32 save_irq_src_dest[OPENPIC_MAX_SOURCES];
static u32 save_cpu_task_pri[OPENPIC_MAX_PROCESSORS];
+static void openpic_cached_enable_irq(u_int irq)
+{
+ check_arg_irq(irq);
+ save_irq_src_vp[irq - open_pic_irq_offset] &= ~OPENPIC_MASK;
+}
+
+static void openpic_cached_disable_irq(u_int irq)
+{
+ check_arg_irq(irq);
+ save_irq_src_vp[irq - open_pic_irq_offset] |= OPENPIC_MASK;
+}
void __pmac
openpic_sleep_save_intrs(void)
{
@@ -878,6 +889,9 @@
spin_lock_irqsave(&openpic_setup_lock, flags);
+ open_pic.enable = openpic_cached_enable_irq;
+ open_pic.disable = openpic_cached_disable_irq;
+
for (i=0; iProcessor[i].Current_Task_Priority);
openpic_writefield(&OpenPIC->Processor[i].Current_Task_Priority,
@@ -921,6 +935,9 @@
openpic_write(&OpenPIC->Processor[i].Current_Task_Priority,
save_cpu_task_pri[i]);
+ open_pic.enable = openpic_enable_irq;
+ open_pic.disable = openpic_disable_irq;
+
spin_unlock_irqrestore(&openpic_setup_lock, flags);
}
#endif /* CONFIG_PMAC_PBOOK */
diff -uNr linux-2.4.22/arch/ppc/kernel/pci.c linux-2.4.22-ben2/arch/ppc/kernel/pci.c
--- linux-2.4.22/arch/ppc/kernel/pci.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/pci.c 2003-08-31 11:42:37.000000000 +0200
@@ -1,5 +1,5 @@
/*
- * Common pmac/prep/chrp pci routines. -- Cort
+ * Common PCI code for PPC architecture
*/
#include
@@ -22,7 +22,7 @@
#include
#include
-#undef DEBUG
+#define DEBUG
#ifdef DEBUG
#define DBG(x...) printk(x)
@@ -116,6 +116,10 @@
struct pci_controller* hose = dev->sysdata;
unsigned long io_offset;
+ if (dev->vendor == PCI_VENDOR_ID_APPLE && dev->device == PCI_DEVICE_ID_APPLE_KEYLARGO) {
+ printk("trying to reloc keylargo !! skipping\n");
+ return;
+ }
new = res->start;
res->flags &= ~IORESOURCE_UNSET;
if (hose && res->flags & IORESOURCE_IO) {
@@ -217,7 +221,8 @@
}
if (dev->device == PCI_DEVICE_ID_TI_1210 ||
dev->device == PCI_DEVICE_ID_TI_1211 ||
- dev->device == PCI_DEVICE_ID_TI_1410) {
+ dev->device == PCI_DEVICE_ID_TI_1410 ||
+ dev->device == PCI_DEVICE_ID_TI_1510) {
u8 val;
/* 0x8c == TI122X_IRQMUX, 2 says to route the INTA
signal out the MFUNC0 pin */
@@ -563,8 +568,9 @@
{
struct resource *pr, *r = &dev->resource[idx];
- DBG("PCI:%s: Resource %d: %08lx-%08lx (f=%lx)\n",
- dev->slot_name, idx, r->start, r->end, r->flags);
+ DBG("PCI:%s: Resource %d: %08lx-%08lx (f=%lx), vd: %04x, dev: %04x\n",
+ dev->slot_name, idx, r->start, r->end, r->flags,
+ dev->vendor, dev->device);
pr = pci_find_parent_resource(dev, r);
if (!pr || request_resource(pr, r) < 0) {
printk(KERN_ERR "PCI: Cannot allocate resource region %d"
@@ -1029,6 +1035,10 @@
}
ranges += np;
}
+ DBG("hose %s, pci_mem_offset: %08lx, start0: %08lx\n",
+ dev->name, hose->pci_mem_offset, hose->mem_resources[0].start);
+ DBG(" io_base_virt: %p, io_base_phys: %08lx, isa_mem_base: %08lx\n",
+ hose->io_base_virt, hose->io_base_phys, isa_mem_base);
}
/* We create the "pci-OF-bus-map" property now so it appears in the
@@ -1341,6 +1351,9 @@
return PCI_SLOT(dev->devfn);
}
+/* Where does that come from ? Doesn't seem to be correct for us, but we
+ * don't use it anyway so ... -BenH.
+ */
void __init
pcibios_fixup_pbus_ranges(struct pci_bus * bus, struct pbus_set_ranges_data * ranges)
{
diff -uNr linux-2.4.22/arch/ppc/kernel/ppc_ksyms.c linux-2.4.22-ben2/arch/ppc/kernel/ppc_ksyms.c
--- linux-2.4.22/arch/ppc/kernel/ppc_ksyms.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/ppc_ksyms.c 2003-08-31 11:39:15.000000000 +0200
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include
#include
@@ -67,9 +68,10 @@
extern int pmac_newworld;
extern int sys_sigreturn(struct pt_regs *regs);
-long long __ashrdi3(long long, int);
-long long __ashldi3(long long, int);
-long long __lshrdi3(long long, int);
+extern long long __ashrdi3(long long, int);
+extern long long __ashldi3(long long, int);
+extern long long __lshrdi3(long long, int);
+
int abs(int);
extern unsigned char __res[];
@@ -128,7 +130,6 @@
EXPORT_SYMBOL(strcmp);
EXPORT_SYMBOL(strncmp);
EXPORT_SYMBOL(strcasecmp);
-EXPORT_SYMBOL(__div64_32);
/* EXPORT_SYMBOL(csum_partial); already in net/netsyms.c */
EXPORT_SYMBOL(csum_partial_copy_generic);
@@ -192,6 +193,7 @@
EXPORT_SYMBOL(flush_icache_user_range);
EXPORT_SYMBOL(flush_icache_page);
EXPORT_SYMBOL(flush_dcache_page);
+EXPORT_SYMBOL(local_flush_tlb_page);
EXPORT_SYMBOL(xchg_u32);
#ifdef CONFIG_ALTIVEC
EXPORT_SYMBOL(last_task_used_altivec);
@@ -288,6 +290,7 @@
EXPORT_SYMBOL_NOVERS(memscan);
EXPORT_SYMBOL_NOVERS(memcmp);
EXPORT_SYMBOL_NOVERS(memchr);
+EXPORT_SYMBOL_NOVERS(__div64_32);
EXPORT_SYMBOL(abs);
@@ -314,7 +317,7 @@
EXPORT_SYMBOL(get_wchan);
EXPORT_SYMBOL(console_drivers);
#ifdef CONFIG_XMON
-extern void xmon_printf(char *fmt, ...);
+extern void xmon_printf(const char *fmt, ...);
EXPORT_SYMBOL(xmon);
EXPORT_SYMBOL(xmon_printf);
#endif
@@ -367,7 +370,13 @@
EXPORT_SYMBOL(ret_from_intercept);
EXPORT_SYMBOL(cur_cpu_spec);
#if defined(CONFIG_ALL_PPC)
+extern int map_page(unsigned long va, unsigned long pa, int flags);
+
+EXPORT_SYMBOL(map_page);
+EXPORT_SYMBOL(get_vm_area);
extern unsigned long agp_special_page;
EXPORT_SYMBOL_NOVERS(agp_special_page);
+EXPORT_SYMBOL(ioremap_bot);
+EXPORT_SYMBOL(local_flush_tlb_all);
#endif /* defined(CONFIG_ALL_PPC) */
diff -uNr linux-2.4.22/arch/ppc/kernel/process.c linux-2.4.22-ben2/arch/ppc/kernel/process.c
--- linux-2.4.22/arch/ppc/kernel/process.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/process.c 2003-08-31 11:39:02.000000000 +0200
@@ -417,6 +417,7 @@
memset(current->thread.vr, 0, sizeof(current->thread.vr));
memset(¤t->thread.vscr, 0, sizeof(current->thread.vscr));
current->thread.vrsave = 0;
+ current->thread.used_vr = 0;
#endif /* CONFIG_ALTIVEC */
}
@@ -506,14 +507,19 @@
if (sp == NULL)
asm("mr %0,1" : "=r" (sp));
printk("Call backtrace: ");
+ if (sp == NULL)
+ sp = (unsigned long *)_get_SP();
while (sp) {
- if (__get_user( i, &sp[1] ))
+ if (__get_user(sp, (unsigned long **)sp))
+ break;
+ if (sp == NULL)
+ break;
+ if (__get_user(i, &sp[1]))
break;
if (cnt++ % 7 == 0)
printk("\n");
printk("%08lX ", i);
- if (cnt > 32) break;
- if (__get_user(sp, (unsigned long **)sp))
+ if (cnt > 32)
break;
}
printk("\n");
diff -uNr linux-2.4.22/arch/ppc/kernel/semaphore.c linux-2.4.22-ben2/arch/ppc/kernel/semaphore.c
--- linux-2.4.22/arch/ppc/kernel/semaphore.c 2003-06-13 16:51:31.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/semaphore.c 2003-08-31 11:41:33.000000000 +0200
@@ -73,9 +73,8 @@
struct task_struct *tsk = current;
DECLARE_WAITQUEUE(wait, tsk);
- tsk->state = TASK_UNINTERRUPTIBLE;
+ __set_task_state(tsk, TASK_UNINTERRUPTIBLE);
add_wait_queue_exclusive(&sem->wait, &wait);
- smp_wmb();
/*
* Try to get the semaphore. If the count is > 0, then we've
@@ -85,10 +84,10 @@
*/
while (__sem_update_count(sem, -1) <= 0) {
schedule();
- tsk->state = TASK_UNINTERRUPTIBLE;
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
}
remove_wait_queue(&sem->wait, &wait);
- tsk->state = TASK_RUNNING;
+ __set_task_state(tsk, TASK_RUNNING);
/*
* If there are any more sleepers, wake one of them up so
@@ -104,9 +103,8 @@
struct task_struct *tsk = current;
DECLARE_WAITQUEUE(wait, tsk);
- tsk->state = TASK_INTERRUPTIBLE;
+ __set_task_state(tsk, TASK_INTERRUPTIBLE);
add_wait_queue_exclusive(&sem->wait, &wait);
- smp_wmb();
while (__sem_update_count(sem, -1) <= 0) {
if (signal_pending(current)) {
@@ -120,10 +118,11 @@
break;
}
schedule();
- tsk->state = TASK_INTERRUPTIBLE;
+ set_task_state(tsk, TASK_INTERRUPTIBLE);
}
- tsk->state = TASK_RUNNING;
remove_wait_queue(&sem->wait, &wait);
+ __set_task_state(tsk, TASK_RUNNING);
+
wake_up(&sem->wait);
return retval;
}
diff -uNr linux-2.4.22/arch/ppc/kernel/setup.c linux-2.4.22-ben2/arch/ppc/kernel/setup.c
--- linux-2.4.22/arch/ppc/kernel/setup.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/setup.c 2003-08-31 11:41:03.000000000 +0200
@@ -52,6 +52,7 @@
struct ide_machdep_calls ppc_ide_md;
char *sysmap;
unsigned long sysmap_size;
+int no_nap;
/* Used with the BI_MEMSIZE bootinfo parameter to store the memory
size value reported by the boot loader. */
@@ -152,7 +153,7 @@
return 0;
pvr = cpu_data[i].pvr;
lpj = cpu_data[i].loops_per_jiffy;
- seq_printf(m, "processor\t: %lu\n", i);
+ seq_printf(m, "processor\t: %u\n", i);
#else
pvr = mfspr(PVR);
lpj = loops_per_jiffy;
@@ -329,6 +330,7 @@
unsigned long r6, unsigned long r7)
{
#ifdef CONFIG_BOOTX_TEXT
+ extern int force_printk_to_btext;
if (boot_text_mapped) {
btext_clearscreen();
btext_welcome();
@@ -415,6 +417,27 @@
}
cmd_line[sizeof(cmd_line) - 1] = 0;
+ /* Debug stuff, do not merge ! */
+#ifdef CONFIG_ADB_PMU
+ if (strstr(cmd_line, "fake_sleep")) {
+ extern int __fake_sleep;
+ __fake_sleep = 1;
+ }
+#endif /* CONFIG_ADB_PMU */
+#ifdef CONFIG_ADB
+ if (strstr(cmd_line, "adb_sync")) {
+ extern int __adb_probe_sync;
+ __adb_probe_sync = 1;
+ }
+#endif /* CONFIG_ADB */
+ if (strstr(cmd_line, "nol3") && cur_cpu_spec[0]->cpu_features & CPU_FTR_L3CR)
+ _set_L3CR(0);
+ if (strstr(cmd_line, "nonap"))
+ cur_cpu_spec[0]->cpu_features &= ~CPU_FTR_CAN_NAP;
+#ifdef CONFIG_BOOTX_TEXT
+ if (strstr(cmd_line, "printkbtext"))
+ force_printk_to_btext = 1;
+#endif
switch (_machine) {
case _MACH_Pmac:
pmac_init(r3, r4, r5, r6, r7);
diff -uNr linux-2.4.22/arch/ppc/kernel/signal.c linux-2.4.22-ben2/arch/ppc/kernel/signal.c
--- linux-2.4.22/arch/ppc/kernel/signal.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/signal.c 2003-08-31 11:41:12.000000000 +0200
@@ -40,17 +40,7 @@
#define GP_REGS_SIZE MIN(sizeof(elf_gregset_t), sizeof(struct pt_regs))
-/*
- * These are the flags in the MSR that the user is allowed to change
- * by modifying the saved value of the MSR on the stack. SE and BE
- * should not be in this list since gdb may want to change these. I.e,
- * you should be able to step out of a signal handler to see what
- * instruction executes next after the signal handler completes.
- * Alternately, if you stepped into a signal handler, you should be
- * able to continue 'til the next breakpoint from within the signal
- * handler, even if the handler returns.
- */
-#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1)
+extern void syscall_direct_return(struct pt_regs *regs);
int do_signal(sigset_t *oldset, struct pt_regs *regs);
@@ -117,7 +107,7 @@
* do_signal() has set R3 to the signal number (the
* first argument of the signal handler), so don't
* overwrite that with EINTR !
- * In the other cases, do_signal() doesn't touch
+ * In the other cases, do_signal() doesn't touch
* R3, so it's still set to -EINTR (see above).
*/
return regs->gpr[3];
@@ -157,13 +147,13 @@
int
-sys_sigaltstack(const stack_t *uss, stack_t *uoss)
+sys_sigaltstack(const stack_t *uss, stack_t *uoss, int r5, int r6,
+ int r7, int r8, struct pt_regs *regs)
{
- struct pt_regs *regs = (struct pt_regs *) &uss;
return do_sigaltstack(uss, uoss, regs->gpr[1]);
}
-int
+int
sys_sigaction(int sig, const struct old_sigaction *act,
struct old_sigaction *oact)
{
@@ -199,270 +189,303 @@
* When we have signals to deliver, we set up on the
* user stack, going down from the original stack pointer:
* a sigregs struct
- * one or more sigcontext structs with
+ * a sigcontext struct
* a gap of __SIGNAL_FRAMESIZE bytes
*
* Each of these things must be a multiple of 16 bytes in size.
*
*/
struct sigregs {
- elf_gregset_t gp_regs;
- double fp_regs[ELF_NFPREG];
- unsigned long tramp[2];
+ struct mcontext mctx; /* all the register values */
/* Programs using the rs6000/xcoff abi can save up to 19 gp regs
and 18 fp regs below sp before decrementing it. */
int abigap[56];
};
-struct rt_sigframe
-{
- unsigned long _unused[2];
- struct siginfo *pinfo;
- void *puc;
- struct siginfo info;
- struct ucontext uc;
-};
-
+/* We use the mc_pad field for the signal return trampoline. */
+#define tramp mc_pad
/*
* When we have rt signals to deliver, we set up on the
* user stack, going down from the original stack pointer:
- * a sigregs struct
- * one rt_sigframe struct (siginfo + ucontext)
- * a gap of __SIGNAL_FRAMESIZE bytes
+ * one rt_sigframe struct (siginfo + ucontext + ABI gap)
+ * a gap of __SIGNAL_FRAMESIZE+16 bytes
+ * (the +16 is to get the siginfo and ucontext in the same
+ * positions as in older kernels).
*
* Each of these things must be a multiple of 16 bytes in size.
*
*/
-int sys_rt_sigreturn(struct pt_regs *regs)
+struct rt_sigframe
{
- struct rt_sigframe *rt_sf;
- struct sigcontext_struct sigctx;
- struct sigregs *sr;
- int ret;
- elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */
- sigset_t set;
- stack_t st;
- unsigned long prevsp;
+ struct siginfo info;
+ struct ucontext uc;
+ /* Programs using the rs6000/xcoff abi can save up to 19 gp regs
+ and 18 fp regs below sp before decrementing it. */
+ int abigap[56];
+};
- rt_sf = (struct rt_sigframe *)(regs->gpr[1] + __SIGNAL_FRAMESIZE);
- if (copy_from_user(&sigctx, &rt_sf->uc.uc_mcontext, sizeof(sigctx))
- || copy_from_user(&set, &rt_sf->uc.uc_sigmask, sizeof(set))
- || copy_from_user(&st, &rt_sf->uc.uc_stack, sizeof(st)))
- goto badframe;
- sigdelsetmask(&set, ~_BLOCKABLE);
- spin_lock_irq(¤t->sigmask_lock);
- current->blocked = set;
- recalc_sigpending(current);
- spin_unlock_irq(¤t->sigmask_lock);
+/*
+ * Save the current user registers on the user stack.
+ * We only save the altivec registers if the process has used
+ * altivec instructions at some point.
+ */
+static int
+save_user_regs(struct pt_regs *regs, struct mcontext *frame, int sigret, int mctxsize)
+{
+ if (mctxsize && mctxsize < sizeof(struct mcontext))
+ return 1;
+
+ /* save general and floating-point registers */
if (regs->msr & MSR_FP)
giveup_fpu(current);
+ if (__copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE)
+ || __copy_to_user(&frame->mc_fregs, current->thread.fpr,
+ ELF_NFPREG * sizeof(double)))
+ return 1;
- rt_sf++; /* Look at next rt_sigframe */
- if (rt_sf == (struct rt_sigframe *)(sigctx.regs)) {
- /* Last stacked signal - restore registers -
- * sigctx is initialized to point to the
- * preamble frame (where registers are stored)
- * see handle_signal()
- */
- sr = (struct sigregs *) sigctx.regs;
- if (copy_from_user(saved_regs, &sr->gp_regs,
- sizeof(sr->gp_regs)))
- goto badframe;
- saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE)
- | (saved_regs[PT_MSR] & MSR_USERCHANGE);
- memcpy(regs, saved_regs, GP_REGS_SIZE);
- if (copy_from_user(current->thread.fpr, &sr->fp_regs,
- sizeof(sr->fp_regs)))
- goto badframe;
- /* This function sets back the stack flags into
- the current task structure. */
- sys_sigaltstack(&st, NULL);
-
- ret = regs->result;
- } else {
- /* More signals to go */
- /* Set up registers for next signal handler */
- regs->gpr[1] = (unsigned long)rt_sf - __SIGNAL_FRAMESIZE;
- if (copy_from_user(&sigctx, &rt_sf->uc.uc_mcontext, sizeof(sigctx)))
- goto badframe;
- sr = (struct sigregs *) sigctx.regs;
- regs->gpr[3] = ret = sigctx.signal;
- /* Get the siginfo */
- get_user(regs->gpr[4], (unsigned long *)&rt_sf->pinfo);
- /* Get the ucontext */
- get_user(regs->gpr[5], (unsigned long *)&rt_sf->puc);
- regs->gpr[6] = (unsigned long) rt_sf;
-
- regs->link = (unsigned long) &sr->tramp;
- regs->nip = sigctx.handler;
- if (get_user(prevsp, &sr->gp_regs[PT_R1])
- || put_user(prevsp, (unsigned long *) regs->gpr[1]))
- goto badframe;
- current->thread.fpscr = 0;
+ current->thread.fpscr = 0; /* turn off all fp exceptions */
+
+#ifdef CONFIG_ALTIVEC
+ /* save altivec registers */
+ if (current->thread.used_vr) {
+ if (regs->msr & MSR_VEC)
+ giveup_altivec(current);
+ if (__copy_to_user(&frame->mc_vregs, current->thread.vr,
+ ELF_NVRREG * sizeof(vector128)))
+ return 1;
+ /* set MSR_VEC in the saved MSR value to indicate that
+ frame->mc_vregs contains valid data */
+ if (__put_user(regs->msr | MSR_VEC, &frame->mc_gregs[PT_MSR]))
+ return 1;
+ }
+ /* else assert((regs->msr & MSR_VEC) == 0) */
+
+ /* We always copy to/from vrsave, it's 0 if we don't have or don't
+ * use altivec. Since VSCR only contains 32 bits saved in the least
+ * significant bits of a vector, we "cheat" and stuff VRSAVE in the
+ * most significant bits of that same vector. --BenH
+ */
+ if (__put_user(current->thread.vrsave, (u32 *)&frame->mc_vregs[32]))
+ return 1;
+#endif /* CONFIG_ALTIVEC */
+
+ if (sigret) {
+ /* Set up the sigreturn trampoline: li r0,sigret; sc */
+ if (__put_user(0x38000000UL + sigret, &frame->tramp[0])
+ || __put_user(0x44000002UL, &frame->tramp[1]))
+ return 1;
+ flush_icache_range((unsigned long) &frame->tramp[0],
+ (unsigned long) &frame->tramp[2]);
}
- return ret;
-badframe:
- do_exit(SIGSEGV);
+ return 0;
}
+/*
+ * Restore the current user register values from the user stack,
+ * (except for MSR).
+ */
+static int
+restore_user_regs(struct pt_regs *regs, struct mcontext *sr, int mctxsize)
+{
+#ifdef CONFIG_ALTIVEC
+ unsigned long msr;
+#endif
+
+ if (mctxsize && mctxsize < sizeof(struct mcontext))
+ return 1;
+
+ /* copy up to but not including MSR */
+ if (__copy_from_user(regs, &sr->mc_gregs, PT_MSR * sizeof(elf_greg_t)))
+ return 1;
+ /* copy from orig_r3 (the word after the MSR) up to the end */
+ if (__copy_from_user(®s->orig_gpr3, &sr->mc_gregs[PT_ORIG_R3],
+ GP_REGS_SIZE - PT_ORIG_R3 * sizeof(elf_greg_t)))
+ return 1;
+
+ /* force the process to reload the FP registers from
+ current->thread when it next does FP instructions */
+ regs->msr &= ~MSR_FP;
+ if (__copy_from_user(current->thread.fpr, &sr->mc_fregs,
+ sizeof(sr->mc_fregs)))
+ return 1;
+
+#ifdef CONFIG_ALTIVEC
+ /* force the process to reload the altivec registers from
+ current->thread when it next does altivec instructions */
+ regs->msr &= ~MSR_VEC;
+ if (!__get_user(msr, &sr->mc_gregs[PT_MSR]) && (msr & MSR_VEC) != 0) {
+ /* restore altivec registers from the stack */
+ if (__copy_from_user(current->thread.vr, &sr->mc_vregs,
+ sizeof(sr->mc_vregs)))
+ return 1;
+ } else if (current->thread.used_vr)
+ memset(¤t->thread.vr, 0, sizeof(current->thread.vr));
+
+ /* Always get VRSAVE back */
+ if (__get_user(current->thread.vrsave, (u32 *)&sr->mc_vregs[32]))
+ return 1;
+#endif /* CONFIG_ALTIVEC */
+
+ return 0;
+}
+
+/*
+ * Restore the user process's signal mask
+ */
static void
-setup_rt_frame(struct pt_regs *regs, struct sigregs *frame,
- signed long newsp)
+restore_sigmask(sigset_t *set)
{
- struct rt_sigframe *rt_sf = (struct rt_sigframe *) newsp;
+ sigdelsetmask(set, ~_BLOCKABLE);
+ spin_lock_irq(¤t->sigmask_lock);
+ current->blocked = *set;
+ recalc_sigpending(current);
+ spin_unlock_irq(¤t->sigmask_lock);
+}
- /* Set up preamble frame */
- if (verify_area(VERIFY_WRITE, frame, sizeof(*frame)))
+/*
+ * Set up a signal frame for a "real-time" signal handler
+ * (one which gets siginfo).
+ */
+static void
+handle_rt_signal(unsigned long sig, struct k_sigaction *ka,
+ siginfo_t *info, sigset_t *oldset, struct pt_regs * regs,
+ unsigned long newsp)
+{
+ struct rt_sigframe *rt_sf;
+ struct mcontext *frame;
+ unsigned long origsp = newsp;
+
+ /* Set up Signal Frame */
+ /* Put a Real Time Context onto stack */
+ newsp -= sizeof(*rt_sf);
+ rt_sf = (struct rt_sigframe *) newsp;
+
+ /* create a stack frame for the caller of the handler */
+ newsp -= __SIGNAL_FRAMESIZE + 16;
+
+ if (verify_area(VERIFY_WRITE, (void *) newsp, origsp - newsp))
goto badframe;
- if (regs->msr & MSR_FP)
- giveup_fpu(current);
- if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE)
- || __copy_to_user(&frame->fp_regs, current->thread.fpr,
- ELF_NFPREG * sizeof(double))
- /* Set up to return from user space.
- It calls the sc exception at offset 0x9999
- for sys_rt_sigreturn().
- */
- || __put_user(0x38006666UL, &frame->tramp[0]) /* li r0,0x6666 */
- || __put_user(0x44000002UL, &frame->tramp[1])) /* sc */
+
+ /* Put the siginfo & fill in most of the ucontext */
+ if (__copy_to_user(&rt_sf->info, info, sizeof(*info))
+ || __put_user(0, &rt_sf->uc.uc_flags)
+ || __put_user(0, &rt_sf->uc.uc_link)
+ || __put_user(current->sas_ss_sp, &rt_sf->uc.uc_stack.ss_sp)
+ || __put_user(sas_ss_flags(regs->gpr[1]),
+ &rt_sf->uc.uc_stack.ss_flags)
+ || __put_user(current->sas_ss_size, &rt_sf->uc.uc_stack.ss_size)
+ || __put_user(&rt_sf->uc.uc_mcontext, &rt_sf->uc.uc_regs)
+ || __copy_to_user(&rt_sf->uc.uc_oldsigmask, oldset, sizeof(*oldset))
+ || __copy_to_user(&rt_sf->uc.uc_sigmask, oldset, sizeof(*oldset)))
goto badframe;
- flush_icache_range((unsigned long) &frame->tramp[0],
- (unsigned long) &frame->tramp[2]);
- current->thread.fpscr = 0; /* turn off all fp exceptions */
- /* Retrieve rt_sigframe from stack and
- set up registers for signal handler
- */
- newsp -= __SIGNAL_FRAMESIZE;
- if (put_user(regs->gpr[1], (unsigned long *)newsp)
- || get_user(regs->nip, &rt_sf->uc.uc_mcontext.handler)
- || get_user(regs->gpr[3], &rt_sf->uc.uc_mcontext.signal)
- || get_user(regs->gpr[4], (unsigned long *)&rt_sf->pinfo)
- || get_user(regs->gpr[5], (unsigned long *)&rt_sf->puc))
+ /* Save user registers on the stack */
+ frame = &rt_sf->uc.uc_mcontext;
+ if (save_user_regs(regs, frame, 0x6666, 0))
goto badframe;
+ if (put_user(regs->gpr[1], (unsigned long *)newsp))
+ goto badframe;
regs->gpr[1] = newsp;
+ regs->gpr[3] = sig;
+ regs->gpr[4] = (unsigned long) &rt_sf->info;
+ regs->gpr[5] = (unsigned long) &rt_sf->uc;
regs->gpr[6] = (unsigned long) rt_sf;
+ regs->nip = (unsigned long) ka->sa.sa_handler;
regs->link = (unsigned long) frame->tramp;
return;
badframe:
#if DEBUG_SIG
- printk("badframe in setup_rt_frame, regs=%p frame=%p newsp=%lx\n",
+ printk("badframe in handle_rt_signal, regs=%p frame=%p newsp=%lx\n",
regs, frame, newsp);
#endif
do_exit(SIGSEGV);
}
-/*
- * Do a signal return; undo the signal stack.
- */
-int sys_sigreturn(struct pt_regs *regs)
+static int do_setcontext(struct ucontext *ucp, struct pt_regs *regs, int mctxsize)
{
- struct sigcontext_struct *sc, sigctx;
- struct sigregs *sr;
- int ret;
- elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */
sigset_t set;
- unsigned long prevsp;
- sc = (struct sigcontext_struct *)(regs->gpr[1] + __SIGNAL_FRAMESIZE);
- if (copy_from_user(&sigctx, sc, sizeof(sigctx)))
- goto badframe;
+ if (__copy_from_user(&set, &ucp->uc_sigmask, sizeof(set)))
+ return -EFAULT;
+ restore_sigmask(&set);
- set.sig[0] = sigctx.oldmask;
-#if _NSIG_WORDS > 1
- set.sig[1] = sigctx._unused[3];
-#endif
- sigdelsetmask(&set, ~_BLOCKABLE);
- spin_lock_irq(¤t->sigmask_lock);
- current->blocked = set;
- recalc_sigpending(current);
- spin_unlock_irq(¤t->sigmask_lock);
- if (regs->msr & MSR_FP )
- giveup_fpu(current);
+ if (restore_user_regs(regs, &ucp->uc_mcontext, mctxsize))
+ return -EFAULT;
- sc++; /* Look at next sigcontext */
- if (sc == (struct sigcontext_struct *)(sigctx.regs)) {
- /* Last stacked signal - restore registers */
- sr = (struct sigregs *) sigctx.regs;
- if (copy_from_user(saved_regs, &sr->gp_regs,
- sizeof(sr->gp_regs)))
- goto badframe;
- saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE)
- | (saved_regs[PT_MSR] & MSR_USERCHANGE);
- memcpy(regs, saved_regs, GP_REGS_SIZE);
-
- if (copy_from_user(current->thread.fpr, &sr->fp_regs,
- sizeof(sr->fp_regs)))
- goto badframe;
-
- ret = regs->result;
-
- } else {
- /* More signals to go */
- regs->gpr[1] = (unsigned long)sc - __SIGNAL_FRAMESIZE;
- if (copy_from_user(&sigctx, sc, sizeof(sigctx)))
- goto badframe;
- sr = (struct sigregs *) sigctx.regs;
- regs->gpr[3] = ret = sigctx.signal;
- regs->gpr[4] = (unsigned long) sc;
- regs->link = (unsigned long) &sr->tramp;
- regs->nip = sigctx.handler;
-
- if (get_user(prevsp, &sr->gp_regs[PT_R1])
- || put_user(prevsp, (unsigned long *) regs->gpr[1]))
- goto badframe;
- current->thread.fpscr = 0;
+ return 0;
+}
+
+int sys_swapcontext(struct ucontext *old_ctx, struct ucontext *new_ctx,
+ int mctxtsize, int r6, int r7, int r8, struct pt_regs *regs)
+{
+ unsigned char tmp;
+
+ if (old_ctx != NULL) {
+ if (verify_area(VERIFY_WRITE, old_ctx, sizeof(*old_ctx))
+ || save_user_regs(regs, &old_ctx->uc_mcontext, 0, mctxtsize)
+ || __copy_to_user(&old_ctx->uc_sigmask,
+ ¤t->blocked, sizeof(sigset_t))
+ /* the next 2 things aren't strictly necessary */
+ || __copy_to_user(&old_ctx->uc_oldsigmask,
+ ¤t->blocked, sizeof(sigset_t))
+ || __put_user(&old_ctx->uc_mcontext, &old_ctx->uc_regs))
+ return -EFAULT;
}
- return ret;
+ if (new_ctx == NULL)
+ return 0;
+ if (verify_area(VERIFY_READ, new_ctx, sizeof(*new_ctx))
+ || __get_user(tmp, (u8 *) new_ctx)
+ || __get_user(tmp, (u8 *) (new_ctx + 1) - 1))
+ return -EFAULT;
-badframe:
- do_exit(SIGSEGV);
+ /*
+ * If we get a fault copying the context into the kernel's
+ * image of the user's registers, we can't just return -EFAULT
+ * because the user's registers will be corrupted. For instance
+ * the NIP value may have been updated but not some of the
+ * other registers. Given that we have done the verify_area
+ * and successfully read the first and last bytes of the region
+ * above, this should only happen in an out-of-memory situation
+ * or if another thread unmaps the region containing the context.
+ * We kill the task with a SIGSEGV in this situation.
+ */
+ if (do_setcontext(new_ctx, regs, mctxtsize))
+ do_exit(SIGSEGV);
+ syscall_direct_return(regs);
+ /* doesn't actually return back to here */
+ return 0;
}
-/*
- * Set up a signal frame.
- */
-static void
-setup_frame(struct pt_regs *regs, struct sigregs *frame,
- unsigned long newsp)
+int sys_rt_sigreturn(struct pt_regs *regs)
{
- struct sigcontext_struct *sc = (struct sigcontext_struct *) newsp;
+ struct rt_sigframe *rt_sf;
+ stack_t st;
- if (verify_area(VERIFY_WRITE, frame, sizeof(*frame)))
- goto badframe;
- if (regs->msr & MSR_FP)
- giveup_fpu(current);
- if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE)
- || __copy_to_user(&frame->fp_regs, current->thread.fpr,
- ELF_NFPREG * sizeof(double))
- || __put_user(0x38007777UL, &frame->tramp[0]) /* li r0,0x7777 */
- || __put_user(0x44000002UL, &frame->tramp[1])) /* sc */
- goto badframe;
- flush_icache_range((unsigned long) &frame->tramp[0],
- (unsigned long) &frame->tramp[2]);
- current->thread.fpscr = 0; /* turn off all fp exceptions */
+ rt_sf = (struct rt_sigframe *)(regs->gpr[1] + __SIGNAL_FRAMESIZE + 16);
+ if (verify_area(VERIFY_READ, rt_sf, sizeof(struct rt_sigframe)))
+ goto bad;
+ if (do_setcontext(&rt_sf->uc, regs, 0))
+ goto bad;
+
+ /*
+ * It's not clear whether or why it is desirable to save the
+ * sigaltstack setting on signal delivery and restore it on
+ * signal return. But other architectures do this and we have
+ * always done it up until now so it is probably better not to
+ * change it. -- paulus
+ */
+ if (__copy_from_user(&st, &rt_sf->uc.uc_stack, sizeof(st)))
+ goto bad;
+ do_sigaltstack(&st, NULL, regs->gpr[1]);
- newsp -= __SIGNAL_FRAMESIZE;
- if (put_user(regs->gpr[1], (unsigned long *)newsp)
- || get_user(regs->nip, &sc->handler)
- || get_user(regs->gpr[3], &sc->signal))
- goto badframe;
- regs->gpr[1] = newsp;
- regs->gpr[4] = (unsigned long) sc;
- regs->link = (unsigned long) frame->tramp;
+ return regs->result;
- return;
-
-badframe:
-#if DEBUG_SIG
- printk("badframe in setup_frame, regs=%p frame=%p newsp=%lx\n",
- regs, frame, newsp);
-#endif
+ bad:
do_exit(SIGSEGV);
}
@@ -472,69 +495,47 @@
static void
handle_signal(unsigned long sig, struct k_sigaction *ka,
siginfo_t *info, sigset_t *oldset, struct pt_regs * regs,
- unsigned long *newspp, unsigned long frame)
+ unsigned long newsp)
{
- struct sigcontext_struct *sc;
- struct rt_sigframe *rt_sf;
-
- if (regs->trap == 0x0C00 /* System Call! */
- && ((int)regs->result == -ERESTARTNOHAND ||
- ((int)regs->result == -ERESTARTSYS &&
- !(ka->sa.sa_flags & SA_RESTART))))
- regs->result = -EINTR;
+ struct sigcontext *sc;
+ struct sigregs *frame;
+ unsigned long origsp = newsp;
/* Set up Signal Frame */
- if (ka->sa.sa_flags & SA_SIGINFO) {
- /* Put a Real Time Context onto stack */
- *newspp -= sizeof(*rt_sf);
- rt_sf = (struct rt_sigframe *) *newspp;
- if (verify_area(VERIFY_WRITE, rt_sf, sizeof(*rt_sf)))
- goto badframe;
-
- if (__put_user((unsigned long) ka->sa.sa_handler, &rt_sf->uc.uc_mcontext.handler)
- || __put_user(&rt_sf->info, &rt_sf->pinfo)
- || __put_user(&rt_sf->uc, &rt_sf->puc)
- /* Put the siginfo */
- || __copy_to_user(&rt_sf->info, info, sizeof(*info))
- /* Create the ucontext */
- || __put_user(0, &rt_sf->uc.uc_flags)
- || __put_user(0, &rt_sf->uc.uc_link)
- || __put_user(current->sas_ss_sp, &rt_sf->uc.uc_stack.ss_sp)
- || __put_user(sas_ss_flags(regs->gpr[1]),
- &rt_sf->uc.uc_stack.ss_flags)
- || __put_user(current->sas_ss_size, &rt_sf->uc.uc_stack.ss_size)
- || __copy_to_user(&rt_sf->uc.uc_sigmask, oldset, sizeof(*oldset))
- /* mcontext.regs points to preamble register frame */
- || __put_user((struct pt_regs *)frame, &rt_sf->uc.uc_mcontext.regs)
- || __put_user(sig, &rt_sf->uc.uc_mcontext.signal))
- goto badframe;
- } else {
- /* Put another sigcontext on the stack */
- *newspp -= sizeof(*sc);
- sc = (struct sigcontext_struct *) *newspp;
- if (verify_area(VERIFY_WRITE, sc, sizeof(*sc)))
- goto badframe;
-
- if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler)
- || __put_user(oldset->sig[0], &sc->oldmask)
-#if _NSIG_WORDS > 1
- || __put_user(oldset->sig[1], &sc->_unused[3])
+ newsp -= sizeof(struct sigregs);
+ frame = (struct sigregs *) newsp;
+
+ /* Put a sigcontext on the stack */
+ newsp -= sizeof(*sc);
+ sc = (struct sigcontext *) newsp;
+
+ /* create a stack frame for the caller of the handler */
+ newsp -= __SIGNAL_FRAMESIZE;
+
+ if (verify_area(VERIFY_WRITE, (void *) newsp, origsp - newsp))
+ goto badframe;
+
+#if _NSIG != 64
+#error "Please adjust handle_signal()"
#endif
- || __put_user((struct pt_regs *)frame, &sc->regs)
- || __put_user(sig, &sc->signal))
- goto badframe;
- }
+ if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler)
+ || __put_user(oldset->sig[0], &sc->oldmask)
+ || __put_user(oldset->sig[1], &sc->_unused[3])
+ || __put_user((struct pt_regs *)frame, &sc->regs)
+ || __put_user(sig, &sc->signal))
+ goto badframe;
- if (ka->sa.sa_flags & SA_ONESHOT)
- ka->sa.sa_handler = SIG_DFL;
+ if (save_user_regs(regs, &frame->mctx, 0x7777, 0))
+ goto badframe;
+
+ if (put_user(regs->gpr[1], (unsigned long *)newsp))
+ goto badframe;
+ regs->gpr[1] = newsp;
+ regs->gpr[3] = sig;
+ regs->gpr[4] = (unsigned long) sc;
+ regs->nip = (unsigned long) ka->sa.sa_handler;
+ regs->link = (unsigned long) frame->mctx.tramp;
- if (!(ka->sa.sa_flags & SA_NODEFER)) {
- spin_lock_irq(¤t->sigmask_lock);
- sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
- sigaddset(¤t->blocked,sig);
- recalc_sigpending(current);
- spin_unlock_irq(¤t->sigmask_lock);
- }
return;
badframe:
@@ -547,30 +548,48 @@
}
/*
- * Note that 'init' is a special process: it doesn't get signals it doesn't
- * want to handle. Thus you cannot kill init even with a SIGKILL even by
- * mistake.
+ * Do a signal return; undo the signal stack.
*/
-int do_signal(sigset_t *oldset, struct pt_regs *regs)
+int sys_sigreturn(struct pt_regs *regs)
{
- siginfo_t info;
- struct k_sigaction *ka;
- unsigned long frame, newsp;
+ struct sigcontext *sc, sigctx;
+ struct mcontext *sr;
+ int ret;
+ sigset_t set;
- if (!oldset)
- oldset = ¤t->blocked;
+ sc = (struct sigcontext *)(regs->gpr[1] + __SIGNAL_FRAMESIZE);
+ if (copy_from_user(&sigctx, sc, sizeof(sigctx)))
+ goto badframe;
- newsp = frame = 0;
+ set.sig[0] = sigctx.oldmask;
+ set.sig[1] = sigctx._unused[3];
+ restore_sigmask(&set);
- for (;;) {
- unsigned long signr;
+ sr = (struct mcontext *) sigctx.regs;
+ if (verify_area(VERIFY_READ, sr, sizeof(*sr))
+ || restore_user_regs(regs, sr, 0))
+ goto badframe;
+
+ ret = regs->result;
+ return ret;
+
+badframe:
+ do_exit(SIGSEGV);
+}
+
+static int get_signal_to_deliver(struct siginfo *infop, struct pt_regs *regs)
+{
+ struct k_sigaction *ka;
+ int signr;
+
+ for (;;) {
spin_lock_irq(¤t->sigmask_lock);
- signr = dequeue_signal(¤t->blocked, &info);
+ signr = dequeue_signal(¤t->blocked, infop);
spin_unlock_irq(¤t->sigmask_lock);
if (!signr)
- break;
+ return 0;
if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
/* Let the debugger run. */
@@ -589,17 +608,17 @@
continue;
/* Update the siginfo structure. Is this good? */
- if (signr != info.si_signo) {
- info.si_signo = signr;
- info.si_errno = 0;
- info.si_code = SI_USER;
- info.si_pid = current->p_pptr->pid;
- info.si_uid = current->p_pptr->uid;
+ if (signr != infop->si_signo) {
+ infop->si_signo = signr;
+ infop->si_errno = 0;
+ infop->si_code = SI_USER;
+ infop->si_pid = current->p_pptr->pid;
+ infop->si_uid = current->p_pptr->uid;
}
/* If the (new) signal is now blocked, requeue it. */
if (sigismember(¤t->blocked, signr)) {
- send_sig_info(signr, &info, current);
+ send_sig_info(signr, infop, current);
continue;
}
}
@@ -646,39 +665,85 @@
/* FALLTHRU */
default:
- sig_exit(signr, exit_code, &info);
+ sig_exit(signr, exit_code, infop);
/* NOTREACHED */
}
}
+ return signr;
+ }
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ */
+int do_signal(sigset_t *oldset, struct pt_regs *regs)
+{
+ siginfo_t info;
+ struct k_sigaction *ka;
+ unsigned long frame, newsp;
+ int signr;
+
+ if (!oldset)
+ oldset = ¤t->blocked;
+
+ newsp = frame = 0;
- if ( (ka->sa.sa_flags & SA_ONSTACK)
- && (! on_sig_stack(regs->gpr[1])))
- newsp = (current->sas_ss_sp + current->sas_ss_size);
- else
- newsp = regs->gpr[1];
- newsp = frame = newsp - sizeof(struct sigregs);
-
- /* Whee! Actually deliver the signal. */
- handle_signal(signr, ka, &info, oldset, regs, &newsp, frame);
- break;
- }
-
- if (regs->trap == 0x0C00 /* System Call! */ &&
- ((int)regs->result == -ERESTARTNOHAND ||
- (int)regs->result == -ERESTARTSYS ||
- (int)regs->result == -ERESTARTNOINTR)) {
- regs->gpr[3] = regs->orig_gpr3;
- regs->nip -= 4; /* Back up & retry system call */
- regs->result = 0;
+ signr = get_signal_to_deliver(&info, regs);
+
+ ka = (signr == 0)? NULL: ¤t->sig->action[signr-1];
+
+ if (regs->trap == 0xc00) { /* system call */
+ switch ((int) regs->result) {
+ case -ERESTARTSYS:
+ if (signr == 0 || (ka->sa.sa_flags & SA_RESTART))
+ goto retry;
+ /* fall through */
+ case -ERESTARTNOHAND:
+ if (signr > 0) {
+ /* make the system call return an EINTR */
+ regs->result = -EINTR;
+ break;
+ }
+ /* fall through */
+ case -ERESTARTNOINTR:
+ retry:
+ /* Back up & retry system call */
+ regs->gpr[3] = regs->orig_gpr3;
+ regs->nip -= 4;
+ regs->result = 0;
+ break;
+ }
}
- if (newsp == frame)
+ if (signr == 0)
return 0; /* no signals delivered */
+ if ((ka->sa.sa_flags & SA_ONSTACK) && current->sas_ss_size
+ && !on_sig_stack(regs->gpr[1]))
+ newsp = current->sas_ss_sp + current->sas_ss_size;
+ else
+ newsp = regs->gpr[1];
+ newsp &= ~0xfUL;
+
+ /* Whee! Actually deliver the signal. */
if (ka->sa.sa_flags & SA_SIGINFO)
- setup_rt_frame(regs, (struct sigregs *) frame, newsp);
+ handle_rt_signal(signr, ka, &info, oldset, regs, newsp);
else
- setup_frame(regs, (struct sigregs *) frame, newsp);
+ handle_signal(signr, ka, &info, oldset, regs, newsp);
+
+ if (ka->sa.sa_flags & SA_ONESHOT)
+ ka->sa.sa_handler = SIG_DFL;
+
+ if (!(ka->sa.sa_flags & SA_NODEFER)) {
+ spin_lock_irq(¤t->sigmask_lock);
+ sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
+ sigaddset(¤t->blocked, signr);
+ recalc_sigpending(current);
+ spin_unlock_irq(¤t->sigmask_lock);
+ }
+
return 1;
}
diff -uNr linux-2.4.22/arch/ppc/kernel/smp-tbsync.c linux-2.4.22-ben2/arch/ppc/kernel/smp-tbsync.c
--- linux-2.4.22/arch/ppc/kernel/smp-tbsync.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/arch/ppc/kernel/smp-tbsync.c 2003-08-31 11:39:12.000000000 +0200
@@ -0,0 +1,182 @@
+/*
+ * Smp timebase synchronization for ppc.
+ *
+ * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se)
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define NUM_ITER 300
+
+enum {
+ kExit=0, kSetAndTest, kTest
+};
+
+static struct {
+ volatile int tbu;
+ volatile int tbl;
+ volatile int mark;
+ volatile int cmd;
+ volatile int handshake;
+ int filler[3];
+
+ volatile int ack;
+ int filler2[7];
+
+ volatile int race_result;
+} *tbsync;
+
+static volatile int running;
+
+static void __devinit
+enter_contest( int mark, int add )
+{
+ while( (int)(get_tbl() - mark) < 0 )
+ tbsync->race_result = add;
+}
+
+void __devinit
+smp_generic_take_timebase( void )
+{
+ int cmd, tbl, tbu;
+
+ local_irq_disable();
+ while( !running )
+ ;
+ rmb();
+
+ for( ;; ) {
+ tbsync->ack = 1;
+ while( !tbsync->handshake )
+ ;
+ rmb();
+
+ cmd = tbsync->cmd;
+ tbl = tbsync->tbl;
+ tbu = tbsync->tbu;
+ tbsync->ack = 0;
+ if( cmd == kExit )
+ return;
+
+ if( cmd == kSetAndTest ) {
+ while( tbsync->handshake )
+ ;
+ asm volatile ("mttbl %0" :: "r" (tbl) );
+ asm volatile ("mttbu %0" :: "r" (tbu) );
+ } else {
+ while( tbsync->handshake )
+ ;
+ }
+ enter_contest( tbsync->mark, -1 );
+ }
+ local_irq_enable();
+}
+
+static int __devinit
+start_contest( int cmd, int offset, int num )
+{
+ int i, tbu, tbl, mark, score=0;
+
+ tbsync->cmd = cmd;
+
+ local_irq_disable();
+ for( i=-3; itbu = tbu = get_tbu();
+ tbsync->tbl = tbl + offset;
+ tbsync->mark = mark = tbl + 400;
+
+ wmb();
+
+ tbsync->handshake = 1;
+ while( tbsync->ack )
+ ;
+
+ while( (int)(get_tbl() - tbl) <= 0 )
+ ;
+ tbsync->handshake = 0;
+ enter_contest( mark, 1 );
+
+ while( !tbsync->ack )
+ ;
+
+ if( tbsync->tbu != get_tbu() || ((tbsync->tbl ^ get_tbl()) & 0x80000000) )
+ continue;
+ if( i++ > 0 )
+ score += tbsync->race_result;
+ }
+ local_irq_enable();
+ return score;
+}
+
+void __devinit
+smp_generic_give_timebase( void )
+{
+ int i, score, score2, old, min=0, max=5000, offset=1000;
+
+ printk("Synchronizing timebase\n");
+
+ /* if this fails then this kernel won't work anyway... */
+ tbsync = kmalloc( sizeof(*tbsync), GFP_KERNEL );
+ memset( tbsync, 0, sizeof(*tbsync) );
+ mb();
+ running = 1;
+
+ while( !tbsync->ack )
+ ;
+
+ /* binary search */
+ for( old=-1 ; old != offset ; offset=(min+max)/2 ) {
+ score = start_contest( kSetAndTest, offset, NUM_ITER );
+
+ printk("score %d, offset %d\n", score, offset );
+
+ if( score > 0 )
+ max = offset;
+ else
+ min = offset;
+ old = offset;
+ }
+ score = start_contest( kSetAndTest, min, NUM_ITER );
+ score2 = start_contest( kSetAndTest, max, NUM_ITER );
+
+ printk( "Min %d (score %d), Max %d (score %d)\n", min, score, max, score2 );
+ score = abs( score );
+ score2 = abs( score2 );
+ offset = (score < score2) ? min : max;
+
+ /* guard against inaccurate mttb */
+ for( i=0; i<10; i++ ) {
+ start_contest( kSetAndTest, offset, NUM_ITER/10 );
+
+ if( (score2=start_contest(kTest, offset, NUM_ITER)) < 0 )
+ score2 = -score2;
+ if( score2 <= score || score2 < 20 )
+ break;
+ }
+ printk("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER );
+
+ /* exiting */
+ tbsync->cmd = kExit;
+ wmb();
+ tbsync->handshake = 1;
+ while( tbsync->ack )
+ ;
+ tbsync->handshake = 0;
+ kfree( tbsync );
+ tbsync = NULL;
+ running = 0;
+
+ /* all done */
+ smp_tb_synchronized = 1;
+}
diff -uNr linux-2.4.22/arch/ppc/kernel/smp.c linux-2.4.22-ben2/arch/ppc/kernel/smp.c
--- linux-2.4.22/arch/ppc/kernel/smp.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/smp.c 2003-08-31 11:40:37.000000000 +0200
@@ -55,14 +55,13 @@
/* all cpu mappings are 1-1 -- Cort */
volatile unsigned long cpu_callin_map[NR_CPUS];
-#define TB_SYNC_PASSES 4
-volatile unsigned long __initdata tb_sync_flag = 0;
-volatile unsigned long __initdata tb_offset = 0;
-
int start_secondary(void *);
extern int cpu_idle(void *unused);
void smp_call_function_interrupt(void);
+extern void smp_generic_take_timebase(void);
+extern void smp_generic_give_timebase(void);
+
/* Low level assembly function used to backup CPU 0 state */
extern void __save_cpu_setup(void);
@@ -230,6 +229,10 @@
timeout = 1000000;
while (atomic_read(&data.started) != cpus) {
if (--timeout == 0) {
+#ifdef CONFIG_XMON
+ xmon_printf("smp_call_function on cpu %d: other cpus not responding (%d)\n",
+ smp_processor_id(), atomic_read(&data.started));
+#endif
printk("smp_call_function on cpu %d: other cpus not responding (%d)\n",
smp_processor_id(), atomic_read(&data.started));
goto out;
@@ -242,6 +245,10 @@
timeout = 1000000;
while (atomic_read(&data.finished) != cpus) {
if (--timeout == 0) {
+#ifdef CONFIG_XMON
+ xmon_printf("smp_call_function on cpu %d: other cpus not finishing (%d/%d)\n",
+ smp_processor_id(), atomic_read(&data.finished), atomic_read(&data.started));
+#endif
printk("smp_call_function on cpu %d: other cpus not finishing (%d/%d)\n",
smp_processor_id(), atomic_read(&data.finished), atomic_read(&data.started));
goto out;
@@ -399,81 +406,6 @@
smp_tb_synchronized = 1;
}
-void __init smp_software_tb_sync(int cpu)
-{
-#define PASSES 4 /* 4 passes.. */
- int pass;
- int i, j;
-
- /* stop - start will be the number of timebase ticks it takes for cpu0
- * to send a message to all others and the first reponse to show up.
- *
- * ASSUMPTION: this time is similiar for all cpus
- * ASSUMPTION: the time to send a one-way message is ping/2
- */
- register unsigned long start = 0;
- register unsigned long stop = 0;
- register unsigned long temp = 0;
-
- set_tb(0, 0);
-
- /* multiple passes to get in l1 cache.. */
- for (pass = 2; pass < 2+PASSES; pass++){
- if (cpu == 0){
- mb();
- for (i = j = 1; i < smp_num_cpus; i++, j++){
- /* skip stuck cpus */
- while (!cpu_callin_map[j])
- ++j;
- while (cpu_callin_map[j] != pass)
- barrier();
- }
- mb();
- tb_sync_flag = pass;
- start = get_tbl(); /* start timing */
- while (tb_sync_flag)
- mb();
- stop = get_tbl(); /* end timing */
- /* theoretically, the divisor should be 2, but
- * I get better results on my dual mtx. someone
- * please report results on other smp machines..
- */
- tb_offset = (stop-start)/4;
- mb();
- tb_sync_flag = pass;
- udelay(10);
- mb();
- tb_sync_flag = 0;
- mb();
- set_tb(0,0);
- mb();
- } else {
- cpu_callin_map[cpu] = pass;
- mb();
- while (!tb_sync_flag)
- mb(); /* wait for cpu0 */
- mb();
- tb_sync_flag = 0; /* send response for timing */
- mb();
- while (!tb_sync_flag)
- mb();
- temp = tb_offset; /* make sure offset is loaded */
- while (tb_sync_flag)
- mb();
- set_tb(0,temp); /* now, set the timebase */
- mb();
- }
- }
- if (cpu == 0) {
- smp_tb_synchronized = 1;
- printk("smp_software_tb_sync: %d passes, final offset: %ld\n",
- PASSES, tb_offset);
- }
- /* so time.c doesn't get confused */
- set_dec(tb_ticks_per_jiffy);
- last_jiffy_stamp(cpu) = 0;
-}
-
void __init smp_commence(void)
{
/*
@@ -486,19 +418,13 @@
/* if the smp_ops->setup_cpu function has not already synched the
* timebases with a nicer hardware-based method, do so now
*
- * I am open to suggestions for improvements to this method
- * -- Troy
- *
- * NOTE: if you are debugging, set smp_tb_synchronized for now
- * since if this code runs pretty early and needs all cpus that
- * reported in in smp_callin_map to be working
- *
- * NOTE2: this code doesn't seem to work on > 2 cpus. -- paulus/BenH
+ * New version backported from Samuel Rydth 2.6 version, unlike 2.6
+ * version works only for 2 CPUs (easy hack)
*/
if (!smp_tb_synchronized && smp_num_cpus == 2) {
unsigned long flags;
__save_and_cli(flags);
- smp_software_tb_sync(0);
+ smp_generic_give_timebase();
__restore_flags(flags);
}
}
@@ -518,9 +444,8 @@
barrier();
/* see smp_commence for more info */
- if (!smp_tb_synchronized && smp_num_cpus == 2) {
- smp_software_tb_sync(cpu);
- }
+ if (!smp_tb_synchronized && smp_num_cpus == 2)
+ smp_generic_take_timebase();
__sti();
}
diff -uNr linux-2.4.22/arch/ppc/kernel/traps.c linux-2.4.22-ben2/arch/ppc/kernel/traps.c
--- linux-2.4.22/arch/ppc/kernel/traps.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/kernel/traps.c 2003-08-31 11:39:39.000000000 +0200
@@ -85,14 +85,25 @@
void die(const char * str, struct pt_regs * fp, long err)
{
+#ifdef CONFIG_BOOTX_TEXT
+ extern int force_printk_to_btext;
+#endif
console_verbose();
spin_lock_irq(&die_lock);
+#ifdef CONFIG_BOOTX_TEXT
+ force_printk_to_btext = 1;
+#endif
#ifdef CONFIG_PMAC_BACKLIGHT
- set_backlight_enable(1);
- set_backlight_level(BACKLIGHT_MAX);
-#endif
+ if (_machine == _MACH_Pmac) {
+ set_backlight_enable(1);
+ set_backlight_level(BACKLIGHT_MAX);
+ }
+#endif
printk("Oops: %s, sig: %ld\n", str, err);
show_regs(fp);
+#ifdef CONFIG_BOOTX_TEXT
+ force_printk_to_btext = 0;
+#endif
spin_unlock_irq(&die_lock);
/* do_exit() should take care of panic'ing from an interrupt
* context so we don't handle it here
@@ -153,9 +164,11 @@
--nip;
rb = (*nip >> 11) & 0x1f;
+#if 0
printk(KERN_DEBUG "%s bad port %lx at %p\n",
(*nip & 0x100)? "OUT to": "IN from",
regs->gpr[rb] - _IO_BASE, nip);
+#endif
regs->nip = fixup;
return 1;
}
diff -uNr linux-2.4.22/arch/ppc/lib/locks.c linux-2.4.22-ben2/arch/ppc/lib/locks.c
--- linux-2.4.22/arch/ppc/lib/locks.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/lib/locks.c 2003-08-31 11:38:35.000000000 +0200
@@ -15,6 +15,14 @@
#if SPINLOCK_DEBUG
+/* Route debug output to xmon when possible, there are more chances
+ * for it to work than the console
+ */
+#ifdef CONFIG_XMON
+extern void xmon_printf(const char* fmt,...);
+#define printk xmon_printf
+#endif
+
#undef INIT_STUCK
#define INIT_STUCK 200000000 /*0xffffffff*/
diff -uNr linux-2.4.22/arch/ppc/mm/fault.c linux-2.4.22-ben2/arch/ppc/mm/fault.c
--- linux-2.4.22/arch/ppc/mm/fault.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/mm/fault.c 2003-08-31 11:41:46.000000000 +0200
@@ -96,7 +96,7 @@
void do_page_fault(struct pt_regs *regs, unsigned long address,
unsigned long error_code)
{
- struct vm_area_struct * vma;
+ struct vm_area_struct * vma, * prev_vma;
struct mm_struct *mm = current->mm;
siginfo_t info;
int code = SEGV_MAPERR;
@@ -177,7 +177,8 @@
&& (!user_mode(regs) || !store_updates_sp(regs)))
goto bad_area;
}
- if (expand_stack(vma, address))
+ vma = find_vma_prev(mm, address, &prev_vma);
+ if (expand_stack(vma, address, prev_vma))
goto bad_area;
good_area:
diff -uNr linux-2.4.22/arch/ppc/mm/init.c linux-2.4.22-ben2/arch/ppc/mm/init.c
--- linux-2.4.22/arch/ppc/mm/init.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/mm/init.c 2003-08-31 11:38:42.000000000 +0200
@@ -457,6 +457,17 @@
high_memory = (void *) __va(PPC_MEMSTART + total_lowmem);
num_physpages = max_mapnr; /* RAM is assumed contiguous */
+ /* Sanity check: did ioremap_bot stomp over vmalloc space ? We keep
+ * a minimal 16Mb guard though if you only have 16Mb left, you'll
+ * probably run into trouble, so we also printk something if you
+ * have less than 64Mb. This is meant to help diagnosing such problems
+ * as debugging it can be really painful. --BenH.
+ */
+ if (VMALLOC_END < (VMALLOC_START + 0x01000000UL))
+ panic("Argh ! Virtual space exhausted !");
+ if (VMALLOC_END < (VMALLOC_START + 0x04000000UL))
+ printk(KERN_WARNING "Warning ! Virtual space small !");
+
totalram_pages += free_all_bootmem();
#ifdef CONFIG_BLK_DEV_INITRD
diff -uNr linux-2.4.22/arch/ppc/mm/pgtable.c linux-2.4.22-ben2/arch/ppc/mm/pgtable.c
--- linux-2.4.22/arch/ppc/mm/pgtable.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/mm/pgtable.c 2003-08-31 11:40:07.000000000 +0200
@@ -238,7 +238,7 @@
printk(KERN_INFO "Memory BAT mapping: BAT2=%ldMb, BAT3=%ldMb,"
" residual: %ldMb\n", __bat2 >> 20, __bat3 >> 20,
- (total_lowmem - (__bat2 - __bat3)) >> 20);
+ (total_memory - (__bat2 - __bat3)) >> 20);
/* On SMP, we limit the lowmem to the area mapped with BATs.
* We also assume nobody will do SMP with 601s
diff -uNr linux-2.4.22/arch/ppc/platforms/Makefile linux-2.4.22-ben2/arch/ppc/platforms/Makefile
--- linux-2.4.22/arch/ppc/platforms/Makefile 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/platforms/Makefile 2003-08-31 11:39:36.000000000 +0200
@@ -41,6 +41,9 @@
prep_time.o prep_setup.o pmac_sleep.o \
pmac_nvram.o
obj-$(CONFIG_PMAC_BACKLIGHT) += pmac_backlight.o
+ifeq ($(CONFIG_ALL_PPC),y)
+obj-$(CONFIG_CPU_FREQ_PMAC) += pmac_cpufreq.o
+endif
obj-$(CONFIG_PPC_RTAS) += error_log.o proc_rtas.o
obj-$(CONFIG_PREP_RESIDUAL) += residual.o
obj-$(CONFIG_GEMINI) += gemini_pci.o gemini_setup.o gemini_prom.o
diff -uNr linux-2.4.22/arch/ppc/platforms/pmac_cpufreq.c linux-2.4.22-ben2/arch/ppc/platforms/pmac_cpufreq.c
--- linux-2.4.22/arch/ppc/platforms/pmac_cpufreq.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/arch/ppc/platforms/pmac_cpufreq.c 2003-08-31 11:41:24.000000000 +0200
@@ -0,0 +1,352 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#undef DEBUG_FREQ
+
+extern void low_choose_750fx_pll(int pll);
+extern void low_sleep_handler(void);
+extern void openpic_sleep_save_intrs(void);
+extern void openpic_sleep_restore_intrs(void);
+extern void enable_kernel_altivec(void);
+extern void enable_kernel_fp(void);
+
+static unsigned int low_freq;
+static unsigned int hi_freq;
+static unsigned int cur_freq;
+static int cpufreq_uses_pmu;
+
+#define PMAC_CPU_LOW_SPEED 1
+#define PMAC_CPU_HIGH_SPEED 0
+
+static inline void
+wakeup_decrementer(void)
+{
+ set_dec(tb_ticks_per_jiffy);
+ /* No currently-supported powerbook has a 601,
+ * so use get_tbl, not native
+ */
+ last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
+}
+
+#ifdef DEBUG_FREQ
+static inline void
+debug_calc_bogomips(void)
+{
+ /* This will cause a recalc of bogomips and display the
+ * result. We backup/restore the value to avoid affecting the
+ * core cpufreq framework's own calculation.
+ */
+ extern void calibrate_delay(void);
+
+ unsigned long save_lpj = loops_per_jiffy;
+ calibrate_delay();
+ loops_per_jiffy = save_lpj;
+}
+#endif
+
+/* Switch CPU speed under 750FX CPU control
+ */
+static int __pmac
+cpu_750fx_cpu_speed(int low_speed)
+{
+#ifdef DEBUG_FREQ
+ printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1));
+#endif
+ low_choose_750fx_pll(low_speed);
+#ifdef DEBUG_FREQ
+ printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1));
+ debug_calc_bogomips();
+#endif
+
+ return 0;
+}
+
+/* Switch CPU speed under PMU control
+ */
+static int __pmac
+pmu_set_cpu_speed(unsigned int low_speed)
+{
+ struct adb_request req;
+ unsigned long save_l2cr;
+ unsigned long save_l3cr;
+
+#ifdef DEBUG_FREQ
+ printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1));
+#endif
+ /* Disable all interrupt sources on openpic */
+ openpic_sleep_save_intrs();
+
+ /* Make sure the PMU is idle */
+ pmu_suspend();
+
+ /* Make sure the decrementer won't interrupt us */
+ asm volatile("mtdec %0" : : "r" (0x7fffffff));
+ /* Make sure any pending DEC interrupt occuring while we did
+ * the above didn't re-enable the DEC */
+ mb();
+ asm volatile("mtdec %0" : : "r" (0x7fffffff));
+
+ /* We can now disable MSR_EE */
+ local_irq_disable();
+
+ /* Giveup the FPU & vec */
+ enable_kernel_fp();
+
+#ifdef CONFIG_ALTIVEC
+ if (cur_cpu_spec[0]->cpu_features & CPU_FTR_ALTIVEC)
+ enable_kernel_altivec();
+#endif /* CONFIG_ALTIVEC */
+
+ /* Save & disable L2 and L3 caches */
+ save_l3cr = _get_L3CR(); /* (returns -1 if not available) */
+ save_l2cr = _get_L2CR(); /* (returns -1 if not available) */
+ if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0)
+ _set_L3CR(save_l3cr & 0x7fffffff);
+ if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
+ _set_L2CR(save_l2cr & 0x7fffffff);
+
+ /* Send the new speed command. My assumption is that this command
+ * will cause PLL_CFG[0..3] to be changed next time CPU goes to sleep
+ */
+ pmu_request(&req, NULL, 6, PMU_CPU_SPEED, 'W', 'O', 'O', 'F', low_speed);
+ while (!req.complete)
+ pmu_poll();
+
+ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,1);
+
+ low_sleep_handler();
+
+ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,0);
+
+ /* Restore L2 cache */
+ if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
+ _set_L2CR(save_l2cr);
+ /* Restore L3 cache */
+ if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0)
+ _set_L3CR(save_l3cr);
+
+ /* Restore userland MMU context */
+ set_context(current->active_mm->context, current->active_mm->pgd);
+
+ pmu_unlock();
+
+#ifdef DEBUG_FREQ
+ printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1));
+#endif
+
+ /* Restore decrementer */
+ wakeup_decrementer();
+
+ /* Restore interrupts */
+ openpic_sleep_restore_intrs();
+
+ pmu_resume();
+
+ /* Let interrupts flow again ... */
+ local_irq_enable();
+
+#ifdef DEBUG_FREQ
+ debug_calc_bogomips();
+#endif
+
+ return 0;
+}
+
+static int __pmac
+do_set_cpu_speed(int speed_mode)
+{
+ struct cpufreq_freqs freqs;
+ int rc;
+
+ freqs.old = cur_freq;
+ freqs.new = (speed_mode == PMAC_CPU_HIGH_SPEED) ? hi_freq : low_freq;
+ freqs.cpu = CPUFREQ_ALL_CPUS;
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ if (cpufreq_uses_pmu)
+ rc = pmu_set_cpu_speed(speed_mode);
+ else
+ rc = cpu_750fx_cpu_speed(speed_mode);
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ cur_freq = (speed_mode == PMAC_CPU_HIGH_SPEED) ? hi_freq : low_freq;
+
+ return rc;
+}
+
+static int __pmac
+pmac_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ if (!policy)
+ return -EINVAL;
+
+ policy->cpu = 0; /* UP only */
+
+ cpufreq_verify_within_limits(policy, low_freq, hi_freq);
+
+ if ((policy->min > low_freq) &&
+ (policy->max < hi_freq))
+ policy->max = hi_freq;
+
+ return 0;
+}
+
+static int __pmac
+pmac_cpufreq_setpolicy(struct cpufreq_policy *policy)
+{
+ int rc;
+
+ if (!policy)
+ return -EINVAL;
+ if (policy->min > low_freq)
+ rc = do_set_cpu_speed(PMAC_CPU_HIGH_SPEED);
+ else if (policy->max < hi_freq)
+ rc = do_set_cpu_speed(PMAC_CPU_LOW_SPEED);
+ else if (policy->policy == CPUFREQ_POLICY_POWERSAVE)
+ rc = do_set_cpu_speed(PMAC_CPU_LOW_SPEED);
+ else
+ rc = do_set_cpu_speed(PMAC_CPU_HIGH_SPEED);
+
+ return rc;
+}
+
+unsigned int __pmac
+pmac_get_cur_cpufreq(void)
+{
+ return cur_freq;
+}
+
+
+/* Currently, we support the following machines:
+ *
+ * - Titanium PowerBook 800 (PMU based, 667Mhz & 800Mhz)
+ * - Titanium PowerBook 500 (PMU based, 300Mhz & 500Mhz)
+ * - iBook2 500 (PMU based, 400Mhz & 500Mhz)
+ * - iBook2 700 (CPU based, 400Mhz & 700Mhz, support low voltage)
+ */
+static int __init
+pmac_cpufreq_setup(void)
+{
+ struct device_node *cpunode;
+ struct cpufreq_driver *driver;
+ u32 *value;
+ int has_freq_ctl = 0;
+ int rc;
+
+ memset(&driver, 0, sizeof(driver));
+
+ /* Assume only one CPU */
+ cpunode = find_type_devices("cpu");
+ if (!cpunode)
+ goto out;
+
+ /* Get current cpu clock freq */
+ value = (u32 *)get_property(cpunode, "clock-frequency", NULL);
+ if (!value)
+ goto out;
+ cur_freq = (*value) / 1000;
+
+ /* Check for newer machines */
+ if (machine_is_compatible("PowerBook3,4") ||
+ machine_is_compatible("PowerBook3,5") ||
+ machine_is_compatible("MacRISC3")) {
+ value = (u32 *)get_property(cpunode, "min-clock-frequency", NULL);
+ if (!value)
+ goto out;
+ low_freq = (*value) / 1000;
+ /* The PowerBook G4 12" (PowerBook6,1) has an error in the device-tree
+ * here */
+ if (low_freq < 100000)
+ low_freq *= 10;
+
+ value = (u32 *)get_property(cpunode, "max-clock-frequency", NULL);
+ if (!value)
+ goto out;
+ hi_freq = (*value) / 1000;
+ has_freq_ctl = 1;
+ cpufreq_uses_pmu = 1;
+ }
+ /* Else check for iBook2 500 */
+ else if (machine_is_compatible("PowerBook4,1")) {
+ /* We only know about 500Mhz model */
+ if (cur_freq < 450000 || cur_freq > 550000)
+ goto out;
+ hi_freq = cur_freq;
+ low_freq = 400000;
+ has_freq_ctl = 1;
+ cpufreq_uses_pmu = 1;
+ }
+ /* Else check for TiPb 500 */
+ else if (machine_is_compatible("PowerBook3,2")) {
+ /* We only know about 500Mhz model */
+ if (cur_freq < 450000 || cur_freq > 550000)
+ goto out;
+ hi_freq = cur_freq;
+ low_freq = 300000;
+ has_freq_ctl = 1;
+ cpufreq_uses_pmu = 1;
+ }
+ /* Else check for 750FX */
+ else if (PVR_VER(mfspr(PVR)) == 0x7000) {
+ if (get_property(cpunode, "dynamic-power-step", NULL) == NULL)
+ goto out;
+ hi_freq = cur_freq;
+ value = (u32 *)get_property(cpunode, "reduced-clock-frequency", NULL);
+ if (!value)
+ goto out;
+ low_freq = (*value) / 1000;
+ cpufreq_uses_pmu = 0;
+ has_freq_ctl = 1;
+ }
+out:
+ if (!has_freq_ctl)
+ return -ENODEV;
+
+ /* initialization of main "cpufreq" code*/
+ driver = kmalloc(sizeof(struct cpufreq_driver) +
+ NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
+ if (!driver)
+ return -ENOMEM;
+
+ driver->policy = (struct cpufreq_policy *) (driver + 1);
+
+#ifdef CONFIG_CPU_FREQ_24_API
+ driver->cpu_min_freq[0] = low_freq;
+ driver->cpu_cur_freq[0] = cur_freq;
+#endif
+
+ driver->verify = &pmac_cpufreq_verify;
+ driver->setpolicy = &pmac_cpufreq_setpolicy;
+
+ driver->policy[0].cpu = 0;
+ driver->policy[0].min = low_freq;
+ driver->policy[0].max = cur_freq;
+ driver->policy[0].max_cpu_freq = hi_freq;
+ driver->policy[0].policy = (cur_freq == low_freq) ?
+ CPUFREQ_POLICY_POWERSAVE : CPUFREQ_POLICY_PERFORMANCE;
+
+ rc = cpufreq_register(driver);
+ if (rc)
+ kfree(driver);
+ return rc;
+}
+
+__initcall(pmac_cpufreq_setup);
+
diff -uNr linux-2.4.22/arch/ppc/platforms/pmac_setup.c linux-2.4.22-ben2/arch/ppc/platforms/pmac_setup.c
--- linux-2.4.22/arch/ppc/platforms/pmac_setup.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/platforms/pmac_setup.c 2003-08-31 11:38:47.000000000 +0200
@@ -50,6 +50,7 @@
#include
#include
#include
+#include
#include
#include
@@ -150,7 +151,15 @@
{
struct device_node *cpu_node;
int *fp, s;
-
+
+#ifdef CONFIG_CPU_FREQ_PMAC
+ extern unsigned int pmac_get_cur_cpufreq(void);
+ unsigned int freq = pmac_get_cur_cpufreq();
+ if (freq != 0) {
+ seq_printf(m, "clock\t\t: %dMHz\n", freq/1000);
+ return 0;
+ }
+#endif /* CONFIG_CPU_FREQ_PMAC */
cpu_node = find_type_devices("cpu");
if (!cpu_node)
return 0;
@@ -197,12 +206,27 @@
}
seq_printf(m, "\n");
}
+ pp = (char *) get_property(np, "scb#", &plen);
+ if (pp != NULL)
+ seq_printf(m, "board revision\t: %08x\n", *((int *)pp));
} else
seq_printf(m, "PowerMac\n");
/* print parsed model */
seq_printf(m, "detected as\t: %d (%s)\n", mbmodel, mbname);
seq_printf(m, "pmac flags\t: %08x\n", mbflags);
+#if 0
+{
+ extern long nap_return_count;
+ extern long nap_enter_count;
+ extern long nap_save_msscr0;
+ extern long dbg_nap_ret;
+ seq_printf(m, "nap_return_count: %d\n", nap_return_count);
+ seq_printf(m, "nap_enter_count\t: %d\n", nap_enter_count);
+ seq_printf(m, "nap_save_msscr0\t: %x\n", nap_save_msscr0);
+ seq_printf(m, "dbg_nap_ret\t: %x\n", dbg_nap_ret);
+}
+#endif
/* find l2 cache info */
np = find_devices("l2-cache");
@@ -309,6 +333,14 @@
sysctrl_regs = (volatile u32 *) ioremap(0xf8000000, 0x1000);
ohare_init();
+ /* Check & display some CPU config registers for diagnostic */
+ if (pvr == 0x8000 || pvr == 0x8001) { /* 745x */
+ printk(KERN_INFO "CPU MSCCR0 : 0x%08x\n", mfspr(SPRN_MSSCR0));
+ printk(KERN_INFO "CPU HID1 : 0x%08x\n", mfspr(SPRN_HID1));
+ }
+ if (pvr == 0x0008 || pvr == 0x7000) /* 750's */
+ printk(KERN_INFO "CPU HID1 : 0x%08x\n", mfspr(SPRN_HID1));
+
/* Lookup PCI hosts */
pmac_find_bridges();
@@ -329,6 +361,9 @@
}
}
+#if 0
+ printk("MSSCR0: %x\n", mfspr(SPRN_MSSCR0));
+#endif
if (ppc_override_l2cr)
printk(KERN_INFO "L2CR overriden (0x%x), backside cache is %s\n",
ppc_override_l2cr_value, (ppc_override_l2cr_value & 0x80000000)
@@ -823,6 +858,12 @@
#endif /* CONFIG_VT */
}
+static int __pmac
+pmac_ide_reserved_hwifs(void)
+{
+ return 0;
+}
+
void __init
pmac_init(unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7)
@@ -864,6 +905,7 @@
select_adb_keyboard();
#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+ ppc_ide_md.reserved_hwifs = pmac_ide_reserved_hwifs;
#ifdef CONFIG_BLK_DEV_IDE_PMAC
ppc_ide_md.ide_init_hwif = pmac_ide_init_hwif_ports;
ppc_ide_md.default_io_base = pmac_ide_get_base;
diff -uNr linux-2.4.22/arch/ppc/platforms/pmac_sleep.S linux-2.4.22-ben2/arch/ppc/platforms/pmac_sleep.S
--- linux-2.4.22/arch/ppc/platforms/pmac_sleep.S 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/platforms/pmac_sleep.S 2003-08-31 11:38:37.000000000 +0200
@@ -44,7 +44,7 @@
.text
.align 5
-#if defined(CONFIG_PMAC_PBOOK)
+#if defined(CONFIG_PMAC_PBOOK) || defined(CONFIG_CPU_FREQ_PMAC)
/* This gets called by via-pmu.c late during the sleep process.
* The PMU was already send the sleep command and will shut us down
@@ -372,7 +372,7 @@
isync
rfi
-#endif /* defined(CONFIG_PMAC_PBOOK) */
+#endif /* defined(CONFIG_PMAC_PBOOK) || defined(CONFIG_CPU_FREQ) */
.data
.balign L1_CACHE_LINE_SIZE
diff -uNr linux-2.4.22/arch/ppc/xmon/start.c linux-2.4.22-ben2/arch/ppc/xmon/start.c
--- linux-2.4.22/arch/ppc/xmon/start.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/xmon/start.c 2003-08-31 11:38:48.000000000 +0200
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -24,16 +25,34 @@
#include
#endif
+#define CONFIG_XMON_FW
+
static volatile unsigned char *sccc, *sccd;
unsigned int TXRDY, RXRDY, DLAB;
extern void xmon_printf(const char *fmt, ...);
static int xmon_expect(const char *str, unsigned int timeout);
+extern char cmd_line[512];
static int use_screen;
static int via_modem;
static int xmon_use_sccb;
static struct device_node *channel_node;
+/* There are used when hooked via firewire */
+#ifdef CONFIG_XMON_FW
+volatile unsigned int xmon_fw_outbuf_size;
+volatile unsigned char xmon_fw_outbuf[1024];
+volatile unsigned int xmon_fw_oflags;
+volatile unsigned int xmon_fw_iflags;
+volatile unsigned int xmon_fw_idata;
+#define XMON_FW_FLAGS_OUT_ENTERED 0x00000001
+#define XMON_FW_FLAGS_OUT_DATA 0x00000002
+#define XMON_FW_FLAGS_OUT_ACK 0x00000004
+#define XMON_FW_FLAGS_IN_ATTACHED 0x00000001
+#define XMON_FW_FLAGS_IN_DATA 0x00000002
+#define XMON_FW_FLAGS_IN_ACK 0x00000004
+#endif
+
#define TB_SPEED 25000000
static inline unsigned int readtb(void)
@@ -112,7 +131,9 @@
volatile unsigned char *base;
use_screen = 0;
-
+ /* Typically used for firewire debugging */
+ if (strstr(cmd_line, "xmon_noio"))
+ return;
if (_machine == _MACH_Pmac) {
struct device_node *np;
unsigned long addr;
@@ -215,20 +236,21 @@
DLAB = 0x80;
#endif /* platform */
+#ifdef CONFIG_MAGIC_SYSRQ
__sysrq_put_key_op('x', &sysrq_xmon_op);
+#endif
}
static int scc_initialized = 0;
void xmon_init_scc(void);
-extern void pmu_poll(void);
extern void cuda_poll(void);
static inline void do_poll_adb(void)
{
#ifdef CONFIG_ADB_PMU
if (sys_ctrler == SYS_CTRLER_PMU)
- pmu_poll();
+ pmu_poll_adb();
#endif /* CONFIG_ADB_PMU */
#ifdef CONFIG_ADB_CUDA
if (sys_ctrler == SYS_CTRLER_CUDA)
@@ -236,6 +258,114 @@
#endif /* CONFIG_ADB_CUDA */
}
+#if 0
+static void
+bx_printf(const char* fmt, ...)
+{
+ static char dbgbuf[2048];
+ va_list ap;
+ int n;
+
+ va_start(ap, fmt);
+ n = vsprintf(dbgbuf, fmt, ap);
+ va_end(ap);
+ btext_drawstring(dbgbuf);
+}
+#endif
+
+#ifdef CONFIG_XMON_FW
+static int
+xmon_fw_write(void* ptr, int nb)
+{
+ char* p = (char *)ptr;
+ int c, i;
+
+ while (nb && (xmon_fw_iflags & XMON_FW_FLAGS_IN_ATTACHED)) {
+ c = (nb > 1024) ? 1024 : nb;
+ memcpy((void *)xmon_fw_outbuf, p, c);
+ xmon_fw_outbuf_size = c;
+ wmb();
+ xmon_fw_oflags |= XMON_FW_FLAGS_OUT_DATA;
+ wmb();
+ for (i=0; i<1000; i++) {
+ rmb();
+ if (xmon_fw_iflags & XMON_FW_FLAGS_IN_ACK)
+ break;
+ mdelay(1);
+ }
+ xmon_fw_oflags &= ~XMON_FW_FLAGS_OUT_DATA;
+ wmb();
+ if ((xmon_fw_iflags & XMON_FW_FLAGS_IN_ACK) == 0) {
+ xmon_fw_iflags = 0;
+ break;
+ }
+ for (i=0; i<1000; i++) {
+ rmb();
+ if ((xmon_fw_iflags & XMON_FW_FLAGS_IN_ACK) == 0)
+ break;
+ mdelay(1);
+ }
+ if ((xmon_fw_iflags & XMON_FW_FLAGS_IN_ACK) != 0) {
+ xmon_fw_iflags = 0;
+ break;
+ }
+ nb -= c;
+ p += c;
+ }
+ return (xmon_fw_iflags & XMON_FW_FLAGS_IN_ATTACHED) != 0;
+}
+
+static int
+xmon_fw_read(void* ptr, int nb)
+{
+ int t, on, i;
+ unsigned int k;
+ char c[3];
+ char* p = (char *)ptr;
+
+ while (nb) {
+ t = 0;
+ on = 0;
+ while(xmon_fw_iflags & XMON_FW_FLAGS_IN_ATTACHED) {
+ if (xmon_fw_iflags & XMON_FW_FLAGS_IN_DATA)
+ break;
+ if (--t < 0) {
+ on = 1 - on;
+ c[0] = on? 0xdb: 0x20;
+ c[1] = '\b';
+ if (!xmon_fw_write(c, 2))
+ break;
+ t = 2000000;
+ }
+ rmb();
+ }
+ k = xmon_fw_idata & 0xff;
+ xmon_fw_oflags |= XMON_FW_FLAGS_OUT_ACK;
+ wmb();
+ for (i=0; i<1000; i++) {
+ rmb();
+ if ((xmon_fw_iflags & XMON_FW_FLAGS_IN_DATA) == 0)
+ break;
+ mdelay(1);
+ }
+ if (xmon_fw_iflags & XMON_FW_FLAGS_IN_DATA) {
+ xmon_fw_iflags = 0;
+ break;
+ }
+ xmon_fw_oflags &= ~XMON_FW_FLAGS_OUT_ACK;
+ wmb();
+ if (on) {
+ c[0] = 0x20; c[1] = '\b';
+ xmon_fw_write(c, 2);
+ }
+ *(p++) = k;
+ nb--;
+ }
+ return (xmon_fw_iflags & XMON_FW_FLAGS_IN_ATTACHED) != 0;
+}
+
+#endif /* CONFIG_XMON_FW */
+
int
xmon_write(void *handle, void *ptr, int nb)
{
@@ -251,9 +381,17 @@
if (--lock_wait == 0)
break;
#endif
-
+#ifdef CONFIG_XMON_FW
+ if (xmon_fw_write(ptr, nb))
+ goto out;
+#endif /* CONFIG_XMON_FW */
#ifdef CONFIG_BOOTX_TEXT
- if (use_screen) {
+#ifdef CONFIG_MORE_DEBUG
+ if (1)
+#else
+ if (use_screen)
+#endif
+ {
/* write it on the screen */
for (i = 0; i < nb; ++i)
btext_drawchar(*p++);
@@ -262,6 +400,9 @@
#endif
if (!scc_initialized)
xmon_init_scc();
+ if (!sccd)
+ goto out;
+
ct = 0;
for (i = 0; i < nb; ++i) {
while ((*sccc & TXRDY) == 0)
@@ -354,6 +495,10 @@
char *p = ptr;
int i;
+#ifdef CONFIG_XMON_FW
+ if (xmon_fw_read(ptr, nb))
+ return nb;
+#endif /* CONFIG_XMON_FW */
#ifdef CONFIG_BOOTX_TEXT
if (use_screen) {
for (i = 0; i < nb; ++i)
@@ -363,6 +508,8 @@
#endif
if (!scc_initialized)
xmon_init_scc();
+ if (sccd == NULL)
+ return 0;
for (i = 0; i < nb; ++i) {
while ((*sccc & RXRDY) == 0)
do_poll_adb();
@@ -385,7 +532,8 @@
static unsigned char scc_inittab[] = {
13, 0, /* set baud rate divisor */
- 12, 1,
+// 12, 1,
+ 12, 0,
14, 1, /* baud rate gen enable, src=rtxc */
11, 0x50, /* clocks = br gen */
5, 0xea, /* tx 8 bits, assert DTR & RTS */
@@ -396,6 +544,8 @@
void
xmon_init_scc()
{
+ if (sccd == NULL && channel_node == NULL)
+ return;
if ( _machine == _MACH_chrp )
{
sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */
@@ -634,11 +784,19 @@
pmu_suspend();
}
#endif
+#ifdef CONFIG_XMON_FW
+ xmon_fw_oflags |= XMON_FW_FLAGS_OUT_ENTERED;
+ wmb();
+#endif /* CONFIG_XMON_FW */
}
void
xmon_leave(void)
{
+#ifdef CONFIG_XMON_FW
+ xmon_fw_oflags &= ~XMON_FW_FLAGS_OUT_ENTERED;
+ wmb();
+#endif /* CONFIG_XMON_FW */
#ifdef CONFIG_ADB_PMU
if (_machine == _MACH_Pmac) {
pmu_resume();
diff -uNr linux-2.4.22/arch/ppc/xmon/xmon.c linux-2.4.22-ben2/arch/ppc/xmon/xmon.c
--- linux-2.4.22/arch/ppc/xmon/xmon.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/ppc/xmon/xmon.c 2003-08-31 11:40:20.000000000 +0200
@@ -209,14 +209,16 @@
#endif /* CONFIG_SMP */
remove_bpts();
#ifdef CONFIG_PMAC_BACKLIGHT
- if( setjmp(bus_error_jmp) == 0 ) {
- debugger_fault_handler = handle_fault;
- sync();
- set_backlight_enable(1);
- set_backlight_level(BACKLIGHT_MAX);
- sync();
+ if (_machine == _MACH_Pmac) {
+ if( setjmp(bus_error_jmp) == 0 ) {
+ debugger_fault_handler = handle_fault;
+ sync();
+ set_backlight_enable(1);
+ set_backlight_level(BACKLIGHT_MAX);
+ sync();
+ }
+ debugger_fault_handler = 0;
}
- debugger_fault_handler = 0;
#endif /* CONFIG_PMAC_BACKLIGHT */
cmd = cmds(excp);
if (cmd == 's') {
diff -uNr linux-2.4.22/arch/s390/mm/fault.c linux-2.4.22-ben2/arch/s390/mm/fault.c
--- linux-2.4.22/arch/s390/mm/fault.c 2002-11-29 00:53:11.000000000 +0100
+++ linux-2.4.22-ben2/arch/s390/mm/fault.c 2003-08-31 11:38:52.000000000 +0200
@@ -210,7 +210,7 @@
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- if (expand_stack(vma, address))
+ if (expand_stack(vma, address, NULL))
goto bad_area;
/*
* Ok, we have a good vm_area for this memory access, so
diff -uNr linux-2.4.22/arch/s390x/mm/fault.c linux-2.4.22-ben2/arch/s390x/mm/fault.c
--- linux-2.4.22/arch/s390x/mm/fault.c 2002-11-29 00:53:11.000000000 +0100
+++ linux-2.4.22-ben2/arch/s390x/mm/fault.c 2003-08-31 11:38:42.000000000 +0200
@@ -210,7 +210,7 @@
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- if (expand_stack(vma, address))
+ if (expand_stack(vma, address, NULL))
goto bad_area;
/*
* Ok, we have a good vm_area for this memory access, so
diff -uNr linux-2.4.22/arch/sh/mm/fault.c linux-2.4.22-ben2/arch/sh/mm/fault.c
--- linux-2.4.22/arch/sh/mm/fault.c 2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.22-ben2/arch/sh/mm/fault.c 2003-08-31 11:40:56.000000000 +0200
@@ -78,7 +78,7 @@
check_stack:
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- if (expand_stack(vma, start) == 0)
+ if (expand_stack(vma, start, NULL) == 0)
goto good_area;
bad_area:
@@ -123,7 +123,7 @@
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- if (expand_stack(vma, address))
+ if (expand_stack(vma, address, NULL))
goto bad_area;
/*
* Ok, we have a good vm_area for this memory access, so
diff -uNr linux-2.4.22/arch/sparc/mm/fault.c linux-2.4.22-ben2/arch/sparc/mm/fault.c
--- linux-2.4.22/arch/sparc/mm/fault.c 2003-06-13 16:51:32.000000000 +0200
+++ linux-2.4.22-ben2/arch/sparc/mm/fault.c 2003-08-31 11:42:09.000000000 +0200
@@ -251,7 +251,7 @@
goto good_area;
if(!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- if(expand_stack(vma, address))
+ if(expand_stack(vma, address, NULL))
goto bad_area;
/*
* Ok, we have a good vm_area for this memory access, so
@@ -498,7 +498,7 @@
goto good_area;
if(!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- if(expand_stack(vma, address))
+ if(expand_stack(vma, address, NULL))
goto bad_area;
good_area:
info.si_code = SEGV_ACCERR;
diff -uNr linux-2.4.22/arch/sparc64/mm/fault.c linux-2.4.22-ben2/arch/sparc64/mm/fault.c
--- linux-2.4.22/arch/sparc64/mm/fault.c 2002-11-29 00:53:12.000000000 +0100
+++ linux-2.4.22-ben2/arch/sparc64/mm/fault.c 2003-08-31 11:40:08.000000000 +0200
@@ -389,7 +389,7 @@
goto bad_area;
}
}
- if (expand_stack(vma, address))
+ if (expand_stack(vma, address, NULL))
goto bad_area;
/*
* Ok, we have a good vm_area for this memory access, so
diff -uNr linux-2.4.22/drivers/block/ll_rw_blk.c linux-2.4.22-ben2/drivers/block/ll_rw_blk.c
--- linux-2.4.22/drivers/block/ll_rw_blk.c 2003-08-25 13:44:41.000000000 +0200
+++ linux-2.4.22-ben2/drivers/block/ll_rw_blk.c 2003-08-31 11:39:42.000000000 +0200
@@ -121,6 +121,10 @@
unsigned long blk_max_low_pfn, blk_max_pfn;
int blk_nohighio = 0;
+int block_dump = 0;
+
+static struct timer_list writeback_timer;
+
static inline int get_max_sectors(kdev_t dev)
{
if (!max_sectors[MAJOR(dev)])
@@ -158,6 +162,12 @@
return i;
}
+void blk_queue_activity_fn(request_queue_t *q, activity_fn *fn, void *data)
+{
+ q->activity_fn = fn;
+ q->activity_data = data;
+}
+
/**
* blk_cleanup_queue: - release a &request_queue_t when it is no longer needed
* @q: the request queue to be released
@@ -540,6 +550,7 @@
q->head_active = 1;
blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH);
+ blk_queue_activity_fn(q, NULL, NULL);
}
#define blkdev_free_rq(list) list_entry((list)->next, struct request, queue);
@@ -855,6 +866,9 @@
static inline void add_request(request_queue_t * q, struct request * req,
struct list_head *insert_here)
{
+ if (q->activity_fn)
+ q->activity_fn(q->activity_data, req->cmd);
+
drive_stat_acct(req->rq_dev, req->cmd, req->nr_sectors, 1);
if (!q->plugged && q->head_active && insert_here == &q->queue_head) {
@@ -1302,6 +1316,9 @@
kstat.pgpgin += count;
break;
}
+
+ if (block_dump)
+ printk("%s: %s block %lu/%u on %s\n", current->comm, rw == WRITE ? "WRITE" : "READ", bh->b_rsector, count, kdevname(bh->b_rdev));
}
/**
@@ -1413,6 +1430,11 @@
extern int stram_device_init (void);
#endif
+static void blk_writeback_timer(unsigned long data)
+{
+ wakeup_bdflush();
+ wakeup_kupdate();
+}
/**
* end_that_request_first - end I/O on one buffer.
@@ -1469,10 +1491,18 @@
return 0;
}
+extern int laptop_mode;
+
void end_that_request_last(struct request *req)
{
struct completion *waiting = req->waiting;
+ /*
+ * schedule the writeout of pending dirty data when the disk is idle
+ */
+ if (laptop_mode && req->cmd == READ)
+ mod_timer(&writeback_timer, jiffies + 5 * HZ);
+
req_finished_io(req);
blkdev_release_request(req);
if (waiting)
@@ -1500,6 +1530,9 @@
blk_max_low_pfn = max_low_pfn - 1;
blk_max_pfn = max_pfn - 1;
+ init_timer(&writeback_timer);
+ writeback_timer.function = blk_writeback_timer;
+
#ifdef CONFIG_AMIGA_Z2RAM
z2_init();
#endif
diff -uNr linux-2.4.22/drivers/char/Config.in linux-2.4.22-ben2/drivers/char/Config.in
--- linux-2.4.22/drivers/char/Config.in 2003-08-25 13:44:41.000000000 +0200
+++ linux-2.4.22-ben2/drivers/char/Config.in 2003-08-31 11:40:19.000000000 +0200
@@ -315,6 +315,7 @@
if [ "$CONFIG_IA64" = "y" ]; then
bool ' HP ZX1 AGP support' CONFIG_AGP_HP_ZX1
fi
+ dep_bool ' Apple UniNorth support' CONFIG_AGP_UNINORTH $CONFIG_ALL_PPC
fi
bool 'Direct Rendering Manager (XFree86 DRI support)' CONFIG_DRM
diff -uNr linux-2.4.22/drivers/char/agp/agpgart_be.c linux-2.4.22-ben2/drivers/char/agp/agpgart_be.c
--- linux-2.4.22/drivers/char/agp/agpgart_be.c 2003-08-25 13:44:41.000000000 +0200
+++ linux-2.4.22-ben2/drivers/char/agp/agpgart_be.c 2003-08-31 11:41:22.000000000 +0200
@@ -52,6 +52,9 @@
#ifdef CONFIG_AGP_NVIDIA
#include
#endif
+#ifdef CONFIG_AGP_UNINORTH
+#include
+#endif
#include
#include "agp.h"
@@ -80,7 +83,7 @@
{
#if defined(__i386__) || defined(__x86_64__)
asm volatile ("wbinvd":::"memory");
-#elif defined(__alpha__) || defined(__ia64__) || defined(__sparc__)
+#elif defined(__alpha__) || defined(__ia64__) || defined(__sparc__) || defined(__powerpc__)
/* ??? I wonder if we'll really need to flush caches, or if the
core logic can manage to keep the system coherent. The ARM
speaks only of using `cflush' to get things in memory in
@@ -4735,6 +4738,405 @@
#endif /* CONFIG_AGP_HP_ZX1 */
+#ifdef CONFIG_AGP_UNINORTH
+
+static int uninorth_fetch_size(void)
+{
+ int i;
+ u32 temp;
+ aper_size_info_32 *values;
+
+ pci_read_config_dword(agp_bridge.dev, UNI_N_CFG_GART_BASE, &temp);
+ temp &= ~(0xfffff000);
+ values = A_SIZE_32(agp_bridge.aperture_sizes);
+
+ for (i = 0; i < agp_bridge.num_aperture_sizes; i++) {
+ if (temp == values[i].size_value) {
+ agp_bridge.previous_size =
+ agp_bridge.current_size = (void *) (values + i);
+ agp_bridge.aperture_size_idx = i;
+ return values[i].size;
+ }
+ }
+
+ agp_bridge.previous_size =
+ agp_bridge.current_size = (void *) (values + 1);
+ agp_bridge.aperture_size_idx = 1;
+ return values[1].size;
+
+ return 0;
+}
+
+static void uninorth_tlbflush(agp_memory * mem)
+{
+ pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL,
+ UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_INVAL);
+ pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL,
+ UNI_N_CFG_GART_ENABLE);
+ pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL,
+ UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_2xRESET);
+ pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL,
+ UNI_N_CFG_GART_ENABLE);
+}
+
+static void uninorth_cleanup(void)
+{
+ pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL,
+ UNI_N_CFG_GART_ENABLE | UNI_N_CFG_GART_INVAL);
+ pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL,
+ 0);
+ pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL,
+ UNI_N_CFG_GART_2xRESET);
+ pci_write_config_dword(agp_bridge.dev, UNI_N_CFG_GART_CTRL,
+ 0);
+}
+
+static int uninorth_configure(void)
+{
+ aper_size_info_32 *current_size;
+
+ current_size = A_SIZE_32(agp_bridge.current_size);
+
+ printk("agp: configuring for size idx: %d\n", current_size->size_value);
+
+ /* aperture size and gatt addr */
+ pci_write_config_dword(agp_bridge.dev,
+ UNI_N_CFG_GART_BASE,
+ (agp_bridge.gatt_bus_addr & 0xfffff000)
+ | current_size->size_value);
+
+ /* HACK ALERT
+ * UniNorth seem to be buggy enough not to handle properly when
+ * the AGP aperture isn't mapped at bus physical address 0
+ */
+ agp_bridge.gart_bus_addr = 0;
+ pci_write_config_dword(agp_bridge.dev,
+ UNI_N_CFG_AGP_BASE, agp_bridge.gart_bus_addr);
+
+ return 0;
+}
+
+static unsigned long uninorth_mask_memory(unsigned long addr, int type)
+{
+ return addr;/* | agp_bridge.masks[0].mask;*/
+}
+
+static int uninorth_insert_memory(agp_memory * mem,
+ off_t pg_start, int type)
+{
+ int i, j, num_entries;
+ void *temp;
+
+ temp = agp_bridge.current_size;
+ num_entries = A_SIZE_32(temp)->num_entries;
+
+ if (type != 0 || mem->type != 0) {
+ /* The generic routines know nothing of memory types */
+ return -EINVAL;
+ }
+ if ((pg_start + mem->page_count) > num_entries) {
+ return -EINVAL;
+ }
+ j = pg_start;
+
+ while (j < (pg_start + mem->page_count)) {
+ if (!PGE_EMPTY(agp_bridge.gatt_table[j])) {
+ return -EBUSY;
+ }
+ j++;
+ }
+
+ if (mem->is_flushed == FALSE) {
+ CACHE_FLUSH();
+ mem->is_flushed = TRUE;
+ }
+ for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+ agp_bridge.gatt_table[j] = cpu_to_le32((mem->memory[i] & 0xfffff000) | 0x00000001UL);
+ flush_dcache_range(__va(mem->memory[i]), __va(mem->memory[i])+0x1000);
+ }
+ (void)in_le32((volatile u32*)&agp_bridge.gatt_table[pg_start]);
+ mb();
+ flush_dcache_range((unsigned long)&agp_bridge.gatt_table[pg_start],
+ (unsigned long)&agp_bridge.gatt_table[pg_start + mem->page_count]);
+
+ agp_bridge.tlb_flush(mem);
+ return 0;
+}
+
+static void uninorth_agp_enable(u32 mode)
+{
+ struct pci_dev *device = NULL;
+ u32 command, scratch, cap_id;
+ u8 cap_ptr;
+
+ pci_read_config_dword(agp_bridge.dev,
+ agp_bridge.capndx + 4,
+ &command);
+
+ /*
+ * PASS1: go throu all devices that claim to be
+ * AGP devices and collect their data.
+ */
+
+ while ((device = pci_find_class(PCI_CLASS_DISPLAY_VGA << 8,
+ device)) != NULL) {
+ pci_read_config_dword(device, 0x04, &scratch);
+
+ if (!(scratch & 0x00100000))
+ continue;
+
+ pci_read_config_byte(device, 0x34, &cap_ptr);
+
+ if (cap_ptr != 0x00) {
+ do {
+ pci_read_config_dword(device,
+ cap_ptr, &cap_id);
+
+ if ((cap_id & 0xff) != 0x02)
+ cap_ptr = (cap_id >> 8) & 0xff;
+ }
+ while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00));
+ }
+ if (cap_ptr != 0x00) {
+ /*
+ * Ok, here we have a AGP device. Disable impossible
+ * settings, and adjust the readqueue to the minimum.
+ */
+
+ pci_read_config_dword(device, cap_ptr + 4, &scratch);
+
+ /* adjust RQ depth */
+ command =
+ ((command & ~0xff000000) |
+ min_t(u32, (mode & 0xff000000),
+ min_t(u32, (command & 0xff000000),
+ (scratch & 0xff000000))));
+
+ /* disable SBA if it's not supported */
+ if (!((command & 0x00000200) &&
+ (scratch & 0x00000200) &&
+ (mode & 0x00000200)))
+ command &= ~0x00000200;
+
+ /* disable FW if it's not supported */
+ if (!((command & 0x00000010) &&
+ (scratch & 0x00000010) &&
+ (mode & 0x00000010)))
+ command &= ~0x00000010;
+
+ if (!((command & 4) &&
+ (scratch & 4) &&
+ (mode & 4)))
+ command &= ~0x00000004;
+
+ if (!((command & 2) &&
+ (scratch & 2) &&
+ (mode & 2)))
+ command &= ~0x00000002;
+
+ if (!((command & 1) &&
+ (scratch & 1) &&
+ (mode & 1)))
+ command &= ~0x00000001;
+ }
+ }
+ /*
+ * PASS2: Figure out the 4X/2X/1X setting and enable the
+ * target (our motherboard chipset).
+ */
+
+ if (command & 4) {
+ command &= ~3; /* 4X */
+ }
+ if (command & 2) {
+ command &= ~5; /* 2X */
+ }
+ if (command & 1) {
+ command &= ~6; /* 1X */
+ }
+ command |= 0x00000100;
+
+ uninorth_tlbflush(NULL);
+
+ do {
+ pci_write_config_dword(agp_bridge.dev,
+ agp_bridge.capndx + 8,
+ command);
+ pci_read_config_dword(agp_bridge.dev,
+ agp_bridge.capndx + 8,
+ &scratch);
+ } while((scratch & 0x100) == 0);
+
+ /*
+ * PASS3: Go throu all AGP devices and update the
+ * command registers.
+ */
+
+ while ((device = pci_find_class(PCI_CLASS_DISPLAY_VGA << 8,
+ device)) != NULL) {
+ pci_read_config_dword(device, 0x04, &scratch);
+
+ if (!(scratch & 0x00100000))
+ continue;
+
+ pci_read_config_byte(device, 0x34, &cap_ptr);
+
+ if (cap_ptr != 0x00) {
+ do {
+ pci_read_config_dword(device,
+ cap_ptr, &cap_id);
+
+ if ((cap_id & 0xff) != 0x02)
+ cap_ptr = (cap_id >> 8) & 0xff;
+ }
+ while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00));
+ }
+ if (cap_ptr != 0x00)
+ pci_write_config_dword(device, cap_ptr + 8, command);
+ }
+
+ uninorth_tlbflush(NULL);
+}
+
+static int uninorth_create_gatt_table(void)
+{
+ char *table;
+ char *table_end;
+ int size;
+ int page_order;
+ int num_entries;
+ int i;
+ void *temp;
+ struct page *page;
+
+ /* The generic routines can't handle 2 level gatt's */
+ if (agp_bridge.size_type == LVL2_APER_SIZE) {
+ return -EINVAL;
+ }
+
+ table = NULL;
+ i = agp_bridge.aperture_size_idx;
+ temp = agp_bridge.current_size;
+ size = page_order = num_entries = 0;
+
+ do {
+ size = A_SIZE_32(temp)->size;
+ page_order = A_SIZE_32(temp)->page_order;
+ num_entries = A_SIZE_32(temp)->num_entries;
+
+ table = (char *) __get_free_pages(GFP_KERNEL, page_order);
+
+ if (table == NULL) {
+ i++;
+ agp_bridge.current_size = A_IDX32();
+ } else {
+ agp_bridge.aperture_size_idx = i;
+ }
+ } while ((table == NULL) &&
+ (i < agp_bridge.num_aperture_sizes));
+
+ if (table == NULL) {
+ return -ENOMEM;
+ }
+ table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
+
+ for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
+ SetPageReserved(page);
+
+ agp_bridge.gatt_table_real = (unsigned long *) table;
+ agp_bridge.gatt_table = (unsigned long *)table;
+ agp_bridge.gatt_bus_addr = virt_to_phys(table);
+
+ for (i = 0; i < num_entries; i++) {
+ agp_bridge.gatt_table[i] =
+ (unsigned long) agp_bridge.scratch_page;
+ }
+
+ flush_dcache_range((unsigned long)table, (unsigned long)table_end);
+
+ return 0;
+}
+
+static int uninorth_free_gatt_table(void)
+{
+ int page_order;
+ char *table, *table_end;
+ void *temp;
+ struct page *page;
+
+ temp = agp_bridge.current_size;
+ page_order = A_SIZE_32(temp)->page_order;
+
+ /* Do not worry about freeing memory, because if this is
+ * called, then all agp memory is deallocated and removed
+ * from the table.
+ */
+
+ table = (char *) agp_bridge.gatt_table_real;
+ table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
+
+ for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
+ ClearPageReserved(page);
+
+ free_pages((unsigned long) agp_bridge.gatt_table_real, page_order);
+
+ return 0;
+}
+
+
+/* Setup function */
+static gatt_mask uninorth_masks[] =
+{
+ {0x00000000, 0}
+};
+
+static aper_size_info_32 uninorth_sizes[7] =
+{
+#if 0 /* Not sure uninorth supports that high aperture sizes */
+ {256, 65536, 6, 64},
+ {128, 32768, 5, 32},
+ {64, 16384, 4, 16},
+#endif
+ {32, 8192, 3, 8},
+ {16, 4096, 2, 4},
+ {8, 2048, 1, 2},
+ {4, 1024, 0, 1}
+};
+
+static int __init uninorth_setup (struct pci_dev *pdev)
+{
+ agp_bridge.masks = uninorth_masks;
+ agp_bridge.aperture_sizes = (void *)uninorth_sizes;
+ agp_bridge.size_type = U32_APER_SIZE;
+ agp_bridge.num_aperture_sizes = 4; //7;
+ agp_bridge.dev_private_data = NULL;
+ agp_bridge.needs_scratch_page = FALSE;
+ agp_bridge.configure = uninorth_configure;
+ agp_bridge.fetch_size = uninorth_fetch_size;
+ agp_bridge.cleanup = uninorth_cleanup;
+ agp_bridge.tlb_flush = uninorth_tlbflush;
+ agp_bridge.mask_memory = uninorth_mask_memory;
+ agp_bridge.agp_enable = uninorth_agp_enable;
+ agp_bridge.cache_flush = global_cache_flush;
+ agp_bridge.create_gatt_table = uninorth_create_gatt_table;
+ agp_bridge.free_gatt_table = uninorth_free_gatt_table;
+ agp_bridge.insert_memory = uninorth_insert_memory;
+ agp_bridge.remove_memory = agp_generic_remove_memory;
+ agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
+ agp_bridge.free_by_type = agp_generic_free_by_type;
+ agp_bridge.agp_alloc_page = agp_generic_alloc_page;
+ agp_bridge.agp_destroy_page = agp_generic_destroy_page;
+ agp_bridge.suspend = agp_generic_suspend;
+ agp_bridge.resume = agp_generic_resume;
+ agp_bridge.cant_use_aperture = 1;
+
+ return 0;
+
+ (void) pdev; /* unused */
+}
+
+#endif /* CONFIG_AGP_UNINORTH */
+
/* per-chipset initialization data.
* note -- all chipsets for a single vendor MUST be grouped together
*/
@@ -5167,6 +5569,33 @@
hp_zx1_setup },
#endif
+#ifdef CONFIG_AGP_UNINORTH
+ { PCI_DEVICE_ID_APPLE_UNI_N_AGP,
+ PCI_VENDOR_ID_APPLE,
+ APPLE_UNINORTH,
+ "Apple",
+ "UniNorth",
+ uninorth_setup },
+ { PCI_DEVICE_ID_APPLE_UNI_N_AGP_P,
+ PCI_VENDOR_ID_APPLE,
+ APPLE_UNINORTH,
+ "Apple",
+ "UniNorth/Pangea",
+ uninorth_setup },
+ { PCI_DEVICE_ID_APPLE_UNI_N_AGP15,
+ PCI_VENDOR_ID_APPLE,
+ APPLE_UNINORTH,
+ "Apple",
+ "UniNorth 1.5",
+ uninorth_setup },
+ { PCI_DEVICE_ID_APPLE_UNI_N_AGP2,
+ PCI_VENDOR_ID_APPLE,
+ APPLE_UNINORTH,
+ "Apple",
+ "UniNorth 2",
+ uninorth_setup },
+#endif /* CONFIG_AGP_UNINORTH */
+
{ 0, }, /* dummy final entry, always present */
};
diff -uNr linux-2.4.22/drivers/char/defkeymap.c linux-2.4.22-ben2/drivers/char/defkeymap.c
--- linux-2.4.22/drivers/char/defkeymap.c 1999-10-07 19:17:09.000000000 +0200
+++ linux-2.4.22-ben2/drivers/char/defkeymap.c 2003-08-31 11:42:13.000000000 +0200
@@ -1,3 +1,4 @@
+
/* Do not edit this file! It was automatically generated by */
/* loadkeys --mktable defkeymap.map > defkeymap.c */
@@ -37,7 +38,7 @@
0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf10a,
0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116,
0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
@@ -56,7 +57,7 @@
0xf912, 0xf913, 0xf30b, 0xf90e, 0xf90f, 0xf910, 0xf30a, 0xf90b,
0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516,
0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
@@ -94,7 +95,7 @@
0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf200,
0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
@@ -132,7 +133,7 @@
0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf200, 0xf50a,
0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
- 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c,
0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
@@ -147,6 +148,7 @@
unsigned int keymap_count = 7;
+
/*
* Philosophy: most people do not define more strings, but they who do
* often want quite a lot of string space. So, we statically allocate
@@ -184,6 +186,7 @@
'\033', '[', 'P', 0,
};
+
char *funcbufptr = func_buf;
int funcbufsize = sizeof(func_buf);
int funcbufleft = 0; /* space left */
diff -uNr linux-2.4.22/drivers/char/drm/drmP.h linux-2.4.22-ben2/drivers/char/drm/drmP.h
--- linux-2.4.22/drivers/char/drm/drmP.h 2002-11-29 00:53:12.000000000 +0100
+++ linux-2.4.22-ben2/drivers/char/drm/drmP.h 2003-08-31 11:39:03.000000000 +0200
@@ -324,6 +324,15 @@
DRM(ioremapfree)( (map)->handle, (map)->size ); \
} while (0)
+#define DRM_IOREMAPAGP(map, dev) \
+ (map)->handle = DRM(ioremap_agp)( (map)->offset, (map)->size, (dev) )
+
+#define DRM_IOREMAPAGPFREE(map) \
+ do { \
+ if ( (map)->handle && (map)->size ) \
+ DRM(ioremap_agp_free)( (map)->handle, (map)->size ); \
+ } while (0)
+
#define DRM_FIND_MAP(_map, _o) \
do { \
struct list_head *_list; \
@@ -758,6 +767,9 @@
extern void *DRM(ioremap)(unsigned long offset, unsigned long size);
extern void DRM(ioremapfree)(void *pt, unsigned long size);
+extern void *DRM(ioremap_agp)(unsigned long offset, unsigned long size, drm_device_t *dev);
+extern void DRM(ioremap_agp_free)(void *pt, unsigned long size);
+
#if __REALLY_HAVE_AGP
extern agp_memory *DRM(alloc_agp)(int pages, u32 type);
extern int DRM(free_agp)(agp_memory *handle, int pages);
diff -uNr linux-2.4.22/drivers/char/drm/drm_memory.h linux-2.4.22-ben2/drivers/char/drm/drm_memory.h
--- linux-2.4.22/drivers/char/drm/drm_memory.h 2002-11-29 00:53:12.000000000 +0100
+++ linux-2.4.22-ben2/drivers/char/drm/drm_memory.h 2003-08-31 11:39:14.000000000 +0200
@@ -313,6 +313,108 @@
return pt;
}
+/* PPC specific routine used by ioremap_agp, to be replaced by some
+ * more generic implementation
+ */
+extern int map_page(unsigned long va, unsigned long pa, int flags);
+
+void *DRM(ioremap_agp)(unsigned long offset, unsigned long size, drm_device_t *dev)
+{
+ void *pt;
+ struct vm_struct *area;
+ struct drm_agp_mem *agpmem;
+ unsigned int flags = _PAGE_NO_CACHE|_PAGE_KERNEL|_PAGE_PRESENT|_PAGE_RW|_PAGE_DIRTY;
+ int err, i;
+
+ printk("drm: ioremap_agp, offset: 0x%08lx, size: 0x%08lx\n", offset, size);
+
+#if __REALLY_HAVE_AGP
+ if (!size) {
+ DRM_MEM_ERROR(DRM_MEM_MAPPINGS,
+ "Mapping 0 bytes at 0x%08lx\n", offset);
+ return NULL;
+ }
+
+ if (!dev->agp || !dev->agp->cant_use_aperture)
+ return DRM(ioremap)(offset, size);
+
+ /* XXX This has to be changed into something more generic
+ * this implementation is really only valid on PPC
+ */
+ area = get_vm_area(size, VM_IOREMAP);
+ if (area == 0) {
+ spin_lock(&DRM(mem_lock));
+ ++DRM(mem_stats)[DRM_MEM_MAPPINGS].fail_count;
+ spin_unlock(&DRM(mem_lock));
+ printk("->NULL\n");
+ return NULL;
+ }
+ pt = (void *)VMALLOC_VMADDR(area->addr);
+ err = 0;
+ for (i = 0; i < size && err == 0; i += PAGE_SIZE) {
+ unsigned long baddr = offset + i;
+ unsigned long index;
+
+ /*
+ * It's AGP memory - find the real physical page to map
+ */
+ for(agpmem = dev->agp->memory; agpmem; agpmem = agpmem->next) {
+ if (agpmem->bound <= baddr &&
+ agpmem->bound + agpmem->pages * PAGE_SIZE > baddr)
+ break;
+ }
+ if (!agpmem) {
+ printk("drm: not matching AGP page in ioremap_agp\n");
+ err = 1;
+ break;
+ }
+ index = (baddr - agpmem->bound) >> PAGE_SHIFT;
+ get_page(virt_to_page(__va(agpmem->memory->memory[index])));
+ err = map_page(((unsigned long)pt)+i, agpmem->memory->memory[index], flags);
+ }
+ if (err) {
+ vfree((void *)pt);
+ spin_lock(&DRM(mem_lock));
+ ++DRM(mem_stats)[DRM_MEM_MAPPINGS].fail_count;
+ spin_unlock(&DRM(mem_lock));
+ printk("->NULL\n");
+ return NULL;
+ }
+
+ spin_lock(&DRM(mem_lock));
+ ++DRM(mem_stats)[DRM_MEM_MAPPINGS].succeed_count;
+ DRM(mem_stats)[DRM_MEM_MAPPINGS].bytes_allocated += size;
+ spin_unlock(&DRM(mem_lock));
+ printk("->pt=0x%p\n", pt);
+ return pt;
+#else
+ return NULL;
+#endif
+}
+
+void DRM(ioremap_agp_free)(void *pt, unsigned long size)
+{
+ int alloc_count;
+ int free_count;
+
+ if (!pt)
+ DRM_MEM_ERROR(DRM_MEM_MAPPINGS,
+ "Attempt to free NULL pointer\n");
+ else
+ vfree(pt);
+
+ spin_lock(&DRM(mem_lock));
+ DRM(mem_stats)[DRM_MEM_MAPPINGS].bytes_freed += size;
+ free_count = ++DRM(mem_stats)[DRM_MEM_MAPPINGS].free_count;
+ alloc_count = DRM(mem_stats)[DRM_MEM_MAPPINGS].succeed_count;
+ spin_unlock(&DRM(mem_lock));
+ if (free_count > alloc_count) {
+ DRM_MEM_ERROR(DRM_MEM_MAPPINGS,
+ "Excess frees: %d frees, %d allocs\n",
+ free_count, alloc_count);
+ }
+}
+
void DRM(ioremapfree)(void *pt, unsigned long size)
{
int alloc_count;
diff -uNr linux-2.4.22/drivers/char/drm/drm_vm.h linux-2.4.22-ben2/drivers/char/drm/drm_vm.h
--- linux-2.4.22/drivers/char/drm/drm_vm.h 2003-06-13 16:51:32.000000000 +0200
+++ linux-2.4.22-ben2/drivers/char/drm/drm_vm.h 2003-08-31 11:42:47.000000000 +0200
@@ -379,7 +379,13 @@
if ( !priv->authenticated ) return -EACCES;
- if (!VM_OFFSET(vma)) return DRM(mmap_dma)(filp, vma);
+ /* We check for "dma". On Apple's UniNorth, it's valid to have
+ * the AGP mapped at physical address 0
+ * --BenH.
+ */
+ if (!VM_OFFSET(vma) && (!dev->agp ||
+ dev->agp->agp_info.device->vendor != PCI_VENDOR_ID_APPLE))
+ return DRM(mmap_dma)(filp, vma);
/* A sequential search of a linked list is
fine here because: 1) there will only be
@@ -419,16 +425,22 @@
switch (map->type) {
case _DRM_AGP:
-#if defined(__alpha__)
- /*
- * On Alpha we can't talk to bus dma address from the
- * CPU, so for memory of type DRM_AGP, we'll deal with
- * sorting out the real physical pages and mappings
- * in nopage()
- */
- vma->vm_ops = &DRM(vm_ops);
- break;
+ if (!dev->agp)
+ return -ENODEV;
+ if (dev->agp->cant_use_aperture) {
+ /*
+ * On Alpha we can't talk to bus dma address from the
+ * CPU, so for memory of type DRM_AGP, we'll deal with
+ * sorting out the real physical pages and mappings
+ * in nopage()
+ */
+#if defined(__powerpc__)
+ pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
#endif
+ vma->vm_flags |= VM_IO;
+ vma->vm_ops = &DRM(vm_ops);
+ break;
+ }
/* fall through to _DRM_FRAME_BUFFER... */
case _DRM_FRAME_BUFFER:
case _DRM_REGISTERS:
diff -uNr linux-2.4.22/drivers/char/drm/r128_cce.c linux-2.4.22-ben2/drivers/char/drm/r128_cce.c
--- linux-2.4.22/drivers/char/drm/r128_cce.c 2002-11-29 00:53:12.000000000 +0100
+++ linux-2.4.22-ben2/drivers/char/drm/r128_cce.c 2003-08-31 11:38:45.000000000 +0200
@@ -37,6 +37,10 @@
#define R128_FIFO_DEBUG 0
+#if __REALLY_HAVE_AGP && defined(CONFIG_ALL_PPC)
+extern unsigned long agp_special_page;
+#endif
+
/* CCE microcode (from ATI) */
static u32 r128_cce_microcode[] = {
@@ -340,6 +344,14 @@
SET_RING_HEAD( &dev_priv->ring, 0 );
if ( !dev_priv->is_pci ) {
+#if __REALLY_HAVE_AGP && defined(CONFIG_ALL_PPC)
+ if (_machine == _MACH_Pmac) {
+ dev_priv->ring.head = (__volatile__ u32 *) agp_special_page;
+ SET_RING_HEAD( &dev_priv->ring, 0 );
+ R128_WRITE( R128_PM4_BUFFER_DL_RPTR_ADDR,
+ __pa( dev_priv->ring.head ) );
+ } else
+#endif
R128_WRITE( R128_PM4_BUFFER_DL_RPTR_ADDR,
dev_priv->ring_rptr->offset );
} else {
@@ -549,9 +561,9 @@
init->sarea_priv_offset);
if ( !dev_priv->is_pci ) {
- DRM_IOREMAP( dev_priv->cce_ring );
- DRM_IOREMAP( dev_priv->ring_rptr );
- DRM_IOREMAP( dev_priv->buffers );
+ DRM_IOREMAPAGP( dev_priv->cce_ring, dev );
+ DRM_IOREMAPAGP( dev_priv->ring_rptr, dev );
+ DRM_IOREMAPAGP( dev_priv->buffers, dev );
if(!dev_priv->cce_ring->handle ||
!dev_priv->ring_rptr->handle ||
!dev_priv->buffers->handle) {
@@ -623,10 +635,11 @@
drm_r128_private_t *dev_priv = dev->dev_private;
if ( !dev_priv->is_pci ) {
- DRM_IOREMAPFREE( dev_priv->cce_ring );
- DRM_IOREMAPFREE( dev_priv->ring_rptr );
- DRM_IOREMAPFREE( dev_priv->buffers );
- } else {
+ DRM_IOREMAPAGPFREE( dev_priv->cce_ring );
+ DRM_IOREMAPAGPFREE( dev_priv->ring_rptr );
+ DRM_IOREMAPAGPFREE( dev_priv->buffers );
+ }
+ else {
if (!DRM(ati_pcigart_cleanup)( dev,
dev_priv->phys_pci_gart,
dev_priv->bus_pci_gart ))
diff -uNr linux-2.4.22/drivers/char/drm/r128_drv.h linux-2.4.22-ben2/drivers/char/drm/r128_drv.h
--- linux-2.4.22/drivers/char/drm/r128_drv.h 2002-11-29 00:53:12.000000000 +0100
+++ linux-2.4.22-ben2/drivers/char/drm/r128_drv.h 2003-08-31 11:41:32.000000000 +0200
@@ -34,8 +34,13 @@
#ifndef __R128_DRV_H__
#define __R128_DRV_H__
+#ifdef __powerpc__
+#define GET_RING_HEAD( ring ) in_le32((volatile u32 *)(ring)->head)
+#define SET_RING_HEAD( ring, val ) out_le32((volatile u32 *)(ring)->head, (val) )
+#else
#define GET_RING_HEAD( ring ) le32_to_cpu( *(ring)->head )
#define SET_RING_HEAD( ring, val ) *(ring)->head = cpu_to_le32( val )
+#endif
typedef struct drm_r128_freelist {
unsigned int age;
@@ -397,6 +402,9 @@
wmb(); \
R128_DEREF(reg) = val; \
} while (0)
+#elif defined (__powerpc__)
+#define R128_READ(reg) in_le32( (volatile u32 *)R128_ADDR( reg ) )
+#define R128_WRITE(reg,val) out_le32( (volatile u32 *)R128_ADDR( reg ), (val) )
#else
#define R128_READ(reg) le32_to_cpu( R128_DEREF( reg ) )
#define R128_WRITE(reg,val) \
@@ -418,6 +426,9 @@
wmb(); \
R128_DEREF8(reg) = val; \
} while (0)
+#elif defined (__powerpc__)
+#define R128_READ8(reg) in_8( (volatile u8 *)R128_ADDR(reg) )
+#define R128_WRITE8(reg,val) out_8( (volatile u8 *)R128_ADDR(reg), val )
#else
#define R128_READ8(reg) R128_DEREF8( reg )
#define R128_WRITE8(reg,val) do { R128_DEREF8( reg ) = val; } while (0)
@@ -493,8 +504,11 @@
* Ring control
*/
+#ifdef __powerpc__
+#define r128_flush_write_combine() (void)in_le32(ring)
+#else
#define r128_flush_write_combine() mb()
-
+#endif
#define R128_VERBOSE 0
diff -uNr linux-2.4.22/drivers/char/drm/radeon_cp.c linux-2.4.22-ben2/drivers/char/drm/radeon_cp.c
--- linux-2.4.22/drivers/char/drm/radeon_cp.c 2002-11-29 00:53:12.000000000 +0100
+++ linux-2.4.22-ben2/drivers/char/drm/radeon_cp.c 2003-08-31 11:40:28.000000000 +0200
@@ -37,12 +37,15 @@
#define RADEON_FIFO_DEBUG 0
-#if defined(__alpha__)
+#if defined(__alpha__) || defined(__powerpc__)
# define PCIGART_ENABLED
#else
# undef PCIGART_ENABLED
#endif
+#if __REALLY_HAVE_AGP && defined(CONFIG_ALL_PPC)
+extern unsigned long agp_special_page;
+#endif
/* CP microcode (from ATI) */
static u32 radeon_cp_microcode[][2] = {
@@ -313,7 +316,7 @@
return RADEON_READ(RADEON_CLOCK_CNTL_DATA);
}
-#if RADEON_FIFO_DEBUG
+#if 1 /* RADEON_FIFO_DEBUG */
static void radeon_status( drm_radeon_private_t *dev_priv )
{
printk( "%s:\n", __FUNCTION__ );
@@ -400,7 +403,8 @@
udelay( 1 );
}
-#if RADEON_FIFO_DEBUG
+#if 1 /* RADEON_FIFO_DEBUG */
+ printk("wait_for_idle timeout\n");
DRM_ERROR( "failed!\n" );
radeon_status( dev_priv );
#endif
@@ -611,6 +615,14 @@
dev_priv->ring.tail = cur_read_ptr;
if ( !dev_priv->is_pci ) {
+#if __REALLY_HAVE_AGP && defined(CONFIG_ALL_PPC)
+ if (_machine == _MACH_Pmac) {
+ dev_priv->ring.head = (__volatile__ u32 *) agp_special_page;
+ *dev_priv->ring.head = cur_read_ptr;
+ RADEON_WRITE( RADEON_CP_RB_RPTR_ADDR,
+ __pa( dev_priv->ring.head ) );
+ } else
+#endif
RADEON_WRITE( RADEON_CP_RB_RPTR_ADDR,
dev_priv->ring_rptr->offset );
} else {
@@ -835,9 +847,9 @@
init->sarea_priv_offset);
if ( !dev_priv->is_pci ) {
- DRM_IOREMAP( dev_priv->cp_ring );
- DRM_IOREMAP( dev_priv->ring_rptr );
- DRM_IOREMAP( dev_priv->buffers );
+ DRM_IOREMAPAGP( dev_priv->cp_ring, dev );
+ DRM_IOREMAPAGP( dev_priv->ring_rptr, dev );
+ DRM_IOREMAPAGP( dev_priv->buffers, dev );
if(!dev_priv->cp_ring->handle ||
!dev_priv->ring_rptr->handle ||
!dev_priv->buffers->handle) {
@@ -982,9 +994,9 @@
drm_radeon_private_t *dev_priv = dev->dev_private;
if ( !dev_priv->is_pci ) {
- DRM_IOREMAPFREE( dev_priv->cp_ring );
- DRM_IOREMAPFREE( dev_priv->ring_rptr );
- DRM_IOREMAPFREE( dev_priv->buffers );
+ DRM_IOREMAPAGPFREE( dev_priv->cp_ring );
+ DRM_IOREMAPAGPFREE( dev_priv->ring_rptr );
+ DRM_IOREMAPAGPFREE( dev_priv->buffers );
} else {
if (!DRM(ati_pcigart_cleanup)( dev,
dev_priv->phys_pci_gart,
@@ -1351,14 +1363,15 @@
int i;
for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
- radeon_update_ring_snapshot( ring );
+ radeon_update_ring_snapshot( dev_priv, ring );
if ( ring->space > n )
return 0;
udelay( 1 );
}
/* FIXME: This return value is ignored in the BEGIN_RING macro! */
-#if RADEON_FIFO_DEBUG
+#if 1 /* RADEON_FIFO_DEBUG */
+ printk("wait ring timeout\n");
radeon_status( dev_priv );
DRM_ERROR( "failed!\n" );
#endif
diff -uNr linux-2.4.22/drivers/char/drm/radeon_drv.h linux-2.4.22-ben2/drivers/char/drm/radeon_drv.h
--- linux-2.4.22/drivers/char/drm/radeon_drv.h 2002-11-29 00:53:12.000000000 +0100
+++ linux-2.4.22-ben2/drivers/char/drm/radeon_drv.h 2003-08-31 11:38:53.000000000 +0200
@@ -31,6 +31,14 @@
#ifndef __RADEON_DRV_H__
#define __RADEON_DRV_H__
+#if defined(__powerpc__)
+#define GET_RING_HEAD(ring) in_le32((volatile u32 *)(ring)->head)
+#define SET_RING_HEAD(ring,val) out_le32((volatile u32 *)(ring)->head, (val))
+#else
+#define GET_RING_HEAD(ring) le32_to_cpu(*(ring)->head)
+#define SET_RING_HEAD(ring,val) *(ring)->head = cpu_to_le32(val)
+#endif
+
typedef struct drm_radeon_freelist {
unsigned int age;
drm_buf_t *buf;
@@ -150,12 +158,7 @@
extern int radeon_wait_ring( drm_radeon_private_t *dev_priv, int n );
static inline void
-radeon_update_ring_snapshot( drm_radeon_ring_buffer_t *ring )
-{
- ring->space = (*(volatile int *)ring->head - ring->tail) * sizeof(u32);
- if ( ring->space <= 0 )
- ring->space += ring->size;
-}
+radeon_update_ring_snapshot( drm_radeon_private_t *dev_priv, drm_radeon_ring_buffer_t *ring );
extern int radeon_do_cp_idle( drm_radeon_private_t *dev_priv );
extern int radeon_do_cleanup_cp( drm_device_t *dev );
@@ -539,6 +542,9 @@
wmb(); \
RADEON_DEREF(reg) = val; \
} while (0)
+#elif defined(__powerpc__)
+#define RADEON_READ(reg) in_le32((volatile u32 *)RADEON_ADDR(reg))
+#define RADEON_WRITE(reg,val) out_le32((volatile u32 *)RADEON_ADDR(reg), (val))
#else
#define RADEON_READ(reg) RADEON_DEREF( reg )
#define RADEON_WRITE(reg, val) do { RADEON_DEREF( reg ) = val; } while (0)
@@ -557,6 +563,9 @@
wmb(); \
RADEON_DEREF8( reg ) = val; \
} while (0)
+#elif defined(__powerpc__)
+#define RADEON_READ8(reg) in_8((volatile u8 *)RADEON_ADDR(reg))
+#define RADEON_WRITE8(reg,val) out_8((volatile u8 *)RADEON_ADDR(reg), (val))
#else
#define RADEON_READ8(reg) RADEON_DEREF8( reg )
#define RADEON_WRITE8(reg, val) do { RADEON_DEREF8( reg ) = val; } while (0)
@@ -652,7 +661,7 @@
drm_radeon_ring_buffer_t *ring = &dev_priv->ring; int i; \
if ( ring->space < ring->high_mark ) { \
for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { \
- radeon_update_ring_snapshot( ring ); \
+ radeon_update_ring_snapshot( dev_priv, ring ); \
if ( ring->space >= ring->high_mark ) \
goto __ring_space_done; \
udelay( 1 ); \
@@ -694,12 +703,28 @@
* Ring control
*/
+#if defined(__powerpc__)
+#define radeon_flush_write_combine() do { mb(); (void)in_le32(ring); mb(); } while(0)
+#else
#define radeon_flush_write_combine() mb()
-
+#endif
#define RADEON_VERBOSE 0
+#define RADEON_DEBUG
+#ifdef RADEON_DEBUG
+#define RING_LOCALS int write; unsigned int mask; volatile u32 *ring; int dbg;
+#define DEBUG_INIT(n) dbg = n
+#define DEBUG_INC() do { if (dbg <= 0) printk("Argh 1 ! (%s:%d)\n", __FILE__, __LINE__); \
+ dbg--; } while(0)
+#define DEBUG_END() do { if (dbg != 0) printk("Argh 2/%d ! (%s:%d)\n", dbg, __FILE__, __LINE__); \
+ dbg--; } while(0)
+#else
#define RING_LOCALS int write; unsigned int mask; volatile u32 *ring;
+#define DEBUG_INIT()
+#define DEBUG_INC()
+#define DEBUG_END()
+#endif
#define BEGIN_RING( n ) do { \
if ( RADEON_VERBOSE ) { \
@@ -713,6 +738,7 @@
ring = dev_priv->ring.start; \
write = dev_priv->ring.tail; \
mask = dev_priv->ring.tail_mask; \
+ DEBUG_INIT(n); \
} while (0)
#define ADVANCE_RING() do { \
@@ -720,6 +746,7 @@
DRM_INFO( "ADVANCE_RING() wr=0x%06x tail=0x%06x\n", \
write, dev_priv->ring.tail ); \
} \
+ DEBUG_END(); \
radeon_flush_write_combine(); \
dev_priv->ring.tail = write; \
RADEON_WRITE( RADEON_CP_RB_WPTR, write ); \
@@ -730,10 +757,20 @@
DRM_INFO( " OUT_RING( 0x%08x ) at 0x%x\n", \
(unsigned int)(x), write ); \
} \
- ring[write++] = (x); \
+ DEBUG_INC(); \
+ ring[write++] = cpu_to_le32(x); \
write &= mask; \
} while (0)
#define RADEON_PERFORMANCE_BOXES 0
+static inline void
+radeon_update_ring_snapshot( drm_radeon_private_t *dev_priv, drm_radeon_ring_buffer_t *ring )
+{
+// ring->space = (GET_RING_HEAD(ring) - ring->tail) * sizeof(u32);
+ ring->space = (RADEON_READ(0x710) - ring->tail) * sizeof(u32);
+ if ( ring->space <= 0 )
+ ring->space += ring->size;
+}
+
#endif /* __RADEON_DRV_H__ */
diff -uNr linux-2.4.22/drivers/char/drm/radeon_state.c linux-2.4.22-ben2/drivers/char/drm/radeon_state.c
--- linux-2.4.22/drivers/char/drm/radeon_state.c 2002-11-29 00:53:12.000000000 +0100
+++ linux-2.4.22-ben2/drivers/char/drm/radeon_state.c 2003-08-31 11:40:20.000000000 +0200
@@ -849,7 +849,7 @@
u32 *data = (u32 *)
((char *)dev_priv->buffers->handle
+ buf->offset + start);
- data[dwords++] = RADEON_CP_PACKET2;
+ data[dwords++] = cpu_to_le32(RADEON_CP_PACKET2);
}
buf_priv->dispatched = 1;
@@ -913,18 +913,22 @@
data = (u32 *)((char *)dev_priv->buffers->handle
+ buf->offset + start);
- data[0] = CP_PACKET3( RADEON_3D_RNDR_GEN_INDX_PRIM, dwords-2 );
+ data[0] = cpu_to_le32(CP_PACKET3( RADEON_3D_RNDR_GEN_INDX_PRIM, dwords-2 ));
- data[1] = offset;
- data[2] = RADEON_MAX_VB_VERTS;
- data[3] = format;
- data[4] = (prim | RADEON_PRIM_WALK_IND |
+ data[1] = cpu_to_le32(offset);
+ data[2] = cpu_to_le32(RADEON_MAX_VB_VERTS);
+ data[3] = cpu_to_le32(format);
+ data[4] = cpu_to_le32((prim | RADEON_PRIM_WALK_IND |
RADEON_COLOR_ORDER_RGBA |
RADEON_VTX_FMT_RADEON_MODE |
- (count << RADEON_NUM_VERTICES_SHIFT) );
+ (count << RADEON_NUM_VERTICES_SHIFT) ));
if ( count & 0x1 ) {
+#ifdef __LITTLE_ENDIAN
data[dwords-1] &= 0x0000ffff;
+#else
+ data[dwords-1] &= 0xffff0000;
+#endif
}
do {
@@ -1067,22 +1071,22 @@
*/
buffer = (u32 *)((char *)dev_priv->buffers->handle + buf->offset);
- buffer[0] = CP_PACKET3( RADEON_CNTL_HOSTDATA_BLT, dwords + 6 );
- buffer[1] = (RADEON_GMC_DST_PITCH_OFFSET_CNTL |
+ buffer[0] = cpu_to_le32(CP_PACKET3( RADEON_CNTL_HOSTDATA_BLT, dwords + 6 ));
+ buffer[1] = cpu_to_le32((RADEON_GMC_DST_PITCH_OFFSET_CNTL |
RADEON_GMC_BRUSH_NONE |
(format << 8) |
RADEON_GMC_SRC_DATATYPE_COLOR |
RADEON_ROP3_S |
RADEON_DP_SRC_SOURCE_HOST_DATA |
RADEON_GMC_CLR_CMP_CNTL_DIS |
- RADEON_GMC_WR_MSK_DIS);
+ RADEON_GMC_WR_MSK_DIS));
- buffer[2] = (tex->pitch << 22) | (tex->offset >> 10);
- buffer[3] = 0xffffffff;
- buffer[4] = 0xffffffff;
- buffer[5] = (y << 16) | image->x;
- buffer[6] = (height << 16) | image->width;
- buffer[7] = dwords;
+ buffer[2] = cpu_to_le32((tex->pitch << 22) | (tex->offset >> 10));
+ buffer[3] = cpu_to_le32(0xffffffff);
+ buffer[4] = cpu_to_le32(0xffffffff);
+ buffer[5] = cpu_to_le32((y << 16) | image->x);
+ buffer[6] = cpu_to_le32((height << 16) | image->width);
+ buffer[7] = cpu_to_le32(dwords);
buffer += 8;
diff -uNr linux-2.4.22/drivers/i2c/i2c-keywest.c linux-2.4.22-ben2/drivers/i2c/i2c-keywest.c
--- linux-2.4.22/drivers/i2c/i2c-keywest.c 2002-11-29 00:53:13.000000000 +0100
+++ linux-2.4.22-ben2/drivers/i2c/i2c-keywest.c 2003-08-31 11:38:53.000000000 +0200
@@ -256,6 +256,7 @@
len = 1;
buffer = &data->byte;
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+ //iface->cur_mode |= KW_I2C_MODE_COMBINED;
break;
case I2C_SMBUS_WORD_DATA:
len = 2;
@@ -267,6 +268,7 @@
len = data->block[0];
buffer = &data->block[1];
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+ //iface->cur_mode |= KW_I2C_MODE_COMBINED;
break;
default:
return -1;
diff -uNr linux-2.4.22/drivers/ide/Config.in linux-2.4.22-ben2/drivers/ide/Config.in
--- linux-2.4.22/drivers/ide/Config.in 2003-08-25 13:44:41.000000000 +0200
+++ linux-2.4.22-ben2/drivers/ide/Config.in 2003-08-31 11:38:52.000000000 +0200
@@ -86,6 +86,9 @@
dep_bool ' Probe internal Kauai ATA/100 first' CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST $CONFIG_BLK_DEV_IDE_PMAC
dep_bool ' PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC $CONFIG_BLK_DEV_IDE_PMAC
dep_bool ' Use DMA by default' CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO $CONFIG_BLK_DEV_IDEDMA_PMAC
+ if [ "$CONFIG_ADB_PMU" = "y" ]; then
+ bool ' Blink laptop LED on activity' CONFIG_PMU_HD_BLINK
+ fi
if [ "$CONFIG_BLK_DEV_IDE_PMAC" = "y" ]; then
define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PMAC
fi
diff -uNr linux-2.4.22/drivers/ide/arm/rapide.c linux-2.4.22-ben2/drivers/ide/arm/rapide.c
--- linux-2.4.22/drivers/ide/arm/rapide.c 2003-06-13 16:51:33.000000000 +0200
+++ linux-2.4.22-ben2/drivers/ide/arm/rapide.c 2003-08-31 11:38:48.000000000 +0200
@@ -40,6 +40,7 @@
}
hw.io_ports[IDE_CONTROL_OFFSET] = port + 0x206;
hw.irq = ec->irq;
+ hw.chipset = ide_generic;
return ide_register_hw(&hw, NULL);
}
diff -uNr linux-2.4.22/drivers/ide/ide-disk.c linux-2.4.22-ben2/drivers/ide/ide-disk.c
--- linux-2.4.22/drivers/ide/ide-disk.c 2003-06-13 16:51:33.000000000 +0200
+++ linux-2.4.22-ben2/drivers/ide/ide-disk.c 2003-08-31 11:38:57.000000000 +0200
@@ -1161,13 +1161,17 @@
{
struct hd_driveid *id = drive->id;
unsigned long capacity = drive->cyl * drive->head * drive->sect;
- unsigned long set_max = idedisk_read_native_max_address(drive);
+ unsigned long set_max = 0;
unsigned long long capacity_2 = capacity;
unsigned long long set_max_ext;
drive->capacity48 = 0;
drive->select.b.lba = 0;
+ /* That stupid compact flash doesn't like the command */
+ if (!drive->is_flash)
+ set_max = idedisk_read_native_max_address(drive);
+
(void) idedisk_supports_host_protected_area(drive);
if (id->cfs_enable_2 & 0x0400) {
@@ -1566,7 +1570,7 @@
static int set_lba_addressing (ide_drive_t *drive, int arg)
{
- return (probe_lba_addressing(drive, arg));
+ return probe_lba_addressing(drive, arg);
}
static void idedisk_add_settings(ide_drive_t *drive)
@@ -1644,13 +1648,20 @@
break;
}
-#if 1
(void) probe_lba_addressing(drive, 1);
-#else
- /* if using 48-bit addressing bump the request size up */
- if (probe_lba_addressing(drive, 1))
- blk_queue_max_sectors(&drive->queue, 2048);
-#endif
+
+ if (drive->addressing == 1) {
+ int max_s = 2048, i, off = drive->select.b.unit << PARTN_BITS;
+ ide_hwif_t *hwif = HWIF(drive);
+
+ if (max_s > hwif->rqsize)
+ max_s = hwif->rqsize;
+
+ for (i = 0; i < (1 << PARTN_BITS); i++)
+ max_sectors[hwif->major][off + i] = max_s;
+
+ printk("%s: max request size: %dKiB\n", drive->name, max_s / 2);
+ }
/* Extract geometry if we did not already have one for the drive */
if (!drive->cyl || !drive->head || !drive->sect) {
diff -uNr linux-2.4.22/drivers/ide/ide-probe.c linux-2.4.22-ben2/drivers/ide/ide-probe.c
--- linux-2.4.22/drivers/ide/ide-probe.c 2003-08-25 13:44:41.000000000 +0200
+++ linux-2.4.22-ben2/drivers/ide/ide-probe.c 2003-08-31 11:40:20.000000000 +0200
@@ -972,6 +972,11 @@
q->queuedata = HWGROUP(drive);
blk_init_queue(q, do_ide_request);
blk_queue_throttle_sectors(q, 1);
+ /*
+ * enable led activity for disk drives only
+ */
+ if (drive->media == ide_disk && HWIF(drive)->led_act)
+ blk_queue_activity_fn(&drive->queue, HWIF(drive)->led_act, drive);
}
#undef __IRQ_HELL_SPIN
@@ -1164,6 +1169,9 @@
units = MAX_DRIVES;
+ if (!hwif->rqsize)
+ hwif->rqsize = hwif->addressing ? 128 : 65536;
+
minors = units * (1<major] = max_sect;
max_readahead[hwif->major] = max_ra;
for (unit = 0; unit < minors; ++unit) {
+ int max_s = 128;
+
*bs++ = BLOCK_SIZE;
- /*
- * IDE can do up to 128K per request == 256
- */
- *max_sect++ = ((hwif->rqsize) ? hwif->rqsize : 128);
+
+ if (max_s > hwif->rqsize)
+ max_s = hwif->rqsize;
+
+ *max_sect++ = max_s;
*max_ra++ = vm_max_readahead;
}
diff -uNr linux-2.4.22/drivers/ide/ppc/pmac.c linux-2.4.22-ben2/drivers/ide/ppc/pmac.c
--- linux-2.4.22/drivers/ide/ppc/pmac.c 2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.22-ben2/drivers/ide/ppc/pmac.c 2003-08-31 11:38:42.000000000 +0200
@@ -38,10 +38,8 @@
#include
#include
#include
-#ifdef CONFIG_PMAC_PBOOK
#include
#include
-#endif
#include "ide_modes.h"
#include "ide-timing.h"
@@ -357,6 +355,89 @@
};
#endif /* CONFIG_PMAC_PBOOK */
+#ifdef CONFIG_PMU_HD_BLINK
+
+/* Set to 50ms */
+#define PMU_HD_BLINK_TIME (HZ/20)
+
+static struct adb_request pmu_blink_on, pmu_blink_off;
+static spinlock_t pmu_blink_lock;
+static unsigned long pmu_blink_stoptime;
+static int pmu_blink_ledstate;
+static struct timer_list pmu_blink_timer;
+
+static void
+pmu_hd_blink_timeout(unsigned long data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pmu_blink_lock, flags);
+
+ /* We may have been triggered again in a racy way, check
+ * that we really want to switch it off
+ */
+ if (time_after(pmu_blink_stoptime, jiffies))
+ goto done;
+
+ /* Previous req. not complete, try 50ms more */
+ if (pmu_blink_off.complete == 0)
+ mod_timer(&pmu_blink_timer, jiffies + PMU_HD_BLINK_TIME);
+ else if (pmu_blink_ledstate) {
+ pmu_request(&pmu_blink_off, NULL, 4, 0xee, 4, 0, 0);
+ pmu_blink_ledstate = 0;
+ }
+done:
+ spin_unlock_irqrestore(&pmu_blink_lock, flags);
+}
+
+static void
+pmu_hd_kick_blink(void *data, int rw)
+{
+ unsigned long flags;
+
+ pmu_blink_stoptime = jiffies + PMU_HD_BLINK_TIME;
+ wmb();
+ mod_timer(&pmu_blink_timer, pmu_blink_stoptime);
+ if (pmu_blink_ledstate == 1)
+ return;
+ spin_lock_irqsave(&pmu_blink_lock, flags);
+ if (pmu_blink_on.complete && !pmu_blink_ledstate) {
+ pmu_request(&pmu_blink_on, NULL, 4, 0xee, 4, 0, 1);
+ pmu_blink_ledstate = 1;
+ }
+ spin_unlock_irqrestore(&pmu_blink_lock, flags);
+}
+
+static int
+pmu_hd_blink_init(void)
+{
+ struct device_node *dt;
+ const char *model;
+
+ if (pmu_get_model() != PMU_KEYLARGO_BASED)
+ return 0;
+
+ dt = find_devices("device-tree");
+ if (dt == NULL)
+ return 0;
+ model = (const char *)get_property(dt, "model", NULL);
+ if (model == NULL)
+ return 0;
+ if (strncmp(model, "PowerBook", strlen("PowerBook")) != 0 &&
+ strncmp(model, "iBook", strlen("iBook")) != 0)
+ return 0;
+
+ pmu_blink_on.complete = 1;
+ pmu_blink_off.complete = 1;
+ spin_lock_init(&pmu_blink_lock);
+ init_timer(&pmu_blink_timer);
+ pmu_blink_timer.function = pmu_hd_blink_timeout;
+
+ return 1;
+}
+
+#endif /* CONFIG_PMU_HD_BLINK */
+
/*
* N.B. this can't be an initfunc, because the media-bay task can
* call ide_[un]register at any time.
@@ -462,7 +543,7 @@
ide_hwif_t *hwif = HWIF(drive);
int result = 1;
- disable_irq(hwif->irq); /* disable_irq_nosync ?? */
+ disable_irq_nosync(hwif->irq);
udelay(1);
SELECT_DRIVE(drive);
SELECT_MASK(drive, 0);
@@ -945,9 +1026,12 @@
unsigned long base, regbase;
int irq;
ide_hwif_t *hwif;
-
+#ifdef CONFIG_PMU_HD_BLINK
+ int has_blink;
+#endif
if (_machine != _MACH_Pmac)
return;
+
pp = &atas;
rp = &removables;
p = find_devices("ATA");
@@ -957,6 +1041,14 @@
p = find_type_devices("ide");
if (p == NULL)
p = find_type_devices("ata");
+
+ if (p == NULL)
+ return;
+
+#ifdef CONFIG_PMU_HD_BLINK
+ has_blink = pmu_hd_blink_init();
+#endif /* CONFIG_PMU_HD_BLINK */
+
/* Move removable devices such as the media-bay CDROM
on the PB3400 to the end of the list. */
for (; p != NULL; p = nextp) {
@@ -1163,6 +1255,11 @@
hwif->selectproc = pmac_ide_selectproc;
hwif->speedproc = pmac_ide_tune_chipset;
+#ifdef CONFIG_PMU_HD_BLINK
+ if (has_blink)
+ hwif->led_act = pmu_hd_kick_blink;
+#endif /* CONFIG_PMU_HD_BLINK */
+
printk(KERN_INFO "ide%d: Found Apple %s controller, bus ID %d%s\n",
i, model_name[pmif->kind], pmif->aapl_bus_id,
in_bay ? " (mediabay)" : "");
@@ -1207,6 +1304,8 @@
#ifdef CONFIG_PMAC_PBOOK
pmu_register_sleep_notifier(&idepmac_sleep_notifier);
#endif /* CONFIG_PMAC_PBOOK */
+
+ mdelay(IDE_WAKEUP_DELAY_MS);
}
#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
diff -uNr linux-2.4.22/drivers/ide/setup-pci.c linux-2.4.22-ben2/drivers/ide/setup-pci.c
--- linux-2.4.22/drivers/ide/setup-pci.c 2003-08-25 13:44:41.000000000 +0200
+++ linux-2.4.22-ben2/drivers/ide/setup-pci.c 2003-08-31 11:42:58.000000000 +0200
@@ -51,7 +51,8 @@
{
int h;
ide_hwif_t *hwif;
-
+ int reserved = ide_reserved_hwifs();
+
/*
* Look for a hwif with matching io_base specified using
* parameters to ide_setup().
@@ -90,18 +91,18 @@
*/
if (bootable) {
for (h = 0; h < MAX_HWIFS; ++h) {
- hwif = &ide_hwifs[h];
+ hwif = ide_hwifs + h;
if (hwif->chipset == ide_unknown)
return hwif; /* pick an unused entry */
}
} else {
- for (h = 2; h < MAX_HWIFS; ++h) {
+ for (h = reserved; h < MAX_HWIFS; ++h) {
hwif = ide_hwifs + h;
if (hwif->chipset == ide_unknown)
return hwif; /* pick an unused entry */
}
}
- for (h = 0; h < 2; ++h) {
+ for (h = 0; h < reserved; ++h) {
hwif = ide_hwifs + h;
if (hwif->chipset == ide_unknown)
return hwif; /* pick an unused entry */
diff -uNr linux-2.4.22/drivers/macintosh/Makefile linux-2.4.22-ben2/drivers/macintosh/Makefile
--- linux-2.4.22/drivers/macintosh/Makefile 2002-11-29 00:53:13.000000000 +0100
+++ linux-2.4.22-ben2/drivers/macintosh/Makefile 2003-08-31 11:42:35.000000000 +0200
@@ -35,6 +35,8 @@
obj-$(CONFIG_INPUT_ADBHID) += adbhid.o
obj-$(CONFIG_PPC_RTC) += rtc.o
obj-$(CONFIG_ANSLCD) += ans-lcd.o
+#obj-$(CONFIG_I2C) += therm_pismo.o
+#obj-$(CONFIG_I2C) += therm_albooks.o
obj-$(CONFIG_ADB_PMU) += via-pmu.o
obj-$(CONFIG_ADB_CUDA) += via-cuda.o
diff -uNr linux-2.4.22/drivers/macintosh/macserial.c linux-2.4.22-ben2/drivers/macintosh/macserial.c
--- linux-2.4.22/drivers/macintosh/macserial.c 2002-08-03 02:39:44.000000000 +0200
+++ linux-2.4.22-ben2/drivers/macintosh/macserial.c 2003-08-31 11:39:32.000000000 +0200
@@ -59,7 +59,7 @@
};
#endif
-#define SUPPORT_SERIAL_DMA
+#undef SUPPORT_SERIAL_DMA
#define MACSERIAL_VERSION "2.0"
/*
@@ -1134,6 +1134,8 @@
*/
static void shutdown(struct mac_serial * info)
{
+ unsigned long flags;
+
OPNDBG("Shutting down serial port %d (irq %d)....\n", info->line,
info->irq);
@@ -1142,6 +1144,8 @@
return;
}
+ save_flags(flags); cli(); /* Disable interrupts */
+
if (info->has_dma) {
del_timer(&info->poll_dma_timer);
dbdma_reset(info->tx_dma);
@@ -1151,6 +1155,8 @@
}
disable_irq(info->irq);
+ restore_flags(flags);
+
info->pendregs[1] = info->curregs[1] = 0;
write_zsreg(info->zs_channel, 1, 0); /* no interrupts */
@@ -1980,6 +1986,7 @@
return;
}
info->flags |= ZILOG_CLOSING;
+ restore_flags(flags);
/*
* Save the termios structure, since this port may have
* separate termios for callout and dialin.
@@ -1994,11 +2001,8 @@
*/
OPNDBG("waiting end of Tx... (timeout:%d)\n", info->closing_wait);
tty->closing = 1;
- if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) {
- restore_flags(flags);
+ if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, info->closing_wait);
- save_flags(flags); cli();
- }
/*
* At this point we stop accepting input. To do this, we
@@ -2017,15 +2021,10 @@
* has completely drained.
*/
OPNDBG("waiting end of Rx...\n");
- restore_flags(flags);
rs_wait_until_sent(tty, info->timeout);
- save_flags(flags); cli();
}
shutdown(info);
- /* restore flags now since shutdown() will have disabled this port's
- specific irqs */
- restore_flags(flags);
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
@@ -2880,7 +2879,7 @@
static int __init serial_console_setup(struct console *co, char *options)
{
struct mac_serial *info;
- int baud = 38400;
+ int baud = 57600; /*38400;*/
int bits = 8;
int parity = 'n';
int cflag = CREAD | HUPCL | CLOCAL;
diff -uNr linux-2.4.22/drivers/macintosh/therm_pismo.c linux-2.4.22-ben2/drivers/macintosh/therm_pismo.c
--- linux-2.4.22/drivers/macintosh/therm_pismo.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/drivers/macintosh/therm_pismo.c 2003-08-31 11:39:12.000000000 +0200
@@ -0,0 +1,269 @@
+/*
+ * Device driver for the i2c thermostat found on some laptops
+ *
+ * Copyright (C) 2001 Benjamin Herrenschmidt
+ *
+ * Actually, there are 2 DS1775R1 on uninorth I2C busses
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define DEBUG
+
+#define I2C_THERMOSTAT_ADDR 0x49
+#define I2C_THERMOSTAT_ADDR2 0x48
+#define MAX_THERMOSTATS 4
+
+
+MODULE_AUTHOR("Benjamin Herrenschmidt ");
+MODULE_DESCRIPTION("Driver for DS1775 thermostat on Apple laptops");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
+
+struct temp_range
+{
+ u8 high; /* Start the fan */
+ u8 low; /* Stop the fan */
+};
+
+struct apple_thermal_info {
+ u8 id; /* Implementation ID */
+ u8 fan_count; /* Number of fans */
+ u8 thermostat_count; /* Number of thermostats */
+ u8 unused[5];
+ struct temp_range ranges[4]; /* Temperature ranges (may be [])*/
+};
+
+struct thermostat {
+ struct i2c_client clt;
+ int th_num;
+ // more to come ?
+};
+
+static struct apple_thermal_info *thinfo;
+static struct thermostat* thermostats[MAX_THERMOSTATS];
+static int therm_count;
+
+static int attach_thermostat(struct i2c_adapter *adapter);
+static int detach_thermostat(struct i2c_client *client);
+
+/* What is this supposed to be ? registered ? I hate
+ * magic numbers like that ...
+ */
+#define I2C_DRIVERID_THERMOSTAT (0xDEAD)
+
+static struct i2c_driver thermostat_driver = {
+ name: "Apple Thermostat",
+ id: I2C_DRIVERID_THERMOSTAT,
+ flags: I2C_DF_NOTIFY,
+ attach_adapter: &attach_thermostat,
+ detach_client: &detach_thermostat,
+ command: NULL,
+ inc_use: NULL,
+ dec_use: NULL
+};
+
+static int
+write_reg(struct thermostat* th, int reg, u8* data, int len)
+{
+ u8 tmp[5];
+ int rc;
+
+ if (len > 4)
+ return -EINVAL;
+ tmp[0] = reg;
+ memcpy(&tmp[1], data, len);
+ rc = i2c_master_send(&th->clt, (const char *)tmp, len+1);
+ if (rc < 0)
+ return rc;
+ if (rc != (len+1))
+ return -ENODEV;
+ return 0;
+}
+
+static int
+read_reg(struct thermostat* th, int reg, u8* data, int len)
+{
+ u8 reg_addr;
+ int rc;
+
+ reg_addr = (u8)reg;
+ rc = i2c_master_send(&th->clt, ®_addr, 1);
+ if (rc < 0)
+ return rc;
+ if (rc != 1)
+ return -ENODEV;
+ rc = i2c_master_recv(&th->clt, (char *)data, len);
+ if (rc < 0)
+ return rc;
+ if (rc != len)
+ return -ENODEV;
+ return 0;
+}
+
+static int
+attach_one_thermostat(struct i2c_adapter *adapter, int addr, int num)
+{
+ struct thermostat* th;
+ int rc;
+ u16 t, tlo, thi;
+ u8 config;
+
+ if (therm_count >= MAX_THERMOSTATS) {
+ printk(KERN_WARNING "Skipped thermostat %d (%s:%x), max count reached !\n",
+ num, adapter->name, addr);
+ return -ENODEV;
+ }
+
+ th = (struct thermostat *)kmalloc(sizeof(struct thermostat), GFP_KERNEL);
+ if (!th)
+ return -ENOMEM;
+ th->clt.addr = addr;
+ th->clt.adapter = adapter;
+ th->clt.driver = &thermostat_driver;
+ th->clt.flags = 0;
+ th->clt.data = (void *)therm_count;
+ th->th_num = num;
+ strcpy(th->clt.name, "thermostat");
+
+ rc = read_reg(th, 1, &config, 1);
+ if (rc < 0) {
+ printk(KERN_ERR "Thermostat %d (%s:%x) failed to read config !\n",
+ num, adapter->name, addr);
+ kfree(th);
+ return -ENODEV;
+ }
+ printk(KERN_INFO "Thermostat %d (%s:%x), config: %02x\n",
+ num, adapter->name, addr, config);
+
+ rc = read_reg(th, 0, (u8 *)&t, 2);
+ if (rc < 0) {
+ printk(KERN_ERR "Thermostat %d (%s:%x) failed to read temp !\n",
+ num, adapter->name, addr);
+ kfree(th);
+ return -ENODEV;
+ }
+ printk(KERN_INFO "Thermostat %d (%s:%x), temp: %04x (about: %d degree C)\n",
+ num, adapter->name, addr, t, t>>8);
+
+ thermostats[therm_count++] = th;
+
+ if (i2c_attach_client(&th->clt)) {
+ printk(KERN_ERR "Thermostat %d (%s:%x), failed to attach client !\n",
+ num, adapter->name, addr);
+ thermostats[--therm_count] = NULL;
+ kfree(th);
+ return -ENODEV;
+ }
+
+ tlo = thi = 0;
+ rc = read_reg(th, 2, (u8 *)&tlo, 2);
+ if (rc < 0) {
+ printk(KERN_WARNING "Thermostat %d (%s:%x) failed to read low threshold !\n",
+ num, adapter->name, addr);
+ }
+ rc = read_reg(th, 3, (u8 *)&thi, 2);
+ if (rc < 0) {
+ printk(KERN_WARNING "Thermostat %d (%s:%x) failed to read high threshold !\n",
+ num, adapter->name, addr);
+ }
+ printk(KERN_INFO "Thermostat %d (%s:%x), tl: %04x (%d degree C), th: %04x (%d degree C)\n",
+ num, adapter->name, addr, tlo, tlo>>8, thi, thi>>8);
+ return 0;
+}
+
+static int
+attach_thermostat(struct i2c_adapter *adapter)
+{
+ unsigned long bus_no;
+ int rc;
+
+ if (strncmp(adapter->name, "uni-n", 5))
+ return 0;
+ bus_no = simple_strtoul(adapter->name + 6, NULL, 10);
+ rc = attach_one_thermostat(adapter, I2C_THERMOSTAT_ADDR, bus_no);
+ if (!rc && thinfo->thermostat_count > 2)
+ attach_one_thermostat(adapter, I2C_THERMOSTAT_ADDR2, bus_no+2);
+
+ return rc;
+}
+
+static int
+detach_thermostat(struct i2c_client *client)
+{
+ int index = (int)client->data;
+ struct thermostat* th;
+
+ if (index >= MAX_THERMOSTATS || !thermostats[index]) {
+ printk(KERN_ERR "Invalid client in deatch_thermostat()\n");
+ return -ENODEV;
+ }
+ th = thermostats[index];
+ i2c_detach_client(&th->clt);
+ thermostats[index] = NULL;
+
+ kfree(th);
+
+ return 0;
+}
+
+static int __init
+thermostat_init(void)
+{
+ struct device_node* np;
+
+ if (!machine_is_compatible("PowerBook3,1"))
+ return -ENODEV;
+
+ np = find_devices("power-mgt");
+ if (!np)
+ return -ENODEV;
+ thinfo = (struct apple_thermal_info *)get_property(np, "thermal-info", NULL);
+ if (!thinfo)
+ return -ENODEV;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG " Thermal Infos found :\n");
+ printk(KERN_DEBUG " implementation id : %d\n", thinfo->id);
+ printk(KERN_DEBUG " fan_count : %d\n", thinfo->fan_count);
+ printk(KERN_DEBUG " thermostat_count : %d\n", thinfo->thermostat_count);
+ printk(KERN_DEBUG " ranges[0] : %d,%d\n",
+ thinfo->ranges[0].high, thinfo->ranges[0].low);
+ printk(KERN_DEBUG " ranges[1] : %d,%d\n",
+ thinfo->ranges[1].high, thinfo->ranges[1].low);
+ printk(KERN_DEBUG " ranges[2] : %d,%d\n",
+ thinfo->ranges[2].high, thinfo->ranges[2].low);
+ printk(KERN_DEBUG " ranges[3] : %d,%d\n",
+ thinfo->ranges[3].high, thinfo->ranges[3].low);
+#endif
+ /* Check against titaniums & ibooks.... */
+ if (thinfo->id != 1 && thinfo->id != 2) {
+ printk(KERN_ERR "thermostat: design id %d unknown !\n", thinfo->id);
+ return -ENODEV;
+ }
+
+ return i2c_add_driver(&thermostat_driver);
+}
+
+static void __exit
+thermostat_exit(void)
+{
+ i2c_del_driver(&thermostat_driver);
+}
+
+module_init(thermostat_init);
+module_exit(thermostat_exit);
diff -uNr linux-2.4.22/drivers/macintosh/via-pmu.c linux-2.4.22-ben2/drivers/macintosh/via-pmu.c
--- linux-2.4.22/drivers/macintosh/via-pmu.c 2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.22-ben2/drivers/macintosh/via-pmu.c 2003-08-31 11:40:49.000000000 +0200
@@ -64,6 +64,13 @@
/* How many iterations between battery polls */
#define BATTERY_POLLING_COUNT 2
+/* Some debugging tools */
+#ifdef CONFIG_XMON
+//#define LIVE_DEBUG(req) ((req) && (req)->data[0] == 0x7d)
+#define LIVE_DEBUG(req) (0)
+static int whacky_debug;
+#endif /* CONFIG_XMON */
+
static volatile unsigned char *via;
/* VIA registers - spaced 0x200 bytes apart */
@@ -107,6 +114,7 @@
intack,
reading,
reading_intr,
+ locked,
} pmu_state;
static volatile enum int_data_state {
@@ -134,6 +142,7 @@
static int pmu_has_adb;
static unsigned char *gpio_reg = NULL;
static int gpio_irq = -1;
+static int gpio_irq_enabled = -1;
static volatile int pmu_suspended = 0;
static spinlock_t pmu_lock;
static u8 pmu_intr_mask;
@@ -144,9 +153,11 @@
static int sleep_in_progress;
static int can_sleep;
#endif /* CONFIG_PMAC_PBOOK */
+static unsigned int pmu_irq_stats[11];
static struct proc_dir_entry *proc_pmu_root;
static struct proc_dir_entry *proc_pmu_info;
+static struct proc_dir_entry *proc_pmu_irqstats;
static struct proc_dir_entry *proc_pmu_options;
#ifdef CONFIG_PMAC_PBOOK
@@ -185,6 +196,8 @@
static void gpio1_interrupt(int irq, void *arg, struct pt_regs *regs);
static int proc_get_info(char *page, char **start, off_t off,
int count, int *eof, void *data);
+static int proc_get_irqstats(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
#ifdef CONFIG_PMAC_BACKLIGHT
static int pmu_set_backlight_level(int level, void* data);
static int pmu_set_backlight_enable(int on, int level, void* data);
@@ -206,7 +219,7 @@
pmu_init,
pmu_send_request,
pmu_adb_autopoll,
- pmu_poll,
+ pmu_poll_adb,
pmu_adb_reset_bus
};
#endif /* CONFIG_ADB */
@@ -419,6 +432,7 @@
if (pmu_kind == PMU_KEYLARGO_BASED && gpio_irq != -1) {
if (request_irq(gpio_irq, gpio1_interrupt, 0, "GPIO1/ADB", (void *)0))
printk(KERN_ERR "pmu: can't get irq %d (GPIO1)\n", gpio_irq);
+ gpio_irq_enabled = 1;
}
/* Enable interrupts */
@@ -466,6 +480,8 @@
int i;
proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root,
proc_get_info, NULL);
+ proc_pmu_irqstats = create_proc_read_entry("interrupts", 0, proc_pmu_root,
+ proc_get_irqstats, NULL);
#ifdef CONFIG_PMAC_PBOOK
for (i=0; i 0)
pmu_version = req.reply[0];
@@ -745,7 +760,7 @@
#endif /* CONFIG_PMAC_PBOOK */
-static int
+static int __pmac
proc_get_info(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
@@ -762,8 +777,35 @@
return p - page;
}
+static int __pmac
+proc_get_irqstats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int i;
+ char* p = page;
+ static const char *irq_names[] = {
+ "Total CB1 triggered events",
+ "Total GPIO1 triggered events",
+ "PC-Card eject button",
+ "Sound/Brightness button",
+ "ADB message",
+ "Battery state change",
+ "Environment interrupt",
+ "Tick timer",
+ "Ghost interrupt (zero len)",
+ "Empty interrupt (empty mask)",
+ "Max irqs in a row"
+ };
+
+ for (i=0; i<11; i++) {
+ p += sprintf(p, " %2u: %10u (%s)\n",
+ i, pmu_irq_stats[i], irq_names[i]);
+ }
+ return p - page;
+}
+
#ifdef CONFIG_PMAC_PBOOK
-static int
+static int __pmac
proc_get_batt(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
@@ -788,7 +830,7 @@
}
#endif /* CONFIG_PMAC_PBOOK */
-static int
+static int __pmac
proc_read_options(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
@@ -802,7 +844,7 @@
return p - page;
}
-static int
+static int __pmac
proc_write_options(struct file *file, const char *buffer,
unsigned long count, void *data)
{
@@ -843,7 +885,7 @@
#ifdef CONFIG_ADB
/* Send an ADB command */
-static int __openfirmware
+static int __pmac
pmu_send_request(struct adb_request *req, int sync)
{
int i, ret;
@@ -923,7 +965,7 @@
}
/* Enable/disable autopolling */
-static int __openfirmware
+static int __pmac
pmu_adb_autopoll(int devs)
{
struct adb_request req;
@@ -946,7 +988,7 @@
}
/* Reset the ADB bus */
-static int __openfirmware
+static int __pmac
pmu_adb_reset_bus(void)
{
struct adb_request req;
@@ -971,8 +1013,7 @@
printk(KERN_ERR "pmu_adb_reset_bus: pmu_queue_request failed\n");
return -EIO;
}
- while (!req.complete)
- pmu_poll();
+ pmu_wait_complete(&req);
if (save_autopoll != 0)
pmu_adb_autopoll(save_autopoll);
@@ -1008,7 +1049,7 @@
return pmu_queue_request(req);
}
-int __openfirmware
+int __pmac
pmu_queue_request(struct adb_request *req)
{
unsigned long flags;
@@ -1050,7 +1091,7 @@
static inline void
wait_for_ack(void)
{
- /* Sightly increased the delay, I had one occurence of the message
+ /* Sightly increased the delay, I had one occurrence of the message
* reported
*/
int timeout = 4000;
@@ -1100,8 +1141,8 @@
(*done)(req);
}
-static void __openfirmware
-pmu_start()
+static void __pmac
+pmu_start(void)
{
struct adb_request *req;
@@ -1122,17 +1163,32 @@
wait_for_ack();
/* set the shift register to shift out and send a byte */
send_byte(req->data[0]);
+#ifdef CONFIG_XMON
+ if (LIVE_DEBUG(req))
+ xmon_printf("R");
+ else
+ whacky_debug = 0;
+#endif /* CONFIG_XMON */
+}
+
+void __openfirmware
+pmu_poll(void)
+{
+ if (!via)
+ return;
+ if (disable_poll)
+ return;
+ via_pmu_interrupt(0, 0, 0);
}
void __openfirmware
-pmu_poll()
+pmu_poll_adb(void)
{
if (!via)
return;
if (disable_poll)
return;
/* Kicks ADB read when PMU is suspended */
- if (pmu_suspended)
adb_int_pending = 1;
do {
via_pmu_interrupt(0, 0, 0);
@@ -1140,6 +1196,15 @@
|| req_awaiting_reply));
}
+void __openfirmware
+pmu_wait_complete(struct adb_request *req)
+{
+ if (!via)
+ return;
+ while((pmu_state != idle && pmu_state != locked) || !req->complete)
+ via_pmu_interrupt(0, 0, 0);
+}
+
/* This function loops until the PMU is idle and prevents it from
* anwsering to ADB interrupts. pmu_request can still be called.
* This is done to avoid spurrious shutdowns when we know we'll have
@@ -1164,6 +1229,8 @@
do {
spin_unlock_irqrestore(&pmu_lock, flags);
+ if (req_awaiting_reply)
+ adb_int_pending = 1;
via_pmu_interrupt(0, 0, 0);
spin_lock_irqsave(&pmu_lock, flags);
if (!adb_int_pending && pmu_state == idle && !req_awaiting_reply) {
@@ -1174,7 +1241,7 @@
pmu_poll();
#else /* SUSPEND_USES_PMU */
if (gpio_irq >= 0)
- disable_irq(gpio_irq);
+ disable_irq_nosync(gpio_irq);
out_8(&via[IER], CB1_INT | IER_CLR);
spin_unlock_irqrestore(&pmu_lock, flags);
#endif /* SUSPEND_USES_PMU */
@@ -1213,19 +1280,50 @@
}
/* Interrupt data could be the result data from an ADB cmd */
-static void __openfirmware
+static void __pmac
pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
{
+ unsigned char ints, pirq;
+ int i = 0;
+
asleep = 0;
if (drop_interrupts || len < 1) {
adb_int_pending = 0;
+ pmu_irq_stats[8]++;
return;
}
+
+ /* Get PMU interrupt mask */
+ ints = data[0];
+
+ /* Record zero interrupts for stats */
+ if (ints == 0)
+ pmu_irq_stats[9]++;
+
+ /* Hack to deal with ADB autopoll flag */
+ if (ints & PMU_INT_ADB)
+ ints &= ~(PMU_INT_ADB_AUTO | PMU_INT_AUTO_SRQ_POLL);
+
+next:
+
+ if (ints == 0) {
+ if (i > pmu_irq_stats[10])
+ pmu_irq_stats[10] = i;
+ return;
+ }
+
+ for (pirq = 0; pirq < 8; pirq++)
+ if (ints & (1 << pirq))
+ break;
+ pmu_irq_stats[pirq]++;
+ i++;
+ ints &= ~(1 << pirq);
+
/* Note: for some reason, we get an interrupt with len=1,
* data[0]==0 after each normal ADB interrupt, at least
* on the Pismo. Still investigating... --BenH
*/
- if (data[0] & PMU_INT_ADB) {
+ if ((1 << pirq) & PMU_INT_ADB) {
if ((data[0] & PMU_INT_ADB_AUTO) == 0) {
struct adb_request *req = req_awaiting_reply;
if (req == 0) {
@@ -1263,32 +1361,40 @@
adb_input(data+1, len-1, regs, 1);
#endif /* CONFIG_ADB */
}
- } else {
+ }
/* Sound/brightness button pressed */
- if ((data[0] & PMU_INT_SNDBRT) && len == 3) {
+ else if ((1 << pirq) & PMU_INT_SNDBRT) {
#ifdef CONFIG_PMAC_BACKLIGHT
+ if (len == 3)
#ifdef CONFIG_INPUT_ADBHID
if (!disable_kernel_backlight)
#endif /* CONFIG_INPUT_ADBHID */
set_backlight_level(data[1] >> 4);
#endif /* CONFIG_PMAC_BACKLIGHT */
}
+ /* Tick interrupt */
+ else if ((1 << pirq) & PMU_INT_TICK) {
#ifdef CONFIG_PMAC_PBOOK
/* Environement or tick interrupt, query batteries */
- if (pmu_battery_count && (data[0] & PMU_INT_TICK)) {
+ if (pmu_battery_count) {
if ((--query_batt_timer) == 0) {
query_battery_state();
query_batt_timer = BATTERY_POLLING_COUNT;
}
- } else if (pmu_battery_count && (data[0] & PMU_INT_ENVIRONMENT))
+ }
+ }
+ else if ((1 << pirq) & PMU_INT_ENVIRONMENT) {
+ if (pmu_battery_count)
query_battery_state();
- if (data[0])
pmu_pass_intr(data, len);
+ } else {
+ pmu_pass_intr(data, len);
#endif /* CONFIG_PMAC_PBOOK */
}
+ goto next;
}
-static struct adb_request* __openfirmware
+static struct adb_request* __pmac
pmu_sr_intr(struct pt_regs *regs)
{
struct adb_request *req;
@@ -1315,17 +1421,29 @@
case sending:
req = current_req;
if (data_len < 0) {
+#ifdef CONFIG_XMON
+ if (LIVE_DEBUG(req))
+ xmon_printf("s");
+#endif /* CONFIG_XMON */
data_len = req->nbytes - 1;
send_byte(data_len);
break;
}
if (data_index <= data_len) {
+#ifdef CONFIG_XMON
+ if (LIVE_DEBUG(req))
+ xmon_printf("S");
+#endif /* CONFIG_XMON */
send_byte(req->data[data_index++]);
break;
}
req->sent = 1;
data_len = pmu_data_len[req->data[0]][1];
if (data_len == 0) {
+#ifdef CONFIG_XMON
+ if (LIVE_DEBUG(req))
+ xmon_printf("D");
+#endif /* CONFIG_XMON */
pmu_state = idle;
current_req = req->next;
if (req->reply_expected)
@@ -1333,6 +1451,10 @@
else
return req;
} else {
+#ifdef CONFIG_XMON
+ if (LIVE_DEBUG(req))
+ xmon_printf("-");
+#endif /* CONFIG_XMON */
pmu_state = reading;
data_index = 0;
reply_ptr = req->reply + req->reply_len;
@@ -1346,15 +1468,27 @@
pmu_state = reading_intr;
reply_ptr = interrupt_data[int_data_last];
recv_byte();
+ if (gpio_irq >= 0 && !gpio_irq_enabled) {
+ enable_irq(gpio_irq);
+ gpio_irq_enabled = 1;
+ }
break;
case reading:
case reading_intr:
if (data_len == -1) {
+#ifdef CONFIG_XMON
+ if (LIVE_DEBUG(current_req))
+ xmon_printf("r");
+#endif /* CONFIG_XMON */
data_len = bite;
if (bite > 32)
printk(KERN_ERR "PMU: bad reply len %d\n", bite);
} else if (data_index < 32) {
+#ifdef CONFIG_XMON
+ if (LIVE_DEBUG(current_req))
+ xmon_printf("R");
+#endif /* CONFIG_XMON */
reply_ptr[data_index++] = bite;
}
if (data_index < data_len) {
@@ -1362,14 +1496,28 @@
break;
}
+#ifdef CONFIG_XMON
+ if (LIVE_DEBUG(current_req)) {
+ whacky_debug = 1;
+ xmon_printf("D");
+ }
+#endif /* CONFIG_XMON */
if (pmu_state == reading_intr) {
pmu_state = idle;
int_data_state[int_data_last] = int_data_ready;
interrupt_data_len[int_data_last] = data_len;
} else {
req = current_req;
+ /*
+ * For PMU sleep and freq change requests, we lock the
+ * PMU until it's explicitely unlocked. This avoids any
+ * spurrious event polling getting in
+ */
current_req = req->next;
req->reply_len += data_index;
+ if (req->data[0] == PMU_SLEEP || req->data[0] == PMU_CPU_SPEED)
+ pmu_state = locked;
+ else
pmu_state = idle;
return req;
}
@@ -1399,6 +1547,10 @@
intr = in_8(&via[IFR]) & (SR_INT | CB1_INT);
if (intr == 0)
break;
+#ifdef CONFIG_XMON
+ if (whacky_debug)
+ xmon_printf("|%02x|", intr);
+#endif /* CONFIG_XMON */
if (++nloop > 1000) {
printk(KERN_DEBUG "PMU: stuck in intr loop, "
"intr=%x, ier=%x pmu_state=%d\n",
@@ -1406,8 +1558,10 @@
break;
}
out_8(&via[IFR], intr);
- if (intr & CB1_INT)
+ if (intr & CB1_INT) {
adb_int_pending = 1;
+ pmu_irq_stats[0]++;
+ }
if (intr & SR_INT) {
req = pmu_sr_intr(regs);
if (req)
@@ -1418,6 +1572,10 @@
recheck:
if (pmu_state == idle) {
if (adb_int_pending) {
+#ifdef CONFIG_XMON
+ if (whacky_debug)
+ xmon_printf("!A!");
+#endif /* CONFIG_XMON */
if (int_data_state[0] == int_data_empty)
int_data_last = 0;
else if (int_data_state[1] == int_data_empty)
@@ -1432,11 +1590,10 @@
wait_for_ack();
send_byte(PMU_INT_ACK);
adb_int_pending = 0;
-no_free_slot:
- ;
} else if (current_req)
pmu_start();
}
+no_free_slot:
/* Mark the oldest buffer for flushing */
if (int_data_state[!int_data_last] == int_data_ready) {
int_data_state[!int_data_last] = int_data_flush;
@@ -1465,17 +1622,39 @@
}
}
+void __pmac
+pmu_unlock(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pmu_lock, flags);
+ if (pmu_state == locked)
+ pmu_state = idle;
+ adb_int_pending = 1;
+ spin_unlock_irqrestore(&pmu_lock, flags);
+}
+
+
static void __openfirmware
gpio1_interrupt(int irq, void *arg, struct pt_regs *regs)
{
+ unsigned long flags;
+
if ((in_8(gpio_reg + 0x9) & 0x02) == 0) {
+ spin_lock_irqsave(&pmu_lock, flags);
+ if (gpio_irq_enabled > 0) {
+ disable_irq_nosync(gpio_irq);
+ gpio_irq_enabled = 0;
+ }
+ pmu_irq_stats[1]++;
adb_int_pending = 1;
+ spin_unlock_irqrestore(&pmu_lock, flags);
via_pmu_interrupt(0, 0, 0);
}
}
#ifdef CONFIG_PMAC_BACKLIGHT
-static int backlight_to_bright[] = {
+static int backlight_to_bright[] __pmacdata = {
0x7f, 0x46, 0x42, 0x3e, 0x3a, 0x36, 0x32, 0x2e,
0x2a, 0x26, 0x22, 0x1e, 0x1a, 0x16, 0x12, 0x0e
};
@@ -1491,13 +1670,11 @@
if (on) {
pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
backlight_to_bright[level]);
- while (!req.complete)
- pmu_poll();
+ pmu_wait_complete(&req);
}
pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF));
- while (!req.complete)
- pmu_poll();
+ pmu_wait_complete(&req);
return 0;
}
@@ -1521,7 +1698,7 @@
}
#endif /* CONFIG_PMAC_BACKLIGHT */
-void __openfirmware
+void __pmac
pmu_enable_irled(int on)
{
struct adb_request req;
@@ -1533,11 +1710,10 @@
pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_IRLED |
(on ? PMU_POW_ON : PMU_POW_OFF));
- while (!req.complete)
- pmu_poll();
+ pmu_wait_complete(&req);
}
-void __openfirmware
+void __pmac
pmu_restart(void)
{
struct adb_request req;
@@ -1554,13 +1730,12 @@
}
pmu_request(&req, NULL, 1, PMU_RESET);
- while(!req.complete || (pmu_state != idle))
- pmu_poll();
+ pmu_wait_complete(&req);
for (;;)
;
}
-void __openfirmware
+void __pmac
pmu_shutdown(void)
{
struct adb_request req;
@@ -1572,14 +1747,12 @@
if (pmu_kind != PMU_KEYLARGO_BASED) {
pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB |
PMU_INT_TICK );
- while(!req.complete)
- pmu_poll();
+ pmu_wait_complete(&req);
}
pmu_request(&req, NULL, 5, PMU_SHUTDOWN,
'M', 'A', 'T', 'T');
- while(!req.complete || (pmu_state != idle))
- pmu_poll();
+ pmu_wait_complete(&req);
for (;;)
;
}
@@ -2245,6 +2418,7 @@
set_context(current->active_mm->context, current->active_mm->pgd);
/* Power things up */
+ pmu_unlock();
pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xfc);
while (!req.complete)
pmu_poll();
@@ -2410,6 +2584,7 @@
//pbook_pci_restore();
#ifdef DEBUG_SLEEP
+ pmu_unlock();
pmu_blink(2);
#endif
/* Restore L2 cache */
@@ -2423,6 +2598,7 @@
set_context(current->active_mm->context, current->active_mm->pgd);
/* Tell PMU we are ready */
+ pmu_unlock();
pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
while (!req.complete)
pmu_poll();
@@ -2555,10 +2731,7 @@
out_be32(mem_ctrl_sleep, 0x3f);
pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
pbook_pci_restore();
-
- /* wait for the PMU interrupt sequence to complete */
- while (asleep)
- mb();
+ pmu_unlock();
/* reenable interrupts */
pmac_sleep_restore_intrs();
@@ -2921,8 +3094,11 @@
EXPORT_SYMBOL(pmu_request);
EXPORT_SYMBOL(pmu_poll);
+EXPORT_SYMBOL(pmu_poll_adb);
+EXPORT_SYMBOL(pmu_wait_complete);
EXPORT_SYMBOL(pmu_suspend);
EXPORT_SYMBOL(pmu_resume);
+EXPORT_SYMBOL(pmu_unlock);
EXPORT_SYMBOL(pmu_i2c_combined_read);
EXPORT_SYMBOL(pmu_i2c_stdsub_write);
EXPORT_SYMBOL(pmu_i2c_simple_read);
diff -uNr linux-2.4.22/drivers/net/Makefile linux-2.4.22-ben2/drivers/net/Makefile
--- linux-2.4.22/drivers/net/Makefile 2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.22-ben2/drivers/net/Makefile 2003-08-31 11:41:48.000000000 +0200
@@ -17,7 +17,7 @@
export-objs := 8390.o arlan.o aironet4500_core.o aironet4500_card.o \
ppp_async.o ppp_generic.o slhc.o pppox.o auto_irq.o \
- net_init.o mii.o
+ net_init.o mii.o sungem_phy.o
list-multi := rcpci.o
rcpci-objs := rcpci45.o rclanmtl.o
@@ -67,7 +67,7 @@
obj-$(CONFIG_SUNQE) += sunqe.o
obj-$(CONFIG_SUNBMAC) += sunbmac.o
obj-$(CONFIG_MYRI_SBUS) += myri_sbus.o
-obj-$(CONFIG_SUNGEM) += sungem.o
+obj-$(CONFIG_SUNGEM) += sungem.o sungem_phy.o
obj-$(CONFIG_MACE) += mace.o
obj-$(CONFIG_BMAC) += bmac.o
diff -uNr linux-2.4.22/drivers/net/irda/irda-usb.c linux-2.4.22-ben2/drivers/net/irda/irda-usb.c
--- linux-2.4.22/drivers/net/irda/irda-usb.c 2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.22-ben2/drivers/net/irda/irda-usb.c 2003-08-31 11:39:12.000000000 +0200
@@ -1386,6 +1386,9 @@
WARNING("usb-irda: bad class_descriptor type\n");
}
else {
+ le16_to_cpus (&(desc->wBaudRate));
+ le16_to_cpus (&(desc->bcdSpecRevision));
+
#ifdef IU_DUMP_CLASS_DESC
irda_usb_dump_class_desc(desc);
#endif /* IU_DUMP_CLASS_DESC */
diff -uNr linux-2.4.22/drivers/net/ne2k-pci.c linux-2.4.22-ben2/drivers/net/ne2k-pci.c
--- linux-2.4.22/drivers/net/ne2k-pci.c 2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.22-ben2/drivers/net/ne2k-pci.c 2003-08-31 11:41:03.000000000 +0200
@@ -69,8 +69,6 @@
#if defined(__powerpc__)
#define inl_le(addr) le32_to_cpu(inl(addr))
#define inw_le(addr) le16_to_cpu(inw(addr))
-#define insl insl_ns
-#define outsl outsl_ns
#endif
#define PFX DRV_NAME ": "
diff -uNr linux-2.4.22/drivers/net/pcnet32.c linux-2.4.22-ben2/drivers/net/pcnet32.c
--- linux-2.4.22/drivers/net/pcnet32.c 2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.22-ben2/drivers/net/pcnet32.c 2003-08-31 11:40:03.000000000 +0200
@@ -569,7 +569,7 @@
break;
case 0x2625:
chipname = "PCnet/FAST III 79C973"; /* PCI */
- fdx = 1; mii = 1;
+ fdx = 1; mii = 1; fset = 1;
break;
case 0x2626:
chipname = "PCnet/Home 79C978"; /* PCI */
@@ -612,6 +612,7 @@
if(fset)
{
+ printk(KERN_INFO PFX "Activating Tx error recovery on %s\n", chipname);
a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800));
a->write_csr(ioaddr, 80, (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00);
dxsuflo = 1;
diff -uNr linux-2.4.22/drivers/net/sungem.c linux-2.4.22-ben2/drivers/net/sungem.c
--- linux-2.4.22/drivers/net/sungem.c 2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.22-ben2/drivers/net/sungem.c 2003-08-31 11:42:22.000000000 +0200
@@ -10,10 +10,6 @@
* - Get rid of all those nasty mdelay's and replace them
* with schedule_timeout.
* - Implement WOL
- * - Currently, forced Gb mode is only supported on bcm54xx
- * PHY for which I use the SPD2 bit of the control register.
- * On m1011 PHY, I can't force as I don't have the specs, but
- * I can at least detect gigabit with autoneg.
*/
#include
@@ -63,12 +59,20 @@
#include
#endif
+#include "sungem_phy.h"
#include "sungem.h"
+/* Stripping FCS is causing problems, disabled for now */
+#undef STRIP_FCS
+
#define DEFAULT_MSG (NETIF_MSG_DRV | \
NETIF_MSG_PROBE | \
NETIF_MSG_LINK)
+#define ADVERTISE_MASK (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \
+ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \
+ SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)
+
#define DRV_NAME "sungem"
#define DRV_VERSION "0.97"
#define DRV_RELDATE "3/20/02"
@@ -81,24 +85,6 @@
MODULE_DESCRIPTION("Sun GEM Gbit ethernet driver");
MODULE_LICENSE("GPL");
-MODULE_PARM(gem_debug, "i");
-MODULE_PARM_DESC(gem_debug, "bitmapped message enable number");
-MODULE_PARM(link_mode, "i");
-MODULE_PARM_DESC(link_mode, "default link mode");
-
-int gem_debug = -1;
-static int link_mode;
-
-static u16 link_modes[] __devinitdata = {
- BMCR_ANENABLE, /* 0 : autoneg */
- 0, /* 1 : 10bt half duplex */
- BMCR_SPEED100, /* 2 : 100bt half duplex */
- BMCR_SPD2, /* bcm54xx only */ /* 3 : 1000bt half duplex */
- BMCR_FULLDPLX, /* 4 : 10bt full duplex */
- BMCR_SPEED100|BMCR_FULLDPLX, /* 5 : 100bt full duplex */
- BMCR_SPD2|BMCR_FULLDPLX /* 6 : 1000bt full duplex */
-};
-
#define GEM_MODULE_NAME "gem"
#define PFX GEM_MODULE_NAME ": "
@@ -119,12 +105,14 @@
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMACP,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMAC2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{0, }
};
MODULE_DEVICE_TABLE(pci, gem_pci_tbl);
-static u16 __phy_read(struct gem *gp, int reg, int phy_addr)
+static u16 __phy_read(struct gem *gp, int phy_addr, int reg)
{
u32 cmd;
int limit = 10000;
@@ -150,12 +138,18 @@
return cmd & MIF_FRAME_DATA;
}
+static inline int _phy_read(struct net_device *dev, int mii_id, int reg)
+{
+ struct gem *gp = dev->priv;
+ return __phy_read(gp, mii_id, reg);
+}
+
static inline u16 phy_read(struct gem *gp, int reg)
{
- return __phy_read(gp, reg, gp->mii_phy_addr);
+ return __phy_read(gp, gp->mii_phy_addr, reg);
}
-static void __phy_write(struct gem *gp, int reg, u16 val, int phy_addr)
+static void __phy_write(struct gem *gp, int phy_addr, int reg, u16 val)
{
u32 cmd;
int limit = 10000;
@@ -177,9 +171,15 @@
}
}
+static inline void _phy_write(struct net_device *dev, int mii_id, int reg, int val)
+{
+ struct gem *gp = dev->priv;
+ __phy_write(gp, mii_id, reg, val & 0xffff);
+}
+
static inline void phy_write(struct gem *gp, int reg, u16 val)
{
- __phy_write(gp, reg, val, gp->mii_phy_addr);
+ __phy_write(gp, gp->mii_phy_addr, reg, val);
}
static void gem_handle_mif_event(struct gem *gp, u32 reg_val, u32 changed_bits)
@@ -228,10 +228,11 @@
if (pcs_miistat & PCS_MIISTAT_LS) {
printk(KERN_INFO "%s: PCS link is now up.\n",
dev->name);
+ netif_carrier_on(gp->dev);
} else {
printk(KERN_INFO "%s: PCS link is now down.\n",
dev->name);
-
+ netif_carrier_off(gp->dev);
/* If this happens and the link timer is not running,
* reset so we re-negotiate.
*/
@@ -359,6 +360,7 @@
rxd->status_word = cpu_to_le64(RXDCTRL_FRESH(gp));
}
+ mb();
gp->rx_new = gp->rx_old = 0;
/* Now we must reprogram the rest of RX unit. */
@@ -400,6 +402,10 @@
gp->dev->name, rxmac_stat);
if (rxmac_stat & MAC_RXSTAT_OFLW) {
+ u32 smac = readl(gp->regs + MAC_SMACHINE);
+
+ printk(KERN_ERR "%s: RX MAC fifo overflow smac[%08x].\n",
+ dev->name, smac);
gp->net_stats.rx_over_errors++;
gp->net_stats.rx_fifo_errors++;
@@ -667,6 +673,7 @@
count = 0;
}
}
+ mb();
if (kick >= 0)
writel(kick, gp->regs + RXDMA_KICK);
}
@@ -893,7 +900,7 @@
/* We must give this initial chunk to the device last.
* Otherwise we could race with the device.
*/
- first_len = skb->len - skb->data_len;
+ first_len = skb_headlen(skb);
first_mapping = pci_map_page(gp->pdev, virt_to_page(skb->data),
((unsigned long) skb->data & ~PAGE_MASK),
first_len, PCI_DMA_TODEVICE);
@@ -936,6 +943,7 @@
if (netif_msg_tx_queued(gp))
printk(KERN_DEBUG "%s: tx queued, slot %d, skblen %d\n",
dev->name, entry, skb->len);
+ mb();
writel(gp->tx_new, gp->regs + TXDMA_KICK);
spin_unlock_irq(&gp->lock);
@@ -1003,7 +1011,7 @@
} while (val & (GREG_SWRST_TXRST | GREG_SWRST_RXRST));
if (limit <= 0)
- printk(KERN_ERR "gem: SW reset is ghetto.\n");
+ printk(KERN_ERR "%s: SW reset is ghetto.\n", gp->dev->name);
}
/* Must be invoked under gp->lock. */
@@ -1030,136 +1038,118 @@
}
-/* Link modes of the BCM5400 PHY */
-static int phy_BCM5400_link_table[8][3] = {
- { 0, 0, 0 }, /* No link */
- { 0, 0, 0 }, /* 10BT Half Duplex */
- { 1, 0, 0 }, /* 10BT Full Duplex */
- { 0, 1, 0 }, /* 100BT Half Duplex */
- { 0, 1, 0 }, /* 100BT Half Duplex */
- { 1, 1, 0 }, /* 100BT Full Duplex*/
- { 1, 0, 1 }, /* 1000BT */
- { 1, 0, 1 }, /* 1000BT */
-};
/* Must be invoked under gp->lock. */
+// XXX dbl check what that function should do when called on PCS PHY
static void gem_begin_auto_negotiation(struct gem *gp, struct ethtool_cmd *ep)
{
- u16 ctl;
+ u32 advertise, features;
+ int autoneg;
+ int speed;
+ int duplex;
+
+ if (gp->phy_type != phy_mii_mdio0 &&
+ gp->phy_type != phy_mii_mdio1)
+ goto non_mii;
+
+ /* Setup advertise */
+ if (found_mii_phy(gp))
+ features = gp->phy_mii.def->features;
+ else
+ features = 0;
+
+ advertise = features & ADVERTISE_MASK;
+ if (gp->phy_mii.advertising != 0)
+ advertise &= gp->phy_mii.advertising;
+
+ autoneg = gp->want_autoneg;
+ speed = gp->phy_mii.speed;
+ duplex = gp->phy_mii.duplex;
/* Setup link parameters */
if (!ep)
goto start_aneg;
if (ep->autoneg == AUTONEG_ENABLE) {
- /* TODO: parse ep->advertising */
- gp->link_advertise |= (ADVERTISE_10HALF | ADVERTISE_10FULL);
- gp->link_advertise |= (ADVERTISE_100HALF | ADVERTISE_100FULL);
- /* Can I advertise gigabit here ? I'd need BCM PHY docs... */
- gp->link_cntl = BMCR_ANENABLE;
+ advertise = ep->advertising;
+ autoneg = 1;
} else {
- gp->link_cntl = 0;
- if (ep->speed == SPEED_100)
- gp->link_cntl |= BMCR_SPEED100;
- else if (ep->speed == SPEED_1000 && gp->gigabit_capable)
- /* Hrm... check if this is right... */
- gp->link_cntl |= BMCR_SPD2;
- if (ep->duplex == DUPLEX_FULL)
- gp->link_cntl |= BMCR_FULLDPLX;
+ autoneg = 0;
+ speed = ep->speed;
+ duplex = ep->duplex;
}
start_aneg:
- if (!gp->hw_running)
+ /* Sanitize settings based on PHY capabilities */
+ if ((features & SUPPORTED_Autoneg) == 0)
+ autoneg = 0;
+ if (speed == SPEED_1000 &&
+ !(features & (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)))
+ speed = SPEED_100;
+ if (speed == SPEED_100 &&
+ !(features & (SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full)))
+ speed = SPEED_10;
+ if (duplex == DUPLEX_FULL &&
+ !(features & (SUPPORTED_1000baseT_Full |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_10baseT_Full)))
+ duplex = DUPLEX_HALF;
+ if (speed == 0)
+ speed = SPEED_10;
+
+ /* If HW is down, we don't try to actually setup the PHY, we
+ * just store the settings
+ */
+ if (!gp->hw_running) {
+ gp->phy_mii.autoneg = gp->want_autoneg = autoneg;
+ gp->phy_mii.speed = speed;
+ gp->phy_mii.duplex = duplex;
return;
+ }
/* Configure PHY & start aneg */
- ctl = phy_read(gp, MII_BMCR);
- ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE);
- ctl |= gp->link_cntl;
- if (ctl & BMCR_ANENABLE) {
- ctl |= BMCR_ANRESTART;
+ gp->want_autoneg = autoneg;
+ if (autoneg) {
+ if (found_mii_phy(gp))
+ gp->phy_mii.def->ops->setup_aneg(&gp->phy_mii, advertise);
gp->lstate = link_aneg;
} else {
+ if (found_mii_phy(gp))
+ gp->phy_mii.def->ops->setup_forced(&gp->phy_mii, speed, duplex);
gp->lstate = link_force_ok;
}
- phy_write(gp, MII_BMCR, ctl);
+non_mii:
gp->timer_ticks = 0;
mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
}
-/* Must be invoked under gp->lock. */
-static void gem_read_mii_link_mode(struct gem *gp, int *fd, int *spd, int *pause)
-{
- u32 val;
-
- *fd = 0;
- *spd = 10;
- *pause = 0;
-
- if (gp->phy_mod == phymod_bcm5400 ||
- gp->phy_mod == phymod_bcm5401 ||
- gp->phy_mod == phymod_bcm5411) {
- int link_mode;
-
- val = phy_read(gp, MII_BCM5400_AUXSTATUS);
- link_mode = ((val & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >>
- MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT);
- *fd = phy_BCM5400_link_table[link_mode][0];
- *spd = phy_BCM5400_link_table[link_mode][2] ?
- 1000 :
- (phy_BCM5400_link_table[link_mode][1] ? 100 : 10);
- val = phy_read(gp, MII_LPA);
- if (val & LPA_PAUSE)
- *pause = 1;
- } else {
- val = phy_read(gp, MII_LPA);
-
- if (val & (LPA_10FULL | LPA_100FULL))
- *fd = 1;
- if (val & (LPA_100FULL | LPA_100HALF))
- *spd = 100;
-
- if (gp->phy_mod == phymod_m1011) {
- val = phy_read(gp, 0x0a);
- if (val & 0xc00)
- *spd = 1000;
- if (val & 0x800)
- *fd = 1;
- }
- }
-}
-
/* A link-up condition has occurred, initialize and enable the
* rest of the chip.
*
* Must be invoked under gp->lock.
*/
-static void gem_set_link_modes(struct gem *gp)
+static int gem_set_link_modes(struct gem *gp)
{
u32 val;
int full_duplex, speed, pause;
full_duplex = 0;
- speed = 10;
+ speed = SPEED_10;
pause = 0;
- if (gp->phy_type == phy_mii_mdio0 ||
- gp->phy_type == phy_mii_mdio1) {
- val = phy_read(gp, MII_BMCR);
- if (val & BMCR_ANENABLE)
- gem_read_mii_link_mode(gp, &full_duplex, &speed, &pause);
- else {
- if (val & BMCR_FULLDPLX)
- full_duplex = 1;
- if (val & BMCR_SPEED100)
- speed = 100;
- }
- } else {
+ if (found_mii_phy(gp)) {
+ if (gp->phy_mii.def->ops->read_link(&gp->phy_mii))
+ return 1;
+ full_duplex = (gp->phy_mii.duplex == DUPLEX_FULL);
+ speed = gp->phy_mii.speed;
+ pause = gp->phy_mii.pause;
+ } else if (gp->phy_type == phy_serialink ||
+ gp->phy_type == phy_serdes) {
u32 pcs_lpa = readl(gp->regs + PCS_MIILP);
if (pcs_lpa & PCS_MIIADV_FD)
full_duplex = 1;
- speed = 1000;
+ speed = SPEED_1000;
}
if (netif_msg_link(gp))
@@ -1183,7 +1173,7 @@
val |= MAC_XIFCFG_FLED;
}
- if (speed == 1000)
+ if (speed == SPEED_1000)
val |= (MAC_XIFCFG_GMII);
writel(val, gp->regs + MAC_XIFCFG);
@@ -1191,7 +1181,7 @@
/* If gigabit and half-duplex, enable carrier extension
* mode. Else, disable it.
*/
- if (speed == 1000 && !full_duplex) {
+ if (speed == SPEED_1000 && !full_duplex) {
val = readl(gp->regs + MAC_TXCFG);
writel(val | MAC_TXCFG_TCE, gp->regs + MAC_TXCFG);
@@ -1239,50 +1229,51 @@
writel(val, gp->regs + MAC_MCCFG);
gem_start_dma(gp);
+
+ return 0;
}
/* Must be invoked under gp->lock. */
static int gem_mdio_link_not_up(struct gem *gp)
{
- u16 val;
-
- if (gp->lstate == link_force_ret) {
+ switch (gp->lstate) {
+ case link_force_ret:
if (netif_msg_link(gp))
printk(KERN_INFO "%s: Autoneg failed again, keeping"
" forced mode\n", gp->dev->name);
- phy_write(gp, MII_BMCR, gp->link_fcntl);
+ gp->phy_mii.def->ops->setup_forced(&gp->phy_mii,
+ gp->last_forced_speed, DUPLEX_HALF);
gp->timer_ticks = 5;
gp->lstate = link_force_ok;
- } else if (gp->lstate == link_aneg) {
- val = phy_read(gp, MII_BMCR);
-
+ return 0;
+ case link_aneg:
if (netif_msg_link(gp))
printk(KERN_INFO "%s: switching to forced 100bt\n",
gp->dev->name);
/* Try forced modes. */
- val &= ~(BMCR_ANRESTART | BMCR_ANENABLE);
- val &= ~(BMCR_FULLDPLX);
- val |= BMCR_SPEED100;
- phy_write(gp, MII_BMCR, val);
+ gp->phy_mii.def->ops->setup_forced(&gp->phy_mii, SPEED_100,
+ DUPLEX_HALF);
gp->timer_ticks = 5;
gp->lstate = link_force_try;
- } else {
+ return 0;
+ case link_force_try:
/* Downgrade from 100 to 10 Mbps if necessary.
* If already at 10Mbps, warn user about the
* situation every 10 ticks.
*/
- val = phy_read(gp, MII_BMCR);
- if (val & BMCR_SPEED100) {
- val &= ~BMCR_SPEED100;
- phy_write(gp, MII_BMCR, val);
+ if (gp->phy_mii.speed == SPEED_100) {
+ gp->phy_mii.def->ops->setup_forced(&gp->phy_mii, SPEED_10,
+ DUPLEX_HALF);
gp->timer_ticks = 5;
if (netif_msg_link(gp))
printk(KERN_INFO "%s: switching to forced 10bt\n",
gp->dev->name);
+ return 0;
} else
return 1;
+ default:
+ return 0;
}
- return 0;
}
static void gem_init_rings(struct gem *);
@@ -1322,7 +1313,8 @@
static void gem_link_timer(unsigned long data)
{
struct gem *gp = (struct gem *) data;
-
+ int restart_aneg = 0;
+
if (!gp->hw_running)
return;
@@ -1334,62 +1326,8 @@
if (gp->reset_task_pending)
goto restart;
- if (gp->phy_type == phy_mii_mdio0 ||
- gp->phy_type == phy_mii_mdio1) {
- u16 val = phy_read(gp, MII_BMSR);
- u16 cntl = phy_read(gp, MII_BMCR);
- int up;
-
- /* When using autoneg, we really wait for ANEGCOMPLETE or we may
- * get a "transcient" incorrect link state
- */
- if (cntl & BMCR_ANENABLE)
- up = (val & (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) == (BMSR_ANEGCOMPLETE | BMSR_LSTATUS);
- else
- up = (val & BMSR_LSTATUS) != 0;
- if (up) {
- /* Ok, here we got a link. If we had it due to a forced
- * fallback, and we were configured for autoneg, we do
- * retry a short autoneg pass. If you know your hub is
- * broken, use ethtool ;)
- */
- if (gp->lstate == link_force_try && (gp->link_cntl & BMCR_ANENABLE)) {
- gp->lstate = link_force_ret;
- gp->link_fcntl = phy_read(gp, MII_BMCR);
- gp->timer_ticks = 5;
- if (netif_msg_link(gp))
- printk(KERN_INFO "%s: Got link after fallback, retrying"
- " autoneg once...\n", gp->dev->name);
- phy_write(gp, MII_BMCR,
- gp->link_fcntl | BMCR_ANENABLE | BMCR_ANRESTART);
- } else if (gp->lstate != link_up) {
- gp->lstate = link_up;
- if (gp->opened)
- gem_set_link_modes(gp);
- }
- } else {
- int restart = 0;
-
- /* If the link was previously up, we restart the
- * whole process
- */
- if (gp->lstate == link_up) {
- gp->lstate = link_down;
- if (netif_msg_link(gp))
- printk(KERN_INFO "%s: Link down\n",
- gp->dev->name);
- gp->reset_task_pending = 2;
- schedule_task(&gp->reset_task);
- restart = 1;
- } else if (++gp->timer_ticks > 10)
- restart = gem_mdio_link_not_up(gp);
-
- if (restart) {
- gem_begin_auto_negotiation(gp, NULL);
- goto out_unlock;
- }
- }
- } else {
+ if (gp->phy_type == phy_serialink ||
+ gp->phy_type == phy_serdes) {
u32 val = readl(gp->regs + PCS_MIISTAT);
if (!(val & PCS_MIISTAT_LS))
@@ -1397,11 +1335,56 @@
if ((val & PCS_MIISTAT_LS) != 0) {
gp->lstate = link_up;
+ netif_carrier_on(gp->dev);
if (gp->opened)
- gem_set_link_modes(gp);
+ (void)gem_set_link_modes(gp);
}
+ goto restart;
+ }
+ if (found_mii_phy(gp) && gp->phy_mii.def->ops->poll_link(&gp->phy_mii)) {
+ /* Ok, here we got a link. If we had it due to a forced
+ * fallback, and we were configured for autoneg, we do
+ * retry a short autoneg pass. If you know your hub is
+ * broken, use ethtool ;)
+ */
+ if (gp->lstate == link_force_try && gp->want_autoneg) {
+ gp->lstate = link_force_ret;
+ gp->last_forced_speed = gp->phy_mii.speed;
+ gp->timer_ticks = 5;
+ if (netif_msg_link(gp))
+ printk(KERN_INFO "%s: Got link after fallback, retrying"
+ " autoneg once...\n", gp->dev->name);
+ gp->phy_mii.def->ops->setup_aneg(&gp->phy_mii, gp->phy_mii.advertising);
+ } else if (gp->lstate != link_up) {
+ gp->lstate = link_up;
+ netif_carrier_on(gp->dev);
+ if (gp->opened && gem_set_link_modes(gp))
+ restart_aneg = 1;
+ }
+ } else {
+ /* If the link was previously up, we restart the
+ * whole process
+ */
+ if (gp->lstate == link_up) {
+ gp->lstate = link_down;
+ if (netif_msg_link(gp))
+ printk(KERN_INFO "%s: Link down\n",
+ gp->dev->name);
+ netif_carrier_off(gp->dev);
+ gp->reset_task_pending = 2;
+ schedule_task(&gp->reset_task);
+ restart_aneg = 1;
+ } else if (++gp->timer_ticks > 10) {
+ if (found_mii_phy(gp))
+ restart_aneg = gem_mdio_link_not_up(gp);
+ else
+ restart_aneg = 1;
+ }
+ }
+ if (restart_aneg) {
+ gem_begin_auto_negotiation(gp, NULL);
+ goto out_unlock;
}
-
restart:
mod_timer(&gp->link_timer, jiffies + ((12 * HZ) / 10));
out_unlock:
@@ -1501,153 +1484,14 @@
txd->control_word = 0;
txd->buffer = 0;
}
-}
-
-/* Must be invoked under gp->lock. */
-static int gem_reset_one_mii_phy(struct gem *gp, int phy_addr)
-{
- u16 val;
- int limit = 10000;
-
- val = __phy_read(gp, MII_BMCR, phy_addr);
- val &= ~BMCR_ISOLATE;
- val |= BMCR_RESET;
- __phy_write(gp, MII_BMCR, val, phy_addr);
-
- udelay(100);
-
- while (limit--) {
- val = __phy_read(gp, MII_BMCR, phy_addr);
- if ((val & BMCR_RESET) == 0)
- break;
- udelay(10);
- }
- if ((val & BMCR_ISOLATE) && limit > 0)
- __phy_write(gp, MII_BMCR, val & ~BMCR_ISOLATE, phy_addr);
-
- return (limit <= 0);
-}
-
-/* Must be invoked under gp->lock. */
-static void gem_init_bcm5201_phy(struct gem *gp)
-{
- u16 data;
-
- data = phy_read(gp, MII_BCM5201_MULTIPHY);
- data &= ~MII_BCM5201_MULTIPHY_SUPERISOLATE;
- phy_write(gp, MII_BCM5201_MULTIPHY, data);
-}
-
-/* Must be invoked under gp->lock. */
-static void gem_init_bcm5400_phy(struct gem *gp)
-{
- u16 data;
-
- /* Configure for gigabit full duplex */
- data = phy_read(gp, MII_BCM5400_AUXCONTROL);
- data |= MII_BCM5400_AUXCONTROL_PWR10BASET;
- phy_write(gp, MII_BCM5400_AUXCONTROL, data);
-
- data = phy_read(gp, MII_BCM5400_GB_CONTROL);
- data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
- phy_write(gp, MII_BCM5400_GB_CONTROL, data);
-
- mdelay(10);
-
- /* Reset and configure cascaded 10/100 PHY */
- gem_reset_one_mii_phy(gp, 0x1f);
-
- data = __phy_read(gp, MII_BCM5201_MULTIPHY, 0x1f);
- data |= MII_BCM5201_MULTIPHY_SERIALMODE;
- __phy_write(gp, MII_BCM5201_MULTIPHY, data, 0x1f);
-
- data = phy_read(gp, MII_BCM5400_AUXCONTROL);
- data &= ~MII_BCM5400_AUXCONTROL_PWR10BASET;
- phy_write(gp, MII_BCM5400_AUXCONTROL, data);
-}
-
-/* Must be invoked under gp->lock. */
-static void gem_init_bcm5401_phy(struct gem *gp)
-{
- u16 data;
- int rev;
-
- rev = phy_read(gp, MII_PHYSID2) & 0x000f;
- if (rev == 0 || rev == 3) {
- /* Some revisions of 5401 appear to need this
- * initialisation sequence to disable, according
- * to OF, "tap power management"
- *
- * WARNING ! OF and Darwin don't agree on the
- * register addresses. OF seem to interpret the
- * register numbers below as decimal
- *
- * Note: This should (and does) match tg3_init_5401phy_dsp
- * in the tg3.c driver. -DaveM
- */
- phy_write(gp, 0x18, 0x0c20);
- phy_write(gp, 0x17, 0x0012);
- phy_write(gp, 0x15, 0x1804);
- phy_write(gp, 0x17, 0x0013);
- phy_write(gp, 0x15, 0x1204);
- phy_write(gp, 0x17, 0x8006);
- phy_write(gp, 0x15, 0x0132);
- phy_write(gp, 0x17, 0x8006);
- phy_write(gp, 0x15, 0x0232);
- phy_write(gp, 0x17, 0x201f);
- phy_write(gp, 0x15, 0x0a20);
- }
-
- /* Configure for gigabit full duplex */
- data = phy_read(gp, MII_BCM5400_GB_CONTROL);
- data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
- phy_write(gp, MII_BCM5400_GB_CONTROL, data);
-
- mdelay(1);
-
- /* Reset and configure cascaded 10/100 PHY */
- gem_reset_one_mii_phy(gp, 0x1f);
-
- data = __phy_read(gp, MII_BCM5201_MULTIPHY, 0x1f);
- data |= MII_BCM5201_MULTIPHY_SERIALMODE;
- __phy_write(gp, MII_BCM5201_MULTIPHY, data, 0x1f);
-}
-
-/* Must be invoked under gp->lock. */
-static void gem_init_bcm5411_phy(struct gem *gp)
-{
- u16 data;
-
- /* Here's some more Apple black magic to setup
- * some voltage stuffs.
- */
- phy_write(gp, 0x1c, 0x8c23);
- phy_write(gp, 0x1c, 0x8ca3);
- phy_write(gp, 0x1c, 0x8c23);
-
- /* Here, Apple seems to want to reset it, do
- * it as well
- */
- phy_write(gp, MII_BMCR, BMCR_RESET);
-
- /* Start autoneg */
- phy_write(gp, MII_BMCR,
- (BMCR_ANENABLE | BMCR_FULLDPLX |
- BMCR_ANRESTART | BMCR_SPD2));
-
- data = phy_read(gp, MII_BCM5400_GB_CONTROL);
- data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
- phy_write(gp, MII_BCM5400_GB_CONTROL, data);
+ mb();
}
/* Must be invoked under gp->lock. */
static void gem_init_phy(struct gem *gp)
{
u32 mifcfg;
-
- if (!gp->wake_on_lan && gp->phy_mod == phymod_bcm5201)
- phy_write(gp, MII_BCM5201_INTERRUPT, 0);
-
+
/* Revert MIF CFG setting done on stop_phy */
mifcfg = readl(gp->regs + MIF_CFG);
mifcfg &= ~MIF_CFG_BBMODE;
@@ -1655,19 +1499,37 @@
#ifdef CONFIG_ALL_PPC
if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
- int i;
+ int i, j;
+ /* Those delay sucks, the HW seem to love them though, I'll
+ * serisouly consider breaking some locks here to be able
+ * to schedule instead
+ */
pmac_call_feature(PMAC_FTR_GMAC_PHY_RESET, gp->of_node, 0, 0);
- for (i = 0; i < 32; i++) {
- gp->mii_phy_addr = i;
- if (phy_read(gp, MII_BMCR) != 0xffff)
+ mdelay(10);
+ for (j = 0; j < 3; j++) {
+ /* Some PHYs used by apple have problem getting back to us,
+ * we _know_ it's actually at addr 0, that's a hack, but
+ * it helps to do that reset now. I suspect some motherboards
+ * don't wire the PHY reset line properly, thus the PHY doesn't
+ * come back with the above pmac_call_feature.
+ */
+ gp->mii_phy_addr = 0;
+ phy_write(gp, MII_BMCR, BMCR_RESET);
+ /* We should probably break some locks here and schedule... */
+ mdelay(10);
+ for (i = 0; i < 32; i++) {
+ gp->mii_phy_addr = i;
+ if (phy_read(gp, MII_BMCR) != 0xffff)
+ break;
+ }
+ if (i == 32) {
+ printk(KERN_WARNING "%s: GMAC PHY not responding !\n",
+ gp->dev->name);
+ gp->mii_phy_addr = 0;
+ } else
break;
}
- if (i == 32) {
- printk(KERN_WARNING "%s: GMAC PHY not responding !\n",
- gp->dev->name);
- return;
- }
}
#endif /* CONFIG_ALL_PPC */
@@ -1690,79 +1552,12 @@
if (gp->phy_type == phy_mii_mdio0 ||
gp->phy_type == phy_mii_mdio1) {
- u32 phy_id;
- u16 val;
-
- /* Take PHY out of isloate mode and reset it. */
- gem_reset_one_mii_phy(gp, gp->mii_phy_addr);
-
- phy_id = (phy_read(gp, MII_PHYSID1) << 16 | phy_read(gp, MII_PHYSID2))
- & 0xfffffff0;
- printk(KERN_INFO "%s: MII PHY ID: %x ", gp->dev->name, phy_id);
- switch(phy_id) {
- case 0x406210:
- gp->phy_mod = phymod_bcm5201;
- gem_init_bcm5201_phy(gp);
- printk("BCM 5201\n");
- break;
-
- case 0x4061e0:
- printk("BCM 5221\n");
- gp->phy_mod = phymod_bcm5221;
- break;
-
- case 0x206040:
- printk("BCM 5400\n");
- gp->phy_mod = phymod_bcm5400;
- gem_init_bcm5400_phy(gp);
- gp->gigabit_capable = 1;
- break;
-
- case 0x206050:
- printk("BCM 5401\n");
- gp->phy_mod = phymod_bcm5401;
- gem_init_bcm5401_phy(gp);
- gp->gigabit_capable = 1;
- break;
-
- case 0x206070:
- printk("BCM 5411\n");
- gp->phy_mod = phymod_bcm5411;
- gem_init_bcm5411_phy(gp);
- gp->gigabit_capable = 1;
- break;
- case 0x1410c60:
- printk("M1011 (Marvel ?)\n");
- gp->phy_mod = phymod_m1011;
- gp->gigabit_capable = 1;
- break;
-
- case 0x18074c0:
- printk("Lucent\n");
- gp->phy_mod = phymod_generic;
- break;
-
- case 0x437420:
- printk("Enable Semiconductor\n");
- gp->phy_mod = phymod_generic;
- break;
-
- default:
- printk("Unknown (Using generic mode)\n");
- gp->phy_mod = phymod_generic;
- break;
- };
+ // XXX check for errors
+ mii_phy_probe(&gp->phy_mii, gp->mii_phy_addr);
- /* Init advertisement and enable autonegotiation. */
- val = phy_read(gp, MII_BMCR);
- val &= ~BMCR_ANENABLE;
- phy_write(gp, MII_BMCR, val);
- udelay(10);
-
- phy_write(gp, MII_ADVERTISE,
- phy_read(gp, MII_ADVERTISE) |
- (ADVERTISE_10HALF | ADVERTISE_10FULL |
- ADVERTISE_100HALF | ADVERTISE_100FULL));
+ /* Init PHY */
+ if (gp->phy_mii.def && gp->phy_mii.def->ops->init)
+ gp->phy_mii.def->ops->init(&gp->phy_mii);
} else {
u32 val;
int limit;
@@ -1819,13 +1614,7 @@
else
val |= PCS_SCTRL_LOOP;
writel(val, gp->regs + PCS_SCTRL);
- gp->gigabit_capable = 1;
}
-
- /* BMCR_SPD2 is a broadcom 54xx specific thing afaik */
- if (gp->phy_mod != phymod_bcm5400 && gp->phy_mod != phymod_bcm5401 &&
- gp->phy_mod != phymod_bcm5411)
- gp->link_cntl &= ~BMCR_SPD2;
}
/* Must be invoked under gp->lock. */
@@ -1914,9 +1703,7 @@
{
unsigned char *e = &gp->dev->dev_addr[0];
- if (gp->pdev->vendor == PCI_VENDOR_ID_SUN &&
- gp->pdev->device == PCI_DEVICE_ID_SUN_GEM)
- writel(0x1bf0, gp->regs + MAC_SNDPAUSE);
+ writel(0x1bf0, gp->regs + MAC_SNDPAUSE);
writel(0x00, gp->regs + MAC_IPG0);
writel(0x08, gp->regs + MAC_IPG1);
@@ -1953,7 +1740,9 @@
writel(0, gp->regs + MAC_AF0MSK);
gp->mac_rx_cfg = gem_setup_multicast(gp);
-
+#ifdef STRIP_FCS
+ gp->mac_rx_cfg |= MAC_RXCFG_SFCS;
+#endif
writel(0, gp->regs + MAC_NCOLL);
writel(0, gp->regs + MAC_FASUCC);
writel(0, gp->regs + MAC_ECOLL);
@@ -2129,12 +1918,15 @@
/* Default aneg parameters */
gp->timer_ticks = 0;
gp->lstate = link_down;
+ netif_carrier_off(gp->dev);
/* Can I advertise gigabit here ? I'd need BCM PHY docs... */
gem_begin_auto_negotiation(gp, NULL);
} else {
- if (gp->lstate == link_up)
+ if (gp->lstate == link_up) {
+ netif_carrier_on(gp->dev);
gem_set_link_modes(gp);
+ }
}
}
@@ -2184,9 +1976,6 @@
{
u32 mifcfg;
- if (!gp->wake_on_lan && gp->phy_mod == phymod_bcm5201)
- phy_write(gp, MII_BCM5201_INTERRUPT, 0);
-
/* Make sure we aren't polling PHY status change. We
* don't currently use that feature though
*/
@@ -2194,9 +1983,6 @@
mifcfg &= ~MIF_CFG_POLL;
writel(mifcfg, gp->regs + MIF_CFG);
- /* Here's a strange hack used by both MacOS 9 and X */
- phy_write(gp, MII_LPA, phy_read(gp, MII_LPA));
-
if (gp->wake_on_lan) {
/* Setup wake-on-lan */
} else
@@ -2210,21 +1996,12 @@
gem_stop(gp);
writel(MAC_TXRST_CMD, gp->regs + MAC_TXRST);
writel(MAC_RXRST_CMD, gp->regs + MAC_RXRST);
- if (gp->phy_mod == phymod_bcm5400 || gp->phy_mod == phymod_bcm5401 ||
- gp->phy_mod == phymod_bcm5411) {
-#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
- phy_write(gp, MII_BMCR, BMCR_PDOWN);
-#endif
- } else if (gp->phy_mod == phymod_bcm5201 || gp->phy_mod == phymod_bcm5221) {
-#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
- u16 val = phy_read(gp, MII_BCM5201_AUXMODE2)
- phy_write(gp, MII_BCM5201_AUXMODE2,
- val & ~MII_BCM5201_AUXMODE2_LOWPOWER);
-#endif
- phy_write(gp, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE);
- } else if (gp->phy_mod == phymod_m1011)
- phy_write(gp, MII_BMCR, BMCR_PDOWN);
+ }
+ if (found_mii_phy(gp) && gp->phy_mii.def->ops->suspend)
+ gp->phy_mii.def->ops->suspend(&gp->phy_mii, 0 /* wake on lan options */);
+
+ if (!gp->wake_on_lan) {
/* According to Apple, we must set the MDIO pins to this begnign
* state or we may 1) eat more current, 2) damage some PHYs
*/
@@ -2330,15 +2107,11 @@
gp->hw_running = 1;
}
- spin_lock_irq(&gp->lock);
-
/* We can now request the interrupt as we know it's masked
* on the controller
*/
if (request_irq(gp->pdev->irq, gem_interrupt,
SA_SHIRQ, dev->name, (void *)dev)) {
- spin_unlock_irq(&gp->lock);
-
printk(KERN_ERR "%s: failed to request irq !\n", gp->dev->name);
#ifdef CONFIG_ALL_PPC
@@ -2349,10 +2122,13 @@
gp->pm_timer.expires = jiffies + 10*HZ;
add_timer(&gp->pm_timer);
up(&gp->pm_sem);
+ spin_unlock_irq(&gp->lock);
return -EAGAIN;
}
+ spin_lock_irq(&gp->lock);
+
/* Allocate & setup ring buffers */
gem_init_rings(gp);
@@ -2526,7 +2302,11 @@
netif_stop_queue(dev);
rxcfg = readl(gp->regs + MAC_RXCFG);
- gp->mac_rx_cfg = rxcfg_new = gem_setup_multicast(gp);
+ rxcfg_new = gem_setup_multicast(gp);
+#ifdef STRIP_FCS
+ rxcfg_new |= MAC_RXCFG_SFCS;
+#endif
+ gp->mac_rx_cfg = rxcfg_new;
writel(rxcfg & ~MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG);
while (readl(gp->regs + MAC_RXCFG) & MAC_RXCFG_ENAB) {
@@ -2551,8 +2331,6 @@
static int gem_ethtool_ioctl(struct net_device *dev, void *ep_user)
{
struct gem *gp = dev->priv;
- u16 bmcr;
- int full_duplex, speed, pause;
struct ethtool_cmd ecmd;
if (copy_from_user(&ecmd, ep_user, sizeof(ecmd)))
@@ -2560,7 +2338,7 @@
switch(ecmd.cmd) {
case ETHTOOL_GDRVINFO: {
- struct ethtool_drvinfo info = { cmd: ETHTOOL_GDRVINFO };
+ struct ethtool_drvinfo info = { .cmd = ETHTOOL_GDRVINFO };
strncpy(info.driver, DRV_NAME, ETHTOOL_BUSINFO_LEN);
strncpy(info.version, DRV_VERSION, ETHTOOL_BUSINFO_LEN);
@@ -2575,41 +2353,36 @@
}
case ETHTOOL_GSET:
- ecmd.supported =
+ if (gp->phy_type == phy_mii_mdio0 ||
+ gp->phy_type == phy_mii_mdio1) {
+ if (gp->phy_mii.def)
+ ecmd.supported = gp->phy_mii.def->features;
+ else
+ ecmd.supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full;
+
+ /* XXX hardcoded stuff for now */
+ ecmd.port = PORT_MII;
+ ecmd.transceiver = XCVR_EXTERNAL;
+ ecmd.phy_address = 0; /* XXX fixed PHYAD */
+
+ /* Return current PHY settings */
+ spin_lock_irq(&gp->lock);
+ ecmd.autoneg = gp->want_autoneg;
+ ecmd.speed = gp->phy_mii.speed;
+ ecmd.duplex = gp->phy_mii.duplex;
+ ecmd.advertising = gp->phy_mii.advertising;
+ /* If we started with a forced mode, we don't have a default
+ * advertise set, we need to return something sensible so
+ * userland can re-enable autoneg properly */
+ if (ecmd.advertising == 0)
+ ecmd.advertising = ecmd.supported;
+ spin_unlock_irq(&gp->lock);
+ } else { // XXX PCS ?
+ ecmd.supported =
(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
- SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
-
- if (gp->gigabit_capable)
- ecmd.supported |=
- (SUPPORTED_1000baseT_Half |
- SUPPORTED_1000baseT_Full);
-
- /* XXX hardcoded stuff for now */
- ecmd.port = PORT_MII;
- ecmd.transceiver = XCVR_EXTERNAL;
- ecmd.phy_address = 0; /* XXX fixed PHYAD */
-
- /* Record PHY settings if HW is on. */
- spin_lock_irq(&gp->lock);
- if (gp->hw_running) {
- bmcr = phy_read(gp, MII_BMCR);
- gem_read_mii_link_mode(gp, &full_duplex, &speed, &pause);
- } else
- bmcr = 0;
- spin_unlock_irq(&gp->lock);
- if (bmcr & BMCR_ANENABLE) {
- ecmd.autoneg = AUTONEG_ENABLE;
- ecmd.speed = speed == 10 ? SPEED_10 : (speed == 1000 ? SPEED_1000 : SPEED_100);
- ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
- } else {
- ecmd.autoneg = AUTONEG_DISABLE;
- ecmd.speed =
- (bmcr & BMCR_SPEED100) ?
- SPEED_100 : SPEED_10;
- ecmd.duplex =
- (bmcr & BMCR_FULLDPLX) ?
- DUPLEX_FULL : DUPLEX_HALF;
+ SUPPORTED_Autoneg);
+ ecmd.advertising = ecmd.supported;
}
if (copy_to_user(ep_user, &ecmd, sizeof(ecmd)))
return -EFAULT;
@@ -2621,13 +2394,18 @@
ecmd.autoneg != AUTONEG_DISABLE)
return -EINVAL;
+ if (ecmd.autoneg == AUTONEG_ENABLE &&
+ ecmd.advertising == 0)
+ return -EINVAL;
+
if (ecmd.autoneg == AUTONEG_DISABLE &&
- ((ecmd.speed != SPEED_100 &&
+ ((ecmd.speed != SPEED_1000 &&
+ ecmd.speed != SPEED_100 &&
ecmd.speed != SPEED_10) ||
(ecmd.duplex != DUPLEX_HALF &&
ecmd.duplex != DUPLEX_FULL)))
return -EINVAL;
-
+
/* Apply settings and restart link process. */
spin_lock_irq(&gp->lock);
gem_begin_auto_negotiation(gp, &ecmd);
@@ -2636,7 +2414,7 @@
return 0;
case ETHTOOL_NWAY_RST:
- if ((gp->link_cntl & BMCR_ANENABLE) == 0)
+ if (!gp->want_autoneg)
return -EINVAL;
/* Restart link process. */
@@ -2652,7 +2430,7 @@
/* get link status */
case ETHTOOL_GLINK: {
- struct ethtool_value edata = { cmd: ETHTOOL_GLINK };
+ struct ethtool_value edata = { .cmd = ETHTOOL_GLINK };
edata.data = (gp->lstate == link_up);
if (copy_to_user(ep_user, &edata, sizeof(edata)))
@@ -2662,7 +2440,7 @@
/* get message-level */
case ETHTOOL_GMSGLVL: {
- struct ethtool_value edata = { cmd: ETHTOOL_GMSGLVL };
+ struct ethtool_value edata = { .cmd = ETHTOOL_GMSGLVL };
edata.data = gp->msg_enable;
if (copy_to_user(ep_user, &edata, sizeof(edata)))
@@ -2740,15 +2518,21 @@
/* Fallthrough... */
case SIOCGMIIREG: /* Read MII PHY register. */
- data->val_out = __phy_read(gp, data->reg_num & 0x1f, data->phy_id & 0x1f);
- rc = 0;
+ if (!gp->hw_running)
+ rc = -EIO;
+ else {
+ data->val_out = __phy_read(gp, data->phy_id & 0x1f, data->reg_num & 0x1f);
+ rc = 0;
+ }
break;
case SIOCSMIIREG: /* Write MII PHY register. */
- if (!capable(CAP_NET_ADMIN)) {
+ if (!capable(CAP_NET_ADMIN))
rc = -EPERM;
- } else {
- __phy_write(gp, data->reg_num & 0x1f, data->val_in, data->phy_id & 0x1f);
+ else if (!gp->hw_running)
+ rc = -EIO;
+ else {
+ __phy_write(gp, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
rc = 0;
}
break;
@@ -2894,7 +2678,7 @@
*/
if (pdev->vendor == PCI_VENDOR_ID_SUN &&
pdev->device == PCI_DEVICE_ID_SUN_GEM &&
- !pci_set_dma_mask(pdev, (u64) 0xffffffffffffffff)) {
+ !pci_set_dma_mask(pdev, (u64) 0xffffffffffffffffULL)) {
pci_using_dac = 1;
} else {
err = pci_set_dma_mask(pdev, (u64) 0xffffffff);
@@ -2934,7 +2718,7 @@
dev->base_addr = (long) pdev;
gp->dev = dev;
- gp->msg_enable = (gem_debug < 0 ? DEFAULT_MSG : gem_debug);
+ gp->msg_enable = DEFAULT_MSG;
spin_lock_init(&gp->lock);
init_MUTEX(&gp->pm_sem);
@@ -2950,13 +2734,9 @@
INIT_TQUEUE(&gp->pm_task, gem_pm_task, gp);
INIT_TQUEUE(&gp->reset_task, gem_reset_task, gp);
- /* Default link parameters */
- if (link_mode >= 0 && link_mode <= 6)
- gp->link_cntl = link_modes[link_mode];
- else
- gp->link_cntl = BMCR_ANENABLE;
gp->lstate = link_down;
gp->timer_ticks = 0;
+ netif_carrier_off(dev);
gp->regs = (unsigned long) ioremap(gemreg_base, gemreg_len);
if (gp->regs == 0UL) {
@@ -2977,16 +2757,18 @@
gem_stop(gp);
spin_unlock_irq(&gp->lock);
+ /* Fill up the mii_phy structure (even if we won't use it) */
+ gp->phy_mii.dev = dev;
+ gp->phy_mii.mdio_read = _phy_read;
+ gp->phy_mii.mdio_write = _phy_write;
+
+ /* By default, we start with autoneg */
+ gp->want_autoneg = 1;
+
if (gem_check_invariants(gp))
goto err_out_iounmap;
- spin_lock_irq(&gp->lock);
- gp->hw_running = 1;
- gem_init_phy(gp);
- gem_begin_auto_negotiation(gp, NULL);
- spin_unlock_irq(&gp->lock);
-
- /* It is guarenteed that the returned buffer will be at least
+ /* It is guaranteed that the returned buffer will be at least
* PAGE_SIZE aligned.
*/
gp->init_block = (struct gem_init_block *)
@@ -3012,12 +2794,23 @@
printk(KERN_INFO "%s: Sun GEM (PCI) 10/100/1000BaseT Ethernet ",
dev->name);
-
for (i = 0; i < 6; i++)
printk("%2.2x%c", dev->dev_addr[i],
i == 5 ? ' ' : ':');
printk("\n");
+ /* Detect & init PHY, start autoneg */
+ spin_lock_irq(&gp->lock);
+ gp->hw_running = 1;
+ gem_init_phy(gp);
+ gem_begin_auto_negotiation(gp, NULL);
+ spin_unlock_irq(&gp->lock);
+
+ if (gp->phy_type == phy_mii_mdio0 ||
+ gp->phy_type == phy_mii_mdio1)
+ printk(KERN_INFO "%s: Found %s PHY\n", dev->name,
+ gp->phy_mii.def ? gp->phy_mii.def->name : "no");
+
pci_set_drvdata(pdev, dev);
dev->open = gem_open;
diff -uNr linux-2.4.22/drivers/net/sungem.h linux-2.4.22-ben2/drivers/net/sungem.h
--- linux-2.4.22/drivers/net/sungem.h 2002-08-03 02:39:44.000000000 +0200
+++ linux-2.4.22-ben2/drivers/net/sungem.h 2003-08-31 11:39:02.000000000 +0200
@@ -805,14 +805,14 @@
u64 buffer;
};
-#define TXDCTRL_BUFSZ 0x0000000000007fff /* Buffer Size */
-#define TXDCTRL_CSTART 0x00000000001f8000 /* CSUM Start Offset */
-#define TXDCTRL_COFF 0x000000001fe00000 /* CSUM Stuff Offset */
-#define TXDCTRL_CENAB 0x0000000020000000 /* CSUM Enable */
-#define TXDCTRL_EOF 0x0000000040000000 /* End of Frame */
-#define TXDCTRL_SOF 0x0000000080000000 /* Start of Frame */
-#define TXDCTRL_INTME 0x0000000100000000 /* "Interrupt Me" */
-#define TXDCTRL_NOCRC 0x0000000200000000 /* No CRC Present */
+#define TXDCTRL_BUFSZ 0x0000000000007fffULL /* Buffer Size */
+#define TXDCTRL_CSTART 0x00000000001f8000ULL /* CSUM Start Offset */
+#define TXDCTRL_COFF 0x000000001fe00000ULL /* CSUM Stuff Offset */
+#define TXDCTRL_CENAB 0x0000000020000000ULL /* CSUM Enable */
+#define TXDCTRL_EOF 0x0000000040000000ULL /* End of Frame */
+#define TXDCTRL_SOF 0x0000000080000000ULL /* Start of Frame */
+#define TXDCTRL_INTME 0x0000000100000000ULL /* "Interrupt Me" */
+#define TXDCTRL_NOCRC 0x0000000200000000ULL /* No CRC Present */
/* GEM requires that RX descriptors are provided four at a time,
* aligned. Also, the RX ring may not wrap around. This means that
@@ -840,13 +840,13 @@
u64 buffer;
};
-#define RXDCTRL_TCPCSUM 0x000000000000ffff /* TCP Pseudo-CSUM */
-#define RXDCTRL_BUFSZ 0x000000007fff0000 /* Buffer Size */
-#define RXDCTRL_OWN 0x0000000080000000 /* GEM owns this entry */
-#define RXDCTRL_HASHVAL 0x0ffff00000000000 /* Hash Value */
-#define RXDCTRL_HPASS 0x1000000000000000 /* Passed Hash Filter */
-#define RXDCTRL_ALTMAC 0x2000000000000000 /* Matched ALT MAC */
-#define RXDCTRL_BAD 0x4000000000000000 /* Frame has bad CRC */
+#define RXDCTRL_TCPCSUM 0x000000000000ffffULL /* TCP Pseudo-CSUM */
+#define RXDCTRL_BUFSZ 0x000000007fff0000ULL /* Buffer Size */
+#define RXDCTRL_OWN 0x0000000080000000ULL /* GEM owns this entry */
+#define RXDCTRL_HASHVAL 0x0ffff00000000000ULL /* Hash Value */
+#define RXDCTRL_HPASS 0x1000000000000000ULL /* Passed Hash Filter */
+#define RXDCTRL_ALTMAC 0x2000000000000000ULL /* Matched ALT MAC */
+#define RXDCTRL_BAD 0x4000000000000000ULL /* Frame has bad CRC */
#define RXDCTRL_FRESH(gp) \
((((RX_BUF_ALLOC_SIZE(gp) - RX_OFFSET) << 16) & RXDCTRL_BUFSZ) | \
@@ -936,16 +936,6 @@
phy_serdes,
};
-enum gem_phy_model {
- phymod_generic,
- phymod_bcm5201,
- phymod_bcm5221,
- phymod_bcm5400,
- phymod_bcm5401,
- phymod_bcm5411,
- phymod_m1011,
-};
-
enum link_state {
link_down = 0, /* No link, will retry */
link_aneg, /* Autoneg in progress */
@@ -980,21 +970,20 @@
struct net_device_stats net_stats;
enum gem_phy_type phy_type;
- enum gem_phy_model phy_mod;
+ struct mii_phy phy_mii;
+
int tx_fifo_sz;
int rx_fifo_sz;
int rx_pause_off;
int rx_pause_on;
int mii_phy_addr;
- int gigabit_capable;
u32 mac_rx_cfg;
u32 swrst_base;
/* Autoneg & PHY control */
- int link_cntl;
- int link_advertise;
- int link_fcntl;
+ int want_autoneg;
+ int last_forced_speed;
enum link_state lstate;
struct timer_list link_timer;
int timer_ticks;
@@ -1014,6 +1003,9 @@
#endif
};
+#define found_mii_phy(gp) ((gp->phy_type == phy_mii_mdio0 || gp->phy_type == phy_mii_mdio1) \
+ && gp->phy_mii.def && gp->phy_mii.def->ops)
+
#define ALIGNED_RX_SKB_ADDR(addr) \
((((unsigned long)(addr) + (64UL - 1UL)) & ~(64UL - 1UL)) - (unsigned long)(addr))
static __inline__ struct sk_buff *gem_alloc_skb(int size, int gfp_flags)
diff -uNr linux-2.4.22/drivers/net/sungem_phy.c linux-2.4.22-ben2/drivers/net/sungem_phy.c
--- linux-2.4.22/drivers/net/sungem_phy.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/drivers/net/sungem_phy.c 2003-08-31 11:42:26.000000000 +0200
@@ -0,0 +1,838 @@
+/*
+ * PHY drivers for the sungem ethernet driver.
+ *
+ * This file could be shared with other drivers.
+ *
+ * (c) 2002, Benjamin Herrenscmidt (benh@kernel.crashing.org)
+ *
+ * TODO:
+ * - Implement WOL
+ * - Add support for PHYs that provide an IRQ line
+ * - Eventually moved the entire polling state machine in
+ * there (out of the eth driver), so that it can easily be
+ * skipped on PHYs that implement it in hardware.
+ * - On LXT971 & BCM5201, Apple uses some chip specific regs
+ * to read the link status. Figure out why and if it makes
+ * sense to do the same (magic aneg ?)
+ * - Apple has some additional power management code for some
+ * Broadcom PHYs that they "hide" from the OpenSource version
+ * of darwin, still need to reverse engineer that
+ */
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "sungem_phy.h"
+
+/* Link modes of the BCM5400 PHY */
+static int phy_BCM5400_link_table[8][3] = {
+ { 0, 0, 0 }, /* No link */
+ { 0, 0, 0 }, /* 10BT Half Duplex */
+ { 1, 0, 0 }, /* 10BT Full Duplex */
+ { 0, 1, 0 }, /* 100BT Half Duplex */
+ { 0, 1, 0 }, /* 100BT Half Duplex */
+ { 1, 1, 0 }, /* 100BT Full Duplex*/
+ { 1, 0, 1 }, /* 1000BT */
+ { 1, 0, 1 }, /* 1000BT */
+};
+
+static inline int __phy_read(struct mii_phy* phy, int id, int reg)
+{
+ return phy->mdio_read(phy->dev, id, reg);
+}
+
+static inline void __phy_write(struct mii_phy* phy, int id, int reg, int val)
+{
+ phy->mdio_write(phy->dev, id, reg, val);
+}
+
+static inline int phy_read(struct mii_phy* phy, int reg)
+{
+ return phy->mdio_read(phy->dev, phy->mii_id, reg);
+}
+
+static inline void phy_write(struct mii_phy* phy, int reg, int val)
+{
+ phy->mdio_write(phy->dev, phy->mii_id, reg, val);
+}
+
+static int reset_one_mii_phy(struct mii_phy* phy, int phy_id)
+{
+ u16 val;
+ int limit = 10000;
+
+ val = __phy_read(phy, phy_id, MII_BMCR);
+ val &= ~BMCR_ISOLATE;
+ val |= BMCR_RESET;
+ __phy_write(phy, phy_id, MII_BMCR, val);
+
+ udelay(100);
+
+ while (limit--) {
+ val = __phy_read(phy, phy_id, MII_BMCR);
+ if ((val & BMCR_RESET) == 0)
+ break;
+ udelay(10);
+ }
+ if ((val & BMCR_ISOLATE) && limit > 0)
+ __phy_write(phy, phy_id, MII_BMCR, val & ~BMCR_ISOLATE);
+
+ return (limit <= 0);
+}
+
+static int bcm5201_init(struct mii_phy* phy)
+{
+ u16 data;
+
+ data = phy_read(phy, MII_BCM5201_MULTIPHY);
+ data &= ~MII_BCM5201_MULTIPHY_SUPERISOLATE;
+ phy_write(phy, MII_BCM5201_MULTIPHY, data);
+
+ return 0;
+}
+
+static int bcm5201_suspend(struct mii_phy* phy, int wol_options)
+{
+ if (!wol_options)
+ phy_write(phy, MII_BCM5201_INTERRUPT, 0);
+
+ /* Here's a strange hack used by both MacOS 9 and X */
+ phy_write(phy, MII_LPA, phy_read(phy, MII_LPA));
+
+ if (!wol_options) {
+#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
+ u16 val = phy_read(phy, MII_BCM5201_AUXMODE2)
+ phy_write(phy, MII_BCM5201_AUXMODE2,
+ val & ~MII_BCM5201_AUXMODE2_LOWPOWER);
+#endif
+ phy_write(phy, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE);
+ }
+
+ return 0;
+}
+
+static int bcm5221_init(struct mii_phy* phy)
+{
+ u16 data;
+
+ data = phy_read(phy, MII_BCM5221_TEST);
+ phy_write(phy, MII_BCM5221_TEST,
+ data | MII_BCM5221_TEST_ENABLE_SHADOWS);
+
+ data = phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2);
+ phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2,
+ data | MII_BCM5221_SHDOW_AUX_STAT2_APD);
+
+ data = phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);
+ phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,
+ data | MII_BCM5221_SHDOW_AUX_MODE4_CLKLOPWR);
+
+ data = phy_read(phy, MII_BCM5221_TEST);
+ phy_write(phy, MII_BCM5221_TEST,
+ data & ~MII_BCM5221_TEST_ENABLE_SHADOWS);
+
+ return 0;
+}
+
+static int bcm5400_init(struct mii_phy* phy)
+{
+ u16 data;
+
+ /* Configure for gigabit full duplex */
+ data = phy_read(phy, MII_BCM5400_AUXCONTROL);
+ data |= MII_BCM5400_AUXCONTROL_PWR10BASET;
+ phy_write(phy, MII_BCM5400_AUXCONTROL, data);
+
+ data = phy_read(phy, MII_BCM5400_GB_CONTROL);
+ data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
+ phy_write(phy, MII_BCM5400_GB_CONTROL, data);
+
+ mdelay(10);
+
+ /* Reset and configure cascaded 10/100 PHY */
+ (void)reset_one_mii_phy(phy, 0x1f);
+
+ data = __phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);
+ data |= MII_BCM5201_MULTIPHY_SERIALMODE;
+ __phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);
+
+ data = phy_read(phy, MII_BCM5400_AUXCONTROL);
+ data &= ~MII_BCM5400_AUXCONTROL_PWR10BASET;
+ phy_write(phy, MII_BCM5400_AUXCONTROL, data);
+
+ return 0;
+}
+
+static int bcm5400_suspend(struct mii_phy* phy, int wol_options)
+{
+#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
+ phy_write(phy, MII_BMCR, BMCR_PDOWN);
+#endif
+ return 0;
+}
+
+static int bcm5401_init(struct mii_phy* phy)
+{
+ u16 data;
+ int rev;
+
+ rev = phy_read(phy, MII_PHYSID2) & 0x000f;
+ if (rev == 0 || rev == 3) {
+ /* Some revisions of 5401 appear to need this
+ * initialisation sequence to disable, according
+ * to OF, "tap power management"
+ *
+ * WARNING ! OF and Darwin don't agree on the
+ * register addresses. OF seem to interpret the
+ * register numbers below as decimal
+ *
+ * Note: This should (and does) match tg3_init_5401phy_dsp
+ * in the tg3.c driver. -DaveM
+ */
+ phy_write(phy, 0x18, 0x0c20);
+ phy_write(phy, 0x17, 0x0012);
+ phy_write(phy, 0x15, 0x1804);
+ phy_write(phy, 0x17, 0x0013);
+ phy_write(phy, 0x15, 0x1204);
+ phy_write(phy, 0x17, 0x8006);
+ phy_write(phy, 0x15, 0x0132);
+ phy_write(phy, 0x17, 0x8006);
+ phy_write(phy, 0x15, 0x0232);
+ phy_write(phy, 0x17, 0x201f);
+ phy_write(phy, 0x15, 0x0a20);
+ }
+
+ /* Configure for gigabit full duplex */
+ data = phy_read(phy, MII_BCM5400_GB_CONTROL);
+ data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
+ phy_write(phy, MII_BCM5400_GB_CONTROL, data);
+
+ mdelay(10);
+
+ /* Reset and configure cascaded 10/100 PHY */
+ (void)reset_one_mii_phy(phy, 0x1f);
+
+ data = __phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);
+ data |= MII_BCM5201_MULTIPHY_SERIALMODE;
+ __phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);
+
+ return 0;
+}
+
+static int bcm5401_suspend(struct mii_phy* phy, int wol_options)
+{
+#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
+ phy_write(phy, MII_BMCR, BMCR_PDOWN);
+#endif
+ return 0;
+}
+
+static int bcm5411_init(struct mii_phy* phy)
+{
+ u16 data;
+
+ /* Here's some more Apple black magic to setup
+ * some voltage stuffs.
+ */
+ phy_write(phy, 0x1c, 0x8c23);
+ phy_write(phy, 0x1c, 0x8ca3);
+ phy_write(phy, 0x1c, 0x8c23);
+
+ /* Here, Apple seems to want to reset it, do
+ * it as well
+ */
+ phy_write(phy, MII_BMCR, BMCR_RESET);
+ phy_write(phy, MII_BMCR, 0x1340);
+
+ data = phy_read(phy, MII_BCM5400_GB_CONTROL);
+ data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
+ phy_write(phy, MII_BCM5400_GB_CONTROL, data);
+
+ mdelay(10);
+
+ /* Reset and configure cascaded 10/100 PHY */
+ (void)reset_one_mii_phy(phy, 0x1f);
+
+ return 0;
+}
+
+static int bcm5411_suspend(struct mii_phy* phy, int wol_options)
+{
+ phy_write(phy, MII_BMCR, BMCR_PDOWN);
+
+ return 0;
+}
+
+static int bcm5421_init(struct mii_phy* phy)
+{
+ u16 data;
+ int rev;
+
+ rev = phy_read(phy, MII_PHYSID2) & 0x000f;
+ if (rev == 0) {
+ /* This is borrowed from MacOS
+ */
+ phy_write(phy, 0x18, 0x1007);
+ data = phy_read(phy, 0x18);
+ phy_write(phy, 0x18, data | 0x0400);
+ phy_write(phy, 0x18, 0x0007);
+ data = phy_read(phy, 0x18);
+ phy_write(phy, 0x18, data | 0x0800);
+ phy_write(phy, 0x17, 0x000a);
+ data = phy_read(phy, 0x15);
+ phy_write(phy, 0x15, data | 0x0200);
+ }
+#if 0
+ /* This has to be verified before I enable it */
+ /* Enable automatic low-power */
+ phy_write(phy, 0x1c, 0x9002);
+ phy_write(phy, 0x1c, 0xa821);
+ phy_write(phy, 0x1c, 0x941d);
+#endif
+ return 0;
+}
+
+static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise)
+{
+ u16 ctl, adv;
+
+ phy->autoneg = 1;
+ phy->speed = SPEED_10;
+ phy->duplex = DUPLEX_HALF;
+ phy->pause = 0;
+ phy->advertising = advertise;
+
+ /* Setup standard advertise */
+ adv = phy_read(phy, MII_ADVERTISE);
+ adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+ if (advertise & ADVERTISED_10baseT_Half)
+ adv |= ADVERTISE_10HALF;
+ if (advertise & ADVERTISED_10baseT_Full)
+ adv |= ADVERTISE_10FULL;
+ if (advertise & ADVERTISED_100baseT_Half)
+ adv |= ADVERTISE_100HALF;
+ if (advertise & ADVERTISED_100baseT_Full)
+ adv |= ADVERTISE_100FULL;
+ phy_write(phy, MII_ADVERTISE, adv);
+
+ /* Setup 1000BT advertise */
+ adv = phy_read(phy, MII_1000BASETCONTROL);
+ adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP|MII_1000BASETCONTROL_HALFDUPLEXCAP);
+ if (advertise & SUPPORTED_1000baseT_Half)
+ adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
+ if (advertise & SUPPORTED_1000baseT_Full)
+ adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
+ phy_write(phy, MII_1000BASETCONTROL, adv);
+
+ /* Start/Restart aneg */
+ ctl = phy_read(phy, MII_BMCR);
+ ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ phy_write(phy, MII_BMCR, ctl);
+
+ return 0;
+}
+
+static int bcm54xx_setup_forced(struct mii_phy *phy, int speed, int fd)
+{
+ u16 ctl;
+
+ phy->autoneg = 0;
+ phy->speed = speed;
+ phy->duplex = fd;
+ phy->pause = 0;
+
+ ctl = phy_read(phy, MII_BMCR);
+ ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE);
+
+ /* First reset the PHY */
+ phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
+
+ /* Select speed & duplex */
+ switch(speed) {
+ case SPEED_10:
+ break;
+ case SPEED_100:
+ ctl |= BMCR_SPEED100;
+ break;
+ case SPEED_1000:
+ ctl |= BMCR_SPD2;
+ }
+ if (fd == DUPLEX_FULL)
+ ctl |= BMCR_FULLDPLX;
+
+ // XXX Should we set the sungem to GII now on 1000BT ?
+
+ phy_write(phy, MII_BMCR, ctl);
+
+ return 0;
+}
+
+static int bcm54xx_read_link(struct mii_phy *phy)
+{
+ int link_mode;
+ u16 val;
+
+ if (phy->autoneg) {
+ val = phy_read(phy, MII_BCM5400_AUXSTATUS);
+ link_mode = ((val & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >>
+ MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT);
+ phy->duplex = phy_BCM5400_link_table[link_mode][0] ? DUPLEX_FULL : DUPLEX_HALF;
+ phy->speed = phy_BCM5400_link_table[link_mode][2] ?
+ SPEED_1000 :
+ (phy_BCM5400_link_table[link_mode][1] ? SPEED_100 : SPEED_10);
+ val = phy_read(phy, MII_LPA);
+ phy->pause = ((val & LPA_PAUSE) != 0);
+ }
+ /* On non-aneg, we assume what we put in BMCR is the speed,
+ * though magic-aneg shouldn't prevent this case from occurring
+ */
+
+ return 0;
+}
+
+static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise)
+{
+ u16 ctl, adv;
+
+ phy->autoneg = 1;
+ phy->speed = SPEED_10;
+ phy->duplex = DUPLEX_HALF;
+ phy->pause = 0;
+ phy->advertising = advertise;
+
+ /* Setup standard advertise */
+ adv = phy_read(phy, MII_ADVERTISE);
+ adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+ if (advertise & ADVERTISED_10baseT_Half)
+ adv |= ADVERTISE_10HALF;
+ if (advertise & ADVERTISED_10baseT_Full)
+ adv |= ADVERTISE_10FULL;
+ if (advertise & ADVERTISED_100baseT_Half)
+ adv |= ADVERTISE_100HALF;
+ if (advertise & ADVERTISED_100baseT_Full)
+ adv |= ADVERTISE_100FULL;
+ phy_write(phy, MII_ADVERTISE, adv);
+
+ /* Setup 1000BT advertise & enable crossover detect
+ * XXX How do we advertise 1000BT ? Darwin source is
+ * confusing here, they read from specific control and
+ * write to control... Someone has specs for those
+ * beasts ?
+ */
+ adv = phy_read(phy, MII_M1011_PHY_SPEC_CONTROL);
+ adv |= MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX;
+ adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
+ MII_1000BASETCONTROL_HALFDUPLEXCAP);
+ if (advertise & SUPPORTED_1000baseT_Half)
+ adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
+ if (advertise & SUPPORTED_1000baseT_Full)
+ adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
+ phy_write(phy, MII_1000BASETCONTROL, adv);
+
+ /* Start/Restart aneg */
+ ctl = phy_read(phy, MII_BMCR);
+ ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ phy_write(phy, MII_BMCR, ctl);
+
+ return 0;
+}
+
+static int marvell_setup_forced(struct mii_phy *phy, int speed, int fd)
+{
+ u16 ctl, ctl2;
+
+ phy->autoneg = 0;
+ phy->speed = speed;
+ phy->duplex = fd;
+ phy->pause = 0;
+
+ ctl = phy_read(phy, MII_BMCR);
+ ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE);
+ ctl |= BMCR_RESET;
+
+ /* Select speed & duplex */
+ switch(speed) {
+ case SPEED_10:
+ break;
+ case SPEED_100:
+ ctl |= BMCR_SPEED100;
+ break;
+ /* I'm not sure about the one below, again, Darwin source is
+ * quite confusing and I lack chip specs
+ */
+ case SPEED_1000:
+ ctl |= BMCR_SPD2;
+ }
+ if (fd == DUPLEX_FULL)
+ ctl |= BMCR_FULLDPLX;
+
+ /* Disable crossover. Again, the way Apple does it is strange,
+ * though I don't assume they are wrong ;)
+ */
+ ctl2 = phy_read(phy, MII_M1011_PHY_SPEC_CONTROL);
+ ctl2 &= ~(MII_M1011_PHY_SPEC_CONTROL_MANUAL_MDIX |
+ MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX |
+ MII_1000BASETCONTROL_FULLDUPLEXCAP |
+ MII_1000BASETCONTROL_HALFDUPLEXCAP);
+ if (speed == SPEED_1000)
+ ctl2 |= (fd == DUPLEX_FULL) ?
+ MII_1000BASETCONTROL_FULLDUPLEXCAP :
+ MII_1000BASETCONTROL_HALFDUPLEXCAP;
+ phy_write(phy, MII_1000BASETCONTROL, ctl2);
+
+ // XXX Should we set the sungem to GII now on 1000BT ?
+
+ phy_write(phy, MII_BMCR, ctl);
+
+ return 0;
+}
+
+static int marvell_read_link(struct mii_phy *phy)
+{
+ u16 status;
+
+ if (phy->autoneg) {
+ status = phy_read(phy, MII_M1011_PHY_SPEC_STATUS);
+ if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
+ return -EAGAIN;
+ if (status & MII_M1011_PHY_SPEC_STATUS_1000)
+ phy->speed = SPEED_1000;
+ else if (status & MII_M1011_PHY_SPEC_STATUS_100)
+ phy->speed = SPEED_100;
+ else
+ phy->speed = SPEED_10;
+ if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
+ phy->duplex = DUPLEX_FULL;
+ else
+ phy->duplex = DUPLEX_HALF;
+ phy->pause = 0; /* XXX Check against spec ! */
+ }
+ /* On non-aneg, we assume what we put in BMCR is the speed,
+ * though magic-aneg shouldn't prevent this case from occurring
+ */
+
+ return 0;
+}
+
+static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
+{
+ u16 ctl, adv;
+
+ phy->autoneg = 1;
+ phy->speed = SPEED_10;
+ phy->duplex = DUPLEX_HALF;
+ phy->pause = 0;
+ phy->advertising = advertise;
+
+ /* Setup standard advertise */
+ adv = phy_read(phy, MII_ADVERTISE);
+ adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+ if (advertise & ADVERTISED_10baseT_Half)
+ adv |= ADVERTISE_10HALF;
+ if (advertise & ADVERTISED_10baseT_Full)
+ adv |= ADVERTISE_10FULL;
+ if (advertise & ADVERTISED_100baseT_Half)
+ adv |= ADVERTISE_100HALF;
+ if (advertise & ADVERTISED_100baseT_Full)
+ adv |= ADVERTISE_100FULL;
+ phy_write(phy, MII_ADVERTISE, adv);
+
+ /* Start/Restart aneg */
+ ctl = phy_read(phy, MII_BMCR);
+ ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ phy_write(phy, MII_BMCR, ctl);
+
+ return 0;
+}
+
+static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
+{
+ u16 ctl;
+
+ phy->autoneg = 0;
+ phy->speed = speed;
+ phy->duplex = fd;
+ phy->pause = 0;
+
+ ctl = phy_read(phy, MII_BMCR);
+ ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE);
+
+ /* First reset the PHY */
+ phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
+
+ /* Select speed & duplex */
+ switch(speed) {
+ case SPEED_10:
+ break;
+ case SPEED_100:
+ ctl |= BMCR_SPEED100;
+ break;
+ case SPEED_1000:
+ default:
+ return -EINVAL;
+ }
+ if (fd == DUPLEX_FULL)
+ ctl |= BMCR_FULLDPLX;
+ phy_write(phy, MII_BMCR, ctl);
+
+ return 0;
+}
+
+static int genmii_poll_link(struct mii_phy *phy)
+{
+ u16 status;
+
+ (void)phy_read(phy, MII_BMSR);
+ status = phy_read(phy, MII_BMSR);
+ if ((status & BMSR_LSTATUS) == 0)
+ return 0;
+ if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE))
+ return 0;
+ return 1;
+}
+
+static int genmii_read_link(struct mii_phy *phy)
+{
+ u16 lpa;
+
+ if (phy->autoneg) {
+ lpa = phy_read(phy, MII_LPA);
+
+ if (lpa & (LPA_10FULL | LPA_100FULL))
+ phy->duplex = DUPLEX_FULL;
+ else
+ phy->duplex = DUPLEX_HALF;
+ if (lpa & (LPA_100FULL | LPA_100HALF))
+ phy->speed = SPEED_100;
+ else
+ phy->speed = SPEED_10;
+ phy->pause = 0;
+ }
+ /* On non-aneg, we assume what we put in BMCR is the speed,
+ * though magic-aneg shouldn't prevent this case from occurring
+ */
+
+ return 0;
+}
+
+
+#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \
+ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \
+ SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII)
+#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \
+ SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)
+
+/* Broadcom BCM 5201 */
+static struct mii_phy_ops bcm5201_phy_ops = {
+ init: bcm5201_init,
+ suspend: bcm5201_suspend,
+ setup_aneg: genmii_setup_aneg,
+ setup_forced: genmii_setup_forced,
+ poll_link: genmii_poll_link,
+ read_link: genmii_read_link,
+};
+
+static struct mii_phy_def bcm5201_phy_def = {
+ phy_id: 0x00406210,
+ phy_id_mask: 0xfffffff0,
+ name: "BCM5201",
+ features: MII_BASIC_FEATURES,
+ magic_aneg: 0,
+ ops: &bcm5201_phy_ops
+};
+
+/* Broadcom BCM 5221 */
+static struct mii_phy_ops bcm5221_phy_ops = {
+ suspend: bcm5201_suspend,
+ init: bcm5221_init,
+ setup_aneg: genmii_setup_aneg,
+ setup_forced: genmii_setup_forced,
+ poll_link: genmii_poll_link,
+ read_link: genmii_read_link,
+};
+
+static struct mii_phy_def bcm5221_phy_def = {
+ phy_id: 0x004061e0,
+ phy_id_mask: 0xfffffff0,
+ name: "BCM5221",
+ features: MII_BASIC_FEATURES,
+ magic_aneg: 0,
+ ops: &bcm5221_phy_ops
+};
+
+/* Broadcom BCM 5400 */
+static struct mii_phy_ops bcm5400_phy_ops = {
+ init: bcm5400_init,
+ suspend: bcm5400_suspend,
+ setup_aneg: bcm54xx_setup_aneg,
+ setup_forced: bcm54xx_setup_forced,
+ poll_link: genmii_poll_link,
+ read_link: bcm54xx_read_link,
+};
+
+static struct mii_phy_def bcm5400_phy_def = {
+ phy_id: 0x00206040,
+ phy_id_mask: 0xfffffff0,
+ name: "BCM5400",
+ features: MII_GBIT_FEATURES,
+ magic_aneg: 1,
+ ops: &bcm5400_phy_ops
+};
+
+/* Broadcom BCM 5401 */
+static struct mii_phy_ops bcm5401_phy_ops = {
+ init: bcm5401_init,
+ suspend: bcm5401_suspend,
+ setup_aneg: bcm54xx_setup_aneg,
+ setup_forced: bcm54xx_setup_forced,
+ poll_link: genmii_poll_link,
+ read_link: bcm54xx_read_link,
+};
+
+static struct mii_phy_def bcm5401_phy_def = {
+ phy_id: 0x00206050,
+ phy_id_mask: 0xfffffff0,
+ name: "BCM5401",
+ features: MII_GBIT_FEATURES,
+ magic_aneg: 1,
+ ops: &bcm5401_phy_ops
+};
+
+/* Broadcom BCM 5411 */
+static struct mii_phy_ops bcm5411_phy_ops = {
+ init: bcm5411_init,
+ suspend: bcm5411_suspend,
+ setup_aneg: bcm54xx_setup_aneg,
+ setup_forced: bcm54xx_setup_forced,
+ poll_link: genmii_poll_link,
+ read_link: bcm54xx_read_link,
+};
+
+static struct mii_phy_def bcm5411_phy_def = {
+ phy_id: 0x00206070,
+ phy_id_mask: 0xfffffff0,
+ name: "BCM5411",
+ features: MII_GBIT_FEATURES,
+ magic_aneg: 1,
+ ops: &bcm5411_phy_ops
+};
+
+/* Broadcom BCM 5421 */
+static struct mii_phy_ops bcm5421_phy_ops = {
+ init: bcm5421_init,
+ suspend: bcm5411_suspend,
+ setup_aneg: bcm54xx_setup_aneg,
+ setup_forced: bcm54xx_setup_forced,
+ poll_link: genmii_poll_link,
+ read_link: bcm54xx_read_link,
+};
+
+static struct mii_phy_def bcm5421_phy_def = {
+ phy_id: 0x002060e0,
+ phy_id_mask: 0xfffffff0,
+ name: "BCM5421",
+ features: MII_GBIT_FEATURES,
+ magic_aneg: 1,
+ ops: &bcm5421_phy_ops
+};
+
+/* Marvell 88E1101 (Apple seem to deal with 2 different revs,
+ * I masked out the 8 last bits to get both, but some specs
+ * would be useful here) --BenH.
+ */
+static struct mii_phy_ops marvell_phy_ops = {
+ setup_aneg: marvell_setup_aneg,
+ setup_forced: marvell_setup_forced,
+ poll_link: genmii_poll_link,
+ read_link: marvell_read_link
+};
+
+static struct mii_phy_def marvell_phy_def = {
+ phy_id: 0x01410c00,
+ phy_id_mask: 0xffffff00,
+ name: "Marvell 88E1101",
+ features: MII_GBIT_FEATURES,
+ magic_aneg: 1,
+ ops: &marvell_phy_ops
+};
+
+/* Generic implementation for most 10/100 PHYs */
+static struct mii_phy_ops generic_phy_ops = {
+ setup_aneg: genmii_setup_aneg,
+ setup_forced: genmii_setup_forced,
+ poll_link: genmii_poll_link,
+ read_link: genmii_read_link
+};
+
+static struct mii_phy_def genmii_phy_def = {
+ phy_id: 0x00000000,
+ phy_id_mask: 0x00000000,
+ name: "Generic MII",
+ features: MII_BASIC_FEATURES,
+ magic_aneg: 0,
+ ops: &generic_phy_ops
+};
+
+static struct mii_phy_def* mii_phy_table[] = {
+ &bcm5201_phy_def,
+ &bcm5221_phy_def,
+ &bcm5400_phy_def,
+ &bcm5401_phy_def,
+ &bcm5411_phy_def,
+ &bcm5421_phy_def,
+ &marvell_phy_def,
+ &genmii_phy_def,
+ NULL
+};
+
+int mii_phy_probe(struct mii_phy *phy, int mii_id)
+{
+ int rc;
+ u32 id;
+ struct mii_phy_def* def;
+ int i;
+
+ /* We do not reset the mii_phy structure as the driver
+ * may re-probe the PHY regulary
+ */
+ phy->mii_id = mii_id;
+
+ /* Take PHY out of isloate mode and reset it. */
+ rc = reset_one_mii_phy(phy, mii_id);
+ if (rc)
+ goto fail;
+
+ /* Read ID and find matching entry */
+ id = (phy_read(phy, MII_PHYSID1) << 16 | phy_read(phy, MII_PHYSID2))
+ & 0xfffffff0;
+ for (i=0; (def = mii_phy_table[i]) != NULL; i++)
+ if ((id & def->phy_id_mask) == def->phy_id)
+ break;
+ /* Should never be NULL (we have a generic entry), but... */
+ if (def == NULL)
+ goto fail;
+
+ phy->def = def;
+
+ return 0;
+fail:
+ phy->speed = 0;
+ phy->duplex = 0;
+ phy->pause = 0;
+ phy->advertising = 0;
+ return -ENODEV;
+}
+
+EXPORT_SYMBOL(mii_phy_probe);
+MODULE_LICENSE("GPL");
+
diff -uNr linux-2.4.22/drivers/net/sungem_phy.h linux-2.4.22-ben2/drivers/net/sungem_phy.h
--- linux-2.4.22/drivers/net/sungem_phy.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/drivers/net/sungem_phy.h 2003-08-31 11:41:49.000000000 +0200
@@ -0,0 +1,116 @@
+#ifndef __SUNGEM_PHY_H__
+#define __SUNGEM_PHY_H__
+
+struct mii_phy;
+
+/* Operations supported by any kind of PHY */
+struct mii_phy_ops
+{
+ int (*init)(struct mii_phy *phy);
+ int (*suspend)(struct mii_phy *phy, int wol_options);
+ int (*setup_aneg)(struct mii_phy *phy, u32 advertise);
+ int (*setup_forced)(struct mii_phy *phy, int speed, int fd);
+ int (*poll_link)(struct mii_phy *phy);
+ int (*read_link)(struct mii_phy *phy);
+};
+
+/* Structure used to statically define an mii/gii based PHY */
+struct mii_phy_def
+{
+ u32 phy_id; /* Concatenated ID1 << 16 | ID2 */
+ u32 phy_id_mask; /* Significant bits */
+ u32 features; /* Ethtool SUPPORTED_* defines */
+ int magic_aneg; /* Autoneg does all speed test for us */
+ const char* name;
+ const struct mii_phy_ops* ops;
+};
+
+/* An instance of a PHY, partially borrowed from mii_if_info */
+struct mii_phy
+{
+ struct mii_phy_def* def;
+ int advertising;
+ int mii_id;
+
+ /* 1: autoneg enabled, 0: disabled */
+ int autoneg;
+
+ /* forced speed & duplex (no autoneg)
+ * partner speed & duplex & pause (autoneg)
+ */
+ int speed;
+ int duplex;
+ int pause;
+
+ /* Provided by host chip */
+ struct net_device* dev;
+ int (*mdio_read) (struct net_device *dev, int mii_id, int reg);
+ void (*mdio_write) (struct net_device *dev, int mii_id, int reg, int val);
+};
+
+/* Pass in a struct mii_phy with dev, mdio_read and mdio_write
+ * filled, the remaining fields will be filled on return
+ */
+extern int mii_phy_probe(struct mii_phy *phy, int mii_id);
+
+
+/* MII definitions missing from mii.h */
+
+#define BMCR_SPD2 0x0040 /* Gigabit enable (bcm54xx) */
+#define LPA_PAUSE 0x0400
+
+/* More PHY registers (model specific) */
+
+/* MII BCM5201 MULTIPHY interrupt register */
+#define MII_BCM5201_INTERRUPT 0x1A
+#define MII_BCM5201_INTERRUPT_INTENABLE 0x4000
+
+#define MII_BCM5201_AUXMODE2 0x1B
+#define MII_BCM5201_AUXMODE2_LOWPOWER 0x0008
+
+#define MII_BCM5201_MULTIPHY 0x1E
+
+/* MII BCM5201 MULTIPHY register bits */
+#define MII_BCM5201_MULTIPHY_SERIALMODE 0x0002
+#define MII_BCM5201_MULTIPHY_SUPERISOLATE 0x0008
+
+/* MII BCM5221 Additional registers */
+#define MII_BCM5221_TEST 0x1f
+#define MII_BCM5221_TEST_ENABLE_SHADOWS 0x0080
+#define MII_BCM5221_SHDOW_AUX_STAT2 0x1b
+#define MII_BCM5221_SHDOW_AUX_STAT2_APD 0x0020
+#define MII_BCM5221_SHDOW_AUX_MODE4 0x1a
+#define MII_BCM5221_SHDOW_AUX_MODE4_CLKLOPWR 0x0004
+
+/* MII BCM5400 1000-BASET Control register */
+#define MII_BCM5400_GB_CONTROL 0x09
+#define MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP 0x0200
+
+/* MII BCM5400 AUXCONTROL register */
+#define MII_BCM5400_AUXCONTROL 0x18
+#define MII_BCM5400_AUXCONTROL_PWR10BASET 0x0004
+
+/* MII BCM5400 AUXSTATUS register */
+#define MII_BCM5400_AUXSTATUS 0x19
+#define MII_BCM5400_AUXSTATUS_LINKMODE_MASK 0x0700
+#define MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT 8
+
+/* 1000BT control (Marvell & BCM54xx at least) */
+#define MII_1000BASETCONTROL 0x09
+#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200
+#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100
+
+/* Marvell 88E1011 PHY control */
+#define MII_M1011_PHY_SPEC_CONTROL 0x10
+#define MII_M1011_PHY_SPEC_CONTROL_MANUAL_MDIX 0x20
+#define MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX 0x40
+
+/* Marvell 88E1011 PHY status */
+#define MII_M1011_PHY_SPEC_STATUS 0x11
+#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000
+#define MII_M1011_PHY_SPEC_STATUS_100 0x4000
+#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000
+#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000
+#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800
+
+#endif /* __SUNGEM_PHY_H__ */
diff -uNr linux-2.4.22/drivers/net/tg3.c linux-2.4.22-ben2/drivers/net/tg3.c
--- linux-2.4.22/drivers/net/tg3.c 2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.22-ben2/drivers/net/tg3.c 2003-08-31 11:38:34.000000000 +0200
@@ -145,6 +145,8 @@
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC9100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_TIGON3,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ 0, }
};
diff -uNr linux-2.4.22/drivers/net/tulip/tulip_core.c linux-2.4.22-ben2/drivers/net/tulip/tulip_core.c
--- linux-2.4.22/drivers/net/tulip/tulip_core.c 2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.22-ben2/drivers/net/tulip/tulip_core.c 2003-08-31 11:40:47.000000000 +0200
@@ -474,6 +474,15 @@
} else
tulip_select_media(dev, 1);
+ /* check for Apple 100BaseTX card and disable loops */
+ if ((dev->dev_addr[0] == 0x00) &&
+ (dev->dev_addr[1] == 0x05) &&
+ (dev->dev_addr[2] == 0x02) &&
+ (tp->chip_id == DC21140)) {
+ outl(0x10f, ioaddr + CSR12);
+ outl(0x03, ioaddr + CSR12);
+ }
+
/* Start the chip's Tx to process setup frame. */
tulip_stop_rxtx(tp);
barrier();
diff -uNr linux-2.4.22/drivers/net/wan/8253x/build linux-2.4.22-ben2/drivers/net/wan/8253x/build
--- linux-2.4.22/drivers/net/wan/8253x/build 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/drivers/net/wan/8253x/build 2003-08-31 11:38:51.000000000 +0200
@@ -0,0 +1,8 @@
+cc -g -o 8253xcfg -I. -U__KERNEL__ 8253xcfg.c
+cc -g -o 8253xspeed -I. -U__KERNEL__ 8253xspeed.c
+cc -g -o 8253xmode -I. -U__KERNEL__ 8253xmode.c
+cc -g -o 8253xpeer -I. -U__KERNEL__ 8253xpeer.c
+cc -g -o eprom9050 -I. -U__KERNEL__ eprom9050.c
+
+
+
diff -uNr linux-2.4.22/drivers/net/wireless/Config.in linux-2.4.22-ben2/drivers/net/wireless/Config.in
--- linux-2.4.22/drivers/net/wireless/Config.in 2002-11-29 00:53:14.000000000 +0100
+++ linux-2.4.22-ben2/drivers/net/wireless/Config.in 2003-08-31 11:39:06.000000000 +0200
@@ -15,6 +15,7 @@
if [ "$CONFIG_PCI" = "y" ]; then
dep_tristate ' Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.) (EXPERIMENTAL)' CONFIG_PLX_HERMES $CONFIG_HERMES $CONFIG_EXPERIMENTAL
dep_tristate ' Prism 2.5 PCI 802.11b adaptor support (EXPERIMENTAL)' CONFIG_PCI_HERMES $CONFIG_HERMES $CONFIG_EXPERIMENTAL
+ dep_tristate ' Prism II devices connected via a TMD7160 (EXPERIMENTAL)' CONFIG_TMD_HERMES $CONFIG_HERMES $CONFIG_EXPERIMENTAL
fi
# If Pcmcia is compiled in, offer Pcmcia cards...
diff -uNr linux-2.4.22/drivers/net/wireless/Makefile linux-2.4.22-ben2/drivers/net/wireless/Makefile
--- linux-2.4.22/drivers/net/wireless/Makefile 2002-08-03 02:39:44.000000000 +0200
+++ linux-2.4.22-ben2/drivers/net/wireless/Makefile 2003-08-31 11:38:34.000000000 +0200
@@ -19,6 +19,7 @@
obj-$(CONFIG_APPLE_AIRPORT) += airport.o
obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o
obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o
+obj-$(CONFIG_TMD_HERMES) += orinoco_tmd.o
obj-$(CONFIG_AIRO) += airo.o
obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o
diff -uNr linux-2.4.22/drivers/pci/pci.c linux-2.4.22-ben2/drivers/pci/pci.c
--- linux-2.4.22/drivers/pci/pci.c 2003-06-13 16:51:35.000000000 +0200
+++ linux-2.4.22-ben2/drivers/pci/pci.c 2003-08-31 11:39:33.000000000 +0200
@@ -28,7 +28,7 @@
#include
#include /* isa_dma_bridge_buggy */
-#undef DEBUG
+#define DEBUG
#ifdef DEBUG
#define DBG(x...) printk(x)
@@ -1059,16 +1059,23 @@
unsigned int pos, reg, next;
u32 l, sz;
struct resource *res;
+ int is_kl = (dev->device == 0x22 && dev->vendor == 0x106b);
+ if (is_kl)
+ howmany = 1;
for(pos=0; posresource[pos];
res->name = dev->name;
reg = PCI_BASE_ADDRESS_0 + (pos << 2);
pci_read_config_dword(dev, reg, &l);
- pci_write_config_dword(dev, reg, ~0);
- pci_read_config_dword(dev, reg, &sz);
- pci_write_config_dword(dev, reg, l);
+ if (is_kl)
+ sz = 0x00080000;
+ else {
+ pci_write_config_dword(dev, reg, ~0);
+ pci_read_config_dword(dev, reg, &sz);
+ pci_write_config_dword(dev, reg, l);
+ }
if (!sz || sz == 0xffffffff)
continue;
if (l == 0xffffffff)
@@ -1309,7 +1316,7 @@
if (!pass)
return max;
pci_read_config_word(dev, PCI_COMMAND, &cr);
- pci_write_config_word(dev, PCI_COMMAND, 0x0000);
+ //pci_write_config_word(dev, PCI_COMMAND, 0x0000);
pci_write_config_word(dev, PCI_STATUS, 0xffff);
child = pci_add_new_bus(bus, dev, ++max);
diff -uNr linux-2.4.22/drivers/sound/dmasound/Config.in linux-2.4.22-ben2/drivers/sound/dmasound/Config.in
--- linux-2.4.22/drivers/sound/dmasound/Config.in 2002-02-25 20:38:04.000000000 +0100
+++ linux-2.4.22-ben2/drivers/sound/dmasound/Config.in 2003-08-31 11:41:23.000000000 +0200
@@ -27,13 +27,27 @@
fi
# the new dmasound_pmac driver needs access to the i2c bus
+# and nvram.
if [ "$CONFIG_DMASOUND_PMAC" = "y" ] ; then
- define_tristate CONFIG_I2C y
- define_tristate CONFIG_I2C_KEYWEST y
+ if [ "$CONFIG_I2C" != "y" ]; then
+ define_tristate CONFIG_I2C y
+ fi
+ if [ "$CONFIG_I2C_KEYWEST" != "y" ]; then
+ define_tristate CONFIG_I2C_KEYWEST y
+ fi
+ if [ "$CONFIG_NVRAM" != "y" -a "$CONFIG_NVRAM" != "m" ]; then
+ define_tristate CONFIG_NVRAM y
+ fi
else
if [ "$CONFIG_DMASOUND_PMAC" = "m" ] ; then
- define_tristate CONFIG_I2C m
- define_tristate CONFIG_I2C_KEYWEST m
+ if [ "$CONFIG_I2C" != "y" -a "$CONFIG_I2C" != "m" ]; then
+ define_tristate CONFIG_I2C m
+ fi
+ if [ "$CONFIG_I2C_KEYWEST" != "y" -a "$CONFIG_I2C_KEYWEST" != "m" ]; then
+ define_tristate CONFIG_I2C_KEYWEST m
+ fi
+ if [ "$CONFIG_NVRAM" != "y" -a "$CONFIG_NVRAM" != "m" ]; then
+ define_tristate CONFIG_NVRAM y
+ fi
fi
fi
-
diff -uNr linux-2.4.22/drivers/sound/dmasound/Makefile linux-2.4.22-ben2/drivers/sound/dmasound/Makefile
--- linux-2.4.22/drivers/sound/dmasound/Makefile 2002-02-25 20:38:04.000000000 +0100
+++ linux-2.4.22-ben2/drivers/sound/dmasound/Makefile 2003-08-31 11:40:06.000000000 +0200
@@ -13,7 +13,8 @@
list-multi := dmasound_pmac.o
-dmasound_pmac-objs := dmasound_awacs.o trans_16.o tas3001c.o dac3550a.o
+dmasound_pmac-objs := dmasound_awacs.o trans_16.o dac3550a.o tas_common.o \
+ tas3001c.o tas3001c_tables.o tas3004.o tas3004_tables.o
obj-$(CONFIG_DMASOUND) += dmasound_core.o
obj-$(CONFIG_DMASOUND_ATARI) += dmasound_atari.o
diff -uNr linux-2.4.22/drivers/sound/dmasound/awacs_defs.h linux-2.4.22-ben2/drivers/sound/dmasound/awacs_defs.h
--- linux-2.4.22/drivers/sound/dmasound/awacs_defs.h 2002-02-25 20:38:04.000000000 +0100
+++ linux-2.4.22-ben2/drivers/sound/dmasound/awacs_defs.h 2003-08-31 11:42:29.000000000 +0200
@@ -168,8 +168,9 @@
#define RATE_LOW 1 /* HIGH = 48kHz, etc; LOW = 44.1kHz, etc. */
-
+/*******************/
/* Burgundy values */
+/*******************/
#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12)
#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12)
@@ -232,4 +233,19 @@
#define DEF_BURGUNDY_ATTENLINEOUT (0xCC)
#define DEF_BURGUNDY_ATTENHP (0xCC)
+/*********************/
+/* i2s layout values */
+/*********************/
+
+#define I2S_REG_INT_CTL 0x00
+#define I2S_REG_SERIAL_FORMAT 0x10
+#define I2S_REG_CODEC_MSG_OUT 0x20
+#define I2S_REG_CODEC_MSG_IN 0x30
+#define I2S_REG_FRAME_COUNT 0x40
+#define I2S_REG_FRAME_MATCH 0x50
+#define I2S_REG_DATAWORD_SIZES 0x60
+#define I2S_REG_PEAKLEVEL_SEL 0x70
+#define I2S_REG_PEAKLEVEL_IN0 0x80
+#define I2S_REG_PEAKLEVEL_IN1 0x90
+
#endif /* _AWACS_DEFS_H_ */
diff -uNr linux-2.4.22/drivers/sound/dmasound/dmasound_awacs.c linux-2.4.22-ben2/drivers/sound/dmasound/dmasound_awacs.c
--- linux-2.4.22/drivers/sound/dmasound/dmasound_awacs.c 2002-08-03 02:39:44.000000000 +0200
+++ linux-2.4.22-ben2/drivers/sound/dmasound/dmasound_awacs.c 2003-08-31 11:42:40.000000000 +0200
@@ -45,7 +45,14 @@
* 01/02/2002 [0.7] - BenH
* - all sort of minor bits went in since the latest update, I
* bumped the version number for that reason
-*/
+ *
+ * 07/26/2002 [0.8] - BenH
+ * - More minor bits since last changelog (I should be more careful
+ * with those)
+ * - Support for snapper & better tumbler integration by Toby Sargeant
+ * - Headphone detect for scremer by Julien Blache
+ * - More tumbler fixed by Andreas Schwab
+ */
/* GENERAL FIXME/TODO: check that the assumptions about what is written to
mac-io is valid for DACA & Tumbler.
@@ -88,10 +95,14 @@
#include "awacs_defs.h"
#include "dmasound.h"
+#include "tas3001c.h"
+#include "tas3004.h"
+#include "tas_common.h"
#define DMASOUND_AWACS_REVISION 0
#define DMASOUND_AWACS_EDITION 7
+#define AWACS_SNAPPER 110 /* fake revision # for snapper */
#define AWACS_BURGUNDY 100 /* fake revision # for burgundy */
#define AWACS_TUMBLER 90 /* fake revision # for tumbler */
#define AWACS_DACA 80 /* fake revision # for daca (ibook) */
@@ -102,11 +113,13 @@
*/
static int awacs_irq, awacs_tx_irq, awacs_rx_irq;
static volatile struct awacs_regs *awacs;
+static volatile u32 *i2s;
static volatile struct dbdma_regs *awacs_txdma, *awacs_rxdma;
static int awacs_rate_index;
static int awacs_subframe;
static int awacs_spkr_vol;
static struct device_node* awacs_node;
+static struct device_node* i2s_node;
static char awacs_name[64];
static int awacs_revision;
@@ -163,6 +176,8 @@
static int cd_lev = 0x6363 ; /* 99 % */
static int line_lev = 0 ;
+static int hdp_connected = 0;
+
/*
* Stuff for outputting a beep. The values range from -327 to +327
* so we can multiply by an amplitude in the range 0..100 to get a
@@ -293,19 +308,6 @@
extern int daca_enter_sleep(void);
extern int daca_leave_sleep(void);
-extern int tas_init(void);
-extern int tas_cleanup(void);
-extern int tumbler_set_volume(uint left_vol, uint right_vol);
-extern void tumbler_get_volume(uint * left_vol, uint *right_vol);
-extern void tumbler_set_treble(int treble);
-extern void tumbler_get_treble(int *treble);
-extern void tumbler_set_bass(int bass);
-extern void tumbler_get_bass(int *bass);
-extern void tumbler_set_pcm_lvl(int pcm_lvl);
-extern void tumbler_get_pcm_lvl(int *pcm_lvl);
-extern int tumbler_enter_sleep(void);
-extern int tumbler_leave_sleep(void);
-
#define TRY_LOCK() \
if ((rc = down_interruptible(&dmasound_sem)) != 0) \
return rc;
@@ -332,7 +334,7 @@
}
-/*** AE - TUMBLER START *********************************************************/
+/*** AE - TUMBLER / SNAPPER START ************************************************/
int gpio_audio_reset, gpio_audio_reset_pol;
@@ -394,17 +396,22 @@
return ((pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio_addr, 0) & 0x02) !=0);
}
+/*
+ * Headphone interrupt via GPIO (Tumbler, Snapper, DACA)
+ */
static void
headphone_intr(int irq, void *devid, struct pt_regs *regs)
{
if (read_audio_gpio(gpio_headphone_detect) == gpio_headphone_detect_pol) {
printk(KERN_INFO "Audio jack plugged, muting speakers.\n");
- write_audio_gpio(gpio_amp_mute, gpio_amp_mute_pol);
write_audio_gpio(gpio_headphone_mute, !gpio_headphone_mute_pol);
+ write_audio_gpio(gpio_amp_mute, gpio_amp_mute_pol);
+ tas_output_device_change(sound_device_id,TAS_OUTPUT_HEADPHONES,0);
} else {
printk(KERN_INFO "Audio jack unplugged, enabling speakers.\n");
write_audio_gpio(gpio_amp_mute, !gpio_amp_mute_pol);
write_audio_gpio(gpio_headphone_mute, gpio_headphone_mute_pol);
+ tas_output_device_change(sound_device_id,TAS_OUTPUT_INTERNAL_SPKR,0);
}
}
@@ -412,7 +419,7 @@
/* Initialize tumbler */
static int
-awacs_tumbler_init(void)
+tas_dmasound_init(void)
{
setup_audio_gpio(
"audio-hw-reset",
@@ -469,15 +476,124 @@
static int
-awacs_tumbler_cleanup(void)
+tas_dmasound_cleanup(void)
{
if (gpio_headphone_irq)
free_irq(gpio_headphone_irq, 0);
return 0;
}
+/* We don't support 48k yet */
+static int tas_freqs[1] = { 44100 } ;
+static int tas_freqs_ok[1] = { 1 } ;
-/*** AE - TUMBLER END *********************************************************/
+/* don't know what to do really - just have to leave it where
+ * OF left things
+*/
+
+static int
+tas_set_frame_rate(void)
+{
+ if (i2s) {
+ out_le32(i2s + (I2S_REG_SERIAL_FORMAT >> 2), 0x41190000);
+ out_le32(i2s + (I2S_REG_DATAWORD_SIZES >> 2), 0x02000200);
+ }
+ dmasound.hard.speed = 44100 ;
+ awacs_rate_index = 0 ;
+ return 44100 ;
+}
+
+static int
+tas_mixer_ioctl(u_int cmd, u_long arg)
+{
+ int data;
+ int rc;
+
+ rc=tas_device_ioctl(cmd, arg);
+ if (rc != -EINVAL) {
+ return rc;
+ }
+
+ if ((cmd & ~0xff) == MIXER_WRITE(0) &&
+ tas_supported_mixers() & (1<<(cmd & 0xff))) {
+ rc = get_user(data, (int *)(arg));
+ if (rc<0) return rc;
+ tas_set_mixer_level(cmd & 0xff, data);
+ tas_get_mixer_level(cmd & 0xff, &data);
+ return ioctl_return2((int *)(arg), data);
+ }
+ if ((cmd & ~0xff) == MIXER_READ(0) &&
+ tas_supported_mixers() & (1<<(cmd & 0xff))) {
+ tas_get_mixer_level(cmd & 0xff, &data);
+ return ioctl_return2((int *)(arg), data);
+ }
+
+ switch(cmd) {
+ case SOUND_MIXER_READ_DEVMASK:
+ data = tas_supported_mixers() | SOUND_MASK_SPEAKER;
+ rc = IOCTL_OUT(arg, data);
+ break;
+ case SOUND_MIXER_READ_STEREODEVS:
+ data = tas_stereo_mixers();
+ rc = IOCTL_OUT(arg, data);
+ break;
+ case SOUND_MIXER_READ_CAPS:
+ rc = IOCTL_OUT(arg, 0);
+ break;
+ case SOUND_MIXER_READ_RECMASK:
+ data = 0;
+ rc = IOCTL_OUT(arg, data);
+ break;
+ case SOUND_MIXER_READ_RECSRC:
+ data = 0;
+ rc = IOCTL_OUT(arg, data);
+ break;
+ case SOUND_MIXER_WRITE_RECSRC:
+ IOCTL_IN(arg, data);
+ data =0;
+ rc = IOCTL_OUT(arg, data);
+ break;
+ case SOUND_MIXER_WRITE_SPEAKER: /* really bell volume */
+ IOCTL_IN(arg, data);
+ beep_vol = data & 0xff;
+ /* fall through */
+ case SOUND_MIXER_READ_SPEAKER:
+ rc = IOCTL_OUT(arg, (beep_vol<<8) | beep_vol);
+ break;
+ case SOUND_MIXER_OUTMASK:
+ case SOUND_MIXER_OUTSRC:
+ default:
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static void __init
+tas_init_frame_rates(unsigned int *prop, unsigned int l)
+{
+ int i ;
+ if (prop) {
+ for (i=0; i<1; i++)
+ tas_freqs_ok[i] = 0;
+ for (l /= sizeof(int); l > 0; --l) {
+ unsigned int r = *prop++;
+ /* Apple 'Fixed' format */
+ if (r >= 0x10000)
+ r >>= 16;
+ for (i = 0; i < 1; ++i) {
+ if (r == tas_freqs[i]) {
+ tas_freqs_ok[i] = 1;
+ break;
+ }
+ }
+ }
+ }
+ /* else we assume that all the rates are available */
+}
+
+
+/*** AE - TUMBLER / SNAPPER END ************************************************/
@@ -509,8 +625,10 @@
static int __init PMacIrqInit(void)
{
- if (request_irq(awacs_irq, pmac_awacs_intr, 0, "Built-in Sound misc", 0)
- || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "Built-in Sound out", 0)
+ if (awacs)
+ if (request_irq(awacs_irq, pmac_awacs_intr, 0, "Built-in Sound misc", 0))
+ return 0;
+ if (request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "Built-in Sound out", 0)
|| request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "Built-in Sound in", 0))
return 0;
return 1;
@@ -523,23 +641,28 @@
DBDMA_DO_STOP(awacs_txdma);
DBDMA_DO_STOP(awacs_rxdma);
- /* disable interrupts from awacs interface */
- out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff);
-
+ if (awacs)
+ /* disable interrupts from awacs interface */
+ out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff);
+
/* Switch off the sound clock */
pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 0);
/* Make sure proper bits are set on pismo & tipb */
- if (machine_is_compatible("PowerBook3,1") ||
- machine_is_compatible("PowerBook3,2")) {
+ if ((machine_is_compatible("PowerBook3,1") ||
+ machine_is_compatible("PowerBook3,2")) && awacs) {
awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
awacs_write(MASK_ADDR1 | awacs_reg[1]);
wait_ms(200);
}
- free_irq(awacs_irq, 0);
+ if (awacs)
+ free_irq(awacs_irq, 0);
free_irq(awacs_tx_irq, 0);
free_irq(awacs_rx_irq, 0);
- /* all OF versions I've seen use this value */
- iounmap((void *)awacs);
+
+ if (awacs)
+ iounmap((void *)awacs);
+ if (i2s)
+ iounmap((void *)i2s);
iounmap((void *)awacs_txdma);
iounmap((void *)awacs_rxdma);
@@ -555,7 +678,9 @@
kfree(beep_dbdma_cmd_space);
if (beep_buf) {
kfree(beep_buf);
+#ifdef CONFIG_VT
kd_mksound = orig_mksound;
+#endif
}
#ifdef CONFIG_PMAC_PBOOK
pmu_unregister_sleep_notifier(&awacs_sleep_notifier);
@@ -569,26 +694,16 @@
DBDMA_DO_STOP(awacs_txdma);
}
-static int tumbler_freqs[2] = { 48000, 44100 } ;
-static int tumbler_freqs_ok[2] = { 1, 1 } ;
-
-/* don't know what to do really - just have to leave it where
- * OF left things
-*/
-
-static int tumbler_set_frame_rate(void)
-{
- dmasound.hard.speed = 44100 ;
- awacs_rate_index = 0 ;
- return 44100 ;
-}
-
/* don't know what to do really - just have to leave it where
* OF left things
*/
static int daca_set_frame_rate(void)
{
+ if (i2s) {
+ out_le32(i2s + (I2S_REG_SERIAL_FORMAT >> 2), 0x41190000);
+ out_le32(i2s + (I2S_REG_DATAWORD_SIZES >> 2), 0x02000200);
+ }
dmasound.hard.speed = 44100 ;
awacs_rate_index = 0 ;
return 44100 ;
@@ -599,7 +714,8 @@
};
static int awacs_freqs_ok[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };
-static int awacs_set_frame_rate(int desired, int catch_r)
+static int
+awacs_set_frame_rate(int desired, int catch_r)
{
int tolerance, i = 8 ;
/*
@@ -623,13 +739,9 @@
return dmasound.hard.speed;
}
-static int burgundy_frame_rates = 1 ;
-static int burgundy_set_frame_rate(void)
+static int
+burgundy_set_frame_rate(void)
{
-#ifdef DEBUG_DMASOUND
-if (burgundy_frame_rates > 1)
- printk("dmasound_pmac: warning Burgundy had more than one frame rate\n");
-#endif
awacs_rate_index = 0 ;
awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) ;
/* XXX disable error interrupt on burgundy for now */
@@ -637,24 +749,24 @@
return 44100 ;
}
-static int set_frame_rate(int desired, int catch_r)
+static int
+set_frame_rate(int desired, int catch_r)
{
switch (awacs_revision) {
case AWACS_BURGUNDY:
- dmasound.hard.speed =
- burgundy_set_frame_rate();
+ dmasound.hard.speed = burgundy_set_frame_rate();
break ;
case AWACS_TUMBLER:
- dmasound.hard.speed =
- tumbler_set_frame_rate();
+ case AWACS_SNAPPER:
+ dmasound.hard.speed = tas_set_frame_rate();
break ;
case AWACS_DACA:
dmasound.hard.speed =
daca_set_frame_rate();
break ;
default:
- dmasound.hard.speed =
- awacs_set_frame_rate(desired, catch_r);
+ dmasound.hard.speed = awacs_set_frame_rate(desired,
+ catch_r);
break ;
}
return dmasound.hard.speed ;
@@ -704,11 +816,13 @@
dmasound.trans_write = &transAwacsExpand;
dmasound.trans_read = &transAwacsNormalRead;
- if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
- out_le32(&awacs->byteswap, BS_VAL);
- else
- out_le32(&awacs->byteswap, 0);
-
+ if (awacs) {
+ if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
+ out_le32(&awacs->byteswap, BS_VAL);
+ else
+ out_le32(&awacs->byteswap, 0);
+ }
+
expand_bal = -dmasound.soft.speed;
}
@@ -793,7 +907,20 @@
static int PMacSetVolume(int volume)
{
- return awacs_volume_setter(volume, 2, MASK_AMUTE, 6);
+ printk(KERN_WARNING "Bogus call to PMacSetVolume !\n");
+ return 0;
+}
+
+static void awacs_setup_for_beep(int speed)
+{
+ out_le32(&awacs->control,
+ (in_le32(&awacs->control) & ~0x1f00)
+ | ((speed > 0 ? speed : awacs_rate_index) << 8));
+
+ if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE) && speed == -1)
+ out_le32(&awacs->byteswap, BS_VAL);
+ else
+ out_le32(&awacs->byteswap, 0);
}
static void __PMacPlay(void)
@@ -816,15 +943,8 @@
out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
while ( (in_le32(&awacs_txdma->status) & RUN) && count--)
udelay(1);
- /* FIXME: check that this is OK for other chip sets */
- out_le32(&awacs->control,
- (in_le32(&awacs->control) & ~0x1f00)
- | (awacs_rate_index << 8));
-
- if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
- out_le32(&awacs->byteswap, BS_VAL);
- else
- out_le32(&awacs->byteswap, 0);
+ if (awacs)
+ awacs_setup_for_beep(-1);
out_le32(&awacs_txdma->cmdptr,
virt_to_bus(&(awacs_tx_cmds[next_frg])));
@@ -925,6 +1045,7 @@
{
int i = write_sq.front;
int stat;
+ int i_nowrap = write_sq.front;
volatile struct dbdma_cmd *cp;
/* != 0 when we are dealing with a DEAD xfer */
static int emergency_in_use = 0 ;
@@ -981,6 +1102,7 @@
emergency_in_use = 0 ; /* done that */
--write_sq.count;
--write_sq.active;
+ i_nowrap++;
if (++i >= write_sq.max_count)
i = 0;
}
@@ -993,7 +1115,7 @@
}
/* if we used some data up then wake the writer to supply some more*/
- if (i != write_sq.front)
+ if (i_nowrap != write_sq.front)
WAKE_UP(write_sq.action_queue);
write_sq.front = i;
@@ -1090,9 +1212,26 @@
pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs)
{
int ctrl = in_le32(&awacs->control);
+ int status = in_le32(&awacs->codec_stat);
+ int r1;
- if (ctrl & MASK_PORTCHG) {
- /* do something when headphone is plugged/unplugged? */
+ if (ctrl & MASK_PORTCHG) {
+ /* tested on Screamer, should work on others too */
+ if (awacs_revision == AWACS_SCREAMER) {
+ if (((status & MASK_HDPCONN) >> 3) && (hdp_connected == 0)) {
+ hdp_connected = 1;
+
+ r1 = awacs_reg[1] | MASK_SPKMUTE;
+ awacs_reg[1] = r1;
+ awacs_write(r1 | MASK_ADDR_MUTE);
+ } else if (((status & MASK_HDPCONN) >> 3 == 0) && (hdp_connected == 1)) {
+ hdp_connected = 0;
+
+ r1 = awacs_reg[1] & ~MASK_SPKMUTE;
+ awacs_reg[1] = r1;
+ awacs_write(r1 | MASK_ADDR_MUTE);
+ }
+ }
}
if (ctrl & MASK_CNTLERR) {
int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16;
@@ -1108,7 +1247,7 @@
awacs_write(int val)
{
int count = 300 ;
- if (awacs_revision >= AWACS_DACA)
+ if (awacs_revision >= AWACS_DACA || !awacs)
return ;
while ((in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) && count--)
@@ -1131,14 +1270,8 @@
out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
while ((in_le32(&awacs_txdma->status) & RUN) && count--)
udelay(1);
- /* FIXME: check this is OK for DACA, Tumbler */
- out_le32(&awacs->control,
- (in_le32(&awacs->control) & ~0x1f00)
- | (awacs_rate_index << 8));
- if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
- out_le32(&awacs->byteswap, BS_VAL);
- else
- out_le32(&awacs->byteswap, 0);
+ if (awacs)
+ awacs_setup_for_beep(-1);
beep_playing = 0;
}
restore_flags(flags);
@@ -1233,11 +1366,8 @@
out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
while ((in_le32(&awacs_txdma->status) & RUN) && count--)
udelay(1); /* timeout > 2 samples at lowest rate*/
- /* FIXME: check this is OK on DACA, Tumbler */
- out_le32(&awacs->control,
- (in_le32(&awacs->control) & ~0x1f00)
- | (beep_speed << 8));
- out_le32(&awacs->byteswap, 0); /* force BE */
+ if (awacs)
+ awacs_setup_for_beep(beep_speed);
out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
(void)in_le32(&awacs_txdma->status);
out_le32(&awacs_txdma->control, RUN | (RUN << 16));
@@ -1263,10 +1393,12 @@
awacs_write(awacs_reg[1] + MASK_ADDR1);
awacs_write(awacs_reg[7] + MASK_ADDR7);
}
- if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
- out_le32(&awacs->byteswap, BS_VAL);
- else
- out_le32(&awacs->byteswap, 0);
+ if (awacs) {
+ if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
+ out_le32(&awacs->byteswap, BS_VAL);
+ else
+ out_le32(&awacs->byteswap, 0);
+ }
}
#ifdef CONFIG_PMAC_PBOOK
@@ -1293,9 +1425,18 @@
/* stop rx - if going - a bit of a daft user... but */
out_le32(&awacs_rxdma->control, (RUN|WAKE|FLUSH << 16));
/* deny interrupts */
+ if (awacs)
+ disable_irq(awacs_irq);
+ disable_irq(awacs_tx_irq);
+ disable_irq(awacs_rx_irq);
+ /* Chip specific sleep code */
switch (awacs_revision) {
case AWACS_TUMBLER:
- tumbler_enter_sleep(); /* Stub for now */
+ case AWACS_SNAPPER:
+ write_audio_gpio(gpio_headphone_mute, gpio_headphone_mute_pol);
+ write_audio_gpio(gpio_amp_mute, gpio_amp_mute_pol);
+ tas_enter_sleep();
+ write_audio_gpio(gpio_audio_reset, gpio_audio_reset_pol);
break ;
case AWACS_DACA:
daca_enter_sleep();
@@ -1308,17 +1449,14 @@
out_le32(&awacs->control, 0x11) ;
break ;
}
- disable_irq(awacs_irq);
- disable_irq(awacs_tx_irq);
- disable_irq(awacs_rx_irq);
/* Disable sound clock */
pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 0);
/* According to Darwin, we do that after turning off the sound
* chip clock. All this will have to be cleaned up once we properly
* parse the OF sound-objects
*/
- if (machine_is_compatible("PowerBook3,1") ||
- machine_is_compatible("PowerBook3,2")) {
+ if ((machine_is_compatible("PowerBook3,1") ||
+ machine_is_compatible("PowerBook3,2")) && awacs) {
awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
awacs_write(MASK_ADDR1 | awacs_reg[1]);
wait_ms(200);
@@ -1327,8 +1465,8 @@
case PBOOK_WAKE:
/* Enable sound clock */
pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 1);
- if (machine_is_compatible("PowerBook3,1") ||
- machine_is_compatible("PowerBook3,2")) {
+ if ((machine_is_compatible("PowerBook3,1") ||
+ machine_is_compatible("PowerBook3,2")) && awacs) {
wait_ms(100);
awacs_reg[1] &= ~(MASK_PAROUT0 | MASK_PAROUT1);
awacs_write(MASK_ADDR1 | awacs_reg[1]);
@@ -1338,8 +1476,15 @@
/* restore settings */
switch (awacs_revision) {
case AWACS_TUMBLER:
+ case AWACS_SNAPPER:
+ write_audio_gpio(gpio_headphone_mute, gpio_headphone_mute_pol);
+ write_audio_gpio(gpio_amp_mute, gpio_amp_mute_pol);
+ write_audio_gpio(gpio_audio_reset, gpio_audio_reset_pol);
+ wait_ms(100);
+ write_audio_gpio(gpio_audio_reset, !gpio_audio_reset_pol);
+ wait_ms(150);
+ tas_leave_sleep(); /* Stub for now */
headphone_intr(0,0,0);
- tumbler_leave_sleep(); /* Stub for now */
break;
case AWACS_DACA:
wait_ms(10); /* Check this !!! */
@@ -1354,17 +1499,20 @@
break ;
}
/* Recalibrate chip */
- if (awacs_revision == AWACS_SCREAMER)
+ if (awacs_revision == AWACS_SCREAMER && awacs)
awacs_recalibrate();
/* Make sure dma is stopped */
PMacSilence();
- enable_irq(awacs_irq);
+ if (awacs)
+ enable_irq(awacs_irq);
enable_irq(awacs_tx_irq);
enable_irq(awacs_rx_irq);
- /* OK, allow ints back again */
- out_le32(&awacs->control, MASK_IEPC
- | (awacs_rate_index << 8) | 0x11
- | (awacs_revision < AWACS_DACA ? MASK_IEE: 0));
+ if (awacs) {
+ /* OK, allow ints back again */
+ out_le32(&awacs->control, MASK_IEPC
+ | (awacs_rate_index << 8) | 0x11
+ | (awacs_revision < AWACS_DACA ? MASK_IEE: 0));
+ }
if (macio_base && is_pbook_g3) {
/* FIXME: should restore the setup we had...*/
out_8(macio_base + 0x37, 3);
@@ -1952,7 +2100,7 @@
case SOUND_MIXER_READ_SPEAKER:
data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER);
data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8);
- rc = IOCTL_OUT(arg, ~data);
+ rc = IOCTL_OUT(arg, (~data) & 0x0000ffff);
break;
case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */
IOCTL_IN(arg, data);
@@ -2007,89 +2155,6 @@
return rc;
}
-static int tumbler_mixer_ioctl(u_int cmd, u_long arg)
-{
- int data;
- int rc;
-
- /* We are, we are, we are... Tumbler (and very dumb) */
- /* Ok, we're not THAT dumb anymore, but still pretty dumb :-) */
-
- switch(cmd) {
- case SOUND_MIXER_READ_DEVMASK:
- data = SOUND_MASK_VOLUME | SOUND_MASK_ALTPCM |
- SOUND_MASK_BASS | SOUND_MASK_TREBLE |
- SOUND_MASK_PCM;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_RECMASK:
- data = 0;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_RECSRC:
- data = 0;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_WRITE_RECSRC:
- IOCTL_IN(arg, data);
- data =0;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_STEREODEVS:
- data = SOUND_MASK_VOLUME | SOUND_MASK_PCM;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_CAPS:
- rc = IOCTL_OUT(arg, 0);
- break;
- case SOUND_MIXER_WRITE_BASS:
- IOCTL_IN(arg, data);
- tumbler_set_bass(data);
- /* Fall through */
- case SOUND_MIXER_READ_BASS:
- tumbler_get_bass(&data);
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_WRITE_TREBLE:
- IOCTL_IN(arg, data);
- tumbler_set_treble(data);
- /* Fall through */
- case SOUND_MIXER_READ_TREBLE:
- tumbler_get_treble(&data);
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_WRITE_PCM:
- IOCTL_IN(arg, data);
- tumbler_set_pcm_lvl(data);
- /* Fall through */
- case SOUND_MIXER_READ_PCM:
- tumbler_get_pcm_lvl(&data);
- IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_WRITE_VOLUME:
- IOCTL_IN(arg, data);
- tumbler_set_volume(data, data);
- /* Fall through */
- case SOUND_MIXER_READ_VOLUME:
- tumbler_get_volume(& data, &data);
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */
- IOCTL_IN(arg, data);
- beep_vol = data & 0xff;
- /* fall through */
- case SOUND_MIXER_READ_ALTPCM:
- rc = IOCTL_OUT(arg, beep_vol);
- break;
- case SOUND_MIXER_OUTMASK:
- case SOUND_MIXER_OUTSRC:
- default:
- rc = -EINVAL;
- }
-
- return rc;
-}
-
static int daca_mixer_ioctl(u_int cmd, u_long arg)
{
int data;
@@ -2154,7 +2219,8 @@
rc = daca_mixer_ioctl(cmd, arg);
break;
case AWACS_TUMBLER:
- rc = tumbler_mixer_ioctl(cmd, arg);
+ case AWACS_SNAPPER:
+ rc = tas_mixer_ioctl(cmd, arg);
break ;
default: /* ;-)) */
rc = awacs_mixer_ioctl(cmd, arg);
@@ -2171,7 +2237,9 @@
case AWACS_TUMBLER:
printk("AE-Init tumbler mixer\n");
break ;
-
+ case AWACS_SNAPPER:
+ printk("AE-Init snapper mixer\n");
+ break ;
case AWACS_DACA:
case AWACS_BURGUNDY:
break ; /* don't know yet */
@@ -2362,12 +2430,12 @@
len += sprintf(b,"44100 ") ;
break ;
case AWACS_TUMBLER:
- for (i=0; i<2; i++){
- if (tumbler_freqs_ok[i])
- len += sprintf(b+len,"%d ", tumbler_freqs[i]) ;
+ case AWACS_SNAPPER:
+ for (i=0; i<1; i++){
+ if (tas_freqs_ok[i])
+ len += sprintf(b+len,"%d ", tas_freqs[i]) ;
}
break ;
-
case AWACS_AWACS:
case AWACS_SCREAMER:
default:
@@ -2469,8 +2537,8 @@
code that looks for chip properties knows how to go about it.
*/
-static struct device_node
-__init *get_snd_io_node(void)
+static struct device_node* __init
+get_snd_io_node(void)
{
struct device_node *np = NULL;
@@ -2491,7 +2559,7 @@
* this seems to be what iBooks (& Tumbler) have.
*/
if (np == NULL)
- np = find_devices("i2s-a");
+ np = i2s_node = find_devices("i2s-a");
/* if we didn't find this - perhaps we are on an early model
* which _only_ has an 'awacs' node
@@ -2511,8 +2579,8 @@
we have to deduce the info other ways for these.
*/
-static struct device_node
-__init *get_snd_info_node(struct device_node *io)
+static struct device_node* __init
+get_snd_info_node(struct device_node *io)
{
struct device_node *info;
@@ -2526,8 +2594,8 @@
/* Find out what type of codec we have.
*/
-static int
-__init get_codec_type(struct device_node *info)
+static int __init
+get_codec_type(struct device_node *info)
{
/* already set if pre-davbus model and info will be NULL */
int codec = awacs_revision ;
@@ -2544,14 +2612,16 @@
codec = AWACS_DACA;
if (device_is_compatible(info, "tumbler"))
codec = AWACS_TUMBLER;
+ if (device_is_compatible(info, "snapper"))
+ codec = AWACS_SNAPPER;
}
return codec ;
}
/* find out what type, if any, of expansion card we have
*/
-static void
-__init get_expansion_type(void)
+static void __init
+get_expansion_type(void)
{
if (find_devices("perch") != NULL)
has_perch = 1;
@@ -2569,8 +2639,8 @@
* Set dmasound.mach.max_dsp_rate on the basis of these routines.
*/
-static void
-__init init_awacs_frame_rates(unsigned int *prop, unsigned int l)
+static void __init
+awacs_init_frame_rates(unsigned int *prop, unsigned int l)
{
int i ;
if (prop) {
@@ -2592,31 +2662,8 @@
/* else we assume that all the rates are available */
}
-static void
-__init init_tumbler_frame_rates(unsigned int *prop, unsigned int l)
-{
- int i ;
- if (prop) {
- for (i=0; i<2; i++)
- tumbler_freqs_ok[i] = 0;
- for (l /= sizeof(int); l > 0; --l) {
- unsigned int r = *prop++;
- /* Apple 'Fixed' format */
- if (r >= 0x10000)
- r >>= 16;
- for (i = 0; i < 2; ++i) {
- if (r == tumbler_freqs[i]) {
- tumbler_freqs_ok[i] = 1;
- break;
- }
- }
- }
- }
- /* else we assume that all the rates are available */
-}
-
-static void
-__init init_burgundy_frame_rates(unsigned int *prop, unsigned int l)
+static void __init
+burgundy_init_frame_rates(unsigned int *prop, unsigned int l)
{
int temp[9] ;
int i = 0 ;
@@ -2641,8 +2688,8 @@
#endif
}
-static void
-__init init_daca_frame_rates(unsigned int *prop, unsigned int l)
+static void __init
+daca_init_frame_rates(unsigned int *prop, unsigned int l)
{
int temp[9] ;
int i = 0 ;
@@ -2668,21 +2715,22 @@
#endif
}
-static void
-__init init_frame_rates(unsigned int *prop, unsigned int l)
+static void __init
+init_frame_rates(unsigned int *prop, unsigned int l)
{
- switch (awacs_revision){
+ switch (awacs_revision) {
case AWACS_TUMBLER:
- init_tumbler_frame_rates(prop, l);
+ case AWACS_SNAPPER:
+ tas_init_frame_rates(prop, l);
break ;
case AWACS_DACA:
- init_daca_frame_rates(prop, l);
+ daca_init_frame_rates(prop, l);
break ;
case AWACS_BURGUNDY:
- init_burgundy_frame_rates(prop, l);
+ burgundy_init_frame_rates(prop, l);
break ;
- default: /* ;-))) */
- init_awacs_frame_rates(prop, l);
+ default:
+ awacs_init_frame_rates(prop, l);
break ;
}
}
@@ -2690,11 +2738,11 @@
/* find things/machines that can't do mac-io byteswap
*/
-static void
-__init set_hw_byteswap(struct device_node *io)
+static void __init
+set_hw_byteswap(struct device_node *io)
{
struct device_node *mio ;
- unsigned int *p, kl = 0 ;
+ unsigned int kl = 0 ;
/* if seems that Keylargo can't byte-swap */
@@ -2741,9 +2789,11 @@
if( beep_dbdma_cmd_space ) kfree(beep_dbdma_cmd_space) ;
return -ENOMEM ;
}
+#ifdef CONFIG_VT
/* OK, we should be safe to claim the mksound vector now */
orig_mksound = kd_mksound;
kd_mksound = awacs_mksound;
+#endif
return 0 ;
}
@@ -2839,23 +2889,26 @@
}
/* all OF versions I've seen use this value */
- awacs = (volatile struct awacs_regs *)
- ioremap(io->addrs[0].address, 0x1000);
+ if (i2s_node)
+ i2s = (u32 *)ioremap(io->addrs[0].address, 0x1000);
+ else
+ awacs = (volatile struct awacs_regs *)
+ ioremap(io->addrs[0].address, 0x1000);
awacs_txdma = (volatile struct dbdma_regs *)
ioremap(io->addrs[1].address, 0x100);
awacs_rxdma = (volatile struct dbdma_regs *)
ioremap(io->addrs[2].address, 0x100);
-#ifdef CONFIG_PMAC_PBOOK
/* first of all make sure that the chip is powered up....*/
pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, io, 0, 1);
- if (awacs_revision == AWACS_SCREAMER)
+ if (awacs_revision == AWACS_SCREAMER && awacs)
awacs_recalibrate();
-#endif
+
awacs_irq = io->intrs[0].line;
awacs_tx_irq = io->intrs[1].line;
awacs_rx_irq = io->intrs[2].line;
+ /* Hack for legacy crap that will be killed someday */
awacs_node = io;
/* if we have an awacs or screamer - probe the chip to make
@@ -2906,8 +2959,9 @@
/* if it's there use it to set up frame rates */
init_frame_rates(prop, l) ;
}
-
- out_le32(&awacs->control, 0x11); /* set everything quiesent */
+
+ if (awacs)
+ out_le32(&awacs->control, 0x11); /* set everything quiesent */
set_hw_byteswap(io) ; /* figure out if the h/w can do it */
@@ -2942,9 +2996,20 @@
#ifdef CONFIG_KMOD
request_module("i2c-keywest");
#endif /* CONFIG_KMOD */
- awacs_tumbler_init();
- tas_init();
+ tas_register_driver(&tas3001c_hooks);
+ tas_init(I2C_DRIVERID_TAS3001C, I2C_DRIVERNAME_TAS3001C);
+ tas_dmasound_init();
+ tas_post_init();
break ;
+ case AWACS_SNAPPER:
+#ifdef CONFIG_KMOD
+ request_module("i2c-keywest");
+#endif /* CONFIG_KMOD */
+ tas_register_driver(&tas3004_hooks);
+ tas_init(I2C_DRIVERID_TAS3004,I2C_DRIVERNAME_TAS3004);
+ tas_dmasound_init();
+ tas_post_init();
+ break;
case AWACS_DACA:
#ifdef CONFIG_KMOD
request_module("i2c-keywest");
@@ -3028,11 +3093,15 @@
dmasound.mach.hardware_afmts = AFMT_S16_BE ;
/* shut out chips that do output only.
- may need to extend this to machines which have no inputs - even tho'
- they use screamer - IIRC one of the powerbooks is like this.
- */
+ * may need to extend this to machines which have no inputs - even tho'
+ * they use screamer - IIRC one of the powerbooks is like this.
+ *
+ * FIXME: Actually, some TUMBLER and SNAPPER do have inputs...
+ */
- if (awacs_revision != AWACS_TUMBLER && awacs_revision != AWACS_DACA) {
+ if (awacs_revision != AWACS_TUMBLER &&
+ awacs_revision != AWACS_SNAPPER &&
+ awacs_revision != AWACS_DACA) {
dmasound.mach.capabilities = DSP_CAP_DUPLEX ;
dmasound.mach.record = PMacRecord ;
}
@@ -3050,6 +3119,9 @@
case AWACS_TUMBLER:
sprintf(awacs_name, "PowerMac Tumbler ") ;
break ;
+ case AWACS_SNAPPER:
+ sprintf(awacs_name, "PowerMac Snapper ") ;
+ break ;
case AWACS_SCREAMER:
sprintf(awacs_name, "PowerMac Screamer ") ;
break ;
@@ -3066,7 +3138,8 @@
{
switch (awacs_revision) {
case AWACS_TUMBLER:
- awacs_tumbler_cleanup();
+ case AWACS_SNAPPER:
+ tas_dmasound_cleanup();
tas_cleanup();
break ;
case AWACS_DACA:
@@ -3081,3 +3154,10 @@
module_init(dmasound_awacs_init);
module_exit(dmasound_awacs_cleanup);
+/*
+ * Local Variables:
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ * End:
+ */
diff -uNr linux-2.4.22/drivers/sound/dmasound/tas3001c.c linux-2.4.22-ben2/drivers/sound/dmasound/tas3001c.c
--- linux-2.4.22/drivers/sound/dmasound/tas3001c.c 2002-02-25 20:38:05.000000000 +0100
+++ linux-2.4.22-ben2/drivers/sound/dmasound/tas3001c.c 2003-08-31 11:38:51.000000000 +0200
@@ -1,38 +1,17 @@
/*
- * Driver for the i2c/i2s based TA3001C sound chip used
- * on some Apple hardware. Also known as "tumbler".
+ * Driver for the i2c/i2s based TA3004 sound chip used
+ * on some Apple hardware. Also known as "snapper".
*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- *
- * Modified by Christopher C. Chimelis :
+ * Tobias Sargeant
+ * Based upon, tas3001c.c by Christopher C. Chimelis :
*
* TODO:
* -----
- * * Enable DRC since the TiBook speakers are less than good
* * Enable control over input line 2 (is this connected?)
- * * Play with the dual six-stage cascading biquad filtering to see how
- * we can use it to our advantage (currently not implemented)
- * * Reorganise driver a bit to make it cleaner and easier to work with
- * (read: use the header file more :-P)
- * * Implement sleep support
- *
- * Version 0.4:
- * ------------
- * * Balance control finally works (can someone document OSS better please?)
- * * Moved to a struct for common values referenced in the driver
- * * Put stubs in for sleep/wake-up support for now. This will take some
- * experimentation to make sure that the timing is right, since the
- * TAS hardware requires specific timing while enabling low-power mode.
- * I may cheat for now and just reset the chip on wake-up, but I'd rather
- * not if I don't have to.
- *
- * Version 0.3:
- * ------------
- * * Fixed volume control
- * * Added bass and treble control
- * * Added PCM line level control (mixer 1 in the TAS manual)
+ * * Implement sleep support (at least mute everything and
+ * * set gains to minimum during sleep)
+ * * Look into some of Darwin's tweaks regarding the mute
+ * * lines (delays & different behaviour on some HW)
*
*/
@@ -45,403 +24,854 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include "dmasound.h"
+#include "tas_common.h"
#include "tas3001c.h"
-#define I2C_DRIVERID_TAS (0xFEBA)
+#include "tas_ioctl.h"
-#define TAS_VERSION "0.3"
-#define TAS_DATE "20011214"
+#define TAS3001C_BIQUAD_FILTER_COUNT 6
+#define TAS3001C_BIQUAD_CHANNEL_COUNT 2
-#define TAS_SETTING_MAX 100
+#define VOL_DEFAULT (100 * 4 / 5)
+#define INPUT_DEFAULT (100 * 4 / 5)
+#define BASS_DEFAULT (100 / 2)
+#define TREBLE_DEFAULT (100 / 2)
+
+struct tas3001c_data_t {
+ struct tas_data_t super;
+ int device_id;
+ int output_id;
+ int speaker_id;
+ struct tas_drce_t drce_state;
+};
-#define VOL_DEFAULT (((((TAS_SETTING_MAX*4)/5)<<0)<<8) | (((TAS_SETTING_MAX*4)/5)<<0))
-#define INPUT_DEFAULT (((TAS_SETTING_MAX*4)/5)<<0)
-#define BASS_DEFAULT ((TAS_SETTING_MAX/2)<<0)
-#define TREBLE_DEFAULT ((TAS_SETTING_MAX/2)<<0)
-static struct i2c_client * tumbler_client = NULL;
+static const union tas_biquad_t
+tas3001c_eq_unity={
+ buf: { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 }
+};
-int tumbler_enter_sleep(void);
-int tumbler_leave_sleep(void);
-static int tas_attach_adapter(struct i2c_adapter *adapter);
-static int tas_detect_client(struct i2c_adapter *adapter, int address);
-static int tas_detach_client(struct i2c_client *client);
+static inline unsigned char db_to_regval(short db) {
+ int r=0;
-/* Unique ID allocation */
-static int tas_id;
-static int tas_initialized;
+ r=(db+0x59a0) / 0x60;
-static struct device_node* tas_node;
-static u8 tas_i2c_address = 0x34;
+ if (r < 0x91) return 0x91;
+ if (r > 0xef) return 0xef;
+ return r;
+}
-struct tas_data_t {
- uint left_vol; /* left volume */
- uint right_vol; /* right volume */
- uint treble; /* treble */
- uint bass; /* bass */
- uint pcm_level; /* pcm level */
-};
+static inline short quantize_db(short db) {
+ return db_to_regval(db) * 0x60 - 0x59a0;
+}
-struct i2c_driver tas_driver = {
- name: "TAS3001C driver V 0.3",
- id: I2C_DRIVERID_TAS,
- flags: I2C_DF_NOTIFY,
- attach_adapter: &tas_attach_adapter,
- detach_client: &tas_detach_client,
- command: NULL,
- inc_use: NULL, /* &tas_inc_use, */
- dec_use: NULL /* &tas_dev_use */
-};
-int
-tumbler_get_volume(uint * left_vol, uint *right_vol)
+static inline int
+register_width(enum tas3001c_reg_t r)
{
- struct tas_data_t *data;
+ switch(r) {
+ case TAS3001C_REG_MCR:
+ case TAS3001C_REG_TREBLE:
+ case TAS3001C_REG_BASS:
+ return 1;
- if (!tumbler_client)
- return -1;
+ case TAS3001C_REG_DRC:
+ return 2;
- data = (struct tas_data_t *) (tumbler_client->data);
- *left_vol = data->left_vol;
- *right_vol = data->right_vol;
-
- return 0;
+ case TAS3001C_REG_MIXER1:
+ case TAS3001C_REG_MIXER2:
+ return 3;
+
+ case TAS3001C_REG_VOLUME:
+ return 6;
+
+ case TAS3001C_REG_LEFT_BIQUAD0:
+ case TAS3001C_REG_LEFT_BIQUAD1:
+ case TAS3001C_REG_LEFT_BIQUAD2:
+ case TAS3001C_REG_LEFT_BIQUAD3:
+ case TAS3001C_REG_LEFT_BIQUAD4:
+ case TAS3001C_REG_LEFT_BIQUAD5:
+ case TAS3001C_REG_LEFT_BIQUAD6:
+
+ case TAS3001C_REG_RIGHT_BIQUAD0:
+ case TAS3001C_REG_RIGHT_BIQUAD1:
+ case TAS3001C_REG_RIGHT_BIQUAD2:
+ case TAS3001C_REG_RIGHT_BIQUAD3:
+ case TAS3001C_REG_RIGHT_BIQUAD4:
+ case TAS3001C_REG_RIGHT_BIQUAD5:
+ case TAS3001C_REG_RIGHT_BIQUAD6:
+ return 15;
+
+ default:
+ return 0;
+ }
}
-int
-tumbler_set_register(uint reg, uint size, char *block)
-{
- if (i2c_smbus_write_block_data(tumbler_client, reg, size, block) < 0) {
- printk("tas3001c: I2C write failed \n");
- return -1;
+static int
+tas3001c_write_register( struct tas3001c_data_t *self,
+ enum tas3001c_reg_t reg_num,
+ char *data,
+ uint write_mode)
+{
+ if (reg_num==TAS3001C_REG_MCR ||
+ reg_num==TAS3001C_REG_BASS ||
+ reg_num==TAS3001C_REG_TREBLE) {
+ return tas_write_byte_register(&self->super,
+ (uint)reg_num,
+ *data,
+ write_mode);
+ } else {
+ return tas_write_register(&self->super,
+ (uint)reg_num,
+ register_width(reg_num),
+ data,
+ write_mode);
}
- return 0;
}
-int
-tumbler_get_pcm_lvl(uint *pcm_lvl)
+static int
+tas3001c_sync_register( struct tas3001c_data_t *self,
+ enum tas3001c_reg_t reg_num)
{
- struct tas_data_t *data;
-
- if (!tumbler_client)
- return -1;
+ if (reg_num==TAS3001C_REG_MCR ||
+ reg_num==TAS3001C_REG_BASS ||
+ reg_num==TAS3001C_REG_TREBLE) {
+ return tas_sync_byte_register(&self->super,
+ (uint)reg_num,
+ register_width(reg_num));
+ } else {
+ return tas_sync_register(&self->super,
+ (uint)reg_num,
+ register_width(reg_num));
+ }
+}
- data = (struct tas_data_t *) (tumbler_client->data);
- *pcm_lvl = data->pcm_level;
+static int
+tas3001c_read_register( struct tas3001c_data_t *self,
+ enum tas3001c_reg_t reg_num,
+ char *data,
+ uint write_mode)
+{
+ return tas_read_register(&self->super,
+ (uint)reg_num,
+ register_width(reg_num),
+ data);
+}
+
+static inline int
+tas3001c_fast_load(struct tas3001c_data_t *self, int fast)
+{
+ if (fast)
+ self->super.shadow[TAS3001C_REG_MCR][0] |= 0x80;
+ else
+ self->super.shadow[TAS3001C_REG_MCR][0] &= 0x7f;
+ return tas3001c_sync_register(self,TAS3001C_REG_MCR);
+}
+
+static uint
+tas3001c_supported_mixers(struct tas3001c_data_t *self)
+{
+ return SOUND_MASK_VOLUME |
+ SOUND_MASK_PCM |
+ SOUND_MASK_ALTPCM |
+ SOUND_MASK_TREBLE |
+ SOUND_MASK_BASS;
+}
- return 0;
+static int
+tas3001c_mixer_is_stereo(struct tas3001c_data_t *self,int mixer)
+{
+ switch(mixer) {
+ case SOUND_MIXER_VOLUME:
+ return 1;
+ default:
+ return 0;
+ }
}
-int
-tumbler_get_treble(uint *treble)
+static uint
+tas3001c_stereo_mixers(struct tas3001c_data_t *self)
{
- struct tas_data_t *data;
+ uint r=tas3001c_supported_mixers(self);
+ uint i;
+
+ for (i=1; idata);
- *treble = data->treble;
+
+ *level=self->super.mixer[mixer];
return 0;
}
-int
-tumbler_get_bass(uint *bass)
+static int
+tas3001c_set_mixer_level(struct tas3001c_data_t *self,int mixer,uint level)
{
- struct tas_data_t *data;
-
- if (!tumbler_client)
- return -1;
+ int rc;
+ tas_shadow_t *shadow;
- data = (struct tas_data_t *) (tumbler_client->data);
- *bass = data->bass;
+ uint temp;
+ uint offset=0;
+ if (!self)
+ return -1;
+
+ shadow=self->super.shadow;
+
+ if (!tas3001c_mixer_is_stereo(self,mixer))
+ level = tas_mono_to_stereo(level);
+
+ switch(mixer) {
+ case SOUND_MIXER_VOLUME:
+ temp = tas3001c_gain.master[level&0xff];
+ shadow[TAS3001C_REG_VOLUME][0] = (temp >> 16) & 0xff;
+ shadow[TAS3001C_REG_VOLUME][1] = (temp >> 8) & 0xff;
+ shadow[TAS3001C_REG_VOLUME][2] = (temp >> 0) & 0xff;
+ temp = tas3001c_gain.master[(level>>8)&0xff];
+ shadow[TAS3001C_REG_VOLUME][3] = (temp >> 16) & 0xff;
+ shadow[TAS3001C_REG_VOLUME][4] = (temp >> 8) & 0xff;
+ shadow[TAS3001C_REG_VOLUME][5] = (temp >> 0) & 0xff;
+ rc = tas3001c_sync_register(self,TAS3001C_REG_VOLUME);
+ break;
+ case SOUND_MIXER_ALTPCM:
+ /* tas3001c_fast_load(self, 1); */
+ level = tas_mono_to_stereo(level);
+ temp = tas3001c_gain.mixer[level&0xff];
+ shadow[TAS3001C_REG_MIXER2][offset+0] = (temp >> 16) & 0xff;
+ shadow[TAS3001C_REG_MIXER2][offset+1] = (temp >> 8) & 0xff;
+ shadow[TAS3001C_REG_MIXER2][offset+2] = (temp >> 0) & 0xff;
+ rc = tas3001c_sync_register(self,TAS3001C_REG_MIXER2);
+ /* tas3001c_fast_load(self, 0); */
+ break;
+ case SOUND_MIXER_PCM:
+ /* tas3001c_fast_load(self, 1); */
+ level = tas_mono_to_stereo(level);
+ temp = tas3001c_gain.mixer[level&0xff];
+ shadow[TAS3001C_REG_MIXER1][offset+0] = (temp >> 16) & 0xff;
+ shadow[TAS3001C_REG_MIXER1][offset+1] = (temp >> 8) & 0xff;
+ shadow[TAS3001C_REG_MIXER1][offset+2] = (temp >> 0) & 0xff;
+ rc = tas3001c_sync_register(self,TAS3001C_REG_MIXER1);
+ /* tas3001c_fast_load(self, 0); */
+ break;
+ case SOUND_MIXER_TREBLE:
+ temp = tas3001c_gain.treble[level&0xff];
+ shadow[TAS3001C_REG_TREBLE][0]=temp&0xff;
+ rc = tas3001c_sync_register(self,TAS3001C_REG_TREBLE);
+ break;
+ case SOUND_MIXER_BASS:
+ temp = tas3001c_gain.bass[level&0xff];
+ shadow[TAS3001C_REG_BASS][0]=temp&0xff;
+ rc = tas3001c_sync_register(self,TAS3001C_REG_BASS);
+ break;
+ default:
+ rc = -1;
+ break;
+ }
+ if (rc < 0)
+ return rc;
+ self->super.mixer[mixer]=level;
return 0;
}
-int
-tumbler_set_bass(uint bass)
+static int
+tas3001c_leave_sleep(struct tas3001c_data_t *self)
{
- uint cur_bass_pers = bass;
- char block;
- struct tas_data_t *data;
+ unsigned char mcr = (1<<6)+(2<<4)+(2<<2);
- if (!tumbler_client)
+ if (!self)
return -1;
- data = (struct tas_data_t *) (tumbler_client->data);
+ /* Make sure something answers on the i2c bus */
+ if (tas3001c_write_register(self, TAS3001C_REG_MCR, &mcr,
+ WRITE_NORMAL|FORCE_WRITE) < 0)
+ return -1;
+
+ tas3001c_fast_load(self, 1);
+
+ (void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD0);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD1);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD2);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD3);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD4);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD5);
+
+ (void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD0);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD1);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD2);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD3);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD4);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD5);
- bass &= 0xff;
- if (bass > TAS_SETTING_MAX)
- bass = TAS_SETTING_MAX;
- bass = ((bass * 72) / TAS_SETTING_MAX) << 0;
- bass = tas_bass_table[bass];
- block = (bass >> 0) & 0xff;
+ tas3001c_fast_load(self, 0);
- if (tumbler_set_register(TAS_SET_BASS, &block) < 0) {
- printk("tas3001c: failed to set bass \n");
+ (void)tas3001c_sync_register(self,TAS3001C_REG_BASS);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_TREBLE);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_MIXER1);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_MIXER2);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_VOLUME);
+
+ return 0;
+}
+
+static int
+tas3001c_enter_sleep(struct tas3001c_data_t *self)
+{
+ /* Stub for now, but I have the details on low-power mode */
+ if (!self)
return -1;
- }
- data->bass = cur_bass_pers;
return 0;
}
-int
-tumbler_set_treble(uint treble)
+static int
+tas3001c_sync_biquad( struct tas3001c_data_t *self,
+ u_int channel,
+ u_int filter)
{
- uint cur_treble_pers = treble;
- char block;
- struct tas_data_t *data;
+ enum tas3001c_reg_t reg;
- if (!tumbler_client)
- return -1;
+ if (channel >= TAS3001C_BIQUAD_CHANNEL_COUNT ||
+ filter >= TAS3001C_BIQUAD_FILTER_COUNT) return -EINVAL;
- data = (struct tas_data_t *) (tumbler_client->data);
+ reg=( channel ? TAS3001C_REG_RIGHT_BIQUAD0 : TAS3001C_REG_LEFT_BIQUAD0 ) + filter;
- treble &= 0xff;
- if (treble > TAS_SETTING_MAX)
- treble = TAS_SETTING_MAX;
- treble = ((treble * 72) / TAS_SETTING_MAX) << 0;
- treble = tas_treble_table[treble];
- block = (treble >> 0) & 0xff;
+ return tas3001c_sync_register(self,reg);
+}
+
+static int
+tas3001c_write_biquad_shadow( struct tas3001c_data_t *self,
+ u_int channel,
+ u_int filter,
+ const union tas_biquad_t *biquad)
+{
+ tas_shadow_t *shadow=self->super.shadow;
+ enum tas3001c_reg_t reg;
+
+ if (channel >= TAS3001C_BIQUAD_CHANNEL_COUNT ||
+ filter >= TAS3001C_BIQUAD_FILTER_COUNT) return -EINVAL;
+
+ reg=( channel ? TAS3001C_REG_RIGHT_BIQUAD0 : TAS3001C_REG_LEFT_BIQUAD0 ) + filter;
+
+ SET_4_20(shadow[reg], 0,biquad->coeff.b0);
+ SET_4_20(shadow[reg], 3,biquad->coeff.b1);
+ SET_4_20(shadow[reg], 6,biquad->coeff.b2);
+ SET_4_20(shadow[reg], 9,biquad->coeff.a1);
+ SET_4_20(shadow[reg],12,biquad->coeff.a2);
- if (tumbler_set_register(TAS_SET_TREBLE, &block) < 0) {
- printk("tas3001c: failed to set treble \n");
- return -1;
- }
- data->treble = cur_treble_pers;
return 0;
}
-int
-tumbler_set_pcm_lvl(uint pcm_lvl)
+static int
+tas3001c_write_biquad( struct tas3001c_data_t *self,
+ u_int channel,
+ u_int filter,
+ const union tas_biquad_t *biquad)
{
- uint pcm_lvl_pers = pcm_lvl;
- unsigned char block[3];
- struct tas_data_t *data;
+ int rc;
- if (!tumbler_client)
- return -1;
+ rc=tas3001c_write_biquad_shadow(self, channel, filter, biquad);
+ if (rc < 0) return rc;
- data = (struct tas_data_t *) (tumbler_client->data);
+ return tas3001c_sync_biquad(self, channel, filter);
+}
- pcm_lvl &= 0xff;
- if (pcm_lvl > TAS_SETTING_MAX)
- pcm_lvl = TAS_SETTING_MAX;
- pcm_lvl = ((pcm_lvl * 176) / TAS_SETTING_MAX) << 0;
+static int
+tas3001c_write_biquad_list( struct tas3001c_data_t *self,
+ u_int filter_count,
+ u_int flags,
+ struct tas_biquad_ctrl_t *biquads)
+{
+ int i;
+ int rc;
- pcm_lvl = tas_input_table[pcm_lvl];
+ if (flags & TAS_BIQUAD_FAST_LOAD) tas3001c_fast_load(self,1);
- block[0] = (pcm_lvl >> 16) & 0xff;
- block[1] = (pcm_lvl >> 8) & 0xff;
- block[2] = (pcm_lvl >> 0) & 0xff;
+ for (i=0; ipcm_level = pcm_lvl_pers;
- return 0;
+ return rc;
}
-int
-tumbler_set_volume(uint left_vol, uint right_vol)
+static int
+tas3001c_read_biquad( struct tas3001c_data_t *self,
+ u_int channel,
+ u_int filter,
+ union tas_biquad_t *biquad)
+{
+ tas_shadow_t *shadow=self->super.shadow;
+ enum tas3001c_reg_t reg;
+
+ if (channel >= TAS3001C_BIQUAD_CHANNEL_COUNT ||
+ filter >= TAS3001C_BIQUAD_FILTER_COUNT) return -EINVAL;
+
+ reg=( channel ? TAS3001C_REG_RIGHT_BIQUAD0 : TAS3001C_REG_LEFT_BIQUAD0 ) + filter;
+
+ biquad->coeff.b0=GET_4_20(shadow[reg], 0);
+ biquad->coeff.b1=GET_4_20(shadow[reg], 3);
+ biquad->coeff.b2=GET_4_20(shadow[reg], 6);
+ biquad->coeff.a1=GET_4_20(shadow[reg], 9);
+ biquad->coeff.a2=GET_4_20(shadow[reg],12);
+
+ return 0;
+}
+
+static int
+tas3001c_eq_rw( struct tas3001c_data_t *self,
+ u_int cmd,
+ u_long arg)
{
- uint left_vol_pers = left_vol;
- uint right_vol_pers = right_vol;
- unsigned char block[6];
- struct tas_data_t *data;
+ int rc;
+ struct tas_biquad_ctrl_t biquad;
- if (!tumbler_client)
- return -1;
+ if (copy_from_user((void *)&biquad, (const void *)arg, sizeof(struct tas_biquad_ctrl_t))) {
+ return -EFAULT;
+ }
- data = (struct tas_data_t *) (tumbler_client->data);
+ if (cmd & SIOC_IN) {
+ rc=tas3001c_write_biquad(self, biquad.channel, biquad.filter, &biquad.data);
+ if (rc != 0) return rc;
+ }
- left_vol &= 0xff;
- if (left_vol > TAS_SETTING_MAX)
- left_vol = TAS_SETTING_MAX;
+ if (cmd & SIOC_OUT) {
+ rc=tas3001c_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
+ if (rc != 0) return rc;
+
+ if (copy_to_user((void *)arg, (const void *)&biquad, sizeof(struct tas_biquad_ctrl_t))) {
+ return -EFAULT;
+ }
- right_vol = (right_vol >> 8) & 0xff;
- if (right_vol > TAS_SETTING_MAX)
- right_vol = TAS_SETTING_MAX;
+ }
+ return 0;
+}
- left_vol = ((left_vol * 176) / TAS_SETTING_MAX) << 0;
- right_vol = ((right_vol * 176) / TAS_SETTING_MAX) << 0;
+static int
+tas3001c_eq_list_rw( struct tas3001c_data_t *self,
+ u_int cmd,
+ u_long arg)
+{
+ int rc;
+ int filter_count;
+ int flags;
+ int i,j;
+ char sync_required[2][6];
+ struct tas_biquad_ctrl_t biquad;
+
+ memset(sync_required,0,sizeof(sync_required));
+
+ if (copy_from_user((void *)&filter_count,
+ (const void *)arg + offsetof(struct tas_biquad_ctrl_list_t,filter_count),
+ sizeof(int))) {
+ return -EFAULT;
+ }
- left_vol = tas_volume_table[left_vol];
- right_vol = tas_volume_table[right_vol];
+ if (copy_from_user((void *)&flags,
+ (const void *)arg + offsetof(struct tas_biquad_ctrl_list_t,flags),
+ sizeof(int))) {
+ return -EFAULT;
+ }
- block[0] = (left_vol >> 16) & 0xff;
- block[1] = (left_vol >> 8) & 0xff;
- block[2] = (left_vol >> 0) & 0xff;
+ if (cmd & SIOC_IN) {
+ }
- block[3] = (right_vol >> 16) & 0xff;
- block[4] = (right_vol >> 8) & 0xff;
- block[5] = (right_vol >> 0) & 0xff;
+ for (i=0; i < filter_count; i++) {
+ if (copy_from_user((void *)&biquad,
+ (const void *)arg + offsetof(struct tas_biquad_ctrl_list_t, biquads[i]),
+ sizeof(struct tas_biquad_ctrl_t))) {
+ return -EFAULT;
+ }
+
+ if (cmd & SIOC_IN) {
+ sync_required[biquad.channel][biquad.filter]=1;
+ rc=tas3001c_write_biquad_shadow(self, biquad.channel, biquad.filter, &biquad.data);
+ if (rc != 0) return rc;
+ }
+
+ if (cmd & SIOC_OUT) {
+ rc=tas3001c_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
+ if (rc != 0) return rc;
+
+ if (copy_to_user((void *)arg + offsetof(struct tas_biquad_ctrl_list_t, biquads[i]),
+ (const void *)&biquad,
+ sizeof(struct tas_biquad_ctrl_t))) {
+ return -EFAULT;
+ }
+ }
+ }
- if (tumbler_set_register(TAS_SET_VOLUME, block) < 0) {
- printk("tas3001c: failed to set volume \n");
- return -1;
+ if (cmd & SIOC_IN) {
+ if (flags & TAS_BIQUAD_FAST_LOAD) tas3001c_fast_load(self,1);
+ for (i=0; i<2; i++) {
+ for (j=0; j<6; j++) {
+ if (sync_required[i][j]) {
+ rc=tas3001c_sync_biquad(self, i, j);
+ if (rc < 0) return rc;
+ }
+ }
+ }
+ if (flags & TAS_BIQUAD_FAST_LOAD) {
+ tas3001c_fast_load(self,0);
+ /* now we need to set up the mixers again,
+ because leaving fast mode resets them. */
+ (void)tas3001c_sync_register(self,TAS3001C_REG_BASS);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_TREBLE);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_MIXER1);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_MIXER2);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_VOLUME);
+ }
}
- data->left_vol = left_vol_pers;
- data->right_vol = right_vol_pers;
return 0;
}
-int
-tumbler_leave_sleep(void)
-{
- /* Stub for now, but I have the details on low-power mode */
- if (!tumbler_client)
- return -1;
+static int
+tas3001c_update_drce( struct tas3001c_data_t *self,
+ int flags,
+ struct tas_drce_t *drce)
+{
+ tas_shadow_t *shadow;
+ shadow=self->super.shadow;
+
+ shadow[TAS3001C_REG_DRC][1] = 0xc1;
+
+ if (flags & TAS_DRCE_THRESHOLD) {
+ self->drce_state.threshold=quantize_db(drce->threshold);
+ shadow[TAS3001C_REG_DRC][2] = db_to_regval(self->drce_state.threshold);
+ }
- return 0;
+ if (flags & TAS_DRCE_ENABLE) {
+ self->drce_state.enable = drce->enable;
+ }
+
+ if (!self->drce_state.enable) {
+ shadow[TAS3001C_REG_DRC][0] = 0xf0;
+ }
+
+#ifdef DEBUG_DRCE
+ printk("DRCE IOCTL: set [ ENABLE:%x THRESH:%x\n",
+ self->drce_state.enable,
+ self->drce_state.threshold);
+
+ printk("DRCE IOCTL: reg [ %02x %02x ]\n",
+ (unsigned char)shadow[TAS3001C_REG_DRC][0],
+ (unsigned char)shadow[TAS3001C_REG_DRC][1]);
+#endif
+
+ return tas3001c_sync_register(self, TAS3001C_REG_DRC);
}
-int
-tumbler_enter_sleep(void)
+static int
+tas3001c_drce_rw( struct tas3001c_data_t *self,
+ u_int cmd,
+ u_long arg)
{
- /* Stub for now, but I have the details on low-power mode */
- if (!tumbler_client)
- return -1;
+ int rc;
+ struct tas_drce_ctrl_t drce_ctrl;
+
+ if (copy_from_user((void *)&drce_ctrl,
+ (const void *)arg,
+ sizeof(struct tas_drce_ctrl_t))) {
+ return -EFAULT;
+ }
+
+#ifdef DEBUG_DRCE
+ printk("DRCE IOCTL: input [ FLAGS:%x ENABLE:%x THRESH:%x\n",
+ drce_ctrl.flags,
+ drce_ctrl.data.enable,
+ drce_ctrl.data.threshold);
+#endif
+
+ if (cmd & SIOC_IN) {
+ rc = tas3001c_update_drce(self, drce_ctrl.flags, &drce_ctrl.data);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (cmd & SIOC_OUT) {
+ if (drce_ctrl.flags & TAS_DRCE_ENABLE)
+ drce_ctrl.data.enable = self->drce_state.enable;
+
+ if (drce_ctrl.flags & TAS_DRCE_THRESHOLD)
+ drce_ctrl.data.threshold = self->drce_state.threshold;
+
+ if (copy_to_user((void *)arg,
+ (const void *)&drce_ctrl,
+ sizeof(struct tas_drce_ctrl_t))) {
+ return -EFAULT;
+ }
+ }
return 0;
}
-static int
-tas_attach_adapter(struct i2c_adapter *adapter)
+static void
+tas3001c_update_device_parameters(struct tas3001c_data_t *self)
{
- if (!strncmp(adapter->name, "mac-io", 6))
- tas_detect_client(adapter, tas_i2c_address);
+ int i,j;
- return 0;
+ if (!self) return;
+
+ if (self->output_id == TAS_OUTPUT_HEADPHONES) {
+ tas3001c_fast_load(self, 1);
+
+ for (i=0; idevice_id == self->device_id &&
+ (eq->output_id == 0 || eq->output_id == self->output_id) &&
+ (eq->speaker_id == 0 || eq->speaker_id == self->speaker_id)) {
+
+ tas3001c_update_drce(self, TAS_DRCE_ALL, eq->drce);
+ tas3001c_write_biquad_list(self, eq->filter_count, TAS_BIQUAD_FAST_LOAD, eq->biquads);
+
+ break;
+ }
+ }
}
-static int
-tas_init_client(struct i2c_client * new_client)
+static void
+tas3001c_device_change_handler(void *self)
{
- /* Make sure something answers on the i2c bus
- */
+ if (!self) return;
- if (i2c_smbus_write_byte_data(new_client, 1, (1<<6)+(2<<4)+(2<<2)+0) < 0)
- return -1;
+ tas3001c_update_device_parameters((struct tas3001c_data_t *)self);
+}
+
+static struct tq_struct device_change_task;
- tumbler_client = new_client;
+static int
+tas3001c_output_device_change( struct tas3001c_data_t *self,
+ int device_id,
+ int output_id,
+ int speaker_id)
+{
+ self->device_id=device_id;
+ self->output_id=output_id;
+ self->speaker_id=speaker_id;
- tumbler_set_volume(VOL_DEFAULT, VOL_DEFAULT);
- tumbler_set_pcm_lvl(INPUT_DEFAULT);
- tumbler_set_bass(BASS_DEFAULT);
- tumbler_set_treble(TREBLE_DEFAULT);
+ schedule_task(&device_change_task);
return 0;
}
static int
-tas_detect_client(struct i2c_adapter *adapter, int address)
-{
- int rc = 0;
- struct i2c_client *new_client;
- struct tas_data_t *data;
- const char *client_name = "tas 3001c Digital Equalizer";
-
- new_client = kmalloc(
- sizeof(struct i2c_client) + sizeof(struct tas_data_t),
- GFP_KERNEL);
- if (!new_client) {
- rc = -ENOMEM;
- goto bail;
- }
+tas3001c_device_ioctl( struct tas3001c_data_t *self,
+ u_int cmd,
+ u_long arg)
+{
+ switch (cmd) {
+ case TAS_READ_EQ:
+ case TAS_WRITE_EQ:
+ return tas3001c_eq_rw(self, cmd, arg);
+
+ case TAS_READ_EQ_LIST:
+ case TAS_WRITE_EQ_LIST:
+ return tas3001c_eq_list_rw(self, cmd, arg);
- /* This is tricky, but it will set the data to the right value. */
- new_client->data = new_client + 1;
- data = (struct tas_data_t *) (new_client->data);
+ case TAS_READ_EQ_FILTER_COUNT:
+ put_user(TAS3001C_BIQUAD_FILTER_COUNT, (uint *)(arg));
+ return 0;
- new_client->addr = address;
- new_client->data = data;
- new_client->adapter = adapter;
- new_client->driver = &tas_driver;
- new_client->flags = 0;
+ case TAS_READ_EQ_CHANNEL_COUNT:
+ put_user(TAS3001C_BIQUAD_CHANNEL_COUNT, (uint *)(arg));
+ return 0;
- strcpy(new_client->name,client_name);
+ case TAS_READ_DRCE:
+ case TAS_WRITE_DRCE:
+ return tas3001c_drce_rw(self, cmd, arg);
- new_client->id = tas_id++; /* Automatically unique */
+ case TAS_READ_DRCE_CAPS:
+ put_user(TAS_DRCE_ENABLE | TAS_DRCE_THRESHOLD, (uint *)(arg));
+ return 0;
- if (tas_init_client(new_client)) {
- rc = -ENODEV;
- goto bail;
+ case TAS_READ_DRCE_MIN:
+ case TAS_READ_DRCE_MAX: {
+ struct tas_drce_ctrl_t drce_ctrl;
+
+ if (copy_from_user((void *)&drce_ctrl,
+ (const void *)arg,
+ sizeof(struct tas_drce_ctrl_t))) {
+ return -EFAULT;
+ }
+
+ if (drce_ctrl.flags & TAS_DRCE_THRESHOLD) {
+ if (cmd == TAS_READ_DRCE_MIN) {
+ drce_ctrl.data.threshold=-36<<8;
+ } else {
+ drce_ctrl.data.threshold=-6<<8;
+ }
+ }
+
+ if (copy_to_user((void *)arg,
+ (const void *)&drce_ctrl,
+ sizeof(struct tas_drce_ctrl_t))) {
+ return -EFAULT;
+ }
}
-
- /* Tell the i2c layer a new client has arrived */
- if (i2c_attach_client(new_client)) {
- rc = -ENODEV;
- goto bail;
}
-bail:
- if (rc && new_client)
- kfree(new_client);
- return rc;
+
+ return -EINVAL;
}
static int
-tas_detach_client(struct i2c_client *client)
+tas3001c_init_mixer(struct tas3001c_data_t *self)
{
- if (client == tumbler_client)
- tumbler_client = NULL;
+ unsigned char mcr = (1<<6)+(2<<4)+(2<<2);
+
+ /* Make sure something answers on the i2c bus */
+ if (tas3001c_write_register(self, TAS3001C_REG_MCR, &mcr,
+ WRITE_NORMAL|FORCE_WRITE) < 0)
+ return -1;
+
+ tas3001c_fast_load(self, 1);
+
+ (void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD0);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD1);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD2);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD3);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD4);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD5);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD6);
+
+ (void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD0);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD1);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD2);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD3);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD4);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD5);
+ (void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD6);
+
+ tas3001c_fast_load(self, 0);
+
+ tas3001c_set_mixer_level(self, SOUND_MIXER_VOLUME, VOL_DEFAULT<<8 | VOL_DEFAULT);
+ tas3001c_set_mixer_level(self, SOUND_MIXER_PCM, INPUT_DEFAULT<<8 | INPUT_DEFAULT);
+ tas3001c_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0);
- i2c_detach_client(client);
- kfree(client);
+ tas3001c_set_mixer_level(self, SOUND_MIXER_BASS, BASS_DEFAULT);
+ tas3001c_set_mixer_level(self, SOUND_MIXER_TREBLE, TREBLE_DEFAULT);
return 0;
}
-int
-tas_cleanup(void)
+static int
+tas3001c_uninit_mixer(struct tas3001c_data_t *self)
{
- if (!tas_initialized)
- return -ENODEV;
- i2c_del_driver(&tas_driver);
- tas_initialized = 0;
+ tas3001c_set_mixer_level(self, SOUND_MIXER_VOLUME, 0);
+ tas3001c_set_mixer_level(self, SOUND_MIXER_PCM, 0);
+ tas3001c_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0);
+
+ tas3001c_set_mixer_level(self, SOUND_MIXER_BASS, 0);
+ tas3001c_set_mixer_level(self, SOUND_MIXER_TREBLE, 0);
return 0;
}
-int
-tas_init(void)
+static int
+tas3001c_init(struct i2c_client *client)
{
- int rc;
- u32* paddr;
-
- if (tas_initialized)
- return 0;
+ struct tas3001c_data_t *self;
+ int i,j;
- tas_node = find_devices("deq");
- if (tas_node == NULL)
- return -ENODEV;
-
- printk(KERN_INFO "tas3001c driver version %s (%s)\n",TAS_VERSION,TAS_DATE);
- paddr = (u32 *)get_property(tas_node, "i2c-address", NULL);
- if (paddr) {
- tas_i2c_address = (*paddr) >> 1;
- printk(KERN_INFO "using i2c address: 0x%x from device-tree\n",
- tas_i2c_address);
- } else
- printk(KERN_INFO "using i2c address: 0x%x (default)\n", tas_i2c_address);
-
- if ((rc = i2c_add_driver(&tas_driver))) {
- printk("tas3001c: Driver registration failed, module not inserted.\n");
- tas_cleanup();
- return rc;
+ self = kmalloc(sizeof(struct tas3001c_data_t) +
+ TAS3001C_REG_MAX * sizeof(tas_shadow_t),
+ GFP_KERNEL);
+ if (self == NULL)
+ return -ENOMEM;
+ client->data = (void *)self;
+ self->super.client = client;
+ self->super.shadow = (tas_shadow_t *)(self+1);
+
+ self->output_id=TAS_OUTPUT_HEADPHONES;
+ self->device_id=0;
+ self->speaker_id=0;
+
+ for (i=0; i
*/
-#ifndef _tas3001c_h_
-#define _tas3001c_h_
+#ifndef _TAS3001C_H_
+#define _TAS3001C_H_
-/*
- * Macros that correspond to the registers that we write to
- * when setting the various values.
- */
-#define TAS_DRC 0x02 /* DRC */
-#define TAS_VOLUME 0x04 /* Volume */
-#define TAS_TREBLE 0x05 /* Treble */
-#define TAS_BASS 0x06 /* Bass */
-#define TAS_MIXER1 0x07 /* PCM line */
-#define TAS_MIXER2 0x08 /* Input (Unk) */
-
-/*
- * Macros that define various arguments to tas_set_register()
- */
-#define TAS_SET_DRC TAS_DRC, 2
-#define TAS_SET_VOLUME TAS_VOLUME, 6
-#define TAS_SET_TREBLE TAS_TREBLE, 1
-#define TAS_SET_BASS TAS_BASS, 1
-#define TAS_SET_MIXER1 TAS_MIXER1, 3
-#define TAS_SET_MIXER2 TAS_MIXER2, 3
+#include
+#include "tas_common.h"
+#include "tas_eq_prefs.h"
/*
- * tas_volume_table contains lookup values for the volume settings
- * for tumbler. This is straight from the programming manual
- * for the chip, however, it's zero-sourced for your shopping pleasure
- * (meaning, you'll have to compute the difference between the desired
- * dB and the index value of the proper setting.
- *
- * This table should've been replaced by the formula:
- * dB = 20 log(x)
- * but, since there's no log() or supporting functions like exp(),
- * my implementation of the above won't work. Yeah, I could do it
- * the hard way, but this table is just easier :-)
- *
- * For reference, -70 dB = tas_volume_table[0]
+ * Macros that correspond to the registers that we write to
+ * when setting the various values.
*/
-static unsigned int tas_volume_table[] = {
- 0x00000015, 0x00000016, 0x00000017, /* -70.0, -69.5, -69.0 */
- 0x00000019, 0x0000001a, 0x0000001c, /* -68.5, -68.0, -67.5 */
- 0x0000001d, 0x0000001f, 0x00000021, /* -67.0, -66.5, -66.0 */
- 0x00000023, 0x00000025, 0x00000027, /* -65.5, -65.0, -64.5 */
- 0x00000029, 0x0000002c, 0x0000002e, /* -64.0, -63.5, -63.0 */
- 0x00000031, 0x00000034, 0x00000037, /* -62.5, -62.0, -61.5 */
- 0x0000003a, 0x0000003e, 0x00000042, /* -61.0, -60.5, -60.0 */
- 0x00000045, 0x0000004a, 0x0000004e, /* -59.5, -59.0, -58.5 */
- 0x00000053, 0x00000057, 0x0000005d, /* -58.0, -57.5, -57.0 */
- 0x00000062, 0x00000068, 0x0000006e, /* -56.5, -56.0, -55.5 */
- 0x00000075, 0x0000007b, 0x00000083, /* -55.0, -54.5, -54.0 */
- 0x0000008b, 0x00000093, 0x0000009b, /* -53.5, -53.0, -52.5 */
- 0x000000a5, 0x000000ae, 0x000000b9, /* -52.0, -51.5, -51.0 */
- 0x000000c4, 0x000000cf, 0x000000dc, /* -50.5, -50.0, -49.5 */
- 0x000000e9, 0x000000f6, 0x00000105, /* -49.0, -48.5, -48.0 */
- 0x00000114, 0x00000125, 0x00000136, /* -47.5, -47.0, -46.5 */
- 0x00000148, 0x0000015c, 0x00000171, /* -46.0, -45.5, -45.0 */
- 0x00000186, 0x0000019e, 0x000001b6, /* -44.5, -44.0, -43.5 */
- 0x000001d0, 0x000001eb, 0x00000209, /* -43.0, -42.5, -42.0 */
- 0x00000227, 0x00000248, 0x0000026b, /* -41.5, -41.0, -40.5 */
- 0x0000028f, 0x000002b6, 0x000002df, /* -40.0, -39.5, -39.0 */
- 0x0000030b, 0x00000339, 0x0000036a, /* -38.5, -38.0, -37.5 */
- 0x0000039e, 0x000003d5, 0x0000040f, /* -37.0, -36.5, -36.0 */
- 0x0000044c, 0x0000048d, 0x000004d2, /* -35.5, -35.0, -34.5 */
- 0x0000051c, 0x00000569, 0x000005bb, /* -34.0, -33.5, -33.0 */
- 0x00000612, 0x0000066e, 0x000006d0, /* -32.5, -32.0, -31.5 */
- 0x00000737, 0x000007a5, 0x00000818, /* -31.0, -30.5, -30.0 */
- 0x00000893, 0x00000915, 0x0000099f, /* -29.5, -29.0, -28.5 */
- 0x00000a31, 0x00000acc, 0x00000b6f, /* -28.0, -27.5, -27.0 */
- 0x00000c1d, 0x00000cd5, 0x00000d97, /* -26.5, -26.0, -25.5 */
- 0x00000e65, 0x00000f40, 0x00001027, /* -25.0, -24.5, -24.0 */
- 0x0000111c, 0x00001220, 0x00001333, /* -23.5, -23.0, -22.5 */
- 0x00001456, 0x0000158a, 0x000016d1, /* -22.0, -21.5, -21.0 */
- 0x0000182b, 0x0000199a, 0x00001b1e, /* -20.5, -20.0, -19.5 */
- 0x00001cb9, 0x00001e6d, 0x0000203a, /* -19.0, -18.5, -18.0 */
- 0x00002223, 0x00002429, 0x0000264e, /* -17.5, -17.0, -16.5 */
- 0x00002893, 0x00002afa, 0x00002d86, /* -16.0, -15.5, -15.0 */
- 0x00003039, 0x00003314, 0x0000361b, /* -14.5, -14.0, -13.5 */
- 0x00003950, 0x00003cb5, 0x0000404e, /* -13.0, -12.5, -12.0 */
- 0x0000441d, 0x00004827, 0x00004c6d, /* -11.5, -11.0, -10.5 */
- 0x000050f4, 0x000055c0, 0x00005ad5, /* -10.0, -09.5, -09.0 */
- 0x00006037, 0x000065ea, 0x00006bf4, /* -08.5, -08.0, -07.5 */
- 0x0000725a, 0x00007920, 0x0000804e, /* -07.0, -06.5, -06.0 */
- 0x000087e8, 0x00008ff6, 0x0000987d, /* -05.5, -05.0, -04.5 */
- 0x0000a186, 0x0000ab19, 0x0000b53c, /* -04.0, -03.5, -03.0 */
- 0x0000bff9, 0x0000cb59, 0x0000d766, /* -02.5, -02.0, -01.5 */
- 0x0000e429, 0x0000f1ae, 0x00010000, /* -01.0, -00.5, 00.0 */
- 0x00010f2b, 0x00011f3d, 0x00013042, /* +00.5, +01.0, +01.5 */
- 0x00014249, 0x00015562, 0x0001699c, /* +02.0, +02.5, +03.0 */
- 0x00017f09, 0x000195bc, 0x0001adc6, /* +03.5, +04.0, +04.5 */
- 0x0001c73d, 0x0001e237, 0x0001feca, /* +05.0, +05.5, +06.0 */
- 0x00021d0e, 0x00023d1d, 0x00025f12, /* +06.5, +07.0, +07.5 */
- 0x0002830b, 0x0002a925, 0x0002d182, /* +08.0, +08.5, +09.0 */
- 0x0002fc42, 0x0003298b, 0x00035983, /* +09.5, +10.0, +10.5 */
- 0x00038c53, 0x0003c225, 0x0003fb28, /* +11.0, +11.5, +12.0 */
- 0x0004378b, 0x00047783, 0x0004bb44, /* +12.5, +13.0, +13.5 */
- 0x0005030a, 0x00054f10, 0x00059f98, /* +14.0, +14.5, +15.0 */
- 0x0005f4e5, 0x00064f40, 0x0006aef6, /* +15.5, +16.0, +16.5 */
- 0x00071457, 0x00077fbb, 0x0007f17b /* +17.0, +17.5, +18.0 */
-};
+#define TAS3001C_VERSION "0.3"
+#define TAS3001C_DATE "20011214"
-/* tas_treble_table[] is a lookup table that holds the values to drop into
- * the treble setting register on the TAS. Again, there is a formula for
- * this one, but we use this instead due to lack of real math functions
- * in the kernel.
- */
-static char tas_treble_table[] = {
- 0x96, 0x95, 0x94, /* -18.0, -17.5, -17.0 */
- 0x93, 0x92, 0x91, /* -16.5, -16.0, -15.5 */
- 0x90, 0x8f, 0x8e, /* -15.0, -14.5, -14.0 */
- 0x8d, 0x8c, 0x8b, /* -13.5, -13.0, -12.5 */
- 0x8a, 0x89, 0x88, /* -12.0, -11.5, -11.0 */
- 0x87, 0x86, 0x85, /* -10.5, -10.0, -09.5 */
- 0x84, 0x83, 0x82, /* -09.0, -08.5, -08.0 */
- 0x81, 0x80, 0x7f, /* -07.5, -07.0, -06.5 */
- 0x7e, 0x7d, 0x7c, /* -06.0, -05.5, -05.0 */
- 0x7b, 0x7a, 0x79, /* -04.5, -04.0, -03.5 */
- 0x78, 0x77, 0x76, /* -03.0, -02.5, -02.0 */
- 0x75, 0x74, 0x73, /* -01.5, -01.0, -00.5 */
- 0x72, 0x71, 0x70, /* 00.0, +00.5, +01.0 */
- 0x6e, 0x6d, 0x6c, /* +01.5, +02.0, +02.5 */
- 0x6b, 0x69, 0x68, /* +03.0, +03.5, +04.0 */
- 0x66, 0x65, 0x63, /* +04.5, +05.0, +05.5 */
- 0x62, 0x60, 0x5e, /* +06.0, +06.5, +07.0 */
- 0x5c, 0x5a, 0x57, /* +07.5, +08.0, +08.5 */
- 0x55, 0x52, 0x4f, /* +09.0, +09.5, +10.0 */
- 0x4c, 0x49, 0x45, /* +10.5, +11.0, +11.5 */
- 0x42, 0x3e, 0x3a, /* +12.0, +12.5, +13.0 */
- 0x36, 0x32, 0x2d, /* +13.5, +14.0, +14.5 */
- 0x28, 0x22, 0x1c, /* +15.0, +15.5, +16.0 */
- 0x16, 0x10, 0x09, /* +16.5, +17.0, +17.5 */
- 0x01 /* +18.0 */
-};
+#define I2C_DRIVERNAME_TAS3001C "TAS3001c driver V " TAS3001C_VERSION
+#define I2C_DRIVERID_TAS3001C (I2C_DRIVERID_TAS_BASE+0)
-/* tas_bass_table[] is a lookup table that holds the values to drop into
- * the bass setting register on the TAS. Again, there is a formula for
- * this one, but we use this instead due to lack of real math functions
- * in the kernel.
- */
-static char tas_bass_table[] = {
- 0x86, 0x82, 0x7f, /* -18.0, -17.5, -17.0 */
- 0x7d, 0x7a, 0x78, /* -16.5, -16.0, -15.5 */
- 0x76, 0x74, 0x72, /* -15.0, -14.5, -14.0 */
- 0x70, 0x6e, 0x6d, /* -13.5, -13.0, -12.5 */
- 0x6b, 0x69, 0x66, /* -12.0, -11.5, -11.0 */
- 0x64, 0x61, 0x5f, /* -10.5, -10.0, -09.5 */
- 0x5d, 0x5c, 0x5a, /* -09.0, -08.5, -08.0 */
- 0x59, 0x58, 0x56, /* -07.5, -07.0, -06.5 */
- 0x55, 0x54, 0x53, /* -06.0, -05.5, -05.0 */
- 0x51, 0x4f, 0x4d, /* -04.5, -04.0, -03.5 */
- 0x4b, 0x49, 0x46, /* -03.0, -02.5, -02.0 */
- 0x44, 0x42, 0x40, /* -01.5, -01.0, -00.5 */
- 0x3e, 0x3c, 0x3b, /* 00.0, +00.5, +01.0 */
- 0x39, 0x38, 0x36, /* +01.5, +02.0, +02.5 */
- 0x35, 0x33, 0x31, /* +03.0, +03.5, +04.0 */
- 0x30, 0x2e, 0x2c, /* +04.5, +05.0, +05.5 */
- 0x2b, 0x29, 0x28, /* +06.0, +06.5, +07.0 */
- 0x26, 0x25, 0x23, /* +07.5, +08.0, +08.5 */
- 0x21, 0x1f, 0x1c, /* +09.0, +09.5, +10.0 */
- 0x19, 0x18, 0x17, /* +10.5, +11.0, +11.5 */
- 0x16, 0x14, 0x13, /* +12.0, +12.5, +13.0 */
- 0x12, 0x10, 0x0f, /* +13.5, +14.0, +14.5 */
- 0x0d, 0x0b, 0x0a, /* +15.0, +15.5, +16.0 */
- 0x08, 0x06, 0x03, /* +16.5, +17.0, +17.5 */
- 0x01 /* +18.0 */
-};
+extern struct tas_driver_hooks_t tas3001c_hooks;
+extern struct tas_gain_t tas3001c_gain;
+extern struct tas_eq_pref_t *tas3001c_eq_prefs[];
+
+enum tas3001c_reg_t {
+ TAS3001C_REG_MCR = 0x01,
+ TAS3001C_REG_DRC = 0x02,
+
+ TAS3001C_REG_VOLUME = 0x04,
+ TAS3001C_REG_TREBLE = 0x05,
+ TAS3001C_REG_BASS = 0x06,
+ TAS3001C_REG_MIXER1 = 0x07,
+ TAS3001C_REG_MIXER2 = 0x08,
+
+ TAS3001C_REG_LEFT_BIQUAD0 = 0x0a,
+ TAS3001C_REG_LEFT_BIQUAD1 = 0x0b,
+ TAS3001C_REG_LEFT_BIQUAD2 = 0x0c,
+ TAS3001C_REG_LEFT_BIQUAD3 = 0x0d,
+ TAS3001C_REG_LEFT_BIQUAD4 = 0x0e,
+ TAS3001C_REG_LEFT_BIQUAD5 = 0x0f,
+ TAS3001C_REG_LEFT_BIQUAD6 = 0x10,
+
+ TAS3001C_REG_RIGHT_BIQUAD0 = 0x13,
+ TAS3001C_REG_RIGHT_BIQUAD1 = 0x14,
+ TAS3001C_REG_RIGHT_BIQUAD2 = 0x15,
+ TAS3001C_REG_RIGHT_BIQUAD3 = 0x16,
+ TAS3001C_REG_RIGHT_BIQUAD4 = 0x17,
+ TAS3001C_REG_RIGHT_BIQUAD5 = 0x18,
+ TAS3001C_REG_RIGHT_BIQUAD6 = 0x19,
-/* tas_input_table[] is a lookup table that holds the values to drop into
- * the setting registers on the TAS for "mixers 1 & 2" (which are the input
- * lines). Again, there is a formula for these, but we use this instead
- * due to lack of real math functions in the kernel.
- */
-static unsigned int tas_input_table[] = {
- 0x00014b, 0x00015f, 0x000174, /* -70.0, -69.5, -69.0 */
- 0x00018a, 0x0001a1, 0x0001ba, /* -68.5, -68.0, -67.5 */
- 0x0001d4, 0x0001f0, 0x00020d, /* -67.0, -66.5, -66.0 */
- 0x00022c, 0x00024d, 0x000270, /* -65.5, -65.0, -64.5 */
- 0x000295, 0x0002bc, 0x0002e6, /* -64.0, -63.5, -63.0 */
- 0x000312, 0x000340, 0x000372, /* -62.5, -62.0, -61.5 */
- 0x0003a6, 0x0003dd, 0x000418, /* -61.0, -60.5, -60.0 */
- 0x000456, 0x000498, 0x0004de, /* -59.5, -59.0, -58.5 */
- 0x000528, 0x000576, 0x0005c9, /* -58.0, -57.5, -57.0 */
- 0x000620, 0x00067d, 0x0006e0, /* -56.5, -56.0, -55.5 */
- 0x000748, 0x0007b7, 0x00082c, /* -55.0, -54.5, -54.0 */
- 0x0008a8, 0x00092b, 0x0009b6, /* -53.5, -53.0, -52.5 */
- 0x000a49, 0x000ae5, 0x000b8b, /* -52.0, -51.5, -51.0 */
- 0x000c3a, 0x000cf3, 0x000db8, /* -50.5, -50.0, -49.5 */
- 0x000e88, 0x000f64, 0x00104e, /* -49.0, -48.5, -48.0 */
- 0x001145, 0x00124b, 0x001361, /* -47.5, -47.0, -46.5 */
- 0x001487, 0x0015be, 0x001708, /* -46.0, -45.5, -45.0 */
- 0x001865, 0x0019d8, 0x001b60, /* -44.5, -44.0, -43.5 */
- 0x001cff, 0x001eb7, 0x002089, /* -43.0, -42.5, -42.0 */
- 0x002276, 0x002481, 0x0026ab, /* -41.5, -41.0, -40.5 */
- 0x0028f5, 0x002b63, 0x002df5, /* -40.0, -39.5, -39.0 */
- 0x0030ae, 0x003390, 0x00369e, /* -38.5, -38.0, -37.5 */
- 0x0039db, 0x003d49, 0x0040ea, /* -37.0, -36.5, -36.0 */
- 0x0044c3, 0x0048d6, 0x004d27, /* -35.5, -35.0, -34.5 */
- 0x0051b9, 0x005691, 0x005bb2, /* -34.0, -33.5, -33.0 */
- 0x006121, 0x0066e3, 0x006cfb, /* -32.5, -32.0, -31.5 */
- 0x007370, 0x007a48, 0x008186, /* -31.0, -30.5, -30.0 */
- 0x008933, 0x009154, 0x0099f1, /* -29.5, -29.0, -28.5 */
- 0x00a310, 0x00acba, 0x00b6f6, /* -28.0, -27.5, -27.0 */
- 0x00c1cd, 0x00cd49, 0x00d973, /* -26.5, -26.0, -25.5 */
- 0x00e655, 0x00f3fb, 0x010270, /* -25.0, -24.5, -24.0 */
- 0x0111c0, 0x0121f9, 0x013328, /* -23.5, -23.0, -22.5 */
- 0x01455b, 0x0158a2, 0x016d0e, /* -22.0, -21.5, -21.0 */
- 0x0182af, 0x019999, 0x01b1de, /* -20.5, -20.0, -19.5 */
- 0x01cb94, 0x01e6cf, 0x0203a7, /* -19.0, -18.5, -18.0 */
- 0x022235, 0x024293, 0x0264db, /* -17.5, -17.0, -16.5 */
- 0x02892c, 0x02afa3, 0x02d862, /* -16.0, -15.5, -15.0 */
- 0x03038a, 0x033142, 0x0361af, /* -14.5, -14.0, -13.5 */
- 0x0394fa, 0x03cb50, 0x0404de, /* -13.0, -12.5, -12.0 */
- 0x0441d5, 0x048268, 0x04c6d0, /* -11.5, -11.0, -10.5 */
- 0x050f44, 0x055c04, 0x05ad50, /* -10.0, -09.5, -09.0 */
- 0x06036e, 0x065ea5, 0x06bf44, /* -08.5, -08.0, -07.5 */
- 0x07259d, 0x079207, 0x0804dc, /* -07.0, -06.5, -06.0 */
- 0x087e80, 0x08ff59, 0x0987d5, /* -05.5, -05.0, -04.5 */
- 0x0a1866, 0x0ab189, 0x0b53be, /* -04.0, -03.5, -03.0 */
- 0x0bff91, 0x0cb591, 0x0d765a, /* -02.5, -02.0, -01.5 */
- 0x0e4290, 0x0f1adf, 0x100000, /* -01.0, -00.5, 00.0 */
- 0x10f2b4, 0x11f3c9, 0x13041a, /* +00.5, +01.0, +01.5 */
- 0x14248e, 0x15561a, 0x1699c0, /* +02.0, +02.5, +03.0 */
- 0x17f094, 0x195bb8, 0x1adc61, /* +03.5, +04.0, +04.5 */
- 0x1c73d5, 0x1e236d, 0x1fec98, /* +05.0, +05.5, +06.0 */
- 0x21d0d9, 0x23d1cd, 0x25f125, /* +06.5, +07.0, +07.5 */
- 0x2830af, 0x2a9254, 0x2d1818, /* +08.0, +08.5, +09.0 */
- 0x2fc420, 0x3298b0, 0x35982f, /* +09.5, +10.0, +10.5 */
- 0x38c528, 0x3c224c, 0x3fb278, /* +11.0, +11.5, +12.0 */
- 0x437880, 0x477828, 0x4bb446, /* +12.5, +13.0, +13.5 */
- 0x5030a1, 0x54f106, 0x59f980, /* +14.0, +14.5, +15.0 */
- 0x5f4e52, 0x64f403, 0x6aef5d, /* +15.5, +16.0, +16.5 */
- 0x714575, 0x77fbaa, 0x7f17af /* +17.0, +17.5, +18.0 */
+ TAS3001C_REG_MAX = 0x20
};
-#endif /* _tas3001c_h_ */
+#endif /* _TAS3001C_H_ */
diff -uNr linux-2.4.22/drivers/sound/dmasound/tas3001c_tables.c linux-2.4.22-ben2/drivers/sound/dmasound/tas3001c_tables.c
--- linux-2.4.22/drivers/sound/dmasound/tas3001c_tables.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/drivers/sound/dmasound/tas3001c_tables.c 2003-08-31 11:41:02.000000000 +0200
@@ -0,0 +1,375 @@
+#include "tas_common.h"
+#include "tas_eq_prefs.h"
+
+static struct tas_drce_t eqp_0e_2_1_drce = {
+ enable: 1,
+ above: { val: 3.0 * (1<<8), expand: 0 },
+ below: { val: 1.0 * (1<<8), expand: 0 },
+ threshold: -15.33 * (1<<8),
+ energy: 2.4 * (1<<12),
+ attack: 0.013 * (1<<12),
+ decay: 0.212 * (1<<12),
+};
+
+static struct tas_biquad_ctrl_t eqp_0e_2_1_biquads[]={
+ { channel: 0, filter: 0, data: { coeff: { 0x0FCAD3, 0xE06A58, 0x0FCAD3, 0xE06B09, 0x0F9657 } } },
+ { channel: 0, filter: 1, data: { coeff: { 0x041731, 0x082E63, 0x041731, 0xFD8D08, 0x02CFBD } } },
+ { channel: 0, filter: 2, data: { coeff: { 0x0FFDC7, 0xE0524C, 0x0FBFAA, 0xE0524C, 0x0FBD72 } } },
+ { channel: 0, filter: 3, data: { coeff: { 0x0F3D35, 0xE228CA, 0x0EC7B2, 0xE228CA, 0x0E04E8 } } },
+ { channel: 0, filter: 4, data: { coeff: { 0x0FCEBF, 0xE181C2, 0x0F2656, 0xE181C2, 0x0EF516 } } },
+ { channel: 0, filter: 5, data: { coeff: { 0x0EC417, 0x073E22, 0x0B0633, 0x073E22, 0x09CA4A } } },
+
+ { channel: 1, filter: 0, data: { coeff: { 0x0FCAD3, 0xE06A58, 0x0FCAD3, 0xE06B09, 0x0F9657 } } },
+ { channel: 1, filter: 1, data: { coeff: { 0x041731, 0x082E63, 0x041731, 0xFD8D08, 0x02CFBD } } },
+ { channel: 1, filter: 2, data: { coeff: { 0x0FFDC7, 0xE0524C, 0x0FBFAA, 0xE0524C, 0x0FBD72 } } },
+ { channel: 1, filter: 3, data: { coeff: { 0x0F3D35, 0xE228CA, 0x0EC7B2, 0xE228CA, 0x0E04E8 } } },
+ { channel: 1, filter: 4, data: { coeff: { 0x0FCEBF, 0xE181C2, 0x0F2656, 0xE181C2, 0x0EF516 } } },
+ { channel: 1, filter: 5, data: { coeff: { 0x0EC417, 0x073E22, 0x0B0633, 0x073E22, 0x09CA4A } } },
+};
+
+static struct tas_eq_pref_t eqp_0e_2_1 = {
+ sample_rate: 44100,
+ device_id: 0x0e,
+ output_id: TAS_OUTPUT_EXTERNAL_SPKR,
+ speaker_id: 0x01,
+
+ drce: &eqp_0e_2_1_drce,
+
+ filter_count: 12,
+ biquads: eqp_0e_2_1_biquads
+};
+
+/* ======================================================================== */
+
+static struct tas_drce_t eqp_10_1_0_drce={
+ enable: 1,
+ above: { val: 3.0 * (1<<8), expand: 0 },
+ below: { val: 1.0 * (1<<8), expand: 0 },
+ threshold: -12.46 * (1<<8),
+ energy: 2.4 * (1<<12),
+ attack: 0.013 * (1<<12),
+ decay: 0.212 * (1<<12),
+};
+
+static struct tas_biquad_ctrl_t eqp_10_1_0_biquads[]={
+ { channel: 0, filter: 0, data: { coeff: { 0x0F4A12, 0xE16BDA, 0x0F4A12, 0xE173F0, 0x0E9C3A } } },
+ { channel: 0, filter: 1, data: { coeff: { 0x02DD54, 0x05BAA8, 0x02DD54, 0xF8001D, 0x037532 } } },
+ { channel: 0, filter: 2, data: { coeff: { 0x0E2FC7, 0xE4D5DC, 0x0D7477, 0xE4D5DC, 0x0BA43F } } },
+ { channel: 0, filter: 3, data: { coeff: { 0x0E7899, 0xE67CCA, 0x0D0E93, 0xE67CCA, 0x0B872D } } },
+ { channel: 0, filter: 4, data: { coeff: { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
+ { channel: 0, filter: 5, data: { coeff: { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
+
+ { channel: 1, filter: 0, data: { coeff: { 0x0F4A12, 0xE16BDA, 0x0F4A12, 0xE173F0, 0x0E9C3A } } },
+ { channel: 1, filter: 1, data: { coeff: { 0x02DD54, 0x05BAA8, 0x02DD54, 0xF8001D, 0x037532 } } },
+ { channel: 1, filter: 2, data: { coeff: { 0x0E2FC7, 0xE4D5DC, 0x0D7477, 0xE4D5DC, 0x0BA43F } } },
+ { channel: 1, filter: 3, data: { coeff: { 0x0E7899, 0xE67CCA, 0x0D0E93, 0xE67CCA, 0x0B872D } } },
+ { channel: 1, filter: 4, data: { coeff: { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
+ { channel: 1, filter: 5, data: { coeff: { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
+};
+
+static struct tas_eq_pref_t eqp_10_1_0 = {
+ sample_rate: 44100,
+ device_id: 0x10,
+ output_id: TAS_OUTPUT_INTERNAL_SPKR,
+ speaker_id: 0x00,
+
+ drce: &eqp_10_1_0_drce,
+
+ filter_count: 12,
+ biquads: eqp_10_1_0_biquads
+};
+
+/* ======================================================================== */
+
+static struct tas_drce_t eqp_15_2_1_drce={
+ enable: 1,
+ above: { val: 3.0 * (1<<8), expand: 0 },
+ below: { val: 1.0 * (1<<8), expand: 0 },
+ threshold: -15.33 * (1<<8),
+ energy: 2.4 * (1<<12),
+ attack: 0.013 * (1<<12),
+ decay: 0.212 * (1<<12),
+};
+
+static struct tas_biquad_ctrl_t eqp_15_2_1_biquads[]={
+ { channel: 0, filter: 0, data: { coeff: { 0x0FE143, 0xE05204, 0x0FCCC5, 0xE05266, 0x0FAE6B } } },
+ { channel: 0, filter: 1, data: { coeff: { 0x102383, 0xE03A03, 0x0FA325, 0xE03A03, 0x0FC6A8 } } },
+ { channel: 0, filter: 2, data: { coeff: { 0x0FF2AB, 0xE06285, 0x0FB20A, 0xE06285, 0x0FA4B5 } } },
+ { channel: 0, filter: 3, data: { coeff: { 0x0F544D, 0xE35971, 0x0D8F3A, 0xE35971, 0x0CE388 } } },
+ { channel: 0, filter: 4, data: { coeff: { 0x13E1D3, 0xF3ECB5, 0x042227, 0xF3ECB5, 0x0803FA } } },
+ { channel: 0, filter: 5, data: { coeff: { 0x0AC119, 0x034181, 0x078AB1, 0x034181, 0x024BCA } } },
+
+ { channel: 1, filter: 0, data: { coeff: { 0x0FE143, 0xE05204, 0x0FCCC5, 0xE05266, 0x0FAE6B } } },
+ { channel: 1, filter: 1, data: { coeff: { 0x102383, 0xE03A03, 0x0FA325, 0xE03A03, 0x0FC6A8 } } },
+ { channel: 1, filter: 2, data: { coeff: { 0x0FF2AB, 0xE06285, 0x0FB20A, 0xE06285, 0x0FA4B5 } } },
+ { channel: 1, filter: 3, data: { coeff: { 0x0F544D, 0xE35971, 0x0D8F3A, 0xE35971, 0x0CE388 } } },
+ { channel: 1, filter: 4, data: { coeff: { 0x13E1D3, 0xF3ECB5, 0x042227, 0xF3ECB5, 0x0803FA } } },
+ { channel: 1, filter: 5, data: { coeff: { 0x0AC119, 0x034181, 0x078AB1, 0x034181, 0x024BCA } } },
+};
+
+static struct tas_eq_pref_t eqp_15_2_1 = {
+ sample_rate: 44100,
+ device_id: 0x15,
+ output_id: TAS_OUTPUT_EXTERNAL_SPKR,
+ speaker_id: 0x01,
+
+ drce: &eqp_15_2_1_drce,
+
+ filter_count: 12,
+ biquads: eqp_15_2_1_biquads
+};
+
+/* ======================================================================== */
+
+static struct tas_drce_t eqp_15_1_0_drce={
+ enable: 1,
+ above: { val: 3.0 * (1<<8), expand: 0 },
+ below: { val: 1.0 * (1<<8), expand: 0 },
+ threshold: 0.0 * (1<<8),
+ energy: 2.4 * (1<<12),
+ attack: 0.013 * (1<<12),
+ decay: 0.212 * (1<<12),
+};
+
+static struct tas_biquad_ctrl_t eqp_15_1_0_biquads[]={
+ { channel: 0, filter: 0, data: { coeff: { 0x0FAD08, 0xE0A5EF, 0x0FAD08, 0xE0A79D, 0x0F5BBE } } },
+ { channel: 0, filter: 1, data: { coeff: { 0x04B38D, 0x09671B, 0x04B38D, 0x000F71, 0x02BEC5 } } },
+ { channel: 0, filter: 2, data: { coeff: { 0x0FDD32, 0xE0A56F, 0x0F8A69, 0xE0A56F, 0x0F679C } } },
+ { channel: 0, filter: 3, data: { coeff: { 0x0FD284, 0xE135FB, 0x0F2161, 0xE135FB, 0x0EF3E5 } } },
+ { channel: 0, filter: 4, data: { coeff: { 0x0E81B1, 0xE6283F, 0x0CE49D, 0xE6283F, 0x0B664F } } },
+ { channel: 0, filter: 5, data: { coeff: { 0x0F2D62, 0xE98797, 0x0D1E19, 0xE98797, 0x0C4B7B } } },
+
+ { channel: 1, filter: 0, data: { coeff: { 0x0FAD08, 0xE0A5EF, 0x0FAD08, 0xE0A79D, 0x0F5BBE } } },
+ { channel: 1, filter: 1, data: { coeff: { 0x04B38D, 0x09671B, 0x04B38D, 0x000F71, 0x02BEC5 } } },
+ { channel: 1, filter: 2, data: { coeff: { 0x0FDD32, 0xE0A56F, 0x0F8A69, 0xE0A56F, 0x0F679C } } },
+ { channel: 1, filter: 3, data: { coeff: { 0x0FD284, 0xE135FB, 0x0F2161, 0xE135FB, 0x0EF3E5 } } },
+ { channel: 1, filter: 4, data: { coeff: { 0x0E81B1, 0xE6283F, 0x0CE49D, 0xE6283F, 0x0B664F } } },
+ { channel: 1, filter: 5, data: { coeff: { 0x0F2D62, 0xE98797, 0x0D1E19, 0xE98797, 0x0C4B7B } } },
+};
+
+static struct tas_eq_pref_t eqp_15_1_0 = {
+ sample_rate: 44100,
+ device_id: 0x15,
+ output_id: TAS_OUTPUT_INTERNAL_SPKR,
+ speaker_id: 0x00,
+
+ drce: &eqp_15_1_0_drce,
+
+ filter_count: 12,
+ biquads: eqp_15_1_0_biquads
+};
+
+/* ======================================================================== */
+
+static struct tas_drce_t eqp_0f_2_1_drce={
+ enable: 1,
+ above: { val: 3.0 * (1<<8), expand: 0 },
+ below: { val: 1.0 * (1<<8), expand: 0 },
+ threshold: -15.33 * (1<<8),
+ energy: 2.4 * (1<<12),
+ attack: 0.013 * (1<<12),
+ decay: 0.212 * (1<<12),
+};
+
+static struct tas_biquad_ctrl_t eqp_0f_2_1_biquads[]={
+ { channel: 0, filter: 0, data: { coeff: { 0x0FE143, 0xE05204, 0x0FCCC5, 0xE05266, 0x0FAE6B } } },
+ { channel: 0, filter: 1, data: { coeff: { 0x102383, 0xE03A03, 0x0FA325, 0xE03A03, 0x0FC6A8 } } },
+ { channel: 0, filter: 2, data: { coeff: { 0x0FF2AB, 0xE06285, 0x0FB20A, 0xE06285, 0x0FA4B5 } } },
+ { channel: 0, filter: 3, data: { coeff: { 0x0F544D, 0xE35971, 0x0D8F3A, 0xE35971, 0x0CE388 } } },
+ { channel: 0, filter: 4, data: { coeff: { 0x13E1D3, 0xF3ECB5, 0x042227, 0xF3ECB5, 0x0803FA } } },
+ { channel: 0, filter: 5, data: { coeff: { 0x0AC119, 0x034181, 0x078AB1, 0x034181, 0x024BCA } } },
+
+ { channel: 1, filter: 0, data: { coeff: { 0x0FE143, 0xE05204, 0x0FCCC5, 0xE05266, 0x0FAE6B } } },
+ { channel: 1, filter: 1, data: { coeff: { 0x102383, 0xE03A03, 0x0FA325, 0xE03A03, 0x0FC6A8 } } },
+ { channel: 1, filter: 2, data: { coeff: { 0x0FF2AB, 0xE06285, 0x0FB20A, 0xE06285, 0x0FA4B5 } } },
+ { channel: 1, filter: 3, data: { coeff: { 0x0F544D, 0xE35971, 0x0D8F3A, 0xE35971, 0x0CE388 } } },
+ { channel: 1, filter: 4, data: { coeff: { 0x13E1D3, 0xF3ECB5, 0x042227, 0xF3ECB5, 0x0803FA } } },
+ { channel: 1, filter: 5, data: { coeff: { 0x0AC119, 0x034181, 0x078AB1, 0x034181, 0x024BCA } } },
+};
+
+static struct tas_eq_pref_t eqp_0f_2_1 = {
+ sample_rate: 44100,
+ device_id: 0x0f,
+ output_id: TAS_OUTPUT_EXTERNAL_SPKR,
+ speaker_id: 0x01,
+
+ drce: &eqp_0f_2_1_drce,
+
+ filter_count: 12,
+ biquads: eqp_0f_2_1_biquads
+};
+
+/* ======================================================================== */
+
+static struct tas_drce_t eqp_0f_1_0_drce={
+ enable: 1,
+ above: { val: 3.0 * (1<<8), expand: 0 },
+ below: { val: 1.0 * (1<<8), expand: 0 },
+ threshold: -15.33 * (1<<8),
+ energy: 2.4 * (1<<12),
+ attack: 0.013 * (1<<12),
+ decay: 0.212 * (1<<12),
+};
+
+static struct tas_biquad_ctrl_t eqp_0f_1_0_biquads[]={
+ { channel: 0, filter: 0, data: { coeff: { 0x0FCAD3, 0xE06A58, 0x0FCAD3, 0xE06B09, 0x0F9657 } } },
+ { channel: 0, filter: 1, data: { coeff: { 0x041731, 0x082E63, 0x041731, 0xFD8D08, 0x02CFBD } } },
+ { channel: 0, filter: 2, data: { coeff: { 0x0FFDC7, 0xE0524C, 0x0FBFAA, 0xE0524C, 0x0FBD72 } } },
+ { channel: 0, filter: 3, data: { coeff: { 0x0F3D35, 0xE228CA, 0x0EC7B2, 0xE228CA, 0x0E04E8 } } },
+ { channel: 0, filter: 4, data: { coeff: { 0x0FCEBF, 0xE181C2, 0x0F2656, 0xE181C2, 0x0EF516 } } },
+ { channel: 0, filter: 5, data: { coeff: { 0x0EC417, 0x073E22, 0x0B0633, 0x073E22, 0x09CA4A } } },
+
+ { channel: 1, filter: 0, data: { coeff: { 0x0FCAD3, 0xE06A58, 0x0FCAD3, 0xE06B09, 0x0F9657 } } },
+ { channel: 1, filter: 1, data: { coeff: { 0x041731, 0x082E63, 0x041731, 0xFD8D08, 0x02CFBD } } },
+ { channel: 1, filter: 2, data: { coeff: { 0x0FFDC7, 0xE0524C, 0x0FBFAA, 0xE0524C, 0x0FBD72 } } },
+ { channel: 1, filter: 3, data: { coeff: { 0x0F3D35, 0xE228CA, 0x0EC7B2, 0xE228CA, 0x0E04E8 } } },
+ { channel: 1, filter: 4, data: { coeff: { 0x0FCEBF, 0xE181C2, 0x0F2656, 0xE181C2, 0x0EF516 } } },
+ { channel: 1, filter: 5, data: { coeff: { 0x0EC417, 0x073E22, 0x0B0633, 0x073E22, 0x09CA4A } } },
+};
+
+static struct tas_eq_pref_t eqp_0f_1_0 = {
+ sample_rate: 44100,
+ device_id: 0x0f,
+ output_id: TAS_OUTPUT_INTERNAL_SPKR,
+ speaker_id: 0x00,
+
+ drce: &eqp_0f_1_0_drce,
+
+ filter_count: 12,
+ biquads: eqp_0f_1_0_biquads
+};
+
+/* ======================================================================== */
+
+static uint tas3001c_master_tab[]={
+ 0x0, 0x75, 0x9c, 0xbb,
+ 0xdb, 0xfb, 0x11e, 0x143,
+ 0x16b, 0x196, 0x1c3, 0x1f5,
+ 0x229, 0x263, 0x29f, 0x2e1,
+ 0x328, 0x373, 0x3c5, 0x41b,
+ 0x478, 0x4dc, 0x547, 0x5b8,
+ 0x633, 0x6b5, 0x740, 0x7d5,
+ 0x873, 0x91c, 0x9d2, 0xa92,
+ 0xb5e, 0xc39, 0xd22, 0xe19,
+ 0xf20, 0x1037, 0x1161, 0x129e,
+ 0x13ed, 0x1551, 0x16ca, 0x185d,
+ 0x1a08, 0x1bcc, 0x1dac, 0x1fa7,
+ 0x21c1, 0x23fa, 0x2655, 0x28d6,
+ 0x2b7c, 0x2e4a, 0x3141, 0x3464,
+ 0x37b4, 0x3b35, 0x3ee9, 0x42d3,
+ 0x46f6, 0x4b53, 0x4ff0, 0x54ce,
+ 0x59f2, 0x5f5f, 0x6519, 0x6b24,
+ 0x7183, 0x783c, 0x7f53, 0x86cc,
+ 0x8ead, 0x96fa, 0x9fba, 0xa8f2,
+ 0xb2a7, 0xbce1, 0xc7a5, 0xd2fa,
+ 0xdee8, 0xeb75, 0xf8aa, 0x1068e,
+ 0x1152a, 0x12487, 0x134ad, 0x145a5,
+ 0x1577b, 0x16a37, 0x17df5, 0x192bd,
+ 0x1a890, 0x1bf7b, 0x1d78d, 0x1f0d1,
+ 0x20b55, 0x22727, 0x24456, 0x262f2,
+ 0x2830b
+};
+
+static uint tas3001c_mixer_tab[]={
+ 0x0, 0x748, 0x9be, 0xbaf,
+ 0xda4, 0xfb1, 0x11de, 0x1431,
+ 0x16ad, 0x1959, 0x1c37, 0x1f4b,
+ 0x2298, 0x2628, 0x29fb, 0x2e12,
+ 0x327d, 0x3734, 0x3c47, 0x41b4,
+ 0x4787, 0x4dbe, 0x546d, 0x5b86,
+ 0x632e, 0x6b52, 0x7400, 0x7d54,
+ 0x873b, 0x91c6, 0x9d1a, 0xa920,
+ 0xb5e5, 0xc38c, 0xd21b, 0xe18f,
+ 0xf1f5, 0x1036a, 0x1160f, 0x129d6,
+ 0x13ed0, 0x1550c, 0x16ca0, 0x185c9,
+ 0x1a07b, 0x1bcc3, 0x1dab9, 0x1fa75,
+ 0x21c0f, 0x23fa3, 0x26552, 0x28d64,
+ 0x2b7c9, 0x2e4a2, 0x31411, 0x3463b,
+ 0x37b44, 0x3b353, 0x3ee94, 0x42d30,
+ 0x46f55, 0x4b533, 0x4fefc, 0x54ce5,
+ 0x59f25, 0x5f5f6, 0x65193, 0x6b23c,
+ 0x71835, 0x783c3, 0x7f52c, 0x86cc0,
+ 0x8eacc, 0x96fa5, 0x9fba0, 0xa8f1a,
+ 0xb2a71, 0xbce0a, 0xc7a4a, 0xd2fa0,
+ 0xdee7b, 0xeb752, 0xf8a9f, 0x1068e4,
+ 0x1152a3, 0x12486a, 0x134ac8, 0x145a55,
+ 0x1577ac, 0x16a370, 0x17df51, 0x192bc2,
+ 0x1a88f8, 0x1bf7b7, 0x1d78c9, 0x1f0d04,
+ 0x20b542, 0x227268, 0x244564, 0x262f26,
+ 0x2830af
+};
+
+static uint tas3001c_treble_tab[]={
+ 0x96, 0x95, 0x95, 0x94,
+ 0x93, 0x92, 0x92, 0x91,
+ 0x90, 0x90, 0x8f, 0x8e,
+ 0x8d, 0x8d, 0x8c, 0x8b,
+ 0x8a, 0x8a, 0x89, 0x88,
+ 0x88, 0x87, 0x86, 0x85,
+ 0x85, 0x84, 0x83, 0x83,
+ 0x82, 0x81, 0x80, 0x80,
+ 0x7f, 0x7e, 0x7e, 0x7d,
+ 0x7c, 0x7b, 0x7b, 0x7a,
+ 0x79, 0x78, 0x78, 0x77,
+ 0x76, 0x76, 0x75, 0x74,
+ 0x73, 0x73, 0x72, 0x71,
+ 0x71, 0x70, 0x6e, 0x6d,
+ 0x6d, 0x6c, 0x6b, 0x6a,
+ 0x69, 0x68, 0x67, 0x66,
+ 0x65, 0x63, 0x62, 0x62,
+ 0x60, 0x5f, 0x5d, 0x5c,
+ 0x5a, 0x58, 0x56, 0x55,
+ 0x53, 0x51, 0x4f, 0x4c,
+ 0x4a, 0x48, 0x45, 0x43,
+ 0x40, 0x3d, 0x3a, 0x37,
+ 0x35, 0x32, 0x2e, 0x2a,
+ 0x27, 0x22, 0x1e, 0x1a,
+ 0x15, 0x11, 0xc, 0x7,
+ 0x1
+};
+
+static uint tas3001c_bass_tab[]={
+ 0x86, 0x83, 0x81, 0x7f,
+ 0x7d, 0x7b, 0x79, 0x78,
+ 0x76, 0x75, 0x74, 0x72,
+ 0x71, 0x6f, 0x6e, 0x6d,
+ 0x6c, 0x6b, 0x69, 0x67,
+ 0x65, 0x64, 0x61, 0x60,
+ 0x5e, 0x5d, 0x5c, 0x5b,
+ 0x5a, 0x59, 0x58, 0x57,
+ 0x56, 0x55, 0x55, 0x54,
+ 0x53, 0x52, 0x50, 0x4f,
+ 0x4d, 0x4c, 0x4b, 0x49,
+ 0x47, 0x45, 0x44, 0x42,
+ 0x41, 0x3f, 0x3e, 0x3d,
+ 0x3c, 0x3b, 0x39, 0x38,
+ 0x37, 0x36, 0x35, 0x34,
+ 0x33, 0x31, 0x30, 0x2f,
+ 0x2e, 0x2c, 0x2b, 0x2b,
+ 0x29, 0x28, 0x27, 0x26,
+ 0x25, 0x24, 0x22, 0x21,
+ 0x20, 0x1e, 0x1c, 0x19,
+ 0x18, 0x18, 0x17, 0x16,
+ 0x15, 0x14, 0x13, 0x12,
+ 0x11, 0x10, 0xf, 0xe,
+ 0xd, 0xb, 0xa, 0x9,
+ 0x8, 0x6, 0x4, 0x2,
+ 0x1
+};
+
+struct tas_gain_t tas3001c_gain = {
+ master: tas3001c_master_tab,
+ treble: tas3001c_treble_tab,
+ bass: tas3001c_bass_tab,
+ mixer: tas3001c_mixer_tab
+};
+
+struct tas_eq_pref_t *tas3001c_eq_prefs[]={
+ &eqp_0e_2_1,
+ &eqp_10_1_0,
+ &eqp_15_2_1,
+ &eqp_15_1_0,
+ &eqp_0f_2_1,
+ &eqp_0f_1_0,
+ NULL
+};
diff -uNr linux-2.4.22/drivers/sound/dmasound/tas3004.c linux-2.4.22-ben2/drivers/sound/dmasound/tas3004.c
--- linux-2.4.22/drivers/sound/dmasound/tas3004.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/drivers/sound/dmasound/tas3004.c 2003-08-31 11:38:41.000000000 +0200
@@ -0,0 +1,1173 @@
+/*
+ * Driver for the i2c/i2s based TA3004 sound chip used
+ * on some Apple hardware. Also known as "snapper".
+ *
+ * Tobias Sargeant
+ * Based upon tas3001c.c by Christopher C. Chimelis :
+ *
+ * TODO:
+ * -----
+ * * Enable control over input line 2 (is this connected?)
+ * * Implement sleep support (at least mute everything and
+ * * set gains to minimum during sleep)
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "dmasound.h"
+#include "tas_common.h"
+#include "tas3004.h"
+
+#include "tas_ioctl.h"
+
+/* #define DEBUG_DRCE */
+
+#define TAS3004_BIQUAD_FILTER_COUNT 7
+#define TAS3004_BIQUAD_CHANNEL_COUNT 2
+
+#define VOL_DEFAULT (100 * 4 / 5)
+#define INPUT_DEFAULT (100 * 4 / 5)
+#define BASS_DEFAULT (100 / 2)
+#define TREBLE_DEFAULT (100 / 2)
+
+struct tas3004_data_t {
+ struct tas_data_t super;
+ int device_id;
+ int output_id;
+ int speaker_id;
+ struct tas_drce_t drce_state;
+};
+
+#define MAKE_TIME(sec,usec) (((sec)<<12) + (50000+(usec/10)*(1<<12))/100000)
+
+#define MAKE_RATIO(i,f) (((i)<<8) + ((500+(f)*(1<<8))/1000))
+
+
+static const union tas_biquad_t
+tas3004_eq_unity={
+ buf: { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 }
+};
+
+
+static const struct tas_drce_t
+tas3004_drce_min={
+ enable: 1,
+ above: { val: MAKE_RATIO(16,0), expand:0 },
+ below: { val: MAKE_RATIO(2,0), expand:0 },
+ threshold: -0x59a0,
+ energy: MAKE_TIME(0, 1700),
+ attack: MAKE_TIME(0, 1700),
+ decay: MAKE_TIME(0, 1700)
+};
+
+
+static const struct tas_drce_t
+tas3004_drce_max={
+ enable: 1,
+ above: { val: MAKE_RATIO(1,500), expand:1 },
+ below: { val: MAKE_RATIO(2,0), expand:1 },
+ threshold: -0x0,
+ energy: MAKE_TIME(2,400000),
+ attack: MAKE_TIME(2,400000),
+ decay: MAKE_TIME(2,400000)
+};
+
+
+static const unsigned short time_constants[]={
+ MAKE_TIME(0, 1700),
+ MAKE_TIME(0, 3500),
+ MAKE_TIME(0, 6700),
+ MAKE_TIME(0, 13000),
+ MAKE_TIME(0, 26000),
+ MAKE_TIME(0, 53000),
+ MAKE_TIME(0,106000),
+ MAKE_TIME(0,212000),
+ MAKE_TIME(0,425000),
+ MAKE_TIME(0,850000),
+ MAKE_TIME(1,700000),
+ MAKE_TIME(2,400000),
+};
+
+static const unsigned short above_threshold_compression_ratio[]={
+ MAKE_RATIO( 1, 70),
+ MAKE_RATIO( 1,140),
+ MAKE_RATIO( 1,230),
+ MAKE_RATIO( 1,330),
+ MAKE_RATIO( 1,450),
+ MAKE_RATIO( 1,600),
+ MAKE_RATIO( 1,780),
+ MAKE_RATIO( 2, 0),
+ MAKE_RATIO( 2,290),
+ MAKE_RATIO( 2,670),
+ MAKE_RATIO( 3,200),
+ MAKE_RATIO( 4, 0),
+ MAKE_RATIO( 5,330),
+ MAKE_RATIO( 8, 0),
+ MAKE_RATIO(16, 0),
+};
+
+static const unsigned short above_threshold_expansion_ratio[]={
+ MAKE_RATIO(1, 60),
+ MAKE_RATIO(1,130),
+ MAKE_RATIO(1,190),
+ MAKE_RATIO(1,250),
+ MAKE_RATIO(1,310),
+ MAKE_RATIO(1,380),
+ MAKE_RATIO(1,440),
+ MAKE_RATIO(1,500)
+};
+
+static const unsigned short below_threshold_compression_ratio[]={
+ MAKE_RATIO(1, 70),
+ MAKE_RATIO(1,140),
+ MAKE_RATIO(1,230),
+ MAKE_RATIO(1,330),
+ MAKE_RATIO(1,450),
+ MAKE_RATIO(1,600),
+ MAKE_RATIO(1,780),
+ MAKE_RATIO(2, 0)
+};
+
+static const unsigned short below_threshold_expansion_ratio[]={
+ MAKE_RATIO(1, 60),
+ MAKE_RATIO(1,130),
+ MAKE_RATIO(1,190),
+ MAKE_RATIO(1,250),
+ MAKE_RATIO(1,310),
+ MAKE_RATIO(1,380),
+ MAKE_RATIO(1,440),
+ MAKE_RATIO(1,500),
+ MAKE_RATIO(1,560),
+ MAKE_RATIO(1,630),
+ MAKE_RATIO(1,690),
+ MAKE_RATIO(1,750),
+ MAKE_RATIO(1,810),
+ MAKE_RATIO(1,880),
+ MAKE_RATIO(1,940),
+ MAKE_RATIO(2, 0)
+};
+
+static inline int
+search( unsigned short val,
+ const unsigned short *arr,
+ const int arrsize) {
+ /*
+ * This could be a binary search, but for small tables,
+ * a linear search is likely to be faster
+ */
+
+ int i;
+
+ for (i=0; i < arrsize; i++)
+ if (arr[i] >= val)
+ goto _1;
+ return arrsize-1;
+ _1:
+ if (i == 0)
+ return 0;
+ return (arr[i]-val < val-arr[i-1]) ? i : i-1;
+}
+
+#define SEARCH(a, b) search(a, b, ARRAY_SIZE(b))
+
+static inline int
+time_index(unsigned short time) {
+ return SEARCH(time, time_constants);
+}
+
+
+static inline int
+above_threshold_compression_index(unsigned short ratio) {
+ return SEARCH(ratio, above_threshold_compression_ratio);
+}
+
+
+static inline int
+above_threshold_expansion_index(unsigned short ratio) {
+ return SEARCH(ratio, above_threshold_expansion_ratio);
+}
+
+
+static inline int
+below_threshold_compression_index(unsigned short ratio) {
+ return SEARCH(ratio, below_threshold_compression_ratio);
+}
+
+
+static inline int
+below_threshold_expansion_index(unsigned short ratio) {
+ return SEARCH(ratio, below_threshold_expansion_ratio);
+}
+
+
+static inline unsigned char db_to_regval(short db) {
+ int r=0;
+
+ r=(db+0x59a0) / 0x60;
+
+ if (r < 0x91) return 0x91;
+ if (r > 0xef) return 0xef;
+ return r;
+}
+
+
+static inline short quantize_db(short db) {
+ return db_to_regval(db) * 0x60 - 0x59a0;
+}
+
+
+static inline int
+register_width(enum tas3004_reg_t r)
+{
+ switch(r) {
+ case TAS3004_REG_MCR:
+ case TAS3004_REG_TREBLE:
+ case TAS3004_REG_BASS:
+ case TAS3004_REG_ANALOG_CTRL:
+ case TAS3004_REG_TEST1:
+ case TAS3004_REG_TEST2:
+ case TAS3004_REG_MCR2:
+ return 1;
+
+ case TAS3004_REG_LEFT_LOUD_BIQUAD_GAIN:
+ case TAS3004_REG_RIGHT_LOUD_BIQUAD_GAIN:
+ return 3;
+
+ case TAS3004_REG_DRC:
+ case TAS3004_REG_VOLUME:
+ return 6;
+
+ case TAS3004_REG_LEFT_MIXER:
+ case TAS3004_REG_RIGHT_MIXER:
+ return 9;
+
+ case TAS3004_REG_TEST:
+ return 10;
+
+ case TAS3004_REG_LEFT_BIQUAD0:
+ case TAS3004_REG_LEFT_BIQUAD1:
+ case TAS3004_REG_LEFT_BIQUAD2:
+ case TAS3004_REG_LEFT_BIQUAD3:
+ case TAS3004_REG_LEFT_BIQUAD4:
+ case TAS3004_REG_LEFT_BIQUAD5:
+ case TAS3004_REG_LEFT_BIQUAD6:
+
+ case TAS3004_REG_RIGHT_BIQUAD0:
+ case TAS3004_REG_RIGHT_BIQUAD1:
+ case TAS3004_REG_RIGHT_BIQUAD2:
+ case TAS3004_REG_RIGHT_BIQUAD3:
+ case TAS3004_REG_RIGHT_BIQUAD4:
+ case TAS3004_REG_RIGHT_BIQUAD5:
+ case TAS3004_REG_RIGHT_BIQUAD6:
+
+ case TAS3004_REG_LEFT_LOUD_BIQUAD:
+ case TAS3004_REG_RIGHT_LOUD_BIQUAD:
+ return 15;
+
+ default:
+ return 0;
+ }
+}
+
+
+static int
+tas3004_write_register( struct tas3004_data_t *self,
+ enum tas3004_reg_t reg_num,
+ char *data,
+ uint write_mode)
+{
+ if (reg_num==TAS3004_REG_MCR ||
+ reg_num==TAS3004_REG_BASS ||
+ reg_num==TAS3004_REG_TREBLE) {
+ return tas_write_byte_register(&self->super,
+ (uint)reg_num,
+ *data,
+ write_mode);
+ } else {
+ return tas_write_register(&self->super,
+ (uint)reg_num,
+ register_width(reg_num),
+ data,
+ write_mode);
+ }
+}
+
+
+static int
+tas3004_sync_register( struct tas3004_data_t *self,
+ enum tas3004_reg_t reg_num)
+{
+ if (reg_num==TAS3004_REG_MCR ||
+ reg_num==TAS3004_REG_BASS ||
+ reg_num==TAS3004_REG_TREBLE) {
+ return tas_sync_byte_register(&self->super,
+ (uint)reg_num,
+ register_width(reg_num));
+ } else {
+ return tas_sync_register(&self->super,
+ (uint)reg_num,
+ register_width(reg_num));
+ }
+}
+
+
+static int
+tas3004_read_register( struct tas3004_data_t *self,
+ enum tas3004_reg_t reg_num,
+ char *data,
+ uint write_mode)
+{
+ return tas_read_register(&self->super,
+ (uint)reg_num,
+ register_width(reg_num),
+ data);
+}
+
+
+static inline int
+tas3004_fast_load(struct tas3004_data_t *self, int fast)
+{
+ if (fast)
+ self->super.shadow[TAS3004_REG_MCR][0] |= 0x80;
+ else
+ self->super.shadow[TAS3004_REG_MCR][0] &= 0x7f;
+ return tas3004_sync_register(self,TAS3004_REG_MCR);
+}
+
+
+static uint
+tas3004_supported_mixers(struct tas3004_data_t *self)
+{
+ return SOUND_MASK_VOLUME |
+ SOUND_MASK_PCM |
+ SOUND_MASK_ALTPCM |
+ SOUND_MASK_IMIX |
+ SOUND_MASK_TREBLE |
+ SOUND_MASK_BASS;
+}
+
+
+static int
+tas3004_mixer_is_stereo(struct tas3004_data_t *self, int mixer)
+{
+ switch(mixer) {
+ case SOUND_MIXER_VOLUME:
+ case SOUND_MIXER_PCM:
+ case SOUND_MIXER_ALTPCM:
+ case SOUND_MIXER_IMIX:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+static uint
+tas3004_stereo_mixers(struct tas3004_data_t *self)
+{
+ uint r = tas3004_supported_mixers(self);
+ uint i;
+
+ for (i=1; isuper.mixer[mixer];
+
+ return 0;
+}
+
+
+static int
+tas3004_set_mixer_level(struct tas3004_data_t *self, int mixer, uint level)
+{
+ int rc;
+ tas_shadow_t *shadow;
+ uint temp;
+ uint offset=0;
+
+ if (!self)
+ return -1;
+
+ shadow = self->super.shadow;
+
+ if (!tas3004_mixer_is_stereo(self,mixer))
+ level = tas_mono_to_stereo(level);
+ switch(mixer) {
+ case SOUND_MIXER_VOLUME:
+ temp = tas3004_gain.master[level&0xff];
+ SET_4_20(shadow[TAS3004_REG_VOLUME], 0, temp);
+ temp = tas3004_gain.master[(level>>8)&0xff];
+ SET_4_20(shadow[TAS3004_REG_VOLUME], 3, temp);
+ rc = tas3004_sync_register(self,TAS3004_REG_VOLUME);
+ break;
+ case SOUND_MIXER_IMIX:
+ offset += 3;
+ case SOUND_MIXER_ALTPCM:
+ offset += 3;
+ case SOUND_MIXER_PCM:
+ /*
+ * Don't load these in fast mode. The documentation
+ * says it can be done in either mode, but testing it
+ * shows that fast mode produces ugly clicking.
+ */
+ /* tas3004_fast_load(self,1); */
+ temp = tas3004_gain.mixer[level&0xff];
+ SET_4_20(shadow[TAS3004_REG_LEFT_MIXER], offset, temp);
+ temp = tas3004_gain.mixer[(level>>8)&0xff];
+ SET_4_20(shadow[TAS3004_REG_RIGHT_MIXER], offset, temp);
+ rc = tas3004_sync_register(self,TAS3004_REG_LEFT_MIXER);
+ if (rc == 0)
+ rc=tas3004_sync_register(self,TAS3004_REG_RIGHT_MIXER);
+ /* tas3004_fast_load(self,0); */
+ break;
+ case SOUND_MIXER_TREBLE:
+ temp = tas3004_gain.treble[level&0xff];
+ shadow[TAS3004_REG_TREBLE][0]=temp&0xff;
+ rc = tas3004_sync_register(self,TAS3004_REG_TREBLE);
+ break;
+ case SOUND_MIXER_BASS:
+ temp = tas3004_gain.bass[level&0xff];
+ shadow[TAS3004_REG_BASS][0]=temp&0xff;
+ rc = tas3004_sync_register(self,TAS3004_REG_BASS);
+ break;
+ default:
+ rc = -1;
+ break;
+ }
+ if (rc < 0)
+ return rc;
+ self->super.mixer[mixer] = level;
+
+ return 0;
+}
+
+
+static int
+tas3004_leave_sleep(struct tas3004_data_t *self)
+{
+ unsigned char mcr = (1<<6)+(2<<4)+(2<<2);
+
+ if (!self)
+ return -1;
+
+ /* Make sure something answers on the i2c bus */
+ if (tas3004_write_register(self, TAS3004_REG_MCR, &mcr,
+ WRITE_NORMAL | FORCE_WRITE) < 0)
+ return -1;
+
+ tas3004_fast_load(self, 1);
+
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD0);
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD1);
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD2);
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD3);
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD4);
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD5);
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD6);
+
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD0);
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD1);
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD2);
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD3);
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD4);
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD5);
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD6);
+
+ tas3004_fast_load(self, 0);
+
+ (void)tas3004_sync_register(self,TAS3004_REG_VOLUME);
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_MIXER);
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_MIXER);
+ (void)tas3004_sync_register(self,TAS3004_REG_TREBLE);
+ (void)tas3004_sync_register(self,TAS3004_REG_BASS);
+
+ return 0;
+}
+
+
+static int
+tas3004_enter_sleep(struct tas3004_data_t *self)
+{
+ if (!self)
+ return -1;
+ return 0;
+}
+
+
+static int
+tas3004_sync_biquad( struct tas3004_data_t *self,
+ u_int channel,
+ u_int filter)
+{
+ enum tas3004_reg_t reg;
+
+ if (channel >= TAS3004_BIQUAD_CHANNEL_COUNT ||
+ filter >= TAS3004_BIQUAD_FILTER_COUNT) return -EINVAL;
+
+ reg=( channel ? TAS3004_REG_RIGHT_BIQUAD0 : TAS3004_REG_LEFT_BIQUAD0 ) + filter;
+
+ return tas3004_sync_register(self,reg);
+}
+
+
+static int
+tas3004_write_biquad_shadow( struct tas3004_data_t *self,
+ u_int channel,
+ u_int filter,
+ const union tas_biquad_t *biquad)
+{
+ tas_shadow_t *shadow=self->super.shadow;
+ enum tas3004_reg_t reg;
+
+ if (channel >= TAS3004_BIQUAD_CHANNEL_COUNT ||
+ filter >= TAS3004_BIQUAD_FILTER_COUNT) return -EINVAL;
+
+ reg=( channel ? TAS3004_REG_RIGHT_BIQUAD0 : TAS3004_REG_LEFT_BIQUAD0 ) + filter;
+
+ SET_4_20(shadow[reg], 0,biquad->coeff.b0);
+ SET_4_20(shadow[reg], 3,biquad->coeff.b1);
+ SET_4_20(shadow[reg], 6,biquad->coeff.b2);
+ SET_4_20(shadow[reg], 9,biquad->coeff.a1);
+ SET_4_20(shadow[reg],12,biquad->coeff.a2);
+
+ return 0;
+}
+
+
+static int
+tas3004_write_biquad( struct tas3004_data_t *self,
+ u_int channel,
+ u_int filter,
+ const union tas_biquad_t *biquad)
+{
+ int rc;
+
+ rc=tas3004_write_biquad_shadow(self, channel, filter, biquad);
+ if (rc < 0) return rc;
+
+ return tas3004_sync_biquad(self, channel, filter);
+}
+
+
+static int
+tas3004_write_biquad_list( struct tas3004_data_t *self,
+ u_int filter_count,
+ u_int flags,
+ struct tas_biquad_ctrl_t *biquads)
+{
+ int i;
+ int rc;
+
+ if (flags & TAS_BIQUAD_FAST_LOAD) tas3004_fast_load(self,1);
+
+ for (i=0; isuper.shadow;
+ enum tas3004_reg_t reg;
+
+ if (channel >= TAS3004_BIQUAD_CHANNEL_COUNT ||
+ filter >= TAS3004_BIQUAD_FILTER_COUNT) return -EINVAL;
+
+ reg=( channel ? TAS3004_REG_RIGHT_BIQUAD0 : TAS3004_REG_LEFT_BIQUAD0 ) + filter;
+
+ biquad->coeff.b0=GET_4_20(shadow[reg], 0);
+ biquad->coeff.b1=GET_4_20(shadow[reg], 3);
+ biquad->coeff.b2=GET_4_20(shadow[reg], 6);
+ biquad->coeff.a1=GET_4_20(shadow[reg], 9);
+ biquad->coeff.a2=GET_4_20(shadow[reg],12);
+
+ return 0;
+}
+
+
+static int
+tas3004_eq_rw( struct tas3004_data_t *self,
+ u_int cmd,
+ u_long arg)
+{
+ int rc;
+ struct tas_biquad_ctrl_t biquad;
+
+ if (copy_from_user((void *)&biquad, (const void *)arg, sizeof(struct tas_biquad_ctrl_t))) {
+ return -EFAULT;
+ }
+
+ if (cmd & SIOC_IN) {
+ rc=tas3004_write_biquad(self, biquad.channel, biquad.filter, &biquad.data);
+ if (rc != 0) return rc;
+ }
+
+ if (cmd & SIOC_OUT) {
+ rc=tas3004_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
+ if (rc != 0) return rc;
+
+ if (copy_to_user((void *)arg, (const void *)&biquad, sizeof(struct tas_biquad_ctrl_t))) {
+ return -EFAULT;
+ }
+
+ }
+ return 0;
+}
+
+
+static int
+tas3004_eq_list_rw( struct tas3004_data_t *self,
+ u_int cmd,
+ u_long arg)
+{
+ int rc = 0;
+ int filter_count;
+ int flags;
+ int i,j;
+ char sync_required[TAS3004_BIQUAD_CHANNEL_COUNT][TAS3004_BIQUAD_FILTER_COUNT];
+ struct tas_biquad_ctrl_t biquad;
+
+ memset(sync_required,0,sizeof(sync_required));
+
+ if (copy_from_user((void *)&filter_count,
+ (const void *)arg + offsetof(struct tas_biquad_ctrl_list_t,filter_count),
+ sizeof(int))) {
+ return -EFAULT;
+ }
+
+ if (copy_from_user((void *)&flags,
+ (const void *)arg + offsetof(struct tas_biquad_ctrl_list_t,flags),
+ sizeof(int))) {
+ return -EFAULT;
+ }
+
+ if (cmd & SIOC_IN) {
+ }
+
+ for (i=0; i < filter_count; i++) {
+ if (copy_from_user((void *)&biquad,
+ (const void *)arg + offsetof(struct tas_biquad_ctrl_list_t, biquads[i]),
+ sizeof(struct tas_biquad_ctrl_t))) {
+ return -EFAULT;
+ }
+
+ if (cmd & SIOC_IN) {
+ sync_required[biquad.channel][biquad.filter]=1;
+ rc=tas3004_write_biquad_shadow(self, biquad.channel, biquad.filter, &biquad.data);
+ if (rc != 0) return rc;
+ }
+
+ if (cmd & SIOC_OUT) {
+ rc=tas3004_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
+ if (rc != 0) return rc;
+
+ if (copy_to_user((void *)arg + offsetof(struct tas_biquad_ctrl_list_t, biquads[i]),
+ (const void *)&biquad,
+ sizeof(struct tas_biquad_ctrl_t))) {
+ return -EFAULT;
+ }
+ }
+ }
+
+ if (cmd & SIOC_IN) {
+ /*
+ * This is OK for the tas3004. For the
+ * tas3001c, going into fast load mode causes
+ * the treble and bass to be reset to 0dB, and
+ * volume controls to be muted.
+ */
+ if (flags & TAS_BIQUAD_FAST_LOAD) tas3004_fast_load(self,1);
+ for (i=0; isuper.shadow;
+
+ if (flags & TAS_DRCE_ABOVE_RATIO) {
+ self->drce_state.above.expand = drce->above.expand;
+ if (drce->above.val == (1<<8)) {
+ self->drce_state.above.val = 1<<8;
+ shadow[TAS3004_REG_DRC][0] = 0x02;
+
+ } else if (drce->above.expand) {
+ i=above_threshold_expansion_index(drce->above.val);
+ self->drce_state.above.val=above_threshold_expansion_ratio[i];
+ shadow[TAS3004_REG_DRC][0] = 0x0a + (i<<3);
+ } else {
+ i=above_threshold_compression_index(drce->above.val);
+ self->drce_state.above.val=above_threshold_compression_ratio[i];
+ shadow[TAS3004_REG_DRC][0] = 0x08 + (i<<3);
+ }
+ }
+
+ if (flags & TAS_DRCE_BELOW_RATIO) {
+ self->drce_state.below.expand = drce->below.expand;
+ if (drce->below.val == (1<<8)) {
+ self->drce_state.below.val = 1<<8;
+ shadow[TAS3004_REG_DRC][1] = 0x02;
+
+ } else if (drce->below.expand) {
+ i=below_threshold_expansion_index(drce->below.val);
+ self->drce_state.below.val=below_threshold_expansion_ratio[i];
+ shadow[TAS3004_REG_DRC][1] = 0x08 + (i<<3);
+ } else {
+ i=below_threshold_compression_index(drce->below.val);
+ self->drce_state.below.val=below_threshold_compression_ratio[i];
+ shadow[TAS3004_REG_DRC][1] = 0x0a + (i<<3);
+ }
+ }
+
+ if (flags & TAS_DRCE_THRESHOLD) {
+ self->drce_state.threshold=quantize_db(drce->threshold);
+ shadow[TAS3004_REG_DRC][2] = db_to_regval(self->drce_state.threshold);
+ }
+
+ if (flags & TAS_DRCE_ENERGY) {
+ i=time_index(drce->energy);
+ self->drce_state.energy=time_constants[i];
+ shadow[TAS3004_REG_DRC][3] = 0x40 + (i<<4);
+ }
+
+ if (flags & TAS_DRCE_ATTACK) {
+ i=time_index(drce->attack);
+ self->drce_state.attack=time_constants[i];
+ shadow[TAS3004_REG_DRC][4] = 0x40 + (i<<4);
+ }
+
+ if (flags & TAS_DRCE_DECAY) {
+ i=time_index(drce->decay);
+ self->drce_state.decay=time_constants[i];
+ shadow[TAS3004_REG_DRC][5] = 0x40 + (i<<4);
+ }
+
+ if (flags & TAS_DRCE_ENABLE) {
+ self->drce_state.enable = drce->enable;
+ }
+
+ if (!self->drce_state.enable) {
+ shadow[TAS3004_REG_DRC][0] |= 0x01;
+ }
+
+#ifdef DEBUG_DRCE
+ printk("DRCE: set [ ENABLE:%x ABOVE:%x/%x BELOW:%x/%x THRESH:%x ENERGY:%x ATTACK:%x DECAY:%x\n",
+ self->drce_state.enable,
+ self->drce_state.above.expand,self->drce_state.above.val,
+ self->drce_state.below.expand,self->drce_state.below.val,
+ self->drce_state.threshold,
+ self->drce_state.energy,
+ self->drce_state.attack,
+ self->drce_state.decay);
+
+ printk("DRCE: reg [ %02x %02x %02x %02x %02x %02x ]\n",
+ (unsigned char)shadow[TAS3004_REG_DRC][0],
+ (unsigned char)shadow[TAS3004_REG_DRC][1],
+ (unsigned char)shadow[TAS3004_REG_DRC][2],
+ (unsigned char)shadow[TAS3004_REG_DRC][3],
+ (unsigned char)shadow[TAS3004_REG_DRC][4],
+ (unsigned char)shadow[TAS3004_REG_DRC][5]);
+#endif
+
+ return tas3004_sync_register(self, TAS3004_REG_DRC);
+}
+
+
+static int
+tas3004_drce_rw( struct tas3004_data_t *self,
+ u_int cmd,
+ u_long arg)
+{
+ int rc;
+ struct tas_drce_ctrl_t drce_ctrl;
+
+ if (copy_from_user((void *)&drce_ctrl,
+ (const void *)arg,
+ sizeof(struct tas_drce_ctrl_t))) {
+ return -EFAULT;
+ }
+
+#ifdef DEBUG_DRCE
+ printk("DRCE: input [ FLAGS:%x ENABLE:%x ABOVE:%x/%x BELOW:%x/%x THRESH:%x ENERGY:%x ATTACK:%x DECAY:%x\n",
+ drce_ctrl.flags,
+ drce_ctrl.data.enable,
+ drce_ctrl.data.above.expand,drce_ctrl.data.above.val,
+ drce_ctrl.data.below.expand,drce_ctrl.data.below.val,
+ drce_ctrl.data.threshold,
+ drce_ctrl.data.energy,
+ drce_ctrl.data.attack,
+ drce_ctrl.data.decay);
+#endif
+
+ if (cmd & SIOC_IN) {
+ rc = tas3004_update_drce(self, drce_ctrl.flags, &drce_ctrl.data);
+ if (rc < 0) return rc;
+ }
+
+ if (cmd & SIOC_OUT) {
+ if (drce_ctrl.flags & TAS_DRCE_ENABLE)
+ drce_ctrl.data.enable = self->drce_state.enable;
+ if (drce_ctrl.flags & TAS_DRCE_ABOVE_RATIO)
+ drce_ctrl.data.above = self->drce_state.above;
+ if (drce_ctrl.flags & TAS_DRCE_BELOW_RATIO)
+ drce_ctrl.data.below = self->drce_state.below;
+ if (drce_ctrl.flags & TAS_DRCE_THRESHOLD)
+ drce_ctrl.data.threshold = self->drce_state.threshold;
+ if (drce_ctrl.flags & TAS_DRCE_ENERGY)
+ drce_ctrl.data.energy = self->drce_state.energy;
+ if (drce_ctrl.flags & TAS_DRCE_ATTACK)
+ drce_ctrl.data.attack = self->drce_state.attack;
+ if (drce_ctrl.flags & TAS_DRCE_DECAY)
+ drce_ctrl.data.decay = self->drce_state.decay;
+
+ if (copy_to_user((void *)arg,
+ (const void *)&drce_ctrl,
+ sizeof(struct tas_drce_ctrl_t))) {
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+
+static void
+tas3004_update_device_parameters(struct tas3004_data_t *self)
+{
+ char data;
+ int i;
+
+ if (!self) return;
+
+ if (self->output_id == TAS_OUTPUT_HEADPHONES) {
+ /* turn on allPass when headphones are plugged in */
+ data = 0x02;
+ } else {
+ data = 0x00;
+ }
+
+ tas3004_write_register(self, TAS3004_REG_MCR2, &data, WRITE_NORMAL | FORCE_WRITE);
+
+ for (i=0; tas3004_eq_prefs[i]; i++) {
+ struct tas_eq_pref_t *eq = tas3004_eq_prefs[i];
+
+ if (eq->device_id == self->device_id &&
+ (eq->output_id == 0 || eq->output_id == self->output_id) &&
+ (eq->speaker_id == 0 || eq->speaker_id == self->speaker_id)) {
+
+ tas3004_update_drce(self, TAS_DRCE_ALL, eq->drce);
+ tas3004_write_biquad_list(self, eq->filter_count, TAS_BIQUAD_FAST_LOAD, eq->biquads);
+
+ break;
+ }
+ }
+}
+
+static void
+tas3004_device_change_handler(void *self)
+{
+ if (!self) return;
+
+ tas3004_update_device_parameters((struct tas3004_data_t *)self);
+}
+
+static struct tq_struct device_change_task;
+
+static int
+tas3004_output_device_change( struct tas3004_data_t *self,
+ int device_id,
+ int output_id,
+ int speaker_id)
+{
+ self->device_id=device_id;
+ self->output_id=output_id;
+ self->speaker_id=speaker_id;
+
+ schedule_task(&device_change_task);
+
+ return 0;
+}
+
+
+static int
+tas3004_device_ioctl( struct tas3004_data_t *self,
+ u_int cmd,
+ u_long arg)
+{
+ switch (cmd) {
+ case TAS_READ_EQ:
+ case TAS_WRITE_EQ:
+ return tas3004_eq_rw(self, cmd, arg);
+
+ case TAS_READ_EQ_LIST:
+ case TAS_WRITE_EQ_LIST:
+ return tas3004_eq_list_rw(self, cmd, arg);
+
+ case TAS_READ_EQ_FILTER_COUNT:
+ put_user(TAS3004_BIQUAD_FILTER_COUNT, (uint *)(arg));
+ return 0;
+
+ case TAS_READ_EQ_CHANNEL_COUNT:
+ put_user(TAS3004_BIQUAD_CHANNEL_COUNT, (uint *)(arg));
+ return 0;
+
+ case TAS_READ_DRCE:
+ case TAS_WRITE_DRCE:
+ return tas3004_drce_rw(self, cmd, arg);
+
+ case TAS_READ_DRCE_CAPS:
+ put_user(TAS_DRCE_ENABLE |
+ TAS_DRCE_ABOVE_RATIO |
+ TAS_DRCE_BELOW_RATIO |
+ TAS_DRCE_THRESHOLD |
+ TAS_DRCE_ENERGY |
+ TAS_DRCE_ATTACK |
+ TAS_DRCE_DECAY,
+ (uint *)(arg));
+ return 0;
+
+ case TAS_READ_DRCE_MIN:
+ case TAS_READ_DRCE_MAX: {
+ struct tas_drce_ctrl_t drce_ctrl;
+ const struct tas_drce_t *drce_copy;
+
+ if (copy_from_user((void *)&drce_ctrl,
+ (const void *)arg,
+ sizeof(struct tas_drce_ctrl_t))) {
+ return -EFAULT;
+ }
+
+ if (cmd == TAS_READ_DRCE_MIN) {
+ drce_copy=&tas3004_drce_min;
+ } else {
+ drce_copy=&tas3004_drce_max;
+ }
+
+ if (drce_ctrl.flags & TAS_DRCE_ABOVE_RATIO) {
+ drce_ctrl.data.above=drce_copy->above;
+ }
+ if (drce_ctrl.flags & TAS_DRCE_BELOW_RATIO) {
+ drce_ctrl.data.below=drce_copy->below;
+ }
+ if (drce_ctrl.flags & TAS_DRCE_THRESHOLD) {
+ drce_ctrl.data.threshold=drce_copy->threshold;
+ }
+ if (drce_ctrl.flags & TAS_DRCE_ENERGY) {
+ drce_ctrl.data.energy=drce_copy->energy;
+ }
+ if (drce_ctrl.flags & TAS_DRCE_ATTACK) {
+ drce_ctrl.data.attack=drce_copy->attack;
+ }
+ if (drce_ctrl.flags & TAS_DRCE_DECAY) {
+ drce_ctrl.data.decay=drce_copy->decay;
+ }
+
+ if (copy_to_user((void *)arg,
+ (const void *)&drce_ctrl,
+ sizeof(struct tas_drce_ctrl_t))) {
+ return -EFAULT;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+
+static int
+tas3004_init_mixer(struct tas3004_data_t *self)
+{
+ unsigned char mcr = (1<<6)+(2<<4)+(2<<2);
+
+ /* Make sure something answers on the i2c bus */
+ if (tas3004_write_register(self, TAS3004_REG_MCR, &mcr,
+ WRITE_NORMAL | FORCE_WRITE) < 0)
+ return -1;
+
+ tas3004_fast_load(self, 1);
+
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD0);
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD1);
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD2);
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD3);
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD4);
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD5);
+ (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD6);
+
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD0);
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD1);
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD2);
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD3);
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD4);
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD5);
+ (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD6);
+
+ tas3004_sync_register(self, TAS3004_REG_DRC);
+
+ tas3004_sync_register(self, TAS3004_REG_MCR2);
+
+ tas3004_fast_load(self, 0);
+
+ tas3004_set_mixer_level(self, SOUND_MIXER_VOLUME, VOL_DEFAULT<<8 | VOL_DEFAULT);
+ tas3004_set_mixer_level(self, SOUND_MIXER_PCM, INPUT_DEFAULT<<8 | INPUT_DEFAULT);
+ tas3004_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0);
+ tas3004_set_mixer_level(self, SOUND_MIXER_IMIX, 0);
+
+ tas3004_set_mixer_level(self, SOUND_MIXER_BASS, BASS_DEFAULT);
+ tas3004_set_mixer_level(self, SOUND_MIXER_TREBLE, TREBLE_DEFAULT);
+
+ return 0;
+}
+
+
+static int
+tas3004_uninit_mixer(struct tas3004_data_t *self)
+{
+ tas3004_set_mixer_level(self, SOUND_MIXER_VOLUME, 0);
+ tas3004_set_mixer_level(self, SOUND_MIXER_PCM, 0);
+ tas3004_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0);
+ tas3004_set_mixer_level(self, SOUND_MIXER_IMIX, 0);
+
+ tas3004_set_mixer_level(self, SOUND_MIXER_BASS, 0);
+ tas3004_set_mixer_level(self, SOUND_MIXER_TREBLE, 0);
+
+ return 0;
+}
+
+
+static int
+tas3004_init(struct i2c_client *client)
+{
+ char drce_init[]={ 0x69, 0x22, 0x9f, 0xb0, 0x60, 0xa0 };
+ char mcr2 = 0;
+
+ struct tas3004_data_t *self;
+ int i, j;
+
+ self = kmalloc(sizeof(struct tas3004_data_t) +
+ TAS3004_REG_MAX * sizeof(tas_shadow_t),
+ GFP_KERNEL);
+ if (self == NULL)
+ return -ENOMEM;
+
+ memset(self,
+ 0,
+ sizeof(struct tas3004_data_t) +
+ TAS3004_REG_MAX * sizeof(tas_shadow_t));
+
+ client->data = (void *)self;
+
+ self->super.client = client;
+ self->super.shadow = (tas_shadow_t *)(self+1);
+
+ self->output_id=TAS_OUTPUT_HEADPHONES;
+ self->device_id=0;
+ self->speaker_id=0;
+
+ for (i=0; i
+ */
+
+#ifndef _TAS3004_H_
+#define _TAS3004_H_
+
+#include
+
+#include "tas_common.h"
+#include "tas_eq_prefs.h"
+
+/*
+ * Macros that correspond to the registers that we write to
+ * when setting the various values.
+ */
+
+#define TAS3004_VERSION "0.3"
+#define TAS3004_DATE "20011214"
+
+#define I2C_DRIVERNAME_TAS3004 "TAS3004 driver V " TAS3004_VERSION
+#define I2C_DRIVERID_TAS3004 (I2C_DRIVERID_TAS_BASE+1)
+
+extern struct tas_driver_hooks_t tas3004_hooks;
+extern struct tas_gain_t tas3004_gain;
+extern struct tas_eq_pref_t *tas3004_eq_prefs[];
+
+enum tas3004_reg_t {
+ TAS3004_REG_MCR = 0x01,
+ TAS3004_REG_DRC = 0x02,
+
+ TAS3004_REG_VOLUME = 0x04,
+ TAS3004_REG_TREBLE = 0x05,
+ TAS3004_REG_BASS = 0x06,
+ TAS3004_REG_LEFT_MIXER = 0x07,
+ TAS3004_REG_RIGHT_MIXER = 0x08,
+
+ TAS3004_REG_LEFT_BIQUAD0 = 0x0a,
+ TAS3004_REG_LEFT_BIQUAD1 = 0x0b,
+ TAS3004_REG_LEFT_BIQUAD2 = 0x0c,
+ TAS3004_REG_LEFT_BIQUAD3 = 0x0d,
+ TAS3004_REG_LEFT_BIQUAD4 = 0x0e,
+ TAS3004_REG_LEFT_BIQUAD5 = 0x0f,
+ TAS3004_REG_LEFT_BIQUAD6 = 0x10,
+
+ TAS3004_REG_RIGHT_BIQUAD0 = 0x13,
+ TAS3004_REG_RIGHT_BIQUAD1 = 0x14,
+ TAS3004_REG_RIGHT_BIQUAD2 = 0x15,
+ TAS3004_REG_RIGHT_BIQUAD3 = 0x16,
+ TAS3004_REG_RIGHT_BIQUAD4 = 0x17,
+ TAS3004_REG_RIGHT_BIQUAD5 = 0x18,
+ TAS3004_REG_RIGHT_BIQUAD6 = 0x19,
+
+ TAS3004_REG_LEFT_LOUD_BIQUAD = 0x21,
+ TAS3004_REG_RIGHT_LOUD_BIQUAD = 0x22,
+
+ TAS3004_REG_LEFT_LOUD_BIQUAD_GAIN = 0x23,
+ TAS3004_REG_RIGHT_LOUD_BIQUAD_GAIN = 0x24,
+
+ TAS3004_REG_TEST = 0x29,
+
+ TAS3004_REG_ANALOG_CTRL = 0x40,
+ TAS3004_REG_TEST1 = 0x41,
+ TAS3004_REG_TEST2 = 0x42,
+ TAS3004_REG_MCR2 = 0x43,
+
+ TAS3004_REG_MAX = 0x44
+};
+
+#endif /* _TAS3004_H_ */
diff -uNr linux-2.4.22/drivers/sound/dmasound/tas3004_tables.c linux-2.4.22-ben2/drivers/sound/dmasound/tas3004_tables.c
--- linux-2.4.22/drivers/sound/dmasound/tas3004_tables.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/drivers/sound/dmasound/tas3004_tables.c 2003-08-31 11:40:02.000000000 +0200
@@ -0,0 +1,301 @@
+#include "tas3004.h"
+#include "tas_eq_prefs.h"
+
+static struct tas_drce_t eqp_17_1_0_drce={
+ enable: 1,
+ above: { val: 3.0 * (1<<8), expand: 0 },
+ below: { val: 1.0 * (1<<8), expand: 0 },
+ threshold: -19.12 * (1<<8),
+ energy: 2.4 * (1<<12),
+ attack: 0.013 * (1<<12),
+ decay: 0.212 * (1<<12),
+};
+
+static struct tas_biquad_ctrl_t eqp_17_1_0_biquads[]={
+ { channel: 0, filter: 0, data: { coeff: { 0x0fd0d4, 0xe05e56, 0x0fd0d4, 0xe05ee1, 0x0fa234 } } },
+ { channel: 0, filter: 1, data: { coeff: { 0x0910d7, 0x088e1a, 0x030651, 0x01dcb1, 0x02c892 } } },
+ { channel: 0, filter: 2, data: { coeff: { 0x0ff895, 0xe0970b, 0x0f7f00, 0xe0970b, 0x0f7795 } } },
+ { channel: 0, filter: 3, data: { coeff: { 0x0fd1c4, 0xe1ac22, 0x0ec8cf, 0xe1ac22, 0x0e9a94 } } },
+ { channel: 0, filter: 4, data: { coeff: { 0x0f7c1c, 0xe3cc03, 0x0df786, 0xe3cc03, 0x0d73a2 } } },
+ { channel: 0, filter: 5, data: { coeff: { 0x11fb92, 0xf5a1a0, 0x073cd2, 0xf5a1a0, 0x093865 } } },
+ { channel: 0, filter: 6, data: { coeff: { 0x0e17a9, 0x068b6c, 0x08a0e5, 0x068b6c, 0x06b88e } } },
+
+ { channel: 1, filter: 0, data: { coeff: { 0x0fd0d4, 0xe05e56, 0x0fd0d4, 0xe05ee1, 0x0fa234 } } },
+ { channel: 1, filter: 1, data: { coeff: { 0x0910d7, 0x088e1a, 0x030651, 0x01dcb1, 0x02c892 } } },
+ { channel: 1, filter: 2, data: { coeff: { 0x0ff895, 0xe0970b, 0x0f7f00, 0xe0970b, 0x0f7795 } } },
+ { channel: 1, filter: 3, data: { coeff: { 0x0fd1c4, 0xe1ac22, 0x0ec8cf, 0xe1ac22, 0x0e9a94 } } },
+ { channel: 1, filter: 4, data: { coeff: { 0x0f7c1c, 0xe3cc03, 0x0df786, 0xe3cc03, 0x0d73a2 } } },
+ { channel: 1, filter: 5, data: { coeff: { 0x11fb92, 0xf5a1a0, 0x073cd2, 0xf5a1a0, 0x093865 } } },
+ { channel: 1, filter: 6, data: { coeff: { 0x0e17a9, 0x068b6c, 0x08a0e5, 0x068b6c, 0x06b88e } } }
+};
+
+static struct tas_eq_pref_t eqp_17_1_0 = {
+ sample_rate: 44100,
+ device_id: 0x17,
+ output_id: TAS_OUTPUT_INTERNAL_SPKR,
+ speaker_id: 0x00,
+
+ drce: &eqp_17_1_0_drce,
+
+ filter_count: 14,
+ biquads: eqp_17_1_0_biquads
+};
+
+/* ======================================================================== */
+
+static struct tas_drce_t eqp_18_1_0_drce={
+ enable: 1,
+ above: { val: 3.0 * (1<<8), expand: 0 },
+ below: { val: 1.0 * (1<<8), expand: 0 },
+ threshold: -13.14 * (1<<8),
+ energy: 2.4 * (1<<12),
+ attack: 0.013 * (1<<12),
+ decay: 0.212 * (1<<12),
+};
+
+static struct tas_biquad_ctrl_t eqp_18_1_0_biquads[]={
+ { channel: 0, filter: 0, data: { coeff: { 0x0f5514, 0xe155d7, 0x0f5514, 0xe15cfa, 0x0eb14b } } },
+ { channel: 0, filter: 1, data: { coeff: { 0x06ec33, 0x02abe3, 0x015eef, 0xf764d9, 0x03922d } } },
+ { channel: 0, filter: 2, data: { coeff: { 0x0ef5f2, 0xe67d1f, 0x0bcf37, 0xe67d1f, 0x0ac529 } } },
+ { channel: 0, filter: 3, data: { coeff: { 0x0db050, 0xe5be4d, 0x0d0c78, 0xe5be4d, 0x0abcc8 } } },
+ { channel: 0, filter: 4, data: { coeff: { 0x0f1298, 0xe64ec6, 0x0cc03e, 0xe64ec6, 0x0bd2d7 } } },
+ { channel: 0, filter: 5, data: { coeff: { 0x0c641a, 0x06537a, 0x08d155, 0x06537a, 0x053570 } } },
+ { channel: 0, filter: 6, data: { coeff: { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
+
+ { channel: 1, filter: 0, data: { coeff: { 0x0f5514, 0xe155d7, 0x0f5514, 0xe15cfa, 0x0eb14b } } },
+ { channel: 1, filter: 1, data: { coeff: { 0x06ec33, 0x02abe3, 0x015eef, 0xf764d9, 0x03922d } } },
+ { channel: 1, filter: 2, data: { coeff: { 0x0ef5f2, 0xe67d1f, 0x0bcf37, 0xe67d1f, 0x0ac529 } } },
+ { channel: 1, filter: 3, data: { coeff: { 0x0db050, 0xe5be4d, 0x0d0c78, 0xe5be4d, 0x0abcc8 } } },
+ { channel: 1, filter: 4, data: { coeff: { 0x0f1298, 0xe64ec6, 0x0cc03e, 0xe64ec6, 0x0bd2d7 } } },
+ { channel: 1, filter: 5, data: { coeff: { 0x0c641a, 0x06537a, 0x08d155, 0x06537a, 0x053570 } } },
+ { channel: 1, filter: 6, data: { coeff: { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } }
+};
+
+static struct tas_eq_pref_t eqp_18_1_0 = {
+ sample_rate: 44100,
+ device_id: 0x18,
+ output_id: TAS_OUTPUT_INTERNAL_SPKR,
+ speaker_id: 0x00,
+
+ drce: &eqp_18_1_0_drce,
+
+ filter_count: 14,
+ biquads: eqp_18_1_0_biquads
+};
+
+/* ======================================================================== */
+
+static struct tas_drce_t eqp_1a_1_0_drce={
+ enable: 1,
+ above: { val: 3.0 * (1<<8), expand: 0 },
+ below: { val: 1.0 * (1<<8), expand: 0 },
+ threshold: -10.75 * (1<<8),
+ energy: 2.4 * (1<<12),
+ attack: 0.013 * (1<<12),
+ decay: 0.212 * (1<<12),
+};
+
+static struct tas_biquad_ctrl_t eqp_1a_1_0_biquads[]={
+ { channel: 0, filter: 0, data: { coeff: { 0x0fb8fd, 0xe08e04, 0x0fb8fd, 0xe08f40, 0x0f7336 } } },
+ { channel: 0, filter: 1, data: { coeff: { 0x06371d, 0x0c6e3a, 0x06371d, 0x05bfd3, 0x031ca2 } } },
+ { channel: 0, filter: 2, data: { coeff: { 0x0fa1c0, 0xe18692, 0x0f030e, 0xe18692, 0x0ea4ce } } },
+ { channel: 0, filter: 3, data: { coeff: { 0x0fe495, 0xe17eff, 0x0f0452, 0xe17eff, 0x0ee8e7 } } },
+ { channel: 0, filter: 4, data: { coeff: { 0x100857, 0xe7e71c, 0x0e9599, 0xe7e71c, 0x0e9df1 } } },
+ { channel: 0, filter: 5, data: { coeff: { 0x0fb26e, 0x06a82c, 0x0db2b4, 0x06a82c, 0x0d6522 } } },
+ { channel: 0, filter: 6, data: { coeff: { 0x11419d, 0xf06cbf, 0x0a4f6e, 0xf06cbf, 0x0b910c } } },
+
+ { channel: 1, filter: 0, data: { coeff: { 0x0fb8fd, 0xe08e04, 0x0fb8fd, 0xe08f40, 0x0f7336 } } },
+ { channel: 1, filter: 1, data: { coeff: { 0x06371d, 0x0c6e3a, 0x06371d, 0x05bfd3, 0x031ca2 } } },
+ { channel: 1, filter: 2, data: { coeff: { 0x0fa1c0, 0xe18692, 0x0f030e, 0xe18692, 0x0ea4ce } } },
+ { channel: 1, filter: 3, data: { coeff: { 0x0fe495, 0xe17eff, 0x0f0452, 0xe17eff, 0x0ee8e7 } } },
+ { channel: 1, filter: 4, data: { coeff: { 0x100857, 0xe7e71c, 0x0e9599, 0xe7e71c, 0x0e9df1 } } },
+ { channel: 1, filter: 5, data: { coeff: { 0x0fb26e, 0x06a82c, 0x0db2b4, 0x06a82c, 0x0d6522 } } },
+ { channel: 1, filter: 6, data: { coeff: { 0x11419d, 0xf06cbf, 0x0a4f6e, 0xf06cbf, 0x0b910c } } }
+};
+
+static struct tas_eq_pref_t eqp_1a_1_0 = {
+ sample_rate: 44100,
+ device_id: 0x1a,
+ output_id: TAS_OUTPUT_INTERNAL_SPKR,
+ speaker_id: 0x00,
+
+ drce: &eqp_1a_1_0_drce,
+
+ filter_count: 14,
+ biquads: eqp_1a_1_0_biquads
+};
+
+/* ======================================================================== */
+
+static struct tas_drce_t eqp_1c_1_0_drce={
+ enable: 1,
+ above: { val: 3.0 * (1<<8), expand: 0 },
+ below: { val: 1.0 * (1<<8), expand: 0 },
+ threshold: -14.34 * (1<<8),
+ energy: 2.4 * (1<<12),
+ attack: 0.013 * (1<<12),
+ decay: 0.212 * (1<<12),
+};
+
+static struct tas_biquad_ctrl_t eqp_1c_1_0_biquads[]={
+ { channel: 0, filter: 0, data: { coeff: { 0x0f4f95, 0xe160d4, 0x0f4f95, 0xe1686e, 0x0ea6c5 } } },
+ { channel: 0, filter: 1, data: { coeff: { 0x066b92, 0x0290d4, 0x0148a0, 0xf6853f, 0x03bfc7 } } },
+ { channel: 0, filter: 2, data: { coeff: { 0x0f57dc, 0xe51c91, 0x0dd1cb, 0xe51c91, 0x0d29a8 } } },
+ { channel: 0, filter: 3, data: { coeff: { 0x0df1cb, 0xe4fa84, 0x0d7cdc, 0xe4fa84, 0x0b6ea7 } } },
+ { channel: 0, filter: 4, data: { coeff: { 0x0eba36, 0xe6aa48, 0x0b9f52, 0xe6aa48, 0x0a5989 } } },
+ { channel: 0, filter: 5, data: { coeff: { 0x0caf02, 0x05ef9d, 0x084beb, 0x05ef9d, 0x04faee } } },
+ { channel: 0, filter: 6, data: { coeff: { 0x0fc686, 0xe22947, 0x0e4b5d, 0xe22947, 0x0e11e4 } } },
+
+ { channel: 1, filter: 0, data: { coeff: { 0x0f4f95, 0xe160d4, 0x0f4f95, 0xe1686e, 0x0ea6c5 } } },
+ { channel: 1, filter: 1, data: { coeff: { 0x066b92, 0x0290d4, 0x0148a0, 0xf6853f, 0x03bfc7 } } },
+ { channel: 1, filter: 2, data: { coeff: { 0x0f57dc, 0xe51c91, 0x0dd1cb, 0xe51c91, 0x0d29a8 } } },
+ { channel: 1, filter: 3, data: { coeff: { 0x0df1cb, 0xe4fa84, 0x0d7cdc, 0xe4fa84, 0x0b6ea7 } } },
+ { channel: 1, filter: 4, data: { coeff: { 0x0eba36, 0xe6aa48, 0x0b9f52, 0xe6aa48, 0x0a5989 } } },
+ { channel: 1, filter: 5, data: { coeff: { 0x0caf02, 0x05ef9d, 0x084beb, 0x05ef9d, 0x04faee } } },
+ { channel: 1, filter: 6, data: { coeff: { 0x0fc686, 0xe22947, 0x0e4b5d, 0xe22947, 0x0e11e4 } } }
+};
+
+static struct tas_eq_pref_t eqp_1c_1_0 = {
+ sample_rate: 44100,
+ device_id: 0x1c,
+ output_id: TAS_OUTPUT_INTERNAL_SPKR,
+ speaker_id: 0x00,
+
+ drce: &eqp_1c_1_0_drce,
+
+ filter_count: 14,
+ biquads: eqp_1c_1_0_biquads
+};
+
+/* ======================================================================== */
+
+static uint tas3004_master_tab[]={
+ 0x0, 0x75, 0x9c, 0xbb,
+ 0xdb, 0xfb, 0x11e, 0x143,
+ 0x16b, 0x196, 0x1c3, 0x1f5,
+ 0x229, 0x263, 0x29f, 0x2e1,
+ 0x328, 0x373, 0x3c5, 0x41b,
+ 0x478, 0x4dc, 0x547, 0x5b8,
+ 0x633, 0x6b5, 0x740, 0x7d5,
+ 0x873, 0x91c, 0x9d2, 0xa92,
+ 0xb5e, 0xc39, 0xd22, 0xe19,
+ 0xf20, 0x1037, 0x1161, 0x129e,
+ 0x13ed, 0x1551, 0x16ca, 0x185d,
+ 0x1a08, 0x1bcc, 0x1dac, 0x1fa7,
+ 0x21c1, 0x23fa, 0x2655, 0x28d6,
+ 0x2b7c, 0x2e4a, 0x3141, 0x3464,
+ 0x37b4, 0x3b35, 0x3ee9, 0x42d3,
+ 0x46f6, 0x4b53, 0x4ff0, 0x54ce,
+ 0x59f2, 0x5f5f, 0x6519, 0x6b24,
+ 0x7183, 0x783c, 0x7f53, 0x86cc,
+ 0x8ead, 0x96fa, 0x9fba, 0xa8f2,
+ 0xb2a7, 0xbce1, 0xc7a5, 0xd2fa,
+ 0xdee8, 0xeb75, 0xf8aa, 0x1068e,
+ 0x1152a, 0x12487, 0x134ad, 0x145a5,
+ 0x1577b, 0x16a37, 0x17df5, 0x192bd,
+ 0x1a890, 0x1bf7b, 0x1d78d, 0x1f0d1,
+ 0x20b55, 0x22727, 0x24456, 0x262f2,
+ 0x2830b
+};
+
+static uint tas3004_mixer_tab[]={
+ 0x0, 0x748, 0x9be, 0xbaf,
+ 0xda4, 0xfb1, 0x11de, 0x1431,
+ 0x16ad, 0x1959, 0x1c37, 0x1f4b,
+ 0x2298, 0x2628, 0x29fb, 0x2e12,
+ 0x327d, 0x3734, 0x3c47, 0x41b4,
+ 0x4787, 0x4dbe, 0x546d, 0x5b86,
+ 0x632e, 0x6b52, 0x7400, 0x7d54,
+ 0x873b, 0x91c6, 0x9d1a, 0xa920,
+ 0xb5e5, 0xc38c, 0xd21b, 0xe18f,
+ 0xf1f5, 0x1036a, 0x1160f, 0x129d6,
+ 0x13ed0, 0x1550c, 0x16ca0, 0x185c9,
+ 0x1a07b, 0x1bcc3, 0x1dab9, 0x1fa75,
+ 0x21c0f, 0x23fa3, 0x26552, 0x28d64,
+ 0x2b7c9, 0x2e4a2, 0x31411, 0x3463b,
+ 0x37b44, 0x3b353, 0x3ee94, 0x42d30,
+ 0x46f55, 0x4b533, 0x4fefc, 0x54ce5,
+ 0x59f25, 0x5f5f6, 0x65193, 0x6b23c,
+ 0x71835, 0x783c3, 0x7f52c, 0x86cc0,
+ 0x8eacc, 0x96fa5, 0x9fba0, 0xa8f1a,
+ 0xb2a71, 0xbce0a, 0xc7a4a, 0xd2fa0,
+ 0xdee7b, 0xeb752, 0xf8a9f, 0x1068e4,
+ 0x1152a3, 0x12486a, 0x134ac8, 0x145a55,
+ 0x1577ac, 0x16a370, 0x17df51, 0x192bc2,
+ 0x1a88f8, 0x1bf7b7, 0x1d78c9, 0x1f0d04,
+ 0x20b542, 0x227268, 0x244564, 0x262f26,
+ 0x2830af
+};
+
+static uint tas3004_treble_tab[]={
+ 0x96, 0x95, 0x95, 0x94,
+ 0x93, 0x92, 0x92, 0x91,
+ 0x90, 0x90, 0x8f, 0x8e,
+ 0x8d, 0x8d, 0x8c, 0x8b,
+ 0x8a, 0x8a, 0x89, 0x88,
+ 0x88, 0x87, 0x86, 0x85,
+ 0x85, 0x84, 0x83, 0x83,
+ 0x82, 0x81, 0x80, 0x80,
+ 0x7f, 0x7e, 0x7e, 0x7d,
+ 0x7c, 0x7b, 0x7b, 0x7a,
+ 0x79, 0x78, 0x78, 0x77,
+ 0x76, 0x76, 0x75, 0x74,
+ 0x73, 0x73, 0x72, 0x71,
+ 0x71, 0x68, 0x45, 0x5b,
+ 0x6d, 0x6c, 0x6b, 0x6a,
+ 0x69, 0x68, 0x67, 0x66,
+ 0x65, 0x63, 0x62, 0x62,
+ 0x60, 0x5e, 0x5c, 0x5b,
+ 0x59, 0x57, 0x55, 0x53,
+ 0x52, 0x4f, 0x4d, 0x4a,
+ 0x48, 0x46, 0x43, 0x40,
+ 0x3d, 0x3a, 0x36, 0x33,
+ 0x2f, 0x2c, 0x27, 0x23,
+ 0x1f, 0x1a, 0x15, 0xf,
+ 0x8, 0x5, 0x2, 0x1,
+ 0x1
+};
+
+static uint tas3004_bass_tab[]={
+ 0x96, 0x95, 0x95, 0x94,
+ 0x93, 0x92, 0x92, 0x91,
+ 0x90, 0x90, 0x8f, 0x8e,
+ 0x8d, 0x8d, 0x8c, 0x8b,
+ 0x8a, 0x8a, 0x89, 0x88,
+ 0x88, 0x87, 0x86, 0x85,
+ 0x85, 0x84, 0x83, 0x83,
+ 0x82, 0x81, 0x80, 0x80,
+ 0x7f, 0x7e, 0x7e, 0x7d,
+ 0x7c, 0x7b, 0x7b, 0x7a,
+ 0x79, 0x78, 0x78, 0x77,
+ 0x76, 0x76, 0x75, 0x74,
+ 0x73, 0x73, 0x72, 0x71,
+ 0x70, 0x6f, 0x6e, 0x6d,
+ 0x6c, 0x6b, 0x6a, 0x6a,
+ 0x69, 0x67, 0x66, 0x66,
+ 0x65, 0x63, 0x62, 0x62,
+ 0x61, 0x60, 0x5e, 0x5d,
+ 0x5b, 0x59, 0x57, 0x55,
+ 0x53, 0x51, 0x4f, 0x4c,
+ 0x4a, 0x48, 0x46, 0x44,
+ 0x41, 0x3e, 0x3b, 0x38,
+ 0x36, 0x33, 0x2f, 0x2b,
+ 0x28, 0x24, 0x20, 0x1c,
+ 0x17, 0x12, 0xd, 0x7,
+ 0x1
+};
+
+struct tas_gain_t tas3004_gain={
+ master: tas3004_master_tab,
+ treble: tas3004_treble_tab,
+ bass: tas3004_bass_tab,
+ mixer: tas3004_mixer_tab
+};
+
+struct tas_eq_pref_t *tas3004_eq_prefs[]={
+ &eqp_17_1_0,
+ &eqp_18_1_0,
+ &eqp_1a_1_0,
+ &eqp_1c_1_0,
+ NULL
+};
diff -uNr linux-2.4.22/drivers/sound/dmasound/tas_common.c linux-2.4.22-ben2/drivers/sound/dmasound/tas_common.c
--- linux-2.4.22/drivers/sound/dmasound/tas_common.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/drivers/sound/dmasound/tas_common.c 2003-08-31 11:38:48.000000000 +0200
@@ -0,0 +1,256 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tas_common.h"
+
+#define CALL0(proc) \
+ do { \
+ struct tas_data_t *self; \
+ if (!tas_client || driver_hooks == NULL) \
+ return -1; \
+ self = (struct tas_data_t *)tas_client->data; \
+ if (driver_hooks->proc) \
+ return driver_hooks->proc(self); \
+ else \
+ return -EINVAL; \
+ } while (0)
+
+#define CALL(proc,arg...) \
+ do { \
+ struct tas_data_t *self; \
+ if (!tas_client || driver_hooks == NULL) \
+ return -1; \
+ self = (struct tas_data_t *)tas_client->data; \
+ if (driver_hooks->proc) \
+ return driver_hooks->proc(self, ## arg); \
+ else \
+ return -EINVAL; \
+ } while (0)
+
+
+static u8 tas_i2c_address = 0x34;
+static struct i2c_client * tas_client = NULL;
+
+static int tas_initialized = 0;
+static struct device_node* tas_node = NULL;
+
+static int tas_attach_adapter(struct i2c_adapter *);
+static int tas_detach_client(struct i2c_client *);
+
+struct i2c_driver tas_driver = {
+ name: "",
+ id: 0,
+ flags: I2C_DF_NOTIFY,
+ attach_adapter: &tas_attach_adapter,
+ detach_client: &tas_detach_client,
+ command: NULL,
+ inc_use: NULL,
+ dec_use: NULL
+};
+
+struct tas_driver_hooks_t *driver_hooks;
+
+int
+tas_register_driver(struct tas_driver_hooks_t *hooks)
+{
+ driver_hooks = hooks;
+ return 0;
+}
+
+int
+tas_get_mixer_level(int mixer, uint *level)
+{
+ CALL(get_mixer_level,mixer,level);
+}
+
+int
+tas_set_mixer_level(int mixer,uint level)
+{
+ CALL(set_mixer_level,mixer,level);
+}
+
+int
+tas_enter_sleep(void)
+{
+ CALL0(enter_sleep);
+}
+
+int
+tas_leave_sleep(void)
+{
+ CALL0(leave_sleep);
+}
+
+int
+tas_supported_mixers(void)
+{
+ CALL0(supported_mixers);
+}
+
+int
+tas_mixer_is_stereo(int mixer)
+{
+ CALL(mixer_is_stereo,mixer);
+}
+
+int
+tas_stereo_mixers(void)
+{
+ CALL0(stereo_mixers);
+}
+
+int
+tas_output_device_change(int device_id,int layout_id,int speaker_id)
+{
+ CALL(output_device_change,device_id,layout_id,speaker_id);
+}
+
+int
+tas_device_ioctl(u_int cmd, u_long arg)
+{
+ CALL(device_ioctl,cmd,arg);
+}
+
+int
+tas_post_init(void)
+{
+ CALL0(post_init);
+}
+
+static int
+tas_detect_client(struct i2c_adapter *adapter, int address)
+{
+ int rc = 0;
+ struct i2c_client *new_client;
+ struct tas_data_t *data;
+ const char *client_name = "tas Digital Equalizer";
+
+ if (driver_hooks == NULL) {
+ printk(KERN_ERR "tas_detect_client called with no hooks !\n");
+ return -ENODEV;
+ }
+
+ new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (!new_client) {
+ rc = -ENOMEM;
+ goto bail;
+ }
+
+ new_client->data = NULL;
+
+ new_client->addr = address;
+ new_client->data = data;
+ new_client->adapter = adapter;
+ new_client->driver = &tas_driver;
+ new_client->flags = 0;
+
+ strcpy(new_client->name,client_name);
+
+ new_client->id = 0; /* Only one instance supported */
+
+ if (driver_hooks->init && driver_hooks->init(new_client)) {
+ rc = -ENODEV;
+ goto bail;
+ }
+
+ /* Tell the i2c layer a new client has arrived */
+ if (i2c_attach_client(new_client)) {
+ if (driver_hooks->uninit)
+ driver_hooks->uninit((struct tas_data_t *)new_client->data);
+ rc = -ENODEV;
+ goto bail;
+ }
+
+ tas_client = new_client;
+bail:
+ if (rc && new_client) {
+ tas_client = NULL;
+ kfree(new_client);
+ }
+ return rc;
+}
+
+static int
+tas_attach_adapter(struct i2c_adapter *adapter)
+{
+ if (!strncmp(adapter->name, "mac-io", 6))
+ return tas_detect_client(adapter, tas_i2c_address);
+ return 0;
+}
+
+static int
+tas_detach_client(struct i2c_client *client)
+{
+ if (client == tas_client) {
+ driver_hooks->uninit((struct tas_data_t *)client->data);
+
+ i2c_detach_client(client);
+ kfree(client);
+ }
+ return 0;
+}
+
+int __init
+tas_cleanup(void)
+{
+ if (!tas_initialized)
+ return -ENODEV;
+ i2c_del_driver(&tas_driver);
+ tas_initialized = 0;
+
+ return 0;
+}
+
+int __init
+tas_init(int driver_id, const char *driver_name)
+{
+ int rc;
+ u32* paddr;
+
+ if (tas_initialized)
+ return 0;
+
+ strncpy(tas_driver.name,driver_name,31);
+ tas_driver.name[31]=0;
+ tas_driver.id=driver_id;
+
+ tas_node = find_devices("deq");
+ if (tas_node == NULL)
+ return -ENODEV;
+
+ printk(KERN_INFO "tas driver [%s])\n",tas_driver.name);
+ paddr = (u32 *)get_property(tas_node, "i2c-address", NULL);
+ if (paddr) {
+ tas_i2c_address = (*paddr) >> 1;
+ printk(KERN_INFO "using i2c address: 0x%x from device-tree\n",
+ tas_i2c_address);
+ } else
+ printk(KERN_INFO "using i2c address: 0x%x (default)\n", tas_i2c_address);
+
+ if ((rc = i2c_add_driver(&tas_driver))) {
+ printk("tas: Driver registration failed, module not inserted.\n");
+ tas_cleanup();
+ return rc;
+ }
+ tas_initialized = 1;
+ return 0;
+}
+/*
+ * Local Variables:
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ * End:
+ */
diff -uNr linux-2.4.22/drivers/sound/dmasound/tas_common.h linux-2.4.22-ben2/drivers/sound/dmasound/tas_common.h
--- linux-2.4.22/drivers/sound/dmasound/tas_common.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/drivers/sound/dmasound/tas_common.h 2003-08-31 11:42:15.000000000 +0200
@@ -0,0 +1,284 @@
+#ifndef _TAS_COMMON_H_
+#define _TAS_COMMON_H_
+
+#include
+#include
+#include
+
+#define I2C_DRIVERID_TAS_BASE (0xFEBA)
+
+#define SET_4_20(shadow, offset, val) \
+ do { \
+ (shadow)[(offset)+0] = ((val) >> 16) & 0xff; \
+ (shadow)[(offset)+1] = ((val) >> 8) & 0xff; \
+ (shadow)[(offset)+2] = ((val) >> 0) & 0xff; \
+ } while (0)
+
+#define GET_4_20(shadow, offset) \
+ (((u_int)((shadow)[(offset)+0]) << 16) | \
+ ((u_int)((shadow)[(offset)+1]) << 8) | \
+ ((u_int)((shadow)[(offset)+2]) << 0))
+
+
+#define TAS_BIQUAD_FAST_LOAD 0x01
+
+#define TAS_DRCE_ENABLE 0x01
+#define TAS_DRCE_ABOVE_RATIO 0x02
+#define TAS_DRCE_BELOW_RATIO 0x04
+#define TAS_DRCE_THRESHOLD 0x08
+#define TAS_DRCE_ENERGY 0x10
+#define TAS_DRCE_ATTACK 0x20
+#define TAS_DRCE_DECAY 0x40
+
+#define TAS_DRCE_ALL 0x7f
+
+
+#define TAS_OUTPUT_HEADPHONES 0x00
+#define TAS_OUTPUT_INTERNAL_SPKR 0x01
+#define TAS_OUTPUT_EXTERNAL_SPKR 0x02
+
+
+union tas_biquad_t {
+ struct {
+ int b0,b1,b2,a1,a2;
+ } coeff;
+ int buf[5];
+};
+
+struct tas_biquad_ctrl_t {
+ u_int channel:4;
+ u_int filter:4;
+
+ union tas_biquad_t data;
+};
+
+struct tas_biquad_ctrl_list_t {
+ int flags;
+ int filter_count;
+ struct tas_biquad_ctrl_t biquads[0];
+};
+
+struct tas_ratio_t {
+ unsigned short val; /* 8.8 */
+ unsigned short expand; /* 0 = compress, !0 = expand. */
+};
+
+struct tas_drce_t {
+ unsigned short enable;
+ struct tas_ratio_t above;
+ struct tas_ratio_t below;
+ short threshold; /* dB, 8.8 signed */
+ unsigned short energy; /* seconds, 4.12 unsigned */
+ unsigned short attack; /* seconds, 4.12 unsigned */
+ unsigned short decay; /* seconds, 4.12 unsigned */
+};
+
+struct tas_drce_ctrl_t {
+ uint flags;
+
+ struct tas_drce_t data;
+};
+
+struct tas_gain_t
+{
+ unsigned int *master;
+ unsigned int *treble;
+ unsigned int *bass;
+ unsigned int *mixer;
+};
+
+typedef char tas_shadow_t[16];
+
+struct tas_data_t
+{
+ struct i2c_client *client;
+ tas_shadow_t *shadow;
+ uint mixer[SOUND_MIXER_NRDEVICES];
+};
+
+typedef int (*tas_hook_init_t)(struct i2c_client *);
+typedef int (*tas_hook_post_init_t)(struct tas_data_t *);
+typedef void (*tas_hook_uninit_t)(struct tas_data_t *);
+
+typedef int (*tas_hook_get_mixer_level_t)(struct tas_data_t *,int,uint *);
+typedef int (*tas_hook_set_mixer_level_t)(struct tas_data_t *,int,uint);
+
+typedef int (*tas_hook_enter_sleep_t)(struct tas_data_t *);
+typedef int (*tas_hook_leave_sleep_t)(struct tas_data_t *);
+
+typedef int (*tas_hook_supported_mixers_t)(struct tas_data_t *);
+typedef int (*tas_hook_mixer_is_stereo_t)(struct tas_data_t *,int);
+typedef int (*tas_hook_stereo_mixers_t)(struct tas_data_t *);
+
+typedef int (*tas_hook_output_device_change_t)(struct tas_data_t *,int,int,int);
+typedef int (*tas_hook_device_ioctl_t)(struct tas_data_t *,u_int,u_long);
+
+struct tas_driver_hooks_t {
+ /*
+ * All hardware initialisation must be performed in
+ * post_init(), as tas_dmasound_init() does a hardware reset.
+ *
+ * init() is called before tas_dmasound_init() so that
+ * ouput_device_change() is always called after i2c driver
+ * initialisation. The implication is that
+ * output_device_change() must cope with the fact that it
+ * may be called before post_init().
+ */
+
+ tas_hook_init_t init;
+ tas_hook_post_init_t post_init;
+ tas_hook_uninit_t uninit;
+
+ tas_hook_get_mixer_level_t get_mixer_level;
+ tas_hook_set_mixer_level_t set_mixer_level;
+
+ tas_hook_enter_sleep_t enter_sleep;
+ tas_hook_leave_sleep_t leave_sleep;
+
+ tas_hook_supported_mixers_t supported_mixers;
+ tas_hook_mixer_is_stereo_t mixer_is_stereo;
+ tas_hook_stereo_mixers_t stereo_mixers;
+
+ tas_hook_output_device_change_t output_device_change;
+ tas_hook_device_ioctl_t device_ioctl;
+};
+
+enum tas_write_mode_t {
+ WRITE_HW = 0x01,
+ WRITE_SHADOW = 0x02,
+ WRITE_NORMAL = 0x03,
+ FORCE_WRITE = 0x04
+};
+
+static inline uint
+tas_mono_to_stereo(uint mono)
+{
+ mono &=0xff;
+ return mono | (mono<<8);
+}
+
+/*
+ * Todo: make these functions a bit more efficient !
+ */
+static inline int
+tas_write_register( struct tas_data_t *self,
+ uint reg_num,
+ uint reg_width,
+ char *data,
+ uint write_mode)
+{
+ int rc;
+
+ if (reg_width==0 || data==NULL || self==NULL)
+ return -EINVAL;
+ if (!(write_mode & FORCE_WRITE) &&
+ !memcmp(data,self->shadow[reg_num],reg_width))
+ return 0;
+
+ if (write_mode & WRITE_SHADOW)
+ memcpy(self->shadow[reg_num],data,reg_width);
+ if (write_mode & WRITE_HW) {
+ rc=i2c_smbus_write_block_data(self->client,
+ reg_num,
+ reg_width,
+ data);
+ if (rc < 0) {
+ printk("tas: I2C block write failed \n");
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static inline int
+tas_sync_register( struct tas_data_t *self,
+ uint reg_num,
+ uint reg_width)
+{
+ int rc;
+
+ if (reg_width==0 || self==NULL)
+ return -EINVAL;
+ rc=i2c_smbus_write_block_data(self->client,
+ reg_num,
+ reg_width,
+ self->shadow[reg_num]);
+ if (rc < 0) {
+ printk("tas: I2C block write failed \n");
+ return rc;
+ }
+ return 0;
+}
+
+static inline int
+tas_write_byte_register( struct tas_data_t *self,
+ uint reg_num,
+ char data,
+ uint write_mode)
+{
+ if (self==NULL)
+ return -1;
+ if (!(write_mode & FORCE_WRITE) && data != self->shadow[reg_num][0])
+ return 0;
+ if (write_mode & WRITE_SHADOW)
+ self->shadow[reg_num][0]=data;
+ if (write_mode & WRITE_HW) {
+ if (i2c_smbus_write_byte_data(self->client, reg_num, data) < 0) {
+ printk("tas: I2C byte write failed \n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static inline int
+tas_sync_byte_register( struct tas_data_t *self,
+ uint reg_num,
+ uint reg_width)
+{
+ if (reg_width==0 || self==NULL)
+ return -1;
+ if (i2c_smbus_write_byte_data(
+ self->client, reg_num, self->shadow[reg_num][0]) < 0) {
+ printk("tas: I2C byte write failed \n");
+ return -1;
+ }
+ return 0;
+}
+
+static inline int
+tas_read_register( struct tas_data_t *self,
+ uint reg_num,
+ uint reg_width,
+ char *data)
+{
+ if (reg_width==0 || data==NULL || self==NULL)
+ return -1;
+ memcpy(data,self->shadow[reg_num],reg_width);
+ return 0;
+}
+
+extern int tas_register_driver(struct tas_driver_hooks_t *hooks);
+
+extern int tas_get_mixer_level(int mixer,uint *level);
+extern int tas_set_mixer_level(int mixer,uint level);
+extern int tas_enter_sleep(void);
+extern int tas_leave_sleep(void);
+extern int tas_supported_mixers(void);
+extern int tas_mixer_is_stereo(int mixer);
+extern int tas_stereo_mixers(void);
+extern int tas_output_device_change(int,int,int);
+extern int tas_device_ioctl(u_int, u_long);
+
+extern int tas_cleanup(void);
+extern int tas_init(int driver_id,const char *driver_name);
+extern int tas_post_init(void);
+
+#endif /* _TAS_COMMON_H_ */
+/*
+ * Local Variables:
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ * End:
+ */
diff -uNr linux-2.4.22/drivers/sound/dmasound/tas_eq_prefs.h linux-2.4.22-ben2/drivers/sound/dmasound/tas_eq_prefs.h
--- linux-2.4.22/drivers/sound/dmasound/tas_eq_prefs.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/drivers/sound/dmasound/tas_eq_prefs.h 2003-08-31 11:39:22.000000000 +0200
@@ -0,0 +1,24 @@
+#ifndef _TAS_EQ_PREFS_H_
+#define _TAS_EQ_PREFS_H_
+
+struct tas_eq_pref_t {
+ u_int sample_rate;
+ u_int device_id;
+ u_int output_id;
+ u_int speaker_id;
+
+ struct tas_drce_t *drce;
+
+ u_int filter_count;
+ struct tas_biquad_ctrl_t *biquads;
+};
+
+#endif /* _TAS_EQ_PREFS_H_ */
+
+/*
+ * Local Variables:
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ * End:
+ */
diff -uNr linux-2.4.22/drivers/sound/dmasound/tas_ioctl.h linux-2.4.22-ben2/drivers/sound/dmasound/tas_ioctl.h
--- linux-2.4.22/drivers/sound/dmasound/tas_ioctl.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.22-ben2/drivers/sound/dmasound/tas_ioctl.h 2003-08-31 11:39:33.000000000 +0200
@@ -0,0 +1,24 @@
+#ifndef _TAS_IOCTL_H_
+#define _TAS_IOCTL_H_
+
+#include
+#include
+
+
+#define TAS_READ_EQ _SIOR('t',0,struct tas_biquad_ctrl_t)
+#define TAS_WRITE_EQ _SIOW('t',0,struct tas_biquad_ctrl_t)
+
+#define TAS_READ_EQ_LIST _SIOR('t',1,struct tas_biquad_ctrl_t)
+#define TAS_WRITE_EQ_LIST _SIOW('t',1,struct tas_biquad_ctrl_t)
+
+#define TAS_READ_EQ_FILTER_COUNT _SIOR('t',2,int)
+#define TAS_READ_EQ_CHANNEL_COUNT _SIOR('t',3,int)
+
+#define TAS_READ_DRCE _SIOR('t',4,struct tas_drce_ctrl_t)
+#define TAS_WRITE_DRCE _SIOW('t',4,struct tas_drce_ctrl_t)
+
+#define TAS_READ_DRCE_CAPS _SIOR('t',5,int)
+#define TAS_READ_DRCE_MIN _SIOR('t',6,int)
+#define TAS_READ_DRCE_MAX _SIOR('t',7,int)
+
+#endif
diff -uNr linux-2.4.22/drivers/usb/host/usb-ohci.c linux-2.4.22-ben2/drivers/usb/host/usb-ohci.c
--- linux-2.4.22/drivers/usb/host/usb-ohci.c 2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.22-ben2/drivers/usb/host/usb-ohci.c 2003-08-31 11:42:31.000000000 +0200
@@ -714,8 +714,10 @@
list_add (&urb->urb_list, entry);
/* drive timeouts by SF (messy, but works) */
- writel (OHCI_INTR_SF, &ohci->regs->intrenable);
- (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
+ if (!ohci->sleeping) {
+ writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+ (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
+ }
}
#endif
@@ -1010,6 +1012,7 @@
case PIPE_CONTROL:
ed->hwNextED = 0;
if (ohci->ed_controltail == NULL) {
+ io_barrier();
writel (ed->dma, &ohci->regs->ed_controlhead);
} else {
ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
@@ -1018,6 +1021,7 @@
if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
!ohci->ed_rm_list[1] && !ohci->sleeping) {
ohci->hc_control |= OHCI_CTRL_CLE;
+ io_barrier();
writel (ohci->hc_control, &ohci->regs->control);
}
ohci->ed_controltail = edi;
@@ -1026,6 +1030,7 @@
case PIPE_BULK:
ed->hwNextED = 0;
if (ohci->ed_bulktail == NULL) {
+ io_barrier();
writel (ed->dma, &ohci->regs->ed_bulkhead);
} else {
ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
@@ -1034,6 +1039,7 @@
if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
!ohci->ed_rm_list[1] && !ohci->sleeping) {
ohci->hc_control |= OHCI_CTRL_BLE;
+ io_barrier();
writel (ohci->hc_control, &ohci->regs->control);
}
ohci->ed_bulktail = edi;
@@ -1126,7 +1132,8 @@
if (ed->ed_prev == NULL) {
if (!ed->hwNextED) {
ohci->hc_control &= ~OHCI_CTRL_CLE;
- writel (ohci->hc_control, &ohci->regs->control);
+ if (!ohci->sleeping)
+ writel (ohci->hc_control, &ohci->regs->control);
}
writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead);
} else {
@@ -1143,7 +1150,8 @@
if (ed->ed_prev == NULL) {
if (!ed->hwNextED) {
ohci->hc_control &= ~OHCI_CTRL_BLE;
- writel (ohci->hc_control, &ohci->regs->control);
+ if (!ohci->sleeping)
+ writel (ohci->hc_control, &ohci->regs->control);
}
writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead);
} else {
@@ -1272,16 +1280,19 @@
return;
ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
+ io_barrier();
if (!ohci->disabled) {
switch (ed->type) {
case PIPE_CONTROL: /* stop control list */
ohci->hc_control &= ~OHCI_CTRL_CLE;
- writel (ohci->hc_control, &ohci->regs->control);
+ if (!ohci->sleeping)
+ writel (ohci->hc_control, &ohci->regs->control);
break;
case PIPE_BULK: /* stop bulk list */
ohci->hc_control &= ~OHCI_CTRL_BLE;
- writel (ohci->hc_control, &ohci->regs->control);
+ if (!ohci->sleeping)
+ writel (ohci->hc_control, &ohci->regs->control);
break;
}
}
@@ -1413,7 +1424,7 @@
}
if (!ohci->sleeping) {
- wmb();
+ io_barrier();
writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
(void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
}
@@ -1442,7 +1453,7 @@
TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
td_fill (ohci, info, data, 0, urb, cnt++);
if (!ohci->sleeping) {
- wmb();
+ io_barrier();
writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
(void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
}
@@ -1550,6 +1561,7 @@
td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0;
ohci->hcca->done_head = 0;
+ wmb();
while (td_list_hc) {
td_list = dma_to_td (ohci, td_list_hc);
@@ -1658,6 +1670,7 @@
/* maybe reenable control and bulk lists */
if (!ohci->disabled) {
+ io_barrier();
if (ctrl) /* reset control list */
writel (0, &ohci->regs->ed_controlcurrent);
if (bulk) /* reset bulk list */
@@ -2318,6 +2331,10 @@
struct ohci_regs * regs = ohci->regs;
int ints;
+ /* Sanity check */
+ if (ohci->sleeping)
+ return;
+
/* avoid (slow) readl if only WDH happened */
if ((ohci->hcca->done_head != 0)
&& !(le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
@@ -2624,6 +2641,16 @@
if (pci_enable_device(dev) < 0)
return -ENODEV;
+#ifdef CONFIG_PMAC_PBOOK
+ {
+ struct device_node *of_node;
+
+ /* Re-enable USB PAD & cell clock */
+ of_node = pci_device_to_OF_node (dev);
+ if (of_node)
+ pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1);
+ }
+#endif
if (!dev->irq) {
err("found OHCI device with no IRQ assigned. check BIOS settings!");
pci_disable_device (dev);
@@ -2719,6 +2746,12 @@
info ("USB suspend: usb-%s", dev->slot_name);
ohci->sleeping = 1;
+#ifdef CONFIG_PMAC_PBOOK
+ if (_machine == _MACH_Pmac)
+ disable_irq (ohci->irq);
+ /* else, 2.4 assumes shared irqs -- don't disable */
+#endif
+
/* First stop processing */
spin_lock_irqsave (&usb_ed_lock, flags);
ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
@@ -2732,11 +2765,6 @@
if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
mdelay (1);
-#ifdef CONFIG_PMAC_PBOOK
- if (_machine == _MACH_Pmac)
- disable_irq (ohci->irq);
- /* else, 2.4 assumes shared irqs -- don't disable */
-#endif
/* Enable remote wakeup */
writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable);
diff -uNr linux-2.4.22/drivers/usb/host/usb-ohci.h linux-2.4.22-ben2/drivers/usb/host/usb-ohci.h
--- linux-2.4.22/drivers/usb/host/usb-ohci.h 2003-06-13 16:51:36.000000000 +0200
+++ linux-2.4.22-ben2/drivers/usb/host/usb-ohci.h 2003-08-31 11:39:34.000000000 +0200
@@ -58,7 +58,7 @@
dma_addr_t dma;
__u32 unused[3];
-} __attribute((aligned(16)));
+} __attribute((aligned(32)));
typedef struct ed ed_t;
@@ -567,7 +567,7 @@
return -ENOMEM;
ohci->dev_cache = pci_pool_create ("ohci_dev", ohci->ohci_dev,
sizeof (struct ohci_device),
- 16 /* byte alignment */,
+ 32 /* byte alignment */,
0 /* no page-crossing issues */,
GFP_KERNEL | OHCI_MEM_FLAGS);
if (!ohci->dev_cache)
diff -uNr linux-2.4.22/drivers/usb/storage/scsiglue.c linux-2.4.22-ben2/drivers/usb/storage/scsiglue.c
--- linux-2.4.22/drivers/usb/storage/scsiglue.c 2003-06-13 16:51:37.000000000 +0200
+++ linux-2.4.22-ben2/drivers/usb/storage/scsiglue.c 2003-08-31 11:38:46.000000000 +0200
@@ -75,16 +75,19 @@
{
struct us_data *us;
char local_name[32];
- /* Note: this function gets called with io_request_lock spinlock helt! */
+ spin_unlock_irq(&io_request_lock);
+
/* This is not nice at all, but how else are we to get the
* data here? */
us = (struct us_data *)sht->proc_dir;
/* set up the name of our subdirectory under /proc/scsi/ */
sprintf(local_name, "usb-storage-%d", us->host_number);
- sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_ATOMIC);
- if (!sht->proc_name)
+ sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_KERNEL);
+ if (!sht->proc_name) {
+ spin_lock_irq(&io_request_lock);
return 0;
+ }
strcpy(sht->proc_name, local_name);
/* we start with no /proc directory entry */
@@ -95,12 +98,14 @@
if (us->host) {
us->host->hostdata[0] = (unsigned long)us;
us->host_no = us->host->host_no;
+ spin_lock_irq(&io_request_lock);
return 1;
}
/* odd... didn't register properly. Abort and free pointers */
kfree(sht->proc_name);
sht->proc_name = NULL;
+ spin_lock_irq(&io_request_lock);
return 0;
}
diff -uNr linux-2.4.22/drivers/video/Config.in linux-2.4.22-ben2/drivers/video/Config.in
--- linux-2.4.22/drivers/video/Config.in 2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.22-ben2/drivers/video/Config.in 2003-08-31 11:38:45.000000000 +0200
@@ -73,7 +73,7 @@
dep_bool ' Apple "valkyrie" display support' CONFIG_FB_VALKYRIE $CONFIG_ALL_PPC
bool ' Chips 65550 display support' CONFIG_FB_CT65550
bool ' IMS Twin Turbo display support' CONFIG_FB_IMSTT
- bool ' S3 Trio display support' CONFIG_FB_S3TRIO
+ dep_bool ' S3 Trio display support' CONFIG_FB_S3TRIO $CONFIG_ALL_PPC
tristate ' VGA 16-color graphics console' CONFIG_FB_VGA16
fi
if [ "$CONFIG_PARISC" = "y" ]; then
diff -uNr linux-2.4.22/drivers/video/aty/atyfb.h linux-2.4.22-ben2/drivers/video/aty/atyfb.h
--- linux-2.4.22/drivers/video/aty/atyfb.h 2002-11-29 00:53:15.000000000 +0100
+++ linux-2.4.22-ben2/drivers/video/aty/atyfb.h 2003-08-31 11:39:52.000000000 +0200
@@ -124,6 +124,7 @@
#endif
} fbcon_cmap;
u8 blitter_may_be_busy;
+ int asleep;
#ifdef __sparc__
u8 mmaped;
int open;
diff -uNr linux-2.4.22/drivers/video/aty/atyfb_base.c linux-2.4.22-ben2/drivers/video/aty/atyfb_base.c
--- linux-2.4.22/drivers/video/aty/atyfb_base.c 2002-11-29 00:53:15.000000000 +0100
+++ linux-2.4.22-ben2/drivers/video/aty/atyfb_base.c 2003-08-31 11:39:06.000000000 +0200
@@ -748,6 +748,8 @@
info->current_par = *par;
+ if (info->asleep)
+ return;
if (info->blitter_may_be_busy)
wait_for_idle(info);
tmp = aty_ld_8(CRTC_GEN_CNTL + 3, info);
@@ -1192,6 +1194,10 @@
return -EINVAL;
par->crtc.xoffset = xoffset;
par->crtc.yoffset = yoffset;
+
+ if (info->asleep)
+ return 0;
+
set_off_pitch(par, info);
return 0;
}
@@ -1660,6 +1666,7 @@
}
break;
case PBOOK_SLEEP_NOW:
+ acquire_console_sem();
if (currcon >= 0)
fb_display[currcon].dispsw = &fbcon_dummy;
if (info->blitter_may_be_busy)
@@ -1675,11 +1682,14 @@
/* Blank display and LCD */
atyfbcon_blank(VESA_POWERDOWN+1, (struct fb_info *)info);
+ info->asleep = 1;
/* Set chip to "suspend" mode */
result = aty_power_mgmt(1, info);
+ release_console_sem();
break;
case PBOOK_WAKE:
+ acquire_console_sem();
/* Wakeup chip */
result = aty_power_mgmt(0, info);
@@ -1690,6 +1700,7 @@
vfree(info->save_framebuffer);
info->save_framebuffer = 0;
}
+ info->asleep = 0;
/* Restore display */
if (currcon >= 0) {
atyfb_set_dispsw(&fb_display[currcon],
@@ -1697,6 +1708,11 @@
info->current_par.accel_flags & FB_ACCELF_TEXT);
}
atyfbcon_blank(0, (struct fb_info *)info);
+ /* XXX We may want to re-call atyfb_set_par and make
+ * sure panning & cursor & palette are correct as we
+ * skipped any change to these during suspend.
+ */
+ release_console_sem();
break;
}
}
@@ -2720,6 +2736,9 @@
struct fb_info_aty *info = (struct fb_info_aty *)fb;
u8 gen_cntl;
+ if (info->asleep)
+ return;
+
#ifdef CONFIG_PMAC_BACKLIGHT
if ((_machine == _MACH_Pmac) && blank)
set_backlight_enable(0);
@@ -2792,23 +2811,25 @@
info->palette[regno].red = red;
info->palette[regno].green = green;
info->palette[regno].blue = blue;
- i = aty_ld_8(DAC_CNTL, info) & 0xfc;
- if (M64_HAS(EXTRA_BRIGHT))
- i |= 0x2; /*DAC_CNTL|0x2 turns off the extra brightness for gt*/
- aty_st_8(DAC_CNTL, i, info);
- aty_st_8(DAC_MASK, 0xff, info);
- scale = (M64_HAS(INTEGRATED) && info->current_par.crtc.bpp == 16) ? 3 : 0;
+ if (!info->asleep) {
+ i = aty_ld_8(DAC_CNTL, info) & 0xfc;
+ if (M64_HAS(EXTRA_BRIGHT))
+ i |= 0x2; /*DAC_CNTL|0x2 turns off the extra brightness for gt*/
+ aty_st_8(DAC_CNTL, i, info);
+ aty_st_8(DAC_MASK, 0xff, info);
+ scale = (M64_HAS(INTEGRATED) && info->current_par.crtc.bpp == 16) ? 3 : 0;
#ifdef CONFIG_ATARI
- out_8(&info->aty_cmap_regs->windex, regno << scale);
- out_8(&info->aty_cmap_regs->lut, red);
- out_8(&info->aty_cmap_regs->lut, green);
- out_8(&info->aty_cmap_regs->lut, blue);
+ out_8(&info->aty_cmap_regs->windex, regno << scale);
+ out_8(&info->aty_cmap_regs->lut, red);
+ out_8(&info->aty_cmap_regs->lut, green);
+ out_8(&info->aty_cmap_regs->lut, blue);
#else
- writeb(regno << scale, &info->aty_cmap_regs->windex);
- writeb(red, &info->aty_cmap_regs->lut);
- writeb(green, &info->aty_cmap_regs->lut);
- writeb(blue, &info->aty_cmap_regs->lut);
+ writeb(regno << scale, &info->aty_cmap_regs->windex);
+ writeb(red, &info->aty_cmap_regs->lut);
+ writeb(green, &info->aty_cmap_regs->lut);
+ writeb(blue, &info->aty_cmap_regs->lut);
#endif
+ }
if (regno < 16)
switch (info->current_par.crtc.bpp) {
#ifdef FBCON_HAS_CFB16
@@ -2859,6 +2880,9 @@
struct vc_data *conp = p->conp;
u32 yres, yoffset, sy, height;
+ if (info->asleep)
+ return 0;
+
yres = ((par->crtc.v_tot_disp >> 16) & 0x7ff) + 1;
yoffset = fb_display[con].var.yoffset;
diff -uNr linux-2.4.22/drivers/video/aty/mach64_cursor.c linux-2.4.22-ben2/drivers/video/aty/mach64_cursor.c
--- linux-2.4.22/drivers/video/aty/mach64_cursor.c 2001-10-25 22:53:52.000000000 +0200
+++ linux-2.4.22-ben2/drivers/video/aty/mach64_cursor.c 2003-08-31 11:39:34.000000000 +0200
@@ -54,7 +54,7 @@
const u8 *blue = cursor_color_map;
int i;
- if (!c)
+ if (!c || fb->asleep)
return;
#ifdef __sparc__
@@ -81,7 +81,7 @@
u8 *ram, m, b;
int x, y;
- if (!c)
+ if (!c || fb->asleep)
return;
#ifdef __sparc__
@@ -118,7 +118,7 @@
u16 xoff, yoff;
int x, y;
- if (!c)
+ if (!c || fb->asleep)
return;
#ifdef __sparc__
@@ -297,8 +297,10 @@
c->mask[i][height-1] = (j >= 8) ? 0xff : (0xff << (8 - j));
}
- aty_set_cursor_color(fb);
- aty_set_cursor_shape(fb);
+ if (!fb->asleep) {
+ aty_set_cursor_color(fb);
+ aty_set_cursor_shape(fb);
+ }
}
return 1;
}
diff -uNr linux-2.4.22/drivers/video/aty128fb.c linux-2.4.22-ben2/drivers/video/aty128fb.c
--- linux-2.4.22/drivers/video/aty128fb.c 2003-06-13 16:51:37.000000000 +0200
+++ linux-2.4.22-ben2/drivers/video/aty128fb.c 2003-08-31 11:39:11.000000000 +0200
@@ -55,7 +55,7 @@
#include
#include
-#ifdef CONFIG_PPC
+#ifdef CONFIG_ALL_PPC
#include
#include
#include