diff -Naur linux-2.4.22-ppc-dev.orig/CHANGES linux-2.4.22-ppc-dev/CHANGES --- linux-2.4.22-ppc-dev.orig/CHANGES 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/CHANGES 2003-08-25 23:37:44.000000000 +0200 @@ -0,0 +1,2 @@ +2.4.22-ben1 : First release + diff -Naur linux-2.4.22-ppc-dev.orig/Documentation/Configure.help linux-2.4.22-ppc-dev/Documentation/Configure.help --- linux-2.4.22-ppc-dev.orig/Documentation/Configure.help 2003-08-27 15:17:42.000000000 +0200 +++ linux-2.4.22-ppc-dev/Documentation/Configure.help 2003-08-25 23:37:54.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 -Naur linux-2.4.22-ppc-dev.orig/Documentation/cpufreq linux-2.4.22-ppc-dev/Documentation/cpufreq --- linux-2.4.22-ppc-dev.orig/Documentation/cpufreq 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/Documentation/cpufreq 2003-08-25 23:37:43.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 -Naur linux-2.4.22-ppc-dev.orig/Documentation/laptop-mode.sh linux-2.4.22-ppc-dev/Documentation/laptop-mode.sh --- linux-2.4.22-ppc-dev.orig/Documentation/laptop-mode.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/Documentation/laptop-mode.sh 2003-08-25 23:37:28.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 -Naur linux-2.4.22-ppc-dev.orig/Documentation/laptop-mode.txt linux-2.4.22-ppc-dev/Documentation/laptop-mode.txt --- linux-2.4.22-ppc-dev.orig/Documentation/laptop-mode.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/Documentation/laptop-mode.txt 2003-08-25 23:37:29.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 -Naur linux-2.4.22-ppc-dev.orig/MAINTAINERS linux-2.4.22-ppc-dev/MAINTAINERS --- linux-2.4.22-ppc-dev.orig/MAINTAINERS 2003-08-27 15:17:43.000000000 +0200 +++ linux-2.4.22-ppc-dev/MAINTAINERS 2003-08-25 23:37:31.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 -Naur linux-2.4.22-ppc-dev.orig/Makefile linux-2.4.22-ppc-dev/Makefile --- linux-2.4.22-ppc-dev.orig/Makefile 2003-08-27 15:17:43.000000000 +0200 +++ linux-2.4.22-ppc-dev/Makefile 2003-08-25 23:37:55.000000000 +0200 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 22 -EXTRAVERSION = +EXTRAVERSION = -ben1 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff -Naur linux-2.4.22-ppc-dev.orig/README.BENH linux-2.4.22-ppc-dev/README.BENH --- linux-2.4.22-ppc-dev.orig/README.BENH 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/README.BENH 2003-08-25 23:37:30.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 -Naur linux-2.4.22-ppc-dev.orig/arch/alpha/mm/fault.c linux-2.4.22-ppc-dev/arch/alpha/mm/fault.c --- linux-2.4.22-ppc-dev.orig/arch/alpha/mm/fault.c 2002-11-29 00:53:08.000000000 +0100 +++ linux-2.4.22-ppc-dev/arch/alpha/mm/fault.c 2003-08-25 23:37:28.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 -Naur linux-2.4.22-ppc-dev.orig/arch/arm/mm/fault-common.c linux-2.4.22-ppc-dev/arch/arm/mm/fault-common.c --- linux-2.4.22-ppc-dev.orig/arch/arm/mm/fault-common.c 2003-08-27 15:17:43.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/arm/mm/fault-common.c 2003-08-25 23:38:04.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 -Naur linux-2.4.22-ppc-dev.orig/arch/cris/mm/fault.c linux-2.4.22-ppc-dev/arch/cris/mm/fault.c --- linux-2.4.22-ppc-dev.orig/arch/cris/mm/fault.c 2003-08-27 15:17:43.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/cris/mm/fault.c 2003-08-25 23:37:53.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 -Naur linux-2.4.22-ppc-dev.orig/arch/i386/mm/fault.c linux-2.4.22-ppc-dev/arch/i386/mm/fault.c --- linux-2.4.22-ppc-dev.orig/arch/i386/mm/fault.c 2002-11-29 00:53:09.000000000 +0100 +++ linux-2.4.22-ppc-dev/arch/i386/mm/fault.c 2003-08-25 23:37:48.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ia64/mm/fault.c linux-2.4.22-ppc-dev/arch/ia64/mm/fault.c --- linux-2.4.22-ppc-dev.orig/arch/ia64/mm/fault.c 2003-08-27 15:17:44.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ia64/mm/fault.c 2003-08-25 23:37:28.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 -Naur linux-2.4.22-ppc-dev.orig/arch/m68k/mm/fault.c linux-2.4.22-ppc-dev/arch/m68k/mm/fault.c --- linux-2.4.22-ppc-dev.orig/arch/m68k/mm/fault.c 2002-11-29 00:53:09.000000000 +0100 +++ linux-2.4.22-ppc-dev/arch/m68k/mm/fault.c 2003-08-25 23:37:46.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 -Naur linux-2.4.22-ppc-dev.orig/arch/mips/mm/fault.c linux-2.4.22-ppc-dev/arch/mips/mm/fault.c --- linux-2.4.22-ppc-dev.orig/arch/mips/mm/fault.c 2003-08-27 15:17:46.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/mips/mm/fault.c 2003-08-25 23:37:48.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 -Naur linux-2.4.22-ppc-dev.orig/arch/mips64/mm/fault.c linux-2.4.22-ppc-dev/arch/mips64/mm/fault.c --- linux-2.4.22-ppc-dev.orig/arch/mips64/mm/fault.c 2003-08-27 15:17:46.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/mips64/mm/fault.c 2003-08-25 23:38:01.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/boot/pmac/coffmain.c linux-2.4.22-ppc-dev/arch/ppc/boot/pmac/coffmain.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/boot/pmac/coffmain.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/boot/pmac/coffmain.c 2003-08-25 23:37:53.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/boot/pmac/misc.S linux-2.4.22-ppc-dev/arch/ppc/boot/pmac/misc.S --- linux-2.4.22-ppc-dev.orig/arch/ppc/boot/pmac/misc.S 2003-06-13 16:51:31.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/boot/pmac/misc.S 2003-08-25 23:37:30.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/boot/pmac/start.c linux-2.4.22-ppc-dev/arch/ppc/boot/pmac/start.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/boot/pmac/start.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/boot/pmac/start.c 2003-08-25 23:37:47.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/config.in linux-2.4.22-ppc-dev/arch/ppc/config.in --- linux-2.4.22-ppc-dev.orig/arch/ppc/config.in 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/config.in 2003-08-25 23:37:27.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/configs/briq_defconfig linux-2.4.22-ppc-dev/arch/ppc/configs/briq_defconfig --- linux-2.4.22-ppc-dev.orig/arch/ppc/configs/briq_defconfig 2003-06-13 16:51:31.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/configs/briq_defconfig 2003-08-25 23:37:42.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/configs/pmac_defconfig linux-2.4.22-ppc-dev/arch/ppc/configs/pmac_defconfig --- linux-2.4.22-ppc-dev.orig/arch/ppc/configs/pmac_defconfig 2003-06-13 16:51:31.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/configs/pmac_defconfig 2003-08-25 23:37:27.000000000 +0200 @@ -30,6 +30,10 @@ # 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 @@ -38,15 +42,13 @@ # 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 +78,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 +102,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 +155,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 @@ -181,14 +184,16 @@ 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 @@ -198,7 +203,13 @@ # 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 # @@ -255,7 +266,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 +CONFIG_IDE_TASK_IOCTL=y # # IDE chipset support/bugfixes @@ -272,9 +283,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 +300,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 +314,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 @@ -353,19 +366,12 @@ # 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 # CONFIG_AIC7XXX_BUILD_FIRMWARE is not set -# CONFIG_AIC7XXX_DEBUG_ENABLE is not set -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 +413,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 # @@ -436,10 +442,11 @@ 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 +460,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 # @@ -537,6 +544,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,6 +562,7 @@ CONFIG_APPLE_AIRPORT=m # CONFIG_PLX_HERMES is not set CONFIG_PCI_HERMES=m +# CONFIG_TMD_HERMES is not set # # Wireless Pcmcia cards support @@ -598,6 +607,21 @@ # 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 + +# # Amateur Radio support # # CONFIG_HAMRADIO is not set @@ -613,7 +637,7 @@ CONFIG_IRLAN=m CONFIG_IRNET=m CONFIG_IRCOMM=m -# CONFIG_IRDA_ULTRA is not set +CONFIG_IRDA_ULTRA=y # # IrDA options @@ -630,7 +654,7 @@ # SIR device drivers # CONFIG_IRTTY_SIR=m -# CONFIG_IRPORT_SIR is not set +CONFIG_IRPORT_SIR=m # # Dongle support @@ -652,7 +676,78 @@ # # ISDN subsystem # -# CONFIG_ISDN is not set +CONFIG_ISDN=m +CONFIG_ISDN_BOOL=y +CONFIG_ISDN_PPP=y +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 + +# +# low-level hardware drivers +# + +# +# Passive ISDN cards +# +CONFIG_ISDN_DRV_HISAX=m +CONFIG_ISDN_HISAX=y + +# +# D-channel protocol features +# +# CONFIG_HISAX_EURO is not set +# CONFIG_HISAX_1TR6 is not set +# CONFIG_HISAX_NI1 is not set +CONFIG_HISAX_MAX_CARDS=8 + +# +# HiSax supported cards +# +# 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 +764,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 +777,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 +828,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 @@ -756,21 +842,23 @@ # CONFIG_VT=y CONFIG_VT_CONSOLE=y -CONFIG_SERIAL=m +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y # CONFIG_SERIAL_EXTENDED is not set # 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 @@ -842,19 +930,59 @@ # 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 + +# +# Video Adapters +# +# 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 @@ -871,6 +999,7 @@ CONFIG_HFS_FS=m # CONFIG_BEFS_FS is not set # CONFIG_BEFS_DEBUG is not set +CONFIG_HFSPLUS_FS=m # CONFIG_BFS_FS is not set CONFIG_EXT3_FS=y CONFIG_JBD=y @@ -897,7 +1026,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 @@ -963,7 +1092,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 +1128,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 +1136,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 @@ -1056,20 +1183,20 @@ # # 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 @@ -1078,7 +1205,7 @@ # 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 @@ -1096,10 +1223,15 @@ # # USB Multimedia devices # - -# -# Video4Linux support is needed for USB Multimedia device support -# +# 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 # # USB Network adaptors @@ -1108,7 +1240,7 @@ # 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_CDCETHER=m # CONFIG_USB_USBNET is not set # @@ -1132,8 +1264,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 diff -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/Makefile linux-2.4.22-ppc-dev/arch/ppc/kernel/Makefile --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/Makefile 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/Makefile 2003-08-25 23:37:55.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/btext.c linux-2.4.22-ppc-dev/arch/ppc/kernel/btext.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/btext.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/btext.c 2003-08-25 23:37:52.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/entry.S linux-2.4.22-ppc-dev/arch/ppc/kernel/entry.S --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/entry.S 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/entry.S 2003-08-25 23:37:35.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/head.S linux-2.4.22-ppc-dev/arch/ppc/kernel/head.S --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/head.S 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/head.S 2003-08-25 23:37:38.000000000 +0200 @@ -320,7 +320,7 @@ DSSALL sync END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) - STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) + STD_EXCEPTION(0x208, MachineCheck, MachineCheckException) /* Data access exception. */ . = 0x300 @@ -983,7 +983,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) @@ -1712,7 +1714,7 @@ .globl intercept_table intercept_table: - .long 0, 0, i0x200, i0x300, i0x400, 0, i0x600, i0x700 + .long 0, 0, i0x208, i0x300, i0x400, 0, i0x600, i0x700 .long i0x800, 0, 0, 0, 0, i0xd00, 0, 0 .long 0, 0, 0, i0x1300, 0, 0, 0, 0 .long 0, 0, 0, 0, 0, 0, 0, 0 diff -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/irq.c linux-2.4.22-ppc-dev/arch/ppc/kernel/irq.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/irq.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/irq.c 2003-08-25 23:37:34.000000000 +0200 @@ -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), @@ -694,6 +716,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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/m8xx_setup.c linux-2.4.22-ppc-dev/arch/ppc/kernel/m8xx_setup.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/m8xx_setup.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/m8xx_setup.c 2003-08-25 23:37:46.000000000 +0200 @@ -43,6 +43,7 @@ #include #include #include +#include #include "ppc8xx_pic.h" diff -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/misc.S linux-2.4.22-ppc-dev/arch/ppc/kernel/misc.S --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/misc.S 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/misc.S 2003-08-25 23:37:35.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/mk_defs.c linux-2.4.22-ppc-dev/arch/ppc/kernel/mk_defs.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/mk_defs.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/mk_defs.c 2003-08-25 23:37:48.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/open_pic.c linux-2.4.22-ppc-dev/arch/ppc/kernel/open_pic.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/open_pic.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/open_pic.c 2003-08-25 23:37:57.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/pci.c linux-2.4.22-ppc-dev/arch/ppc/kernel/pci.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/pci.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/pci.c 2003-08-25 23:38:04.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/ppc_ksyms.c linux-2.4.22-ppc-dev/arch/ppc/kernel/ppc_ksyms.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/ppc_ksyms.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/ppc_ksyms.c 2003-08-25 23:37:36.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,6 +370,10 @@ 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); #endif /* defined(CONFIG_ALL_PPC) */ diff -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/process.c linux-2.4.22-ppc-dev/arch/ppc/kernel/process.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/process.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/process.c 2003-08-25 23:37:33.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/setup.c linux-2.4.22-ppc-dev/arch/ppc/kernel/setup.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/setup.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/setup.c 2003-08-25 23:37:52.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/signal.c linux-2.4.22-ppc-dev/arch/ppc/kernel/signal.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/signal.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/signal.c 2003-08-25 23:37:53.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/smp-tbsync.c linux-2.4.22-ppc-dev/arch/ppc/kernel/smp-tbsync.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/smp-tbsync.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/smp-tbsync.c 2003-08-25 23:37:36.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/smp.c linux-2.4.22-ppc-dev/arch/ppc/kernel/smp.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/smp.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/smp.c 2003-08-25 23:37:50.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/traps.c linux-2.4.22-ppc-dev/arch/ppc/kernel/traps.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/kernel/traps.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/kernel/traps.c 2003-08-25 23:37:40.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/lib/locks.c linux-2.4.22-ppc-dev/arch/ppc/lib/locks.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/lib/locks.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/lib/locks.c 2003-08-25 23:37:25.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/mm/fault.c linux-2.4.22-ppc-dev/arch/ppc/mm/fault.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/mm/fault.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/mm/fault.c 2003-08-25 23:37:55.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/mm/init.c linux-2.4.22-ppc-dev/arch/ppc/mm/init.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/mm/init.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/mm/init.c 2003-08-25 23:37:27.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/mm/pgtable.c linux-2.4.22-ppc-dev/arch/ppc/mm/pgtable.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/mm/pgtable.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/mm/pgtable.c 2003-08-25 23:37:44.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/platforms/Makefile linux-2.4.22-ppc-dev/arch/ppc/platforms/Makefile --- linux-2.4.22-ppc-dev.orig/arch/ppc/platforms/Makefile 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/platforms/Makefile 2003-08-25 23:37:40.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/platforms/pmac_cpufreq.c linux-2.4.22-ppc-dev/arch/ppc/platforms/pmac_cpufreq.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/platforms/pmac_cpufreq.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/arch/ppc/platforms/pmac_cpufreq.c 2003-08-25 23:37:54.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/platforms/pmac_setup.c linux-2.4.22-ppc-dev/arch/ppc/platforms/pmac_setup.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/platforms/pmac_setup.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/platforms/pmac_setup.c 2003-08-25 23:37:28.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/platforms/pmac_sleep.S linux-2.4.22-ppc-dev/arch/ppc/platforms/pmac_sleep.S --- linux-2.4.22-ppc-dev.orig/arch/ppc/platforms/pmac_sleep.S 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/platforms/pmac_sleep.S 2003-08-25 23:37:26.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/xmon/start.c linux-2.4.22-ppc-dev/arch/ppc/xmon/start.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/xmon/start.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/xmon/start.c 2003-08-25 23:37:28.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 -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/xmon/xmon.c linux-2.4.22-ppc-dev/arch/ppc/xmon/xmon.c --- linux-2.4.22-ppc-dev.orig/arch/ppc/xmon/xmon.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/xmon/xmon.c 2003-08-25 23:37:48.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 -Naur linux-2.4.22-ppc-dev.orig/arch/s390/mm/fault.c linux-2.4.22-ppc-dev/arch/s390/mm/fault.c --- linux-2.4.22-ppc-dev.orig/arch/s390/mm/fault.c 2002-11-29 00:53:11.000000000 +0100 +++ linux-2.4.22-ppc-dev/arch/s390/mm/fault.c 2003-08-25 23:37:30.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 -Naur linux-2.4.22-ppc-dev.orig/arch/s390x/mm/fault.c linux-2.4.22-ppc-dev/arch/s390x/mm/fault.c --- linux-2.4.22-ppc-dev.orig/arch/s390x/mm/fault.c 2002-11-29 00:53:11.000000000 +0100 +++ linux-2.4.22-ppc-dev/arch/s390x/mm/fault.c 2003-08-25 23:37:27.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 -Naur linux-2.4.22-ppc-dev.orig/arch/sh/mm/fault.c linux-2.4.22-ppc-dev/arch/sh/mm/fault.c --- linux-2.4.22-ppc-dev.orig/arch/sh/mm/fault.c 2003-08-27 15:17:47.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/sh/mm/fault.c 2003-08-25 23:37:52.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 -Naur linux-2.4.22-ppc-dev.orig/arch/sparc/mm/fault.c linux-2.4.22-ppc-dev/arch/sparc/mm/fault.c --- linux-2.4.22-ppc-dev.orig/arch/sparc/mm/fault.c 2003-06-13 16:51:32.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/sparc/mm/fault.c 2003-08-25 23:37:58.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 -Naur linux-2.4.22-ppc-dev.orig/arch/sparc64/mm/fault.c linux-2.4.22-ppc-dev/arch/sparc64/mm/fault.c --- linux-2.4.22-ppc-dev.orig/arch/sparc64/mm/fault.c 2002-11-29 00:53:12.000000000 +0100 +++ linux-2.4.22-ppc-dev/arch/sparc64/mm/fault.c 2003-08-25 23:37:45.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/block/ll_rw_blk.c linux-2.4.22-ppc-dev/drivers/block/ll_rw_blk.c --- linux-2.4.22-ppc-dev.orig/drivers/block/ll_rw_blk.c 2003-08-27 15:17:50.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/block/ll_rw_blk.c 2003-08-25 23:37:41.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/char/Config.in linux-2.4.22-ppc-dev/drivers/char/Config.in --- linux-2.4.22-ppc-dev.orig/drivers/char/Config.in 2003-08-27 15:17:52.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/char/Config.in 2003-08-25 23:37:47.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/char/agp/agpgart_be.c linux-2.4.22-ppc-dev/drivers/char/agp/agpgart_be.c --- linux-2.4.22-ppc-dev.orig/drivers/char/agp/agpgart_be.c 2003-08-27 15:17:53.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/char/agp/agpgart_be.c 2003-08-25 23:37:54.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/char/defkeymap.c linux-2.4.22-ppc-dev/drivers/char/defkeymap.c --- linux-2.4.22-ppc-dev.orig/drivers/char/defkeymap.c 1999-10-07 19:17:09.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/char/defkeymap.c 2003-08-25 23:38:00.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/char/drm/drmP.h linux-2.4.22-ppc-dev/drivers/char/drm/drmP.h --- linux-2.4.22-ppc-dev.orig/drivers/char/drm/drmP.h 2002-11-29 00:53:12.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/char/drm/drmP.h 2003-08-25 23:37:33.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/char/drm/drm_memory.h linux-2.4.22-ppc-dev/drivers/char/drm/drm_memory.h --- linux-2.4.22-ppc-dev.orig/drivers/char/drm/drm_memory.h 2002-11-29 00:53:12.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/char/drm/drm_memory.h 2003-08-25 23:37:36.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/char/drm/drm_vm.h linux-2.4.22-ppc-dev/drivers/char/drm/drm_vm.h --- linux-2.4.22-ppc-dev.orig/drivers/char/drm/drm_vm.h 2003-06-13 16:51:32.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/char/drm/drm_vm.h 2003-08-25 23:38:10.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/char/drm/r128_cce.c linux-2.4.22-ppc-dev/drivers/char/drm/r128_cce.c --- linux-2.4.22-ppc-dev.orig/drivers/char/drm/r128_cce.c 2002-11-29 00:53:12.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/char/drm/r128_cce.c 2003-08-25 23:37:27.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/char/drm/r128_drv.h linux-2.4.22-ppc-dev/drivers/char/drm/r128_drv.h --- linux-2.4.22-ppc-dev.orig/drivers/char/drm/r128_drv.h 2002-11-29 00:53:12.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/char/drm/r128_drv.h 2003-08-25 23:37:54.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/char/drm/radeon_cp.c linux-2.4.22-ppc-dev/drivers/char/drm/radeon_cp.c --- linux-2.4.22-ppc-dev.orig/drivers/char/drm/radeon_cp.c 2002-11-29 00:53:12.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/char/drm/radeon_cp.c 2003-08-25 23:37:49.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/char/drm/radeon_drv.h linux-2.4.22-ppc-dev/drivers/char/drm/radeon_drv.h --- linux-2.4.22-ppc-dev.orig/drivers/char/drm/radeon_drv.h 2002-11-29 00:53:12.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/char/drm/radeon_drv.h 2003-08-25 23:37:30.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/char/drm/radeon_state.c linux-2.4.22-ppc-dev/drivers/char/drm/radeon_state.c --- linux-2.4.22-ppc-dev.orig/drivers/char/drm/radeon_state.c 2002-11-29 00:53:12.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/char/drm/radeon_state.c 2003-08-25 23:37:48.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/i2c/i2c-keywest.c linux-2.4.22-ppc-dev/drivers/i2c/i2c-keywest.c --- linux-2.4.22-ppc-dev.orig/drivers/i2c/i2c-keywest.c 2002-11-29 00:53:13.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/i2c/i2c-keywest.c 2003-08-25 23:37:30.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/ide/Config.in linux-2.4.22-ppc-dev/drivers/ide/Config.in --- linux-2.4.22-ppc-dev.orig/drivers/ide/Config.in 2003-08-27 15:17:54.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/ide/Config.in 2003-08-25 23:37:29.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/ide/arm/rapide.c linux-2.4.22-ppc-dev/drivers/ide/arm/rapide.c --- linux-2.4.22-ppc-dev.orig/drivers/ide/arm/rapide.c 2003-06-13 16:51:33.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/ide/arm/rapide.c 2003-08-25 23:37:29.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/ide/ide-disk.c linux-2.4.22-ppc-dev/drivers/ide/ide-disk.c --- linux-2.4.22-ppc-dev.orig/drivers/ide/ide-disk.c 2003-06-13 16:51:33.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/ide/ide-disk.c 2003-08-25 23:37:31.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/ide/ide-probe.c linux-2.4.22-ppc-dev/drivers/ide/ide-probe.c --- linux-2.4.22-ppc-dev.orig/drivers/ide/ide-probe.c 2003-08-27 15:17:54.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/ide/ide-probe.c 2003-08-25 23:37:48.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/ide/ppc/pmac.c linux-2.4.22-ppc-dev/drivers/ide/ppc/pmac.c --- linux-2.4.22-ppc-dev.orig/drivers/ide/ppc/pmac.c 2003-06-13 16:51:34.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/ide/ppc/pmac.c 2003-08-25 23:37:27.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/ide/setup-pci.c linux-2.4.22-ppc-dev/drivers/ide/setup-pci.c --- linux-2.4.22-ppc-dev.orig/drivers/ide/setup-pci.c 2003-08-27 15:17:54.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/ide/setup-pci.c 2003-08-25 23:38:12.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/macintosh/Makefile linux-2.4.22-ppc-dev/drivers/macintosh/Makefile --- linux-2.4.22-ppc-dev.orig/drivers/macintosh/Makefile 2002-11-29 00:53:13.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/macintosh/Makefile 2003-08-25 23:38:04.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/macintosh/macserial.c linux-2.4.22-ppc-dev/drivers/macintosh/macserial.c --- linux-2.4.22-ppc-dev.orig/drivers/macintosh/macserial.c 2002-08-03 02:39:44.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/macintosh/macserial.c 2003-08-25 23:37:39.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/macintosh/therm_pismo.c linux-2.4.22-ppc-dev/drivers/macintosh/therm_pismo.c --- linux-2.4.22-ppc-dev.orig/drivers/macintosh/therm_pismo.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/macintosh/therm_pismo.c 2003-08-25 23:37:36.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/macintosh/via-pmu.c linux-2.4.22-ppc-dev/drivers/macintosh/via-pmu.c --- linux-2.4.22-ppc-dev.orig/drivers/macintosh/via-pmu.c 2003-08-27 15:17:54.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/macintosh/via-pmu.c 2003-08-25 23:37:51.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() +pmu_poll(void) +{ + if (!via) + return; + if (disable_poll) + return; + via_pmu_interrupt(0, 0, 0); +} + +void __openfirmware +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 -Naur linux-2.4.22-ppc-dev.orig/drivers/net/Makefile linux-2.4.22-ppc-dev/drivers/net/Makefile --- linux-2.4.22-ppc-dev.orig/drivers/net/Makefile 2003-08-27 15:17:54.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/net/Makefile 2003-08-25 23:37:55.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/net/irda/irda-usb.c linux-2.4.22-ppc-dev/drivers/net/irda/irda-usb.c --- linux-2.4.22-ppc-dev.orig/drivers/net/irda/irda-usb.c 2003-08-27 15:17:55.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/net/irda/irda-usb.c 2003-08-25 23:37:36.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/net/ne2k-pci.c linux-2.4.22-ppc-dev/drivers/net/ne2k-pci.c --- linux-2.4.22-ppc-dev.orig/drivers/net/ne2k-pci.c 2003-08-27 15:17:55.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/net/ne2k-pci.c 2003-08-25 23:37:52.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/net/pcnet32.c linux-2.4.22-ppc-dev/drivers/net/pcnet32.c --- linux-2.4.22-ppc-dev.orig/drivers/net/pcnet32.c 2003-08-27 15:17:55.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/net/pcnet32.c 2003-08-25 23:37:44.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/net/sungem.c linux-2.4.22-ppc-dev/drivers/net/sungem.c --- linux-2.4.22-ppc-dev.orig/drivers/net/sungem.c 2003-08-27 15:17:56.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/net/sungem.c 2003-08-25 23:38:01.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/net/sungem.h linux-2.4.22-ppc-dev/drivers/net/sungem.h --- linux-2.4.22-ppc-dev.orig/drivers/net/sungem.h 2002-08-03 02:39:44.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/net/sungem.h 2003-08-25 23:37:33.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/net/sungem_phy.c linux-2.4.22-ppc-dev/drivers/net/sungem_phy.c --- linux-2.4.22-ppc-dev.orig/drivers/net/sungem_phy.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/net/sungem_phy.c 2003-08-25 23:38:02.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/net/sungem_phy.h linux-2.4.22-ppc-dev/drivers/net/sungem_phy.h --- linux-2.4.22-ppc-dev.orig/drivers/net/sungem_phy.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/net/sungem_phy.h 2003-08-25 23:37:55.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/net/tg3.c linux-2.4.22-ppc-dev/drivers/net/tg3.c --- linux-2.4.22-ppc-dev.orig/drivers/net/tg3.c 2003-08-27 15:17:58.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/net/tg3.c 2003-08-25 23:37:25.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/net/tulip/tulip_core.c linux-2.4.22-ppc-dev/drivers/net/tulip/tulip_core.c --- linux-2.4.22-ppc-dev.orig/drivers/net/tulip/tulip_core.c 2003-08-27 15:17:58.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/net/tulip/tulip_core.c 2003-08-25 23:37:51.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/net/wan/8253x/build linux-2.4.22-ppc-dev/drivers/net/wan/8253x/build --- linux-2.4.22-ppc-dev.orig/drivers/net/wan/8253x/build 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/net/wan/8253x/build 2003-08-25 23:37:29.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/net/wireless/Config.in linux-2.4.22-ppc-dev/drivers/net/wireless/Config.in --- linux-2.4.22-ppc-dev.orig/drivers/net/wireless/Config.in 2002-11-29 00:53:14.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/net/wireless/Config.in 2003-08-25 23:37:34.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/net/wireless/Makefile linux-2.4.22-ppc-dev/drivers/net/wireless/Makefile --- linux-2.4.22-ppc-dev.orig/drivers/net/wireless/Makefile 2002-08-03 02:39:44.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/net/wireless/Makefile 2003-08-25 23:37:25.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/pci/pci.c linux-2.4.22-ppc-dev/drivers/pci/pci.c --- linux-2.4.22-ppc-dev.orig/drivers/pci/pci.c 2003-06-13 16:51:35.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/pci/pci.c 2003-08-25 23:37:39.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/sound/.version linux-2.4.22-ppc-dev/drivers/sound/.version --- linux-2.4.22-ppc-dev.orig/drivers/sound/.version 1997-11-10 08:01:54.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/sound/.version 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -3.8s -0x030804 diff -Naur linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/Config.in linux-2.4.22-ppc-dev/drivers/sound/dmasound/Config.in --- linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/Config.in 2002-02-25 20:38:04.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/sound/dmasound/Config.in 2003-08-25 23:37:54.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/Makefile linux-2.4.22-ppc-dev/drivers/sound/dmasound/Makefile --- linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/Makefile 2002-02-25 20:38:04.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/sound/dmasound/Makefile 2003-08-25 23:37:44.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/awacs_defs.h linux-2.4.22-ppc-dev/drivers/sound/dmasound/awacs_defs.h --- linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/awacs_defs.h 2002-02-25 20:38:04.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/sound/dmasound/awacs_defs.h 2003-08-25 23:38:02.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/dmasound_awacs.c linux-2.4.22-ppc-dev/drivers/sound/dmasound/dmasound_awacs.c --- linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/dmasound_awacs.c 2002-08-03 02:39:44.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/sound/dmasound/dmasound_awacs.c 2003-08-25 23:38:07.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas3001c.c linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas3001c.c --- linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas3001c.c 2002-02-25 20:38:05.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas3001c.c 2003-08-25 23:37:29.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas3001c_tables.c linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas3001c_tables.c --- linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas3001c_tables.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas3001c_tables.c 2003-08-25 23:37:52.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas3004.c linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas3004.c --- linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas3004.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas3004.c 2003-08-25 23:37:26.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas3004_tables.c linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas3004_tables.c --- linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas3004_tables.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas3004_tables.c 2003-08-25 23:37:44.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas_common.c linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas_common.c --- linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas_common.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas_common.c 2003-08-25 23:37:28.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas_common.h linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas_common.h --- linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas_common.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas_common.h 2003-08-25 23:38:00.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas_eq_prefs.h linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas_eq_prefs.h --- linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas_eq_prefs.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas_eq_prefs.h 2003-08-25 23:37:37.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas_ioctl.h linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas_ioctl.h --- linux-2.4.22-ppc-dev.orig/drivers/sound/dmasound/tas_ioctl.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/sound/dmasound/tas_ioctl.h 2003-08-25 23:37:39.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/usb/host/usb-ohci.c linux-2.4.22-ppc-dev/drivers/usb/host/usb-ohci.c --- linux-2.4.22-ppc-dev.orig/drivers/usb/host/usb-ohci.c 2003-08-27 15:18:01.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/usb/host/usb-ohci.c 2003-08-25 23:38:03.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/usb/host/usb-ohci.h linux-2.4.22-ppc-dev/drivers/usb/host/usb-ohci.h --- linux-2.4.22-ppc-dev.orig/drivers/usb/host/usb-ohci.h 2003-06-13 16:51:36.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/usb/host/usb-ohci.h 2003-08-25 23:37:39.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/usb/storage/scsiglue.c linux-2.4.22-ppc-dev/drivers/usb/storage/scsiglue.c --- linux-2.4.22-ppc-dev.orig/drivers/usb/storage/scsiglue.c 2003-06-13 16:51:37.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/usb/storage/scsiglue.c 2003-08-25 23:37:28.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/video/Config.in linux-2.4.22-ppc-dev/drivers/video/Config.in --- linux-2.4.22-ppc-dev.orig/drivers/video/Config.in 2003-08-27 15:18:01.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/video/Config.in 2003-08-25 23:37:27.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/video/aty/atyfb.h linux-2.4.22-ppc-dev/drivers/video/aty/atyfb.h --- linux-2.4.22-ppc-dev.orig/drivers/video/aty/atyfb.h 2002-11-29 00:53:15.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/video/aty/atyfb.h 2003-08-25 23:37:43.000000000 +0200 @@ -124,6 +124,7 @@ #endif } fbcon_cmap; u8 blitter_may_be_busy; + int asleep; #ifdef __sparc__ u8 mmaped; int open; diff -Naur linux-2.4.22-ppc-dev.orig/drivers/video/aty/atyfb_base.c linux-2.4.22-ppc-dev/drivers/video/aty/atyfb_base.c --- linux-2.4.22-ppc-dev.orig/drivers/video/aty/atyfb_base.c 2002-11-29 00:53:15.000000000 +0100 +++ linux-2.4.22-ppc-dev/drivers/video/aty/atyfb_base.c 2003-08-25 23:37:34.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/video/aty/mach64_cursor.c linux-2.4.22-ppc-dev/drivers/video/aty/mach64_cursor.c --- linux-2.4.22-ppc-dev.orig/drivers/video/aty/mach64_cursor.c 2001-10-25 22:53:52.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/video/aty/mach64_cursor.c 2003-08-25 23:37:39.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 -Naur linux-2.4.22-ppc-dev.orig/drivers/video/aty128fb.c linux-2.4.22-ppc-dev/drivers/video/aty128fb.c --- linux-2.4.22-ppc-dev.orig/drivers/video/aty128fb.c 2003-06-13 16:51:37.000000000 +0200 +++ linux-2.4.22-ppc-dev/drivers/video/aty128fb.c 2003-08-25 23:37:35.000000000 +0200 @@ -55,7 +55,7 @@ #include #include -#ifdef CONFIG_PPC +#ifdef CONFIG_ALL_PPC #include #include #include